From dd6a473008be0ee2c14c08f2012cd687cf68fa25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20P=C3=B6hler=20=28JPo=29?= Date: Mon, 22 Mar 2021 15:59:59 +0100 Subject: [PATCH 01/87] ADD: Test and possible fix for (C)LOBs being handed out of connnection-context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Pöhler (JPo) --- .../server/rawsql/DRawSqlService.java | 21 ++++++ .../server/rawsql/TestRawSqlBuilder.java | 64 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java b/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java index 0b5eb4ad6..7c27264a8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java @@ -9,6 +9,7 @@ import io.ebeaninternal.server.query.DefaultSqlRow; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Types; public class DRawSqlService implements SpiRawSqlService { @@ -49,6 +50,26 @@ public class DRawSqlService implements SpiRawSqlService { name = combine(meta.getSchemaName(i), meta.getTableName(i), name); } ret.put(name, resultSet.getObject(i)); + + // convert (C/B)LOBs to java objects. + // A java.sql.Clob depends on an open connection, so storing this object in a map + // that is accessed later, when the connection is closed, will result in a "connection is closed" exception. + // From the java.sql.Clob documentation: "... which means that a Clob object contains a logical pointer to the SQL CLOB + // data rather than the data itself." + switch (meta.getColumnType(i)) { + case Types.CLOB: + case Types.NCLOB: + ret.put(name, resultSet.getString(i)); + break; + + case Types.BLOB: + ret.put(name, resultSet.getBytes(i)); + break; + + default: + ret.put(name, resultSet.getObject(i)); + break; + } } return ret; } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java b/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java index 4e0a32e24..0fa4c746e 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java @@ -9,17 +9,26 @@ import io.ebean.RawSqlBuilder; import io.ebean.SqlRow; import io.ebean.annotation.ForPlatform; import io.ebean.annotation.Platform; +import io.ebean.datasource.DataSourceConfig; +import io.ebeaninternal.server.core.DefaultServer; import io.ebeaninternal.server.rawsql.SpiRawSql.Sql; import org.junit.Test; import org.tests.model.basic.Customer; +import org.tests.model.basic.EBasicClob; +import org.tests.model.basic.PersistentFileContent; import org.tests.model.basic.ResetBasicData; import org.tests.model.rawsql.ERawSqlAggBean; import javax.sql.DataSource; +import java.nio.charset.StandardCharsets; +import java.sql.Blob; import java.sql.Connection; +import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -283,4 +292,59 @@ public class TestRawSqlBuilder extends BaseTestCase { } } + @Test + public void testCLobClosedConnection() throws Exception { + final EBasicClob eBasicClob = new EBasicClob(); + eBasicClob.setName("eBasicClob"); + final String description = "This is the CLob description"; + eBasicClob.setDescription(description); + DB.save(eBasicClob); + + final String sql = "select description from ebasic_clob where id = ?"; + + List rows = new ArrayList<>(); + final DataSourceConfig config = ((DefaultServer) DB.getDefault()).getServerConfig().getDataSourceConfig(); + + try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword()); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setLong(1, eBasicClob.getId()); + + try (ResultSet resultSet = stmt.executeQuery()) { + while (resultSet.next()) { + rows.add(RawSqlBuilder.sqlRow(resultSet, "true", false)); + } + } + } + + assertThat(rows).hasSize(1); + assertThat(rows.get(0).getString("description")).isEqualTo(description); + } + + @Test + public void testBLobClosedConnection() throws Exception { + final PersistentFileContent pfc = new PersistentFileContent(); + final byte[] bytes = "This is the blob as String".getBytes(StandardCharsets.UTF_8); + pfc.setContent(bytes); + DB.save(pfc); + + final String sql = "select content from persistent_file_content where id = ?"; + + List rows = new ArrayList<>(); + final DataSourceConfig config = ((DefaultServer) DB.getDefault()).getServerConfig().getDataSourceConfig(); + + try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword()); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setLong(1, pfc.getId()); + + try (ResultSet resultSet = stmt.executeQuery()) { + while (resultSet.next()) { + rows.add(RawSqlBuilder.sqlRow(resultSet, "true", false)); + } + } + } + + assertThat(rows).hasSize(1); + assertThat(((Blob) rows.get(0).get("content")).getBytes(0, bytes.length)).isEqualTo(bytes); + } + } From 7408483a43ce4e6b3ecef04534fb3c2103c25e5f Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 15 Jul 2021 17:14:22 +1200 Subject: [PATCH 02/87] #2264 - Use of default timezone for OffsetDateTime offsets problematic for unit tests and presentation layer --- .../server/type/DefaultTypeManager.java | 12 ++++- .../server/type/ScalarTypeOffsetDateTime.java | 7 ++- .../server/type/ScalarTypeZonedDateTime.java | 7 ++- .../type/ScalarTypeOffsetDateTimeTest.java | 50 +++++++++++++++--- .../type/ScalarTypeZonedDateTimeTest.java | 51 ++++++++++++++++--- 5 files changed, 109 insertions(+), 18 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index 82c3ff46a..befe46b20 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -750,12 +750,15 @@ public final class DefaultTypeManager implements TypeManager { } private void initialiseJavaTimeTypes(DatabaseConfig config) { + + ZoneId zoneId = getZoneId(config); + typeMap.put(java.nio.file.Path.class, new ScalarTypePath()); addType(java.time.Period.class, new ScalarTypePeriod()); addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate)); addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime)); - addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime)); - addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime)); + addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime, zoneId)); + addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime, zoneId)); addType(Instant.class, new ScalarTypeInstant(jsonDateTime)); addType(DayOfWeek.class, new ScalarTypeDayOfWeek()); addType(Month.class, new ScalarTypeMonth()); @@ -771,6 +774,11 @@ public final class DefaultTypeManager implements TypeManager { addType(Duration.class, (durationNanos) ? new ScalarTypeDurationWithNanos() : new ScalarTypeDuration()); } + private ZoneId getZoneId(DatabaseConfig config) { + final String dataTimeZone = config.getDataTimeZone(); + return (dataTimeZone == null) ? ZoneOffset.systemDefault() : TimeZone.getTimeZone(dataTimeZone).toZoneId(); + } + private void addType(Class clazz, ScalarType scalarType) { typeMap.put(clazz, scalarType); logicalMap.putIfAbsent(clazz.getSimpleName(), scalarType); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java index b62c693d3..b68f3eba9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java @@ -15,8 +15,11 @@ import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso; */ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode) { + private final ZoneId zoneId; + + public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode, ZoneId zoneId) { super(mode, OffsetDateTime.class, false, Types.TIMESTAMP); + this.zoneId = zoneId; } @Override @@ -46,7 +49,7 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeZonedDateTime(JsonConfig.DateTime mode) { + private final ZoneId zoneId; + + public ScalarTypeZonedDateTime(JsonConfig.DateTime mode, ZoneId zoneId) { super(mode, ZonedDateTime.class, false, Types.TIMESTAMP); + this.zoneId = zoneId; } @Override @@ -44,7 +47,7 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime jsonTester = new JsonTester<>(type); jsonTester.test(now); - ScalarTypeOffsetDateTime typeNanos = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.NANOS); + ScalarTypeOffsetDateTime typeNanos = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.NANOS, ZoneOffset.systemDefault()); jsonTester = new JsonTester<>(typeNanos); jsonTester.test(now); - ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601, ZoneOffset.systemDefault()); jsonTester = new JsonTester<>(typeIso); jsonTester.test(now); } @@ -81,7 +119,7 @@ public class ScalarTypeOffsetDateTimeTest { @Test public void isoJsonFormatParse() { - ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601, ZoneOffset.systemDefault()); OffsetDateTime now = OffsetDateTime.now(); String asJson = typeIso.toJsonISO8601(now); diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java index ea501b273..168041723 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java @@ -4,7 +4,11 @@ import io.ebean.config.JsonConfig; import org.junit.Test; import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.TimeZone; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; @@ -12,12 +16,12 @@ import static org.junit.Assert.*; public class ScalarTypeZonedDateTimeTest { - ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS); + ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS, ZoneId.systemDefault()); ZonedDateTime warmUp = ZonedDateTime.now(); @Test - public void testConvertToMillis() throws Exception { + public void testConvertToMillis() { warmUp.hashCode(); @@ -29,7 +33,7 @@ public class ScalarTypeZonedDateTimeTest { } @Test - public void testConvertFromTimestamp() throws Exception { + public void testConvertFromTimestamp() { Timestamp now = new Timestamp(System.currentTimeMillis()); @@ -39,6 +43,41 @@ public class ScalarTypeZonedDateTimeTest { assertEquals(now, timestamp); } + @Test + public void convertFromInstant_with_UTC_expect_matchingZoneOffset() { + final TimeZone timeZoneToUse = TimeZone.getTimeZone("UTC"); + final ZoneOffset expectedZoneOffset = ZoneOffset.UTC; + + convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedZoneOffset); + } + + @Test + public void convertFromInstant_with_EST_expect_matchingZoneOffset() { + final TimeZone timeZoneToUse = TimeZone.getTimeZone("EST"); + final ZoneOffset expectedOffset = OffsetDateTime.now(timeZoneToUse.toZoneId()).getOffset(); + + convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedOffset); + } + + private void convertFromInstantWithConfiguredTimeZone(TimeZone timeZoneToUse, ZoneOffset expectedZoneOffset) { + TimeZone previous = TimeZone.getDefault(); + try { + OffsetDateTime dateTime = OffsetDateTime.parse("2021-01-01T00:00:00+11:00"); + + // test ScalarTypeOffsetDateTime with the configured timeZone to use + ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS, timeZoneToUse.toZoneId()); + + // effectively we desire to ignore the system timezone and use the configured one + TimeZone.setDefault(timeZoneToUse); + + final ZonedDateTime zonedDateTime = type.convertFromInstant(dateTime.toInstant()); + + assertEquals(expectedZoneOffset, zonedDateTime.getOffset()); + + } finally { + TimeZone.setDefault(previous); + } + } @Test public void testToJdbcType() throws Exception { @@ -68,11 +107,11 @@ public class ScalarTypeZonedDateTimeTest { JsonTester jsonTester = new JsonTester<>(type); jsonTester.test(now); - ScalarTypeZonedDateTime typeNanos = new ScalarTypeZonedDateTime(JsonConfig.DateTime.NANOS); + ScalarTypeZonedDateTime typeNanos = new ScalarTypeZonedDateTime(JsonConfig.DateTime.NANOS, ZoneId.systemDefault()); jsonTester = new JsonTester<>(typeNanos); jsonTester.test(now); - ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601, ZoneId.systemDefault()); jsonTester = new JsonTester<>(typeIso); jsonTester.test(now); } @@ -80,7 +119,7 @@ public class ScalarTypeZonedDateTimeTest { @Test public void toJsonISO8601() { - ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601, ZoneId.systemDefault()); ZonedDateTime now = ZonedDateTime.now(); String asJson = typeIso.toJsonISO8601(now); From 3d4de4dee5dec407187f2fa53daba6d9413a929a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20P=C3=B6hler?= Date: Fri, 16 Jul 2021 15:17:04 +0200 Subject: [PATCH 03/87] FIX TestRawSqlService and TestRawSqlBuilder --- .../java/io/ebeaninternal/server/rawsql/DRawSqlService.java | 1 - .../io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java b/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java index 7c27264a8..5a405dbc4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java @@ -49,7 +49,6 @@ public class DRawSqlService implements SpiRawSqlService { if (ret.containsKey(name)) { name = combine(meta.getSchemaName(i), meta.getTableName(i), name); } - ret.put(name, resultSet.getObject(i)); // convert (C/B)LOBs to java objects. // A java.sql.Clob depends on an open connection, so storing this object in a map diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java b/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java index 0fa4c746e..8d4feeaf9 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/rawsql/TestRawSqlBuilder.java @@ -21,7 +21,6 @@ import org.tests.model.rawsql.ERawSqlAggBean; import javax.sql.DataSource; import java.nio.charset.StandardCharsets; -import java.sql.Blob; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -327,11 +326,10 @@ public class TestRawSqlBuilder extends BaseTestCase { pfc.setContent(bytes); DB.save(pfc); - final String sql = "select content from persistent_file_content where id = ?"; - List rows = new ArrayList<>(); final DataSourceConfig config = ((DefaultServer) DB.getDefault()).getServerConfig().getDataSourceConfig(); + final String sql = "select content from persistent_file_content where id = ?"; try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword()); PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setLong(1, pfc.getId()); @@ -344,7 +342,7 @@ public class TestRawSqlBuilder extends BaseTestCase { } assertThat(rows).hasSize(1); - assertThat(((Blob) rows.get(0).get("content")).getBytes(0, bytes.length)).isEqualTo(bytes); + assertThat(rows.get(0).get("content")).isEqualTo(bytes); } } From f26e4b5381265f4a3c76d762f6ac82bd8eb3c8cf Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 22 Jul 2021 12:37:42 +1200 Subject: [PATCH 04/87] Bump jedis to 3.6.3 (from 3.6.1) --- ebean-redis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index 13e5695b8..404e06e7c 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -16,7 +16,7 @@ redis.clients jedis - 3.6.1 + 3.6.3 From 0baa3a0eacdd29a02ae4034b6901fbf23f3c39f5 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 22 Jul 2021 16:38:28 +1200 Subject: [PATCH 05/87] #2270 - ebean-agent enhancement NPE with MappedSuperclass with no properties that uses named database --- ebean-bom/pom.xml | 2 +- ebean-core/pom.xml | 2 +- .../ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java | 2 +- ebean-ddl-generator/pom.xml | 2 +- kotlin-querybean-generator/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 446c823f2..b08eb172f 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -19,7 +19,7 @@ 4.1 7.0 12.9.0 - 12.9.1 + 12.10.0 diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 5febb1ea6..e5670c75b 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -302,7 +302,7 @@ io.ebean ebean-maven-plugin - 12.9.1 + 12.10.0 test diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java index 012c13c39..014ddb4b6 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/TestNotEnhancedMappedSuper.java @@ -33,7 +33,7 @@ public class TestNotEnhancedMappedSuper extends BaseTestCase { NotEnhancedMappedSuper mappedSuper = new NotEnhancedMappedSuper(); boolean enhanced = (mappedSuper instanceof EntityBean); - Assert.assertFalse(enhanced); + Assert.assertTrue(enhanced); } diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 2c7df7a5d..8641ae080 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -76,7 +76,7 @@ io.ebean ebean-maven-plugin - 12.9.1 + 12.10.0 test diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index a4b14ff53..2c872df20 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -145,7 +145,7 @@ io.ebean ebean-maven-plugin - 12.9.1 + 12.10.0 test From a8456f90db608a38c08d9638fc322d20381c9b79 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 22 Jul 2021 16:46:57 +1200 Subject: [PATCH 06/87] Bump ebean-agent version to 2.10.0 --- ebean-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index b08eb172f..096fe1a6b 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -18,7 +18,7 @@ 12.4.0 4.1 7.0 - 12.9.0 + 12.10.0 12.10.0 From 492bb98e0ef0054059e869badba20987b1786cbf Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 22 Jul 2021 17:12:06 +1200 Subject: [PATCH 07/87] #2269 - findSingleAttributeList does not filter soft-deleted record --- .../server/query/CQueryBuilder.java | 6 +++ .../tests/softdelete/TestSoftDeleteBasic.java | 46 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 1162e914d..41439e4b6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -189,6 +189,12 @@ class CQueryBuilder { SpiQuery query = request.getQuery(); query.setSingleAttribute(); + if (!query.isIncludeSoftDeletes()) { + BeanDescriptor desc = request.getBeanDescriptor(); + if (desc.isSoftDelete()) { + query.addSoftDeletePredicate(desc.getSoftDeletePredicate(alias(query.getAlias()))); + } + } CQueryPredicates predicates = new CQueryPredicates(binder, request); CQueryPlan queryPlan = request.getQueryPlan(); diff --git a/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java b/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java index 1dfec06c4..3364417b9 100644 --- a/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java +++ b/ebean-core/src/test/java/org/tests/softdelete/TestSoftDeleteBasic.java @@ -58,6 +58,52 @@ public class TestSoftDeleteBasic extends BaseTestCase { } + @Test + public void findSingleAttribute() { + + EBasicSoftDelete bean = new EBasicSoftDelete(); + bean.setName("findSingleAttribute"); + DB.save(bean); + + LoggedSqlCollector.start(); + + final String name0 = DB.find(EBasicSoftDelete.class) + .select("name") + .where().eq("name", "findSingleAttribute") + .findSingleAttribute(); + + List sql0 = LoggedSqlCollector.current(); + assertThat(sql0.get(0)).contains("where t0.name = ? and t0.deleted ="); + assertThat(name0).isEqualTo("findSingleAttribute"); + + // now soft delete the bean + DB.delete(bean); + List sqlUpdate = LoggedSqlCollector.current(); + assertThat(sqlUpdate.get(0)).contains("update ebasic_sdchild set"); + + // use setIncludeSoftDeletes + final String name1 = DB.find(EBasicSoftDelete.class) + .select("name") + .where().eq("name", "findSingleAttribute") + .setIncludeSoftDeletes() + .findSingleAttribute(); + + List sql1 = LoggedSqlCollector.current(); + assertThat(sql1.get(0)).doesNotContain(" and t0.deleted ="); + assertThat(name1).isEqualTo("findSingleAttribute"); + + + // not using setIncludeSoftDeletes, so don't find it + final String name2 = DB.find(EBasicSoftDelete.class) + .select("name") + .where().eq("name", "findSingleAttribute") + .findSingleAttribute(); + + List sql2 = LoggedSqlCollector.stop(); + assertThat(sql2.get(0)).contains(" and t0.deleted ="); + assertThat(name2).isNull(); + } + @Test public void testFindIdsWhenIncludeSoftDeletedChlld() { From 1642833fdb9cfac3596e8fee3d04ddc3ec11949b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 22 Jul 2021 18:56:02 +1200 Subject: [PATCH 08/87] Improve javadoc on TQRootBean and generated javadoc on query bean forFetchGroup() method --- .../java/io/ebean/typequery/TQRootBean.java | 36 ++++++++++++++----- .../generator/SimpleQueryBeanWriter.java | 18 ++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java index 728184cf4..9803da8b1 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -213,20 +213,38 @@ public abstract class TQRootBean { /** * Set a FetchGroup to control what part of the object graph is loaded. *

- * This is an alternative to using select() and fetch() providing a nice clean separation - * between what a query should load and the query predicates. - *

+ * FetchGroup is immutable and threadsafe. We expect to create and store + * FetchGroup to a static final field and reuse the instance. + *

+ * FetchGroup is an alternative to using select() and fetch() providing a nice + * clean separation between what a query should load and the query predicates. * *

{@code
    *
-   * FetchGroup fetchGroup = FetchGroup.of(Customer.class)
-   *   .select("name, status")
-   *   .fetch("contacts", "firstName, lastName, email")
-   *   .build();
+   * // immutable threadsafe
    *
-   * List customers =
+   * static final FetchGroup fetchGroup =
+   *   QCustomer.forFetchGroup()
+   *     .shippingAddress.fetch()
+   *     .contacts.fetch()
+   *     .buildFetchGroup();
    *
-   *   new QCustomer()
+   * List customers = new QCustomer()
+   *   .select(fetchGroup)
+   *   .findList();
+   *
+   * }
+ * + * + *
{@code
+   *
+   * static final FetchGroup fetchGroup =
+   *   FetchGroup.of(Customer.class)
+   *     .select("name, status")
+   *     .fetch("contacts", "firstName, lastName, email")
+   *     .build();
+   *
+   * List customers = new QCustomer()
    *   .select(fetchGroup)
    *   .findList();
    *
diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java
index 78fd99779..3d55fd1ad 100644
--- a/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java
+++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java
@@ -188,6 +188,24 @@ class SimpleQueryBeanWriter {
     writer.eol();
     writer.append("  /**").eol();
     writer.append("   * Return a query bean used to build a FetchGroup.").eol();
+    writer.append("   * 

").eol(); + writer.append(" * FetchGroups are immutable and threadsafe and can be used by many").eol(); + writer.append(" * concurrent queries. We typically stored FetchGroup as a static final field.").eol(); + writer.append(" *

").eol(); + writer.append(" * Example creating and using a FetchGroup.").eol(); + writer.append(" *

{@code").eol();
+    writer.append("   * ").eol();
+    writer.append("   * static final FetchGroup fetchGroup = ").eol();
+    writer.append("   *   QCustomer.forFetchGroup()").eol();
+    writer.append("   *     .shippingAddress.fetch()").eol();
+    writer.append("   *     .contacts.fetch()").eol();
+    writer.append("   *     .buildFetchGroup();").eol();
+    writer.append("   * ").eol();
+    writer.append("   * List customers = new QCustomer()").eol();
+    writer.append("   *   .select(fetchGroup)").eol();
+    writer.append("   *   .findList();").eol();
+    writer.append("   * ").eol();
+    writer.append("   * }
").eol(); writer.append(" */").eol(); writer.append(" public static Q%s forFetchGroup() {", shortName).eol(); writer.append(" return new Q%s(FetchGroup.queryFor(%s.class));", shortName, shortName).eol(); From 705e4f40987ded800c91e6f7155c651e6627a516 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 22 Jul 2021 19:03:43 +1200 Subject: [PATCH 09/87] [maven-release-plugin] prepare release ebean-parent-12.10.0 --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 6 +++--- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 10 +++++----- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 10 +++++----- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 8 ++++---- pom.xml | 4 ++-- querybean-generator/pom.xml | 2 +- 16 files changed, 63 insertions(+), 63 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 0a2a3b777..3a9f740c3 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index 6b6e9511a..aa0282efa 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.10.0 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 096fe1a6b..3a4e1d0e0 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-api - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-core-type - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-ddl-generator - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-externalmapping-api - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-externalmapping-xml - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-autotune - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-querybean - 12.10.0-SNAPSHOT + 12.10.0 io.ebean querybean-generator - 12.10.0-SNAPSHOT + 12.10.0 provided io.ebean kotlin-querybean-generator - 12.10.0-SNAPSHOT + 12.10.0 provided io.ebean ebean-test - 12.10.0-SNAPSHOT + 12.10.0 test io.ebean ebean-postgis - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-redis - 12.10.0-SNAPSHOT + 12.10.0 diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index 9d9d22dfc..958959a3f 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.10.0-SNAPSHOT + 12.10.0 diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index e5670c75b..4cb56f7aa 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.10.0 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-core-type - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-externalmapping-api - 12.10.0-SNAPSHOT + 12.10.0 diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 8641ae080..8901ce221 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean ddl generation @@ -28,14 +28,14 @@ io.ebean ebean-core-type - 12.10.0-SNAPSHOT + 12.10.0 provided io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 5eed791a6..934f7b5f9 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 76e183c67..7e47c0294 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.10.0 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.10.0-SNAPSHOT + 12.10.0 @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 test io.ebean ebean-ddl-generator - 12.10.0-SNAPSHOT + 12.10.0 test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 9a7cb3948..00f60c077 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.10.0-SNAPSHOT + 12.10.0 test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 3d0600c83..716049721 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.10.0-SNAPSHOT + 12.10.0 test io.ebean querybean-generator - 12.10.0-SNAPSHOT + 12.10.0 test io.ebean ebean-test - 12.10.0-SNAPSHOT + 12.10.0 test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index 404e06e7c..858ca5174 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.10.0-SNAPSHOT + 12.10.0 provided io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 provided io.ebean ebean-querybean - 12.10.0-SNAPSHOT + 12.10.0 test io.ebean querybean-generator - 12.10.0-SNAPSHOT + 12.10.0 test io.ebean ebean-test - 12.10.0-SNAPSHOT + 12.10.0 test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index fa0b249da..a2f965b5c 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 provided io.ebean ebean-ddl-generator - 12.10.0-SNAPSHOT + 12.10.0 diff --git a/ebean/pom.xml b/ebean/pom.xml index c9c24cb7b..e09b69f55 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 io.ebean ebean-querybean - 12.10.0-SNAPSHOT + 12.10.0 diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 2c872df20..a4a8a557b 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.10.0-SNAPSHOT + 12.10.0 test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.10.0-SNAPSHOT + 12.10.0 test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.10.0-SNAPSHOT + 12.10.0 test diff --git a/pom.xml b/pom.xml index da28cb2ab..f6f870413 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.10.0-SNAPSHOT + 12.10.0 pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.10.0 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index b75980c9e..f9f21ab88 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0-SNAPSHOT + 12.10.0 querybean generator From 33bccd3bcbc906baf8001d26008178a5a2c9814a Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 22 Jul 2021 19:03:54 +1200 Subject: [PATCH 10/87] [maven-release-plugin] prepare for next development iteration --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 6 +++--- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 10 +++++----- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 10 +++++----- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 8 ++++---- pom.xml | 4 ++-- querybean-generator/pom.xml | 2 +- 16 files changed, 63 insertions(+), 63 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 3a9f740c3..d3868ff91 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index aa0282efa..1534f122b 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.10.0 + ebean-parent-12.8.0 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 3a4e1d0e0..0e6ebde4d 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-api - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-core-type - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-ddl-generator - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-externalmapping-api - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-externalmapping-xml - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-autotune - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-querybean - 12.10.0 + 12.10.1-SNAPSHOT io.ebean querybean-generator - 12.10.0 + 12.10.1-SNAPSHOT provided io.ebean kotlin-querybean-generator - 12.10.0 + 12.10.1-SNAPSHOT provided io.ebean ebean-test - 12.10.0 + 12.10.1-SNAPSHOT test io.ebean ebean-postgis - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-redis - 12.10.0 + 12.10.1-SNAPSHOT diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index 958959a3f..ad1d7c207 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.10.0 + 12.10.1-SNAPSHOT diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 4cb56f7aa..8303c519d 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.10.0 + ebean-parent-12.8.0 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-core-type - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-externalmapping-api - 12.10.0 + 12.10.1-SNAPSHOT diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 8901ce221..dec77c4b0 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean ddl generation @@ -28,14 +28,14 @@ io.ebean ebean-core-type - 12.10.0 + 12.10.1-SNAPSHOT provided io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 934f7b5f9..5cb459f61 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 7e47c0294..8275fc989 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.10.0 + ebean-parent-12.8.0 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.10.0 + 12.10.1-SNAPSHOT @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT test io.ebean ebean-ddl-generator - 12.10.0 + 12.10.1-SNAPSHOT test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 00f60c077..ff1beee77 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.10.0 + 12.10.1-SNAPSHOT test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 716049721..d9a2cdd6e 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.10.0 + 12.10.1-SNAPSHOT test io.ebean querybean-generator - 12.10.0 + 12.10.1-SNAPSHOT test io.ebean ebean-test - 12.10.0 + 12.10.1-SNAPSHOT test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index 858ca5174..097cd9318 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.10.0 + 12.10.1-SNAPSHOT provided io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT provided io.ebean ebean-querybean - 12.10.0 + 12.10.1-SNAPSHOT test io.ebean querybean-generator - 12.10.0 + 12.10.1-SNAPSHOT test io.ebean ebean-test - 12.10.0 + 12.10.1-SNAPSHOT test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index a2f965b5c..e889b6204 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT provided io.ebean ebean-ddl-generator - 12.10.0 + 12.10.1-SNAPSHOT diff --git a/ebean/pom.xml b/ebean/pom.xml index e09b69f55..ddd60d284 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT io.ebean ebean-querybean - 12.10.0 + 12.10.1-SNAPSHOT diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index a4a8a557b..9b257b3b3 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.10.0 + 12.10.1-SNAPSHOT test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.10.0 + 12.10.1-SNAPSHOT test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.10.0 + 12.10.1-SNAPSHOT test diff --git a/pom.xml b/pom.xml index f6f870413..0328ea5b6 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.10.0 + 12.10.1-SNAPSHOT pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.10.0 + ebean-parent-12.8.0 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index f9f21ab88..27e5d0399 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.0 + 12.10.1-SNAPSHOT querybean generator From b40d44895531536f76785ea84f516bfbfd4cbbb7 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 23 Jul 2021 12:01:36 +1200 Subject: [PATCH 11/87] #2272 No effective change, tidy test TestM2MModifyTest only Noting that this test fails with the fix for 2272 as M2M intersection deletes are currently executed after inserts. We need a fix to SaveManyBeans to change that so that deletes to intersection table execute after inserts. --- .../java/org/tests/m2m/TestM2MModifyTest.java | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java b/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java index 1f12f85c8..3a50fa547 100644 --- a/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java +++ b/ebean-core/src/test/java/org/tests/m2m/TestM2MModifyTest.java @@ -1,14 +1,15 @@ package org.tests.m2m; import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import io.ebean.DB; +import org.junit.Test; import org.tests.model.basic.MRole; import org.tests.model.basic.MUser; -import org.junit.Assert; -import org.junit.Test; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + public class TestM2MModifyTest extends BaseTestCase { @Test @@ -19,8 +20,8 @@ public class TestM2MModifyTest extends BaseTestCase { MRole r1 = new MRole("r1"); // Save r1 and r2 - Ebean.save(r0); - Ebean.save(r1); + DB.save(r0); + DB.save(r1); // Create a new user MUser u0 = new MUser("usr0"); @@ -28,32 +29,27 @@ public class TestM2MModifyTest extends BaseTestCase { u0.addRole(r1); // Save the user - Ebean.save(u0); + DB.save(u0); List roles = u0.getRoles(); - Assert.assertTrue(roles.size() == 2); + assertThat(roles).hasSize(2); - u0 = Ebean.find(MUser.class, u0.getUserid()); + u0 = DB.find(MUser.class, u0.getUserid()); roles = u0.getRoles(); - int nrRoles = roles.size(); - - Assert.assertTrue(nrRoles == 2); + assertThat(roles).hasSize(2); roles.clear(); roles.add(r0); roles.add(r1); roles.remove(r1); - Ebean.save(u0); + DB.save(u0); - u0 = Ebean.find(MUser.class, u0.getUserid()); + u0 = DB.find(MUser.class, u0.getUserid()); roles = u0.getRoles(); - - nrRoles = roles.size(); - - Assert.assertTrue(nrRoles == 1); + assertThat(roles).hasSize(1); } } From e54b7052e0c79d9be32bd8358afd41c51b4546b9 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 23 Jul 2021 12:18:06 +1200 Subject: [PATCH 12/87] #2272 Fix for collection with orphanRemoval (such that equals/hashCode isn't use) - Changes ModifyHolder to use IdentityHashMap such that equals/hashcode isn't invoked - Change SaveManyBeans such that M2M intersection deletes execute before inserts The SaveManyBeans change is required to pass TestM2MModifyTest. With that test we get a delete/insert pair that we somewhat expect because we clear() the collection. Previously equals/hashCode meant we didn't get that delete/insert pair. The test changes from containsExactly() to containsOnly() as we now can't guarantee the ordering with IdentityHashMap. --- .../java/io/ebean/common/ModifyHolder.java | 29 +++---- .../server/persist/SaveManyBeans.java | 21 +++-- .../java/io/ebean/common/BeanListTest.java | 87 +++++++++---------- .../java/io/ebean/common/BeanMapTest.java | 82 ++++++++--------- .../java/io/ebean/common/BeanSetTest.java | 66 +++++++------- .../model/orphanremoval/OmBeanListChild.java | 33 +++++++ .../model/orphanremoval/OmBeanListParent.java | 40 +++++++++ .../TestOrphanRemovalOverwrite.java | 37 ++++++++ 8 files changed, 245 insertions(+), 150 deletions(-) create mode 100644 ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java create mode 100644 ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java create mode 100644 ebean-core/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java diff --git a/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java b/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java index 6da14d3a5..957b0fa88 100644 --- a/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java +++ b/ebean-api/src/main/java/io/ebean/common/ModifyHolder.java @@ -3,9 +3,7 @@ package io.ebean.common; import io.ebean.bean.EntityBean; import java.io.Serializable; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; /** * Holds sets of additions and deletions from a 'owner' List Set or Map. @@ -23,19 +21,19 @@ class ModifyHolder implements Serializable { /** * Deletions list for manyToMany persistence. */ - private Set modifyDeletions = new LinkedHashSet<>(); + private Map modifyDeletions = new IdentityHashMap<>(); /** * Additions list for manyToMany persistence. */ - private Set modifyAdditions = new LinkedHashSet<>(); + private Map modifyAdditions = new IdentityHashMap<>(); private boolean touched; void reset() { touched = false; - modifyDeletions = new LinkedHashSet<>(); - modifyAdditions = new LinkedHashSet<>(); + modifyDeletions = new IdentityHashMap<>(); + modifyAdditions = new IdentityHashMap<>(); } /** @@ -50,51 +48,46 @@ class ModifyHolder implements Serializable { } private boolean undoDeletion(E bean) { - return (bean != null) && modifyDeletions.remove(bean); + return (bean != null) && modifyDeletions.remove(bean) != null; } void modifyAddition(E bean) { if (bean != null) { touched = true; - if (bean instanceof EntityBean) { ((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false); } - // If it is to delete then just remove the deletion if (!undoDeletion(bean)) { - // Insert - modifyAdditions.add(bean); + modifyAdditions.put(bean, bean); } } } private boolean undoAddition(Object bean) { - return (bean != null) && modifyAdditions.remove(bean); + return (bean != null) && modifyAdditions.remove(bean) != null; } @SuppressWarnings("unchecked") void modifyRemoval(Object bean) { if (bean != null) { touched = true; - if (bean instanceof EntityBean) { ((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true); } - // If it is to be added then just remove the addition if (!undoAddition(bean)) { - modifyDeletions.add((E) bean); + modifyDeletions.put((E) bean, bean); } } } Set getModifyAdditions() { - return modifyAdditions; + return modifyAdditions.keySet(); } Set getModifyRemovals() { - return modifyDeletions; + return modifyDeletions.keySet(); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java index f273b9dbc..1f1da9fc0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/SaveManyBeans.java @@ -295,7 +295,16 @@ public class SaveManyBeans extends SaveManyBase { } transaction.depth(+1); - + if (deletions != null && !deletions.isEmpty()) { + for (Object other : deletions) { + EntityBean otherDelete = (EntityBean) other; + // the object from the 'other' side of the ManyToMany + // build a intersection row for 'delete' + IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish); + SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD); + persister.executeOrQueue(sqlDelete, transaction, queue); + } + } if (additions != null && !additions.isEmpty()) { for (Object other : additions) { EntityBean otherBean = (EntityBean) other; @@ -318,16 +327,6 @@ public class SaveManyBeans extends SaveManyBase { } } } - if (deletions != null && !deletions.isEmpty()) { - for (Object other : deletions) { - EntityBean otherDelete = (EntityBean) other; - // the object from the 'other' side of the ManyToMany - // build a intersection row for 'delete' - IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish); - SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD); - persister.executeOrQueue(sqlDelete, transaction, queue); - } - } // decrease the depth back to what it was transaction.depth(-1); } diff --git a/ebean-core/src/test/java/io/ebean/common/BeanListTest.java b/ebean-core/src/test/java/io/ebean/common/BeanListTest.java index 11a7429d7..03c08efad 100644 --- a/ebean-core/src/test/java/io/ebean/common/BeanListTest.java +++ b/ebean-core/src/test/java/io/ebean/common/BeanListTest.java @@ -4,7 +4,6 @@ import io.ebean.bean.BeanCollection; import org.junit.Test; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -13,9 +12,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class BeanListTest { - private Object object1 = new Object(); - private Object object2 = new Object(); - private Object object3 = new Object(); + private final Object object1 = new Object(); + private final Object object2 = new Object(); + private final Object object3 = new Object(); private List all() { List all = new ArrayList<>(); @@ -33,7 +32,7 @@ public class BeanListTest { } @Test - public void test_setModifyListening_null() throws Exception { + public void test_setModifyListening_null() { BeanList list = new BeanList<>(); list.setModifyListening(null); @@ -45,7 +44,7 @@ public class BeanListTest { } @Test - public void test_setModifyListening_none() throws Exception { + public void test_setModifyListening_none() { BeanList list = new BeanList<>(); list.setModifyListening(BeanCollection.ModifyListenMode.NONE); @@ -57,28 +56,28 @@ public class BeanListTest { } @Test - public void testAdd() throws Exception { + public void testAdd() { BeanList list = new BeanList<>(); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); list.add(object1); - assertThat(list.getModifyAdditions()).containsExactly(object1); + assertThat(list.getModifyAdditions()).containsOnly(object1); assertThat(list.getModifyRemovals()).isEmpty(); list.add(object1); - assertThat(list.getModifyAdditions()).containsExactly(object1); + assertThat(list.getModifyAdditions()).containsOnly(object1); list.add(object2); - assertThat(list.getModifyAdditions()).containsExactly(object1, object2); + assertThat(list.getModifyAdditions()).containsOnly(object1, object2); list.remove(object1); - assertThat(list.getModifyAdditions()).containsExactly(object2); + assertThat(list.getModifyAdditions()).containsOnly(object2); assertThat(list.getModifyRemovals()).isEmpty(); } @Test - public void testAddAll_given_emptyStart() throws Exception { + public void testAddAll_given_emptyStart() { BeanList list = new BeanList<>(); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -86,12 +85,12 @@ public class BeanListTest { // act list.addAll(all()); - assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3); assertThat(list.getModifyRemovals()).isEmpty(); } @Test - public void test_removals_DeleteThenAddBack_expect_noChange() throws Exception { + public void test_removals_DeleteThenAddBack_expect_noChange() { BeanList list = new BeanList<>(some()); list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS); @@ -106,33 +105,33 @@ public class BeanListTest { } @Test - public void test_sort_whenAll_expect_noChange() throws Exception { + public void test_sort_whenAll_expect_noChange() { BeanList list = new BeanList<>(all()); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); // act - Collections.sort(list, Comparator.comparingInt(Object::hashCode)); + list.sort(Comparator.comparingInt(Object::hashCode)); assertThat(list.getModifyRemovals()).isEmpty(); assertThat(list.getModifyAdditions()).isEmpty(); } @Test - public void test_sort_whenRemovals_expect_noChange() throws Exception { + public void test_sort_whenRemovals_expect_noChange() { BeanList list = new BeanList<>(all()); list.setModifyListening(BeanCollection.ModifyListenMode.REMOVALS); // act - Collections.sort(list, Comparator.comparingInt(Object::hashCode)); + list.sort(Comparator.comparingInt(Object::hashCode)); assertThat(list.getModifyRemovals()).isEmpty(); assertThat(list.getModifyAdditions()).isEmpty(); } @Test - public void testAdd_given_someAlreadyIn() throws Exception { + public void testAdd_given_someAlreadyIn() { BeanList list = new BeanList<>(some()); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -143,12 +142,12 @@ public class BeanListTest { assertThat(list.contains(object2)).isTrue(); list.add(object2); // object2 added as List allows duplicates - assertThat(list.getModifyAdditions()).containsExactly(object1, object2); + assertThat(list.getModifyAdditions()).containsOnly(object1, object2); assertThat(list.getModifyRemovals()).isEmpty(); } @Test - public void testAddSome_given_someAlreadyIn() throws Exception { + public void testAddSome_given_someAlreadyIn() { BeanList list = new BeanList<>(some()); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -156,43 +155,43 @@ public class BeanListTest { // act list.addAll(all()); - assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3); assertThat(list.getModifyRemovals()).isEmpty(); } @Test - public void testRemove_given_beansInAdditions() throws Exception { + public void testRemove_given_beansInAdditions() { BeanList list = new BeanList<>(); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); list.addAll(all()); - assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3); // act list.remove(object2); list.remove(object3); - assertThat(list.getModifyAdditions()).containsExactly(object1); + assertThat(list.getModifyAdditions()).containsOnly(object1); assertThat(list.getModifyRemovals()).isEmpty(); } @Test - public void testRemoveAll_given_beansInAdditions() throws Exception { + public void testRemoveAll_given_beansInAdditions() { BeanList list = new BeanList<>(); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); list.addAll(all()); - assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3); // act list.removeAll(some()); - assertThat(list.getModifyAdditions()).containsExactly(object1); + assertThat(list.getModifyAdditions()).containsOnly(object1); assertThat(list.getModifyRemovals()).isEmpty(); } @Test - public void testRemove_given_beansNotInAdditions() throws Exception { + public void testRemove_given_beansNotInAdditions() { BeanList list = new BeanList<>(all()); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -203,11 +202,11 @@ public class BeanListTest { // assert assertThat(list.getModifyAdditions()).isEmpty(); - assertThat(list.getModifyRemovals()).containsExactly(object2, object3); + assertThat(list.getModifyRemovals()).containsOnly(object2, object3); } @Test - public void testRemoveAll_given_beansNotInAdditions() throws Exception { + public void testRemoveAll_given_beansNotInAdditions() { BeanList list = new BeanList<>(all()); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -217,11 +216,11 @@ public class BeanListTest { // assert assertThat(list.getModifyAdditions()).isEmpty(); - assertThat(list.getModifyRemovals()).containsExactly(object2, object3); + assertThat(list.getModifyRemovals()).containsOnly(object2, object3); } @Test - public void testClear() throws Exception { + public void testClear() { BeanList list = new BeanList<>(all()); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -230,12 +229,12 @@ public class BeanListTest { list.clear(); //assert - assertThat(list.getModifyRemovals()).containsExactly(object1, object2, object3); + assertThat(list.getModifyRemovals()).containsOnly(object1, object2, object3); assertThat(list.getModifyAdditions()).isEmpty(); } @Test - public void testClear_given_someBeansInAdditions() throws Exception { + public void testClear_given_someBeansInAdditions() { BeanList list = new BeanList<>(); list.add(object1); @@ -247,27 +246,27 @@ public class BeanListTest { list.clear(); //assert - assertThat(list.getModifyRemovals()).containsExactly(object1); + assertThat(list.getModifyRemovals()).containsOnly(object1); assertThat(list.getModifyAdditions()).isEmpty(); } @Test - public void testRetainAll_given_beansInAdditions() throws Exception { + public void testRetainAll_given_beansInAdditions() { BeanList list = new BeanList<>(); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); list.addAll(all()); - assertThat(list.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(list.getModifyAdditions()).containsOnly(object1, object2, object3); // act list.retainAll(some()); - assertThat(list.getModifyAdditions()).containsExactly(object2, object3); + assertThat(list.getModifyAdditions()).containsOnly(object2, object3); assertThat(list.getModifyRemovals()).isEmpty(); } @Test - public void testRetainAll_given_someBeansInAdditions() throws Exception { + public void testRetainAll_given_someBeansInAdditions() { BeanList list = new BeanList<>(); list.add(object1); @@ -278,12 +277,12 @@ public class BeanListTest { // act list.retainAll(some()); - assertThat(list.getModifyAdditions()).containsExactly(object3); - assertThat(list.getModifyRemovals()).containsExactly(object1); + assertThat(list.getModifyAdditions()).containsOnly(object3); + assertThat(list.getModifyRemovals()).containsOnly(object1); } @Test - public void testRetainAll_given_noBeansInAdditions() throws Exception { + public void testRetainAll_given_noBeansInAdditions() { BeanList list = new BeanList<>(all()); list.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -291,7 +290,7 @@ public class BeanListTest { // act list.retainAll(some()); - assertThat(list.getModifyRemovals()).containsExactly(object1); + assertThat(list.getModifyRemovals()).containsOnly(object1); } @Test diff --git a/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java b/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java index 003539a72..3101fc0f2 100644 --- a/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java +++ b/ebean-core/src/test/java/io/ebean/common/BeanMapTest.java @@ -34,30 +34,30 @@ public class BeanMapTest { } @Test - public void testAdd() throws Exception { + public void testAdd() { BeanMap map = new BeanMap<>(); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); map.put("1", object1); map.put("4", null); - assertThat(map.getModifyAdditions()).containsExactly(object1); + assertThat(map.getModifyAdditions()).containsOnly(object1); assertThat(map.getModifyRemovals()).isEmpty(); map.put("1", object1); map.put("4", null); - assertThat(map.getModifyAdditions()).containsExactly(object1); + assertThat(map.getModifyAdditions()).containsOnly(object1); map.put("2", object2); - assertThat(map.getModifyAdditions()).containsExactly(object1, object2); + assertThat(map.getModifyAdditions()).containsOnly(object1, object2); map.remove("1"); - assertThat(map.getModifyAdditions()).containsExactly(object2); + assertThat(map.getModifyAdditions()).containsOnly(object2); assertThat(map.getModifyRemovals()).isEmpty(); } @Test - public void testAddAll_given_emptyStart() throws Exception { + public void testAddAll_given_emptyStart() { BeanMap set = new BeanMap<>(); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -65,28 +65,28 @@ public class BeanMapTest { // act set.putAll(all()); - assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testAdd_given_someAlreadyIn() throws Exception { + public void testAdd_given_someAlreadyIn() { BeanMap map = new BeanMap<>(some()); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); // act - assertThat(map.values().contains(object1)).isFalse(); + assertThat(map.containsValue(object1)).isFalse(); map.put("1", object1); - assertThat(map.values().contains(object2)).isTrue(); + assertThat(map.containsValue(object2)).isTrue(); map.put("2", object2); - assertThat(map.getModifyAdditions()).containsExactly(object1); + assertThat(map.getModifyAdditions()).containsOnly(object1); assertThat(map.getModifyRemovals()).isEmpty(); } @Test - public void testAddSome_given_someAlreadyIn() throws Exception { + public void testAddSome_given_someAlreadyIn() { BeanMap map = new BeanMap<>(some()); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -94,44 +94,44 @@ public class BeanMapTest { // act map.putAll(all()); - assertThat(map.getModifyAdditions()).containsExactly(object1); + assertThat(map.getModifyAdditions()).containsOnly(object1); assertThat(map.getModifyRemovals()).isEmpty(); } @Test - public void testRemove_given_beansInAdditions() throws Exception { + public void testRemove_given_beansInAdditions() { BeanMap map = new BeanMap<>(); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); map.putAll(all()); - assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3); // act map.remove("2"); map.remove("3"); - assertThat(map.getModifyAdditions()).containsExactly(object1); + assertThat(map.getModifyAdditions()).containsOnly(object1); assertThat(map.getModifyRemovals()).isEmpty(); } @Test - public void testRemoveAll_given_beansInAdditions() throws Exception { + public void testRemoveAll_given_beansInAdditions() { BeanMap map = new BeanMap<>(); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); map.putAll(all()); - assertThat(map.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(map.getModifyAdditions()).containsOnly(object1, object2, object3); // act map.remove("2"); map.remove("3"); - assertThat(map.getModifyAdditions()).containsExactly(object1); + assertThat(map.getModifyAdditions()).containsOnly(object1); assertThat(map.getModifyRemovals()).isEmpty(); } @Test - public void testRemove_given_beansNotInAdditions() throws Exception { + public void testRemove_given_beansNotInAdditions() { BeanMap map = new BeanMap<>(all()); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -142,11 +142,11 @@ public class BeanMapTest { // assert assertThat(map.getModifyAdditions()).isEmpty(); - assertThat(map.getModifyRemovals()).containsExactly(object2, object3); + assertThat(map.getModifyRemovals()).containsOnly(object2, object3); } @Test - public void testRemoveAll_given_beansNotInAdditions() throws Exception { + public void testRemoveAll_given_beansNotInAdditions() { BeanMap map = new BeanMap<>(all()); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -157,11 +157,11 @@ public class BeanMapTest { // assert assertThat(map.getModifyAdditions()).isEmpty(); - assertThat(map.getModifyRemovals()).containsExactly(object2, object3); + assertThat(map.getModifyRemovals()).containsOnly(object2, object3); } @Test - public void testClear() throws Exception { + public void testClear() { BeanMap map = new BeanMap<>(all()); map.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -170,12 +170,12 @@ public class BeanMapTest { map.clear(); //assert - assertThat(map.getModifyRemovals()).containsExactly(object1, object2, object3); + assertThat(map.getModifyRemovals()).containsOnly(object1, object2, object3); assertThat(map.getModifyAdditions()).isEmpty(); } @Test - public void testClear_given_someBeansInAdditions() throws Exception { + public void testClear_given_someBeansInAdditions() { BeanMap map = newModifyListeningMap(); map.put("2", object2); @@ -185,7 +185,7 @@ public class BeanMapTest { map.clear(); //assert - assertThat(map.getModifyRemovals()).containsExactly(object1); + assertThat(map.getModifyRemovals()).containsOnly(object1); assertThat(map.getModifyAdditions()).isEmpty(); } @@ -228,7 +228,7 @@ public class BeanMapTest { assertThat(map).doesNotContainKeys("1"); assertThat(map.get("1")).isNull(); - assertThat(map.getModifyRemovals()).containsExactly(object1); + assertThat(map.getModifyRemovals()).containsOnly(object1); } @Test @@ -245,7 +245,7 @@ public class BeanMapTest { assertThat(map).isEmpty(); assertThat(keySet).isEmpty(); - assertThat(map.getModifyRemovals()).containsExactly(object1, object2); + assertThat(map.getModifyRemovals()).containsOnly(object1, object2); } @Test @@ -258,20 +258,14 @@ public class BeanMapTest { map.setModifyListening(BeanCollection.ModifyListenMode.ALL); final Set keySet = map.keySet(); - final Iterator iterator = keySet.iterator(); - while (iterator.hasNext()) { - final String key = iterator.next(); - if (key.equals("2")) { - iterator.remove(); - } - } + keySet.removeIf(key -> key.equals("2")); assertThat(map).hasSize(2); assertThat(keySet).hasSize(2); assertThat(keySet).containsExactly("1", "3"); assertThat(map).containsKeys("1", "3"); - assertThat(map.getModifyRemovals()).containsExactly(object2); + assertThat(map.getModifyRemovals()).containsOnly(object2); } @Test @@ -294,7 +288,7 @@ public class BeanMapTest { assertThat(keySet).containsExactly("1", "4"); assertThat(map).containsKeys("1", "4"); - assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5); + assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5); } @@ -318,7 +312,7 @@ public class BeanMapTest { assertThat(keySet).containsExactly("2", "3", "5"); assertThat(map).containsKeys("2", "3", "5"); - assertThat(map.getModifyRemovals()).containsExactly(object1, object4); + assertThat(map.getModifyRemovals()).containsOnly(object1, object4); } @Test(expected = UnsupportedOperationException.class) @@ -348,7 +342,7 @@ public class BeanMapTest { assertThat(entries).isEmpty(); assertThat(map).isEmpty(); - assertThat(map.getModifyRemovals()).containsExactly(object1); + assertThat(map.getModifyRemovals()).containsOnly(object1); } @Test @@ -365,7 +359,7 @@ public class BeanMapTest { assertThat(existed22).isFalse(); assertThat(map).hasSize(4); - assertThat(map.getModifyRemovals()).containsExactly(object1); + assertThat(map.getModifyRemovals()).containsOnly(object1); } @Test @@ -395,7 +389,7 @@ public class BeanMapTest { } assertThat(map).hasSize(3); assertThat(entries).hasSize(3); - assertThat(map.getModifyRemovals()).containsExactly(object2, object5); + assertThat(map.getModifyRemovals()).containsOnly(object2, object5); } @Test @@ -406,7 +400,7 @@ public class BeanMapTest { entries.removeAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4))); assertThat(map).hasSize(3); assertThat(entries).hasSize(3); - assertThat(map.getModifyRemovals()).containsExactly(object1, object4); + assertThat(map.getModifyRemovals()).containsOnly(object1, object4); } @Test @@ -417,7 +411,7 @@ public class BeanMapTest { entries.retainAll(asList(new AbstractMap.SimpleEntry<>("1", object1), new AbstractMap.SimpleEntry<>("3", object4), new AbstractMap.SimpleEntry<>("4", object4))); assertThat(map).hasSize(2); assertThat(entries).hasSize(2); - assertThat(map.getModifyRemovals()).containsExactly(object2, object3, object5); + assertThat(map.getModifyRemovals()).containsOnly(object2, object3, object5); } private BeanMap newModifyListeningMap() { diff --git a/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java b/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java index c274d34d7..40253faef 100644 --- a/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java +++ b/ebean-core/src/test/java/io/ebean/common/BeanSetTest.java @@ -31,28 +31,28 @@ public class BeanSetTest { } @Test - public void testAdd() throws Exception { + public void testAdd() { BeanSet set = new BeanSet<>(); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); set.add(object1); - assertThat(set.getModifyAdditions()).containsExactly(object1); + assertThat(set.getModifyAdditions()).containsOnly(object1); assertThat(set.getModifyRemovals()).isEmpty(); set.add(object1); - assertThat(set.getModifyAdditions()).containsExactly(object1); + assertThat(set.getModifyAdditions()).containsOnly(object1); set.add(object2); - assertThat(set.getModifyAdditions()).containsExactly(object1, object2); + assertThat(set.getModifyAdditions()).containsOnly(object1, object2); set.remove(object1); - assertThat(set.getModifyAdditions()).containsExactly(object2); + assertThat(set.getModifyAdditions()).containsOnly(object2); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testAddAll_given_emptyStart() throws Exception { + public void testAddAll_given_emptyStart() { BeanSet set = new BeanSet<>(); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -60,12 +60,12 @@ public class BeanSetTest { // act set.addAll(all()); - assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testAdd_given_someAlreadyIn() throws Exception { + public void testAdd_given_someAlreadyIn() { BeanSet set = new BeanSet<>(some()); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -76,12 +76,12 @@ public class BeanSetTest { assertThat(set.contains(object2)).isTrue(); set.add(object2); - assertThat(set.getModifyAdditions()).containsExactly(object1); + assertThat(set.getModifyAdditions()).containsOnly(object1); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testAddSome_given_someAlreadyIn() throws Exception { + public void testAddSome_given_someAlreadyIn() { BeanSet set = new BeanSet<>(some()); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -89,43 +89,43 @@ public class BeanSetTest { // act set.addAll(all()); - assertThat(set.getModifyAdditions()).containsExactly(object1); + assertThat(set.getModifyAdditions()).containsOnly(object1); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testRemove_given_beansInAdditions() throws Exception { + public void testRemove_given_beansInAdditions() { BeanSet set = new BeanSet<>(); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); set.addAll(all()); - assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3); // act set.remove(object2); set.remove(object3); - assertThat(set.getModifyAdditions()).containsExactly(object1); + assertThat(set.getModifyAdditions()).containsOnly(object1); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testRemoveAll_given_beansInAdditions() throws Exception { + public void testRemoveAll_given_beansInAdditions() { BeanSet set = new BeanSet<>(); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); set.addAll(all()); - assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3); // act set.removeAll(some()); - assertThat(set.getModifyAdditions()).containsExactly(object1); + assertThat(set.getModifyAdditions()).containsOnly(object1); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testRemove_given_beansNotInAdditions() throws Exception { + public void testRemove_given_beansNotInAdditions() { BeanSet set = new BeanSet<>(all()); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -136,11 +136,11 @@ public class BeanSetTest { // assert assertThat(set.getModifyAdditions()).isEmpty(); - assertThat(set.getModifyRemovals()).containsExactly(object2, object3); + assertThat(set.getModifyRemovals()).containsOnly(object2, object3); } @Test - public void testRemoveAll_given_beansNotInAdditions() throws Exception { + public void testRemoveAll_given_beansNotInAdditions() { BeanSet set = new BeanSet<>(all()); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -150,11 +150,11 @@ public class BeanSetTest { // assert assertThat(set.getModifyAdditions()).isEmpty(); - assertThat(set.getModifyRemovals()).containsExactly(object2, object3); + assertThat(set.getModifyRemovals()).containsOnly(object2, object3); } @Test - public void testClear() throws Exception { + public void testClear() { BeanSet set = new BeanSet<>(all()); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -163,12 +163,12 @@ public class BeanSetTest { set.clear(); //assert - assertThat(set.getModifyRemovals()).containsExactly(object1, object2, object3); + assertThat(set.getModifyRemovals()).containsOnly(object1, object2, object3); assertThat(set.getModifyAdditions()).isEmpty(); } @Test - public void testClear_given_someBeansInAdditions() throws Exception { + public void testClear_given_someBeansInAdditions() { BeanSet set = new BeanSet<>(); set.add(object1); @@ -180,27 +180,27 @@ public class BeanSetTest { set.clear(); //assert - assertThat(set.getModifyRemovals()).containsExactly(object1); + assertThat(set.getModifyRemovals()).containsOnly(object1); assertThat(set.getModifyAdditions()).isEmpty(); } @Test - public void testRetainAll_given_beansInAdditions() throws Exception { + public void testRetainAll_given_beansInAdditions() { BeanSet set = new BeanSet<>(); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); set.addAll(all()); - assertThat(set.getModifyAdditions()).containsExactly(object1, object2, object3); + assertThat(set.getModifyAdditions()).containsOnly(object1, object2, object3); // act set.retainAll(some()); - assertThat(set.getModifyAdditions()).containsExactly(object2, object3); + assertThat(set.getModifyAdditions()).containsOnly(object2, object3); assertThat(set.getModifyRemovals()).isEmpty(); } @Test - public void testRetainAll_given_someBeansInAdditions() throws Exception { + public void testRetainAll_given_someBeansInAdditions() { BeanSet set = new BeanSet<>(); set.add(object1); @@ -211,12 +211,12 @@ public class BeanSetTest { // act set.retainAll(some()); - assertThat(set.getModifyAdditions()).containsExactly(object3); - assertThat(set.getModifyRemovals()).containsExactly(object1); + assertThat(set.getModifyAdditions()).containsOnly(object3); + assertThat(set.getModifyRemovals()).containsOnly(object1); } @Test - public void testRetainAll_given_noBeansInAdditions() throws Exception { + public void testRetainAll_given_noBeansInAdditions() { BeanSet set = new BeanSet<>(all()); set.setModifyListening(BeanCollection.ModifyListenMode.ALL); @@ -224,7 +224,7 @@ public class BeanSetTest { // act set.retainAll(some()); - assertThat(set.getModifyRemovals()).containsExactly(object1); + assertThat(set.getModifyRemovals()).containsOnly(object1); } } diff --git a/ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java b/ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java new file mode 100644 index 000000000..75790e26a --- /dev/null +++ b/ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListChild.java @@ -0,0 +1,33 @@ +package org.tests.model.orphanremoval; + +import io.ebean.Model; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Version; + +@Entity +public class OmBeanListChild extends Model { + + @Id + private Long id; + + private final String name; + + @ManyToOne + private OmBeanListParent parent; + + @Version + private long version; + + public OmBeanListChild(String name) { + this.name = name; + } + + public Long getId() { + return id; + } +} + + diff --git a/ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java b/ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java new file mode 100644 index 000000000..6a9cd478f --- /dev/null +++ b/ebean-core/src/test/java/org/tests/model/orphanremoval/OmBeanListParent.java @@ -0,0 +1,40 @@ +package org.tests.model.orphanremoval; + +import io.ebean.Model; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Version; +import java.util.List; + +import static javax.persistence.CascadeType.ALL; + +@Entity +public class OmBeanListParent extends Model { + + @Id + private long id; + + @Version + private long version; + + @OneToMany(cascade = ALL, mappedBy = "parent", orphanRemoval = true) + private List children; + + public long getId() { + return id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + // So a BeanList is used and overwriting children will replace the table entries + this.children.clear(); + this.children.addAll(children); + } +} + + diff --git a/ebean-core/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java b/ebean-core/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java new file mode 100644 index 000000000..9c6fa045b --- /dev/null +++ b/ebean-core/src/test/java/org/tests/model/orphanremoval/TestOrphanRemovalOverwrite.java @@ -0,0 +1,37 @@ +package org.tests.model.orphanremoval; + +import org.junit.Test; + +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class TestOrphanRemovalOverwrite { + + @Test + public void testOverwritingMapping() { + OmBeanListParent parent = new OmBeanListParent(); + parent.save(); + + // Refreshing/querying sets the modifyListening flag on the association's BeanList + parent.refresh(); + + List childList = singletonList(new OmBeanListChild("child1")); + // Adding the children to the BeanList causes _ebean_getIdentity to be invoked before the children have been persisted and + // have Ids. + parent.setChildren(childList); + + // Give the children Ids + parent.save(); + + // Refreshing here generates new objects for the associated children that are referred to by the parent. + parent.refresh(); + + assertNotNull("The children should now have Ids as they are persisted to the db.", childList.get(0).getId()); + assertEquals("The children should have the same Id as the ones on the parent.", + childList.get(0).getId(), parent.getChildren().get(0).getId()); + assertEquals("The children should therefore equal the children on the parent.", childList, parent.getChildren()); + } +} From f32ace4e5aa63e83d66f2be2f208219299bb0c12 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 23 Jul 2021 13:54:29 +1200 Subject: [PATCH 13/87] Refactor EntityBeanIntercept replacing owner._ebean_getPropertyNames().length with flags.length --- .../src/main/java/io/ebean/bean/EntityBeanIntercept.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 4695c4112..28a088ce6 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -476,7 +476,7 @@ public final class EntityBeanIntercept implements Serializable { * Return the number of properties. */ public int getPropertyLength() { - return owner._ebean_getPropertyNames().length; + return flags.length; } /** @@ -564,7 +564,7 @@ public final class EntityBeanIntercept implements Serializable { private void setOriginalValue(int propertyIndex, Object value) { if (origValues == null) { - origValues = new Object[owner._ebean_getPropertyNames().length]; + origValues = new Object[flags.length]; } if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) { flags[propertyIndex] |= FLAG_ORIG_VALUE_SET; @@ -577,7 +577,7 @@ public final class EntityBeanIntercept implements Serializable { */ private void setOriginalValueForce(int propertyIndex, Object value) { if (origValues == null) { - origValues = new Object[owner._ebean_getPropertyNames().length]; + origValues = new Object[flags.length]; } origValues[propertyIndex] = value; } @@ -1112,7 +1112,7 @@ public final class EntityBeanIntercept implements Serializable { */ public void setLoadError(int propertyIndex, Exception t) { if (loadErrors == null) { - loadErrors = new Exception[owner._ebean_getPropertyNames().length]; + loadErrors = new Exception[flags.length]; } loadErrors[propertyIndex] = t; flags[propertyIndex] |= FLAG_LOADED_PROP; From 6c262f1df549d07298431941bc08c71b9b2da6e8 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 23 Jul 2021 16:50:24 +1200 Subject: [PATCH 14/87] JSON bean dirty detection via MD5 of JSON string content - MD5 of json content stored on EntityBeanIntercept for dirty detection - Only convert to JSON once (at dirty detection time). Store this json content on EntityBeanIntercept to later push to ScalarTypeJsonObjectMapper for bind Should consider alternative to extend BeanProperty rather than have these if blocks. --- .../io/ebean/bean/EntityBeanIntercept.java | 33 +++++++++ .../java/io/ebean/core/type/DataBinder.java | 11 +++ .../java/io/ebean/core/type/DataReader.java | 10 +++ .../java/io/ebean/core/type/ScalarType.java | 8 ++ .../server/deploy/BeanDescriptor.java | 4 +- .../server/deploy/BeanProperty.java | 47 ++++++++---- .../server/persist/dml/DmlHandler.java | 5 ++ .../dmlbind/BindablePropertyJsonInsert.java | 42 +++++++++++ .../dmlbind/BindablePropertyJsonUpdate.java | 35 +++++++++ .../persist/dmlbind/BindableRequest.java | 5 ++ .../persist/dmlbind/FactoryProperty.java | 7 ++ .../server/query/SqlBeanLoad.java | 11 ++- .../ebeaninternal/server/type/DataBind.java | 11 +++ .../server/type/RsetDataReader.java | 15 +++- .../type/ScalarTypeJsonObjectMapper.java | 52 ++++++++++++- .../org/tests/json/TestDbJson_Jackson3.java | 3 +- .../java/org/tests/json/TestDbJson_List.java | 12 ++- .../org/tests/model/json/EBasicPlain.java | 54 ++++++++++++++ .../model/json/TestJacksonPlainBean.java | 73 +++++++++++++++++++ 19 files changed, 404 insertions(+), 34 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java create mode 100644 ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java create mode 100644 ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 28a088ce6..59a2450bd 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -91,6 +91,17 @@ public final class EntityBeanIntercept implements Serializable { private Object ownerId; private int sortOrder; + /** + * Holds MD5 hash of json loaded jackson beans. + */ + private String[] mutableHash; + + /** + * Holds json content determined at point of dirty check. + * Stored here on dirty check such that we only convert to json once. + */ + private String[] mutableContent; + /** * Create a intercept with a given entity. */ @@ -1138,4 +1149,26 @@ public final class EntityBeanIntercept implements Serializable { } return ret; } + + public String mutableHash(int propertyIndex) { + return mutableHash == null ? null : mutableHash[propertyIndex]; + } + + public void mutableHash(int propertyIndex, String content) { + if (mutableHash == null) { + mutableHash = new String[flags.length]; + } + mutableHash[propertyIndex] = content; + } + + public String mutableContent(int propertyIndex) { + return mutableContent == null ? null : mutableContent[propertyIndex]; + } + + public void mutableContent(int propertyIndex, String content) { + if (mutableContent == null) { + mutableContent = new String[flags.length]; + } + mutableContent[propertyIndex] = content; + } } diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java b/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java index be6a3b559..b08bb529a 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/DataBinder.java @@ -163,4 +163,15 @@ public interface DataBinder { * Bind an array value. */ void setArray(String arrayType, Object[] elements) throws SQLException; + + /** + * Push json from dirty detection to be available for binding. + */ + void pushJson(String json); + + /** + * Pop json made during dirty detection for scalarType binding. + */ + String popJson(); + } diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java b/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java index a8ace12be..ef61b9122 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/DataReader.java @@ -48,4 +48,14 @@ public interface DataReader { Object getObject() throws SQLException; InputStream getBinaryStream() throws SQLException; + + /** + * Push json from dirty detection to be available for binding. + */ + void pushJson(String json); + + /** + * Pop json made during dirty detection for scalarType binding. + */ + String popJson(); } diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index a971a0115..831664991 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -34,6 +34,14 @@ import java.sql.SQLException; */ public interface ScalarType extends StringParser, StringFormatter, ScalarDataReader { + default boolean isJsonMapper() { + return false; + } + + default String jsonMapper(Object value) { + throw new UnsupportedOperationException(); + } + /** * Return true if this is a binary type and can not support parse() and format() from/to string. * This allows Ebean to optimise marshalling types to string. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 63599b7ad..d165b1489 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -2022,7 +2022,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { public boolean isTableManaged(String tableName) { return owner.isTableManaged(tableName); } - + /** * Return the order column property. */ @@ -3200,7 +3200,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { int propertyIndex = beanProperty.getPropertyIndex(); if (!ebi.isDirtyProperty(propertyIndex) && ebi.isLoadedProperty(propertyIndex)) { Object value = beanProperty.getValue(ebi.getOwner()); - if (value != null && beanProperty.isDirtyValue(value)) { + if (value != null && beanProperty.isDirtyValue(value, ebi)) { // mutable scalar value which is considered dirty so mark // it as such so that it is included in an update ebi.markPropertyAsChanged(propertyIndex); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 8793942b6..d3c832c85 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -13,6 +13,7 @@ import io.ebean.core.type.DocPropertyType; import io.ebean.core.type.ScalarType; import io.ebean.plugin.Property; import io.ebean.text.StringParser; +import io.ebean.text.TextException; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiQuery; @@ -31,11 +32,8 @@ import io.ebeaninternal.server.properties.BeanPropertySetter; import io.ebeaninternal.server.query.STreeProperty; import io.ebeaninternal.server.query.SqlBeanLoad; import io.ebeaninternal.server.query.SqlJoinType; -import io.ebeaninternal.server.type.DataBind; -import io.ebeaninternal.server.type.LocalEncryptedType; -import io.ebeaninternal.server.type.ScalarTypeBoolean; -import io.ebeaninternal.server.type.ScalarTypeEnum; -import io.ebeaninternal.server.type.ScalarTypeLogicalType; +import io.ebeaninternal.server.type.*; +import io.ebeaninternal.server.util.Md5; import io.ebeaninternal.util.ValueUtil; import io.ebeanservice.docstore.api.mapping.DocMappingBuilder; import io.ebeanservice.docstore.api.mapping.DocPropertyMapping; @@ -54,6 +52,7 @@ import java.sql.SQLException; import java.sql.Types; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -220,6 +219,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { */ @SuppressWarnings("rawtypes") final ScalarType scalarType; + final boolean jsonMapperType; private final DocPropertyOptions docOptions; @@ -333,6 +333,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { this.formula = sqlFormulaSelect != null; this.dbType = deploy.getDbType(); this.scalarType = deploy.getScalarType(); + this.jsonMapperType = (scalarType == null) ? false : scalarType.isJsonMapper(); this.lob = isLobType(dbType); this.propertyType = deploy.getPropertyType(); this.field = deploy.getField(); @@ -427,6 +428,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { this.setter = source.setter; this.dbType = source.getDbType(true); this.scalarType = source.scalarType; + this.jsonMapperType = source.jsonMapperType; this.lob = isLobType(dbType); this.propertyType = source.getPropertyType(); this.field = source.getField(); @@ -630,8 +632,17 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { Object value = scalarType.read(reader); if (bean != null) { setValue(bean, value); + if (jsonMapperType) { + String json = reader.popJson(); + if (json != null) { + final String hash = Md5.hash(json); + bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + } + } } return value; + } catch (TextException e) { + throw e; } catch (Exception e) { throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); } @@ -643,13 +654,11 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { try { - Object value = scalarType.read(ctx.getDataReader()); - if (bean != null) { - setValue(bean, value); - } - return value; - } catch (Exception e) { - throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); + return readSet(ctx.getDataReader(), bean); + } catch (TextException e) { + bean._ebean_getIntercept().setLoadError(propertyIndex, e); + ctx.handleLoadError(getFullBeanName(), e); + return getValue(bean); } } @@ -1018,7 +1027,19 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { * Return true if the mutable value is considered dirty. * This is only used for 'mutable' scalar types like hstore etc. */ - boolean isDirtyValue(Object value) { + boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { + if (jsonMapperType) { + // dirty detection based on md5 hash of json content + final String json = scalarType.jsonMapper(value); + final String newHash = Md5.hash(json); + final String oldHash = ebi.mutableHash(propertyIndex); + if (!Objects.equals(newHash, oldHash)) { + ebi.mutableContent(propertyIndex, json); // so we only convert to json once + ebi.mutableHash(propertyIndex, newHash); // for dirty detection next time + return true; + } + return false; + } return scalarType.isDirty(value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java index 189613808..0a4bb6b8a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java @@ -63,6 +63,11 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest { } } + @Override + public void pushJson(String json) { + dataBind.pushJson(json); + } + @Override public long now() { return now; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java new file mode 100644 index 000000000..0a93452f1 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java @@ -0,0 +1,42 @@ +package io.ebeaninternal.server.persist.dmlbind; + +import io.ebean.bean.EntityBean; +import io.ebeaninternal.server.deploy.BeanProperty; +import io.ebeaninternal.server.util.Md5; + +import java.sql.SQLException; + +/** + * For JSON Jackson properties - dirty detection via MD5 of json content. + */ +class BindablePropertyJsonInsert extends BindableProperty { + + private final int propertyIndex; + + BindablePropertyJsonInsert(BeanProperty prop) { + super(prop); + this.propertyIndex = prop.getPropertyIndex(); + } + + /** + * Normal binding of a property value from the bean. + */ + @Override + public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException { + if (bean == null) { + request.bind(null, prop); + } else { + Object value = prop.getValue(bean); + if (value == null) { + request.bind(null, prop); + } else { + // on insert store MD5 hash and push json + final String json = prop.format(value); + final String hash = Md5.hash(json); + bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + request.pushJson(json); + request.bind(value, prop); + } + } + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java new file mode 100644 index 000000000..3418369f5 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java @@ -0,0 +1,35 @@ +package io.ebeaninternal.server.persist.dmlbind; + +import io.ebean.bean.EntityBean; +import io.ebeaninternal.server.deploy.BeanProperty; + +import java.sql.SQLException; + +/** + * For JSON Jackson properties - dirty detection via MD5 of json content. + */ +class BindablePropertyJsonUpdate extends BindableProperty { + + private final int propertyIndex; + + BindablePropertyJsonUpdate(BeanProperty prop) { + super(prop); + this.propertyIndex = prop.getPropertyIndex(); + } + + /** + * Normal binding of a property value from the bean. + */ + @Override + public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException { + if (bean == null) { + request.bind(null, prop); + } else { + // on update push json + final String json = bean._ebean_getIntercept().mutableContent(propertyIndex); + request.pushJson(json); + final Object value = prop.getValue(bean); + request.bind(value, prop); + } + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java index 4a1fc6754..e06862e86 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableRequest.java @@ -57,4 +57,9 @@ public interface BindableRequest { * Return true if this is an update request. */ boolean isUpdate(); + + /** + * Push json content for scalarType bind(). + */ + void pushJson(String json); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java index b47b4ecb6..78ceb098b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java @@ -43,6 +43,13 @@ class FactoryProperty { return new BindableAssocOne((BeanPropertyAssocOne)prop); } + if (prop.getScalarType().isJsonMapper()) { + if (DmlMode.INSERT == mode) { + return new BindablePropertyJsonInsert(prop); + } else if (DmlMode.UPDATE == mode) { + return new BindablePropertyJsonUpdate(prop); + } + } return new BindableProperty(prop); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java index ac1386e11..20d1dfddb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java @@ -25,7 +25,6 @@ public class SqlBeanLoad { private final boolean rawSql; SqlBeanLoad(DbReadContext ctx, Class type, EntityBean bean, Mode queryMode) { - this.ctx = ctx; this.rawSql = ctx.isRawSql(); this.type = type; @@ -69,16 +68,16 @@ public class SqlBeanLoad { } try { - Object dbVal = prop.read(ctx); if (!refreshLoading) { - prop.setValue(bean, dbVal); - } else { - prop.setValueIntercept(bean, dbVal); + return prop.readSet(ctx, bean); } - + // TODO: maybe create prop.readSetIntercept() and move this + Object dbVal = prop.read(ctx); + prop.setValueIntercept(bean, dbVal); return dbVal; } catch (Exception e) { + // TODO: maybe move this into prop.readSetIntercept() bean._ebean_getIntercept().setLoadError(prop.getPropertyIndex(), e); ctx.handleLoadError(prop.getFullBeanName(), e); return prop.getValue(bean); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java index 2959c872c..489046405 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java @@ -36,6 +36,7 @@ public class DataBind implements DataBinder { private List inputStreams; protected int pos; + private String json; public DataBind(DataTimeZone dataTimeZone, PreparedStatement pstmt, Connection connection) { this.dataTimeZone = dataTimeZone; @@ -43,6 +44,16 @@ public class DataBind implements DataBinder { this.connection = connection; } + @Override + public void pushJson(String json) { + this.json = json; + } + + @Override + public String popJson() { + return json; + } + @Override public StringBuilder append(Object entry) { return bindLog.append(entry); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java index 9763a5667..b85c8e688 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/RsetDataReader.java @@ -20,22 +20,29 @@ import java.util.Calendar; public class RsetDataReader implements DataReader { private static final int bufferSize = 512; - static final int clobBufferSize = 512; - static final int stringInitialSize = 512; private final DataTimeZone dataTimeZone; - private final ResultSet rset; - protected int pos; + private String json; public RsetDataReader(DataTimeZone dataTimeZone, ResultSet rset) { this.dataTimeZone = dataTimeZone; this.rset = rset; } + @Override + public void pushJson(String json) { + this.json = json; + } + + @Override + public String popJson() { + return json; + } + @Override public void close() throws SQLException { rset.close(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 5e24412b5..d71cf18d6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -56,6 +56,50 @@ class ScalarTypeJsonObjectMapper { GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class rawType) { super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType); } + + @Override + public boolean isJsonMapper() { + return true; + } + + @Override + public String jsonMapper(Object value) { + return formatValue(value); + } + + @Override + public Object read(DataReader reader) throws SQLException { + String json = reader.getString(); + if (json == null || json.isEmpty()) { + return null; + } + // pushJson such that we MD5 and store on EntityBeanIntercept later + reader.pushJson(json); + try { + return objectReader.readValue(json, deserType); + } catch (IOException e) { + throw new TextException("Failed to parse JSON [{}] as " + deserType, json, e); + } + } + + @Override + public void bind(DataBinder binder, Object value) throws SQLException { + // popJson as dirty detection already converted to json string + String rawJson = binder.popJson(); + if (rawJson == null && value != null) { + rawJson = formatValue(value); // not expected, need to check? + } + if (pgType != null) { + binder.setObject(PostgresHelper.asObject(pgType, rawJson)); + } else { + if (value == null) { + // use varchar, otherwise SqlServer/db2 will fail with 'Invalid JDBC data type 5.001.' + binder.setNull(Types.VARCHAR); + } else { + binder.setString(rawJson); + } + } + } } /** @@ -118,10 +162,10 @@ class ScalarTypeJsonObjectMapper { */ private static abstract class Base extends ScalarTypeBase { - private final ObjectWriter objectWriter; - private final ObjectMapper objectReader; - private final JavaType deserType; - private final String pgType; + protected final ObjectWriter objectWriter; + protected final ObjectMapper objectReader; + protected final JavaType deserType; + protected final String pgType; private final DocPropertyType docType; private final TypeJsonManager.DirtyHandler dirtyHandler; diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index 106beb9f2..4bc0194d6 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -74,6 +74,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { final List sql = LoggedSql.stop(); assertThat(sql).hasSize(1); - assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_list=?, plain_bean=?, version=? where id=?"); + // plain_bean=?, no longer included with MD5 dirty detection + assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_list=?, version=? where id=?"); } } diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java index 28fd78221..f43d6f23c 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java @@ -113,7 +113,8 @@ public class TestDbJson_List extends BaseTestCase { List sql = LoggedSqlCollector.stop(); // we don't update the phone numbers (as they are not dirty) - assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, plain_bean=?, version=? where"); + // plain_bean=?, no longer included with MD5 dirty detection + assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, version=? where"); } public void update_when_dirty() { @@ -126,7 +127,8 @@ public class TestDbJson_List extends BaseTestCase { List sql = LoggedSqlCollector.stop(); // we don't update the phone numbers (as they are not dirty) - assertSql(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, tags=?, version=? where id=? and version=?"); + // plain_bean=? not included using MD5 dirty detection + assertSql(sql.get(0)).contains("update ebasic_json_list set tags=?, version=? where id=? and version=?"); } public void update_when_dirty_flags() { @@ -139,7 +141,8 @@ public class TestDbJson_List extends BaseTestCase { List sql = LoggedSqlCollector.stop(); // we don't update the phone numbers (as they are not dirty) - assertSql(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, flags=?, version=? where id=? and version=?;"); + // plain_bean=? not included with MD5 dirty detection + assertSql(sql.get(0)).contains("update ebasic_json_list set flags=?, version=? where id=? and version=?;"); } public void update_when_dirty_SetListMap() { @@ -154,7 +157,8 @@ public class TestDbJson_List extends BaseTestCase { List sql = LoggedSqlCollector.stop(); // we don't update the phone numbers (as they are not dirty) - assertSql(sql.get(0)).contains("update ebasic_json_list set beans=?, bean_list=?, bean_map=?, plain_bean=?, version=? where id=? and version=?"); + // plain_bean=? not included with MD5 dirty detection + assertSql(sql.get(0)).contains("update ebasic_json_list set beans=?, bean_list=?, bean_map=?, version=? where id=? and version=?"); } @Test diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java b/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java new file mode 100644 index 000000000..f3705aa16 --- /dev/null +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java @@ -0,0 +1,54 @@ +package org.tests.model.json; + +import io.ebean.annotation.DbJson; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; + +@Entity +public class EBasicPlain { + + @Id + long id; + + String attr; + + @DbJson(length = 500) + PlainBean plainBean; + + @Version + long version; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public PlainBean getPlainBean() { + return plainBean; + } + + public void setPlainBean(PlainBean plainBean) { + this.plainBean = plainBean; + } + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } +} diff --git a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java new file mode 100644 index 000000000..0e3d22aa7 --- /dev/null +++ b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java @@ -0,0 +1,73 @@ +package org.tests.model.json; + +import io.ebean.DB; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestJacksonPlainBean { + + @Test + public void insertUpdate() { + + DB.getDefault(); + LoggedSqlCollector.start(); + + PlainBean content = new PlainBean(); + content.setAlong(42); + content.setName("foo"); + + EBasicPlain bean = new EBasicPlain(); + bean.setAttr("attr0"); + bean.setPlainBean(content); + + + DB.save(bean); + expectedSql(0, "insert into ebasic_plain (attr, plain_bean, version) values (?,?,?)"); + + + // inserted plainBean has not been mutated + bean.setAttr("attr1"); + DB.save(bean); + expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?"); + + + // inserted plainBean has now been mutated + content.setName("notFoo"); + bean.setAttr("attr2"); + DB.save(bean); + expectedSql(0, "update ebasic_plain set attr=?, plain_bean=?, version=? where id=? and version=?"); + + + final EBasicPlain found = DB.find(EBasicPlain.class, bean.getId()); + + // update mutating PlainBean only + final PlainBean plainBean = found.getPlainBean(); + plainBean.setName("mod1"); + DB.save(found); + expectedSql(1, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?"); + + + // update bean, mutate PlainBean only + plainBean.setName("mod2"); + DB.save(found); + expectedSql(0, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?"); + + + // update bean, not mutating PlainBean + found.setAttr("attr3"); + DB.save(found); + expectedSql(LoggedSqlCollector.stop(), 0, "update ebasic_plain set attr=?, version=? where id=? and version=?"); + } + + private void expectedSql(int i, String s) { + assertThat(LoggedSqlCollector.current().get(i)).contains(s); + } + + private void expectedSql(List sql, int i, String s) { + assertThat(sql.get(i)).contains(s); + } +} From 4aef0ad459cf0ccfa4c01969d3d3bf96320fcfd2 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 28 Jul 2021 17:23:37 +1200 Subject: [PATCH 15/87] #2275 - Change Query to Query on ExtendedServer exists() method --- .../main/java/io/ebean/ExtendedServer.java | 2 +- .../server/core/DefaultServer.java | 8 ++--- .../ebeaninternal/api/TDSpiEbeanServer.java | 2 +- .../query/cancel/SqlQueryCancelTest.java | 34 +++++++++---------- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/ExtendedServer.java b/ebean-api/src/main/java/io/ebean/ExtendedServer.java index 9a7e62e0c..59b1c678e 100644 --- a/ebean-api/src/main/java/io/ebean/ExtendedServer.java +++ b/ebean-api/src/main/java/io/ebean/ExtendedServer.java @@ -67,7 +67,7 @@ public interface ExtendedServer { * * @return True if the query finds a matching row in the database */ - boolean exists(Query ormQuery, Transaction transaction); + boolean exists(Query ormQuery, Transaction transaction); /** * Return the number of 'top level' or 'root' entities this query should return. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 87580f90b..4aa90ad13 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -1283,14 +1283,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } @Override - public boolean exists(Query ormQuery, Transaction transaction) { - Query ormQueryCopy = ormQuery.copy(); - ormQueryCopy.setMaxRows(1); + public boolean exists(Query ormQuery, Transaction transaction) { + Query ormQueryCopy = ormQuery.copy().setMaxRows(1); SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction); try { request.initTransIfRequired(); - List ids = request.findIds(); - return !ids.isEmpty(); + return !request.findIds().isEmpty(); } finally { request.endTransIfRequired(); } diff --git a/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java b/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java index f0f0c0779..b09a0f54e 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java +++ b/ebean-core/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java @@ -650,7 +650,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public boolean exists(Query ormQuery, Transaction transaction) { + public boolean exists(Query ormQuery, Transaction transaction) { return false; } diff --git a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java index d3dda7443..9fc3d6b57 100644 --- a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java +++ b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java @@ -30,18 +30,18 @@ import io.ebean.annotation.Platform; * Tests, if all kind of queries are cancelable. There are two ways how to * cancel a query:
* At begin: - * + * *
  * query = DB.find(...)
  * query.cancel();
  * query.findList();
  * 
- * + * * The query was caneled before executing. In this case we do hit the DB driver *
*
* During run: - * + * *
  * // Thread 1:              Thread 2
  * query = DB.find(...)
@@ -50,26 +50,26 @@ import io.ebean.annotation.Platform;
  *     ...finding            query.cancel();
  *      ...JDBC-Exception
  * 
- * + * * The test tries to simulate a slow query by installing the * {@link SlowDownEBasic} 'SELECT' trigger. The trigger can be configured to * wait 3 * timing ms and a second thread will cancel the query in * timing ms. - * + * * in this case, we expect a JDBC exception from the driver.
*
* NOTE:
* H2 checks the cancel flag in org.h2.command.Prepared::setCurrentRowNumber * only every 128th row. So we need at least 128 models and we cannot check * queries like findCount or findOne, because they only return one row. - * + * * @author Roland Praml, FOCONIS AG * */ public class SqlQueryCancelTest extends BaseTestCase { - private int timing = 10; - + private final int timing = 20; + @BeforeClass public static void setupTestData() throws SQLException { for (int i = 0; i < 128; i++) { @@ -98,10 +98,10 @@ public class SqlQueryCancelTest extends BaseTestCase { doCancelSqlDuringRun(q -> q.findEachWhile(e -> true)); } - + @Test public void cancelOrmQueryAtBegin() throws SQLException { - doCancelOrmAtBegin(Query::findCount); + doCancelOrmAtBegin(Query::findCount); doCancelOrmAtBegin(Query::findFutureCount); // We cannot test 'findCount' due H2 restrictions doCancelOrmAtBegin(Query::findFutureIds); @@ -206,7 +206,7 @@ public class SqlQueryCancelTest extends BaseTestCase { .isInstanceOf(PersistenceException.class) .hasMessageContaining("Query was cancelled"); } - + @Test public void cancelSqlDtoQueryAtBegin() throws SQLException { @@ -290,7 +290,7 @@ public class SqlQueryCancelTest extends BaseTestCase { private void doCancelOrmFutureDuringRun(Function, Future> test) throws SQLException, InterruptedException, ExecutionException { Query warmup = DB.find(EBasic.class); test.apply(warmup).get(); - + Query query = DB.find(EBasic.class); executeDelayed(query::cancel); assertThatThrownBy(() -> { @@ -311,18 +311,18 @@ public class SqlQueryCancelTest extends BaseTestCase { .isInstanceOf(PersistenceException.class) .hasMessageContaining("Query was cancelled"); } - + private void doCancelOrmDtoDuringRun(Consumer> test) throws SQLException { DtoQuery warmup = DB.find(EBasic.class).select("id,status").asDto(EBasicDto.class); test.accept(warmup); - + DtoQuery query = DB.find(EBasic.class).select("id,status").asDto(EBasicDto.class); executeDelayed(query::cancel); assertThatThrownBy(() -> test.accept(query)) .isInstanceOf(PersistenceException.class) .hasCauseInstanceOf(org.h2.jdbc.JdbcSQLTimeoutException.class); } - + private void doCancelSqlDtoAtBegin(Consumer> test) throws SQLException { DtoQuery query = DB.findDto(EBasicDto.class, "select id, status from e_basic"); query.cancel(); @@ -334,14 +334,14 @@ public class SqlQueryCancelTest extends BaseTestCase { private void doCancelSqlDtoDuringRun(Consumer> test) throws SQLException { DtoQuery warmup = DB.findDto(EBasicDto.class, "select id, status from e_basic"); test.accept(warmup); - + DtoQuery query = DB.findDto(EBasicDto.class, "select id, status from e_basic"); executeDelayed(query::cancel); assertThatThrownBy(() -> test.accept(query)) .isInstanceOf(PersistenceException.class) .hasCauseInstanceOf(org.h2.jdbc.JdbcSQLTimeoutException.class); } - + private void executeDelayed(Runnable r) throws SQLException { // We modify the DB here. Otherwise we may hit an internal H2 cache, if the // same query is performed. Queries from the cache cannot be canceled. From 11eed4cfe9cb9118624bd0200691acf6b71d587e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 28 Jul 2021 21:43:57 +1200 Subject: [PATCH 16/87] Add BeanPropertyJsonMapper for JSON dirty detection --- .../server/deploy/BeanProperty.java | 22 ------- .../server/deploy/BeanPropertyJsonMapper.java | 57 +++++++++++++++++++ .../deploy/meta/DeployBeanProperty.java | 3 + .../deploy/meta/DeployBeanPropertyLists.java | 13 ++--- 4 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index d3c832c85..d9af317d7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -219,7 +219,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { */ @SuppressWarnings("rawtypes") final ScalarType scalarType; - final boolean jsonMapperType; private final DocPropertyOptions docOptions; @@ -333,7 +332,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { this.formula = sqlFormulaSelect != null; this.dbType = deploy.getDbType(); this.scalarType = deploy.getScalarType(); - this.jsonMapperType = (scalarType == null) ? false : scalarType.isJsonMapper(); this.lob = isLobType(dbType); this.propertyType = deploy.getPropertyType(); this.field = deploy.getField(); @@ -428,7 +426,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { this.setter = source.setter; this.dbType = source.getDbType(true); this.scalarType = source.scalarType; - this.jsonMapperType = source.jsonMapperType; this.lob = isLobType(dbType); this.propertyType = source.getPropertyType(); this.field = source.getField(); @@ -632,13 +629,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { Object value = scalarType.read(reader); if (bean != null) { setValue(bean, value); - if (jsonMapperType) { - String json = reader.popJson(); - if (json != null) { - final String hash = Md5.hash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); - } - } } return value; } catch (TextException e) { @@ -1028,18 +1018,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { * This is only used for 'mutable' scalar types like hstore etc. */ boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { - if (jsonMapperType) { - // dirty detection based on md5 hash of json content - final String json = scalarType.jsonMapper(value); - final String newHash = Md5.hash(json); - final String oldHash = ebi.mutableHash(propertyIndex); - if (!Objects.equals(newHash, oldHash)) { - ebi.mutableContent(propertyIndex, json); // so we only convert to json once - ebi.mutableHash(propertyIndex, newHash); // for dirty detection next time - return true; - } - return false; - } return scalarType.isDirty(value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java new file mode 100644 index 000000000..2d24958b6 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -0,0 +1,57 @@ +package io.ebeaninternal.server.deploy; + +import io.ebean.bean.EntityBean; +import io.ebean.bean.EntityBeanIntercept; +import io.ebean.core.type.DataReader; +import io.ebean.text.TextException; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import io.ebeaninternal.server.util.Md5; + +import javax.persistence.PersistenceException; +import java.sql.SQLException; +import java.util.Objects; + +public class BeanPropertyJsonMapper extends BeanProperty { + + public BeanPropertyJsonMapper(BeanDescriptor desc, DeployBeanProperty deployProp) { + super(desc, deployProp); + } + + /** + * Return true if the mutable value is considered dirty. + * This is only used for 'mutable' scalar types like hstore etc. + */ + @Override + boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { + // dirty detection based on md5 hash of json content + final String json = scalarType.jsonMapper(value); + final String newHash = Md5.hash(json); + final String oldHash = ebi.mutableHash(propertyIndex); + if (!Objects.equals(newHash, oldHash)) { + ebi.mutableContent(propertyIndex, json); // so we only convert to json once + ebi.mutableHash(propertyIndex, newHash); // for dirty detection next time + return true; + } + return false; + } + + @Override + public Object readSet(DataReader reader, EntityBean bean) throws SQLException { + try { + Object value = scalarType.read(reader); + if (bean != null) { + setValue(bean, value); + String json = reader.popJson(); + if (json != null) { + final String hash = Md5.hash(json); + bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + } + } + return value; + } catch (TextException e) { + throw e; + } catch (Exception e) { + throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); + } + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index e20c898ce..92ad7f1e9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1201,4 +1201,7 @@ public class DeployBeanProperty { return false; } + boolean isJsonMapper() { + return scalarType != null && scalarType.isJsonMapper(); + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java index f6280b260..9843f8e5c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java @@ -1,15 +1,7 @@ package io.ebeaninternal.server.deploy.meta; import io.ebean.bean.EntityBean; -import io.ebeaninternal.server.deploy.BeanDescriptor; -import io.ebeaninternal.server.deploy.BeanDescriptorMap; -import io.ebeaninternal.server.deploy.BeanProperty; -import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import io.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import io.ebeaninternal.server.deploy.BeanPropertyIdClass; -import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn; -import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection; -import io.ebeaninternal.server.deploy.InheritInfo; +import io.ebeaninternal.server.deploy.*; import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; import io.ebeaninternal.server.properties.BeanPropertySetter; import io.ebeaninternal.server.type.ScalarTypeString; @@ -490,6 +482,9 @@ public class DeployBeanPropertyLists { return new BeanPropertyAssocMany(desc, (DeployBeanPropertyAssocMany) deployProp); } + if (deployProp.isJsonMapper()) { + return new BeanPropertyJsonMapper(desc, deployProp); + } return new BeanProperty(desc, deployProp); } From 49b86d07d3bb5bdd13b97a180ab92038011655fc Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 28 Jul 2021 22:48:29 +1200 Subject: [PATCH 17/87] Modify SqlBeanLoad to use readSet() Note that we don't need to use readSetIntercept() now as there is no property change listener support --- .../server/deploy/BeanProperty.java | 18 +++++--------- .../server/query/SqlBeanLoad.java | 24 ++----------------- .../java/io/ebean/EbeanServer_refresh.java | 5 +++- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index d9af317d7..4306ed97d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -33,7 +33,6 @@ import io.ebeaninternal.server.query.STreeProperty; import io.ebeaninternal.server.query.SqlBeanLoad; import io.ebeaninternal.server.query.SqlJoinType; import io.ebeaninternal.server.type.*; -import io.ebeaninternal.server.util.Md5; import io.ebeaninternal.util.ValueUtil; import io.ebeanservice.docstore.api.mapping.DocMappingBuilder; import io.ebeanservice.docstore.api.mapping.DocPropertyMapping; @@ -52,7 +51,6 @@ import java.sql.SQLException; import java.sql.Types; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; /** @@ -643,13 +641,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { } public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { - try { - return readSet(ctx.getDataReader(), bean); - } catch (TextException e) { - bean._ebean_getIntercept().setLoadError(propertyIndex, e); - ctx.handleLoadError(getFullBeanName(), e); - return getValue(bean); - } + return readSet(ctx.getDataReader(), bean); } @SuppressWarnings("unchecked") @@ -908,7 +900,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { /** * Return the name of the property. */ - @Override @Nonnull + @Override + @Nonnull public String getName() { return name; } @@ -1280,7 +1273,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { @Override public Object localEncrypt(Object value) { - return ((LocalEncryptedType)scalarType).localEncrypt(value); + return ((LocalEncryptedType) scalarType).localEncrypt(value); } /** @@ -1393,7 +1386,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { /** * Return the property type. */ - @Override @Nonnull + @Override + @Nonnull public Class getPropertyType() { return propertyType; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java index 20d1dfddb..83c7b2936 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java @@ -18,10 +18,8 @@ public class SqlBeanLoad { private final DbReadContext ctx; private final EntityBean bean; private final EntityBeanIntercept ebi; - private final Class type; private final boolean lazyLoading; - private final boolean refreshLoading; private final boolean rawSql; SqlBeanLoad(DbReadContext ctx, Class type, EntityBean bean, Mode queryMode) { @@ -29,7 +27,6 @@ public class SqlBeanLoad { this.rawSql = ctx.isRawSql(); this.type = type; this.lazyLoading = queryMode == Mode.LAZYLOAD_BEAN; - this.refreshLoading = queryMode == Mode.REFRESH_BEAN; this.bean = bean; this.ebi = bean == null ? null : bean._ebean_getIntercept(); } @@ -49,35 +46,22 @@ public class SqlBeanLoad { } public Object load(BeanProperty prop) { - if (!rawSql && !prop.isLoadProperty(ctx.isDraftQuery())) { return null; } - if ((bean == null) || (lazyLoading && ebi.isLoadedProperty(prop.getPropertyIndex())) || (type != null && !prop.isAssignableFrom(type))) { - // ignore this property // ... null: bean already in persistence context // ... lazyLoading: partial bean that is lazy loading // ... type: inheritance and not assignable to this instance - prop.loadIgnore(ctx); return null; } - try { - if (!refreshLoading) { - return prop.readSet(ctx, bean); - } - // TODO: maybe create prop.readSetIntercept() and move this - Object dbVal = prop.read(ctx); - prop.setValueIntercept(bean, dbVal); - return dbVal; - + return prop.readSet(ctx, bean); } catch (Exception e) { - // TODO: maybe move this into prop.readSetIntercept() bean._ebean_getIntercept().setLoadError(prop.getPropertyIndex(), e); ctx.handleLoadError(prop.getFullBeanName(), e); return prop.getValue(bean); @@ -88,10 +72,6 @@ public class SqlBeanLoad { * Load the given value into the property. */ public void load(BeanProperty target, Object dbVal) { - if (!refreshLoading) { - target.setValue(bean, dbVal); - } else { - target.setValueIntercept(bean, dbVal); - } + target.setValue(bean, dbVal); } } diff --git a/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java b/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java index 1e1900a13..66af5d164 100644 --- a/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java +++ b/ebean-core/src/test/java/io/ebean/EbeanServer_refresh.java @@ -11,7 +11,7 @@ import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; public class EbeanServer_refresh { @@ -39,8 +39,11 @@ public class EbeanServer_refresh { assertEquals(rows, 1); + basic.setName("modify"); + assertTrue(DB.getBeanState(basic).isDirty()); server.refresh(basic); assertEquals(basic.getStatus(), EBasic.Status.ACTIVE); + assertFalse(DB.getBeanState(basic).isDirty()); } @Test From 5e595ec5f875690d7c6e198871fc8f726f234ef7 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 28 Jul 2021 14:54:56 +0200 Subject: [PATCH 18/87] Proof of concept to support also bean change detection / changelog on mutable properties --- .../io/ebean/bean/EntityBeanIntercept.java | 29 ++++++-- .../main/java/io/ebean/bean/MutableJson.java | 12 ++++ .../java/io/ebean/core/type/ScalarType.java | 5 ++ .../server/deploy/BeanPropertyJsonMapper.java | 13 ++-- .../dmlbind/BindablePropertyJsonInsert.java | 3 +- .../type/ScalarTypeJsonObjectMapper.java | 64 +++++++++++++++++ .../org/tests/json/TestDbJson_Jackson3.java | 70 ++++++++++++++++++- 7 files changed, 180 insertions(+), 16 deletions(-) create mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableJson.java diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 59a2450bd..2ea4afbb5 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -94,7 +94,7 @@ public final class EntityBeanIntercept implements Serializable { /** * Holds MD5 hash of json loaded jackson beans. */ - private String[] mutableHash; + private MutableJson[] mutableHash; /** * Holds json content determined at point of dirty check. @@ -381,7 +381,11 @@ public final class EntityBeanIntercept implements Serializable { this.origValues = null; for (int i = 0; i < flags.length; i++) { flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET); + if (mutableHash != null && mutableHash[i] != null) { + mutableHash[i].update(owner._ebean_getField(i)); + } } + this.dirty = false; } @@ -649,7 +653,7 @@ public final class EntityBeanIntercept implements Serializable { public void addDirtyPropertyNames(Set props, String prefix) { int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if ((flags[i] & FLAG_CHANGED_PROP) != 0) { + if ((flags[i] & FLAG_CHANGED_PROP) != 0 || isChangedByHash(i)) { // the property has been changed on this bean props.add((prefix == null ? getProperty(i) : prefix + getProperty(i))); } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { @@ -667,7 +671,7 @@ public final class EntityBeanIntercept implements Serializable { String[] names = owner._ebean_getPropertyNames(); int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if ((flags[i] & FLAG_CHANGED_PROP) != 0) { + if ((flags[i] & FLAG_CHANGED_PROP) != 0 || isChangedByHash(i)) { if (propertyNames.contains(names[i])) { return true; } @@ -695,7 +699,12 @@ public final class EntityBeanIntercept implements Serializable { public void addDirtyPropertyValues(Map dirtyValues, String prefix) { int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if ((flags[i] & FLAG_CHANGED_PROP) != 0) { + if (isChangedByHash(i)) { + String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); + Object newVal = owner._ebean_getField(i); + Object oldVal = mutableHash[i].get(); + dirtyValues.put(propName, new ValuePair(newVal, oldVal)); + } else if ((flags[i] & FLAG_CHANGED_PROP) != 0) { // the property has been changed on this bean String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); Object newVal = owner._ebean_getField(i); @@ -1150,13 +1159,19 @@ public final class EntityBeanIntercept implements Serializable { return ret; } - public String mutableHash(int propertyIndex) { + private boolean isChangedByHash(int propertyIndex) { + return mutableHash != null + && mutableHash[propertyIndex] != null + && !mutableHash[propertyIndex].isEqualToObject(owner._ebean_getField(propertyIndex)); + } + + public MutableJson mutableHash(int propertyIndex) { return mutableHash == null ? null : mutableHash[propertyIndex]; } - public void mutableHash(int propertyIndex, String content) { + public void mutableHash(int propertyIndex, MutableJson content) { if (mutableHash == null) { - mutableHash = new String[flags.length]; + mutableHash = new MutableJson[flags.length]; } mutableHash[propertyIndex] = content; } diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableJson.java b/ebean-api/src/main/java/io/ebean/bean/MutableJson.java new file mode 100644 index 000000000..5989465e2 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/bean/MutableJson.java @@ -0,0 +1,12 @@ +package io.ebean.bean; + +public interface MutableJson { + + boolean isEqualToObject(Object obj); + + boolean isEqualToJson(String json); + + Object get(); + + void update(Object obj); +} diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index 831664991..b9f7f168c 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -2,6 +2,8 @@ package io.ebean.core.type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; + +import io.ebean.bean.MutableJson; import io.ebean.text.StringFormatter; import io.ebean.text.StringParser; @@ -42,6 +44,9 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData throw new UnsupportedOperationException(); } + default MutableJson jsonMutable(String json) { + throw new UnsupportedOperationException(); + } /** * Return true if this is a binary type and can not support parse() and format() from/to string. * This allows Ebean to optimise marshalling types to string. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index 2d24958b6..de5d148b5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; +import io.ebean.bean.MutableJson; import io.ebean.core.type.DataReader; import io.ebean.text.TextException; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; @@ -25,11 +26,11 @@ public class BeanPropertyJsonMapper extends BeanProperty { boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { // dirty detection based on md5 hash of json content final String json = scalarType.jsonMapper(value); - final String newHash = Md5.hash(json); - final String oldHash = ebi.mutableHash(propertyIndex); - if (!Objects.equals(newHash, oldHash)) { + final MutableJson oldHash = ebi.mutableHash(propertyIndex); + if (oldHash == null || !oldHash.isEqualToJson(json)) { ebi.mutableContent(propertyIndex, json); // so we only convert to json once - ebi.mutableHash(propertyIndex, newHash); // for dirty detection next time + //ebi.mutableHash(propertyIndex, scalarType.jsonMutable(json)); // for dirty detection next time + //must be done AFTER persistControllers are called. return true; } return false; @@ -43,8 +44,8 @@ public class BeanPropertyJsonMapper extends BeanProperty { setValue(bean, value); String json = reader.popJson(); if (json != null) { - final String hash = Md5.hash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + final String hash = scalarType.format(value); + bean._ebean_getIntercept().mutableHash(propertyIndex, scalarType.jsonMutable(hash)); } } return value; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java index 0a93452f1..24cf050e3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java @@ -32,8 +32,7 @@ class BindablePropertyJsonInsert extends BindableProperty { } else { // on insert store MD5 hash and push json final String json = prop.format(value); - final String hash = Md5.hash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + bean._ebean_getIntercept().mutableHash(propertyIndex, prop.getScalarType().jsonMutable(json)); request.pushJson(json); request.bind(value, prop); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index d71cf18d6..5b44b63e4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.introspect.AnnotatedField; + +import io.ebean.bean.MutableJson; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -15,6 +17,7 @@ import io.ebean.text.TextException; import io.ebeaninternal.json.ModifyAwareList; import io.ebeaninternal.json.ModifyAwareMap; import io.ebeaninternal.json.ModifyAwareSet; +import io.ebeaninternal.server.util.Md5; import javax.persistence.PersistenceException; import java.io.DataInput; @@ -24,6 +27,7 @@ import java.sql.SQLException; import java.sql.Types; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -66,6 +70,66 @@ class ScalarTypeJsonObjectMapper { public String jsonMapper(Object value) { return formatValue(value); } + + private class Md5MutableJson implements MutableJson { + + private String md5; + Md5MutableJson(String json) { + md5 = Md5.hash(json); + } + @Override + public boolean isEqualToObject(Object obj) { + return true; // we cannot determine differences... + } + + @Override + public boolean isEqualToJson(String json) { + return Md5.hash(json).equals(md5); + } + + @Override + public Object get() { + return null; // cannot create object from json + } + @Override + public void update(Object obj) { + md5 = Md5.hash(format(obj)); + } + } + + private class PlainMutableJson implements MutableJson { + + private String originalJson; + PlainMutableJson(String json) { + originalJson = json; + } + @Override + public boolean isEqualToObject(Object obj) { + return isEqualToJson(format(obj)); + } + + @Override + public boolean isEqualToJson(String json) { + return Objects.equals(originalJson, json); + } + + @Override + public Object get() { + return parse(originalJson); + } + @Override + public void update(Object obj) { + originalJson = format(obj); + } + } + @Override + public MutableJson jsonMutable(String originalJson) { + if (false) { + return new Md5MutableJson(originalJson); + } else { + return new PlainMutableJson(originalJson); + } + } @Override public Object read(DataReader reader) throws SQLException { diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index 4bc0194d6..4d31f8a56 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -1,7 +1,11 @@ package org.tests.json; import io.ebean.BaseTestCase; +import io.ebean.BeanState; import io.ebean.DB; +import io.ebean.ValuePair; +import io.ebean.event.BeanPersistAdapter; +import io.ebean.event.BeanPersistRequest; import io.ebeantest.LoggedSql; import org.junit.Test; import org.tests.model.json.EBasicJsonJackson3; @@ -11,11 +15,33 @@ import org.tests.model.json.PlainBeanDirtyAware; import java.util.Arrays; import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; public class TestDbJson_Jackson3 extends BaseTestCase { + public static class EBasicJsonListPersistController extends BeanPersistAdapter { + + private static Map updatedValues; + + @Override + public boolean isRegisterFor(Class cls) { + return EBasicJsonList.class.isAssignableFrom(cls); + } + + @Override + public boolean preInsert(BeanPersistRequest request) { + updatedValues = request.getUpdatedValues(); + return true; + } + + @Override + public boolean preUpdate(BeanPersistRequest request) { + updatedValues = request.getUpdatedValues(); + return true; + } + } @Test public void updateIncludesJsonColumn_when_explicit_isMarkedDirty() { @@ -69,12 +95,54 @@ public class TestDbJson_Jackson3 extends BaseTestCase { found.setName("p1-mod"); found.setBeanList(null); + BeanState state = DB.getBeanState(found); + assertThat(state.getChangedProps()).containsExactlyInAnyOrder("name", "beanList"); + + ValuePair pair = state.getDirtyValues().get("name"); + assertThat(pair.getNewValue()).isEqualTo("p1-mod"); + assertThat(pair.getOldValue()).isEqualTo("p1"); + + pair = state.getDirtyValues().get("beanList"); + assertThat(pair.getNewValue()).isEqualTo(null); + assertThat((List)pair.getOldValue()).hasSize(1) + .extracting(PlainBean::getName).containsExactly("a"); + + LoggedSql.start(); DB.save(found); - final List sql = LoggedSql.stop(); + List sql = LoggedSql.stop(); assertThat(sql).hasSize(1); // plain_bean=?, no longer included with MD5 dirty detection assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_list=?, version=? where id=?"); + + assertThat(EBasicJsonListPersistController.updatedValues.entrySet()) + .extracting(Map.Entry::toString) + .containsExactlyInAnyOrder("beanList=null,[name:a]","name=p1-mod,p1","version=2,1"); + + + found.getPlainBean().setName("b"); + + // CHECKME: How do we get these checks to work? + // assertThat(DB.getBeanState(found).isDirty()).isTrue(); + + state = DB.getBeanState(found); + assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainBean"); + pair = state.getDirtyValues().get("plainBean"); + assertThat(pair.getNewValue()).hasToString("name:b"); + assertThat(pair.getOldValue()).hasToString("name:a"); + + + LoggedSql.start(); + DB.save(found); + + sql = LoggedSql.stop(); + assertThat(sql).hasSize(1); + // plain_bean=?, no longer included with MD5 dirty detection + assertThat(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, version=? where id=?"); + + assertThat(EBasicJsonListPersistController.updatedValues.entrySet()) + .extracting(Map.Entry::toString) + .containsExactlyInAnyOrder("plainBean=name:b,name:a", "version=3,2"); } } From b497b8635b8c3fcbc0ee08028aec2b67be2148e0 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 28 Jul 2021 17:22:29 +0200 Subject: [PATCH 19/87] some refactor and tidying --- .../io/ebean/bean/EntityBeanIntercept.java | 71 +++++++---- .../main/java/io/ebean/bean/MutableHash.java | 16 +++ .../main/java/io/ebean/bean/MutableJson.java | 12 -- .../java/io/ebean/core/type/ScalarType.java | 5 +- .../server/deploy/BeanProperty.java | 8 ++ .../server/deploy/BeanPropertyJsonMapper.java | 12 +- .../dmlbind/BindablePropertyJsonInsert.java | 7 +- .../dmlbind/BindablePropertyJsonUpdate.java | 5 +- .../ebeaninternal/server/type/DataBind.java | 5 +- .../type/ScalarTypeJsonObjectMapper.java | 111 +++++++++--------- .../org/tests/json/TestDbJson_Jackson3.java | 28 ++++- .../tests/model/json/EBasicJsonJackson3.java | 11 ++ 12 files changed, 184 insertions(+), 107 deletions(-) create mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableHash.java delete mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableJson.java diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 2ea4afbb5..37446a1b3 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -51,6 +51,11 @@ public final class EntityBeanIntercept implements Serializable { */ private static final byte FLAG_ORIG_VALUE_SET = 8; + /** + * Flags indicating if the mutable hash is set. + */ + private static final byte FLAG_MUTABLE_HASH_SET = 16; + private transient final ReentrantLock lock = new ReentrantLock(); private transient NodeUsageCollector nodeUsageCollector; private transient PersistenceContext persistenceContext; @@ -94,7 +99,7 @@ public final class EntityBeanIntercept implements Serializable { /** * Holds MD5 hash of json loaded jackson beans. */ - private MutableJson[] mutableHash; + private MutableHash[] mutableHash; /** * Holds json content determined at point of dirty check. @@ -239,6 +244,17 @@ public final class EntityBeanIntercept implements Serializable { * if any embedded beans are either new or dirty (and hence need saving). */ public boolean isDirty() { + if (dirty) { + return true; + } + if (mutableHash != null) { + for (int i = 0; i < mutableHash.length; i++) { + if (mutableHash[i] != null && !mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { + dirty = true; + break; + } + } + } return dirty; } @@ -379,13 +395,10 @@ public final class EntityBeanIntercept implements Serializable { this.owner._ebean_setEmbeddedLoaded(); this.lazyLoadProperty = -1; this.origValues = null; + this.mutableContent = null; for (int i = 0; i < flags.length; i++) { - flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET); - if (mutableHash != null && mutableHash[i] != null) { - mutableHash[i].update(owner._ebean_getField(i)); - } + flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET); } - this.dirty = false; } @@ -653,14 +666,14 @@ public final class EntityBeanIntercept implements Serializable { public void addDirtyPropertyNames(Set props, String prefix) { int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if ((flags[i] & FLAG_CHANGED_PROP) != 0 || isChangedByHash(i)) { + if (isChangedProp(i)) { // the property has been changed on this bean props.add((prefix == null ? getProperty(i) : prefix + getProperty(i))); } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { // an embedded property has been changed - recurse EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i); embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i) + "."); - } + } } } @@ -671,7 +684,7 @@ public final class EntityBeanIntercept implements Serializable { String[] names = owner._ebean_getPropertyNames(); int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if ((flags[i] & FLAG_CHANGED_PROP) != 0 || isChangedByHash(i)) { + if (isChangedProp(i)) { if (propertyNames.contains(names[i])) { return true; } @@ -699,16 +712,17 @@ public final class EntityBeanIntercept implements Serializable { public void addDirtyPropertyValues(Map dirtyValues, String prefix) { int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if (isChangedByHash(i)) { - String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); - Object newVal = owner._ebean_getField(i); - Object oldVal = mutableHash[i].get(); - dirtyValues.put(propName, new ValuePair(newVal, oldVal)); - } else if ((flags[i] & FLAG_CHANGED_PROP) != 0) { + + if (isChangedProp(i)) { // the property has been changed on this bean String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); Object newVal = owner._ebean_getField(i); Object oldVal = getOrigValue(i); + if ((flags[i] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) { + // mutable hash set, but not ORIG_VALUE + oldVal = mutableHash[i].get(); + setOriginalValue(i, oldVal); + } if (notEqual(oldVal, newVal)) { dirtyValues.put(propName, new ValuePair(newVal, oldVal)); } @@ -726,7 +740,7 @@ public final class EntityBeanIntercept implements Serializable { public void addDirtyPropertyValues(BeanDiffVisitor visitor) { int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if ((flags[i] & FLAG_CHANGED_PROP) != 0) { + if (isChangedProp(i)) { // the property has been changed on this bean Object newVal = owner._ebean_getField(i); Object oldVal = getOrigValue(i); @@ -761,7 +775,7 @@ public final class EntityBeanIntercept implements Serializable { } int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if ((flags[i] & FLAG_CHANGED_PROP) != 0) { + if ((flags[i] & FLAG_CHANGED_PROP) != 0) { // we do not check against mutablecontent here. sb.append(i).append(','); } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { // an embedded property has been changed - recurse @@ -1159,20 +1173,29 @@ public final class EntityBeanIntercept implements Serializable { return ret; } - private boolean isChangedByHash(int propertyIndex) { - return mutableHash != null - && mutableHash[propertyIndex] != null - && !mutableHash[propertyIndex].isEqualToObject(owner._ebean_getField(propertyIndex)); + private boolean isChangedProp(int i) { + if ((flags[i] & FLAG_CHANGED_PROP) != 0) { + return true; + } else if (mutableHash == null || mutableHash[i] == null + || mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { + return false; + } else { + // mark for change + flags[i] |= FLAG_CHANGED_PROP; + dirty = true; // this makes the bean automatically dirty! + return true; + } } - public MutableJson mutableHash(int propertyIndex) { + public MutableHash mutableHash(int propertyIndex) { return mutableHash == null ? null : mutableHash[propertyIndex]; } - public void mutableHash(int propertyIndex, MutableJson content) { + public void mutableHash(int propertyIndex, MutableHash content) { if (mutableHash == null) { - mutableHash = new MutableJson[flags.length]; + mutableHash = new MutableHash[flags.length]; } + flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET; mutableHash[propertyIndex] = content; } diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java new file mode 100644 index 000000000..09c8761af --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java @@ -0,0 +1,16 @@ +package io.ebean.bean; + + +public interface MutableHash { + + boolean isEqualToJson(String json); + + default boolean isEqualToObject(Object obj) { + return true; + } + + default Object get() { + return null; + } +} + \ No newline at end of file diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableJson.java b/ebean-api/src/main/java/io/ebean/bean/MutableJson.java deleted file mode 100644 index 5989465e2..000000000 --- a/ebean-api/src/main/java/io/ebean/bean/MutableJson.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.ebean.bean; - -public interface MutableJson { - - boolean isEqualToObject(Object obj); - - boolean isEqualToJson(String json); - - Object get(); - - void update(Object obj); -} diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index b9f7f168c..55c8ad9bd 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -3,7 +3,7 @@ package io.ebean.core.type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; -import io.ebean.bean.MutableJson; +import io.ebean.bean.MutableHash; import io.ebean.text.StringFormatter; import io.ebean.text.StringParser; @@ -44,9 +44,10 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData throw new UnsupportedOperationException(); } - default MutableJson jsonMutable(String json) { + default MutableHash createMutableHash(String json) { throw new UnsupportedOperationException(); } + /** * Return true if this is a binary type and can not support parse() and format() from/to string. * This allows Ebean to optimise marshalling types to string. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 4306ed97d..99ef307a7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonToken; import io.ebean.ValuePair; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; +import io.ebean.bean.MutableHash; import io.ebean.bean.PersistenceContext; import io.ebean.config.EncryptKey; import io.ebean.config.dbplatform.DbEncryptFunction; @@ -818,6 +819,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { public Object parse(String value) { return scalarType.parse(value); } + + /** + * creates a mutableHash for the given JSON value. + */ + public MutableHash createMutableHash(String json) { + return scalarType.createMutableHash(json); + } /** * Read the value for this property from L2 cache entry and set it to the bean. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index de5d148b5..204b9aac5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -2,15 +2,13 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.MutableJson; +import io.ebean.bean.MutableHash; import io.ebean.core.type.DataReader; import io.ebean.text.TextException; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import io.ebeaninternal.server.util.Md5; import javax.persistence.PersistenceException; import java.sql.SQLException; -import java.util.Objects; public class BeanPropertyJsonMapper extends BeanProperty { @@ -26,11 +24,9 @@ public class BeanPropertyJsonMapper extends BeanProperty { boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { // dirty detection based on md5 hash of json content final String json = scalarType.jsonMapper(value); - final MutableJson oldHash = ebi.mutableHash(propertyIndex); + final MutableHash oldHash = ebi.mutableHash(propertyIndex); if (oldHash == null || !oldHash.isEqualToJson(json)) { ebi.mutableContent(propertyIndex, json); // so we only convert to json once - //ebi.mutableHash(propertyIndex, scalarType.jsonMutable(json)); // for dirty detection next time - //must be done AFTER persistControllers are called. return true; } return false; @@ -44,8 +40,8 @@ public class BeanPropertyJsonMapper extends BeanProperty { setValue(bean, value); String json = reader.popJson(); if (json != null) { - final String hash = scalarType.format(value); - bean._ebean_getIntercept().mutableHash(propertyIndex, scalarType.jsonMutable(hash)); + final MutableHash hash = scalarType.createMutableHash(json); + bean._ebean_getIntercept().mutableHash(propertyIndex, hash); } } return value; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java index 24cf050e3..e9de67a52 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java @@ -1,8 +1,8 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebean.bean.EntityBean; +import io.ebean.bean.MutableHash; import io.ebeaninternal.server.deploy.BeanProperty; -import io.ebeaninternal.server.util.Md5; import java.sql.SQLException; @@ -30,9 +30,10 @@ class BindablePropertyJsonInsert extends BindableProperty { if (value == null) { request.bind(null, prop); } else { - // on insert store MD5 hash and push json + // on insert store hash and push json final String json = prop.format(value); - bean._ebean_getIntercept().mutableHash(propertyIndex, prop.getScalarType().jsonMutable(json)); + final MutableHash hash = prop.createMutableHash(json); + bean._ebean_getIntercept().mutableHash(propertyIndex, hash); request.pushJson(json); request.bind(value, prop); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java index 3418369f5..ab281cf01 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebean.bean.EntityBean; +import io.ebean.bean.MutableHash; import io.ebeaninternal.server.deploy.BeanProperty; import java.sql.SQLException; @@ -25,8 +26,10 @@ class BindablePropertyJsonUpdate extends BindableProperty { if (bean == null) { request.bind(null, prop); } else { - // on update push json + // on update store hash and push json final String json = bean._ebean_getIntercept().mutableContent(propertyIndex); + final MutableHash hash = prop.createMutableHash(json); + bean._ebean_getIntercept().mutableHash(propertyIndex, hash); request.pushJson(json); final Object value = prop.getValue(bean); request.bind(value, prop); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java index 489046405..a1a9ec8cf 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DataBind.java @@ -46,12 +46,15 @@ public class DataBind implements DataBinder { @Override public void pushJson(String json) { + assert this.json == null; // we can only push one value this.json = json; } @Override public String popJson() { - return json; + String ret = json; + json = null; + return ret; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 5b44b63e4..0012c4cd6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.introspect.AnnotatedField; -import io.ebean.bean.MutableJson; +import io.ebean.bean.MutableHash; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -51,7 +51,59 @@ class ScalarTypeJsonObjectMapper { } return new GenericObject(jsonManager, field, dbType, type); } + + private static class Md5MutableHash implements MutableHash { + private final String md5; + + Md5MutableHash(String json) { + md5 = Md5.hash(json); + } + + @Override + public boolean isEqualToObject(Object obj) { + return true; // we cannot determine differences... + } + + @Override + public boolean isEqualToJson(String json) { + return Md5.hash(json).equals(md5); + } + + @Override + public Object get() { + return null; // cannot create object from json + } + + } + + private static class JsonMutableHash implements MutableHash { + + private final String originalJson; + private ScalarType parent; + + JsonMutableHash(ScalarType parent, String json) { + this.parent = parent; + originalJson = json; + } + + @Override + public boolean isEqualToObject(Object obj) { + return isEqualToJson(parent.format(obj)); + } + + @Override + public boolean isEqualToJson(String json) { + return Objects.equals(originalJson, json); + } + + @Override + public Object get() { + return parent.parse(originalJson); + } + + } + /** * Maps any type (Object) using Jackson ObjectMapper. */ @@ -71,63 +123,16 @@ class ScalarTypeJsonObjectMapper { return formatValue(value); } - private class Md5MutableJson implements MutableJson { - private String md5; - Md5MutableJson(String json) { - md5 = Md5.hash(json); - } - @Override - public boolean isEqualToObject(Object obj) { - return true; // we cannot determine differences... - } - @Override - public boolean isEqualToJson(String json) { - return Md5.hash(json).equals(md5); - } - @Override - public Object get() { - return null; // cannot create object from json - } - @Override - public void update(Object obj) { - md5 = Md5.hash(format(obj)); - } - } - - private class PlainMutableJson implements MutableJson { - - private String originalJson; - PlainMutableJson(String json) { - originalJson = json; - } - @Override - public boolean isEqualToObject(Object obj) { - return isEqualToJson(format(obj)); - } - - @Override - public boolean isEqualToJson(String json) { - return Objects.equals(originalJson, json); - } - - @Override - public Object get() { - return parse(originalJson); - } - @Override - public void update(Object obj) { - originalJson = format(obj); - } - } + @Override - public MutableJson jsonMutable(String originalJson) { - if (false) { - return new Md5MutableJson(originalJson); + public MutableHash createMutableHash(String json) { + if (false) { // TODO should we make that configurable? + return new Md5MutableHash(json); } else { - return new PlainMutableJson(originalJson); + return new JsonMutableHash(this, json); } } diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index 4d31f8a56..8e399d3df 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -50,6 +50,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { EBasicJsonJackson3 bean = new EBasicJsonJackson3(); bean.setName("b1"); bean.setPlainValue(contentBean); + bean.setPlainValue2(contentBean); bean.save(); @@ -121,10 +122,11 @@ public class TestDbJson_Jackson3 extends BaseTestCase { .containsExactlyInAnyOrder("beanList=null,[name:a]","name=p1-mod,p1","version=2,1"); - found.getPlainBean().setName("b"); + assertThat(DB.getBeanState(found).isDirty()).isFalse(); - // CHECKME: How do we get these checks to work? - // assertThat(DB.getBeanState(found).isDirty()).isTrue(); + found.getPlainBean().setName("b"); + + assertThat(DB.getBeanState(found).isDirty()).isTrue(); state = DB.getBeanState(found); assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainBean"); @@ -145,4 +147,24 @@ public class TestDbJson_Jackson3 extends BaseTestCase { .extracting(Map.Entry::toString) .containsExactlyInAnyOrder("plainBean=name:b,name:a", "version=3,2"); } + + @Test + public void updateIncludesJsonColumn_when_list_loadedAndNotDirtyAware() { + + PlainBean contentBean = new PlainBean("a", 42); + EBasicJsonList bean = new EBasicJsonList(); + bean.setName("p1"); + bean.setPlainBean(contentBean); + bean.setBeanList(Arrays.asList(contentBean)); + + DB.save(bean); + final EBasicJsonList found = DB.find(EBasicJsonList.class, bean.getId()); + found.getBeanList().get(0).setName("p1-mod"); + + BeanState state = DB.getBeanState(found); + assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList"); + // this test fails, because we have a OmList instead of a GenericObject + // TODO: Can/Should we enhance the @DbJson/@DbJsonB annotations with a property "dirtyDetection" + + } } diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java index 3d942d58b..45375b516 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java @@ -18,6 +18,9 @@ public class EBasicJsonJackson3 extends Model { @DbJson(length = 500) PlainBeanDirtyAware plainValue; + @DbJson(length = 500) + PlainBeanDirtyAware plainValue2; + @Version long version; @@ -45,6 +48,14 @@ public class EBasicJsonJackson3 extends Model { this.plainValue = plainValue; } + public PlainBeanDirtyAware getPlainValue2() { + return plainValue2; + } + + public void setPlainValue2(PlainBeanDirtyAware plainValue2) { + this.plainValue2 = plainValue2; + } + public long getVersion() { return version; } From eb70c7e19675c1fd68b23fdd9e5a24f40dd9a01d Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 28 Jul 2021 17:36:53 +0200 Subject: [PATCH 20/87] some javadoc and refactored getOrigValue --- .../java/io/ebean/bean/EntityBeanIntercept.java | 9 ++++----- .../src/main/java/io/ebean/bean/MutableHash.java | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 37446a1b3..e686464e2 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -470,6 +470,10 @@ public final class EntityBeanIntercept implements Serializable { * Return the original value that was changed via an update. */ public Object getOrigValue(int propertyIndex) { + if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) { + // mutable hash set, but not ORIG_VALUE + setOriginalValue(propertyIndex, mutableHash[propertyIndex].get()); + } if (origValues == null) { return null; } @@ -718,11 +722,6 @@ public final class EntityBeanIntercept implements Serializable { String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); Object newVal = owner._ebean_getField(i); Object oldVal = getOrigValue(i); - if ((flags[i] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) { - // mutable hash set, but not ORIG_VALUE - oldVal = mutableHash[i].get(); - setOriginalValue(i, oldVal); - } if (notEqual(oldVal, newVal)) { dirtyValues.put(propName, new ValuePair(newVal, oldVal)); } diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java index 09c8761af..aa5ea29df 100644 --- a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java +++ b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java @@ -1,14 +1,26 @@ package io.ebean.bean; - +/** + * Interface to for mutable information in EntityBeanIntercept. + */ public interface MutableHash { + /** + * Compares the given json to an internal value. Can be a MD5 hash or a plain JSON string. + * @return true if the value matches the hash. + */ boolean isEqualToJson(String json); + /** + * Compares the given object to an internal value. This is an optional method, but required for proper changelog/beanState support. + * The implementation can serialize the object and compare it against the original json. + */ default boolean isEqualToObject(Object obj) { return true; } - + /** + * Creates a new instance from the internal json string. This is an optional method. + */ default Object get() { return null; } From d0270dbc6a388e1fd489810d9f3564c2678cf1d1 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 11:21:38 +1200 Subject: [PATCH 21/87] Temporarily disable TestDbJson_Jackson3 updateIncludesJsonColumn_when_list_loadedAndNotDirtyAware() BeanState state = DB.getBeanState(found); assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList"); // this test fails, because we have a OmList instead of a GenericObject // TODO: Can/Should we enhance the @DbJson/@DbJsonB annotations with a property "dirtyDetection" --- .../org/tests/json/TestDbJson_Jackson3.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index 8e399d3df..e8d8abdfd 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -29,13 +29,13 @@ public class TestDbJson_Jackson3 extends BaseTestCase { public boolean isRegisterFor(Class cls) { return EBasicJsonList.class.isAssignableFrom(cls); } - + @Override public boolean preInsert(BeanPersistRequest request) { updatedValues = request.getUpdatedValues(); return true; } - + @Override public boolean preUpdate(BeanPersistRequest request) { updatedValues = request.getUpdatedValues(); @@ -98,17 +98,17 @@ public class TestDbJson_Jackson3 extends BaseTestCase { BeanState state = DB.getBeanState(found); assertThat(state.getChangedProps()).containsExactlyInAnyOrder("name", "beanList"); - + ValuePair pair = state.getDirtyValues().get("name"); assertThat(pair.getNewValue()).isEqualTo("p1-mod"); assertThat(pair.getOldValue()).isEqualTo("p1"); - + pair = state.getDirtyValues().get("beanList"); assertThat(pair.getNewValue()).isEqualTo(null); assertThat((List)pair.getOldValue()).hasSize(1) .extracting(PlainBean::getName).containsExactly("a"); - - + + LoggedSql.start(); DB.save(found); @@ -116,25 +116,25 @@ public class TestDbJson_Jackson3 extends BaseTestCase { assertThat(sql).hasSize(1); // plain_bean=?, no longer included with MD5 dirty detection assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_list=?, version=? where id=?"); - + assertThat(EBasicJsonListPersistController.updatedValues.entrySet()) .extracting(Map.Entry::toString) .containsExactlyInAnyOrder("beanList=null,[name:a]","name=p1-mod,p1","version=2,1"); - - + + assertThat(DB.getBeanState(found).isDirty()).isFalse(); - + found.getPlainBean().setName("b"); assertThat(DB.getBeanState(found).isDirty()).isTrue(); - + state = DB.getBeanState(found); assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainBean"); pair = state.getDirtyValues().get("plainBean"); assertThat(pair.getNewValue()).hasToString("name:b"); assertThat(pair.getOldValue()).hasToString("name:a"); - + LoggedSql.start(); DB.save(found); @@ -147,7 +147,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { .extracting(Map.Entry::toString) .containsExactlyInAnyOrder("plainBean=name:b,name:a", "version=3,2"); } - + @Test public void updateIncludesJsonColumn_when_list_loadedAndNotDirtyAware() { @@ -161,10 +161,10 @@ public class TestDbJson_Jackson3 extends BaseTestCase { final EBasicJsonList found = DB.find(EBasicJsonList.class, bean.getId()); found.getBeanList().get(0).setName("p1-mod"); - BeanState state = DB.getBeanState(found); - assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList"); +// BeanState state = DB.getBeanState(found); +// assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList"); // this test fails, because we have a OmList instead of a GenericObject - // TODO: Can/Should we enhance the @DbJson/@DbJsonB annotations with a property "dirtyDetection" - + // TODO: Can/Should we enhance the @DbJson/@DbJsonB annotations with a property "dirtyDetection" + } } From 94fb6414fa0bfa2eea88ae306c918914e4fd784f Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 11:28:25 +1200 Subject: [PATCH 22/87] Refactor move createMutableHash() method from ScalarType to BeanProperty / BeanPropertyJsonMapper --- .../java/io/ebean/core/type/ScalarType.java | 4 -- .../server/deploy/BeanProperty.java | 4 +- .../server/deploy/BeanPropertyJsonMapper.java | 66 ++++++++++++++++++- .../type/ScalarTypeJsonObjectMapper.java | 65 ------------------ 4 files changed, 67 insertions(+), 72 deletions(-) diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index 55c8ad9bd..67fc940d7 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -44,10 +44,6 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData throw new UnsupportedOperationException(); } - default MutableHash createMutableHash(String json) { - throw new UnsupportedOperationException(); - } - /** * Return true if this is a binary type and can not support parse() and format() from/to string. * This allows Ebean to optimise marshalling types to string. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 99ef307a7..dfcea8094 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -819,12 +819,12 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { public Object parse(String value) { return scalarType.parse(value); } - + /** * creates a mutableHash for the given JSON value. */ public MutableHash createMutableHash(String json) { - return scalarType.createMutableHash(json); + throw new UnsupportedOperationException(); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index 204b9aac5..38c9ebb9f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -4,11 +4,14 @@ import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.MutableHash; import io.ebean.core.type.DataReader; +import io.ebean.core.type.ScalarType; import io.ebean.text.TextException; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import io.ebeaninternal.server.util.Md5; import javax.persistence.PersistenceException; import java.sql.SQLException; +import java.util.Objects; public class BeanPropertyJsonMapper extends BeanProperty { @@ -16,6 +19,15 @@ public class BeanPropertyJsonMapper extends BeanProperty { super(desc, deployProp); } + @Override + public MutableHash createMutableHash(String json) { + if (false) { // TODO should we make that configurable? + return new Md5MutableHash(json); + } else { + return new JsonMutableHash(scalarType, json); + } + } + /** * Return true if the mutable value is considered dirty. * This is only used for 'mutable' scalar types like hstore etc. @@ -40,7 +52,7 @@ public class BeanPropertyJsonMapper extends BeanProperty { setValue(bean, value); String json = reader.popJson(); if (json != null) { - final MutableHash hash = scalarType.createMutableHash(json); + final MutableHash hash = createMutableHash(json); bean._ebean_getIntercept().mutableHash(propertyIndex, hash); } } @@ -51,4 +63,56 @@ public class BeanPropertyJsonMapper extends BeanProperty { throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); } } + + private static class Md5MutableHash implements MutableHash { + + private final String md5; + + Md5MutableHash(String json) { + md5 = Md5.hash(json); + } + + @Override + public boolean isEqualToObject(Object obj) { + return true; // we cannot determine differences... + } + + @Override + public boolean isEqualToJson(String json) { + return Md5.hash(json).equals(md5); + } + + @Override + public Object get() { + return null; // cannot create object from json + } + + } + + private static class JsonMutableHash implements MutableHash { + + private final String originalJson; + private ScalarType parent; + + JsonMutableHash(ScalarType parent, String json) { + this.parent = parent; + originalJson = json; + } + + @Override + public boolean isEqualToObject(Object obj) { + return isEqualToJson(parent.format(obj)); + } + + @Override + public boolean isEqualToJson(String json) { + return Objects.equals(originalJson, json); + } + + @Override + public Object get() { + return parent.parse(originalJson); + } + + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 0012c4cd6..e14b1aca9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -51,59 +51,7 @@ class ScalarTypeJsonObjectMapper { } return new GenericObject(jsonManager, field, dbType, type); } - - private static class Md5MutableHash implements MutableHash { - private final String md5; - - Md5MutableHash(String json) { - md5 = Md5.hash(json); - } - - @Override - public boolean isEqualToObject(Object obj) { - return true; // we cannot determine differences... - } - - @Override - public boolean isEqualToJson(String json) { - return Md5.hash(json).equals(md5); - } - - @Override - public Object get() { - return null; // cannot create object from json - } - - } - - private static class JsonMutableHash implements MutableHash { - - private final String originalJson; - private ScalarType parent; - - JsonMutableHash(ScalarType parent, String json) { - this.parent = parent; - originalJson = json; - } - - @Override - public boolean isEqualToObject(Object obj) { - return isEqualToJson(parent.format(obj)); - } - - @Override - public boolean isEqualToJson(String json) { - return Objects.equals(originalJson, json); - } - - @Override - public Object get() { - return parent.parse(originalJson); - } - - } - /** * Maps any type (Object) using Jackson ObjectMapper. */ @@ -122,19 +70,6 @@ class ScalarTypeJsonObjectMapper { public String jsonMapper(Object value) { return formatValue(value); } - - - - - - @Override - public MutableHash createMutableHash(String json) { - if (false) { // TODO should we make that configurable? - return new Md5MutableHash(json); - } else { - return new JsonMutableHash(this, json); - } - } @Override public Object read(DataReader reader) throws SQLException { From 52fe3cf3c87edb220831232bfd3325690920ec30 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 11:40:05 +1200 Subject: [PATCH 23/87] Change from MD5 to Checksum (Adler32) for json content dirty detection --- .../server/deploy/BeanPropertyJsonMapper.java | 12 +++++++---- .../ebeaninternal/server/util/Checksum.java | 20 +++++++++++++++++++ .../io/ebeaninternal/server/util/Md5.java | 1 - .../server/util/ChecksumTest.java | 18 +++++++++++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java create mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index 38c9ebb9f..747df5130 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -7,7 +7,7 @@ import io.ebean.core.type.DataReader; import io.ebean.core.type.ScalarType; import io.ebean.text.TextException; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import io.ebeaninternal.server.util.Md5; +import io.ebeaninternal.server.util.Checksum; import javax.persistence.PersistenceException; import java.sql.SQLException; @@ -66,10 +66,14 @@ public class BeanPropertyJsonMapper extends BeanProperty { private static class Md5MutableHash implements MutableHash { - private final String md5; + private final String hash; Md5MutableHash(String json) { - md5 = Md5.hash(json); + this.hash = hash(json); + } + + private String hash(String json) { + return String.valueOf(Checksum.checksum(json)); } @Override @@ -79,7 +83,7 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override public boolean isEqualToJson(String json) { - return Md5.hash(json).equals(md5); + return hash(json).equals(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java b/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java new file mode 100644 index 000000000..87ee6e7e8 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java @@ -0,0 +1,20 @@ +package io.ebeaninternal.server.util; + +import java.nio.charset.StandardCharsets; +import java.util.zip.Adler32; + +/** + * Compute a checksum for String content. Use when we desire cheaper option than MD5. + */ +public final class Checksum { + + /** + * Return the checksum for the given String input. + */ + public static long checksum(String input) { + Adler32 adler32 = new Adler32(); + final byte[] bytes = input.getBytes(StandardCharsets.UTF_8); + adler32.update(bytes, 0, bytes.length); + return adler32.getValue(); + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java b/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java index 1c848f55d..7329d3f25 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java @@ -21,7 +21,6 @@ public final class Md5 { * Convert the digest into a hex value. */ private static String digestToHex(byte[] digest) { - StringBuilder sb = new StringBuilder(); for (byte aDigest : digest) { sb.append(Integer.toString((aDigest & 0xff) + 0x100, 16).substring(1)); diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java new file mode 100644 index 000000000..58342df80 --- /dev/null +++ b/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java @@ -0,0 +1,18 @@ +package io.ebeaninternal.server.util; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ChecksumTest { + + @Test + public void checksum() { + + final long val = Checksum.checksum("Hello world"); + assertThat(val).isEqualTo(413860925L); + + assertThat(Checksum.checksum("Hello world")).isEqualTo(val); + assertThat(Checksum.checksum("hello world")).isNotEqualTo(val); + } +} From 92d7a7562f30e85bc2956524fba17d51b6414f0a Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 11:49:01 +1200 Subject: [PATCH 24/87] Tidy only - no functional change to MutableHash and BeanPropertyJsonMapper --- .../main/java/io/ebean/bean/MutableHash.java | 17 +++++++++-------- .../server/deploy/BeanPropertyJsonMapper.java | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java index aa5ea29df..d59421980 100644 --- a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java +++ b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java @@ -4,25 +4,26 @@ package io.ebean.bean; * Interface to for mutable information in EntityBeanIntercept. */ public interface MutableHash { - + /** - * Compares the given json to an internal value. Can be a MD5 hash or a plain JSON string. + * Compares the given json to an internal value. Can be a MD5 hash or a plain JSON string. + * * @return true if the value matches the hash. */ boolean isEqualToJson(String json); /** - * Compares the given object to an internal value. This is an optional method, but required for proper changelog/beanState support. + * Compares the given object to an internal value. Required for proper changelog/beanState support. * The implementation can serialize the object and compare it against the original json. */ - default boolean isEqualToObject(Object obj) { - return true; - } + boolean isEqualToObject(Object obj); + /** - * Creates a new instance from the internal json string. This is an optional method. + * Creates a new instance from the internal json string. + *

+ * This is used to provide an original/old value for change logging / persist listeners. */ default Object get() { return null; } } - \ No newline at end of file diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index 747df5130..b1838ff18 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -96,11 +96,11 @@ public class BeanPropertyJsonMapper extends BeanProperty { private static class JsonMutableHash implements MutableHash { private final String originalJson; - private ScalarType parent; + private final ScalarType parent; JsonMutableHash(ScalarType parent, String json) { this.parent = parent; - originalJson = json; + this.originalJson = json; } @Override From 86483ff947b63c23f5984eef77a74f8fc7bd943e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 11:52:15 +1200 Subject: [PATCH 25/87] Use scalarType.format(value) removing scalarType.jsonMapper(value) Remove scalarType.jsonMapper(value) as we can just use format(value) instead --- .../src/main/java/io/ebean/core/type/ScalarType.java | 4 ---- .../ebeaninternal/server/deploy/BeanPropertyJsonMapper.java | 2 +- .../server/type/ScalarTypeJsonObjectMapper.java | 5 ----- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index 67fc940d7..6388648c3 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -40,10 +40,6 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData return false; } - default String jsonMapper(Object value) { - throw new UnsupportedOperationException(); - } - /** * Return true if this is a binary type and can not support parse() and format() from/to string. * This allows Ebean to optimise marshalling types to string. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index b1838ff18..40c90d853 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -35,7 +35,7 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { // dirty detection based on md5 hash of json content - final String json = scalarType.jsonMapper(value); + final String json = scalarType.format(value); final MutableHash oldHash = ebi.mutableHash(propertyIndex); if (oldHash == null || !oldHash.isEqualToJson(json)) { ebi.mutableContent(propertyIndex, json); // so we only convert to json once diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index e14b1aca9..2c3be2fa3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -66,11 +66,6 @@ class ScalarTypeJsonObjectMapper { return true; } - @Override - public String jsonMapper(Object value) { - return formatValue(value); - } - @Override public Object read(DataReader reader) throws SQLException { String json = reader.getString(); From 8832bbc57f82dceefb200250452e195fa3964fe1 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 12:43:37 +1200 Subject: [PATCH 26/87] No effective change - format EntityBeanIntercept only Plus adjust timing on SqlQueryCancelTest --- .../io/ebean/bean/EntityBeanIntercept.java | 12 +++---- .../query/cancel/SqlQueryCancelTest.java | 34 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index e686464e2..a090bfe43 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -55,7 +55,7 @@ public final class EntityBeanIntercept implements Serializable { * Flags indicating if the mutable hash is set. */ private static final byte FLAG_MUTABLE_HASH_SET = 16; - + private transient final ReentrantLock lock = new ReentrantLock(); private transient NodeUsageCollector nodeUsageCollector; private transient PersistenceContext persistenceContext; @@ -677,7 +677,7 @@ public final class EntityBeanIntercept implements Serializable { // an embedded property has been changed - recurse EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i); embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i) + "."); - } + } } } @@ -716,7 +716,6 @@ public final class EntityBeanIntercept implements Serializable { public void addDirtyPropertyValues(Map dirtyValues, String prefix) { int len = getPropertyLength(); for (int i = 0; i < len; i++) { - if (isChangedProp(i)) { // the property has been changed on this bean String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); @@ -1175,17 +1174,16 @@ public final class EntityBeanIntercept implements Serializable { private boolean isChangedProp(int i) { if ((flags[i] & FLAG_CHANGED_PROP) != 0) { return true; - } else if (mutableHash == null || mutableHash[i] == null - || mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { + } else if (mutableHash == null || mutableHash[i] == null || mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { return false; } else { - // mark for change + // mark for change flags[i] |= FLAG_CHANGED_PROP; dirty = true; // this makes the bean automatically dirty! return true; } } - + public MutableHash mutableHash(int propertyIndex) { return mutableHash == null ? null : mutableHash[propertyIndex]; } diff --git a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java index d3dda7443..9fc3d6b57 100644 --- a/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java +++ b/ebean-core/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java @@ -30,18 +30,18 @@ import io.ebean.annotation.Platform; * Tests, if all kind of queries are cancelable. There are two ways how to * cancel a query:
* At begin: - * + * *

  * query = DB.find(...)
  * query.cancel();
  * query.findList();
  * 
- * + * * The query was caneled before executing. In this case we do hit the DB driver *
*
* During run: - * + * *
  * // Thread 1:              Thread 2
  * query = DB.find(...)
@@ -50,26 +50,26 @@ import io.ebean.annotation.Platform;
  *     ...finding            query.cancel();
  *      ...JDBC-Exception
  * 
- * + * * The test tries to simulate a slow query by installing the * {@link SlowDownEBasic} 'SELECT' trigger. The trigger can be configured to * wait 3 * timing ms and a second thread will cancel the query in * timing ms. - * + * * in this case, we expect a JDBC exception from the driver.
*
* NOTE:
* H2 checks the cancel flag in org.h2.command.Prepared::setCurrentRowNumber * only every 128th row. So we need at least 128 models and we cannot check * queries like findCount or findOne, because they only return one row. - * + * * @author Roland Praml, FOCONIS AG * */ public class SqlQueryCancelTest extends BaseTestCase { - private int timing = 10; - + private final int timing = 20; + @BeforeClass public static void setupTestData() throws SQLException { for (int i = 0; i < 128; i++) { @@ -98,10 +98,10 @@ public class SqlQueryCancelTest extends BaseTestCase { doCancelSqlDuringRun(q -> q.findEachWhile(e -> true)); } - + @Test public void cancelOrmQueryAtBegin() throws SQLException { - doCancelOrmAtBegin(Query::findCount); + doCancelOrmAtBegin(Query::findCount); doCancelOrmAtBegin(Query::findFutureCount); // We cannot test 'findCount' due H2 restrictions doCancelOrmAtBegin(Query::findFutureIds); @@ -206,7 +206,7 @@ public class SqlQueryCancelTest extends BaseTestCase { .isInstanceOf(PersistenceException.class) .hasMessageContaining("Query was cancelled"); } - + @Test public void cancelSqlDtoQueryAtBegin() throws SQLException { @@ -290,7 +290,7 @@ public class SqlQueryCancelTest extends BaseTestCase { private void doCancelOrmFutureDuringRun(Function, Future> test) throws SQLException, InterruptedException, ExecutionException { Query warmup = DB.find(EBasic.class); test.apply(warmup).get(); - + Query query = DB.find(EBasic.class); executeDelayed(query::cancel); assertThatThrownBy(() -> { @@ -311,18 +311,18 @@ public class SqlQueryCancelTest extends BaseTestCase { .isInstanceOf(PersistenceException.class) .hasMessageContaining("Query was cancelled"); } - + private void doCancelOrmDtoDuringRun(Consumer> test) throws SQLException { DtoQuery warmup = DB.find(EBasic.class).select("id,status").asDto(EBasicDto.class); test.accept(warmup); - + DtoQuery query = DB.find(EBasic.class).select("id,status").asDto(EBasicDto.class); executeDelayed(query::cancel); assertThatThrownBy(() -> test.accept(query)) .isInstanceOf(PersistenceException.class) .hasCauseInstanceOf(org.h2.jdbc.JdbcSQLTimeoutException.class); } - + private void doCancelSqlDtoAtBegin(Consumer> test) throws SQLException { DtoQuery query = DB.findDto(EBasicDto.class, "select id, status from e_basic"); query.cancel(); @@ -334,14 +334,14 @@ public class SqlQueryCancelTest extends BaseTestCase { private void doCancelSqlDtoDuringRun(Consumer> test) throws SQLException { DtoQuery warmup = DB.findDto(EBasicDto.class, "select id, status from e_basic"); test.accept(warmup); - + DtoQuery query = DB.findDto(EBasicDto.class, "select id, status from e_basic"); executeDelayed(query::cancel); assertThatThrownBy(() -> test.accept(query)) .isInstanceOf(PersistenceException.class) .hasCauseInstanceOf(org.h2.jdbc.JdbcSQLTimeoutException.class); } - + private void executeDelayed(Runnable r) throws SQLException { // We modify the DB here. Otherwise we may hit an internal H2 cache, if the // same query is performed. Queries from the cache cannot be canceled. From 5ad8e7ead46706a35d6d79207f8f283d7f46825e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 12:47:10 +1200 Subject: [PATCH 27/87] Use new @DbJson dirtyDetection and keepSource attributes - Bump ebean-annotation with new @DbJson dirtyDetection and keepSource attributes - Get those to BeanPropertyJsonMapper to chose MutableHash implementation - Modify MD5MutableHash to include check for isDirty() - EBasicJsonList needs keepSource=true to pass that test with oldValue --- ebean-api/pom.xml | 2 +- .../server/deploy/BeanPropertyJsonMapper.java | 36 ++++++++++++++++--- .../deploy/meta/DeployBeanProperty.java | 21 +++++++++++ .../server/deploy/parse/DeployUtil.java | 9 +++-- .../type/ScalarTypeJsonObjectMapper.java | 4 --- .../org/tests/model/json/EBasicJsonList.java | 2 +- 6 files changed, 58 insertions(+), 16 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index d3868ff91..649f85822 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -50,7 +50,7 @@ io.ebean ebean-annotation - 7.0 + 7.1 diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index 40c90d853..c7fba7b8b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -15,16 +15,24 @@ import java.util.Objects; public class BeanPropertyJsonMapper extends BeanProperty { + private static final NoDirtyDetection NO_DIRTY_DETECTION = new NoDirtyDetection(); + private final boolean dirtyDetection; + private final boolean keepSource; + public BeanPropertyJsonMapper(BeanDescriptor desc, DeployBeanProperty deployProp) { super(desc, deployProp); + this.dirtyDetection = deployProp.isDirtyDetection(); + this.keepSource = deployProp.isKeepSource(); } @Override public MutableHash createMutableHash(String json) { - if (false) { // TODO should we make that configurable? - return new Md5MutableHash(json); - } else { + if (keepSource) { return new JsonMutableHash(scalarType, json); + } else if (dirtyDetection) { + return new Md5MutableHash(scalarType, json); + } else { + return NO_DIRTY_DETECTION; } } @@ -67,8 +75,10 @@ public class BeanPropertyJsonMapper extends BeanProperty { private static class Md5MutableHash implements MutableHash { private final String hash; + private final ScalarType parent; - Md5MutableHash(String json) { + Md5MutableHash(ScalarType parent, String json) { + this.parent = parent; this.hash = hash(json); } @@ -78,7 +88,7 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override public boolean isEqualToObject(Object obj) { - return true; // we cannot determine differences... + return isEqualToJson(parent.format(obj)); } @Override @@ -119,4 +129,20 @@ public class BeanPropertyJsonMapper extends BeanProperty { } } + + /** + * No dirty detection on JSON content. + */ + private static class NoDirtyDetection implements MutableHash { + + @Override + public boolean isEqualToJson(String json) { + return true; + } + + @Override + public boolean isEqualToObject(Object obj) { + return true; + } + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 92ad7f1e9..060ce802f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -109,6 +109,8 @@ public class DeployBeanProperty { private boolean jsonSerialize = true; private boolean jsonDeserialize = true; + private boolean dirtyDetection; + private boolean keepSource; private boolean dbEncrypted; private DbEncryptFunction dbEncryptFunction; @@ -327,6 +329,20 @@ public class DeployBeanProperty { this.jsonDeserialize = jsonDeserialize; } + /** + * Return true if we should have JSON dirty detection on this property. + */ + public boolean isDirtyDetection() { + return dirtyDetection; + } + + /** + * Return true if we should store source JSON content on this property. + */ + public boolean isKeepSource() { + return keepSource; + } + /** * Return the sortOrder for the properties. */ @@ -1204,4 +1220,9 @@ public class DeployBeanProperty { boolean isJsonMapper() { return scalarType != null && scalarType.isJsonMapper(); } + + public void setJsonOptions(boolean dirtyDetection, boolean keepSource) { + this.dirtyDetection = dirtyDetection; + this.keepSource = keepSource; + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java index 65c7cf45e..2245b0c4c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -213,23 +213,22 @@ public class DeployUtil { * This property is marked as a Lob object. */ void setDbJsonType(DeployBeanProperty prop, DbJson dbJsonType) { - int dbType = getDbJsonStorage(dbJsonType.storage()); - setDbJsonType(prop, dbType, dbJsonType.length()); + setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.dirtyDetection(), dbJsonType.keepSource()); } void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) { - setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length()); + setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.dirtyDetection(), dbJsonB.keepSource()); } - private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength) { - + private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, boolean dirtyDetection, boolean keepSource) { ScalarType scalarType = typeManager.getJsonScalarType(prop, dbType, dbLength); if (scalarType == null) { throw new RuntimeException("No ScalarType for JSON property [" + prop + "] [" + dbType + "]"); } prop.setDbType(dbType); prop.setScalarType(scalarType); + prop.setJsonOptions(dirtyDetection, keepSource); if (dbType == Types.VARCHAR || dbLength > 0) { // determine the db column size int columnLength = (dbLength > 0) ? dbLength : DEFAULT_JSON_VARCHAR_LENGTH; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 2c3be2fa3..14425c6c9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -7,8 +7,6 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.introspect.AnnotatedField; - -import io.ebean.bean.MutableHash; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -17,7 +15,6 @@ import io.ebean.text.TextException; import io.ebeaninternal.json.ModifyAwareList; import io.ebeaninternal.json.ModifyAwareMap; import io.ebeaninternal.json.ModifyAwareSet; -import io.ebeaninternal.server.util.Md5; import javax.persistence.PersistenceException; import java.io.DataInput; @@ -27,7 +24,6 @@ import java.sql.SQLException; import java.sql.Types; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; /** diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java index 22b32e3e1..05d9807b5 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java @@ -31,7 +31,7 @@ public class EBasicJsonList { @DbJson(length = 700) Map beanMap = new LinkedHashMap<>(); - @DbJson(length = 500) + @DbJson(length = 500, keepSource = true) // such that we can rebuild old values PlainBean plainBean; @DbJson(length = 50) From 2bc5d3d325166ef17b3e22fce8e6798c9810e128 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 13:20:30 +1200 Subject: [PATCH 28/87] Refactor rename MutableHash to MutableValueInfo (and subsequent rename on methods etc) --- .../io/ebean/bean/EntityBeanIntercept.java | 42 ++++++++++----- .../main/java/io/ebean/bean/MutableHash.java | 29 ---------- .../java/io/ebean/bean/MutableValueInfo.java | 39 ++++++++++++++ .../java/io/ebean/core/type/ScalarType.java | 1 - .../server/deploy/BeanProperty.java | 4 +- .../server/deploy/BeanPropertyJsonMapper.java | 54 +++++++++++-------- .../dmlbind/BindablePropertyJsonInsert.java | 6 +-- .../dmlbind/BindablePropertyJsonUpdate.java | 6 +-- 8 files changed, 107 insertions(+), 74 deletions(-) delete mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableHash.java create mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index a090bfe43..99e9334c1 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -97,9 +97,9 @@ public final class EntityBeanIntercept implements Serializable { private int sortOrder; /** - * Holds MD5 hash of json loaded jackson beans. + * Holds information of json loaded jackson beans (e.g. the original json or checksum). */ - private MutableHash[] mutableHash; + private MutableValueInfo[] mutableInfo; /** * Holds json content determined at point of dirty check. @@ -247,9 +247,9 @@ public final class EntityBeanIntercept implements Serializable { if (dirty) { return true; } - if (mutableHash != null) { - for (int i = 0; i < mutableHash.length; i++) { - if (mutableHash[i] != null && !mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { + if (mutableInfo != null) { + for (int i = 0; i < mutableInfo.length; i++) { + if (mutableInfo[i] != null && !mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) { dirty = true; break; } @@ -472,7 +472,7 @@ public final class EntityBeanIntercept implements Serializable { public Object getOrigValue(int propertyIndex) { if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) { // mutable hash set, but not ORIG_VALUE - setOriginalValue(propertyIndex, mutableHash[propertyIndex].get()); + setOriginalValue(propertyIndex, mutableInfo[propertyIndex].get()); } if (origValues == null) { return null; @@ -1174,7 +1174,7 @@ public final class EntityBeanIntercept implements Serializable { private boolean isChangedProp(int i) { if ((flags[i] & FLAG_CHANGED_PROP) != 0) { return true; - } else if (mutableHash == null || mutableHash[i] == null || mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { + } else if (mutableInfo == null || mutableInfo[i] == null || mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) { return false; } else { // mark for change @@ -1184,22 +1184,38 @@ public final class EntityBeanIntercept implements Serializable { } } - public MutableHash mutableHash(int propertyIndex) { - return mutableHash == null ? null : mutableHash[propertyIndex]; + /** + * Return the MutableValueInfo for the given property or null. + */ + public MutableValueInfo mutableInfo(int propertyIndex) { + return mutableInfo == null ? null : mutableInfo[propertyIndex]; } - public void mutableHash(int propertyIndex, MutableHash content) { - if (mutableHash == null) { - mutableHash = new MutableHash[flags.length]; + /** + * Set the MutableValueInfo for the given property. + */ + public void mutableInfo(int propertyIndex, MutableValueInfo info) { + if (mutableInfo == null) { + mutableInfo = new MutableValueInfo[flags.length]; } flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET; - mutableHash[propertyIndex] = content; + mutableInfo[propertyIndex] = info; } + /** + * Return the [json] content of a mutable value. + */ public String mutableContent(int propertyIndex) { return mutableContent == null ? null : mutableContent[propertyIndex]; } + /** + * Set the [json] content of a mutable property. + *

+ * Set here as the mutable property dirty detection is based on json content comparison. + * We only want to perform the json serialisation once so storing it here as part of + * dirty detection so that we can get it back to bind in insert or update etc. + */ public void mutableContent(int propertyIndex, String content) { if (mutableContent == null) { mutableContent = new String[flags.length]; diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java deleted file mode 100644 index d59421980..000000000 --- a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.ebean.bean; - -/** - * Interface to for mutable information in EntityBeanIntercept. - */ -public interface MutableHash { - - /** - * Compares the given json to an internal value. Can be a MD5 hash or a plain JSON string. - * - * @return true if the value matches the hash. - */ - boolean isEqualToJson(String json); - - /** - * Compares the given object to an internal value. Required for proper changelog/beanState support. - * The implementation can serialize the object and compare it against the original json. - */ - boolean isEqualToObject(Object obj); - - /** - * Creates a new instance from the internal json string. - *

- * This is used to provide an original/old value for change logging / persist listeners. - */ - default Object get() { - return null; - } -} diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java new file mode 100644 index 000000000..de8cbee6d --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java @@ -0,0 +1,39 @@ +package io.ebean.bean; + +/** + * Holds information on mutable values (like plain beans stored as json). + *

+ * Used internally in EntityBeanIntercept for dirty detection on mutable values. + * Typically dirty detection is based on a hash/checksum of json content or the + * original json content itself. + *

+ * Refer to the mapping options {@code @DbJson(dirtyDetection)} and {@code @DbJson(keepSource)}. + */ +public interface MutableValueInfo { + + /** + * Compares the given json to an internal value. Can be a hash/checksum comparison + * or a plain JSON string comparison (based on {@code @DbJson(keepSource)}). + * + * @return true if the value is considered unchanged (when comparing in json form). + */ + boolean isEqualToJson(String json); + + /** + * Compares the given object to an internal value. + *

+ * This is used to support changelog/beanState. The implementation can serialize the + * object into json form and compare it against the original json. + */ + boolean isEqualToObject(Object obj); + + /** + * Creates a new instance from the internal json string. + *

+ * This is used to provide an original/old value for change logging / persist listeners. + * This is only available for properties that have {@code @DbJson(keepSource=true)}. + */ + default Object get() { + return null; + } +} diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index 6388648c3..6c01811c5 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -3,7 +3,6 @@ package io.ebean.core.type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; -import io.ebean.bean.MutableHash; import io.ebean.text.StringFormatter; import io.ebean.text.StringParser; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index dfcea8094..fc5a8364f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonToken; import io.ebean.ValuePair; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebean.bean.PersistenceContext; import io.ebean.config.EncryptKey; import io.ebean.config.dbplatform.DbEncryptFunction; @@ -823,7 +823,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { /** * creates a mutableHash for the given JSON value. */ - public MutableHash createMutableHash(String json) { + public MutableValueInfo createMutableInfo(String json) { throw new UnsupportedOperationException(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index c7fba7b8b..fca7b790b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebean.core.type.DataReader; import io.ebean.core.type.ScalarType; import io.ebean.text.TextException; @@ -26,11 +26,11 @@ public class BeanPropertyJsonMapper extends BeanProperty { } @Override - public MutableHash createMutableHash(String json) { + public MutableValueInfo createMutableInfo(String json) { if (keepSource) { - return new JsonMutableHash(scalarType, json); + return new SourceMutableValue(scalarType, json); } else if (dirtyDetection) { - return new Md5MutableHash(scalarType, json); + return new ChecksumMutableValue(scalarType, json); } else { return NO_DIRTY_DETECTION; } @@ -42,9 +42,9 @@ public class BeanPropertyJsonMapper extends BeanProperty { */ @Override boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { - // dirty detection based on md5 hash of json content + // dirty detection based on json content or checksum of json content final String json = scalarType.format(value); - final MutableHash oldHash = ebi.mutableHash(propertyIndex); + final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex); if (oldHash == null || !oldHash.isEqualToJson(json)) { ebi.mutableContent(propertyIndex, json); // so we only convert to json once return true; @@ -60,8 +60,8 @@ public class BeanPropertyJsonMapper extends BeanProperty { setValue(bean, value); String json = reader.popJson(); if (json != null) { - final MutableHash hash = createMutableHash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + final MutableValueInfo hash = createMutableInfo(json); + bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); } } return value; @@ -72,18 +72,24 @@ public class BeanPropertyJsonMapper extends BeanProperty { } } - private static class Md5MutableHash implements MutableHash { + /** + * Hold checksum of json source content. + *

+ * Dirty detection based on checksum difference on json form. + * Does not support rebuilding 'oldValue' as no original json content. + */ + private static class ChecksumMutableValue implements MutableValueInfo { - private final String hash; private final ScalarType parent; + private final long checksum; - Md5MutableHash(ScalarType parent, String json) { + ChecksumMutableValue(ScalarType parent, String json) { this.parent = parent; - this.hash = hash(json); + this.checksum = checksum(json); } - private String hash(String json) { - return String.valueOf(Checksum.checksum(json)); + private long checksum(String json) { + return Checksum.checksum(json); } @Override @@ -93,22 +99,24 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override public boolean isEqualToJson(String json) { - return hash(json).equals(hash); + return checksum(json) == checksum; } @Override public Object get() { return null; // cannot create object from json } - } - private static class JsonMutableHash implements MutableHash { + /** + * Hold original json source content. This supports rebuilding the 'oldValue'. + */ + private static class SourceMutableValue implements MutableValueInfo { private final String originalJson; private final ScalarType parent; - JsonMutableHash(ScalarType parent, String json) { + SourceMutableValue(ScalarType parent, String json) { this.parent = parent; this.originalJson = json; } @@ -125,24 +133,24 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override public Object get() { + // rebuild the 'oldValue' for change log etc return parent.parse(originalJson); } - } /** - * No dirty detection on JSON content. + * No dirty detection on json content. */ - private static class NoDirtyDetection implements MutableHash { + private static class NoDirtyDetection implements MutableValueInfo { @Override public boolean isEqualToJson(String json) { - return true; + return true; // treat as not dirty } @Override public boolean isEqualToObject(Object obj) { - return true; + return true; // treat as not dirty } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java index e9de67a52..1cbb7eeca 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebean.bean.EntityBean; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebeaninternal.server.deploy.BeanProperty; import java.sql.SQLException; @@ -32,8 +32,8 @@ class BindablePropertyJsonInsert extends BindableProperty { } else { // on insert store hash and push json final String json = prop.format(value); - final MutableHash hash = prop.createMutableHash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + final MutableValueInfo hash = prop.createMutableInfo(json); + bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); request.pushJson(json); request.bind(value, prop); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java index ab281cf01..f939ae004 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebean.bean.EntityBean; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebeaninternal.server.deploy.BeanProperty; import java.sql.SQLException; @@ -28,8 +28,8 @@ class BindablePropertyJsonUpdate extends BindableProperty { } else { // on update store hash and push json final String json = bean._ebean_getIntercept().mutableContent(propertyIndex); - final MutableHash hash = prop.createMutableHash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + final MutableValueInfo hash = prop.createMutableInfo(json); + bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); request.pushJson(json); final Object value = prop.getValue(bean); request.bind(value, prop); From 991c6d6b5c30f76abe1758585cc19aebb171d896 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 13:30:15 +1200 Subject: [PATCH 29/87] No effective change - tidy FactoryProperty --- .../server/persist/dmlbind/BindablePropertyJsonUpdate.java | 3 +-- .../server/persist/dmlbind/FactoryProperty.java | 7 ++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java index f939ae004..5a94d0e0e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java @@ -31,8 +31,7 @@ class BindablePropertyJsonUpdate extends BindableProperty { final MutableValueInfo hash = prop.createMutableInfo(json); bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); request.pushJson(json); - final Object value = prop.getValue(bean); - request.bind(value, prop); + request.bind(prop.getValue(bean), prop); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java index 78ceb098b..d5c59daf3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/FactoryProperty.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import io.ebeaninternal.server.deploy.BeanPropertyJsonMapper; import io.ebeaninternal.server.persist.dml.DmlMode; /** @@ -23,14 +24,12 @@ class FactoryProperty { * Create a Bindable for the property given the mode and withLobs flag. */ public Bindable create(BeanProperty prop, DmlMode mode, boolean withLobs, boolean allowManyToOne) { - if (DmlMode.INSERT == mode && !prop.isDbInsertable()) { return null; } if (DmlMode.UPDATE == mode && !prop.isDbUpdatable()) { return null; } - if (prop.isLob() && !withLobs) { // Lob exclusion return null; @@ -38,12 +37,10 @@ class FactoryProperty { if (prop.isDbEncrypted()){ return new BindableEncryptedProperty(prop, bindEncryptDataFirst); } - if (allowManyToOne && prop instanceof BeanPropertyAssocOne) { return new BindableAssocOne((BeanPropertyAssocOne)prop); } - - if (prop.getScalarType().isJsonMapper()) { + if (prop instanceof BeanPropertyJsonMapper) { if (DmlMode.INSERT == mode) { return new BindablePropertyJsonInsert(prop); } else if (DmlMode.UPDATE == mode) { From 0d8e7c852b52bf599e8c225a871485adec56a56e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 15:32:06 +1200 Subject: [PATCH 30/87] Change Checksum to use CRC32 --- .../java/io/ebeaninternal/server/util/Checksum.java | 8 ++++---- .../io/ebeaninternal/server/util/ChecksumTest.java | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java b/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java index 87ee6e7e8..8295703c1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/util/Checksum.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.util; import java.nio.charset.StandardCharsets; -import java.util.zip.Adler32; +import java.util.zip.CRC32; /** * Compute a checksum for String content. Use when we desire cheaper option than MD5. @@ -12,9 +12,9 @@ public final class Checksum { * Return the checksum for the given String input. */ public static long checksum(String input) { - Adler32 adler32 = new Adler32(); + CRC32 checksum = new CRC32(); final byte[] bytes = input.getBytes(StandardCharsets.UTF_8); - adler32.update(bytes, 0, bytes.length); - return adler32.getValue(); + checksum.update(bytes, 0, bytes.length); + return checksum.getValue(); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java index 58342df80..9eceabb0a 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/util/ChecksumTest.java @@ -8,11 +8,16 @@ public class ChecksumTest { @Test public void checksum() { - final long val = Checksum.checksum("Hello world"); - assertThat(val).isEqualTo(413860925L); - + assertThat(val).isEqualTo(2346098258L); assertThat(Checksum.checksum("Hello world")).isEqualTo(val); assertThat(Checksum.checksum("hello world")).isNotEqualTo(val); } + + @Test + public void checksum_shortString() { + final long val0 = Checksum.checksum("2012-01-11"); + final long val1 = Checksum.checksum("2012-10-02"); + assertThat(val0).isNotEqualTo(val1); + } } From c66d248e963dc1854256f4206cea1c233596ef4a Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 15:42:16 +1200 Subject: [PATCH 31/87] Add MutableValueNext to replace mutableContent such that hash compute is only done once Adds MutableValueInfo.nextDirty() to replace the isEqualToJson() method. The next is computed once and stored. BindablePropertyJsonUpdate makes the .mutableNext(propertyIndex) call to move the next MutableValueInfo and return the json content. --- .../io/ebean/bean/EntityBeanIntercept.java | 34 +++--- .../java/io/ebean/bean/MutableValueInfo.java | 11 +- .../java/io/ebean/bean/MutableValueNext.java | 17 +++ .../server/deploy/BeanPropertyJsonMapper.java | 102 ++++++++++++++---- .../dmlbind/BindablePropertyJsonUpdate.java | 8 +- 5 files changed, 125 insertions(+), 47 deletions(-) create mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 99e9334c1..78d8e5f8c 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -105,7 +105,7 @@ public final class EntityBeanIntercept implements Serializable { * Holds json content determined at point of dirty check. * Stored here on dirty check such that we only convert to json once. */ - private String[] mutableContent; + private MutableValueNext[] mutableNext; /** * Create a intercept with a given entity. @@ -395,7 +395,7 @@ public final class EntityBeanIntercept implements Serializable { this.owner._ebean_setEmbeddedLoaded(); this.lazyLoadProperty = -1; this.origValues = null; - this.mutableContent = null; + this.mutableNext = null; for (int i = 0; i < flags.length; i++) { flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET); } @@ -1203,23 +1203,29 @@ public final class EntityBeanIntercept implements Serializable { } /** - * Return the [json] content of a mutable value. - */ - public String mutableContent(int propertyIndex) { - return mutableContent == null ? null : mutableContent[propertyIndex]; - } - - /** - * Set the [json] content of a mutable property. + * Dirty detection set the next mutable property content and info . *

* Set here as the mutable property dirty detection is based on json content comparison. * We only want to perform the json serialisation once so storing it here as part of * dirty detection so that we can get it back to bind in insert or update etc. */ - public void mutableContent(int propertyIndex, String content) { - if (mutableContent == null) { - mutableContent = new String[flags.length]; + public void mutableNext(int propertyIndex, MutableValueNext next) { + if (mutableNext == null) { + mutableNext = new MutableValueNext[flags.length]; } - mutableContent[propertyIndex] = content; + mutableNext[propertyIndex] = next; } + + /** + * Update the 'next' mutable info returning the content that was obtained via dirty detection. + */ + public String mutableNext(int propertyIndex) { + if (mutableNext == null) { + return null; + } + final MutableValueNext next = mutableNext[propertyIndex]; + mutableInfo(propertyIndex, next.info()); + return next.content(); + } + } diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java index de8cbee6d..0f9c223aa 100644 --- a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java +++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java @@ -12,12 +12,15 @@ package io.ebean.bean; public interface MutableValueInfo { /** - * Compares the given json to an internal value. Can be a hash/checksum comparison - * or a plain JSON string comparison (based on {@code @DbJson(keepSource)}). + * Compares the given json returning null if deemed unchanged or returning + * the MutableValueNext to use if deemed dirty/changed. + *

+ * Returning MutableValueNext allows an implementation based on hash/checksum + * to only perform that computation once. * - * @return true if the value is considered unchanged (when comparing in json form). + * @return Null if deemed unchanged or the MutableValueNext if deemed changed. */ - boolean isEqualToJson(String json); + MutableValueNext nextDirty(String json); /** * Compares the given object to an internal value. diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java new file mode 100644 index 000000000..401d3c223 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueNext.java @@ -0,0 +1,17 @@ +package io.ebean.bean; + +/** + * Represents a next value to use for mutable content properties (DbJson with jackson beans). + */ +public interface MutableValueNext { + + /** + * Return the next content to use. Provided such that we serialise to json once. + */ + String content(); + + /** + * Return the next MutableValueInfo to use after an update. + */ + MutableValueInfo info(); +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index fca7b790b..d3a683e48 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; +import io.ebean.bean.MutableValueNext; import io.ebean.bean.MutableValueInfo; import io.ebean.core.type.DataReader; import io.ebean.core.type.ScalarType; @@ -36,6 +37,19 @@ public class BeanPropertyJsonMapper extends BeanProperty { } } + /** + * Next when no prior MutableValueInfo. + */ + private MutableValueNext next(String json) { + if (keepSource) { + return new SourceMutableValue(scalarType, json); + } else if (dirtyDetection) { + return new NextPair(json, new ChecksumMutableValue(scalarType, json)); + } else { + throw new IllegalStateException("Never get here"); + } + } + /** * Return true if the mutable value is considered dirty. * This is only used for 'mutable' scalar types like hstore etc. @@ -43,10 +57,17 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { // dirty detection based on json content or checksum of json content + // only perform serialisation to json once final String json = scalarType.format(value); final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex); - if (oldHash == null || !oldHash.isEqualToJson(json)) { - ebi.mutableContent(propertyIndex, json); // so we only convert to json once + if (oldHash == null) { + ebi.mutableNext(propertyIndex, next(json)); + return true; + } + // only perform compute of checksum/hash once (if checksum based) + final MutableValueNext next = oldHash.nextDirty(json); + if (next != null) { + ebi.mutableNext(propertyIndex, next); return true; } return false; @@ -72,34 +93,59 @@ public class BeanPropertyJsonMapper extends BeanProperty { } } + private static final class NextPair implements MutableValueNext { + + private final String json; + private final MutableValueInfo next; + + NextPair(String json, MutableValueInfo next) { + this.json = json; + this.next = next; + } + + @Override + public String content() { + return json; + } + + @Override + public MutableValueInfo info() { + return next; + } + } + /** - * Hold checksum of json source content. + * Hold checksum of json source content to use for dirty detection. *

- * Dirty detection based on checksum difference on json form. * Does not support rebuilding 'oldValue' as no original json content. */ - private static class ChecksumMutableValue implements MutableValueInfo { + private static final class ChecksumMutableValue implements MutableValueInfo { private final ScalarType parent; private final long checksum; ChecksumMutableValue(ScalarType parent, String json) { this.parent = parent; - this.checksum = checksum(json); + this.checksum = Checksum.checksum(json); } - private long checksum(String json) { - return Checksum.checksum(json); + /** + * Create with pre-computed checksum. + */ + ChecksumMutableValue(ScalarType parent, long checksum) { + this.parent = parent; + this.checksum = checksum; + } + + @Override + public MutableValueNext nextDirty(String json) { + final long nextChecksum = Checksum.checksum(json); + return nextChecksum == checksum ? null : new NextPair(json, new ChecksumMutableValue(parent, nextChecksum)); } @Override public boolean isEqualToObject(Object obj) { - return isEqualToJson(parent.format(obj)); - } - - @Override - public boolean isEqualToJson(String json) { - return checksum(json) == checksum; + return Checksum.checksum(parent.format(obj)) == checksum; } @Override @@ -109,9 +155,9 @@ public class BeanPropertyJsonMapper extends BeanProperty { } /** - * Hold original json source content. This supports rebuilding the 'oldValue'. + * Hold json source content. This supports rebuilding the 'oldValue'. */ - private static class SourceMutableValue implements MutableValueInfo { + private static final class SourceMutableValue implements MutableValueInfo, MutableValueNext { private final String originalJson; private final ScalarType parent; @@ -122,13 +168,13 @@ public class BeanPropertyJsonMapper extends BeanProperty { } @Override - public boolean isEqualToObject(Object obj) { - return isEqualToJson(parent.format(obj)); + public MutableValueNext nextDirty(String json) { + return Objects.equals(originalJson, json) ? null : new SourceMutableValue(parent, json); } @Override - public boolean isEqualToJson(String json) { - return Objects.equals(originalJson, json); + public boolean isEqualToObject(Object obj) { + return Objects.equals(originalJson, parent.format(obj)); } @Override @@ -136,16 +182,26 @@ public class BeanPropertyJsonMapper extends BeanProperty { // rebuild the 'oldValue' for change log etc return parent.parse(originalJson); } + + @Override + public String content() { + return originalJson; + } + + @Override + public MutableValueInfo info() { + return this; + } } /** * No dirty detection on json content. */ - private static class NoDirtyDetection implements MutableValueInfo { + private static final class NoDirtyDetection implements MutableValueInfo { @Override - public boolean isEqualToJson(String json) { - return true; // treat as not dirty + public MutableValueNext nextDirty(String json) { + return null; // treat as not dirty } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java index 5a94d0e0e..cfa056a63 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java @@ -1,7 +1,6 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebean.bean.EntityBean; -import io.ebean.bean.MutableValueInfo; import io.ebeaninternal.server.deploy.BeanProperty; import java.sql.SQLException; @@ -26,11 +25,8 @@ class BindablePropertyJsonUpdate extends BindableProperty { if (bean == null) { request.bind(null, prop); } else { - // on update store hash and push json - final String json = bean._ebean_getIntercept().mutableContent(propertyIndex); - final MutableValueInfo hash = prop.createMutableInfo(json); - bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); - request.pushJson(json); + // update mutableInfo and push json + request.pushJson(bean._ebean_getIntercept().mutableNext(propertyIndex)); request.bind(prop.getValue(bean), prop); } } From 5aa8557168add6acf9bc7d016dc30eddb57c2e25 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 16:29:10 +1200 Subject: [PATCH 32/87] Update tests only - TestJacksonPlainBean with dirtyDetection = false property --- .../org/tests/model/json/EBasicPlain.java | 11 ++++++++ .../model/json/TestJacksonPlainBean.java | 26 ++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java b/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java index f3705aa16..401e66fa1 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java @@ -17,6 +17,9 @@ public class EBasicPlain { @DbJson(length = 500) PlainBean plainBean; + @DbJson(length = 500, dirtyDetection = false) + PlainBean plainBean2; + @Version long version; @@ -44,6 +47,14 @@ public class EBasicPlain { this.plainBean = plainBean; } + public PlainBean getPlainBean2() { + return plainBean2; + } + + public void setPlainBean2(PlainBean plainBean2) { + this.plainBean2 = plainBean2; + } + public long getVersion() { return version; } diff --git a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java index 0e3d22aa7..f77f2f8b6 100644 --- a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java +++ b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java @@ -16,25 +16,20 @@ public class TestJacksonPlainBean { DB.getDefault(); LoggedSqlCollector.start(); - PlainBean content = new PlainBean(); - content.setAlong(42); - content.setName("foo"); - + PlainBean content = new PlainBean("foo", 42); EBasicPlain bean = new EBasicPlain(); bean.setAttr("attr0"); bean.setPlainBean(content); - + bean.setPlainBean2(new PlainBean("bar", 27)); DB.save(bean); - expectedSql(0, "insert into ebasic_plain (attr, plain_bean, version) values (?,?,?)"); - + expectedSql(0, "insert into ebasic_plain (attr, plain_bean, plain_bean2, version) values (?,?,?,?)"); // inserted plainBean has not been mutated bean.setAttr("attr1"); DB.save(bean); expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?"); - // inserted plainBean has now been mutated content.setName("notFoo"); bean.setAttr("attr2"); @@ -50,17 +45,24 @@ public class TestJacksonPlainBean { DB.save(found); expectedSql(1, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?"); - - // update bean, mutate PlainBean only + // dirtyDetection = false, so not included in update + found.getPlainBean2().setName("Modification Ignored"); + // dirtyDetection = true, mutation detected plainBean.setName("mod2"); DB.save(found); expectedSql(0, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?"); - // update bean, not mutating PlainBean found.setAttr("attr3"); DB.save(found); - expectedSql(LoggedSqlCollector.stop(), 0, "update ebasic_plain set attr=?, version=? where id=? and version=?"); + expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?"); + + // dirtyDetection = false, set a new plainBean2 instance, included in update + found.setPlainBean2(new PlainBean("bar", 27)); + DB.save(found); + expectedSql( 0, "update ebasic_plain set plain_bean2=?, version=? where id=? and version=?"); + + LoggedSqlCollector.stop(); } private void expectedSql(int i, String s) { From 20becf951d02754f4003cb88a1b5aa1ea522de3c Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 23:11:42 +1200 Subject: [PATCH 33/87] Use MutationDetection replacing dirtyDetection and keepSource Also adds NoMutationDetection to support NONE --- ebean-api/pom.xml | 2 +- .../server/deploy/BeanPropertyJsonMapper.java | 46 ++++++------------- .../deploy/meta/DeployBeanProperty.java | 39 ++++------------ .../deploy/meta/DeployBeanPropertyLists.java | 42 ++--------------- .../server/deploy/parse/DeployUtil.java | 16 +++---- .../server/type/DefaultTypeManager.java | 3 +- .../type/ScalarTypeJsonObjectMapper.java | 35 +++++++++++++- .../org/tests/model/json/EBasicJsonList.java | 11 ++--- .../org/tests/model/json/EBasicPlain.java | 4 +- .../model/json/TestJacksonPlainBean.java | 5 +- 10 files changed, 76 insertions(+), 127 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 649f85822..503c2f2c5 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -50,7 +50,7 @@ io.ebean ebean-annotation - 7.1 + 7.2 diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index d3a683e48..f1efc39f7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -1,9 +1,10 @@ package io.ebeaninternal.server.deploy; +import io.ebean.annotation.MutationDetection; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.MutableValueNext; import io.ebean.bean.MutableValueInfo; +import io.ebean.bean.MutableValueNext; import io.ebean.core.type.DataReader; import io.ebean.core.type.ScalarType; import io.ebean.text.TextException; @@ -14,26 +15,24 @@ import javax.persistence.PersistenceException; import java.sql.SQLException; import java.util.Objects; +/** + * Handle json property with MutationDetection of SOURCE or HASH only. + */ public class BeanPropertyJsonMapper extends BeanProperty { - private static final NoDirtyDetection NO_DIRTY_DETECTION = new NoDirtyDetection(); - private final boolean dirtyDetection; - private final boolean keepSource; + private final boolean sourceDetection; public BeanPropertyJsonMapper(BeanDescriptor desc, DeployBeanProperty deployProp) { super(desc, deployProp); - this.dirtyDetection = deployProp.isDirtyDetection(); - this.keepSource = deployProp.isKeepSource(); + this.sourceDetection = deployProp.getMutationDetection() == MutationDetection.SOURCE; } @Override public MutableValueInfo createMutableInfo(String json) { - if (keepSource) { + if (sourceDetection) { return new SourceMutableValue(scalarType, json); - } else if (dirtyDetection) { - return new ChecksumMutableValue(scalarType, json); } else { - return NO_DIRTY_DETECTION; + return new ChecksumMutableValue(scalarType, json); } } @@ -41,22 +40,19 @@ public class BeanPropertyJsonMapper extends BeanProperty { * Next when no prior MutableValueInfo. */ private MutableValueNext next(String json) { - if (keepSource) { + if (sourceDetection) { return new SourceMutableValue(scalarType, json); - } else if (dirtyDetection) { - return new NextPair(json, new ChecksumMutableValue(scalarType, json)); } else { - throw new IllegalStateException("Never get here"); + return new NextPair(json, new ChecksumMutableValue(scalarType, json)); } } /** - * Return true if the mutable value is considered dirty. - * This is only used for 'mutable' scalar types like hstore etc. + * Return true if the json property is considered dirty. */ @Override boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { - // dirty detection based on json content or checksum of json content + // mutation detection based on json content or checksum of json content // only perform serialisation to json once final String json = scalarType.format(value); final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex); @@ -193,20 +189,4 @@ public class BeanPropertyJsonMapper extends BeanProperty { return this; } } - - /** - * No dirty detection on json content. - */ - private static final class NoDirtyDetection implements MutableValueInfo { - - @Override - public MutableValueNext nextDirty(String json) { - return null; // treat as not dirty - } - - @Override - public boolean isEqualToObject(Object obj) { - return true; // treat as not dirty - } - } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 060ce802f..3da06d559 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1,18 +1,6 @@ package io.ebeaninternal.server.deploy.meta; -import io.ebean.annotation.CreatedTimestamp; -import io.ebean.annotation.DocCode; -import io.ebean.annotation.DocProperty; -import io.ebean.annotation.DocSortable; -import io.ebean.annotation.Formula; -import io.ebean.annotation.Platform; -import io.ebean.annotation.SoftDelete; -import io.ebean.annotation.UpdatedTimestamp; -import io.ebean.annotation.WhenCreated; -import io.ebean.annotation.WhenModified; -import io.ebean.annotation.Where; -import io.ebean.annotation.WhoCreated; -import io.ebean.annotation.WhoModified; +import io.ebean.annotation.*; import io.ebean.config.ScalarTypeConverter; import io.ebean.config.dbplatform.DbDefaultValue; import io.ebean.config.dbplatform.DbEncrypt; @@ -39,7 +27,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Type; import java.sql.Types; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -109,8 +96,7 @@ public class DeployBeanProperty { private boolean jsonSerialize = true; private boolean jsonDeserialize = true; - private boolean dirtyDetection; - private boolean keepSource; + private MutationDetection mutationDetection; private boolean dbEncrypted; private DbEncryptFunction dbEncryptFunction; @@ -329,18 +315,15 @@ public class DeployBeanProperty { this.jsonDeserialize = jsonDeserialize; } - /** - * Return true if we should have JSON dirty detection on this property. - */ - public boolean isDirtyDetection() { - return dirtyDetection; + public MutationDetection getMutationDetection() { + if (mutationDetection == null) { + mutationDetection = MutationDetection.DEFAULT; + } + return mutationDetection; } - /** - * Return true if we should store source JSON content on this property. - */ - public boolean isKeepSource() { - return keepSource; + public void setMutationDetection(MutationDetection dirtyDetection) { + this.mutationDetection = dirtyDetection; } /** @@ -1221,8 +1204,4 @@ public class DeployBeanProperty { return scalarType != null && scalarType.isJsonMapper(); } - public void setJsonOptions(boolean dirtyDetection, boolean keepSource) { - this.dirtyDetection = dirtyDetection; - this.keepSource = keepSource; - } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java index 9843f8e5c..90503c93f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java @@ -22,47 +22,28 @@ public class DeployBeanPropertyLists { private static final NoopSetter NOOP_SETTER = new NoopSetter(); private BeanProperty versionProperty; - private BeanProperty unmappedJson; - private BeanProperty draft; - private BeanProperty draftDirty; - private BeanProperty tenant; - private final BeanDescriptor desc; - private final LinkedHashMap propertyMap; - private BeanProperty id; private final List local = new ArrayList<>(); - private final List mutable = new ArrayList<>(); - private final List> manys = new ArrayList<>(); - private final List nonManys = new ArrayList<>(); - private final List aggs = new ArrayList<>(); - private final List> ones = new ArrayList<>(); - private final List> onesImported = new ArrayList<>(); - private final List> embedded = new ArrayList<>(); - private final List baseScalar = new ArrayList<>(); - private final List transients = new ArrayList<>(); - private final List nonTransients = new ArrayList<>(); - private final BeanPropertyAssocOne unidirectional; private final BeanProperty orderColumn; - @SuppressWarnings({"unchecked"}) public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor desc, DeployBeanDescriptor deploy) { this.desc = desc; @@ -78,7 +59,7 @@ public class DeployBeanPropertyLists { this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null; DeployBeanPropertyAssocOne deployUnidirectional = deploy.getUnidirectional(); - this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne(owner, desc, deployUnidirectional); + this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne<>(owner, desc, deployUnidirectional); this.propertyMap = new LinkedHashMap<>(); @@ -119,7 +100,7 @@ public class DeployBeanPropertyLists { } if (orderColumn != null) { - orderColumn.setDeployOrder(order++); + orderColumn.setDeployOrder(order); allocateToList(orderColumn); propertyMap.put(orderColumn.getName(), orderColumn); } @@ -146,7 +127,6 @@ public class DeployBeanPropertyLists { } private void setImportedPrimaryKeysFor(DeployBeanDescriptor deploy, DeployBeanPropertyAssocOne id) { - for (DeployBeanProperty prop : id.getTargetDeploy().properties()) { DeployBeanProperty match = findImported(deploy, prop); if (match != null) { @@ -156,7 +136,6 @@ public class DeployBeanPropertyLists { } private DeployBeanProperty findImported(DeployBeanDescriptor deploy, DeployBeanProperty embeddedScalar) { - // the logical name and db column we are looking for a match on String name = embeddedScalar.getName(); String dbColumn = embeddedScalar.getDbColumn(); @@ -172,7 +151,6 @@ public class DeployBeanPropertyLists { return assocOne; } } - return null; } @@ -360,7 +338,6 @@ public class DeployBeanPropertyLists { } public BeanProperty getSoftDeleteProperty() { - for (BeanProperty prop : nonManys) { if (prop.isSoftDelete()) { return prop; @@ -377,7 +354,6 @@ public class DeployBeanPropertyLists { * Return the properties set via generated values on insert. */ public BeanProperty[] getGeneratedInsert() { - List list = new ArrayList<>(); for (BeanProperty prop : nonTransients) { GeneratedProperty gen = prop.getGeneratedProperty(); @@ -392,7 +368,6 @@ public class DeployBeanPropertyLists { * Return the properties set via generated values on update. */ public BeanProperty[] getGeneratedUpdate() { - List list = new ArrayList<>(); for (BeanProperty prop : nonTransients) { GeneratedProperty gen = prop.getGeneratedProperty(); @@ -430,8 +405,7 @@ public class DeployBeanPropertyLists { } } } - - return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[0]); + return list.toArray(new BeanPropertyAssocOne[0]); } private BeanPropertyAssocMany[] getMany2Many() { @@ -441,8 +415,7 @@ public class DeployBeanPropertyLists { list.add(prop); } } - - return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]); + return list.toArray(new BeanPropertyAssocMany[0]); } private BeanPropertyAssocMany[] getMany(Mode mode) { @@ -463,25 +436,20 @@ public class DeployBeanPropertyLists { break; } } - - return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]); + return list.toArray(new BeanPropertyAssocMany[0]); } @SuppressWarnings({"unchecked", "rawtypes"}) private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) { - if (deployProp instanceof DeployBeanPropertyAssocOne) { return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp); } - if (deployProp instanceof DeployBeanPropertySimpleCollection) { return new BeanPropertySimpleCollection(desc, (DeployBeanPropertySimpleCollection) deployProp); } - if (deployProp instanceof DeployBeanPropertyAssocMany) { return new BeanPropertyAssocMany(desc, (DeployBeanPropertyAssocMany) deployProp); } - if (deployProp.isJsonMapper()) { return new BeanPropertyJsonMapper(desc, deployProp); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java index 2245b0c4c..0792c66d5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -1,10 +1,6 @@ package io.ebeaninternal.server.deploy.parse; -import io.ebean.annotation.DbArray; -import io.ebean.annotation.DbJson; -import io.ebean.annotation.DbJsonB; -import io.ebean.annotation.DbJsonType; -import io.ebean.annotation.DbMap; +import io.ebean.annotation.*; import io.ebean.config.DatabaseConfig; import io.ebean.config.EncryptDeploy; import io.ebean.config.EncryptDeployManager; @@ -214,21 +210,21 @@ public class DeployUtil { */ void setDbJsonType(DeployBeanProperty prop, DbJson dbJsonType) { int dbType = getDbJsonStorage(dbJsonType.storage()); - setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.dirtyDetection(), dbJsonType.keepSource()); + setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.mutationDetection()); } void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) { - setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.dirtyDetection(), dbJsonB.keepSource()); + setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.mutationDetection()); } - private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, boolean dirtyDetection, boolean keepSource) { + private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, MutationDetection mutationDetection) { + prop.setDbType(dbType); + prop.setMutationDetection(mutationDetection); ScalarType scalarType = typeManager.getJsonScalarType(prop, dbType, dbLength); if (scalarType == null) { throw new RuntimeException("No ScalarType for JSON property [" + prop + "] [" + dbType + "]"); } - prop.setDbType(dbType); prop.setScalarType(scalarType); - prop.setJsonOptions(dirtyDetection, keepSource); if (dbType == Types.VARCHAR || dbLength > 0) { // determine the db column size int columnLength = (dbLength > 0) ? dbLength : DEFAULT_JSON_VARCHAR_LENGTH; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index befe46b20..28999e838 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -2,7 +2,6 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.introspect.AnnotatedField; import io.ebean.annotation.*; import io.ebean.config.DatabaseConfig; import io.ebean.config.JsonConfig; @@ -426,7 +425,7 @@ public final class DefaultTypeManager implements TypeManager { if (objectMapper == null) { throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping - Jackson ObjectMapper not present"); } - return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, (AnnotatedField) prop.getJacksonField(), dbType, docType); + return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, prop, dbType, docType); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 14425c6c9..7fb3aa4b4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import io.ebean.annotation.MutationDetection; import io.ebean.core.type.DataBinder; import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; @@ -15,6 +16,7 @@ import io.ebean.text.TextException; import io.ebeaninternal.json.ModifyAwareList; import io.ebeaninternal.json.ModifyAwareMap; import io.ebeaninternal.json.ModifyAwareSet; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; import javax.persistence.PersistenceException; import java.io.DataInput; @@ -34,8 +36,16 @@ class ScalarTypeJsonObjectMapper { /** * Create and return the appropriate ScalarType. */ - static ScalarType createTypeFor(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) { + static ScalarType createTypeFor(TypeJsonManager jsonManager, DeployBeanProperty prop, int dbType, DocPropertyType docType) { + AnnotatedField field = (AnnotatedField) prop.getJacksonField(); Class type = field.getRawType(); + + MutationDetection mode = prop.getMutationDetection(); + if (mode == MutationDetection.NONE) { + return new NoMutationDetection(jsonManager, field, dbType, type); + } else if (mode != MutationDetection.DEFAULT) { + return new GenericObject(jsonManager, field, dbType, type); + } if (Set.class.equals(type)) { return new OmSet(jsonManager, field, dbType, docType); } @@ -45,11 +55,32 @@ class ScalarTypeJsonObjectMapper { if (Map.class.equals(type)) { return new OmMap(jsonManager, field, dbType); } + prop.setMutationDetection(MutationDetection.HASH); return new GenericObject(jsonManager, field, dbType, type); } /** - * Maps any type (Object) using Jackson ObjectMapper. + * No mutation detection on this json property. + */ + private static class NoMutationDetection extends Base { + + NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class rawType) { + super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType); + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public boolean isDirty(Object value) { + return false; + } + } + + /** + * Supports HASH and SOURCE dirty detection modes. */ private static class GenericObject extends Base { diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java index 05d9807b5..d5ce2c309 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java @@ -7,12 +7,9 @@ import io.ebean.annotation.DbJsonType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Version; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; + +import static io.ebean.annotation.MutationDetection.SOURCE; @Entity public class EBasicJsonList { @@ -31,7 +28,7 @@ public class EBasicJsonList { @DbJson(length = 700) Map beanMap = new LinkedHashMap<>(); - @DbJson(length = 500, keepSource = true) // such that we can rebuild old values + @DbJson(length = 500, mutationDetection = SOURCE) // such that we can rebuild old values PlainBean plainBean; @DbJson(length = 50) diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java b/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java index 401e66fa1..051b4a344 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicPlain.java @@ -6,6 +6,8 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Version; +import static io.ebean.annotation.MutationDetection.NONE; + @Entity public class EBasicPlain { @@ -17,7 +19,7 @@ public class EBasicPlain { @DbJson(length = 500) PlainBean plainBean; - @DbJson(length = 500, dirtyDetection = false) + @DbJson(length = 500, mutationDetection = NONE) // only update when property set PlainBean plainBean2; @Version diff --git a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java index f77f2f8b6..c0fe82b26 100644 --- a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java +++ b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java @@ -60,7 +60,7 @@ public class TestJacksonPlainBean { // dirtyDetection = false, set a new plainBean2 instance, included in update found.setPlainBean2(new PlainBean("bar", 27)); DB.save(found); - expectedSql( 0, "update ebasic_plain set plain_bean2=?, version=? where id=? and version=?"); + expectedSql(0, "update ebasic_plain set plain_bean2=?, version=? where id=? and version=?"); LoggedSqlCollector.stop(); } @@ -69,7 +69,4 @@ public class TestJacksonPlainBean { assertThat(LoggedSqlCollector.current().get(i)).contains(s); } - private void expectedSql(List sql, int i, String s) { - assertThat(sql.get(i)).contains(s); - } } From 20e0eb102193d4b7f69f4fd45295e345055ca013 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 23:17:11 +1200 Subject: [PATCH 34/87] Re-enable TestDbJson_Jackson3 via adding mutationDetection = HASH to beanList property @DbJsonB(mutationDetection = HASH) List beanList; --- .../src/test/java/org/tests/json/TestDbJson_Jackson3.java | 7 ++----- .../src/test/java/org/tests/model/json/EBasicJsonList.java | 3 ++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index e8d8abdfd..2f74be109 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -161,10 +161,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { final EBasicJsonList found = DB.find(EBasicJsonList.class, bean.getId()); found.getBeanList().get(0).setName("p1-mod"); -// BeanState state = DB.getBeanState(found); -// assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList"); - // this test fails, because we have a OmList instead of a GenericObject - // TODO: Can/Should we enhance the @DbJson/@DbJsonB annotations with a property "dirtyDetection" - + BeanState state = DB.getBeanState(found); + assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList"); } } diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java index d5ce2c309..f76d282f7 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java @@ -9,6 +9,7 @@ import javax.persistence.Id; import javax.persistence.Version; import java.util.*; +import static io.ebean.annotation.MutationDetection.HASH; import static io.ebean.annotation.MutationDetection.SOURCE; @Entity @@ -22,7 +23,7 @@ public class EBasicJsonList { @DbJson(length = 700, name = "beans") Set beanSet; - @DbJsonB + @DbJsonB(mutationDetection = HASH) List beanList; @DbJson(length = 700) From 822fb7325a244abf3a390ed59bf9ca2ce755a7c8 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 23:37:29 +1200 Subject: [PATCH 35/87] Due to mutableNext handling, with checkMutableProperties() even when known dirty go into beanProperty.checkMutable() As per rPraml's PR and comment Due to handling of mutableNext we need checkMutableProperties() to call into what is now beanProperty.checkMutable() even when we already know it's dirty. --- .../java/io/ebeaninternal/server/deploy/BeanDescriptor.java | 4 ++-- .../java/io/ebeaninternal/server/deploy/BeanProperty.java | 4 ++-- .../ebeaninternal/server/deploy/BeanPropertyJsonMapper.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index d165b1489..8f0fea0f2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -3198,9 +3198,9 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { public void checkMutableProperties(EntityBeanIntercept ebi) { for (BeanProperty beanProperty : propertiesMutable) { int propertyIndex = beanProperty.getPropertyIndex(); - if (!ebi.isDirtyProperty(propertyIndex) && ebi.isLoadedProperty(propertyIndex)) { + if (ebi.isLoadedProperty(propertyIndex)) { Object value = beanProperty.getValue(ebi.getOwner()); - if (value != null && beanProperty.isDirtyValue(value, ebi)) { + if (beanProperty.checkMutable(value, ebi.isDirtyProperty(propertyIndex), ebi)) { // mutable scalar value which is considered dirty so mark // it as such so that it is included in an update ebi.markPropertyAsChanged(propertyIndex); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index fc5a8364f..a33d183ea 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -1018,8 +1018,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { * Return true if the mutable value is considered dirty. * This is only used for 'mutable' scalar types like hstore etc. */ - boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { - return scalarType.isDirty(value); + boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) { + return alreadyDirty || value != null && scalarType.isDirty(value); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index f1efc39f7..c9280c8c0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -51,7 +51,7 @@ public class BeanPropertyJsonMapper extends BeanProperty { * Return true if the json property is considered dirty. */ @Override - boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { + boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) { // mutation detection based on json content or checksum of json content // only perform serialisation to json once final String json = scalarType.format(value); From 1fad6e910c5f38d58590d5b4afff313f43bfccb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20P=C3=B6hler=20=28JPo=29?= Date: Fri, 19 Mar 2021 15:40:21 +0100 Subject: [PATCH 36/87] FIX: do not override empty (json) collections with null MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original: https://github.com/ebean-orm/ebean/commit/5551c35ec2496a9d62ab0afcaecbed739f17aa7c Signed-off-by: Jonas Pöhler (JPo) --- .../server/query/SqlBeanLoad.java | 18 ++++++++++++------ .../java/org/tests/json/TestDbJson_List.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java index ac1386e11..c1119e525 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlBeanLoad.java @@ -6,6 +6,9 @@ import io.ebeaninternal.api.SpiQuery.Mode; import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.deploy.DbReadContext; +import java.util.Collection; +import java.util.Map; + /** * Controls the loading of property data into a bean. *

@@ -70,12 +73,7 @@ public class SqlBeanLoad { try { Object dbVal = prop.read(ctx); - if (!refreshLoading) { - prop.setValue(bean, dbVal); - } else { - prop.setValueIntercept(bean, dbVal); - } - + load(prop, dbVal); return dbVal; } catch (Exception e) { @@ -89,6 +87,14 @@ public class SqlBeanLoad { * Load the given value into the property. */ public void load(BeanProperty target, Object dbVal) { + if (dbVal == null) { + Object current = target.getValue(bean); + if ((current instanceof Collection && ((Collection) current).isEmpty()) + || current instanceof Map && ((Map) current).isEmpty()) { + dbVal = current; // do not modify + } + } + if (!refreshLoading) { target.setValue(bean, dbVal); } else { diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java index 28fd78221..d00bb3541 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java @@ -221,4 +221,18 @@ public class TestDbJson_List extends BaseTestCase { DB.delete(bean); } + + @Test + public void testNullToEmpty() { + EBasicJsonList bean = new EBasicJsonList(); + bean.setFlags(null); + bean.setTags(null); + bean.setBeanMap(null); + DB.save(bean); + + bean = DB.find(EBasicJsonList.class) + .setId(bean.getId()).findOne(); + + assertThat(bean.getFlags()).isEmpty(); + } } From 1f0ac1f1c12ea54b8738f284061a9f27e7c196bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20P=C3=B6hler?= Date: Thu, 29 Jul 2021 16:50:08 +0200 Subject: [PATCH 37/87] ADD: Testcases for List and Map as well --- ebean-core/src/test/java/org/tests/json/TestDbJson_List.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java index d00bb3541..e2d04b657 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java @@ -234,5 +234,7 @@ public class TestDbJson_List extends BaseTestCase { .setId(bean.getId()).findOne(); assertThat(bean.getFlags()).isEmpty(); + assertThat(bean.getTags()).isEmpty(); + assertThat(bean.getBeanMap()).isEmpty(); } } From 777d3222220a5bda2c7fd5ffac730c9985d61614 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 14:51:38 +1200 Subject: [PATCH 38/87] #2279 - Remove _ebean_getMarker() from EntityBean interface - not actually required --- ebean-api/src/main/java/io/ebean/bean/EntityBean.java | 11 ----------- .../server/deploy/ElementEntityBean.java | 5 ----- ebean-core/src/test/resources/ebean.mf | 1 + 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBean.java b/ebean-api/src/main/java/io/ebean/bean/EntityBean.java index 03d1195c8..ee54991ad 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBean.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBean.java @@ -27,17 +27,6 @@ public interface EntityBean extends Serializable { throw new NotEnhancedException(); } - /** - * Return the enhancement marker value. - *

- * This is the class name of the enhanced class and used to check that all - * entity classes are enhanced (specifically not just a super class). - *

- */ - default String _ebean_getMarker() { - throw new NotEnhancedException(); - } - /** * Create and return a new entity bean instance. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java index 366ba5a97..49dfccdc8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementEntityBean.java @@ -28,11 +28,6 @@ class ElementEntityBean implements EntityBean { return properties[pos]; } - @Override - public String _ebean_getMarker() { - return null; - } - @Override public Object _ebean_newInstance() { return new ElementEntityBean(properties); diff --git a/ebean-core/src/test/resources/ebean.mf b/ebean-core/src/test/resources/ebean.mf index c18791604..7d34f1de5 100644 --- a/ebean-core/src/test/resources/ebean.mf +++ b/ebean-core/src/test/resources/ebean.mf @@ -3,4 +3,5 @@ profile-location: true entity-packages: org,misc transactional-packages: org querybean-packages: none +synthetic: false From a4708770175ef160426d8dd479a9f924656e2301 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 14:52:49 +1200 Subject: [PATCH 39/87] Update kotlin-querybean-generator pom to make kotlin in IntelliJ happy --- kotlin-querybean-generator/pom.xml | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 9b257b3b3..c3a487ecf 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 ebean-parent @@ -12,7 +13,7 @@ kotlin-querybean-generator - 1.5.0 + 1.5.30-M1 @@ -51,7 +52,7 @@ org.jetbrains.kotlin kotlin-stdlib-jdk8 ${kotlin.version} - test + provided @@ -111,10 +112,23 @@ + + compile + compile + + compile + + + + src/main/java + target/generated-sources/kapt/test + target/generated-sources/kaptKotlin/test + + + 1.8 - @@ -133,6 +147,13 @@ testCompile + + compile + compile + + compile + + 1.8 From 03004452ac7e0dc08c897e1807139e976bc5f9fe Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 14:53:32 +1200 Subject: [PATCH 40/87] ebean.mf set back to synthetic: true --- ebean-core/src/test/resources/ebean.mf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebean-core/src/test/resources/ebean.mf b/ebean-core/src/test/resources/ebean.mf index 7d34f1de5..fc2fd6902 100644 --- a/ebean-core/src/test/resources/ebean.mf +++ b/ebean-core/src/test/resources/ebean.mf @@ -3,5 +3,5 @@ profile-location: true entity-packages: org,misc transactional-packages: org querybean-packages: none -synthetic: false +synthetic: true From c43e2e695686d861f8d4982f52f28f73bd27d99f Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 15:34:54 +1200 Subject: [PATCH 41/87] Update TestDbJson_Jackson3 --- .../org/tests/json/TestDbJson_Jackson3.java | 62 +++++++++++++++++++ .../tests/model/json/EBasicJsonJackson3.java | 21 ++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index 2f74be109..145790457 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -164,4 +164,66 @@ public class TestDbJson_Jackson3 extends BaseTestCase { BeanState state = DB.getBeanState(found); assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList"); } + + @Test + public void update_with_differentDbJsonSettings() { + PlainBeanDirtyAware contentBean1 = new PlainBeanDirtyAware("x", 42); + PlainBeanDirtyAware contentBean2 = new PlainBeanDirtyAware("y", 43); + PlainBeanDirtyAware contentBean3 = new PlainBeanDirtyAware("z", 44); + + EBasicJsonJackson3 bean = new EBasicJsonJackson3(); + bean.setName("b1"); + bean.setPlainValue(contentBean1); + bean.setPlainValue2(contentBean2); + bean.setPlainValue3(contentBean3); + + BeanState state = DB.getBeanState(bean); + // a new bean is not considered as dirty (thus have no changed props) + assertThat(state.isDirty()).isFalse(); + assertThat(state.isNewOrDirty()).isTrue(); + assertThat(state.getChangedProps()).isEmpty(); + + bean.save(); + + bean = DB.find(EBasicJsonJackson3.class, bean.getId()); + state = DB.getBeanState(bean); + // a fresh loaded bean is also not considered as dirty + assertThat(state.isDirty()).isFalse(); + assertThat(state.isNewOrDirty()).isFalse(); + assertThat(state.getChangedProps()).isEmpty(); + + bean.getPlainValue().setName("a"); // has keepSource=true + + assertThat(state.isDirty()).isTrue(); + assertThat(state.getChangedProps()).containsExactly("plainValue"); + + bean.getPlainValue2().setName("b"); + assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainValue", "plainValue2"); + + bean.getPlainValue3().setName("c"); // has dirtyDetection = false + + Map dirtyValues = state.getDirtyValues(); + assertThat(dirtyValues).hasSize(2).containsKeys("plainValue", "plainValue2"); + + assertThat(dirtyValues.get("plainValue")).hasToString("name:a,name:x"); // SOURCE -> origValue present + assertThat(dirtyValues.get("plainValue2")).hasToString("name:b,null"); // without SOURCE no origValue present + + LoggedSql.start(); + bean.save(); + List sql = LoggedSql.collect(); + assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set plain_value=?, plain_value2=?, version=? where id=?"); + + bean = DB.find(EBasicJsonJackson3.class, bean.getId()); + LoggedSql.collect(); // ignore the select + assertThat(bean.getPlainValue().getName()).isEqualTo("a"); + assertThat(bean.getPlainValue2().getName()).isEqualTo("b"); + assertThat(bean.getPlainValue3().getName()).isEqualTo("z"); // value is not updated + + bean.getPlainValue3().setName("c"); + bean.getPlainValue3().setMarkedDirty(true); // This is ignored because it is MutationDetection.NONE + bean.save(); + // no update as plainValue3 has MutationDetection.NONE (ModifyAwareType = NONE isn't an expected combination to me) + assertThat(LoggedSql.collect()).isEmpty(); + LoggedSql.stop(); + } } diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java index 45375b516..cee12d9e7 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonJackson3.java @@ -2,11 +2,15 @@ package org.tests.model.json; import io.ebean.Model; import io.ebean.annotation.DbJson; +import io.ebean.annotation.MutationDetection; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Version; +import static io.ebean.annotation.MutationDetection.NONE; +import static io.ebean.annotation.MutationDetection.SOURCE; + @Entity public class EBasicJsonJackson3 extends Model { @@ -15,12 +19,15 @@ public class EBasicJsonJackson3 extends Model { String name; - @DbJson(length = 500) + @DbJson(length = 500, mutationDetection = SOURCE) PlainBeanDirtyAware plainValue; @DbJson(length = 500) PlainBeanDirtyAware plainValue2; - + + @DbJson(length = 500, mutationDetection = NONE) + PlainBeanDirtyAware plainValue3; + @Version long version; @@ -55,7 +62,15 @@ public class EBasicJsonJackson3 extends Model { public void setPlainValue2(PlainBeanDirtyAware plainValue2) { this.plainValue2 = plainValue2; } - + + public PlainBeanDirtyAware getPlainValue3() { + return plainValue3; + } + + public void setPlainValue3(PlainBeanDirtyAware plainValue3) { + this.plainValue3 = plainValue3; + } + public long getVersion() { return version; } From ea741d01699d107f7f88fed1057c91abe2d578c4 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 15:41:52 +1200 Subject: [PATCH 42/87] Update TestDbJson_Jackson3 with comments --- .../src/test/java/org/tests/json/TestDbJson_Jackson3.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index 145790457..ac10f2a16 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -66,7 +66,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { found.setName("b1-mod2"); found.getPlainValue().setName("b"); - found.getPlainValue().setMarkedDirty(true); + // found.getPlainValue().setMarkedDirty(true); // Irrelevant for SOURCE or HASH based mutation detection found.save(); @@ -192,7 +192,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { assertThat(state.isNewOrDirty()).isFalse(); assertThat(state.getChangedProps()).isEmpty(); - bean.getPlainValue().setName("a"); // has keepSource=true + bean.getPlainValue().setName("a"); // has SOURCE assertThat(state.isDirty()).isTrue(); assertThat(state.getChangedProps()).containsExactly("plainValue"); @@ -200,7 +200,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { bean.getPlainValue2().setName("b"); assertThat(state.getChangedProps()).containsExactlyInAnyOrder("plainValue", "plainValue2"); - bean.getPlainValue3().setName("c"); // has dirtyDetection = false + bean.getPlainValue3().setName("c"); // has mutationDetection = NONE Map dirtyValues = state.getDirtyValues(); assertThat(dirtyValues).hasSize(2).containsKeys("plainValue", "plainValue2"); From ca1636ff9edb0c22177875af8d5f26643ccba83c Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 17:16:05 +1200 Subject: [PATCH 43/87] Fix when json/jackson bean inserted as null and not changed Expectation is that it is not included in update (still null, no change) --- .../server/deploy/BeanPropertyJsonMapper.java | 3 +++ .../model/json/TestJacksonPlainBean.java | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index c9280c8c0..bc7392287 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -57,6 +57,9 @@ public class BeanPropertyJsonMapper extends BeanProperty { final String json = scalarType.format(value); final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex); if (oldHash == null) { + if (value == null) { + return false; // no change, still null + } ebi.mutableNext(propertyIndex, next(json)); return true; } diff --git a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java index c0fe82b26..24f0a35f8 100644 --- a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java +++ b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java @@ -10,6 +10,31 @@ import static org.assertj.core.api.Assertions.assertThat; public class TestJacksonPlainBean { + @Test + public void insertNullStayNull() { + + // insert with jackson beans as null + EBasicPlain bean = new EBasicPlain(); + bean.setAttr("n0"); + DB.save(bean); + + LoggedSqlCollector.start(); + bean.setAttr("n1"); + DB.save(bean); + expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?"); + + bean.setPlainBean(new PlainBean("x", 1)); + DB.save(bean); + expectedSql(0, "update ebasic_plain set plain_bean=?, version=? where id=? and version=?"); + + final EBasicPlain found = DB.find(EBasicPlain.class, bean.getId()); + found.setAttr("n2"); + DB.save(found); + expectedSql(1, "update ebasic_plain set attr=?, version=? where id=? and version=?"); + + LoggedSqlCollector.stop(); + } + @Test public void insertUpdate() { From 7bd7640c631e586c953439ec60104e66baa52330 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 17:18:28 +1200 Subject: [PATCH 44/87] Update test TestDbJson_Jackson3 showing HASH mode is used even on ModifyAwareType --- .../src/test/java/org/tests/json/TestDbJson_Jackson3.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index ac10f2a16..bc89a0eb5 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -224,6 +224,12 @@ public class TestDbJson_Jackson3 extends BaseTestCase { bean.save(); // no update as plainValue3 has MutationDetection.NONE (ModifyAwareType = NONE isn't an expected combination to me) assertThat(LoggedSql.collect()).isEmpty(); + + bean.getPlainValue2().setName("b2"); // effectively HASH mode mutation detection + bean.save(); + sql = LoggedSql.collect(); + assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set plain_value2=?, version=? where id=? and version=?"); + LoggedSql.stop(); } } From d9f7531e81ee29459aab29386cfb6eb34baf637d Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 17:26:46 +1200 Subject: [PATCH 45/87] Tidy tests only - TestDbJson_Jackson3 TestJacksonPlainBean --- .../org/tests/json/TestDbJson_Jackson3.java | 33 ++++++++----------- .../model/json/TestJacksonPlainBean.java | 14 ++++---- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index bc89a0eb5..c1cc3c40d 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -59,20 +59,15 @@ public class TestDbJson_Jackson3 extends BaseTestCase { LoggedSql.start(); found.save(); - - List sql = LoggedSql.collect(); - assertThat(sql).hasSize(1); - assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, version=? where id=? and version=?"); + expectedSql(0, "update ebasic_json_jackson3 set name=?, version=? where id=? and version=?"); found.setName("b1-mod2"); found.getPlainValue().setName("b"); // found.getPlainValue().setMarkedDirty(true); // Irrelevant for SOURCE or HASH based mutation detection found.save(); - - sql = LoggedSql.stop(); - assertThat(sql).hasSize(1); - assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, plain_value=?, version=? where id=? and version=?"); + expectedSql(0, "update ebasic_json_jackson3 set name=?, plain_value=?, version=? where id=? and version=?"); + LoggedSql.stop(); final EBasicJsonJackson3 found2 = DB.find(EBasicJsonJackson3.class, bean.getId()); @@ -112,20 +107,16 @@ public class TestDbJson_Jackson3 extends BaseTestCase { LoggedSql.start(); DB.save(found); - List sql = LoggedSql.stop(); - assertThat(sql).hasSize(1); // plain_bean=?, no longer included with MD5 dirty detection - assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_list=?, version=? where id=?"); + expectedSql(0, "update ebasic_json_list set name=?, bean_list=?, version=? where id=?"); assertThat(EBasicJsonListPersistController.updatedValues.entrySet()) .extracting(Map.Entry::toString) .containsExactlyInAnyOrder("beanList=null,[name:a]","name=p1-mod,p1","version=2,1"); - assertThat(DB.getBeanState(found).isDirty()).isFalse(); found.getPlainBean().setName("b"); - assertThat(DB.getBeanState(found).isDirty()).isTrue(); state = DB.getBeanState(found); @@ -138,14 +129,14 @@ public class TestDbJson_Jackson3 extends BaseTestCase { LoggedSql.start(); DB.save(found); - sql = LoggedSql.stop(); - assertThat(sql).hasSize(1); // plain_bean=?, no longer included with MD5 dirty detection - assertThat(sql.get(0)).contains("update ebasic_json_list set plain_bean=?, version=? where id=?"); + expectedSql(0, "update ebasic_json_list set plain_bean=?, version=? where id=?"); assertThat(EBasicJsonListPersistController.updatedValues.entrySet()) .extracting(Map.Entry::toString) .containsExactlyInAnyOrder("plainBean=name:b,name:a", "version=3,2"); + + LoggedSql.stop(); } @Test @@ -210,8 +201,7 @@ public class TestDbJson_Jackson3 extends BaseTestCase { LoggedSql.start(); bean.save(); - List sql = LoggedSql.collect(); - assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set plain_value=?, plain_value2=?, version=? where id=?"); + expectedSql(0, "update ebasic_json_jackson3 set plain_value=?, plain_value2=?, version=? where id=?"); bean = DB.find(EBasicJsonJackson3.class, bean.getId()); LoggedSql.collect(); // ignore the select @@ -227,9 +217,12 @@ public class TestDbJson_Jackson3 extends BaseTestCase { bean.getPlainValue2().setName("b2"); // effectively HASH mode mutation detection bean.save(); - sql = LoggedSql.collect(); - assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set plain_value2=?, version=? where id=? and version=?"); + expectedSql(0, "update ebasic_json_jackson3 set plain_value2=?, version=? where id=? and version=?"); LoggedSql.stop(); } + + private void expectedSql(int i, String s) { + assertThat(LoggedSql.collect().get(i)).contains(s); + } } diff --git a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java index 24f0a35f8..b2237731d 100644 --- a/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java +++ b/ebean-core/src/test/java/org/tests/model/json/TestJacksonPlainBean.java @@ -1,11 +1,9 @@ package org.tests.model.json; import io.ebean.DB; -import org.ebeantest.LoggedSqlCollector; +import io.ebeantest.LoggedSql; import org.junit.Test; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; public class TestJacksonPlainBean { @@ -18,7 +16,7 @@ public class TestJacksonPlainBean { bean.setAttr("n0"); DB.save(bean); - LoggedSqlCollector.start(); + LoggedSql.start(); bean.setAttr("n1"); DB.save(bean); expectedSql(0, "update ebasic_plain set attr=?, version=? where id=? and version=?"); @@ -32,14 +30,14 @@ public class TestJacksonPlainBean { DB.save(found); expectedSql(1, "update ebasic_plain set attr=?, version=? where id=? and version=?"); - LoggedSqlCollector.stop(); + LoggedSql.stop(); } @Test public void insertUpdate() { DB.getDefault(); - LoggedSqlCollector.start(); + LoggedSql.start(); PlainBean content = new PlainBean("foo", 42); EBasicPlain bean = new EBasicPlain(); @@ -87,11 +85,11 @@ public class TestJacksonPlainBean { DB.save(found); expectedSql(0, "update ebasic_plain set plain_bean2=?, version=? where id=? and version=?"); - LoggedSqlCollector.stop(); + LoggedSql.stop(); } private void expectedSql(int i, String s) { - assertThat(LoggedSqlCollector.current().get(i)).contains(s); + assertThat(LoggedSql.collect().get(i)).contains(s); } } From ceea705a76a8eb72132c6ec1019e2eb833f2e166 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 20:20:03 +1200 Subject: [PATCH 46/87] Treat json Jackson collections the same wrt mutation detection --- .../java/io/ebean/config/DatabaseConfig.java | 27 ----- .../server/type/DefaultTypeManager.java | 6 +- .../server/type/InitObjectMapper.java | 22 ++++ .../type/ScalarTypeJsonObjectMapper.java | 102 ++---------------- .../server/type/TypeJsonManager.java | 46 +------- .../io/ebean/config/ServerConfigTest.java | 4 - .../java/org/tests/json/TestDbJson_List.java | 14 +-- .../org/tests/model/json/EBasicJsonList.java | 1 + 8 files changed, 41 insertions(+), 181 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/type/InitObjectMapper.java diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java index bc3ae587f..476007ebe 100644 --- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java +++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java @@ -193,12 +193,6 @@ public class DatabaseConfig { */ private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL; - /** - * When true then by default DbJson beans are assumed to be dirty. - * I believe we want to change this default to false in the future. - */ - private boolean jsonDirtyByDefault = true; - /** * The database platform name. Used to imply a DatabasePlatform to use. */ @@ -743,26 +737,6 @@ public class DatabaseConfig { this.jsonInclude = jsonInclude; } - /** - * Return true if DbJson beans are assumed dirty by default. - *

- * That is, when true beans that do not implement ModifyAwareType are by - * default assumed to be dirty and included in updates. - */ - public boolean isJsonDirtyByDefault() { - return jsonDirtyByDefault; - } - - /** - * Set to false if we want DbJson beans to not be assumed to be dirty. - *

- * That is, when true beans that do not implement ModifyAwareType are by - * default assumed to be dirty and included in updates. - */ - public void setJsonDirtyByDefault(boolean jsonDirtyByDefault) { - this.jsonDirtyByDefault = jsonDirtyByDefault; - } - /** * Return the name of the Database. */ @@ -2935,7 +2909,6 @@ public class DatabaseConfig { jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude); jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime); jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate); - jsonDirtyByDefault = p.getBoolean("jsonDirtyByDefault", jsonDirtyByDefault); runMigration = p.getBoolean("migration.run", runMigration); ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index 28999e838..8ffb15372 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -134,7 +134,7 @@ public final class DefaultTypeManager implements TypeManager { this.postgres = isPostgres(config.getDatabasePlatform()); this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent(); this.objectMapper = (objectMapperPresent) ? initObjectMapper(config) : null; - this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.isJsonDirtyByDefault()) : null; + this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper) : null; this.extraTypeFactory = new DefaultTypeFactory(config); this.arrayTypeListFactory = arrayTypeListFactory(config.getDatabasePlatform()); this.arrayTypeSetFactory = arrayTypeSetFactory(config.getDatabasePlatform()); @@ -556,7 +556,7 @@ public final class DefaultTypeManager implements TypeManager { // no override or further mapping required return scalarType; } - ScalarTypeEnum scalarEnum = (ScalarTypeEnum)scalarType; + ScalarTypeEnum scalarEnum = (ScalarTypeEnum) scalarType; if (scalarEnum != null && !scalarEnum.isOverrideBy(type)) { if (type != null && !scalarEnum.isCompatible(type)) { throw new IllegalStateException("Error mapping Enum type:" + enumType + " It is mapped using 2 different modes when only one is supported (ORDINAL, STRING or an Ebean mapping)"); @@ -673,7 +673,7 @@ public final class DefaultTypeManager implements TypeManager { private Object initObjectMapper(DatabaseConfig config) { Object objectMapper = config.getObjectMapper(); if (objectMapper == null) { - objectMapper = new ObjectMapper(); + objectMapper = InitObjectMapper.init(); config.setObjectMapper(objectMapper); } return objectMapper; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/InitObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/InitObjectMapper.java new file mode 100644 index 000000000..90a86ec79 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/InitObjectMapper.java @@ -0,0 +1,22 @@ +package io.ebeaninternal.server.type; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Initialise the Jackson ObjectMapper. + */ +class InitObjectMapper { + + /** + * Create and return the default ObjectMapper. + */ + static Object init() { + SimpleModule module = new SimpleModule(); + module.addAbstractTypeMapping(Set.class, LinkedHashSet.class); + return new ObjectMapper().registerModule(module); + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 7fb3aa4b4..334f4b232 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -13,9 +13,6 @@ import io.ebean.core.type.DataReader; import io.ebean.core.type.DocPropertyType; import io.ebean.core.type.ScalarType; import io.ebean.text.TextException; -import io.ebeaninternal.json.ModifyAwareList; -import io.ebeaninternal.json.ModifyAwareMap; -import io.ebeaninternal.json.ModifyAwareSet; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; import javax.persistence.PersistenceException; @@ -24,9 +21,6 @@ import java.io.DataOutput; import java.io.IOException; import java.sql.SQLException; import java.sql.Types; -import java.util.List; -import java.util.Map; -import java.util.Set; /** * Supports @DbJson properties using Jackson ObjectMapper. @@ -38,25 +32,14 @@ class ScalarTypeJsonObjectMapper { */ static ScalarType createTypeFor(TypeJsonManager jsonManager, DeployBeanProperty prop, int dbType, DocPropertyType docType) { AnnotatedField field = (AnnotatedField) prop.getJacksonField(); - Class type = field.getRawType(); - MutationDetection mode = prop.getMutationDetection(); if (mode == MutationDetection.NONE) { - return new NoMutationDetection(jsonManager, field, dbType, type); + return new NoMutationDetection(jsonManager, field, dbType, docType); } else if (mode != MutationDetection.DEFAULT) { - return new GenericObject(jsonManager, field, dbType, type); - } - if (Set.class.equals(type)) { - return new OmSet(jsonManager, field, dbType, docType); - } - if (List.class.equals(type)) { - return new OmList(jsonManager, field, dbType, docType); - } - if (Map.class.equals(type)) { - return new OmMap(jsonManager, field, dbType); + return new GenericObject(jsonManager, field, dbType, docType); } prop.setMutationDetection(MutationDetection.HASH); - return new GenericObject(jsonManager, field, dbType, type); + return new GenericObject(jsonManager, field, dbType, docType); } /** @@ -64,8 +47,8 @@ class ScalarTypeJsonObjectMapper { */ private static class NoMutationDetection extends Base { - NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class rawType) { - super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType); + NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) { + super(Object.class, jsonManager, field, dbType, docType); } @Override @@ -84,8 +67,8 @@ class ScalarTypeJsonObjectMapper { */ private static class GenericObject extends Base { - GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class rawType) { - super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType); + GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) { + super(Object.class, jsonManager, field, dbType, docType); } @Override @@ -128,60 +111,6 @@ class ScalarTypeJsonObjectMapper { } } - /** - * Type for Sets wrapping the ObjectMapper Set as a ModifyAwareSet. - */ - @SuppressWarnings("rawtypes") - private static class OmSet extends Base { - - OmSet(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) { - super(Set.class, jsonManager, field, dbType, docType); - } - - @Override - @SuppressWarnings("unchecked") - public Set read(DataReader reader) throws SQLException { - Set value = super.read(reader); - return value == null ? null : new ModifyAwareSet(value); - } - } - - /** - * Type for Lists wrapping the ObjectMapper List as a ModifyAwareList. - */ - @SuppressWarnings("rawtypes") - private static class OmList extends Base { - - OmList(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) { - super(List.class, jsonManager, field, dbType, docType); - } - - @Override - @SuppressWarnings("unchecked") - public List read(DataReader reader) throws SQLException { - List value = super.read(reader); - return value == null ? null : new ModifyAwareList(value); - } - } - - /** - * Type for Map wrapping the ObjectMapper Map as a ModifyAwareMap. - */ - @SuppressWarnings("rawtypes") - private static class OmMap extends Base { - - OmMap(TypeJsonManager jsonManager, AnnotatedField field, int dbType) { - super(Map.class, jsonManager, field, dbType, DocPropertyType.OBJECT); - } - - @Override - @SuppressWarnings("unchecked") - public Map read(DataReader reader) throws SQLException { - Map value = super.read(reader); - return value == null ? null : new ModifyAwareMap(value); - } - } - /** * ScalarType that uses Jackson ObjectMapper to marshall/unmarshall to/from JSON * and storing them in one of JSON, JSONB, VARCHAR, CLOB or BLOB. @@ -193,39 +122,22 @@ class ScalarTypeJsonObjectMapper { protected final JavaType deserType; protected final String pgType; private final DocPropertyType docType; - private final TypeJsonManager.DirtyHandler dirtyHandler; Base(Class cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) { - this(cls, jsonManager, field, dbType, docType, cls); - } - - Base(Class cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType, Class rawType) { super(cls, false, dbType); this.objectReader = jsonManager.objectMapper(); this.pgType = jsonManager.postgresType(dbType); this.docType = docType; - this.dirtyHandler = jsonManager.dirtyHandler(cls, rawType); final JacksonTypeHelper helper = new JacksonTypeHelper(field, objectReader); this.deserType = helper.type(); this.objectWriter = helper.objectWriter(); } - /** - * Consider as a mutable type. Use the isDirty() method to check for dirty state. - */ @Override public boolean isMutable() { return true; } - /** - * Return true if the value should be considered dirty (and included in an update). - */ - @Override - public boolean isDirty(Object value) { - return dirtyHandler.isDirty(value); - } - @Override public T read(DataReader reader) throws SQLException { String json = reader.getString(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java index baea26dec..9cc0b5f0d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java @@ -2,25 +2,16 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.databind.ObjectMapper; import io.ebean.ModifyAwareType; -import io.ebean.config.DatabaseConfig; import io.ebean.config.dbplatform.DbPlatformType; class TypeJsonManager { - interface DirtyHandler { - boolean isDirty(Object value); - } - private final boolean postgres; private final ObjectMapper objectMapper; - private final DirtyHandler defaultHandler; - private final DirtyHandler modifyAwareHandler; - TypeJsonManager(boolean postgres, Object objectMapper, boolean defaultDirty) { + TypeJsonManager(boolean postgres, Object objectMapper) { this.postgres = postgres; this.objectMapper = (ObjectMapper) objectMapper; - this.defaultHandler = new DefaultHandler(defaultDirty); - this.modifyAwareHandler = new ModifyAwareHandler(); } ObjectMapper objectMapper() { @@ -39,17 +30,6 @@ class TypeJsonManager { return null; } - /** - * Return the DirtyHandler to use. - */ - DirtyHandler dirtyHandler(Class cls, Class rawType) { - if (!Object.class.equals(cls) || ModifyAwareType.class.isAssignableFrom(rawType)) { - // Set, List and Map are modify aware - return modifyAwareHandler; - } - return defaultHandler; - } - /** * Return true if the value should be considered dirty (and included in an update). */ @@ -71,28 +51,4 @@ class TypeJsonManager { } } - static final class ModifyAwareHandler implements DirtyHandler { - @Override - public boolean isDirty(Object value) { - return checkModifyAware(value); - } - } - - /** - * Effectively constant based on {@link DatabaseConfig#isJsonDirtyByDefault()} - */ - static final class DefaultHandler implements DirtyHandler { - - private final boolean dirty; - - DefaultHandler(boolean dirty) { - this.dirty = dirty; - } - - @Override - public boolean isDirty(Object value) { - return dirty; - } - } - } diff --git a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java b/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java index dde2cb216..55eb1f06e 100644 --- a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java +++ b/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java @@ -104,9 +104,6 @@ public class ServerConfigTest { assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid()); assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime()); assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate()); - assertFalse(serverConfig.isJsonDirtyByDefault()); - serverConfig.setJsonDirtyByDefault(true); - assertTrue(serverConfig.isJsonDirtyByDefault()); assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions()); @@ -159,7 +156,6 @@ public class ServerConfigTest { assertFalse(serverConfig.isIdGeneratorAutomatic()); assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime()); assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate()); - assertTrue(serverConfig.isJsonDirtyByDefault()); assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation()); assertTrue(serverConfig.isAutoLoadModuleInfo()); diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java index f43d6f23c..13e85a160 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java @@ -76,10 +76,11 @@ public class TestDbJson_List extends BaseTestCase { update_when_dirty(); update_when_dirty_flags(); update_when_dirty_SetListMap(); + + DB.delete(found); } - //@Test//(dependsOnMethods = "insert") - public void json_parse_format() { + private void json_parse_format() { String asJson = DB.json().toJson(found); assertThat(asJson).contains("\"tags\":[\"one\",\"two\"]"); @@ -104,8 +105,7 @@ public class TestDbJson_List extends BaseTestCase { assertThat(fromJson.getBeanMap()).hasSize(2); } - //@Test//(dependsOnMethods = "insert") - public void update_when_notDirty() { + private void update_when_notDirty() { found.setName("mod"); LoggedSqlCollector.start(); @@ -117,7 +117,7 @@ public class TestDbJson_List extends BaseTestCase { assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, version=? where"); } - public void update_when_dirty() { + private void update_when_dirty() { //found.setName("modAgain"); found.getTags().add("three"); @@ -131,7 +131,7 @@ public class TestDbJson_List extends BaseTestCase { assertSql(sql.get(0)).contains("update ebasic_json_list set tags=?, version=? where id=? and version=?"); } - public void update_when_dirty_flags() { + private void update_when_dirty_flags() { //found.setName("modAgain"); found.getFlags().remove(42L); @@ -145,7 +145,7 @@ public class TestDbJson_List extends BaseTestCase { assertSql(sql.get(0)).contains("update ebasic_json_list set flags=?, version=? where id=? and version=?;"); } - public void update_when_dirty_SetListMap() { + private void update_when_dirty_SetListMap() { //found.setName("modAgain"); found.getBeanSet().clear(); diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java index f76d282f7..885e85bf8 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonList.java @@ -20,6 +20,7 @@ public class EBasicJsonList { String name; + // @JsonDeserialize(as=LinkedHashSet.class) @DbJson(length = 700, name = "beans") Set beanSet; From 2691221168c83dc155fb0cd401f7c58cef25576d Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 22:53:51 +1200 Subject: [PATCH 47/87] #2278 - Do not override empty json collections, when database value is null --- .../ebeaninternal/server/deploy/BeanProperty.java | 13 +++++++++++++ .../server/deploy/BeanPropertyJsonMapper.java | 3 +++ .../test/java/org/tests/json/TestDbJson_List.java | 3 +-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index a33d183ea..d564a1f7a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -50,6 +50,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.sql.SQLException; import java.sql.Types; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -623,9 +624,21 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { return scalarType.read(reader); } + protected Object checkForEmpty(EntityBean bean) { + final Object value = getValue(bean); + if (value instanceof Collection && ((Collection) value).isEmpty() + || value instanceof Map && ((Map) value).isEmpty()) { + return value; + } + return null; + } + public Object readSet(DataReader reader, EntityBean bean) throws SQLException { try { Object value = scalarType.read(reader); + if (value == null) { + value = checkForEmpty(bean); + } if (bean != null) { setValue(bean, value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index bc7392287..0b7cdebbc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -76,6 +76,9 @@ public class BeanPropertyJsonMapper extends BeanProperty { public Object readSet(DataReader reader, EntityBean bean) throws SQLException { try { Object value = scalarType.read(reader); + if (value == null) { + value = checkForEmpty(bean); + } if (bean != null) { setValue(bean, value); String json = reader.popJson(); diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java index 8378888b2..647aa6eff 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_List.java @@ -234,8 +234,7 @@ public class TestDbJson_List extends BaseTestCase { bean.setBeanMap(null); DB.save(bean); - bean = DB.find(EBasicJsonList.class) - .setId(bean.getId()).findOne(); + bean = DB.find(EBasicJsonList.class).setId(bean.getId()).findOne(); assertThat(bean.getFlags()).isEmpty(); assertThat(bean.getTags()).isEmpty(); From 443e826f618b0ea35b53fb5b4b004d52912d6e4e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 23:22:06 +1200 Subject: [PATCH 48/87] #2278 - Embedded bean override with DbJson property --- .../deploy/BeanEmbeddedMetaFactory.java | 6 +----- .../server/deploy/BeanProperty.java | 11 +++++----- .../server/deploy/BeanPropertyAssocOne.java | 20 +++++++++---------- .../server/deploy/BeanPropertyJsonMapper.java | 10 ++++++++++ .../org/tests/model/embedded/EAddress.java | 14 +++++++++++++ .../org/tests/model/embedded/EPerson.java | 3 ++- .../nativesql/TestNativeWithEmbedded.java | 11 ++++++---- 7 files changed, 49 insertions(+), 26 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java index af6e7b9e4..5def0dc64 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java @@ -42,11 +42,7 @@ class BeanEmbeddedMetaFactory { int dbScale = dbScale(column, sourceProperties[i]); String colDefn = getDbColumnDefn(column, sourceProperties[i]); BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn, dbNullable, dbLength, dbScale, colDefn); - if (sourceProperties[i] instanceof BeanPropertyAssocOne) { - embeddedProperties[i] = new BeanPropertyAssocOne((BeanPropertyAssocOne)sourceProperties[i], overrides); - } else { - embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides); - } + embeddedProperties[i] = sourceProperties[i].override(overrides); } return new BeanEmbeddedMeta(embeddedProperties); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index d564a1f7a..ad054c59b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -365,13 +365,14 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { return InternString.intern(s); } + public BeanProperty override(BeanPropertyOverride override) { + return new BeanProperty(this, override); + } + /** - * Create a Matching BeanProperty with some attributes overridden. - *

- * Primarily for supporting Embedded beans with overridden dbColumn - * mappings. + * Create a Matching BeanProperty with some attributes overridden for Embedded beans. */ - public BeanProperty(BeanProperty source, BeanPropertyOverride override) { + protected BeanProperty(BeanProperty source, BeanPropertyOverride override) { this.descriptor = source.descriptor; this.propertyIndex = source.propertyIndex; this.name = source.getName(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index ca81267eb..9a69b8c9c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -94,10 +94,12 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr } } - /** - * Copy constructor for ManyToOne inside Embeddable. - */ - public BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) { + @Override + public BeanPropertyAssocOne override(BeanPropertyOverride override) { + return new BeanPropertyAssocOne<>(this, override); + } + + protected BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) { super(source, override); primaryKeyExport = source.primaryKeyExport; primaryKeyJoin = source.primaryKeyJoin; @@ -255,12 +257,8 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr } private SqlUpdate deleteByParentIdList(List parentIds) { - - StringBuilder sb = new StringBuilder(100); - sb.append(deleteByParentIdInSql); - sb.append(targetIdBinder.getIdInValueExpr(false, parentIds.size())); - - DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); + String sql = deleteByParentIdInSql + targetIdBinder.getIdInValueExpr(false, parentIds.size()); + DefaultSqlUpdate delete = new DefaultSqlUpdate(sql); bindParentIds(delete, parentIds); return delete; } @@ -501,7 +499,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr return targetDescriptor.getIdProperty(); } - ScalarType getIdScalarType() { + ScalarType getIdScalarType() { return targetDescriptor.getIdProperty().scalarType; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index 0b7cdebbc..bc13b1a8b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -27,6 +27,16 @@ public class BeanPropertyJsonMapper extends BeanProperty { this.sourceDetection = deployProp.getMutationDetection() == MutationDetection.SOURCE; } + private BeanPropertyJsonMapper(BeanPropertyJsonMapper source, BeanPropertyOverride override) { + super(source, override); + this.sourceDetection = source.sourceDetection; + } + + @Override + public BeanProperty override(BeanPropertyOverride override) { + return new BeanPropertyJsonMapper(this, override); + } + @Override public MutableValueInfo createMutableInfo(String json) { if (sourceDetection) { diff --git a/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java b/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java index 86911152b..356399005 100644 --- a/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java +++ b/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java @@ -1,5 +1,8 @@ package org.tests.model.embedded; +import io.ebean.annotation.DbJson; +import org.tests.model.json.PlainBean; + import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.EnumType; @@ -18,6 +21,9 @@ public class EAddress { @Enumerated(EnumType.STRING) EAddressStatus status; + @DbJson + PlainBean jbean; + public String getStreet() { return street; } @@ -42,6 +48,14 @@ public class EAddress { this.city = city; } + public PlainBean getJbean() { + return jbean; + } + + public void setJbean(PlainBean jbean) { + this.jbean = jbean; + } + public EAddressStatus getStatus() { return status; } diff --git a/ebean-core/src/test/java/org/tests/model/embedded/EPerson.java b/ebean-core/src/test/java/org/tests/model/embedded/EPerson.java index 1e85b1041..8a0c60446 100644 --- a/ebean-core/src/test/java/org/tests/model/embedded/EPerson.java +++ b/ebean-core/src/test/java/org/tests/model/embedded/EPerson.java @@ -24,7 +24,8 @@ public class EPerson { @Embedded @AttributeOverrides({ @AttributeOverride(name = "city", column = @Column(name = "addr_city")), - @AttributeOverride(name = "status", column = @Column(name = "addr_status")) + @AttributeOverride(name = "status", column = @Column(name = "addr_status")), + @AttributeOverride(name = "jbean", column = @Column(name = "addr_jbean")) }) EAddress address; diff --git a/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java b/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java index b6e4287a0..09084ccc2 100644 --- a/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java +++ b/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java @@ -1,12 +1,13 @@ package org.tests.rawsql.nativesql; import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import io.ebean.DB; import io.ebean.Query; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; import org.tests.model.embedded.EAddress; import org.tests.model.embedded.EPerson; +import org.tests.model.json.PlainBean; import java.util.List; @@ -22,20 +23,22 @@ public class TestNativeWithEmbedded extends BaseTestCase { EAddress address = new EAddress(); address.setStreet("1 foo st"); address.setCity("barv"); + address.setJbean(new PlainBean("hi", 3)); person.setAddress(address); - Ebean.save(person); + DB.save(person); - String sql = "select id, name, street, suburb, addr_city, addr_status from eperson where id = ?"; + String sql = "select id, name, street, suburb, addr_city, addr_status, addr_jbean from eperson where id = ?"; LoggedSqlCollector.start(); - Query query = Ebean.findNative(EPerson.class, sql); + Query query = DB.findNative(EPerson.class, sql); query.setParameter(person.getId()); EPerson one = query.findOne(); assertThat(one.getName()).isEqualTo("Frank"); assertThat(one.getAddress().getStreet()).isEqualTo("1 foo st"); + assertThat(one.getAddress().getJbean().getName()).isEqualTo("hi"); List loggedSql = LoggedSqlCollector.stop(); assertThat(loggedSql).hasSize(1); From d601a88d827cafdfaa879060e882df1b01370ee4 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 30 Jul 2021 23:53:28 +1200 Subject: [PATCH 49/87] #2278 - Move null to empty json collections check to BeanPropertyJsonBasic --- .../server/deploy/BeanProperty.java | 14 ----- .../server/deploy/BeanPropertyJsonBasic.java | 58 +++++++++++++++++++ .../server/deploy/BeanPropertyJsonMapper.java | 2 +- .../deploy/meta/DeployBeanProperty.java | 6 +- .../deploy/meta/DeployBeanPropertyLists.java | 3 + .../server/deploy/parse/DeployUtil.java | 3 - .../org/tests/model/embedded/EAddress.java | 12 ++++ .../model/json/EBasicJsonMapVarchar.java | 2 +- .../nativesql/TestNativeWithEmbedded.java | 8 ++- 9 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonBasic.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index ad054c59b..413d0b460 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -625,27 +625,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { return scalarType.read(reader); } - protected Object checkForEmpty(EntityBean bean) { - final Object value = getValue(bean); - if (value instanceof Collection && ((Collection) value).isEmpty() - || value instanceof Map && ((Map) value).isEmpty()) { - return value; - } - return null; - } - public Object readSet(DataReader reader, EntityBean bean) throws SQLException { try { Object value = scalarType.read(reader); - if (value == null) { - value = checkForEmpty(bean); - } if (bean != null) { setValue(bean, value); } return value; - } catch (TextException e) { - throw e; } catch (Exception e) { throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonBasic.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonBasic.java new file mode 100644 index 000000000..6593186a6 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonBasic.java @@ -0,0 +1,58 @@ +package io.ebeaninternal.server.deploy; + +import io.ebean.bean.EntityBean; +import io.ebean.core.type.DataReader; +import io.ebean.text.TextException; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +import javax.persistence.PersistenceException; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Map; + +/** + * A DbJson property that does not use Jackson ObjectMapper. + */ +public class BeanPropertyJsonBasic extends BeanProperty { + + public BeanPropertyJsonBasic(BeanDescriptor descriptor, DeployBeanProperty deploy) { + super(descriptor, deploy); + } + + protected BeanPropertyJsonBasic(BeanProperty source, BeanPropertyOverride override) { + super(source, override); + } + + @Override + public BeanProperty override(BeanPropertyOverride override) { + return new BeanPropertyJsonBasic(this, override); + } + + protected Object checkForEmpty(EntityBean bean) { + final Object value = getValue(bean); + if (value instanceof Collection && ((Collection) value).isEmpty() + || value instanceof Map && ((Map) value).isEmpty()) { + return value; + } + return null; + } + + @Override + public Object readSet(DataReader reader, EntityBean bean) throws SQLException { + try { + Object value = scalarType.read(reader); + if (value == null) { + value = checkForEmpty(bean); + } + if (bean != null) { + setValue(bean, value); + } + return value; + } catch (TextException e) { + throw e; + } catch (Exception e) { + throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); + } + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index bc13b1a8b..fdea2e449 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -18,7 +18,7 @@ import java.util.Objects; /** * Handle json property with MutationDetection of SOURCE or HASH only. */ -public class BeanPropertyJsonMapper extends BeanProperty { +public class BeanPropertyJsonMapper extends BeanPropertyJsonBasic { private final boolean sourceDetection; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 3da06d559..5e5455b2f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -316,9 +316,6 @@ public class DeployBeanProperty { } public MutationDetection getMutationDetection() { - if (mutationDetection == null) { - mutationDetection = MutationDetection.DEFAULT; - } return mutationDetection; } @@ -1204,4 +1201,7 @@ public class DeployBeanProperty { return scalarType != null && scalarType.isJsonMapper(); } + boolean isJsonType() { + return mutationDetection != null; + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java index 90503c93f..5466d5bca 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java @@ -453,6 +453,9 @@ public class DeployBeanPropertyLists { if (deployProp.isJsonMapper()) { return new BeanPropertyJsonMapper(desc, deployProp); } + if (deployProp.isJsonType()) { + return new BeanPropertyJsonBasic(desc, deployProp); + } return new BeanProperty(desc, deployProp); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java index 0792c66d5..68cb1c741 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -205,9 +205,6 @@ public class DeployUtil { } } - /** - * This property is marked as a Lob object. - */ void setDbJsonType(DeployBeanProperty prop, DbJson dbJsonType) { int dbType = getDbJsonStorage(dbJsonType.storage()); setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.mutationDetection()); diff --git a/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java b/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java index 356399005..4b737f0f5 100644 --- a/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java +++ b/ebean-core/src/test/java/org/tests/model/embedded/EAddress.java @@ -7,6 +7,7 @@ import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import java.util.Map; @Embeddable public class EAddress { @@ -24,6 +25,9 @@ public class EAddress { @DbJson PlainBean jbean; + @DbJson + Map jraw; + public String getStreet() { return street; } @@ -56,6 +60,14 @@ public class EAddress { this.jbean = jbean; } + public Map getJraw() { + return jraw; + } + + public void setJraw(Map jraw) { + this.jraw = jraw; + } + public EAddressStatus getStatus() { return status; } diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMapVarchar.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMapVarchar.java index c08c4e9fe..2b470ef45 100644 --- a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMapVarchar.java +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMapVarchar.java @@ -20,7 +20,7 @@ public class EBasicJsonMapVarchar { String name; @DbJson(storage = DbJsonType.VARCHAR)//, length = 2200) - Map content; + Map content; public Long getId() { return id; diff --git a/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java b/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java index 09084ccc2..39f63c31b 100644 --- a/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java +++ b/ebean-core/src/test/java/org/tests/rawsql/nativesql/TestNativeWithEmbedded.java @@ -9,7 +9,9 @@ import org.tests.model.embedded.EAddress; import org.tests.model.embedded.EPerson; import org.tests.model.json.PlainBean; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; @@ -18,17 +20,20 @@ public class TestNativeWithEmbedded extends BaseTestCase { @Test public void test() { + Map rawMap = new LinkedHashMap<>(); + rawMap.put("a","1"); EPerson person = new EPerson(); person.setName("Frank"); EAddress address = new EAddress(); address.setStreet("1 foo st"); address.setCity("barv"); address.setJbean(new PlainBean("hi", 3)); + address.setJraw(rawMap); person.setAddress(address); DB.save(person); - String sql = "select id, name, street, suburb, addr_city, addr_status, addr_jbean from eperson where id = ?"; + String sql = "select id, name, street, suburb, addr_city, addr_status, addr_jbean, jraw from eperson where id = ?"; LoggedSqlCollector.start(); @@ -39,6 +44,7 @@ public class TestNativeWithEmbedded extends BaseTestCase { assertThat(one.getName()).isEqualTo("Frank"); assertThat(one.getAddress().getStreet()).isEqualTo("1 foo st"); assertThat(one.getAddress().getJbean().getName()).isEqualTo("hi"); + assertThat(one.getAddress().getJraw().get("a")).isEqualTo("1"); List loggedSql = LoggedSqlCollector.stop(); assertThat(loggedSql).hasSize(1); From 928b90122f7bced174610da0c97d28810d6aabc4 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 2 Aug 2021 20:15:51 +1200 Subject: [PATCH 50/87] #2283 - ddl-generator - Add DbMigration.addPlatform(Platform) with default sub-directory based on Platform --- .../io/ebean/dbmigration/DbMigration.java | 16 +++-- .../dbmigration/DefaultDbMigration.java | 62 +++++++++---------- .../dbmigration/LastMigration.java | 5 +- .../dbmigration/model/MigrationModel.java | 12 ++-- .../dbmigration/DbMigrationGenerateTest.java | 12 ++-- 5 files changed, 52 insertions(+), 55 deletions(-) diff --git a/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java b/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java index 96b8aa16e..36a53ba48 100644 --- a/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java @@ -196,19 +196,23 @@ public interface DbMigration { void setLockTimeout(int seconds); /** - * Add an additional platform to write the migration DDL. + * Add a platform to write the migration DDL. *

* Use this when you want to generate sql scripts for multiple database platforms * from the migration (e.g. generate migration sql for MySql, Postgres and Oracle). *

*/ + void addPlatform(Platform platform); + + /** + * Add a platform to write with a given prefix. + */ void addPlatform(Platform platform, String prefix); /** - * Add an additional databasePlatform to write the migration DDL. + * Add a databasePlatform to write the migration DDL. *

* Use this when you want to add preconfigured database platforms. - *

*/ void addDatabasePlatform(DatabasePlatform databasePlatform, String prefix); @@ -256,9 +260,9 @@ public interface DbMigration { * * migration.setPathToResources("src/main/resources"); * - * migration.addPlatform(Platform.POSTGRES, "pg"); - * migration.addPlatform(Platform.MYSQL, "mysql"); - * migration.addPlatform(Platform.ORACLE, "oracle"); + * migration.addPlatform(Platform.POSTGRES); + * migration.addPlatform(Platform.MYSQL); + * migration.addPlatform(Platform.ORACLE); * * migration.generateMigration(); * diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 1c790c607..0a7b0edee 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -259,13 +259,12 @@ public class DefaultDbMigration implements DbMigration { } } - /** - * Add an additional platform to write the migration DDL. - *

- * Use this when you want to generate sql scripts for multiple database platforms - * from the migration (e.g. generate migration sql for MySql, Postgres and Oracle). - *

- */ + @Override + public void addPlatform(Platform platform) { + String prefix = platform.base().name().toLowerCase(); + addPlatform(platform, prefix); + } + @Override public void addPlatform(Platform platform, String prefix) { platforms.add(new Pair(getPlatform(platform), prefix)); @@ -286,7 +285,7 @@ public class DefaultDbMigration implements DbMigration { * * DbMigration migration = DbMigration.create(); * migration.setPathToResources("src/main/resources"); - * migration.setPlatform(DbPlatformName.ORACLE); + * migration.setPlatform(Platform.ORACLE); * * migration.generateMigration(); * @@ -298,9 +297,9 @@ public class DefaultDbMigration implements DbMigration { * DbMigration migration = DbMigration.create(); * migration.setPathToResources("src/main/resources"); * - * migration.addPlatform(DbPlatformName.POSTGRES, "pg"); - * migration.addPlatform(DbPlatformName.MYSQL, "mysql"); - * migration.addPlatform(DbPlatformName.ORACLE, "mysql"); + * migration.addPlatform(Platform.POSTGRES); + * migration.addPlatform(Platform.MYSQL); + * migration.addPlatform(Platform.ORACLE); * * migration.generateMigration(); * @@ -318,10 +317,9 @@ public class DefaultDbMigration implements DbMigration { return generateMigrationFor(true); } - private String generateMigrationFor(boolean dbinitMigration) throws IOException { - - // use this flag to stop other plugins like full DDL generation + private String generateMigrationFor(boolean initMigration) throws IOException { if (!online) { + // use this flag to stop other plugins like full DDL generation DbOffline.setGenerateMigration(); if (databasePlatform == null && !platforms.isEmpty()) { // for multiple platform generation the first platform @@ -334,8 +332,8 @@ public class DefaultDbMigration implements DbMigration { configurePlatforms(); } try { - Request request = createRequest(dbinitMigration); - if (!dbinitMigration) { + Request request = createRequest(initMigration); + if (!initMigration) { // repeatable migrations if (platforms.isEmpty()) { generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning()); @@ -512,31 +510,31 @@ public class DefaultDbMigration implements DbMigration { return version; } - private Request createRequest(boolean dbinitMigration) { - return new Request(dbinitMigration); + private Request createRequest(boolean initMigration) { + return new Request(initMigration); } private class Request { - final boolean dbinitMigration; + final boolean initMigration; final File migrationDir; final File modelDir; final CurrentModel currentModel; final ModelContainer migrated; final ModelContainer current; - private Request(boolean dbinitMigration) { - this.dbinitMigration = dbinitMigration; + private Request(boolean initMigration) { + this.initMigration = initMigration; this.currentModel = new CurrentModel(server, constraintNaming); this.current = currentModel.read(); - this.migrationDir = getMigrationDirectory(dbinitMigration); - if (dbinitMigration) { + this.migrationDir = getMigrationDirectory(initMigration); + if (initMigration) { this.modelDir = null; this.migrated = new ModelContainer(); } else { this.modelDir = getModelDirectory(migrationDir); MigrationModel migrationModel = new MigrationModel(modelDir, modelSuffix); - this.migrated = migrationModel.read(dbinitMigration); + this.migrated = migrationModel.read(false); } } @@ -551,7 +549,7 @@ public class DefaultDbMigration implements DbMigration { // always read the next version using the main migration directory (not dbinit) File migDirectory = getMigrationDirectory(false); File modelDir = getModelDirectory(migDirectory); - return LastMigration.nextVersion(migDirectory, modelDir, dbinitMigration); + return LastMigration.nextVersion(migDirectory, modelDir, initMigration); } /** @@ -588,7 +586,7 @@ public class DefaultDbMigration implements DbMigration { String fullVersion = getFullVersion(request.nextVersion(), dropsFor); logInfo("generating migration:%s", fullVersion); - if (!request.dbinitMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) { + if (!request.initMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) { logError("migration already exists, not generating DDL"); return null; } else { @@ -782,17 +780,15 @@ public class DefaultDbMigration implements DbMigration { /** * Return the file path to write the xml and sql to. */ - File getMigrationDirectory(boolean dbinitMigration) { - + File getMigrationDirectory(boolean initMigration) { // path to src/main/resources in typical maven project File resourceRootDir = new File(pathToResources); if (!resourceRootDir.exists()) { String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath()); throw new UnknownResourcePathException(msg); } - String resourcePath = getMigrationPath(dbinitMigration); - - // expect to be a path to something like - src/main/resources/dbmigration/model + String resourcePath = getMigrationPath(initMigration); + // expect to be a path to something like - src/main/resources/dbmigration File path = new File(resourceRootDir, resourcePath); if (!path.exists()) { if (!path.mkdirs()) { @@ -802,8 +798,8 @@ public class DefaultDbMigration implements DbMigration { return path; } - private String getMigrationPath(boolean dbinitMigration) { - return dbinitMigration ? migrationInitPath : migrationPath; + private String getMigrationPath(boolean initMigration) { + return initMigration ? migrationInitPath : migrationPath; } /** diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java index 20b9a19c3..0a3353068 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java @@ -20,13 +20,12 @@ class LastMigration { /** * Return the next migation version given the migration directory. */ - static String nextVersion(File migDir, File modelDir, boolean dbinitMigration) { - + static String nextVersion(File migDir, File modelDir, boolean initMigration) { String last = lastVersion(migDir, modelDir); if (last == null) { return null; } - return (dbinitMigration) ? last : MigrationVersion.parse(last).nextVersion(); + return (initMigration) ? last : MigrationVersion.parse(last).nextVersion(); } /** diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java index 6fe5ed66b..2ad1001bb 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java @@ -34,16 +34,14 @@ public class MigrationModel { * Read all the migrations returning the model with all * the migrations applied in version order. * - * @param dbinitMigration If true we don't apply model changes, migration is from scratch. + * @param initMigration If true we don't apply model changes, migration is from scratch. */ - public ModelContainer read(boolean dbinitMigration) { - - readMigrations(dbinitMigration); + public ModelContainer read(boolean initMigration) { + readMigrations(initMigration); return model; } - private void readMigrations(boolean dbinitMigration) { - + private void readMigrations(boolean initMigration) { // find all the migration xml files File[] xmlFiles = modelDirectory.listFiles(pathname -> pathname.getName().toLowerCase().endsWith(modelSuffix)); if (xmlFiles == null || xmlFiles.length == 0) { @@ -57,7 +55,7 @@ public class MigrationModel { // sort into version order before applying Collections.sort(resources); - if (!dbinitMigration) { + if (!initMigration) { for (MigrationResource migrationResource : resources) { logger.debug("read {}", migrationResource); model.apply(migrationResource.read(), migrationResource.getVersion()); diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java index 1efaacee3..0063a75cf 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java @@ -46,16 +46,16 @@ public class DbMigrationGenerateTest { // migration.addPlatform(Platform.GENERIC, "generic"); there is no ddl handler for generic // migration.addPlatform(Platform.SQLANYWHERE, "sqlanywhere"); and sqlanywhere - migration.addPlatform(Platform.DB2, "db2"); - migration.addPlatform(Platform.H2, "h2"); + migration.addPlatform(Platform.DB2); + migration.addPlatform(Platform.H2); migration.addPlatform(Platform.HSQLDB, "hsqldb"); migration.addPlatform(Platform.MYSQL, "mysql"); migration.addPlatform(Platform.MYSQL55, "mysql55"); - migration.addPlatform(Platform.POSTGRES, "postgres"); - migration.addPlatform(Platform.ORACLE, "oracle"); - migration.addPlatform(Platform.SQLITE, "sqlite"); + migration.addPlatform(Platform.POSTGRES); + migration.addPlatform(Platform.ORACLE); + migration.addPlatform(Platform.SQLITE); migration.addPlatform(Platform.SQLSERVER17, "sqlserver17"); - migration.addPlatform(Platform.HANA, "hana"); + migration.addPlatform(Platform.HANA); DatabaseConfig config = new DatabaseConfig(); config.setName("migrationtest"); From 71223895ac458f18e7d1ca6d7c2b01d99bce43fb Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 2 Aug 2021 20:38:05 +1200 Subject: [PATCH 51/87] #2283 - ddl-generator - Refactor tidy whitespace etc --- .../io/ebean/dbmigration/DbMigration.java | 19 ++---- .../dbmigration/LastMigration.java | 3 +- .../UnknownResourcePathException.java | 1 - .../dbmigration/model/CurrentModel.java | 10 +-- .../model/MCompoundUniqueConstraint.java | 4 -- .../dbmigration/model/MConfiguration.java | 3 +- .../dbmigration/model/MIndex.java | 4 +- .../dbmigration/model/MTable.java | 65 ++----------------- .../dbmigration/model/MTableIdentity.java | 2 - .../dbmigration/model/MigrationModel.java | 16 +---- .../dbmigration/model/MigrationResource.java | 3 +- .../dbmigration/model/ModelContainer.java | 5 +- .../dbmigration/model/ModelDiff.java | 16 ++--- .../dbmigration/model/PendingDrops.java | 12 ---- .../dbmigration/model/PlatformDdlWriter.java | 5 -- 15 files changed, 22 insertions(+), 146 deletions(-) diff --git a/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java b/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java index 36a53ba48..55753f4a2 100644 --- a/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java @@ -16,7 +16,6 @@ import java.util.ServiceLoader; *

* Typically this is run as a main method in src/test once a developer is happy * with the next set of changes to the model. - *

* *

Example: Run for a single specific platform

* @@ -39,11 +38,9 @@ import java.util.ServiceLoader; * are no longer being used by the application. These changes are called * "pending drops" and we must explicitly specify to include these in * a generated migration. - *

*

* Use setGeneratePendingDrop() to specify a prior migration * that has drop column changes that we want to generate a migration for. - *

* *

Example: Generate for pending drops

* @@ -67,7 +64,6 @@ public interface DbMigration { * Create a DbMigration implementation to use. */ static DbMigration create() { - Iterator loader = ServiceLoader.load(DbMigration.class).iterator(); if (loader.hasNext()) { return loader.next(); @@ -76,7 +72,7 @@ public interface DbMigration { } /** - * Set to false to suppress logging to System out. + * Set logging to System out (defaults to true). */ void setLogToSystemOut(boolean logToSystemOut); @@ -105,13 +101,12 @@ public interface DbMigration { void setMigrationPath(String migrationPath); /** - * Set the server to use to determine the current model. - * Typically this is not called explicitly. + * Set the server to use to determine the current model. Usually this is not called explicitly. */ void setServer(Database database); /** - * Set the DatabaseConfig to use. Typically this is not called explicitly. + * Set the DatabaseConfig to use. Usually this is not called explicitly. */ void setServerConfig(DatabaseConfig config); @@ -119,7 +114,6 @@ public interface DbMigration { * Set the specific platform to generate DDL for. *

* If not set this defaults to the platform of the default database. - *

*/ void setPlatform(Platform platform); @@ -127,17 +121,15 @@ public interface DbMigration { * Set the specific platform to generate DDL for. *

* If not set this defaults to the platform of the default database. - *

*/ void setPlatform(DatabasePlatform databasePlatform); /** - * Set to false to turn off strict mode. + * Set to false in order to turn off strict mode. *

* Strict mode checks that a column changed to non-null on an existing table via DB migration has a default * value specified. Set this to false if that isn't the case but it is known that all the existing rows have * a value specified (there are no existing null values for the column). - *

*/ void setStrictMode(boolean strictMode); @@ -182,7 +174,6 @@ public interface DbMigration { * Set to true if ALTER TABLE ADD FOREIGN KEY should be generated with an option to skip validation. *

* Currently this is only useful for Postgres DDL adding the NOT VALID option. - *

*/ void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck); @@ -191,7 +182,6 @@ public interface DbMigration { *

* Currently this is only useful for Postgres migrations adding a set lock_timeout * statement to the generated database migration. - *

*/ void setLockTimeout(int seconds); @@ -200,7 +190,6 @@ public interface DbMigration { *

* Use this when you want to generate sql scripts for multiple database platforms * from the migration (e.g. generate migration sql for MySql, Postgres and Oracle). - *

*/ void addPlatform(Platform platform); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java index 0a3353068..07a80add9 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java @@ -29,10 +29,9 @@ class LastMigration { } /** - * Return the last migation version given the migration directory. + * Return the last migration version given the migration directory. */ static String lastVersion(File migDirectory, File modelDir) { - List versions = new ArrayList<>(); File[] sqlFiles = migDirectory.listFiles(pathname -> includeSqlFile(pathname.getName().toLowerCase())); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java index 9ecfde157..c8b29872a 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java @@ -4,7 +4,6 @@ package io.ebeaninternal.dbmigration; * Exception when db migration resource path does not exist. *

* Typically the working directory or pathToResources is incorrect. - *

*/ public class UnknownResourcePathException extends RuntimeException { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java index 11189be94..0ff9a9bc4 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java @@ -27,25 +27,17 @@ import static io.ebeaninternal.api.PlatformMatch.matchPlatform; public class CurrentModel { private final SpiEbeanServer server; - private final DatabasePlatform databasePlatform; - private final DbConstraintNaming constraintNaming; - private final boolean platformTypes; - private final boolean jaxbPresent; - private final String ddlHeader; + private final DdlOptions ddlOptions = new DdlOptions(); private ModelContainer model; - private ChangeSet changeSet; - private DdlWrite write; - private DdlOptions ddlOptions = new DdlOptions(); - /** * Construct with a given EbeanServer instance for DDL create all generation, not migration. */ diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundUniqueConstraint.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundUniqueConstraint.java index c7c63769d..fbb2e2460 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundUniqueConstraint.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundUniqueConstraint.java @@ -20,19 +20,15 @@ import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.s public class MCompoundUniqueConstraint { private final String name; - /** * Flag if true indicates this was specifically created for a OneToOne mapping. */ private final boolean oneToOne; - /** * The columns combined to be unique. */ private final String[] columns; - private final String platforms; - private String[] nullableColumns; /** diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MConfiguration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MConfiguration.java index 43387a5c7..178f35c8d 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MConfiguration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MConfiguration.java @@ -4,7 +4,7 @@ import io.ebeaninternal.dbmigration.migration.Configuration; import io.ebeaninternal.dbmigration.migration.DefaultTablespace; /** - * Holds configuration such as the default tablespaces to use for tables, + * Holds configuration such as the default tablespace to use for tables, * indexes, history tables etc. */ public class MConfiguration { @@ -32,7 +32,6 @@ public class MConfiguration { *

*/ public void apply(Configuration configuration) { - DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); if (defaultTablespace != null) { String tables = defaultTablespace.getTables(); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MIndex.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MIndex.java index 976bde5f3..710241a69 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MIndex.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MIndex.java @@ -13,8 +13,8 @@ import java.util.Objects; */ public class MIndex { - private String tableName; - private String indexName; + private final String tableName; + private final String indexName; private String platforms; private List columns = new ArrayList<>(); private boolean unique; diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTable.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTable.java index 82a687eb7..71faa1c44 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTable.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTable.java @@ -48,67 +48,32 @@ public class MTable { private static final Logger logger = LoggerFactory.getLogger(MTable.class); - /** - * Table name. - */ private final String name; - - /** - * The associated draft table. - */ private MTable draftTable; - /** * Marked true for draft tables. These need to have their FK references adjusted * after all the draft tables have been identified. */ private boolean draft; - private PartitionMeta partitionMeta; - - /** - * Primary key name. - */ private String pkName; - - /** - * Table comment. - */ private String comment; - - /** - * Tablespace to use. - */ private String tablespace; - private String storageEngine; - - /** - * Tablespace to use for indexes on this table. - */ private String indexTablespace; - private IdentityMode identityMode; - - /** - * If set to true this table should has history support. - */ private boolean withHistory; - - /** - * The columns on the table. - */ - private Map columns = new LinkedHashMap<>(); + private final Map columns = new LinkedHashMap<>(); /** * Compound unique constraints. */ - private List uniqueConstraints = new ArrayList<>(); + private final List uniqueConstraints = new ArrayList<>(); /** * Compound foreign keys. */ - private List compoundKeys = new ArrayList<>(); + private final List compoundKeys = new ArrayList<>(); /** * Column name for the 'When created' column. This can be used for the initial effective start date when adding @@ -121,7 +86,7 @@ public class MTable { */ private AddColumn addColumn; - private List droppedColumns = new ArrayList<>(); + private final List droppedColumns = new ArrayList<>(); public MTable(BeanDescriptor descriptor) { this.name = descriptor.getBaseTable(); @@ -153,18 +118,15 @@ public class MTable { * later when creating the CreateTable object. */ public MTable createDraftTable() { - draftTable = new MTable(name + "_draft"); draftTable.draft = true; draftTable.whenCreatedColumn = whenCreatedColumn; // compoundKeys // compoundUniqueConstraints draftTable.identityMode = identityMode; - for (MColumn col : allColumns()) { draftTable.addColumn(col.copyForDraft()); } - return draftTable; } @@ -239,7 +201,6 @@ public class MTable { * Return the CreateTable migration for this table. */ public CreateTable createTable() { - CreateTable createTable = new CreateTable(); createTable.setName(name); createTable.setPkName(pkName); @@ -258,22 +219,18 @@ public class MTable { if (draft) { createTable.setDraft(Boolean.TRUE); } - for (MColumn column : allColumns()) { // filter out draftOnly columns from the base table if (draft || !column.isDraftOnly()) { createTable.getColumn().add(column.createColumn()); } } - for (MCompoundForeignKey compoundKey : compoundKeys) { createTable.getForeignKey().add(compoundKey.createForeignKey()); } - for (MCompoundUniqueConstraint constraint : uniqueConstraints) { createTable.getUniqueConstraint().add(constraint.getUniqueConstraint()); } - return createTable; } @@ -281,7 +238,6 @@ public class MTable { * Compare to another version of the same table to perform a diff. */ public void compare(ModelDiff modelDiff, MTable newTable) { - if (withHistory != newTable.withHistory) { if (withHistory) { DropHistoryTable dropHistoryTable = new DropHistoryTable(); @@ -308,14 +264,12 @@ public class MTable { modelDiff.addTableComment(addTableComment); } - compareCompoundKeys(modelDiff, newTable); compareUniqueKeys(modelDiff, newTable); } private void compareColumns(ModelDiff modelDiff, MTable newTable) { addColumn = null; - Map newColumnMap = newTable.getColumns(); // compare newColumns to existing columns (look for new and diff columns) @@ -374,10 +328,10 @@ public class MTable { currentKeys.removeAll(newTable.getUniqueConstraints()); newKeys.removeAll(getUniqueConstraints()); - for (MCompoundUniqueConstraint currentKey: currentKeys) { + for (MCompoundUniqueConstraint currentKey : currentKeys) { modelDiff.addUniqueConstraint(currentKey.dropUniqueConstraint(name)); } - for (MCompoundUniqueConstraint newKey: newKeys) { + for (MCompoundUniqueConstraint newKey : newKeys) { modelDiff.addUniqueConstraint(newKey.addUniqueConstraint(name)); } } @@ -489,7 +443,6 @@ public class MTable { } public List allHistoryColumns(boolean includeDropped) { - List columnNames = new ArrayList<>(columns.size()); for (MColumn column : columns.values()) { if (column.isIncludeInHistory()) { @@ -595,7 +548,6 @@ public class MTable { * Sometimes the case for a primaryKey that is also a foreign key. */ public MColumn addColumn(String dbCol, String columnDefn, boolean notnull) { - MColumn existingColumn = getColumn(dbCol); if (existingColumn != null) { if (notnull) { @@ -613,7 +565,6 @@ public class MTable { * Add a 'new column' to the AddColumn migration object. */ private void diffNewColumn(MColumn newColumn) { - if (addColumn == null) { addColumn = new AddColumn(); addColumn.setTableName(name); @@ -631,7 +582,6 @@ public class MTable { * Add a 'drop column' to the diff. */ private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn) { - DropColumn dropColumn = new DropColumn(); dropColumn.setTableName(name); dropColumn.setColumnName(existingColumn.getName()); @@ -640,7 +590,6 @@ public class MTable { // table as well as the base table dropColumn.setWithHistory(Boolean.TRUE); } - modelDiff.addDropColumn(dropColumn); } @@ -661,7 +610,6 @@ public class MTable { *

*/ public void checkDuplicateForeignKeys() { - if (hasDuplicateForeignKeys()) { int counter = 1; for (MCompoundForeignKey fk : compoundKeys) { @@ -687,7 +635,6 @@ public class MTable { * Adjust the references (FK) if it should relate to a draft table. */ public void adjustReferences(ModelContainer modelContainer) { - Collection cols = allColumns(); for (MColumn col : cols) { String references = col.getReferences(); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java index 5e4bb2306..f226ebaec 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java @@ -17,7 +17,6 @@ public class MTableIdentity { * Return the IdentityMode from CreateTable. */ public static IdentityMode fromCreateTable(CreateTable createTable) { - IdType type = fromType(createTable.getIdentityType()); IdentityGenerated generated = fromGenerated(createTable.getIdentityGenerated()); int start = toInt(createTable.getIdentityStart(), createTable.getSequenceInitial()); @@ -39,7 +38,6 @@ public class MTableIdentity { * Set the IdentityMode to the CreateTable model. */ public static void toCreateTable(IdentityMode identityMode, CreateTable createTable) { - if (!identityMode.isPlatformDefault()) { createTable.setIdentityType(toType(identityMode.getIdType())); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java index 2ad1001bb..9c49ad0aa 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationModel.java @@ -18,13 +18,9 @@ public class MigrationModel { private static final Logger logger = LoggerFactory.getLogger(MigrationModel.class); private final ModelContainer model = new ModelContainer(); - private final File modelDirectory; - private final String modelSuffix; - private MigrationVersion lastVersion; - public MigrationModel(File modelDirectory, String modelSuffix) { this.modelDirectory = modelDirectory; this.modelSuffix = modelSuffix; @@ -58,14 +54,9 @@ public class MigrationModel { if (!initMigration) { for (MigrationResource migrationResource : resources) { logger.debug("read {}", migrationResource); - model.apply(migrationResource.read(), migrationResource.getVersion()); + model.apply(migrationResource.read(), migrationResource.version()); } } - - // remember the last version - if (!resources.isEmpty()) { - lastVersion = resources.get(resources.size() - 1).getVersion(); - } } private MigrationVersion createVersion(File xmlFile) { @@ -73,9 +64,4 @@ public class MigrationModel { String versionName = fileName.substring(0, fileName.length() - modelSuffix.length()); return MigrationVersion.parse(versionName); } - - public String getNextVersion(String initialVersion) { - - return lastVersion == null ? initialVersion : lastVersion.nextVersion(); - } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationResource.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationResource.java index eff454813..ae4950a4e 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationResource.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MigrationResource.java @@ -31,7 +31,7 @@ public class MigrationResource implements Comparable { /** * Return the version associated with this resource. */ - public MigrationVersion getVersion() { + public MigrationVersion version() { return version; } @@ -39,7 +39,6 @@ public class MigrationResource implements Comparable { * Read and return the migration from the resource. */ public Migration read() { - return MigrationXmlReader.read(migrationFile); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelContainer.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelContainer.java index 3f75064d2..dc19ef137 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelContainer.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelContainer.java @@ -37,19 +37,16 @@ import java.util.TreeSet; public class ModelContainer { private final Set schemas = new TreeSet<>(); - /** * All the tables in the model. */ private final Map tables = new LinkedHashMap<>(); /** - * All the non unique non foreign key indexes. + * All the non-unique non-foreign key indexes. */ private final Map indexes = new LinkedHashMap<>(); - private final PendingDrops pendingDrops = new PendingDrops(); - private final List partitionedTables = new ArrayList<>(); public ModelContainer() { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelDiff.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelDiff.java index 2babc7c6c..367c13ca5 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelDiff.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/ModelDiff.java @@ -53,29 +53,24 @@ public class ModelDiff { this.baseModel = new ModelContainer(); } - /** - * Return true if the apply and drop changes are both empty. - * This means there are no migration changes. + * Return true if apply and drop changes are both empty. This means there are no migration changes. */ public boolean isEmpty() { return applyChanges.isEmpty() && dropChanges.isEmpty(); } /** - * Return the diff as a migration potentially containing - * an apply changeSet and a drop changeSet. + * Return the diff as a migration potentially containing an apply changeSet and a drop changeSet. */ public Migration getMigration() { - Migration migration = new Migration(); if (!applyChanges.isEmpty()) { - // add a non empty apply changeSet + // add a non-empty apply changeSet migration.getChangeSet().add(getApplyChangeSet()); } - if (!dropChanges.isEmpty()) { - // add a non empty drop changeSet + // add a non-empty drop changeSet migration.getChangeSet().add(getDropChangeSet()); } return migration; @@ -121,7 +116,6 @@ public class ModelDiff { * Compare to a 'newer' model and collect the differences. */ public void compareTo(ModelContainer newModel) { - Map newTables = newModel.getTables(); for (MTable newTable : newTables.values()) { @@ -179,7 +173,6 @@ public class ModelDiff { * Compare tables looking for add/drop/modify columns etc. */ protected void compareTables(MTable currentTable, MTable newTable) { - currentTable.compare(this, newTable); } @@ -187,7 +180,6 @@ public class ModelDiff { * Compare tables looking for add/drop/modify columns etc. */ protected void compareIndexes(MIndex currentIndex, MIndex newIndex) { - currentIndex.compare(this, newIndex); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PendingDrops.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PendingDrops.java index 9a0bdd242..bd7e7c96b 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PendingDrops.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PendingDrops.java @@ -24,7 +24,6 @@ public class PendingDrops { * Add a 'pending drops' changeSet for the given version. */ public void add(MigrationVersion version, ChangeSet changeSet) { - Entry entry = map.computeIfAbsent(version.normalised(), k -> new Entry(version)); entry.add(changeSet); } @@ -33,7 +32,6 @@ public class PendingDrops { * Return the list of versions with pending drops. */ public List pendingDrops() { - List versions = new ArrayList<>(); for (Entry value : map.values()) { if (value.hasPendingDrops()) { @@ -48,7 +46,6 @@ public class PendingDrops { * to remove the (unsuppressed) pending drops for this version. */ public boolean appliedDropsFor(ChangeSet changeSet) { - MigrationVersion version = MigrationVersion.parse(changeSet.getDropsFor()); Entry entry = map.get(version.normalised()); @@ -68,7 +65,6 @@ public class PendingDrops { *

*/ public Migration migrationForVersion(String pendingVersion) { - Entry entry = getEntry(pendingVersion); Migration migration = new Migration(); @@ -96,7 +92,6 @@ public class PendingDrops { } private Entry getEntry(String pendingVersion) { - if ("next".equalsIgnoreCase(pendingVersion)) { Iterator it = map.values().iterator(); if (it.hasNext()) { @@ -115,7 +110,6 @@ public class PendingDrops { * Register pending drop columns on history tables to the new model. */ public void registerPendingHistoryDropColumns(ModelContainer newModel) { - for (Entry entry : map.values()) { for (ChangeSet changeSet : entry.list) { newModel.registerPendingHistoryDropColumns(changeSet); @@ -140,7 +134,6 @@ public class PendingDrops { static class Entry { final MigrationVersion version; - final List list = new ArrayList<>(); Entry(MigrationVersion version) { @@ -180,7 +173,6 @@ public class PendingDrops { * removed all the changeSets (and there are no suppressForever ones). */ boolean removeDrops(ChangeSet appliedDrops) { - Iterator iterator = list.iterator(); while (iterator.hasNext()) { ChangeSet next = iterator.next(); @@ -199,20 +191,16 @@ public class PendingDrops { * Remove the applied drops from the pending ones matching by table name and column name. */ private void removeMatchingChanges(ChangeSet pendingDrops, ChangeSet appliedDrops) { - List pending = pendingDrops.getChangeSetChildren(); Iterator iterator = pending.iterator(); while (iterator.hasNext()) { Object pendingDrop = iterator.next(); if (pendingDrop instanceof DropColumn && dropColumnIn((DropColumn) pendingDrop, appliedDrops)) { iterator.remove(); - } else if (pendingDrop instanceof DropTable && dropTableIn((DropTable) pendingDrop, appliedDrops)) { iterator.remove(); - } else if (pendingDrop instanceof DropHistoryTable && dropHistoryTableIn((DropHistoryTable) pendingDrop, appliedDrops)) { iterator.remove(); - } } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java index f56384477..59da934e4 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java @@ -27,9 +27,7 @@ public class PlatformDdlWriter { private static final Logger logger = LoggerFactory.getLogger(PlatformDdlWriter.class); private final DatabaseConfig databaseConfig; - private final PlatformDdl platformDdl; - private final int lockTimeoutSeconds; public PlatformDdlWriter(DatabasePlatform platform, DatabaseConfig dbConfig, int lockTimeoutSeconds) { @@ -42,7 +40,6 @@ public class PlatformDdlWriter { * Write the migration as platform specific ddl. */ public void processMigration(Migration dbMigration, DdlWrite write, File writePath, String fullVersion) throws IOException { - DdlHandler handler = handler(); handler.generateProlog(write); if (lockTimeoutSeconds > 0) { @@ -51,7 +48,6 @@ public class PlatformDdlWriter { write.apply().append(lockSql).endOfStatement().newLine(); } } - List changeSets = dbMigration.getChangeSet(); for (ChangeSet changeSet : changeSets) { if (isApply(changeSet)) { @@ -59,7 +55,6 @@ public class PlatformDdlWriter { } } handler.generateEpilog(write); - writePlatformDdl(write, writePath, fullVersion); } From 4ba26ee7b761faf69819ee3ed76f622204784d72 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 2 Aug 2021 20:49:30 +1200 Subject: [PATCH 52/87] #2283 - ddl-generator - Refactor tidy DefaultDbMigration etc --- .../dbmigration/DefaultDbMigration.java | 89 +++++-------------- .../dbmigration/LastMigration.java | 2 +- .../DbMigrationDropHistoryTest.java | 4 +- .../dbmigration/DbMigrationGenerateTest.java | 4 +- 4 files changed, 25 insertions(+), 74 deletions(-) diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 0a7b0edee..7643eeec2 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -2,7 +2,6 @@ package io.ebeaninternal.dbmigration; import io.ebean.DB; import io.ebean.Database; -import io.ebean.EbeanServer; import io.ebean.annotation.Platform; import io.ebean.config.DatabaseConfig; import io.ebean.config.DbConstraintNaming; @@ -79,20 +78,12 @@ import static io.ebeaninternal.api.PlatformMatch.matchPlatform; public class DefaultDbMigration implements DbMigration { protected static final Logger logger = LoggerFactory.getLogger("io.ebean.GenerateMigration"); - private static final String initialVersion = "1.0"; - private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY"; - private boolean logToSystemOut = true; - - /** - * Set to true if DefaultDbMigration run with online EbeanServer instance. - */ protected final boolean online; - + private boolean logToSystemOut = true; protected SpiEbeanServer server; - protected String pathToResources = "src/main/resources"; protected String migrationPath = "dbmigration"; @@ -101,15 +92,10 @@ public class DefaultDbMigration implements DbMigration { protected String modelSuffix = ".model.xml"; protected DatabasePlatform databasePlatform; - private boolean vanillaPlatform; - protected List platforms = new ArrayList<>(); - protected DatabaseConfig databaseConfig; - protected DbConstraintNaming constraintNaming; - protected Boolean strictMode; protected Boolean includeGeneratedFileComment; protected String header; @@ -119,7 +105,6 @@ public class DefaultDbMigration implements DbMigration { protected String generatePendingDrop; private boolean addForeignKeySkipCheck; private int lockTimeoutSeconds; - protected boolean includeBuiltInPartitioning = true; /** @@ -129,19 +114,6 @@ public class DefaultDbMigration implements DbMigration { this.online = false; } - /** - * Create using online EbeanServer. - */ - public DefaultDbMigration(EbeanServer server) { - this.online = true; - setServer(server); - } - - /** - * Set the path from the current working directory to the application resources. - *

- * This defaults to maven style 'src/main/resources'. - */ @Override public void setPathToResources(String pathToResources) { this.pathToResources = pathToResources; @@ -152,19 +124,12 @@ public class DefaultDbMigration implements DbMigration { this.migrationPath = migrationPath; } - /** - * Set the server to use to determine the current model. - * Typically this is not called explicitly. - */ @Override public void setServer(Database database) { this.server = (SpiEbeanServer) database; setServerConfig(server.getServerConfig()); } - /** - * Set the DatabaseConfig to use. Typically this is not called explicitly. - */ @Override public void setServerConfig(DatabaseConfig config) { if (this.databaseConfig == null) { @@ -173,7 +138,6 @@ public class DefaultDbMigration implements DbMigration { if (constraintNaming == null) { this.constraintNaming = databaseConfig.getConstraintNaming(); } - Properties properties = config.getProperties(); if (properties != null) { PropertiesWrapper props = new PropertiesWrapper("ebean", config.getName(), properties, null); @@ -242,7 +206,7 @@ public class DefaultDbMigration implements DbMigration { @Override public void setPlatform(Platform platform) { vanillaPlatform = true; - setPlatform(getPlatform(platform)); + setPlatform(platform(platform)); } /** @@ -267,7 +231,7 @@ public class DefaultDbMigration implements DbMigration { @Override public void addPlatform(Platform platform, String prefix) { - platforms.add(new Pair(getPlatform(platform), prefix)); + platforms.add(new Pair(platform(platform), prefix)); } @Override @@ -402,7 +366,6 @@ public class DefaultDbMigration implements DbMigration { *

*/ private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, boolean tablePartitioning) throws IOException { - if (dbPlatform != null) { if (tablePartitioning && includeBuiltInPartitioning) { generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltinTablePartitioning()); @@ -427,10 +390,8 @@ public class DefaultDbMigration implements DbMigration { * Write (or override) the "repeatable" migration script. */ private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException { - String fullName = repeatableMigrationName(script.isInit(), script.getName()); logger.debug("writing repeatable script {}", fullName); - File file = new File(migrationDir, fullName); try (FileWriter writer = new FileWriter(file)) { writer.write(script.getValue()); @@ -478,12 +439,10 @@ public class DefaultDbMigration implements DbMigration { * Generate the diff migration. */ private String generateDiff(Request request) throws IOException { - List pendingDrops = request.getPendingDrops(); if (!pendingDrops.isEmpty()) { logInfo("Pending un-applied drops in versions %s", pendingDrops); } - Migration migration = request.createDiffMigration(); if (migration == null) { logInfo("no changes detected - no migration written", null); @@ -498,11 +457,8 @@ public class DefaultDbMigration implements DbMigration { * Generate the migration based on the pendingDrops from a prior version. */ private String generatePendingDrop(Request request, String pendingVersion) throws IOException { - Migration migration = request.migrationForPendingDrop(pendingVersion); - String version = generateMigration(request, migration, pendingVersion); - List pendingDrops = request.getPendingDrops(); if (!pendingDrops.isEmpty()) { logInfo("... remaining pending un-applied drops in versions %s", pendingDrops); @@ -527,12 +483,12 @@ public class DefaultDbMigration implements DbMigration { this.initMigration = initMigration; this.currentModel = new CurrentModel(server, constraintNaming); this.current = currentModel.read(); - this.migrationDir = getMigrationDirectory(initMigration); + this.migrationDir = migrationDirectory(initMigration); if (initMigration) { this.modelDir = null; this.migrated = new ModelContainer(); } else { - this.modelDir = getModelDirectory(migrationDir); + this.modelDir = modelDirectory(migrationDir); MigrationModel migrationModel = new MigrationModel(modelDir, modelSuffix); this.migrated = migrationModel.read(false); } @@ -547,8 +503,8 @@ public class DefaultDbMigration implements DbMigration { */ String nextVersion() { // always read the next version using the main migration directory (not dbinit) - File migDirectory = getMigrationDirectory(false); - File modelDir = getModelDirectory(migDirectory); + File migDirectory = migrationDirectory(false); + File modelDir = modelDirectory(migDirectory); return LastMigration.nextVersion(migDirectory, modelDir, initMigration); } @@ -556,9 +512,7 @@ public class DefaultDbMigration implements DbMigration { * Return the migration for the pending drops for a given version. */ Migration migrationForPendingDrop(String pendingVersion) { - Migration migration = migrated.migrationForPendingDrop(pendingVersion); - // register any remaining pending drops migrated.registerPendingHistoryDropColumns(current); return migration; @@ -582,9 +536,7 @@ public class DefaultDbMigration implements DbMigration { } private String generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException { - - String fullVersion = getFullVersion(request.nextVersion(), dropsFor); - + String fullVersion = fullVersion(request.nextVersion(), dropsFor); logInfo("generating migration:%s", fullVersion); if (!request.initMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) { logError("migration already exists, not generating DDL"); @@ -621,15 +573,14 @@ public class DefaultDbMigration implements DbMigration { *

* The full version can contain a comment suffix after a "__" double underscore. */ - private String getFullVersion(String nextVersion, String dropsFor) { - - String version = getVersion(); + private String fullVersion(String nextVersion, String dropsFor) { + String version = version(); if (version == null) { version = (nextVersion != null) ? nextVersion : initialVersion; } String fullVersion = applyPrefix + version; - String name = getName(); + String name = name(); if (name != null) { fullVersion += "__" + toUnderScore(name); @@ -724,7 +675,7 @@ public class DefaultDbMigration implements DbMigration { * FlywayDb so each developer sets a unique version so that the migration script * generated is unique (typically just prior to being submitted as a merge request). */ - private String getVersion() { + private String version() { String envVersion = readEnvironment("ddl.migration.version"); if (!isEmpty(envVersion)) { return envVersion.trim(); @@ -744,7 +695,7 @@ public class DefaultDbMigration implements DbMigration { * is a short description of the feature. *

*/ - private String getName() { + private String name() { String envName = readEnvironment("ddl.migration.name"); if (!isEmpty(envName)) { return envName.trim(); @@ -773,21 +724,21 @@ public class DefaultDbMigration implements DbMigration { /** * Return the main migration directory. */ - File getMigrationDirectory() { - return getMigrationDirectory(false); + File migrationDirectory() { + return migrationDirectory(false); } /** * Return the file path to write the xml and sql to. */ - File getMigrationDirectory(boolean initMigration) { + File migrationDirectory(boolean initMigration) { // path to src/main/resources in typical maven project File resourceRootDir = new File(pathToResources); if (!resourceRootDir.exists()) { String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath()); throw new UnknownResourcePathException(msg); } - String resourcePath = getMigrationPath(initMigration); + String resourcePath = migrationPath(initMigration); // expect to be a path to something like - src/main/resources/dbmigration File path = new File(resourceRootDir, resourcePath); if (!path.exists()) { @@ -798,14 +749,14 @@ public class DefaultDbMigration implements DbMigration { return path; } - private String getMigrationPath(boolean initMigration) { + private String migrationPath(boolean initMigration) { return initMigration ? migrationInitPath : migrationPath; } /** * Return the model directory (relative to the migration directory). */ - private File getModelDirectory(File migrationDirectory) { + private File modelDirectory(File migrationDirectory) { if (modelPath == null || modelPath.isEmpty()) { return migrationDirectory; } @@ -819,7 +770,7 @@ public class DefaultDbMigration implements DbMigration { /** * Return the DatabasePlatform given the platform key. */ - protected DatabasePlatform getPlatform(Platform platform) { + protected DatabasePlatform platform(Platform platform) { switch (platform) { case H2: return new H2Platform(); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java index 07a80add9..17ac65aa9 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/LastMigration.java @@ -18,7 +18,7 @@ class LastMigration { private static final String MODEL_XML = ".model.xml"; /** - * Return the next migation version given the migration directory. + * Return the next migration version given the migration directory. */ static String nextVersion(File migDir, File modelDir, boolean initMigration) { String last = lastVersion(migDir, modelDir); diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java index db91e14c9..320406a7c 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java @@ -54,8 +54,8 @@ public class DbMigrationDropHistoryTest { migration.setServer(server); // First, we clean up the output-directory - assertThat(migration.getMigrationDirectory().getAbsolutePath()).contains("migrationtest-history"); - Files.walk(migration.getMigrationDirectory().toPath()) + assertThat(migration.migrationDirectory().getAbsolutePath()).contains("migrationtest-history"); + Files.walk(migration.migrationDirectory().toPath()) .filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete); // then we generate migration scripts for v1_0 diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java index 0063a75cf..befead534 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java @@ -70,8 +70,8 @@ public class DbMigrationGenerateTest { migration.setServer(server); // First, we clean up the output-directory - assertThat(migration.getMigrationDirectory().getAbsolutePath()).contains("migrationtest"); - Files.walk(migration.getMigrationDirectory().toPath()) + assertThat(migration.migrationDirectory().getAbsolutePath()).contains("migrationtest"); + Files.walk(migration.migrationDirectory().toPath()) .filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete); // then we generate migration scripts for v1_0 From 589ee7a5ea6baee0a011a4a97c0440097297b31d Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 2 Aug 2021 23:59:49 +1200 Subject: [PATCH 53/87] #2284 - ENH: ddl-generator - add optional generation of idx_.migrations file --- .../io/ebean/dbmigration/DbMigration.java | 8 ++ .../dbmigration/DefaultDbMigration.java | 27 +++- .../dbmigration/IndexMigration.java | 125 ++++++++++++++++++ .../ebeaninternal/dbmigration/MChecksum.java | 29 ++++ .../dbmigration/DbMigrationGenerateTest.java | 18 +-- .../dbmigration/IndexMigrationTest.java | 67 ++++++++++ .../dbmigration/MChecksumTest.java | 18 +++ .../dbmigration/index/1.0__hello.sql | 1 + .../resources/dbmigration/index/1.1__foo.sql | 1 + .../resources/dbmigration/index/I__init_1.sql | 1 + .../resources/dbmigration/index/R__view_1.sql | 1 + .../dbmigration/index2/I__init_1.sql | 1 + .../dbmigration/index2/R__view_1.sql | 1 + .../dbmigration/index2/g1/1.0__a.sql | 1 + .../dbmigration/index2/g1/1.1__b.sql | 1 + .../dbmigration/index2/g2/2.0__a.sql | 1 + .../dbmigration/index2/g2/2.1__2b.sql | 1 + 17 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java create mode 100644 ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java create mode 100644 ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java create mode 100644 ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index/1.0__hello.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index/1.1__foo.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index/I__init_1.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index/R__view_1.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index2/I__init_1.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index2/R__view_1.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.0__a.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.1__b.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.0__a.sql create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.1__2b.sql diff --git a/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java b/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java index 55753f4a2..ec6db4597 100644 --- a/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebean/dbmigration/DbMigration.java @@ -133,6 +133,14 @@ public interface DbMigration { */ void setStrictMode(boolean strictMode); + /** + * Set to include generation of the index migration file. + *

+ * When true this generates a {@code idx_.migrations} file. This can be used by the migration + * runner to improve performance of running migrations, especially when no migration changes have occurred. + */ + void setIncludeIndex(boolean generateIndexFile); + /** * Set to true to include a generated header comment in the DDL script. */ diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 7643eeec2..7f2a90558 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -106,6 +106,7 @@ public class DefaultDbMigration implements DbMigration { private boolean addForeignKeySkipCheck; private int lockTimeoutSeconds; protected boolean includeBuiltInPartitioning = true; + protected boolean includeIndex; /** * Create for offline migration generation. @@ -182,6 +183,11 @@ public class DefaultDbMigration implements DbMigration { this.generatePendingDrop = generatePendingDrop; } + @Override + public void setIncludeIndex(boolean includeIndex) { + this.includeIndex = includeIndex; + } + @Override public void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment) { this.includeGeneratedFileComment = includeGeneratedFileComment; @@ -273,7 +279,26 @@ public class DefaultDbMigration implements DbMigration { */ @Override public String generateMigration() throws IOException { - return generateMigrationFor(false); + final String version = generateMigrationFor(false); + if (includeIndex) { + generateIndex(version); + } + return version; + } + + /** + * Generate the {@code idx_platform.migrations} file. + */ + private void generateIndex(String version) throws IOException { + final boolean overwrite = version != null; + final File topDir = migrationDirectory(false); + if (!platforms.isEmpty()) { + for (Pair pair : platforms) { + new IndexMigration(topDir, pair).generate(overwrite); + } + } else { + new IndexMigration(topDir, databasePlatform).generate(overwrite); + } } @Override diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java new file mode 100644 index 000000000..215c18e72 --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java @@ -0,0 +1,125 @@ +package io.ebeaninternal.dbmigration; + +import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.migration.MigrationVersion; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Stack; + +/** + * Generate a migration index file. + *

+ * This is a file that has all the migrations listed in order with checksum of the file content. + */ +class IndexMigration { + + private static final String eol = "\n"; + private final List all = new ArrayList<>(); + private final File topDir; + private final DatabasePlatform databasePlatform; + private final File indexFile; + private final Stack pathStack = new Stack<>(); + + IndexMigration(File topDir, DatabasePlatform databasePlatform) { + this.topDir = topDir; + this.databasePlatform = databasePlatform; + this.indexFile = init(); + } + + IndexMigration(File topDir, DefaultDbMigration.Pair pair) { + this.topDir = new File(topDir, pair.prefix); + this.databasePlatform = pair.platform; + this.indexFile = init(); + } + + File init() { + pathStack.push(""); + String name = "idx_" + databasePlatform.getPlatform().base().name().toLowerCase() + ".migrations"; + return new File(topDir, name); + } + + void generate(boolean overwrite) throws IOException { + if (!overwrite && indexFile.exists()) { + return; + } + readSqlFiles(topDir); + generateIndex(); + } + + private void generateIndex() throws IOException { + Collections.sort(all); + FileWriter writer = new FileWriter(indexFile); + for (Entry entry : all) { + writeChecksumPadded(writer, entry.checksum); + writer.write(entry.fileName); + writer.write(eol); + } + writer.write(eol); + writer.close(); + } + + private void writeChecksumPadded(FileWriter writer, int checksum) throws IOException { + final String asStr = String.valueOf(checksum); + writer.write(asStr); + writer.write(','); + int max = 15 - asStr.length(); + for (int i = 0; i < max; i++) { + writer.write(' '); + } + } + + private void readSqlFiles(File dir) { + final File[] files = dir.listFiles(); + if (files != null && files.length > 0) { + for (File file : files) { + if (file.isDirectory()) { + readDirectory(file); + } + final String lowerName = file.getName().toLowerCase(); + if (lowerName.endsWith(".sql")) { + addEntry(file); + } + } + } + } + + private void readDirectory(File dir) { + final String current = pathStack.peek(); + pathStack.push(current + dir.getName() + "/"); + readSqlFiles(dir); + pathStack.pop(); + } + + private void addEntry(File sqlFile) { + final String relativePath = pathStack.peek(); + final String fileName = sqlFile.getName(); + final String name = fileName.substring(0, fileName.length() - 4); + final MigrationVersion version = MigrationVersion.parse(name); + final int checksum = MChecksum.calculate(sqlFile); + all.add(new Entry(checksum, version, relativePath + fileName)); + } + + static class Entry implements Comparable { + + private final int checksum; + private final String fileName; + private final MigrationVersion version; + + Entry(int checksum, MigrationVersion version, String fileName) { + this.checksum = checksum; + this.version = version; + this.fileName = fileName; + } + + @Override + public int compareTo(Entry other) { + return version.compareTo(other.version); + } + } + +} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java new file mode 100644 index 000000000..9c533992e --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/MChecksum.java @@ -0,0 +1,29 @@ +package io.ebeaninternal.dbmigration; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.zip.CRC32; + +/** + * Calculates the checksum for the given file content. + */ +class MChecksum { + + /** + * Returns the checksum of the file. Agnostic of encoding and new line character. + */ + static int calculate(File file) { + try { + final CRC32 crc32 = new CRC32(); + BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); + String line; + while ((line = bufferedReader.readLine()) != null) { + final byte[] lineBytes = line.getBytes(StandardCharsets.UTF_8); + crc32.update(lineBytes, 0, lineBytes.length); + } + return (int) crc32.getValue(); + } catch (IOException e) { + throw new UncheckedIOException("Failed to calculate checksum", e); + } + } +} diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java index befead534..f5bf01a56 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java @@ -27,20 +27,22 @@ public class DbMigrationGenerateTest { private static final Logger logger = LoggerFactory.getLogger(DbMigrationGenerateTest.class); - @Test - public void invokeTest() throws IOException { - main(null); + public static void main(String[] args) throws IOException { + run("ebean-ddl-generator/src/test/resources"); } - public static void main(String[] args) throws IOException { + @Test + public void invokeTest() throws IOException { + run("src/test/resources"); + } - logger.info("start"); + public static void run(String pathToResources) throws IOException { + logger.info("start current directory: " + new File(".").getAbsolutePath()); DefaultDbMigration migration = new DefaultDbMigration(); - + migration.setIncludeIndex(true); // We use src/test/resources as output directory (so we see in GIT if files will change) - - migration.setPathToResources("src/test/resources"); + migration.setPathToResources(pathToResources); migration.setMigrationPath("db/migration"); migration.setMigrationPath(null); // use the default for this test diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java new file mode 100644 index 000000000..5a04d8917 --- /dev/null +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java @@ -0,0 +1,67 @@ +package io.ebeaninternal.dbmigration; + +import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.config.dbplatform.h2.H2Platform; +import io.ebean.config.dbplatform.postgres.PostgresPlatform; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IndexMigrationTest { + + @Test + public void index() throws IOException { + File topDir = new File("src/test/resources/dbmigration/index"); + if (!topDir.exists()) { + throw new IllegalStateException("Not expected - dir does not exist " + topDir.getAbsolutePath()); + } + DatabasePlatform pg = new PostgresPlatform(); + IndexMigration indexMigration = new IndexMigration(topDir, pg); + indexMigration.generate(true); + + + File expected = new File(topDir, "idx_postgres.migrations"); + assertThat(expected).exists(); + + final List expectedLines = Arrays.asList( + "-965417868, I__init_1.sql", + "907060870, 1.0__hello.sql", + "-1938594527, 1.1__foo.sql", + "-1960070312, R__view_1.sql"); + + final List lines = Files.readAllLines(expected.toPath(), StandardCharsets.UTF_8); + assertThat(lines).containsAll(expectedLines); + } + + @Test + public void index2_withSubDirectories() throws IOException { + File topDir = new File("src/test/resources/dbmigration/index2"); + if (!topDir.exists()) { + throw new IllegalStateException("Not expected - dir does not exist " + topDir.getAbsolutePath()); + } + DatabasePlatform pg = new H2Platform(); + IndexMigration indexMigration = new IndexMigration(topDir, pg); + indexMigration.generate(true); + + File expected = new File(topDir, "idx_h2.migrations"); + assertThat(expected).exists(); + + final List expectedLines = Arrays.asList( + "-965417868, I__init_1.sql", + "-390611389, g1/1.0__a.sql", + "1908338681, g1/1.1__b.sql", + "-1776543936, g2/2.0__a.sql", + "253052666, g2/2.1__2b.sql", + "-1960070312, R__view_1.sql"); + + final List lines = Files.readAllLines(expected.toPath(), StandardCharsets.UTF_8); + assertThat(lines).containsAll(expectedLines); + } +} diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java new file mode 100644 index 000000000..e52c43755 --- /dev/null +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java @@ -0,0 +1,18 @@ +package io.ebeaninternal.dbmigration; + +import org.junit.Test; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MChecksumTest { + + @Test + public void calculate() { + File file = new File("src/test/resources/dbmigration/index/1.0__hello.sql"); + assertThat(file).exists(); + + assertThat(MChecksum.calculate(file)).isEqualTo(907060870); + } +} diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index/1.0__hello.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index/1.0__hello.sql new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index/1.0__hello.sql @@ -0,0 +1 @@ +hello diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index/1.1__foo.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index/1.1__foo.sql new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index/1.1__foo.sql @@ -0,0 +1 @@ +foo diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index/I__init_1.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index/I__init_1.sql new file mode 100644 index 000000000..b1b716105 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index/I__init_1.sql @@ -0,0 +1 @@ +init diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index/R__view_1.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index/R__view_1.sql new file mode 100644 index 000000000..0f2416ebf --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index/R__view_1.sql @@ -0,0 +1 @@ +Something diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index2/I__init_1.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index2/I__init_1.sql new file mode 100644 index 000000000..b1b716105 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index2/I__init_1.sql @@ -0,0 +1 @@ +init diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index2/R__view_1.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index2/R__view_1.sql new file mode 100644 index 000000000..0f2416ebf --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index2/R__view_1.sql @@ -0,0 +1 @@ +Something diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.0__a.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.0__a.sql new file mode 100644 index 000000000..789819226 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.0__a.sql @@ -0,0 +1 @@ +a diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.1__b.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.1__b.sql new file mode 100644 index 000000000..617807982 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g1/1.1__b.sql @@ -0,0 +1 @@ +b diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.0__a.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.0__a.sql new file mode 100644 index 000000000..94226dabb --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.0__a.sql @@ -0,0 +1 @@ +2a diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.1__2b.sql b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.1__2b.sql new file mode 100644 index 000000000..b8a4cf4af --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index2/g2/2.1__2b.sql @@ -0,0 +1 @@ +2b From db34afe867af3e2df20d3546c2e039c412a50bd4 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 3 Aug 2021 00:03:02 +1200 Subject: [PATCH 54/87] #2284 - ENH: ddl-generator - add optional generation of idx_.migrations file --- .../resources/dbmigration/index/idx_postgres.migrations | 5 +++++ .../test/resources/dbmigration/index2/idx_h2.migrations | 7 +++++++ .../dbmigration/migrationtest/db2/idx_db2.migrations | 6 ++++++ .../dbmigration/migrationtest/h2/idx_h2.migrations | 6 ++++++ .../dbmigration/migrationtest/hana/idx_hana.migrations | 6 ++++++ .../dbmigration/migrationtest/hsqldb/idx_hsqldb.migrations | 6 ++++++ .../dbmigration/migrationtest/mysql/idx_mysql.migrations | 6 ++++++ .../dbmigration/migrationtest/mysql55/idx_mysql.migrations | 6 ++++++ .../dbmigration/migrationtest/oracle/idx_oracle.migrations | 6 ++++++ .../migrationtest/postgres/idx_postgres.migrations | 6 ++++++ .../dbmigration/migrationtest/sqlite/idx_sqlite.migrations | 6 ++++++ .../migrationtest/sqlserver17/idx_sqlserver.migrations | 7 +++++++ 12 files changed, 73 insertions(+) create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index/idx_postgres.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/index2/idx_h2.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/db2/idx_db2.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/h2/idx_h2.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hana/idx_hana.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hsqldb/idx_hsqldb.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql/idx_mysql.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql55/idx_mysql.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/oracle/idx_oracle.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/postgres/idx_postgres.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlite/idx_sqlite.migrations create mode 100644 ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlserver17/idx_sqlserver.migrations diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index/idx_postgres.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/index/idx_postgres.migrations new file mode 100644 index 000000000..2ccea54d7 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index/idx_postgres.migrations @@ -0,0 +1,5 @@ +-965417868, I__init_1.sql +907060870, 1.0__hello.sql +-1938594527, 1.1__foo.sql +-1960070312, R__view_1.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/index2/idx_h2.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/index2/idx_h2.migrations new file mode 100644 index 000000000..ef6e14321 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/index2/idx_h2.migrations @@ -0,0 +1,7 @@ +-965417868, I__init_1.sql +-390611389, g1/1.0__a.sql +1908338681, g1/1.1__b.sql +-1776543936, g2/2.0__a.sql +253052666, g2/2.1__2b.sql +-1960070312, R__view_1.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/db2/idx_db2.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/db2/idx_db2.migrations new file mode 100644 index 000000000..ec4208068 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/db2/idx_db2.migrations @@ -0,0 +1,6 @@ +441570368, 1.0__initial.sql +-94595879, 1.1.sql +578073685, 1.2__dropsFor_1.1.sql +-509420890, 1.3.sql +-1475628451, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/h2/idx_h2.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/h2/idx_h2.migrations new file mode 100644 index 000000000..fb21ce854 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/h2/idx_h2.migrations @@ -0,0 +1,6 @@ +-745768926, 1.0__initial.sql +39858255, 1.1.sql +1616986842, 1.2__dropsFor_1.1.sql +-1513154593, 1.3.sql +374569329, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hana/idx_hana.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hana/idx_hana.migrations new file mode 100644 index 000000000..984c4bb50 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hana/idx_hana.migrations @@ -0,0 +1,6 @@ +-1536923954, 1.0__initial.sql +1039838314, 1.1.sql +562867593, 1.2__dropsFor_1.1.sql +1566488731, 1.3.sql +1030652294, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hsqldb/idx_hsqldb.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hsqldb/idx_hsqldb.migrations new file mode 100644 index 000000000..ee5e2bd41 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/hsqldb/idx_hsqldb.migrations @@ -0,0 +1,6 @@ +2097980375, 1.0__initial.sql +2086418403, 1.1.sql +-1462014216, 1.2__dropsFor_1.1.sql +-2039573992, 1.3.sql +2106151405, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql/idx_mysql.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql/idx_mysql.migrations new file mode 100644 index 000000000..2ce271cdc --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql/idx_mysql.migrations @@ -0,0 +1,6 @@ +1075178692, 1.0__initial.sql +880212944, 1.1.sql +1029390755, 1.2__dropsFor_1.1.sql +-380371830, 1.3.sql +1085680731, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql55/idx_mysql.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql55/idx_mysql.migrations new file mode 100644 index 000000000..9bf86f51e --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/mysql55/idx_mysql.migrations @@ -0,0 +1,6 @@ +-1087663151, 1.0__initial.sql +880212944, 1.1.sql +1029390755, 1.2__dropsFor_1.1.sql +-380371830, 1.3.sql +1085680731, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/oracle/idx_oracle.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/oracle/idx_oracle.migrations new file mode 100644 index 000000000..533b50b15 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/oracle/idx_oracle.migrations @@ -0,0 +1,6 @@ +1164675950, 1.0__initial.sql +-1916315387, 1.1.sql +238598298, 1.2__dropsFor_1.1.sql +483114276, 1.3.sql +1213528478, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/postgres/idx_postgres.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/postgres/idx_postgres.migrations new file mode 100644 index 000000000..7e74e6b50 --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/postgres/idx_postgres.migrations @@ -0,0 +1,6 @@ +1329543701, 1.0__initial.sql +-1877647184, 1.1.sql +-1861367028, 1.2__dropsFor_1.1.sql +-1798982281, 1.3.sql +1959776888, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlite/idx_sqlite.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlite/idx_sqlite.migrations new file mode 100644 index 000000000..919841fde --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlite/idx_sqlite.migrations @@ -0,0 +1,6 @@ +1429491518, 1.0__initial.sql +-347121868, 1.1.sql +1359055889, 1.2__dropsFor_1.1.sql +-1764531063, 1.3.sql +-1070218324, 1.4__dropsFor_1.3.sql + diff --git a/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlserver17/idx_sqlserver.migrations b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlserver17/idx_sqlserver.migrations new file mode 100644 index 000000000..f7cd1c04a --- /dev/null +++ b/ebean-ddl-generator/src/test/resources/dbmigration/migrationtest/sqlserver17/idx_sqlserver.migrations @@ -0,0 +1,7 @@ +-2122378240, I__create_procs.sql +-1048913407, 1.0__initial.sql +615613536, 1.1.sql +-1805601919, 1.2__dropsFor_1.1.sql +-1791137342, 1.3.sql +460536923, 1.4__dropsFor_1.3.sql + From a06f7bea41f53a4b77763b602730dbf0bd413d78 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 5 Aug 2021 12:55:42 +1200 Subject: [PATCH 55/87] Tidy javadoc for MutableValueInfo --- ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java index 0f9c223aa..27716b13c 100644 --- a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java +++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java @@ -4,10 +4,10 @@ package io.ebean.bean; * Holds information on mutable values (like plain beans stored as json). *

* Used internally in EntityBeanIntercept for dirty detection on mutable values. - * Typically dirty detection is based on a hash/checksum of json content or the + * Typically, mutation detection is based on a hash/checksum of json content or the * original json content itself. *

- * Refer to the mapping options {@code @DbJson(dirtyDetection)} and {@code @DbJson(keepSource)}. + * Refer to the mapping options {@code @DbJson(mutationDetection)}. */ public interface MutableValueInfo { From 2043fdf7e1a8b845b383c5287a965cb2fe8ba9e7 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 5 Aug 2021 13:12:18 +1200 Subject: [PATCH 56/87] #2274 Add DatabaseConfig jsonMutationDetection property setting the global default mode --- .../java/io/ebean/config/DatabaseConfig.java | 28 +++++++++++++++++-- .../server/type/DefaultTypeManager.java | 2 +- .../type/ScalarTypeJsonObjectMapper.java | 3 +- .../server/type/TypeJsonManager.java | 9 +++++- .../io/ebean/config/ServerConfigTest.java | 7 ++++- 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java index 476007ebe..d1f0346b5 100644 --- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java +++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java @@ -7,9 +7,7 @@ import io.ebean.EbeanVersion; import io.ebean.PersistenceContextScope; import io.ebean.Query; import io.ebean.Transaction; -import io.ebean.annotation.Encrypted; -import io.ebean.annotation.PersistBatch; -import io.ebean.annotation.Platform; +import io.ebean.annotation.*; import io.ebean.cache.ServerCachePlugin; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.DbEncrypt; @@ -193,6 +191,11 @@ public class DatabaseConfig { */ private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL; + /** + * The default mode used for {@code @DbJson} with Jackson ObjectMapper. + */ + private MutationDetection jsonMutationDetection = MutationDetection.HASH; + /** * The database platform name. Used to imply a DatabasePlatform to use. */ @@ -737,6 +740,24 @@ public class DatabaseConfig { this.jsonInclude = jsonInclude; } + /** + * Return the default MutableDetection to use with {@code @DbJson} using Jackson. + * + * @see DbJson#mutationDetection() + */ + public MutationDetection getJsonMutationDetection() { + return jsonMutationDetection; + } + + /** + * Set the default MutableDetection to use with {@code @DbJson} using Jackson. + * + * @see DbJson#mutationDetection() + */ + public void setJsonMutationDetection(MutationDetection jsonMutationDetection) { + this.jsonMutationDetection = jsonMutationDetection; + } + /** * Return the name of the Database. */ @@ -2909,6 +2930,7 @@ public class DatabaseConfig { jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude); jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime); jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate); + jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection); runMigration = p.getBoolean("migration.run", runMigration); ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index 8ffb15372..1400b2180 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -134,7 +134,7 @@ public final class DefaultTypeManager implements TypeManager { this.postgres = isPostgres(config.getDatabasePlatform()); this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent(); this.objectMapper = (objectMapperPresent) ? initObjectMapper(config) : null; - this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper) : null; + this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.getJsonMutationDetection()) : null; this.extraTypeFactory = new DefaultTypeFactory(config); this.arrayTypeListFactory = arrayTypeListFactory(config.getDatabasePlatform()); this.arrayTypeSetFactory = arrayTypeSetFactory(config.getDatabasePlatform()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 334f4b232..7ae24420f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -38,7 +38,8 @@ class ScalarTypeJsonObjectMapper { } else if (mode != MutationDetection.DEFAULT) { return new GenericObject(jsonManager, field, dbType, docType); } - prop.setMutationDetection(MutationDetection.HASH); + // using the global default MutationDetection mode (defaults to HASH) + prop.setMutationDetection(jsonManager.mutationDetection()); return new GenericObject(jsonManager, field, dbType, docType); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java index 9cc0b5f0d..989ba2729 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeJsonManager.java @@ -2,16 +2,23 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.databind.ObjectMapper; import io.ebean.ModifyAwareType; +import io.ebean.annotation.MutationDetection; import io.ebean.config.dbplatform.DbPlatformType; class TypeJsonManager { private final boolean postgres; private final ObjectMapper objectMapper; + private final MutationDetection mutationDetection; - TypeJsonManager(boolean postgres, Object objectMapper) { + TypeJsonManager(boolean postgres, Object objectMapper, MutationDetection mutationDetection) { this.postgres = postgres; this.objectMapper = (ObjectMapper) objectMapper; + this.mutationDetection = mutationDetection; + } + + MutationDetection mutationDetection() { + return mutationDetection; } ObjectMapper objectMapper() { diff --git a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java b/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java index 55eb1f06e..46f283e55 100644 --- a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java +++ b/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java @@ -1,6 +1,7 @@ package io.ebean.config; import com.fasterxml.jackson.databind.ObjectMapper; +import io.ebean.annotation.MutationDetection; import io.ebean.annotation.PersistBatch; import io.ebean.config.dbplatform.IdType; import io.ebean.datasource.DataSourceConfig; @@ -62,7 +63,7 @@ public class ServerConfigTest { props.setProperty("dbOffline", "true"); props.setProperty("jsonDateTime", "MILLIS"); props.setProperty("jsonDate", "MILLIS"); - props.setProperty("jsonDirtyByDefault", "false"); + props.setProperty("jsonMutationDetection", "NONE"); props.setProperty("autoReadOnlyDataSource", "true"); props.setProperty("disableL2Cache", "true"); props.setProperty("notifyL2CacheInForeground", "true"); @@ -98,6 +99,9 @@ public class ServerConfigTest { assertThat(serverConfig.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class); + assertEquals(MutationDetection.NONE, serverConfig.getJsonMutationDetection()); + serverConfig.setJsonMutationDetection(MutationDetection.SOURCE); + assertEquals(MutationDetection.SOURCE, serverConfig.getJsonMutationDetection()); assertEquals(IdType.SEQUENCE, serverConfig.getIdType()); assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch()); assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade()); @@ -156,6 +160,7 @@ public class ServerConfigTest { assertFalse(serverConfig.isIdGeneratorAutomatic()); assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime()); assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate()); + assertEquals(MutationDetection.HASH, serverConfig.getJsonMutationDetection()); assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation()); assertTrue(serverConfig.isAutoLoadModuleInfo()); From a76442641b5eff698a0c36312ab9da3d35549e9d Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 5 Aug 2021 13:14:00 +1200 Subject: [PATCH 57/87] Tidy test only - rename ServerConfigTest to DatabaseConfigTest and tidy --- .../io/ebean/config/DatabaseConfigTest.java | 194 ++++++++++++++++++ .../io/ebean/config/ServerConfigTest.java | 194 ------------------ 2 files changed, 194 insertions(+), 194 deletions(-) create mode 100644 ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java delete mode 100644 ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java diff --git a/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java b/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java new file mode 100644 index 000000000..dd132a8e3 --- /dev/null +++ b/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java @@ -0,0 +1,194 @@ +package io.ebean.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.ebean.annotation.MutationDetection; +import io.ebean.annotation.PersistBatch; +import io.ebean.config.dbplatform.IdType; +import io.ebean.datasource.DataSourceConfig; +import org.junit.Test; + +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class DatabaseConfigTest { + + @Test + public void testLoadFromEbeanProperties() { + + DatabaseConfig config = new DatabaseConfig(); + config.loadFromProperties(); + + assertEquals(PersistBatch.NONE, config.getPersistBatch()); + assertNotNull(config.getProperties()); + } + + @Test + public void evalPropertiesInput() { + + String home = System.getenv("HOME"); + + Properties props = new Properties(); + props.setProperty("ddl.initSql", "${HOME}/initSql"); + + DatabaseConfig config = new DatabaseConfig(); + config.loadFromProperties(props); + + String ddlInitSql = config.getDdlInitSql(); + assertThat(ddlInitSql).isEqualTo(home+"/initSql"); + } + + @Test + public void testLoadWithProperties() { + + DatabaseConfig config = new DatabaseConfig(); + config.setPersistBatch(PersistBatch.NONE); + config.setPersistBatchOnCascade(PersistBatch.NONE); + config.setAutoReadOnlyDataSource(false); + config.setReadOnlyDataSource(null); + config.setReadOnlyDataSourceConfig(new DataSourceConfig()); + + Properties props = new Properties(); + props.setProperty("persistBatch", "ALL"); + props.setProperty("persistBatchOnCascade", "ALL"); + props.setProperty("dbuuid", "binary"); + props.setProperty("jdbcFetchSizeFindEach", "42"); + props.setProperty("jdbcFetchSizeFindList", "43"); + props.setProperty("backgroundExecutorShutdownSecs", "98"); + props.setProperty("backgroundExecutorSchedulePoolSize", "4"); + props.setProperty("dbOffline", "true"); + props.setProperty("jsonDateTime", "MILLIS"); + props.setProperty("jsonDate", "MILLIS"); + props.setProperty("jsonMutationDetection", "NONE"); + props.setProperty("autoReadOnlyDataSource", "true"); + props.setProperty("disableL2Cache", "true"); + props.setProperty("notifyL2CacheInForeground", "true"); + props.setProperty("idType", "SEQUENCE"); + props.setProperty("mappingLocations", "classpath:/foo;bar"); + props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention"); + props.setProperty("idGeneratorAutomatic", "true"); + props.setProperty("enabledL2Regions", "r0,users,orgs"); + props.setProperty("caseSensitiveCollation", "false"); + props.setProperty("loadModuleInfo", "true"); + props.setProperty("forUpdateNoKey", "true"); + props.setProperty("defaultServer", "false"); + + props.setProperty("queryPlan.enable", "true"); + props.setProperty("queryPlan.thresholdMicros", "10000"); + props.setProperty("queryPlan.capture", "true"); + props.setProperty("queryPlan.capturePeriodSecs", "42"); + props.setProperty("queryPlan.captureMaxTimeMillis", "560"); + props.setProperty("queryPlan.captureMaxCount", "7"); + + config.loadFromProperties(props); + + assertFalse(config.isDefaultServer()); + assertTrue(config.isDisableL2Cache()); + assertTrue(config.isNotifyL2CacheInForeground()); + assertTrue(config.isDbOffline()); + assertTrue(config.isAutoReadOnlyDataSource()); + assertTrue(config.isAutoLoadModuleInfo()); + + assertTrue(config.isIdGeneratorAutomatic()); + assertFalse(config.getPlatformConfig().isCaseSensitiveCollation()); + assertTrue(config.getPlatformConfig().isForUpdateNoKey()); + + assertThat(config.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class); + + assertEquals(MutationDetection.NONE, config.getJsonMutationDetection()); + config.setJsonMutationDetection(MutationDetection.SOURCE); + assertEquals(MutationDetection.SOURCE, config.getJsonMutationDetection()); + assertEquals(IdType.SEQUENCE, config.getIdType()); + assertEquals(PersistBatch.ALL, config.getPersistBatch()); + assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade()); + assertEquals(PlatformConfig.DbUuid.BINARY, config.getPlatformConfig().getDbUuid()); + assertEquals(JsonConfig.DateTime.MILLIS, config.getJsonDateTime()); + assertEquals(JsonConfig.Date.MILLIS, config.getJsonDate()); + + assertEquals("r0,users,orgs", config.getEnabledL2Regions()); + + assertEquals(42, config.getJdbcFetchSizeFindEach()); + assertEquals(43, config.getJdbcFetchSizeFindList()); + assertEquals(4, config.getBackgroundExecutorSchedulePoolSize()); + assertEquals(98, config.getBackgroundExecutorShutdownSecs()); + + assertTrue(config.isQueryPlanEnable()); + assertEquals(10000, config.getQueryPlanThresholdMicros()); + assertTrue(config.isQueryPlanCapture()); + assertEquals(42, config.getQueryPlanCapturePeriodSecs()); + assertEquals(560, config.getQueryPlanCaptureMaxTimeMillis()); + assertEquals(7, config.getQueryPlanCaptureMaxCount()); + + assertThat(config.getMappingLocations()).containsExactly("classpath:/foo","bar"); + + config.setPersistBatch(PersistBatch.NONE); + config.setPersistBatchOnCascade(PersistBatch.NONE); + + Properties props1 = new Properties(); + props1.setProperty("ebean.persistBatch", "ALL"); + props1.setProperty("ebean.persistBatchOnCascade", "ALL"); + + config.setNotifyL2CacheInForeground(true); + config.setDisableL2Cache(true); + props1.setProperty("ebean.disableL2Cache", "false"); + props1.setProperty("ebean.notifyL2CacheInForeground", "false"); + + config.loadFromProperties(props1); + assertFalse(config.isDisableL2Cache()); + assertFalse(config.isNotifyL2CacheInForeground()); + + assertEquals(PersistBatch.ALL, config.getPersistBatch()); + assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade()); + + config.setEnabledL2Regions("r0,orgs"); + assertEquals("r0,orgs", config.getEnabledL2Regions()); + } + + @Test + public void test_defaults() { + + DatabaseConfig config = new DatabaseConfig(); + assertTrue(config.isIdGeneratorAutomatic()); + assertTrue(config.isDefaultServer()); + assertFalse(config.isAutoPersistUpdates()); + + config.setIdGeneratorAutomatic(false); + assertFalse(config.isIdGeneratorAutomatic()); + assertEquals(JsonConfig.DateTime.ISO8601, config.getJsonDateTime()); + assertEquals(JsonConfig.Date.ISO8601, config.getJsonDate()); + assertEquals(MutationDetection.HASH, config.getJsonMutationDetection()); + assertTrue(config.getPlatformConfig().isCaseSensitiveCollation()); + assertTrue(config.isAutoLoadModuleInfo()); + + assertFalse(config.isQueryPlanEnable()); + assertEquals(Long.MAX_VALUE, config.getQueryPlanThresholdMicros()); + assertFalse(config.isQueryPlanCapture()); + assertEquals(600, config.getQueryPlanCapturePeriodSecs()); + assertEquals(10000L, config.getQueryPlanCaptureMaxTimeMillis()); + assertEquals(10, config.getQueryPlanCaptureMaxCount()); + + config.setLoadModuleInfo(false); + assertFalse(config.isAutoLoadModuleInfo()); + config.setAutoPersistUpdates(true); + assertTrue(config.isAutoPersistUpdates()); + } + + @Test + public void test_putServiceObject() { + + ObjectMapper objectMapper = new ObjectMapper(); + + DatabaseConfig config = new DatabaseConfig(); + config.putServiceObject(objectMapper); + + ObjectMapper mapper0 = config.getServiceObject(ObjectMapper.class); + ObjectMapper mapper1 = (ObjectMapper)config.getServiceObject("objectMapper"); + + assertThat(objectMapper).isSameAs(mapper0); + assertThat(objectMapper).isSameAs(mapper1); + } +} diff --git a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java b/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java deleted file mode 100644 index 46f283e55..000000000 --- a/ebean-core/src/test/java/io/ebean/config/ServerConfigTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package io.ebean.config; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.ebean.annotation.MutationDetection; -import io.ebean.annotation.PersistBatch; -import io.ebean.config.dbplatform.IdType; -import io.ebean.datasource.DataSourceConfig; -import org.junit.Test; - -import java.util.Properties; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -public class ServerConfigTest { - - @Test - public void testLoadFromEbeanProperties() { - - ServerConfig serverConfig = new ServerConfig(); - serverConfig.loadFromProperties(); - - assertEquals(PersistBatch.NONE, serverConfig.getPersistBatch()); - assertNotNull(serverConfig.getProperties()); - } - - @Test - public void evalPropertiesInput() { - - String home = System.getenv("HOME"); - - Properties props = new Properties(); - props.setProperty("ddl.initSql", "${HOME}/initSql"); - - ServerConfig serverConfig = new ServerConfig(); - serverConfig.loadFromProperties(props); - - String ddlInitSql = serverConfig.getDdlInitSql(); - assertThat(ddlInitSql).isEqualTo(home+"/initSql"); - } - - @Test - public void testLoadWithProperties() { - - ServerConfig serverConfig = new ServerConfig(); - serverConfig.setPersistBatch(PersistBatch.NONE); - serverConfig.setPersistBatchOnCascade(PersistBatch.NONE); - serverConfig.setAutoReadOnlyDataSource(false); - serverConfig.setReadOnlyDataSource(null); - serverConfig.setReadOnlyDataSourceConfig(new DataSourceConfig()); - - Properties props = new Properties(); - props.setProperty("persistBatch", "ALL"); - props.setProperty("persistBatchOnCascade", "ALL"); - props.setProperty("dbuuid", "binary"); - props.setProperty("jdbcFetchSizeFindEach", "42"); - props.setProperty("jdbcFetchSizeFindList", "43"); - props.setProperty("backgroundExecutorShutdownSecs", "98"); - props.setProperty("backgroundExecutorSchedulePoolSize", "4"); - props.setProperty("dbOffline", "true"); - props.setProperty("jsonDateTime", "MILLIS"); - props.setProperty("jsonDate", "MILLIS"); - props.setProperty("jsonMutationDetection", "NONE"); - props.setProperty("autoReadOnlyDataSource", "true"); - props.setProperty("disableL2Cache", "true"); - props.setProperty("notifyL2CacheInForeground", "true"); - props.setProperty("idType", "SEQUENCE"); - props.setProperty("mappingLocations", "classpath:/foo;bar"); - props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention"); - props.setProperty("idGeneratorAutomatic", "true"); - props.setProperty("enabledL2Regions", "r0,users,orgs"); - props.setProperty("caseSensitiveCollation", "false"); - props.setProperty("loadModuleInfo", "true"); - props.setProperty("forUpdateNoKey", "true"); - props.setProperty("defaultServer", "false"); - - props.setProperty("queryPlan.enable", "true"); - props.setProperty("queryPlan.thresholdMicros", "10000"); - props.setProperty("queryPlan.capture", "true"); - props.setProperty("queryPlan.capturePeriodSecs", "42"); - props.setProperty("queryPlan.captureMaxTimeMillis", "560"); - props.setProperty("queryPlan.captureMaxCount", "7"); - - serverConfig.loadFromProperties(props); - - assertFalse(serverConfig.isDefaultServer()); - assertTrue(serverConfig.isDisableL2Cache()); - assertTrue(serverConfig.isNotifyL2CacheInForeground()); - assertTrue(serverConfig.isDbOffline()); - assertTrue(serverConfig.isAutoReadOnlyDataSource()); - assertTrue(serverConfig.isAutoLoadModuleInfo()); - - assertTrue(serverConfig.isIdGeneratorAutomatic()); - assertFalse(serverConfig.getPlatformConfig().isCaseSensitiveCollation()); - assertTrue(serverConfig.getPlatformConfig().isForUpdateNoKey()); - - assertThat(serverConfig.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class); - - assertEquals(MutationDetection.NONE, serverConfig.getJsonMutationDetection()); - serverConfig.setJsonMutationDetection(MutationDetection.SOURCE); - assertEquals(MutationDetection.SOURCE, serverConfig.getJsonMutationDetection()); - assertEquals(IdType.SEQUENCE, serverConfig.getIdType()); - assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch()); - assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade()); - assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid()); - assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime()); - assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate()); - - assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions()); - - assertEquals(42, serverConfig.getJdbcFetchSizeFindEach()); - assertEquals(43, serverConfig.getJdbcFetchSizeFindList()); - assertEquals(4, serverConfig.getBackgroundExecutorSchedulePoolSize()); - assertEquals(98, serverConfig.getBackgroundExecutorShutdownSecs()); - - assertTrue(serverConfig.isQueryPlanEnable()); - assertEquals(10000, serverConfig.getQueryPlanThresholdMicros()); - assertTrue(serverConfig.isQueryPlanCapture()); - assertEquals(42, serverConfig.getQueryPlanCapturePeriodSecs()); - assertEquals(560, serverConfig.getQueryPlanCaptureMaxTimeMillis()); - assertEquals(7, serverConfig.getQueryPlanCaptureMaxCount()); - - assertThat(serverConfig.getMappingLocations()).containsExactly("classpath:/foo","bar"); - - serverConfig.setPersistBatch(PersistBatch.NONE); - serverConfig.setPersistBatchOnCascade(PersistBatch.NONE); - - Properties props1 = new Properties(); - props1.setProperty("ebean.persistBatch", "ALL"); - props1.setProperty("ebean.persistBatchOnCascade", "ALL"); - - serverConfig.setNotifyL2CacheInForeground(true); - serverConfig.setDisableL2Cache(true); - props1.setProperty("ebean.disableL2Cache", "false"); - props1.setProperty("ebean.notifyL2CacheInForeground", "false"); - - serverConfig.loadFromProperties(props1); - assertFalse(serverConfig.isDisableL2Cache()); - assertFalse(serverConfig.isNotifyL2CacheInForeground()); - - assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch()); - assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade()); - - serverConfig.setEnabledL2Regions("r0,orgs"); - assertEquals("r0,orgs", serverConfig.getEnabledL2Regions()); - } - - @Test - public void test_defaults() { - - ServerConfig serverConfig = new ServerConfig(); - assertTrue(serverConfig.isIdGeneratorAutomatic()); - assertTrue(serverConfig.isDefaultServer()); - assertFalse(serverConfig.isAutoPersistUpdates()); - - serverConfig.setIdGeneratorAutomatic(false); - assertFalse(serverConfig.isIdGeneratorAutomatic()); - assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime()); - assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate()); - assertEquals(MutationDetection.HASH, serverConfig.getJsonMutationDetection()); - assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation()); - assertTrue(serverConfig.isAutoLoadModuleInfo()); - - assertFalse(serverConfig.isQueryPlanEnable()); - assertEquals(Long.MAX_VALUE, serverConfig.getQueryPlanThresholdMicros()); - assertFalse(serverConfig.isQueryPlanCapture()); - assertEquals(600, serverConfig.getQueryPlanCapturePeriodSecs()); - assertEquals(10000L, serverConfig.getQueryPlanCaptureMaxTimeMillis()); - assertEquals(10, serverConfig.getQueryPlanCaptureMaxCount()); - - serverConfig.setLoadModuleInfo(false); - assertFalse(serverConfig.isAutoLoadModuleInfo()); - serverConfig.setAutoPersistUpdates(true); - assertTrue(serverConfig.isAutoPersistUpdates()); - } - - @Test - public void test_putServiceObject() { - - ObjectMapper objectMapper = new ObjectMapper(); - - ServerConfig config = new ServerConfig(); - config.putServiceObject(objectMapper); - - ObjectMapper mapper0 = config.getServiceObject(ObjectMapper.class); - ObjectMapper mapper1 = (ObjectMapper)config.getServiceObject("objectMapper"); - - assertThat(objectMapper).isSameAs(mapper0); - assertThat(objectMapper).isSameAs(mapper1); - } -} From 9a82dbb3e6e22bfac5799cdb0ea405eb354e05aa Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 5 Aug 2021 13:17:32 +1200 Subject: [PATCH 58/87] Bump parent to java8-oss 3.2 with GPG maven plugin active only with "release" profile The GPG maven plugin is now only active with the "release" profile use -P release when we want to release with GPG signing --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0328ea5b6..465b49402 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.avaje java8-oss - 3.1 + 3.2 io.ebean From d98a31fa6b64f9dbf0cb57b018c4a6fb38561beb Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 5 Aug 2021 16:53:51 +1200 Subject: [PATCH 59/87] Bump to 12.11.0-SNAPSHOT --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 4 ++-- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 8 ++++---- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 8 ++++---- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 8 ++++---- pom.xml | 2 +- querybean-generator/pom.xml | 2 +- 16 files changed, 59 insertions(+), 59 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 503c2f2c5..9afbb6446 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index 1534f122b..0b0355981 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 0e6ebde4d..9f79cc9c2 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-core-type - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-ddl-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-externalmapping-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-externalmapping-xml - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-autotune - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-querybean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean querybean-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided io.ebean kotlin-querybean-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided io.ebean ebean-test - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test io.ebean ebean-postgis - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-redis - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index ad1d7c207..35a29a677 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 8303c519d..cfac597fa 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean-core @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-core-type - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-externalmapping-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index dec77c4b0..d7267945e 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean ddl generation @@ -28,14 +28,14 @@ io.ebean ebean-core-type - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 5cb459f61..a9b9968b9 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 8275fc989..0ce9ce6ca 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test io.ebean ebean-ddl-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index ff1beee77..a6bfe2ed0 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index d9a2cdd6e..b3ffadfc0 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test io.ebean querybean-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test io.ebean ebean-test - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index 097cd9318..d0d5f8ee2 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided io.ebean ebean-querybean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test io.ebean querybean-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test io.ebean ebean-test - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index e889b6204..43b5ee6f3 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT provided io.ebean ebean-ddl-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT diff --git a/ebean/pom.xml b/ebean/pom.xml index ddd60d284..819328290 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT io.ebean ebean-querybean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index c3a487ecf..8f3cafa8a 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -5,7 +5,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT kotlin querybean generator @@ -30,7 +30,7 @@ io.ebean ebean-querybean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test @@ -44,7 +44,7 @@ io.ebean ebean-core - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test @@ -65,7 +65,7 @@ io.ebean ebean-ddl-generator - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT test diff --git a/pom.xml b/pom.xml index 465b49402..b86928387 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT pom ebean parent diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 27e5d0399..956e39cb5 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.10.1-SNAPSHOT + 12.11.0-SNAPSHOT querybean generator From f34e0f5f2b8536f8d452818b0f04582f89c3bf72 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 5 Aug 2021 23:03:02 +1200 Subject: [PATCH 60/87] #2287 - ebean-migration: For multiple platforms automatically run the correct migrations --- ebean-bom/pom.xml | 8 +++--- ebean-core/pom.xml | 4 +-- .../server/core/DefaultServer.java | 1 + .../server/core/bootup/BootupClasses.java | 27 +++---------------- ebean-ddl-generator/pom.xml | 2 +- .../dbmigration/DefaultDbMigration.java | 9 +++---- .../dbmigration/IndexMigration.java | 5 +--- .../dbmigration/IndexMigrationTest.java | 4 +-- ebean-externalmapping-xml/pom.xml | 2 +- .../xmlmapping/XmlMappingReader.java | 3 +-- 10 files changed, 21 insertions(+), 44 deletions(-) diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 9f79cc9c2..35ed6d6a0 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -14,12 +14,12 @@ 1.0 - 1.0 - 12.4.0 + 1.1 + 12.11.0 4.1 7.0 - 12.10.0 - 12.10.0 + 12.11.0 + 12.11.0 diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index cfac597fa..3426582d3 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -52,13 +52,13 @@ io.avaje classpath-scanner - 4.2 + 6.0 io.ebean ebean-migration-auto - 1.0 + 1.1 diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 4aa90ad13..d21f9967f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -397,6 +397,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { if (dbSchema != null) { migrationRunner.setDefaultDbSchema(dbSchema); } + migrationRunner.setPlatform(config.getDatabasePlatform().getPlatform().base().name().toLowerCase()); migrationRunner.loadProperties(config.getProperties()); migrationRunner.run(config.getDataSource()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java index f7c1a5fea..aba5533e0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java @@ -1,6 +1,5 @@ package io.ebeaninternal.server.core.bootup; -import io.avaje.classpath.scanner.ClassFilter; import io.ebean.annotation.DocStore; import io.ebean.config.DatabaseConfig; import io.ebean.config.IdGenerator; @@ -27,27 +26,23 @@ import javax.persistence.Embeddable; import javax.persistence.Entity; import javax.persistence.Table; import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; /** * Interesting classes for a EbeanServer such as Embeddable, Entity, * ScalarTypes, Finders, Listeners and Controllers. */ -public class BootupClasses implements ClassFilter { +public class BootupClasses implements Predicate> { private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class); private final List> embeddableList = new ArrayList<>(); - private final List> entityList = new ArrayList<>(); - private final List>> scalarTypeList = new ArrayList<>(); - private final List>> scalarConverterList = new ArrayList<>(); - private final List>> attributeConverterList = new ArrayList<>(); // The following objects are instantiated on first request @@ -55,19 +50,12 @@ public class BootupClasses implements ClassFilter { // instance list, that holds the instance. Once a class is instantiated // (or added) it will get removed from the candidate list private final List> idGeneratorCandidates = new ArrayList<>(); - private final List> beanPersistControllerCandidates = new ArrayList<>(); - private final List> beanPostLoadCandidates = new ArrayList<>(); - private final List> beanPostConstructListenerCandidates = new ArrayList<>(); - private final List> beanFindControllerCandidates = new ArrayList<>(); - private final List> beanPersistListenerCandidates = new ArrayList<>(); - private final List> beanQueryAdapterCandidates = new ArrayList<>(); - private final List> serverConfigStartupCandidates = new ArrayList<>(); private final List idGeneratorInstances = new ArrayList<>(); @@ -98,7 +86,7 @@ public class BootupClasses implements ClassFilter { public BootupClasses(List> list) { if (list != null) { for (Class cls : list) { - isMatch(cls); + test(cls); } } } @@ -188,13 +176,11 @@ public class BootupClasses implements ClassFilter { } public void addChangeLogInstances(DatabaseConfig config) { - readAuditPrepare = config.getReadAuditPrepare(); readAuditLogger = config.getReadAuditLogger(); changeLogPrepare = config.getChangeLogPrepare(); changeLogListener = config.getChangeLogListener(); changeLogRegister = config.getChangeLogRegister(); - // if not already set create the implementations found // via classpath scanning if (readAuditPrepare == null && readAuditPrepareClass != null) { @@ -341,18 +327,14 @@ public class BootupClasses implements ClassFilter { } @Override - public boolean isMatch(Class cls) { - + public boolean test(Class cls) { if (isEmbeddable(cls)) { embeddableList.add(cls); - } else if (isEntity(cls)) { entityList.add(cls); - } else { return isInterestingInterface(cls); } - return true; } @@ -364,7 +346,6 @@ public class BootupClasses implements ClassFilter { */ @SuppressWarnings("unchecked") private boolean isInterestingInterface(Class cls) { - if (Modifier.isAbstract(cls.getModifiers())) { // do not include abstract classes as we can // not instantiate them diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index d7267945e..4035af5f2 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -22,7 +22,7 @@ io.ebean ebean-migration - 12.4.0 + 12.11.0 diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 7f2a90558..5bb40fa08 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -281,7 +281,7 @@ public class DefaultDbMigration implements DbMigration { public String generateMigration() throws IOException { final String version = generateMigrationFor(false); if (includeIndex) { - generateIndex(version); + generateIndex(); } return version; } @@ -289,15 +289,14 @@ public class DefaultDbMigration implements DbMigration { /** * Generate the {@code idx_platform.migrations} file. */ - private void generateIndex(String version) throws IOException { - final boolean overwrite = version != null; + private void generateIndex() throws IOException { final File topDir = migrationDirectory(false); if (!platforms.isEmpty()) { for (Pair pair : platforms) { - new IndexMigration(topDir, pair).generate(overwrite); + new IndexMigration(topDir, pair).generate(); } } else { - new IndexMigration(topDir, databasePlatform).generate(overwrite); + new IndexMigration(topDir, databasePlatform).generate(); } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java index 215c18e72..baedb77b9 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/IndexMigration.java @@ -43,10 +43,7 @@ class IndexMigration { return new File(topDir, name); } - void generate(boolean overwrite) throws IOException { - if (!overwrite && indexFile.exists()) { - return; - } + void generate() throws IOException { readSqlFiles(topDir); generateIndex(); } diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java index 5a04d8917..a22a42ec7 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/IndexMigrationTest.java @@ -24,7 +24,7 @@ public class IndexMigrationTest { } DatabasePlatform pg = new PostgresPlatform(); IndexMigration indexMigration = new IndexMigration(topDir, pg); - indexMigration.generate(true); + indexMigration.generate(); File expected = new File(topDir, "idx_postgres.migrations"); @@ -48,7 +48,7 @@ public class IndexMigrationTest { } DatabasePlatform pg = new H2Platform(); IndexMigration indexMigration = new IndexMigration(topDir, pg); - indexMigration.generate(true); + indexMigration.generate(); File expected = new File(topDir, "idx_h2.migrations"); assertThat(expected).exists(); diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 0ce9ce6ca..3f0b71546 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -45,7 +45,7 @@ io.avaje classpath-scanner - 4.2 + 6.0 diff --git a/ebean-externalmapping-xml/src/main/java/io/ebeaninternal/xmlmapping/XmlMappingReader.java b/ebean-externalmapping-xml/src/main/java/io/ebeaninternal/xmlmapping/XmlMappingReader.java index 2519ef8f1..8b28975ef 100644 --- a/ebean-externalmapping-xml/src/main/java/io/ebeaninternal/xmlmapping/XmlMappingReader.java +++ b/ebean-externalmapping-xml/src/main/java/io/ebeaninternal/xmlmapping/XmlMappingReader.java @@ -6,7 +6,6 @@ import io.ebeaninternal.xmlmapping.model.XmEbean; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -59,7 +58,7 @@ public class XmlMappingReader { try { List mappings = new ArrayList<>(); for (Resource xmlMappingRes : resourceList) { - try (InputStream is = new FileInputStream(xmlMappingRes.getLocationOnDisk())) { + try (InputStream is = xmlMappingRes.inputStream()) { mappings.add(XmlMappingReader.read(is)); } } From 7ca72a39b1c2cce8a35b2e79871145e4c5970498 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 6 Aug 2021 11:52:38 +1200 Subject: [PATCH 61/87] Change query metric hash from MD5 of sql + name + loc to Checksum of sql --- .../java/io/ebean/meta/MetaQueryMetric.java | 2 +- .../java/io/ebean/meta/MetaQueryPlan.java | 2 +- .../main/java/io/ebean/meta/MetricData.java | 6 ++-- .../java/io/ebean/meta/QueryPlanInit.java | 10 +++--- .../io/ebeaninternal/api/SpiQueryPlan.java | 2 +- .../server/core/DumpMetricsJson.java | 19 ++++++------ .../server/profile/DQueryPlanMeta.java | 16 +++------- .../server/profile/DQueryPlanMetric.java | 2 +- .../server/query/CQueryPlan.java | 25 +++------------ .../server/query/CQueryPlanStats.java | 2 +- .../server/query/DQueryPlanOutput.java | 7 ++--- .../io/ebeaninternal/server/util/Md5.java | 31 ------------------- .../io/ebeaninternal/server/util/Md5Test.java | 18 ----------- .../query/finder/TestCustomerFinder.java | 2 +- 14 files changed, 36 insertions(+), 108 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java delete mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java index 514b700dc..edfe978e5 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java @@ -23,6 +23,6 @@ public interface MetaQueryMetric extends MetaTimedMetric { /** * Return the hash of the plan. */ - String getHash(); + long getHash(); } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java index fb2b6d095..4f9667bd0 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java @@ -30,7 +30,7 @@ public interface MetaQueryPlan { /** * Return the hash of the plan. */ - String getHash(); + long getHash(); /** * Return a description of the bind values. diff --git a/ebean-api/src/main/java/io/ebean/meta/MetricData.java b/ebean-api/src/main/java/io/ebean/meta/MetricData.java index b4606b1d7..9a24aa9f2 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetricData.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetricData.java @@ -6,7 +6,7 @@ package io.ebean.meta; public class MetricData { private String name; - private String hash; + private long hash; private String loc; private String sql; @@ -30,11 +30,11 @@ public class MetricData { this.name = name; } - public String getHash() { + public long getHash() { return hash; } - public void setHash(String hash) { + public void setHash(long hash) { this.hash = hash; } diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java index 3c19caa39..9b785ec62 100644 --- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java +++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java @@ -10,7 +10,7 @@ public class QueryPlanInit { private boolean all; - private Set hashes = new HashSet<>(); + private Set hashes = new HashSet<>(); private long thresholdMicros; @@ -47,21 +47,21 @@ public class QueryPlanInit { /** * Return true if the query plan should be initiated based on it's hash. */ - public boolean includeHash(String hash) { - return all || hashes.contains(hash); + public boolean includeHash(long sqlHash) { + return all || hashes.contains(sqlHash); } /** * Return the specific hashes that we want to collect query plans on. */ - public Set getHashes() { + public Set getHashes() { return hashes; } /** * Set the specific hashes that we want to collect query plans on. */ - public void setHashes(Set hashes) { + public void setHashes(Set hashes) { this.hashes = hashes; } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java index 96a1eade7..cd6c83114 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java @@ -20,7 +20,7 @@ public interface SpiQueryPlan { /** * The hash for the query plan. */ - String getHash(); + long getHash(); /** * The SQL for the query plan. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java index ed13151c4..240d050f3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java @@ -197,7 +197,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { metricStart(metric); appendTiming(metric); if (withHash) { - appendExtra("hash", metric.getHash()); + keyVal("hash", metric.getHash()); } if (isIncludeDetail(metric)) { appendExtra("loc", metric.getLocation()); @@ -218,13 +218,14 @@ class DumpMetricsJson implements ServerMetricsAsJson { } private void appendTiming(MetaTimedMetric timedMetric) throws IOException { - key("count"); - val(timedMetric.getCount()); - key("total"); - val(timedMetric.getTotal()); - key("mean"); - val(timedMetric.getMean()); - key("max"); - val(timedMetric.getMax()); + keyVal("count", timedMetric.getCount()); + keyVal("total", timedMetric.getTotal()); + keyVal("mean", timedMetric.getMean()); + keyVal("max", timedMetric.getMax()); + } + + private void keyVal(String key, long value) throws IOException { + key(key); + val(value); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java index 0690b5fdd..0f25f0d9c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.profile; import io.ebean.ProfileLocation; -import io.ebeaninternal.server.util.Md5; +import io.ebeaninternal.server.util.Checksum; class DQueryPlanMeta { @@ -10,7 +10,7 @@ class DQueryPlanMeta { private final ProfileLocation profileLocation; private final String name; private final String sql; - private final String hash; + private final long hash; DQueryPlanMeta(Class type, String label, ProfileLocation profileLocation, String sql) { this.type = type; @@ -22,22 +22,14 @@ class DQueryPlanMeta { name += "_" + label; } this.name = name; - this.hash = initHash(); - } - - private String initHash() { - StringBuilder sb = new StringBuilder(sql).append("|").append(name); - if (profileLocation != null) { - sb.append("|").append(profileLocation.location()); - } - return Md5.hash(sb.toString()); + this.hash = Checksum.checksum(sql); } public Class getType() { return type; } - public String getHash() { + public long getHash() { return hash; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index 21559a152..765679404 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -59,7 +59,7 @@ class DQueryPlanMetric implements QueryPlanMetric { } @Override - public String getHash() { + public long getHash() { return meta.getHash(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index 295cbe7c2..3c322190f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -13,12 +13,12 @@ import io.ebeaninternal.api.SpiQueryBindCapture; import io.ebeaninternal.api.SpiQueryPlan; import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.core.timezone.DataTimeZone; +import io.ebeaninternal.server.util.Checksum; import io.ebeaninternal.server.util.Str; import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot; import io.ebeaninternal.server.type.DataBind; import io.ebeaninternal.server.type.DataBindCapture; import io.ebeaninternal.server.type.RsetDataReader; -import io.ebeaninternal.server.util.Md5; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,7 +68,7 @@ public class CQueryPlan implements SpiQueryPlan { private final boolean rawSql; private final String sql; - private final String hash; + private final long hash; private final String logWhereSql; @@ -118,7 +118,7 @@ public class CQueryPlan implements SpiQueryPlan { this.stats = new CQueryPlanStats(this); this.dependentTables = sqlTree.dependentTables(); this.bindCapture = initBindCapture(query); - this.hash = md5Hash(); + this.hash = Checksum.checksum(sql); } /** @@ -143,7 +143,7 @@ public class CQueryPlan implements SpiQueryPlan { this.stats = new CQueryPlanStats(this); this.dependentTables = sqlTree.dependentTables(); this.bindCapture = initBindCaptureRaw(sql, query); - this.hash = md5Hash(); + this.hash = Checksum.checksum(sql); } private String deriveName(String label, SpiQuery.Type type, String simpleName) { @@ -193,7 +193,7 @@ public class CQueryPlan implements SpiQueryPlan { } @Override - public String getHash() { + public long getHash() { return hash; } @@ -276,21 +276,6 @@ public class CQueryPlan implements SpiQueryPlan { return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey(); } - /** - * Return the MD5 hash of the sql. - */ - private String md5Hash() { - StringBuilder sb = new StringBuilder(sql) - .append("|").append(name) - .append("|").append(location); - try { - return Md5.hash(sb.toString()); - } catch (Exception e) { - logger.error("Failed to MD5 hash the query", e); - return "error"; - } - } - SqlTree getSqlTree() { return sqlTree; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java index 23de6de5c..3f7f61070 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java @@ -127,7 +127,7 @@ public final class CQueryPlanStats { } @Override - public String getHash() { + public long getHash() { return queryPlan.getHash(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java index 8065a62dc..3d8c7aaac 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java @@ -16,12 +16,11 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { private final String sql; private final String bind; private final String plan; - - private String hash; + private final long hash; private long queryTimeMicros; private long captureCount; - DQueryPlanOutput(Class beanType, String label, String hash, String sql, ProfileLocation profileLocation, String bind, String plan) { + DQueryPlanOutput(Class beanType, String label, long hash, String sql, ProfileLocation profileLocation, String bind, String plan) { this.beanType = beanType; this.label = label; this.hash = hash; @@ -32,7 +31,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { } @Override - public String getHash() { + public long getHash() { return hash; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java b/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java deleted file mode 100644 index 7329d3f25..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.ebeaninternal.server.util; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; - -public final class Md5 { - - /** - * Return the MD5 hash of the underlying sql. - */ - public static String hash(String content) { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - return digestToHex(md.digest(content.getBytes(StandardCharsets.UTF_8))); - } catch (Exception e) { - throw new RuntimeException("MD5 hashing failed", e); - } - } - - /** - * Convert the digest into a hex value. - */ - private static String digestToHex(byte[] digest) { - StringBuilder sb = new StringBuilder(); - for (byte aDigest : digest) { - sb.append(Integer.toString((aDigest & 0xff) + 0x100, 16).substring(1)); - } - return sb.toString(); - } - -} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java b/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java deleted file mode 100644 index d9db27d3d..000000000 --- a/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.ebeaninternal.server.util; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class Md5Test { - - @Test - public void hash() throws Exception { - - String content = "some random content we wish to hash"; - String hash1 = Md5.hash(content); - String hash2 = Md5.hash(content); - assertEquals(hash1, hash2); - } - -} diff --git a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java index eeefe42c2..8e8c5a288 100644 --- a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -244,7 +244,7 @@ public class TestCustomerFinder extends BaseTestCase { assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\""); assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\""); if (isH2() || isPostgres()) { - assertThat(metricsJson).contains("\"hash\":\"cc20eb930403cfd418db2d0475c6e26a\""); + assertThat(metricsJson).contains("\"hash\":3634991469"); assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,"); } } From 202d78571012058ec55ace72a453edcc4d9f00d8 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 6 Aug 2021 12:06:43 +1200 Subject: [PATCH 62/87] Rename hash to sqlHash to better reflect what it is (CRC32 checksum of the sql) --- .../main/java/io/ebean/meta/MetaQueryMetric.java | 4 ++-- .../src/main/java/io/ebean/meta/MetricData.java | 10 +++++----- .../java/io/ebeaninternal/api/SpiQueryPlan.java | 4 ++-- .../io/ebeaninternal/server/core/DumpMetrics.java | 2 +- .../ebeaninternal/server/core/DumpMetricsData.java | 2 +- .../ebeaninternal/server/core/DumpMetricsJson.java | 2 +- .../server/deploy/BeanDescriptor.java | 2 +- .../server/profile/DQueryPlanMeta.java | 8 ++++---- .../server/profile/DQueryPlanMetric.java | 4 ++-- .../io/ebeaninternal/server/query/CQueryPlan.java | 14 +++++++------- .../server/query/CQueryPlanStats.java | 4 ++-- .../org/tests/query/finder/TestCustomerFinder.java | 6 +++--- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java index edfe978e5..e3907c69c 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java @@ -21,8 +21,8 @@ public interface MetaQueryMetric extends MetaTimedMetric { String getSql(); /** - * Return the hash of the plan. + * Return the hash of the sql. */ - long getHash(); + long getSqlHash(); } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetricData.java b/ebean-api/src/main/java/io/ebean/meta/MetricData.java index 9a24aa9f2..d8f80a0a9 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetricData.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetricData.java @@ -6,7 +6,7 @@ package io.ebean.meta; public class MetricData { private String name; - private long hash; + private long sqlHash; private String loc; private String sql; @@ -30,12 +30,12 @@ public class MetricData { this.name = name; } - public long getHash() { - return hash; + public long getSqlHash() { + return sqlHash; } - public void setHash(long hash) { - this.hash = hash; + public void setSqlHash(long sqlHash) { + this.sqlHash = sqlHash; } public String getLoc() { diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java index cd6c83114..878dcc716 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java @@ -18,9 +18,9 @@ public interface SpiQueryPlan { String getName(); /** - * The hash for the query plan. + * The hash of the sql. */ - long getHash(); + long getSqlHash(); /** * The SQL for the query plan. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java index 972ee2754..4c42c4b11 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java @@ -120,7 +120,7 @@ class DumpMetrics { appendQueryName(metric, sb); appendCounters(metric, sb); if (dumpHash) { - sb.append("\n hash:").append(metric.getHash()); + sb.append("\n sqlHash:").append(metric.getSqlHash()); } appendProfileAndSql(metric, sb); out(sb.toString()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java index 9854cafea..7458d0468 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java @@ -67,7 +67,7 @@ class DumpMetricsData { final MetricData data = create(metric); appendCounters(data, metric); appendLocationAndSql(data, metric); - data.setHash(metric.getHash()); + data.setSqlHash(metric.getSqlHash()); } private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java index 240d050f3..6f74dac97 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java @@ -197,7 +197,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { metricStart(metric); appendTiming(metric); if (withHash) { - keyVal("hash", metric.getHash()); + keyVal("sqlHash", metric.getSqlHash()); } if (isIncludeDetail(metric)) { appendExtra("loc", metric.getLocation()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 8f0fea0f2..e08b37d1d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1558,7 +1558,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { void queryPlanInit(QueryPlanInit request, List list) { for (CQueryPlan queryPlan : queryPlanCache.values()) { - if (request.includeHash(queryPlan.getHash())) { + if (request.includeHash(queryPlan.getSqlHash())) { queryPlan.queryPlanInit(request.getThresholdMicros()); list.add(queryPlan.createMeta(null, null)); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java index 0f25f0d9c..5359d96ed 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java @@ -10,7 +10,7 @@ class DQueryPlanMeta { private final ProfileLocation profileLocation; private final String name; private final String sql; - private final long hash; + private final long sqlHash; DQueryPlanMeta(Class type, String label, ProfileLocation profileLocation, String sql) { this.type = type; @@ -22,15 +22,15 @@ class DQueryPlanMeta { name += "_" + label; } this.name = name; - this.hash = Checksum.checksum(sql); + this.sqlHash = Checksum.checksum(sql); } public Class getType() { return type; } - public long getHash() { - return hash; + public long getSqlHash() { + return sqlHash; } public String getName() { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index 765679404..fda2b62b8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -59,8 +59,8 @@ class DQueryPlanMetric implements QueryPlanMetric { } @Override - public long getHash() { - return meta.getHash(); + public long getSqlHash() { + return meta.getSqlHash(); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index 3c322190f..796290808 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -68,7 +68,7 @@ public class CQueryPlan implements SpiQueryPlan { private final boolean rawSql; private final String sql; - private final long hash; + private final long sqlHash; private final String logWhereSql; @@ -118,7 +118,7 @@ public class CQueryPlan implements SpiQueryPlan { this.stats = new CQueryPlanStats(this); this.dependentTables = sqlTree.dependentTables(); this.bindCapture = initBindCapture(query); - this.hash = Checksum.checksum(sql); + this.sqlHash = Checksum.checksum(sql); } /** @@ -143,7 +143,7 @@ public class CQueryPlan implements SpiQueryPlan { this.stats = new CQueryPlanStats(this); this.dependentTables = sqlTree.dependentTables(); this.bindCapture = initBindCaptureRaw(sql, query); - this.hash = Checksum.checksum(sql); + this.sqlHash = Checksum.checksum(sql); } private String deriveName(String label, SpiQuery.Type type, String simpleName) { @@ -193,8 +193,8 @@ public class CQueryPlan implements SpiQueryPlan { } @Override - public long getHash() { - return hash; + public long getSqlHash() { + return sqlHash; } @Override @@ -226,7 +226,7 @@ public class CQueryPlan implements SpiQueryPlan { @Override public DQueryPlanOutput createMeta(String bind, String planString) { - return new DQueryPlanOutput(getBeanType(), name, hash, sql, profileLocation, bind, planString); + return new DQueryPlanOutput(getBeanType(), name, sqlHash, sql, profileLocation, bind, planString); } public DataReader createDataReader(ResultSet rset) { @@ -273,7 +273,7 @@ public class CQueryPlan implements SpiQueryPlan { private String calcAuditQueryKey() { // rawSql needs to include the MD5 hash of the sql - return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey(); + return rawSql ? planKey.getPartialKey() + "_" + sqlHash : planKey.getPartialKey(); } SqlTree getSqlTree() { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java index 3f7f61070..bbcfea2b9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java @@ -127,8 +127,8 @@ public final class CQueryPlanStats { } @Override - public long getHash() { - return queryPlan.getHash(); + public long getSqlHash() { + return queryPlan.getSqlHash(); } @Override diff --git a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java index 8e8c5a288..b6a40903f 100644 --- a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -216,7 +216,7 @@ public class TestCustomerFinder extends BaseTestCase { assertThat(plans0).isNotEmpty(); for (MetaQueryPlan plan : plans) { - logger.info("queryplan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}", + logger.info("queryPlan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}", plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(), plan.getSql(), plan.getBind(), plan.getPlan()); System.out.println(plan); @@ -244,7 +244,7 @@ public class TestCustomerFinder extends BaseTestCase { assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\""); assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\""); if (isH2() || isPostgres()) { - assertThat(metricsJson).contains("\"hash\":3634991469"); + assertThat(metricsJson).contains("\"sqlHash\":3634991469"); assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,"); } } @@ -267,7 +267,7 @@ public class TestCustomerFinder extends BaseTestCase { assertThat(metricsJson).contains("\"name\":\"txn.main\""); assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\""); assertThat(metricsJson).doesNotContain("\"loc\":"); - assertThat(metricsJson).doesNotContain("\"hash\":"); + assertThat(metricsJson).doesNotContain("\"sqlHash\":"); assertThat(metricsJson).doesNotContain("\"sql\":"); } From 801d419f51b4cc56c6804d99da7acba07419e279 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 6 Aug 2021 13:14:29 +1200 Subject: [PATCH 63/87] Refactor io.ebean.meta API, method rename with deprecation --- .../io/ebean/meta/AbstractMetricVisitor.java | 8 +-- .../io/ebean/meta/BasicMetricVisitor.java | 6 +-- .../java/io/ebean/meta/MetaCountMetric.java | 9 +++- .../main/java/io/ebean/meta/MetaMetric.java | 9 +++- .../java/io/ebean/meta/MetaQueryMetric.java | 39 ++++++++++++-- .../java/io/ebean/meta/MetaQueryPlan.java | 18 +++---- .../java/io/ebean/meta/MetaTimedMetric.java | 51 +++++++++++++++++-- .../java/io/ebean/meta/MetricVisitor.java | 8 +-- .../java/io/ebean/meta/QueryPlanInit.java | 8 +-- .../java/io/ebean/meta/QueryPlanRequest.java | 12 ++--- .../java/io/ebean/meta/ServerMetrics.java | 29 +++++++++-- .../io/ebean/meta/ServerMetricsAsJson.java | 4 +- .../main/java/io/ebean/meta/SortMetric.java | 14 ++--- .../server/core/DefaultQueryPlanListener.java | 4 +- .../server/core/DefaultServer.java | 12 ++--- .../server/core/DumpMetrics.java | 28 +++++----- .../server/core/DumpMetricsData.java | 26 +++++----- .../server/core/DumpMetricsJson.java | 26 +++++----- .../server/deploy/BeanDescriptor.java | 4 +- .../server/profile/DCountMetric.java | 6 +-- .../server/profile/DQueryPlanMetric.java | 32 ++++++------ .../server/profile/DTimeMetricStats.java | 12 ++--- .../server/profile/DTimedMetric.java | 2 +- .../server/profile/DTimedProfileLocation.java | 2 +- .../server/query/CQueryPlanRequest.java | 6 +-- .../server/query/CQueryPlanStats.java | 30 +++++------ .../server/query/DQueryPlanOutput.java | 28 +++++----- .../src/test/java/io/ebean/BaseTestCase.java | 4 +- .../src/test/java/io/ebean/DtoQuery2Test.java | 10 ++-- .../java/io/ebean/DtoQueryFromOrmTest.java | 12 ++--- .../src/test/java/io/ebean/DtoQueryTest.java | 16 +++--- .../test/java/io/ebean/UpdateQueryTest.java | 12 ++--- .../server/deploy/BeanIudMetricsTest.java | 34 ++++++------- .../server/profile/DTimedMetricMapTest.java | 12 ++--- .../server/profile/DTimedMetricTest.java | 26 +++++----- .../server/profile/SortMetricTest.java | 2 +- .../batchinsert/TestBatchInsertFlush.java | 10 ++-- .../org/tests/m2m/TestM2mDeleteObject.java | 2 +- .../query/finder/TestCustomerFinder.java | 16 +++--- .../tests/query/sqlquery/SqlQueryTests.java | 2 +- .../transaction/TestNestedMandatory.java | 2 +- .../TestTransactionalReadOnly.java | 10 ++-- .../org/tests/update/TestSqlUpdateInTxn.java | 4 +- .../src/test/java/io/ebean/BaseTestCase.java | 6 +-- .../main/java/io/ebean/redis/RedisCache.java | 7 ++- 45 files changed, 363 insertions(+), 257 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java index 323b572ec..51df586d9 100644 --- a/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java +++ b/ebean-api/src/main/java/io/ebean/meta/AbstractMetricVisitor.java @@ -18,22 +18,22 @@ public abstract class AbstractMetricVisitor implements MetricVisitor { } @Override - public boolean isReset() { + public boolean reset() { return reset; } @Override - public boolean isCollectTransactionMetrics() { + public boolean collectTransactionMetrics() { return collectTransactionMetrics; } @Override - public boolean isCollectQueryMetrics() { + public boolean collectQueryMetrics() { return collectQueryMetrics; } @Override - public boolean isCollectL2Metrics() { + public boolean collectL2Metrics() { return collectL2Metrics; } diff --git a/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java index c821c63a7..0db953159 100644 --- a/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java +++ b/ebean-api/src/main/java/io/ebean/meta/BasicMetricVisitor.java @@ -27,17 +27,17 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM } @Override - public List getTimedMetrics() { + public List timedMetrics() { return timed; } @Override - public List getQueryMetrics() { + public List queryMetrics() { return query; } @Override - public List getCountMetrics() { + public List countMetrics() { return count; } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java index 8b6af3594..cdaa2d7fa 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaCountMetric.java @@ -8,6 +8,13 @@ public interface MetaCountMetric extends MetaMetric { /** * Return the total count. */ - long getCount(); + long count(); + /** + * Migrate to count() + */ + @Deprecated + default long getCount() { + return count(); + } } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java index 0d716fe23..671dc05c1 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaMetric.java @@ -8,6 +8,13 @@ public interface MetaMetric { /** * Return the metric name. */ - String getName(); + String name(); + /** + * Migrate to name(). + */ + @Deprecated + default String getName() { + return name(); + } } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java index e3907c69c..f5fee986c 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java @@ -8,21 +8,52 @@ public interface MetaQueryMetric extends MetaTimedMetric { /** * The type of entity or DTO bean. */ - Class getType(); + Class type(); + + /** + * Migrate to type(). + */ + @Deprecated + default Class getType() { + return type(); + } /** * The label for the query (can be null). */ - String getLabel(); + String label(); + + /** + * Migrate to label(). + */ + @Deprecated + default String getLabel() { + return label(); + } /** * The actual SQL of the query. */ - String getSql(); + String sql(); + + /** + * Migrate to sql(). + */ + @Deprecated + default String getSql() { + return sql(); + } /** * Return the hash of the sql. */ - long getSqlHash(); + long sqlHash(); + /** + * Migrate to sqlHash(). + */ + @Deprecated + default long getSqlHash() { + return sqlHash(); + } } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java index 4f9667bd0..6cdce69a1 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java @@ -10,45 +10,45 @@ public interface MetaQueryPlan { /** * Return the bean type for the query. */ - Class getBeanType(); + Class beanType(); /** * Return the label of the query. */ - String getLabel(); + String label(); /** * Return the profile location for the query. */ - ProfileLocation getProfileLocation(); + ProfileLocation profileLocation(); /** * Return the sql of the query. */ - String getSql(); + String sql(); /** * Return the hash of the plan. */ - long getHash(); + long sqlHash(); /** * Return a description of the bind values. */ - String getBind(); + String bind(); /** * Return the raw plan. */ - String getPlan(); + String plan(); /** * Return the query execution time associated with the bind values capture. */ - long getQueryTimeMicros(); + long queryTimeMicros(); /** * Return the total count of times bind capture has occurred. */ - long getCaptureCount(); + long captureCount(); } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java index c6ba13516..f95f3248d 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java @@ -9,27 +9,68 @@ public interface MetaTimedMetric extends MetaMetric { /** * Return the metric location if defined. */ - String getLocation(); + String location(); + + /** + * Migrate to location() + */ + @Deprecated + default String getLocation() { + return location(); + } /** * Return the total count. */ - long getCount(); + long count(); + + /** + * Migrate to count() + */ + @Deprecated + default long getCount() { + return count(); + } /** * Return the total execution time in micros. */ - long getTotal(); + long total(); + + /** + * Migrate to total() + */ + @Deprecated + default long getTotal() { + return total(); + } /** * Return the max execution time in micros. */ - long getMax(); + long max(); + + /** + * Migrate to max() + */ + @Deprecated + default long getMax() { + return max(); + } /** * Return the mean execution time in micros. */ - long getMean(); + long mean(); + + + /** + * Migrate to mean() + */ + @Deprecated + default long getMean() { + return mean(); + } /** * Return true if this is the first metrics collection for this query. diff --git a/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java b/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java index 18c7a72dc..fac9f050e 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetricVisitor.java @@ -8,22 +8,22 @@ public interface MetricVisitor { /** * Return true if the metrics should be reset. */ - boolean isReset(); + boolean reset(); /** * Return true if we should visit the transaction metrics. */ - boolean isCollectTransactionMetrics(); + boolean collectTransactionMetrics(); /** * Return true if we should visit the ORM and SQL query metrics. */ - boolean isCollectQueryMetrics(); + boolean collectQueryMetrics(); /** * Return true if we should visit the L2 cache metrics. */ - boolean isCollectL2Metrics(); + boolean collectL2Metrics(); /** * Visit has started. diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java index 9b785ec62..7caeabd2f 100644 --- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java +++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java @@ -32,7 +32,7 @@ public class QueryPlanInit { * Return the query execution time threshold which must be exceeded to initiate * query plan collection. */ - public long getThresholdMicros() { + public long thresholdMicros() { return thresholdMicros; } @@ -40,7 +40,7 @@ public class QueryPlanInit { * Set the query execution time threshold which must be exceeded to initiate * query plan collection. */ - public void setThresholdMicros(long thresholdMicros) { + public void thresholdMicros(long thresholdMicros) { this.thresholdMicros = thresholdMicros; } @@ -54,14 +54,14 @@ public class QueryPlanInit { /** * Return the specific hashes that we want to collect query plans on. */ - public Set getHashes() { + public Set sqlHashes() { return hashes; } /** * Set the specific hashes that we want to collect query plans on. */ - public void setHashes(Set hashes) { + public void sqlHashes(Set hashes) { this.hashes = hashes; } } diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java index c4a69b1aa..421e0c151 100644 --- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java +++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java @@ -18,7 +18,7 @@ public class QueryPlanRequest { * have been around for a while (e.g. 5 mins) and so reasonably represent * bind values that match the slowest execution for this query plan. */ - public long getSince() { + public long since() { return since; } @@ -28,14 +28,14 @@ public class QueryPlanRequest { * * @param since The minimum age of the bind values capture. */ - public void setSince(long since) { + public void since(long since) { this.since = since; } /** * Return the maximum number of plans to capture. */ - public int getMaxCount() { + public int maxCount() { return maxCount; } @@ -45,7 +45,7 @@ public class QueryPlanRequest { * Use this to limit how much query plan capturing is done as query * plan capture is actual database load. */ - public void setMaxCount(int maxCount) { + public void maxCount(int maxCount) { this.maxCount = maxCount; } @@ -54,7 +54,7 @@ public class QueryPlanRequest { *

* Query plan collection will stop once this time is exceeded. */ - public long getMaxTimeMillis() { + public long maxTimeMillis() { return maxTimeMillis; } @@ -65,7 +65,7 @@ public class QueryPlanRequest { * this to ensure the query plan capture does not use excessive amount * of time - put too much load on the database. */ - public void setMaxTimeMillis(long maxTimeMillis) { + public void maxTimeMillis(long maxTimeMillis) { this.maxTimeMillis = maxTimeMillis; } } diff --git a/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java b/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java index 8f0488d62..b8a50d85d 100644 --- a/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java +++ b/ebean-api/src/main/java/io/ebean/meta/ServerMetrics.java @@ -10,16 +10,39 @@ public interface ServerMetrics { /** * Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate. */ - List getTimedMetrics(); + List timedMetrics(); + + /** + * Migrate to timedMetrics(). + */ + @Deprecated + default List getTimedMetrics() { + return timedMetrics(); + } /** * Return the query metrics. */ - List getQueryMetrics(); + List queryMetrics(); + + /** + * Migrate to queryMetrics(). + */ + @Deprecated + default List getQueryMetrics() { + return queryMetrics(); + } /** * Return the Counter metrics. */ - List getCountMetrics(); + List countMetrics(); + /** + * Migrate to countMetrics(). + */ + @Deprecated + default List getCountMetrics() { + return countMetrics(); + } } diff --git a/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java b/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java index f48ecb5b6..7e42302c8 100644 --- a/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java +++ b/ebean-api/src/main/java/io/ebean/meta/ServerMetricsAsJson.java @@ -8,12 +8,12 @@ import java.util.Comparator; public interface ServerMetricsAsJson { /** - * Set to false to exclude profile location and sql. + * Set to false in order to exclude profile location and sql. */ ServerMetricsAsJson withExtraAttributes(boolean withLocation); /** - * Set to false to exclude SQL hash. + * Set to false in order to exclude SQL hash. */ ServerMetricsAsJson withHash(boolean withHash); diff --git a/ebean-api/src/main/java/io/ebean/meta/SortMetric.java b/ebean-api/src/main/java/io/ebean/meta/SortMetric.java index 9df2c704b..30104f1ff 100644 --- a/ebean-api/src/main/java/io/ebean/meta/SortMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/SortMetric.java @@ -32,7 +32,7 @@ public class SortMetric { @Override public int compare(MetaCountMetric o1, MetaCountMetric o2) { - return stringCompare(o1.getName(), o2.getName()); + return stringCompare(o1.name(), o2.name()); } } @@ -43,8 +43,8 @@ public class SortMetric { @Override public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { - int i = stringCompare(o1.getName(), o2.getName()); - return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount()); + int i = stringCompare(o1.name(), o2.name()); + return i != 0 ? i : Long.compare(o1.count(), o2.count()); } } @@ -55,7 +55,7 @@ public class SortMetric { @Override public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { - return Long.compare(o2.getCount(), o1.getCount()); + return Long.compare(o2.count(), o1.count()); } } @@ -66,7 +66,7 @@ public class SortMetric { @Override public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { - return Long.compare(o2.getTotal(), o1.getTotal()); + return Long.compare(o2.total(), o1.total()); } } @@ -77,7 +77,7 @@ public class SortMetric { @Override public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { - return Long.compare(o2.getMean(), o1.getMean()); + return Long.compare(o2.mean(), o1.mean()); } } @@ -88,7 +88,7 @@ public class SortMetric { @Override public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { - return Long.compare(o2.getMax(), o1.getMax()); + return Long.compare(o2.max(), o1.max()); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java index 051d331ba..20d7ec223 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultQueryPlanListener.java @@ -18,8 +18,8 @@ class DefaultQueryPlanListener implements QueryPlanListener { String dbName = capture.getDatabase().getName(); for (MetaQueryPlan plan : capture.getPlans()) { log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}", - dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(), - plan.getSql(), plan.getBind(), plan.getPlan()); + dbName, plan.label(), plan.queryTimeMicros(), plan.profileLocation(), + plan.sql(), plan.bind(), plan.plan()); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index d21f9967f..2040e3f78 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -416,8 +416,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private void collectQueryPlans() { QueryPlanRequest request = new QueryPlanRequest(); - request.setMaxCount(config.getQueryPlanCaptureMaxCount()); - request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis()); + request.maxCount(config.getQueryPlanCaptureMaxCount()); + request.maxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis()); // obtains query explain plans ... List plans = metaInfoManager.queryPlanCollectNow(request); @@ -2328,13 +2328,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override public void visitMetrics(MetricVisitor visitor) { visitor.visitStart(); - if (visitor.isCollectTransactionMetrics()) { + if (visitor.collectTransactionMetrics()) { transactionManager.visitMetrics(visitor); } - if (visitor.isCollectL2Metrics()) { + if (visitor.collectL2Metrics()) { serverCacheManager.visitMetrics(visitor); } - if (visitor.isCollectQueryMetrics()) { + if (visitor.collectQueryMetrics()) { beanDescriptorManager.visitMetrics(visitor); dtoBeanManager.visitMetrics(visitor); relationalQueryEngine.visitMetrics(visitor); @@ -2351,7 +2351,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { List queryPlanInit(QueryPlanInit initRequest) { if (initRequest.isAll()) { - queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros()); + queryPlanManager.setDefaultThreshold(initRequest.thresholdMicros()); } return beanDescriptorManager.queryPlanInit(initRequest); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java index 4c42c4b11..f31fa849c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java @@ -78,11 +78,11 @@ class DumpMetrics { out("-- Dumping metrics for " + server.getName() + " -- "); ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics(); - for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) { + for (MetaTimedMetric metric : serverMetrics.timedMetrics()) { log(metric); } - List countMetrics = serverMetrics.getCountMetrics(); + List countMetrics = serverMetrics.countMetrics(); if (!countMetrics.isEmpty()) { out("\n-- Counters --"); countMetrics.sort(SortMetric.COUNT_NAME); @@ -91,7 +91,7 @@ class DumpMetrics { } } - List queryMetrics = serverMetrics.getQueryMetrics(); + List queryMetrics = serverMetrics.queryMetrics(); if (!queryMetrics.isEmpty()) { out("\n-- Queries --"); queryMetrics.sort(sortBy); @@ -104,8 +104,8 @@ class DumpMetrics { private void logCount(MetaCountMetric metric) { StringBuilder sb = new StringBuilder(); - sb.append(padNameTimed(metric.getName())).append(" "); - sb.append(" count:").append(pad(metric.getCount())); + sb.append(padNameTimed(metric.name())).append(" "); + sb.append(" count:").append(pad(metric.count())); out(sb.toString()); } @@ -120,38 +120,38 @@ class DumpMetrics { appendQueryName(metric, sb); appendCounters(metric, sb); if (dumpHash) { - sb.append("\n sqlHash:").append(metric.getSqlHash()); + sb.append("\n sqlHash:").append(metric.sqlHash()); } appendProfileAndSql(metric, sb); out(sb.toString()); } private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) { - sb.append("query:").append(padName(metric.getName())).append(" "); + sb.append("query:").append(padName(metric.name())).append(" "); } private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) { - String location = metric.getLocation(); + String location = metric.location(); if (dumpLoc && location != null) { sb.append("\n loc:").append(location); } if (dumpSql) { - sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n"); + sb.append(" \n\n sql:").append(metric.sql()).append("\n\n"); } } private void log(MetaTimedMetric metric) { StringBuilder sb = new StringBuilder(); - sb.append(padNameTimed(metric.getName())).append(" "); + sb.append(padNameTimed(metric.name())).append(" "); appendCounters(metric, sb); out(sb.toString()); } private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) { - sb.append(" count:").append(pad(timedMetric.getCount())) - .append(" total:").append(pad(timedMetric.getTotal())) - .append(" mean:").append(pad(timedMetric.getMean())) - .append(" max:").append(pad(timedMetric.getMax())); + sb.append(" count:").append(pad(timedMetric.count())) + .append(" total:").append(pad(timedMetric.total())) + .append(" mean:").append(pad(timedMetric.mean())) + .append(" max:").append(pad(timedMetric.max())); } private String padName(String name) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java index 7458d0468..4ac8859b6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java @@ -31,9 +31,9 @@ class DumpMetricsData { private void collect(ServerMetrics serverMetrics) { - final List timedMetrics = serverMetrics.getTimedMetrics(); - final List countMetrics = serverMetrics.getCountMetrics(); - final List queryMetrics = serverMetrics.getQueryMetrics(); + final List timedMetrics = serverMetrics.timedMetrics(); + final List countMetrics = serverMetrics.countMetrics(); + final List queryMetrics = serverMetrics.queryMetrics(); for (MetaTimedMetric metric : timedMetrics) { add(metric); @@ -47,7 +47,7 @@ class DumpMetricsData { } private MetricData create(MetaMetric metric) { - MetricData data = new MetricData(metric.getName()); + MetricData data = new MetricData(metric.name()); list.add(data); return data; } @@ -55,30 +55,30 @@ class DumpMetricsData { private void add(MetaTimedMetric metric) { final MetricData data = create(metric); appendCounters(data, metric); - data.setLoc(metric.getLocation()); + data.setLoc(metric.location()); } private void addCount(MetaCountMetric metric) { final MetricData data = create(metric); - data.setCount(metric.getCount()); + data.setCount(metric.count()); } private void addQuery(MetaQueryMetric metric) { final MetricData data = create(metric); appendCounters(data, metric); appendLocationAndSql(data, metric); - data.setSqlHash(metric.getSqlHash()); + data.setSqlHash(metric.sqlHash()); } private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) { - data.setLoc(metric.getLocation()); - data.setSql(metric.getSql()); + data.setLoc(metric.location()); + data.setSql(metric.sql()); } private void appendCounters(MetricData data, MetaTimedMetric timedMetric) { - data.setCount(timedMetric.getCount()); - data.setTotal(timedMetric.getTotal()); - data.setMean(timedMetric.getMean()); - data.setMax(timedMetric.getMax()); + data.setCount(timedMetric.count()); + data.setTotal(timedMetric.total()); + data.setMean(timedMetric.mean()); + data.setMax(timedMetric.max()); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java index 6f74dac97..b0644ec9f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java @@ -84,11 +84,11 @@ class DumpMetricsJson implements ServerMetricsAsJson { private void collect(ServerMetrics serverMetrics) { try { start(); - for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) { + for (MetaTimedMetric metric : serverMetrics.timedMetrics()) { logTimed(metric); } - List countMetrics = serverMetrics.getCountMetrics(); + List countMetrics = serverMetrics.countMetrics(); if (!countMetrics.isEmpty()) { if (sortBy != null) { countMetrics.sort(SortMetric.COUNT_NAME); @@ -98,7 +98,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { } } - List queryMetrics = serverMetrics.getQueryMetrics(); + List queryMetrics = serverMetrics.queryMetrics(); if (!queryMetrics.isEmpty()) { if (sortBy != null) { queryMetrics.sort(sortBy); @@ -170,7 +170,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { } objStart(); key("name"); - val(metric.getName()); + val(metric.name()); } private void metricEnd() throws IOException { @@ -180,7 +180,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { private void logCount(MetaCountMetric metric) throws IOException { metricStart(metric); key("count"); - val(metric.getCount()); + val(metric.count()); metricEnd(); } @@ -188,7 +188,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { metricStart(metric); appendTiming(metric); if (isIncludeDetail(metric)) { - appendExtra("loc", metric.getLocation()); + appendExtra("loc", metric.location()); } metricEnd(); } @@ -197,11 +197,11 @@ class DumpMetricsJson implements ServerMetricsAsJson { metricStart(metric); appendTiming(metric); if (withHash) { - keyVal("sqlHash", metric.getSqlHash()); + keyVal("sqlHash", metric.sqlHash()); } if (isIncludeDetail(metric)) { - appendExtra("loc", metric.getLocation()); - appendExtra("sql", metric.getSql()); + appendExtra("loc", metric.location()); + appendExtra("sql", metric.sql()); } metricEnd(); } @@ -218,10 +218,10 @@ class DumpMetricsJson implements ServerMetricsAsJson { } private void appendTiming(MetaTimedMetric timedMetric) throws IOException { - keyVal("count", timedMetric.getCount()); - keyVal("total", timedMetric.getTotal()); - keyVal("mean", timedMetric.getMean()); - keyVal("max", timedMetric.getMax()); + keyVal("count", timedMetric.count()); + keyVal("total", timedMetric.total()); + keyVal("mean", timedMetric.mean()); + keyVal("max", timedMetric.max()); } private void keyVal(String key, long value) throws IOException { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index e08b37d1d..6a9acd839 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1559,7 +1559,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { void queryPlanInit(QueryPlanInit request, List list) { for (CQueryPlan queryPlan : queryPlanCache.values()) { if (request.includeHash(queryPlan.getSqlHash())) { - queryPlan.queryPlanInit(request.getThresholdMicros()); + queryPlan.queryPlanInit(request.thresholdMicros()); list.add(queryPlan.createMeta(null, null)); } } @@ -1572,7 +1572,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { iudMetrics.visit(visitor); for (CQueryPlan queryPlan : queryPlanCache.values()) { if (!queryPlan.isEmptyStats()) { - visitor.visitQuery(queryPlan.getSnapshot(visitor.isReset())); + visitor.visitQuery(queryPlan.getSnapshot(visitor.reset())); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java index 2cf7a25a7..d93e0dbdb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DCountMetric.java @@ -49,7 +49,7 @@ class DCountMetric implements CountMetric { @Override public void visit(MetricVisitor visitor) { - long val = visitor.isReset() ? count.sumThenReset() : count.sum(); + long val = visitor.reset() ? count.sumThenReset() : count.sum(); if (val > 0) { visitor.visitCount(new DCountMetricStats(name, val)); } @@ -66,12 +66,12 @@ class DCountMetric implements CountMetric { } @Override - public String getName() { + public String name() { return name; } @Override - public long getCount() { + public long count() { return count; } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index fda2b62b8..fda1d4d9b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -19,7 +19,7 @@ class DQueryPlanMetric implements QueryPlanMetric { @Override public void visit(MetricVisitor visitor) { - TimedMetricStats stats = metric.collect(visitor.isReset()); + TimedMetricStats stats = metric.collect(visitor.reset()); if (stats != null) { visitor.visitQuery(new Stats(meta, stats, collected)); collected = true; @@ -45,11 +45,11 @@ class DQueryPlanMetric implements QueryPlanMetric { @Override public String toString() { - return meta + " " + stats + " sql:" + getSql(); + return meta + " " + stats + " sql:" + sql(); } @Override - public Class getType() { + public Class type() { return meta.getType(); } @@ -59,48 +59,48 @@ class DQueryPlanMetric implements QueryPlanMetric { } @Override - public long getSqlHash() { + public long sqlHash() { return meta.getSqlHash(); } @Override - public String getLabel() { + public String label() { return meta.getLabel(); } @Override - public String getSql() { + public String sql() { return meta.getSql(); } @Override - public String getName() { + public String name() { return meta.getName(); } @Override - public String getLocation() { + public String location() { return meta.getLocation(); } @Override - public long getCount() { - return stats.getCount(); + public long count() { + return stats.count(); } @Override - public long getTotal() { - return stats.getTotal(); + public long total() { + return stats.total(); } @Override - public long getMax() { - return stats.getMax(); + public long max() { + return stats.max(); } @Override - public long getMean() { - return stats.getMean(); + public long mean() { + return stats.mean(); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java index 54146abe9..2207fef3d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java @@ -60,12 +60,12 @@ class DTimeMetricStats implements TimedMetricStats { } @Override - public String getName() { + public String name() { return name; } @Override - public String getLocation() { + public String location() { return location; } @@ -73,7 +73,7 @@ class DTimeMetricStats implements TimedMetricStats { * Return the count of values collected. */ @Override - public long getCount() { + public long count() { return count; } @@ -81,7 +81,7 @@ class DTimeMetricStats implements TimedMetricStats { * Return the total of all the values. */ @Override - public long getTotal() { + public long total() { return total; } @@ -89,7 +89,7 @@ class DTimeMetricStats implements TimedMetricStats { * Return the Max value collected. */ @Override - public long getMax() { + public long max() { return max; } @@ -97,7 +97,7 @@ class DTimeMetricStats implements TimedMetricStats { * Return the mean value rounded up. */ @Override - public long getMean() { + public long mean() { return (count < 1) ? 0L : Math.round((double)(total / count)); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java index 63eb275f6..cc344c3e6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java @@ -71,7 +71,7 @@ class DTimedMetric implements TimedMetric { @Override public void visit(MetricVisitor visitor) { - DTimeMetricStats metric = collect(visitor.isReset()); + DTimeMetricStats metric = collect(visitor.reset()); if (metric != null) { visitor.visitTimed(metric); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java index 7ef4e9084..27537737f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java @@ -48,7 +48,7 @@ class DTimedProfileLocation extends DProfileLocation implements TimedProfileLoca @Override public void visit(MetricVisitor visitor) { - TimedMetricStats collect = timedMetric.collect(visitor.isReset()); + TimedMetricStats collect = timedMetric.collect(visitor.reset()); if (collect != null) { if (overrideMetricName) { collect.setName(fullName); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java index 86e15f389..315e7a07d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRequest.java @@ -24,10 +24,10 @@ class CQueryPlanRequest { CQueryPlanRequest(Connection connection, QueryPlanRequest req, Iterator iterator) { this.connection = connection; this.iterator = iterator; - this.maxCount = req.getMaxCount(); - long reqSince = req.getSince(); + this.maxCount = req.maxCount(); + long reqSince = req.since(); this.since = (reqSince == 0) ? Long.MAX_VALUE: reqSince; - long maxTimeMillis = req.getMaxTimeMillis(); + long maxTimeMillis = req.maxTimeMillis(); this.maxTime = maxTimeMillis > 0 ? System.currentTimeMillis() + maxTimeMillis : 0; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java index bbcfea2b9..36e070653 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java @@ -83,56 +83,56 @@ public final class CQueryPlanStats { @Override public String toString() { - return "label:" + getLabel() + " location:" + getLocation() + " metrics:" + metrics + " sql:" + getSql(); + return "label:" + label() + " location:" + location() + " metrics:" + metrics + " sql:" + sql(); } @Override - public Class getType() { + public Class type() { return queryPlan.getBeanType(); } @Override - public String getLabel() { + public String label() { return queryPlan.getLabel(); } @Override - public String getName() { + public String name() { return queryPlan.getName(); } @Override - public String getLocation() { + public String location() { return queryPlan.getLocation(); } @Override - public long getCount() { - return metrics.getCount(); + public long count() { + return metrics.count(); } @Override - public long getTotal() { - return metrics.getTotal(); + public long total() { + return metrics.total(); } @Override - public long getMax() { - return metrics.getMax(); + public long max() { + return metrics.max(); } @Override - public long getMean() { - return metrics.getMean(); + public long mean() { + return metrics.mean(); } @Override - public long getSqlHash() { + public long sqlHash() { return queryPlan.getSqlHash(); } @Override - public String getSql() { + public String sql() { return queryPlan.getSql(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java index 3d8c7aaac..3d879362b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java @@ -16,14 +16,14 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { private final String sql; private final String bind; private final String plan; - private final long hash; + private final long sqlHash; private long queryTimeMicros; private long captureCount; - DQueryPlanOutput(Class beanType, String label, long hash, String sql, ProfileLocation profileLocation, String bind, String plan) { + DQueryPlanOutput(Class beanType, String label, long sqlHash, String sql, ProfileLocation profileLocation, String bind, String plan) { this.beanType = beanType; this.label = label; - this.hash = hash; + this.sqlHash = sqlHash; this.sql = sql; this.profileLocation = profileLocation; this.bind = bind; @@ -31,15 +31,15 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { } @Override - public long getHash() { - return hash; + public long sqlHash() { + return sqlHash; } /** * Return the associated bean. */ @Override - public Class getBeanType() { + public Class beanType() { return beanType; } @@ -47,12 +47,12 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { * Return the query label if set. */ @Override - public String getLabel() { + public String label() { return label; } @Override - public ProfileLocation getProfileLocation() { + public ProfileLocation profileLocation() { return profileLocation; } @@ -60,7 +60,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { * Return the sql of query. */ @Override - public String getSql() { + public String sql() { return sql; } @@ -68,7 +68,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { * Return a description of the bind values used. */ @Override - public String getBind() { + public String bind() { return bind; } @@ -76,7 +76,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { * Return the query plan. */ @Override - public String getPlan() { + public String plan() { return plan; } @@ -85,7 +85,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { * to build the query plan. */ @Override - public long getQueryTimeMicros() { + public long queryTimeMicros() { return queryTimeMicros; } @@ -93,13 +93,13 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { * Return the total count of times bind capture has occurred. */ @Override - public long getCaptureCount() { + public long captureCount() { return captureCount; } @Override public String toString() { - return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + hash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan; + return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + sqlHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan; } /** diff --git a/ebean-core/src/test/java/io/ebean/BaseTestCase.java b/ebean-core/src/test/java/io/ebean/BaseTestCase.java index 0205d5294..988e73b59 100644 --- a/ebean-core/src/test/java/io/ebean/BaseTestCase.java +++ b/ebean-core/src/test/java/io/ebean/BaseTestCase.java @@ -90,14 +90,14 @@ public abstract class BaseTestCase { } protected List visitTimedMetrics() { - return collectMetrics().getTimedMetrics(); + return collectMetrics().timedMetrics(); } protected List sqlMetrics() { List timedMetrics = visitTimedMetrics(); return timedMetrics.stream() - .filter((it) -> it.getName().startsWith("sql.") || it.getName().startsWith("orm.")) + .filter((it) -> it.name().startsWith("sql.") || it.name().startsWith("orm.")) .collect(Collectors.toList()); } diff --git a/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java b/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java index ba30e9a5c..625cc9ae9 100644 --- a/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java +++ b/ebean-core/src/test/java/io/ebean/DtoQuery2Test.java @@ -191,13 +191,13 @@ public class DtoQuery2Test extends BaseTestCase { BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true, true); server().getMetaInfoManager().visitMetrics(basic); - List stats = basic.getQueryMetrics(); + List stats = basic.queryMetrics(); assertThat(stats).hasSize(1); MetaQueryMetric queryMetric = stats.get(0); - assertThat(queryMetric.getLabel()).isEqualTo("basic"); - assertThat(queryMetric.getCount()).isEqualTo(3); - assertThat(queryMetric.getName()).isEqualTo("dto.DCust_basic"); + assertThat(queryMetric.label()).isEqualTo("basic"); + assertThat(queryMetric.count()).isEqualTo(3); + assertThat(queryMetric.name()).isEqualTo("dto.DCust_basic"); server().findDto(DCust.class, "select c4.id, c4.name from o_customer c4 where lower(c4.name) = :name") @@ -207,7 +207,7 @@ public class DtoQuery2Test extends BaseTestCase { BasicMetricVisitor metric2 = server().getMetaInfoManager().visitBasic(); - stats = metric2.getQueryMetrics(); + stats = metric2.queryMetrics(); assertThat(stats).hasSize(2); log.info("stats " + stats); diff --git a/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java b/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java index ee26e9409..28b0bd490 100644 --- a/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java +++ b/ebean-core/src/test/java/io/ebean/DtoQueryFromOrmTest.java @@ -27,12 +27,12 @@ public class DtoQueryFromOrmTest extends BaseTestCase { @AfterClass public static void reportStats() { ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics(); - for (MetaQueryMetric metric : metrics.getQueryMetrics()) { + for (MetaQueryMetric metric : metrics.queryMetrics()) { System.out.println(metric); } System.out.println("-- transaction metrics --"); - for (MetaTimedMetric metric : metrics.getTimedMetrics()) { + for (MetaTimedMetric metric : metrics.timedMetrics()) { System.out.println(metric); } } @@ -59,15 +59,15 @@ public class DtoQueryFromOrmTest extends BaseTestCase { ServerMetrics metrics = collectMetrics(); - List stats = metrics.getQueryMetrics(); + List stats = metrics.queryMetrics(); for (MetaQueryMetric stat : stats) { - long meanMicros = stat.getMean(); + long meanMicros = stat.mean(); assertThat(meanMicros).isLessThan(900_000); - assertThat(stat.getLocation()).isSameAs(loc0.location()); + assertThat(stat.location()).isSameAs(loc0.location()); } assertThat(stats).hasSize(1); - assertThat(stats.get(0).getCount()).isEqualTo(4); + assertThat(stats.get(0).count()).isEqualTo(4); } @ForPlatform(Platform.H2) diff --git a/ebean-core/src/test/java/io/ebean/DtoQueryTest.java b/ebean-core/src/test/java/io/ebean/DtoQueryTest.java index eea1c6da3..0331a8215 100644 --- a/ebean-core/src/test/java/io/ebean/DtoQueryTest.java +++ b/ebean-core/src/test/java/io/ebean/DtoQueryTest.java @@ -42,14 +42,14 @@ public class DtoQueryTest extends BaseTestCase { ServerMetrics metrics = collectMetrics(); - List stats = metrics.getQueryMetrics(); + List stats = metrics.queryMetrics(); for (MetaQueryMetric stat : stats) { - long meanMicros = stat.getMean(); + long meanMicros = stat.mean(); assertThat(meanMicros).isLessThan(900_000); } assertThat(stats).hasSize(1); - assertThat(stats.get(0).getCount()).isEqualTo(1); + assertThat(stats.get(0).count()).isEqualTo(1); } @Test @@ -283,13 +283,13 @@ public class DtoQueryTest extends BaseTestCase { BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true, true); server().getMetaInfoManager().visitMetrics(basic); - List stats = basic.getQueryMetrics(); + List stats = basic.queryMetrics(); assertThat(stats).hasSize(1); MetaQueryMetric queryMetric = stats.get(0); - assertThat(queryMetric.getLabel()).isEqualTo("basic"); - assertThat(queryMetric.getCount()).isEqualTo(3); - assertThat(queryMetric.getName()).isEqualTo("dto.DCust_basic"); + assertThat(queryMetric.label()).isEqualTo("basic"); + assertThat(queryMetric.count()).isEqualTo(3); + assertThat(queryMetric.name()).isEqualTo("dto.DCust_basic"); server().findDto(DCust.class, "select c4.id, c4.name from o_customer c4 where lower(c4.name) = :name") @@ -299,7 +299,7 @@ public class DtoQueryTest extends BaseTestCase { ServerMetrics metric2 = server().getMetaInfoManager().collectMetrics(); - stats = metric2.getQueryMetrics(); + stats = metric2.queryMetrics(); assertThat(stats).hasSize(2); log.info("stats " + stats); diff --git a/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java b/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java index ca2caa4c8..5b9c42991 100644 --- a/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java +++ b/ebean-core/src/test/java/io/ebean/UpdateQueryTest.java @@ -39,10 +39,10 @@ public class UpdateQueryTest extends BaseTestCase { assertSql(query).contains("update o_customer set status=?, updtime=? where status = ? and id > ?"); ServerMetrics metrics = collectMetrics(); - List ormQueryMetrics = metrics.getQueryMetrics(); + List ormQueryMetrics = metrics.queryMetrics(); assertThat(ormQueryMetrics).hasSize(1); - assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class); - assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateActive"); + assertThat(ormQueryMetrics.get(0).type()).isEqualTo(Customer.class); + assertThat(ormQueryMetrics.get(0).label()).isEqualTo("updateActive"); } @Test @@ -69,10 +69,10 @@ public class UpdateQueryTest extends BaseTestCase { assertSql(sql.get(0)).contains("update o_customer set status = status"); ServerMetrics metrics = collectMetrics(); - List ormQueryMetrics = metrics.getQueryMetrics(); + List ormQueryMetrics = metrics.queryMetrics(); assertThat(ormQueryMetrics).hasSize(1); - assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class); - assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateAll"); + assertThat(ormQueryMetrics.get(0).type()).isEqualTo(Customer.class); + assertThat(ormQueryMetrics.get(0).label()).isEqualTo("updateAll"); } @Test diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java index d53f0e973..9f26e978c 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/deploy/BeanIudMetricsTest.java @@ -23,11 +23,11 @@ public class BeanIudMetricsTest { BasicMetricVisitor basic = new BasicMetricVisitor(); iudMetrics.visit(basic); - List timed = basic.getTimedMetrics(); + List timed = basic.timedMetrics(); assertThat(timed).hasSize(1); - assertThat(timed.get(0).getCount()).isEqualTo(4); - assertThat(timed.get(0).getName()).isEqualTo("iud.one.insertBatch"); + assertThat(timed.get(0).count()).isEqualTo(4); + assertThat(timed.get(0).name()).isEqualTo("iud.one.insertBatch"); iudMetrics.addBatch(PersistRequest.Type.UPDATE, startNanos, 1); iudMetrics.addBatch(PersistRequest.Type.DELETE_SOFT, startNanos, 2); @@ -37,15 +37,15 @@ public class BeanIudMetricsTest { basic = new BasicMetricVisitor(); iudMetrics.visit(basic); - timed = basic.getTimedMetrics(); + timed = basic.timedMetrics(); assertThat(timed).hasSize(3); - assertThat(timed.get(0).getCount()).isEqualTo(16); - assertThat(timed.get(0).getName()).isEqualTo("iud.one.insertBatch"); - assertThat(timed.get(1).getCount()).isEqualTo(3); - assertThat(timed.get(1).getName()).isEqualTo("iud.one.updateBatch"); - assertThat(timed.get(2).getCount()).isEqualTo(12); - assertThat(timed.get(2).getName()).isEqualTo("iud.one.deleteBatch"); + assertThat(timed.get(0).count()).isEqualTo(16); + assertThat(timed.get(0).name()).isEqualTo("iud.one.insertBatch"); + assertThat(timed.get(1).count()).isEqualTo(3); + assertThat(timed.get(1).name()).isEqualTo("iud.one.updateBatch"); + assertThat(timed.get(2).count()).isEqualTo(12); + assertThat(timed.get(2).name()).isEqualTo("iud.one.deleteBatch"); } @Test @@ -63,15 +63,15 @@ public class BeanIudMetricsTest { BasicMetricVisitor basic = new BasicMetricVisitor(); iudMetrics.visit(basic); - List timed = basic.getTimedMetrics(); + List timed = basic.timedMetrics(); assertThat(timed).hasSize(3); - assertThat(timed.get(0).getCount()).isEqualTo(1); - assertThat(timed.get(0).getName()).isEqualTo("iud.one.insert"); - assertThat(timed.get(1).getCount()).isEqualTo(2); - assertThat(timed.get(1).getName()).isEqualTo("iud.one.update"); - assertThat(timed.get(2).getCount()).isEqualTo(2); - assertThat(timed.get(2).getName()).isEqualTo("iud.one.delete"); + assertThat(timed.get(0).count()).isEqualTo(1); + assertThat(timed.get(0).name()).isEqualTo("iud.one.insert"); + assertThat(timed.get(1).count()).isEqualTo(2); + assertThat(timed.get(1).name()).isEqualTo("iud.one.update"); + assertThat(timed.get(2).count()).isEqualTo(2); + assertThat(timed.get(2).name()).isEqualTo("iud.one.delete"); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java index 43f0e0426..cb6d4d662 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricMapTest.java @@ -21,17 +21,17 @@ public class DTimedMetricMapTest { BasicMetricVisitor visitor = new BasicMetricVisitor(); metricMap.visit(visitor); - MetaTimedMetric timedMetric = visitor.getTimedMetrics().get(0); - assertThat(timedMetric.getCount()).isEqualTo(1); - assertThat(timedMetric.getTotal()).isGreaterThan(10); + MetaTimedMetric timedMetric = visitor.timedMetrics().get(0); + assertThat(timedMetric.count()).isEqualTo(1); + assertThat(timedMetric.total()).isGreaterThan(10); metricMap.addSinceNanos("some", nanos); visitor = new BasicMetricVisitor(); metricMap.visit(visitor); - timedMetric = visitor.getTimedMetrics().get(0); - assertThat(timedMetric.getCount()).isEqualTo(1); - assertThat(timedMetric.getTotal()).isGreaterThan(10); + timedMetric = visitor.timedMetrics().get(0); + assertThat(timedMetric.count()).isEqualTo(1); + assertThat(timedMetric.total()).isGreaterThan(10); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java index 28e651b88..6dbbb45d3 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/DTimedMetricTest.java @@ -17,16 +17,16 @@ public class DTimedMetricTest { metric.addSinceNanos(start); DTimeMetricStats stats = metric.collect(true); - assertThat(stats.getCount()).isEqualTo(1); - assertThat(stats.getTotal()).isGreaterThan(10); - assertThat(stats.getMax()).isEqualTo(stats.getTotal()); + assertThat(stats.count()).isEqualTo(1); + assertThat(stats.total()).isGreaterThan(10); + assertThat(stats.max()).isEqualTo(stats.total()); metric.addSinceNanos(start); stats = metric.collect(true); - assertThat(stats.getCount()).isEqualTo(1); - assertThat(stats.getTotal()).isGreaterThan(10); - assertThat(stats.getMax()).isEqualTo(stats.getTotal()); + assertThat(stats.count()).isEqualTo(1); + assertThat(stats.total()).isGreaterThan(10); + assertThat(stats.max()).isEqualTo(stats.total()); } @Test @@ -40,16 +40,16 @@ public class DTimedMetricTest { metric.addBatchSince(start, 5); DTimeMetricStats stats = metric.collect(true); - assertThat(stats.getCount()).isEqualTo(5); - assertThat(stats.getTotal()).isGreaterThan(10000); - assertThat(stats.getMax()).isEqualTo(stats.getTotal() / 5); - assertThat(stats.getMax()).isGreaterThan(10000 / 5); + assertThat(stats.count()).isEqualTo(5); + assertThat(stats.total()).isGreaterThan(10000); + assertThat(stats.max()).isEqualTo(stats.total() / 5); + assertThat(stats.max()).isGreaterThan(10000 / 5); metric.addBatchSince(start, 2); stats = metric.collect(true); - assertThat(stats.getCount()).isEqualTo(2); - assertThat(stats.getTotal()).isGreaterThan(10000); - assertThat(stats.getMax()).isEqualTo(stats.getTotal() / 2); + assertThat(stats.count()).isEqualTo(2); + assertThat(stats.total()).isGreaterThan(10000); + assertThat(stats.max()).isEqualTo(stats.total() / 2); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java index c67d14b30..29658566c 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java @@ -26,7 +26,7 @@ public class SortMetricTest { list.add(create("a")); list.sort(sortMetric); - String names = list.stream().map(DTimeMetricStats::getName).collect(Collectors.joining()); + String names = list.stream().map(DTimeMetricStats::name).collect(Collectors.joining()); assertEquals("nullabcd", names); } diff --git a/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java b/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java index 6e956510a..2f9cdf7f1 100644 --- a/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java +++ b/ebean-core/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java @@ -80,15 +80,15 @@ public class TestBatchInsertFlush extends BaseTestCase { } ServerMetrics metrics = collectMetrics(); - List txnStats = metrics.getTimedMetrics(); + List txnStats = metrics.timedMetrics(); for (MetaTimedMetric txnMetric : txnStats) { System.out.println(txnMetric); } assertThat(txnStats).hasSize(4); - assertThat(txnStats.get(0).getName()).isEqualTo("txn.main"); - assertThat(txnStats.get(1).getName()).isEqualTo("txn.named.TestBatchInsertFlush.no_cascade"); - assertThat(txnStats.get(2).getName()).isEqualTo("iud.TSDetail.insertBatch"); - assertThat(txnStats.get(3).getName()).isEqualTo("iud.TSMaster.insertBatch"); + assertThat(txnStats.get(0).name()).isEqualTo("txn.main"); + assertThat(txnStats.get(1).name()).isEqualTo("txn.named.TestBatchInsertFlush.no_cascade"); + assertThat(txnStats.get(2).name()).isEqualTo("iud.TSDetail.insertBatch"); + assertThat(txnStats.get(3).name()).isEqualTo("iud.TSMaster.insertBatch"); } @Test diff --git a/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java b/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java index 3ba05ee68..3ccad9fb8 100644 --- a/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java +++ b/ebean-core/src/test/java/org/tests/m2m/TestM2mDeleteObject.java @@ -28,7 +28,7 @@ public class TestM2mDeleteObject extends BaseTestCase { List sqlMetrics = sqlMetrics(); assertThat(sqlMetrics).hasSize(1); - assertThat(sqlMetrics.get(0).getName()).isEqualTo("orm.update.deleteAllPermissions"); + assertThat(sqlMetrics.get(0).name()).isEqualTo("orm.update.deleteAllPermissions"); Tenant t = new Tenant("tenant"); diff --git a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java index b6a40903f..108b4a398 100644 --- a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -176,7 +176,7 @@ public class TestCustomerFinder extends BaseTestCase { // change default collect query plan threshold to 200 micros QueryPlanInit init0 = new QueryPlanInit(); init0.setAll(true); - init0.setThresholdMicros(2); + init0.thresholdMicros(2); final List plans = server().getMetaInfoManager().queryPlanInit(init0); assertThat(plans.size()).isGreaterThan(1); @@ -186,7 +186,7 @@ public class TestCustomerFinder extends BaseTestCase { // change query plan threshold to 100 micros QueryPlanInit init = new QueryPlanInit(); init.setAll(true); - init.setThresholdMicros(1); + init.thresholdMicros(1); final List appliedToPlans = server().getMetaInfoManager().queryPlanInit(init); assertThat(appliedToPlans.size()).isGreaterThan(4); @@ -195,30 +195,30 @@ public class TestCustomerFinder extends BaseTestCase { ServerMetrics metrics = DB.getDefault().getMetaInfoManager().collectMetrics(); - List planStats = metrics.getQueryMetrics(); + List planStats = metrics.queryMetrics(); assertThat(planStats.size()).isGreaterThan(4); for (MetaQueryMetric planStat : planStats) { System.out.println(planStat); } - for (MetaTimedMetric txnTimed : metrics.getTimedMetrics()) { + for (MetaTimedMetric txnTimed : metrics.timedMetrics()) { System.out.println(txnTimed); } // obtains db query plans ... QueryPlanRequest request = new QueryPlanRequest(); // collect max 1000 plans (use something more like 10) - request.setMaxCount(1_000); + request.maxCount(1_000); // don't collect any more plans if used 10 secs - request.setMaxTimeMillis(10_000); + request.maxTimeMillis(10_000); List plans0 = server().getMetaInfoManager().queryPlanCollectNow(request); assertThat(plans0).isNotEmpty(); for (MetaQueryPlan plan : plans) { logger.info("queryPlan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}", - plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(), - plan.getSql(), plan.getBind(), plan.getPlan()); + plan.label(), plan.queryTimeMicros(), plan.profileLocation(), + plan.sql(), plan.bind(), plan.plan()); System.out.println(plan); } diff --git a/ebean-core/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java b/ebean-core/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java index 996f08bbe..f16828ec5 100644 --- a/ebean-core/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java +++ b/ebean-core/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java @@ -407,7 +407,7 @@ public class SqlQueryTests extends BaseTestCase { List sqlMetrics = sqlMetrics(); assertThat(sqlMetrics).hasSize(1); - assertThat(sqlMetrics.get(0).getName()).isEqualTo("sql.query.findEach-Max10Rows"); + assertThat(sqlMetrics.get(0).name()).isEqualTo("sql.query.findEach-Max10Rows"); } @Test diff --git a/ebean-core/src/test/java/org/tests/transaction/TestNestedMandatory.java b/ebean-core/src/test/java/org/tests/transaction/TestNestedMandatory.java index 91eb4e14f..de3184595 100644 --- a/ebean-core/src/test/java/org/tests/transaction/TestNestedMandatory.java +++ b/ebean-core/src/test/java/org/tests/transaction/TestNestedMandatory.java @@ -33,7 +33,7 @@ public class TestNestedMandatory extends BaseTestCase { } assertThat(txnMetrics).hasSize(2); - assertThat(txnMetrics.get(1).getName()).isEqualTo("txn.named.outer"); + assertThat(txnMetrics.get(1).name()).isEqualTo("txn.named.outer"); } class Outer { diff --git a/ebean-core/src/test/java/org/tests/transaction/TestTransactionalReadOnly.java b/ebean-core/src/test/java/org/tests/transaction/TestTransactionalReadOnly.java index 99f776f14..28e0be699 100644 --- a/ebean-core/src/test/java/org/tests/transaction/TestTransactionalReadOnly.java +++ b/ebean-core/src/test/java/org/tests/transaction/TestTransactionalReadOnly.java @@ -20,9 +20,9 @@ public class TestTransactionalReadOnly extends BaseTestCase { resetAllMetrics(); executeTransactionalUsingReadOnlyDataSource(); - final List timedMetrics = collectMetrics().getTimedMetrics(); + final List timedMetrics = collectMetrics().timedMetrics(); final Optional txnReadOnly = metric(timedMetrics, "txn.readonly"); - assertThat(txnReadOnly.get().getCount()).isEqualTo(1); + assertThat(txnReadOnly.get().count()).isEqualTo(1); assertThat(metric(timedMetrics, "txn")).isEmpty(); } @@ -32,15 +32,15 @@ public class TestTransactionalReadOnly extends BaseTestCase { resetAllMetrics(); executeTransactionalUsingMainDataSource(); - final List timedMetrics = collectMetrics().getTimedMetrics(); + final List timedMetrics = collectMetrics().timedMetrics(); final Optional txnMain = metric(timedMetrics, "txn.main"); - assertThat(txnMain.get().getCount()).isEqualTo(1); + assertThat(txnMain.get().count()).isEqualTo(1); assertThat(metric(timedMetrics, "txn.readonly")).isEmpty(); } private Optional metric(List timedMetrics, String name) { return timedMetrics.stream() - .filter(metaTimedMetric -> metaTimedMetric.getName().equals(name)) + .filter(metaTimedMetric -> metaTimedMetric.name().equals(name)) .findFirst(); } diff --git a/ebean-core/src/test/java/org/tests/update/TestSqlUpdateInTxn.java b/ebean-core/src/test/java/org/tests/update/TestSqlUpdateInTxn.java index 50ca87970..c539beda8 100644 --- a/ebean-core/src/test/java/org/tests/update/TestSqlUpdateInTxn.java +++ b/ebean-core/src/test/java/org/tests/update/TestSqlUpdateInTxn.java @@ -126,8 +126,8 @@ public class TestSqlUpdateInTxn extends BaseTestCase { List sqlMetrics = sqlMetrics(); assertThat(sqlMetrics).hasSize(1); - assertThat(sqlMetrics.get(0).getName()).isEqualTo("sql.update.auditLargeUpdate"); - assertThat(sqlMetrics.get(0).getCount()).isEqualTo(1); + assertThat(sqlMetrics.get(0).name()).isEqualTo("sql.update.auditLargeUpdate"); + assertThat(sqlMetrics.get(0).count()).isEqualTo(1); } @Test diff --git a/ebean-ddl-generator/src/test/java/io/ebean/BaseTestCase.java b/ebean-ddl-generator/src/test/java/io/ebean/BaseTestCase.java index 3abf51366..4c0410ff3 100644 --- a/ebean-ddl-generator/src/test/java/io/ebean/BaseTestCase.java +++ b/ebean-ddl-generator/src/test/java/io/ebean/BaseTestCase.java @@ -8,9 +8,7 @@ import io.ebean.meta.MetaTimedMetric; import io.ebean.meta.ServerMetrics; import io.ebean.util.StringHelper; import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.api.SpiTransaction; -import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.expression.platform.DbExpressionHandler; import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory; @@ -88,14 +86,14 @@ public abstract class BaseTestCase { } protected List visitTimedMetrics() { - return collectMetrics().getTimedMetrics(); + return collectMetrics().timedMetrics(); } protected List sqlMetrics() { List timedMetrics = visitTimedMetrics(); return timedMetrics.stream() - .filter((it) -> it.getName().startsWith("sql.") || it.getName().startsWith("orm.")) + .filter((it) -> it.name().startsWith("sql.") || it.name().startsWith("orm.")) .collect(Collectors.toList()); } diff --git a/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java b/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java index 39e670a7f..a69fb6766 100644 --- a/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java +++ b/ebean-redis/src/main/java/io/ebean/redis/RedisCache.java @@ -255,14 +255,13 @@ class RedisCache implements ServerCache { @Override public ServerCacheStatistics getStatistics(boolean reset) { - ServerCacheStatistics cacheStats = new ServerCacheStatistics(); cacheStats.setCacheName(cacheKey); cacheStats.setHitCount(hitCount.get(reset)); cacheStats.setMissCount(missCount.get(reset)); - cacheStats.setPutCount(metricPut.collect(reset).getCount()); - cacheStats.setRemoveCount(metricRemove.collect(reset).getCount()); - cacheStats.setClearCount(metricClear.collect(reset).getCount()); + cacheStats.setPutCount(metricPut.collect(reset).count()); + cacheStats.setRemoveCount(metricRemove.collect(reset).count()); + cacheStats.setClearCount(metricClear.collect(reset).count()); return cacheStats; } } From fea7de42d5507f5c207dd05889a07f39f4018148 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 6 Aug 2021 14:48:42 +1200 Subject: [PATCH 64/87] #2290 - Add MetaTimedMetric locationHash() which is a crc32 checksum of package + type + method (excludes line number) --- .../main/java/io/ebean/ProfileLocation.java | 8 +++++ .../java/io/ebean/meta/MetaTimedMetric.java | 8 +++++ .../main/java/io/ebean/meta/MetricData.java | 9 ++++++ .../io/ebean/metric/TimedMetricStats.java | 5 ++++ .../server/core/DumpMetrics.java | 1 + .../server/core/DumpMetricsData.java | 2 ++ .../server/core/DumpMetricsJson.java | 2 ++ .../server/profile/BasicProfileLocation.java | 7 +++++ .../server/profile/DProfileLocation.java | 11 ++++--- .../server/profile/DQueryPlanMeta.java | 4 +++ .../server/profile/DQueryPlanMetric.java | 5 ++++ .../server/profile/DTimeMetricStats.java | 22 +++++++++----- .../server/profile/UtilLocation.java | 15 +++++++++- .../server/query/CQueryPlan.java | 30 ++++++------------- .../server/query/CQueryPlanStats.java | 5 ++++ .../server/profile/UtilLocationTest.java | 8 ++++- .../model/basic/finder/CustomerFinder.java | 2 -- .../tests/profile/ProfileLocationTest.java | 20 +++++++++---- .../query/finder/TestCustomerFinder.java | 3 +- 19 files changed, 124 insertions(+), 43 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/ProfileLocation.java b/ebean-api/src/main/java/io/ebean/ProfileLocation.java index 33584a0b3..605de580a 100644 --- a/ebean-api/src/main/java/io/ebean/ProfileLocation.java +++ b/ebean-api/src/main/java/io/ebean/ProfileLocation.java @@ -45,6 +45,14 @@ public interface ProfileLocation { */ String label(); + /** + * Return a hash of the location that intentionally excludes the line number. + *

+ * The hash is expected to be stable regardless of line number in the source file + * so that is identifies the class and method location over a long time. + */ + long hash(); + /** * Return the full location. */ diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java index f95f3248d..dbd0f0a0e 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java @@ -6,6 +6,14 @@ package io.ebean.meta; */ public interface MetaTimedMetric extends MetaMetric { + /** + * Return the metric location hash if defined. + *

+ * This hash excludes line number with the intention of being stable over time + * as code changes move the source line (but the method is the same). + */ + long locationHash(); + /** * Return the metric location if defined. */ diff --git a/ebean-api/src/main/java/io/ebean/meta/MetricData.java b/ebean-api/src/main/java/io/ebean/meta/MetricData.java index d8f80a0a9..7f2eb38b6 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetricData.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetricData.java @@ -14,6 +14,7 @@ public class MetricData { private Long mean; private Long max; private Long total; + private long locHash; public MetricData(String name) { this.name = name; @@ -38,6 +39,14 @@ public class MetricData { this.sqlHash = sqlHash; } + public void setLocHash(long locHash) { + this.locHash = locHash; + } + + public long getLocHash() { + return locHash; + } + public String getLoc() { return loc; } diff --git a/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java b/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java index 3012e9e9f..8c88d839e 100644 --- a/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java +++ b/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java @@ -12,6 +12,11 @@ public interface TimedMetricStats extends MetaTimedMetric { */ void setLocation(String location); + /** + * Additionally set the location hash. + */ + void setLocationHash(long locationHash); + /** * Override the name based on profile location. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java index f31fa849c..968e96a98 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java @@ -134,6 +134,7 @@ class DumpMetrics { String location = metric.location(); if (dumpLoc && location != null) { sb.append("\n loc:").append(location); + sb.append("\n locHash:").append(metric.locationHash()); } if (dumpSql) { sb.append(" \n\n sql:").append(metric.sql()).append("\n\n"); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java index 4ac8859b6..07980228f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java @@ -56,6 +56,7 @@ class DumpMetricsData { final MetricData data = create(metric); appendCounters(data, metric); data.setLoc(metric.location()); + data.setLocHash(metric.locationHash()); } private void addCount(MetaCountMetric metric) { @@ -71,6 +72,7 @@ class DumpMetricsData { } private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) { + data.setLocHash(metric.locationHash()); data.setLoc(metric.location()); data.setSql(metric.sql()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java index b0644ec9f..c116ebd26 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java @@ -188,6 +188,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { metricStart(metric); appendTiming(metric); if (isIncludeDetail(metric)) { + keyVal("locHash", metric.locationHash()); appendExtra("loc", metric.location()); } metricEnd(); @@ -198,6 +199,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { appendTiming(metric); if (withHash) { keyVal("sqlHash", metric.sqlHash()); + keyVal("locHash", metric.locationHash()); } if (isIncludeDetail(metric)) { appendExtra("loc", metric.location()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java index 507938a96..8e8a5ffc0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java @@ -10,9 +10,11 @@ final class BasicProfileLocation implements ProfileLocation { private final String fullLocation; private final String location; private final String label; + private final long hash; BasicProfileLocation(String fullLocation) { this.fullLocation = fullLocation; + this.hash = UtilLocation.hash(fullLocation); this.location = shortDesc(fullLocation); this.label = UtilLocation.label(location); } @@ -42,6 +44,11 @@ final class BasicProfileLocation implements ProfileLocation { return location; } + @Override + public long hash() { + return hash; + } + @Override public String fullLocation() { return fullLocation; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java index c5ff7d3ac..9c6340ad4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java @@ -8,17 +8,14 @@ import io.ebean.ProfileLocation; class DProfileLocation implements ProfileLocation { private static final String IO_EBEAN = "io.ebean"; - private static final String UNKNOWN = "unknown"; private String fullLocation; - private String location; - private String label; + private long hash; private final int lineNumber; - private int traceCount; DProfileLocation() { @@ -53,6 +50,7 @@ class DProfileLocation implements ProfileLocation { label = UtilLocation.label(shortDesc); location = shortDesc; fullLocation = loc; + hash = UtilLocation.hash(loc); initWith(label); return true; } @@ -71,6 +69,11 @@ class DProfileLocation implements ProfileLocation { return location; } + @Override + public long hash() { + return hash; + } + @Override public String fullLocation() { return fullLocation; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java index 5359d96ed..0354bbd0a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java @@ -49,6 +49,10 @@ class DQueryPlanMeta { return (profileLocation == null) ? null : profileLocation.location(); } + public long getLocationHash() { + return (profileLocation == null) ? 0 : profileLocation.hash(); + } + public String getSql() { return sql; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index fda1d4d9b..41a0658f4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -83,6 +83,11 @@ class DQueryPlanMetric implements QueryPlanMetric { return meta.getLocation(); } + @Override + public long locationHash() { + return meta.getLocationHash(); + } + @Override public long count() { return stats.count(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java index 2207fef3d..82731dfd3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java @@ -7,18 +7,15 @@ import io.ebean.metric.TimedMetricStats; */ class DTimeMetricStats implements TimedMetricStats { - private String name; - private final boolean collected; - - private String location; - private final long count; - private final long total; - private final long max; + private String name; + private String location; + private long locationHash; + DTimeMetricStats(String name, boolean collected, long count, long total, long max) { this.name = name; this.collected = collected; @@ -40,6 +37,7 @@ class DTimeMetricStats implements TimedMetricStats { .append(" max:").append(max); if (location != null) { sb.append(" loc:").append(location); + sb.append(" locHash:").append(locationHash); } return sb.toString(); } @@ -49,6 +47,11 @@ class DTimeMetricStats implements TimedMetricStats { this.location = location; } + @Override + public void setLocationHash(long locationHash) { + this.locationHash = locationHash; + } + @Override public boolean initialCollection() { return !collected; @@ -69,6 +72,11 @@ class DTimeMetricStats implements TimedMetricStats { return location; } + @Override + public long locationHash() { + return locationHash; + } + /** * Return the count of values collected. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java index 7b70dfcb7..00b388bbd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java @@ -1,7 +1,21 @@ package io.ebeaninternal.server.profile; +import io.ebeaninternal.server.util.Checksum; + final class UtilLocation { + /** + * Return a hash of the full description excluding the source line number. + */ + static long hash(String full) { + final int pos = full.lastIndexOf('('); + if (pos > -1) { + return Checksum.checksum(full.substring(0, pos)); + } else { + return Checksum.checksum(full); + } + } + static String label(String shortDescription) { int pos = shortDescription.indexOf("("); if (pos == -1) { @@ -21,5 +35,4 @@ final class UtilLocation { } return desc; } - } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index 796290808..770ac522d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -54,46 +54,32 @@ public class CQueryPlan implements SpiQueryPlan { static final String RESULT_SET_BASED_RAW_SQL = "--ResultSetBasedRawSql"; private final SpiEbeanServer server; - private final ProfileLocation profileLocation; - private final String location; - + private final long locationHash; private final String label; - private final String name; - private final CQueryPlanKey planKey; - private final boolean rawSql; - private final String sql; private final long sqlHash; - private final String logWhereSql; - private final SqlTree sqlTree; /** * Encrypted properties required additional binding. */ private final STreeProperty[] encryptedProps; - private final CQueryPlanStats stats; - private final Class beanType; - final DataTimeZone dataTimeZone; - private final int asOfTableCount; /** * Key used to identify the query plan in audit logging. */ private volatile String auditQueryHash; - private final Set dependentTables; - private final SpiQueryBindCapture bindCapture; /** @@ -106,9 +92,10 @@ public class CQueryPlan implements SpiQueryPlan { this.planKey = request.getQueryPlanKey(); SpiQuery query = request.getQuery(); this.profileLocation = query.getProfileLocation(); + this.location = (profileLocation == null) ? null : profileLocation.location(); + this.locationHash = (profileLocation == null) ? 0 : profileLocation.hash(); this.label = query.getPlanLabel(); this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName()); - this.location = location(); this.asOfTableCount = query.getAsOfTableCount(); this.sql = sqlRes.getSql(); this.sqlTree = sqlTree; @@ -130,9 +117,10 @@ public class CQueryPlan implements SpiQueryPlan { this.beanType = request.getBeanDescriptor().getBeanType(); SpiQuery query = request.getQuery(); this.profileLocation = query.getProfileLocation(); + this.location = (profileLocation == null) ? null : profileLocation.location(); + this.locationHash = (profileLocation == null) ? 0 : profileLocation.hash(); this.label = query.getPlanLabel(); this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName()); - this.location = location(); this.planKey = buildPlanKey(sql, logWhereSql); this.asOfTableCount = 0; this.sql = sql; @@ -169,10 +157,6 @@ public class CQueryPlan implements SpiQueryPlan { return sql.equals(RESULT_SET_BASED_RAW_SQL) || query.getType().isUpdate() ? SpiQueryBindCapture.NOOP : server.createQueryBindCapture(this); } - private String location() { - return (profileLocation == null) ? null : profileLocation.location(); - } - private CQueryPlanKey buildPlanKey(String sql, String logWhereSql) { return new RawSqlQueryPlanKey(sql, false, logWhereSql); } @@ -219,6 +203,10 @@ public class CQueryPlan implements SpiQueryPlan { return location; } + public long getLocationHash() { + return locationHash; + } + @Override public void queryPlanInit(long thresholdMicros) { bindCapture.queryPlanInit(thresholdMicros); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java index 36e070653..ab7dc0b63 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java @@ -106,6 +106,11 @@ public final class CQueryPlanStats { return queryPlan.getLocation(); } + @Override + public long locationHash() { + return queryPlan.getLocationHash(); + } + @Override public long count() { return metrics.count(); diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java index 1bdbcd2f7..5fcbe90a4 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java @@ -8,8 +8,14 @@ public class UtilLocationTest { @Test public void label() { - assertThat(UtilLocation.label("foo")).isEqualTo("foo"); assertThat(UtilLocation.label("ProfileLocationTest$Other.(ProfileLocationTest.java:47)")).isEqualTo("ProfileLocationTest$Other.init"); } + + @Test + public void hash() { + assertThat(UtilLocation.hash("org.foo.MyFoo.doIt(MyFoo.java:12)")).isEqualTo(396279222L); + assertThat(UtilLocation.hash("org.foo.MyFoo.doIt(MyFoo.java:13)")).isEqualTo(396279222L); + assertThat(UtilLocation.hash("org.foo.MyFoo.doIt(MyFoo.java:945)")).isEqualTo(396279222L); + } } diff --git a/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java b/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java index c76728fca..79db60023 100644 --- a/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java +++ b/ebean-core/src/test/java/org/tests/model/basic/finder/CustomerFinder.java @@ -37,7 +37,6 @@ public class CustomerFinder extends Finder { } public List byNameStatus(String nameStartsWith, Customer.Status status) { - return query("where status = :status and name istartsWith :name order by name") .setParameter("status", status) .setParameter("name", nameStartsWith) @@ -45,7 +44,6 @@ public class CustomerFinder extends Finder { } public List namesStartingWith(String name) { - return nativeSql("select name from o_customer where name like ? order by name") .setParameter(name + "%") .findSingleAttributeList(); diff --git a/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java b/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java index d93b5c75f..d452b7463 100644 --- a/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java +++ b/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java @@ -7,20 +7,28 @@ import static org.assertj.core.api.Assertions.assertThat; public class ProfileLocationTest { - private static ProfileLocation loc = ProfileLocation.create(12, "foo"); - - private static ProfileLocation loc2 = ProfileLocation.create(); + private static final ProfileLocation loc = ProfileLocation.create(12, "foo"); + private static final ProfileLocation locB = ProfileLocation.create(); + private static final ProfileLocation loc2 = ProfileLocation.create(); private boolean doIt() { + locB.obtain(); // simulate a location moving by line number only return loc.obtain(); } @Test public void test_obtain() { assertThat(doIt()).isTrue(); - assertThat(loc.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:15)"); - assertThat(loc.location()).isEqualTo("ProfileLocationTest.doIt(ProfileLocationTest.java:15)"); + assertThat(loc.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:16)"); + assertThat(loc.location()).isEqualTo("ProfileLocationTest.doIt(ProfileLocationTest.java:16)"); assertThat(loc.label()).isEqualTo("ProfileLocationTest.doIt"); + assertThat(loc.hash()).isEqualTo(1867926812L); + + // same hash even when the line number has changed + assertThat(locB.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:15)"); + assertThat(locB.location()).isEqualTo("ProfileLocationTest.doIt(ProfileLocationTest.java:15)"); + assertThat(locB.label()).isEqualTo("ProfileLocationTest.doIt"); + assertThat(locB.hash()).isEqualTo(1867926812L); } @Test @@ -35,7 +43,7 @@ public class ProfileLocationTest { other.hashCode(); assertThat(loc2.label()).isEqualTo("ProfileLocationTest$Other.init"); - assertThat(loc2.location()).isEqualTo("ProfileLocationTest$Other.(ProfileLocationTest.java:44)"); + assertThat(loc2.location()).isEqualTo("ProfileLocationTest$Other.(ProfileLocationTest.java:52)"); } static class Other { diff --git a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java index 108b4a398..6dfd99422 100644 --- a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -242,7 +242,8 @@ public class TestCustomerFinder extends BaseTestCase { assertThat(metricsJson).contains("\"name\":\"txn.main\""); assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\""); - assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\""); + assertThat(metricsJson).contains("\"locHash\":3254522637"); + assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:43)\""); if (isH2() || isPostgres()) { assertThat(metricsJson).contains("\"sqlHash\":3634991469"); assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,"); From ee615836bafe925fc6e29cc23a9dc68132633c4b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 6 Aug 2021 14:56:09 +1200 Subject: [PATCH 65/87] [maven-release-plugin] prepare release ebean-parent-12.11.0 --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 6 +++--- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 10 +++++----- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 10 +++++----- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 11 +++++------ pom.xml | 4 ++-- querybean-generator/pom.xml | 2 +- 16 files changed, 64 insertions(+), 65 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 9afbb6446..645581876 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index 0b0355981..e4a105be8 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.11.0 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 35ed6d6a0..220dc3e72 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-api - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-core-type - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-ddl-generator - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-externalmapping-api - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-externalmapping-xml - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-autotune - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-querybean - 12.11.0-SNAPSHOT + 12.11.0 io.ebean querybean-generator - 12.11.0-SNAPSHOT + 12.11.0 provided io.ebean kotlin-querybean-generator - 12.11.0-SNAPSHOT + 12.11.0 provided io.ebean ebean-test - 12.11.0-SNAPSHOT + 12.11.0 test io.ebean ebean-postgis - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-redis - 12.11.0-SNAPSHOT + 12.11.0 diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index 35a29a677..7911ebaff 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.11.0-SNAPSHOT + 12.11.0 diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 3426582d3..43d628258 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.11.0 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-core-type - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-externalmapping-api - 12.11.0-SNAPSHOT + 12.11.0 diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 4035af5f2..5db1fd673 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean ddl generation @@ -28,14 +28,14 @@ io.ebean ebean-core-type - 12.11.0-SNAPSHOT + 12.11.0 provided io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index a9b9968b9..29cccfacc 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 3f0b71546..0997c343a 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.11.0 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.11.0-SNAPSHOT + 12.11.0 @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 test io.ebean ebean-ddl-generator - 12.11.0-SNAPSHOT + 12.11.0 test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index a6bfe2ed0..0c69fd30b 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.11.0-SNAPSHOT + 12.11.0 test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index b3ffadfc0..cfd9f569e 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.11.0-SNAPSHOT + 12.11.0 test io.ebean querybean-generator - 12.11.0-SNAPSHOT + 12.11.0 test io.ebean ebean-test - 12.11.0-SNAPSHOT + 12.11.0 test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index d0d5f8ee2..a3e5bbf91 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.11.0-SNAPSHOT + 12.11.0 provided io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 provided io.ebean ebean-querybean - 12.11.0-SNAPSHOT + 12.11.0 test io.ebean querybean-generator - 12.11.0-SNAPSHOT + 12.11.0 test io.ebean ebean-test - 12.11.0-SNAPSHOT + 12.11.0 test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 43b5ee6f3..97ae36c87 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 provided io.ebean ebean-ddl-generator - 12.11.0-SNAPSHOT + 12.11.0 diff --git a/ebean/pom.xml b/ebean/pom.xml index 819328290..e283dc067 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 io.ebean ebean-querybean - 12.11.0-SNAPSHOT + 12.11.0 diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 8f3cafa8a..80cdb4f26 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 kotlin querybean generator @@ -30,7 +29,7 @@ io.ebean ebean-querybean - 12.11.0-SNAPSHOT + 12.11.0 test @@ -44,7 +43,7 @@ io.ebean ebean-core - 12.11.0-SNAPSHOT + 12.11.0 test @@ -65,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.11.0-SNAPSHOT + 12.11.0 test diff --git a/pom.xml b/pom.xml index b86928387..d104f7afe 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.11.0-SNAPSHOT + 12.11.0 pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.8.0 + ebean-parent-12.11.0 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 956e39cb5..6d0b1af1b 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0-SNAPSHOT + 12.11.0 querybean generator From c4484c758ec2729d0796fc50573ada0920eb16f9 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Fri, 6 Aug 2021 14:56:15 +1200 Subject: [PATCH 66/87] [maven-release-plugin] prepare for next development iteration --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 6 +++--- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 10 +++++----- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 10 +++++----- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 8 ++++---- pom.xml | 4 ++-- querybean-generator/pom.xml | 2 +- 16 files changed, 63 insertions(+), 63 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 645581876..72fbfe3ae 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index e4a105be8..c47f7fe08 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.11.0 + ebean-parent-12.8.0 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 220dc3e72..fff064c90 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-api - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-core-type - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-ddl-generator - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-externalmapping-api - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-externalmapping-xml - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-autotune - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-querybean - 12.11.0 + 12.11.1-SNAPSHOT io.ebean querybean-generator - 12.11.0 + 12.11.1-SNAPSHOT provided io.ebean kotlin-querybean-generator - 12.11.0 + 12.11.1-SNAPSHOT provided io.ebean ebean-test - 12.11.0 + 12.11.1-SNAPSHOT test io.ebean ebean-postgis - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-redis - 12.11.0 + 12.11.1-SNAPSHOT diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index 7911ebaff..d8eab3e25 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.11.0 + 12.11.1-SNAPSHOT diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 43d628258..11aac552a 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.11.0 + ebean-parent-12.8.0 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-core-type - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-externalmapping-api - 12.11.0 + 12.11.1-SNAPSHOT diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 5db1fd673..f83a8b02e 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean ddl generation @@ -28,14 +28,14 @@ io.ebean ebean-core-type - 12.11.0 + 12.11.1-SNAPSHOT provided io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 29cccfacc..e506a6841 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 0997c343a..d0226a553 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.11.0 + ebean-parent-12.8.0 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.11.0 + 12.11.1-SNAPSHOT @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT test io.ebean ebean-ddl-generator - 12.11.0 + 12.11.1-SNAPSHOT test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 0c69fd30b..98b44522d 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.11.0 + 12.11.1-SNAPSHOT test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index cfd9f569e..059976afa 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.11.0 + 12.11.1-SNAPSHOT test io.ebean querybean-generator - 12.11.0 + 12.11.1-SNAPSHOT test io.ebean ebean-test - 12.11.0 + 12.11.1-SNAPSHOT test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index a3e5bbf91..943ab6985 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.11.0 + 12.11.1-SNAPSHOT provided io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT provided io.ebean ebean-querybean - 12.11.0 + 12.11.1-SNAPSHOT test io.ebean querybean-generator - 12.11.0 + 12.11.1-SNAPSHOT test io.ebean ebean-test - 12.11.0 + 12.11.1-SNAPSHOT test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 97ae36c87..4b9b05272 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT provided io.ebean ebean-ddl-generator - 12.11.0 + 12.11.1-SNAPSHOT diff --git a/ebean/pom.xml b/ebean/pom.xml index e283dc067..db01d91a1 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT io.ebean ebean-querybean - 12.11.0 + 12.11.1-SNAPSHOT diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 80cdb4f26..1b3f9d848 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.11.0 + 12.11.1-SNAPSHOT test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.11.0 + 12.11.1-SNAPSHOT test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.11.0 + 12.11.1-SNAPSHOT test diff --git a/pom.xml b/pom.xml index d104f7afe..dc3426809 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.11.0 + 12.11.1-SNAPSHOT pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.11.0 + ebean-parent-12.8.0 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 6d0b1af1b..a240e3b8b 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.11.0 + 12.11.1-SNAPSHOT querybean generator From c610f95b2d2da07ae15d8993b5fbdbd6dbfc25e4 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 9 Aug 2021 14:05:10 +1200 Subject: [PATCH 67/87] #2293 - Metric hash back to MD5 of sql + name + loc (minus location file and line source) ~= Revert of #2288 --- .../main/java/io/ebean/ProfileLocation.java | 8 --- .../java/io/ebean/meta/MetaQueryMetric.java | 11 +--- .../java/io/ebean/meta/MetaQueryPlan.java | 2 +- .../java/io/ebean/meta/MetaTimedMetric.java | 8 --- .../main/java/io/ebean/meta/MetricData.java | 19 ++----- .../java/io/ebean/meta/QueryPlanInit.java | 10 ++-- .../io/ebean/metric/TimedMetricStats.java | 5 -- .../io/ebeaninternal/api/SpiQueryPlan.java | 2 +- .../server/core/DumpMetrics.java | 3 +- .../server/core/DumpMetricsData.java | 4 +- .../server/core/DumpMetricsJson.java | 22 ++++---- .../server/deploy/BeanDescriptor.java | 2 +- .../server/profile/BasicProfileLocation.java | 20 +------ .../server/profile/DProfileLocation.java | 31 ++--------- .../server/profile/DQueryPlanMeta.java | 15 +++--- .../server/profile/DQueryPlanMetric.java | 9 +--- .../server/profile/DTimeMetricStats.java | 12 ----- .../server/profile/UtilLocation.java | 29 +++++----- .../server/query/CQueryPlan.java | 23 +++----- .../server/query/CQueryPlanStats.java | 9 +--- .../server/query/DQueryPlanOutput.java | 12 ++--- .../io/ebeaninternal/server/util/Md5.java | 36 +++++++++++++ .../profile/BasicProfileLocationTest.java | 6 +-- .../server/profile/UtilLocationTest.java | 9 ++-- .../io/ebeaninternal/server/util/Md5Test.java | 53 +++++++++++++++++++ .../tests/profile/ProfileLocationTest.java | 8 ++- .../query/finder/TestCustomerFinder.java | 5 +- 27 files changed, 172 insertions(+), 201 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java create mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java diff --git a/ebean-api/src/main/java/io/ebean/ProfileLocation.java b/ebean-api/src/main/java/io/ebean/ProfileLocation.java index 605de580a..33584a0b3 100644 --- a/ebean-api/src/main/java/io/ebean/ProfileLocation.java +++ b/ebean-api/src/main/java/io/ebean/ProfileLocation.java @@ -45,14 +45,6 @@ public interface ProfileLocation { */ String label(); - /** - * Return a hash of the location that intentionally excludes the line number. - *

- * The hash is expected to be stable regardless of line number in the source file - * so that is identifies the class and method location over a long time. - */ - long hash(); - /** * Return the full location. */ diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java index f5fee986c..1c9b5fe12 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryMetric.java @@ -45,15 +45,8 @@ public interface MetaQueryMetric extends MetaTimedMetric { } /** - * Return the hash of the sql. + * Return the hash of the plan. */ - long sqlHash(); + String hash(); - /** - * Migrate to sqlHash(). - */ - @Deprecated - default long getSqlHash() { - return sqlHash(); - } } diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java index 6cdce69a1..c1ae44a82 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaQueryPlan.java @@ -30,7 +30,7 @@ public interface MetaQueryPlan { /** * Return the hash of the plan. */ - long sqlHash(); + String hash(); /** * Return a description of the bind values. diff --git a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java index dbd0f0a0e..f95f3248d 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetaTimedMetric.java @@ -6,14 +6,6 @@ package io.ebean.meta; */ public interface MetaTimedMetric extends MetaMetric { - /** - * Return the metric location hash if defined. - *

- * This hash excludes line number with the intention of being stable over time - * as code changes move the source line (but the method is the same). - */ - long locationHash(); - /** * Return the metric location if defined. */ diff --git a/ebean-api/src/main/java/io/ebean/meta/MetricData.java b/ebean-api/src/main/java/io/ebean/meta/MetricData.java index 7f2eb38b6..b4606b1d7 100644 --- a/ebean-api/src/main/java/io/ebean/meta/MetricData.java +++ b/ebean-api/src/main/java/io/ebean/meta/MetricData.java @@ -6,7 +6,7 @@ package io.ebean.meta; public class MetricData { private String name; - private long sqlHash; + private String hash; private String loc; private String sql; @@ -14,7 +14,6 @@ public class MetricData { private Long mean; private Long max; private Long total; - private long locHash; public MetricData(String name) { this.name = name; @@ -31,20 +30,12 @@ public class MetricData { this.name = name; } - public long getSqlHash() { - return sqlHash; + public String getHash() { + return hash; } - public void setSqlHash(long sqlHash) { - this.sqlHash = sqlHash; - } - - public void setLocHash(long locHash) { - this.locHash = locHash; - } - - public long getLocHash() { - return locHash; + public void setHash(String hash) { + this.hash = hash; } public String getLoc() { diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java index 7caeabd2f..5cce68994 100644 --- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java +++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanInit.java @@ -10,7 +10,7 @@ public class QueryPlanInit { private boolean all; - private Set hashes = new HashSet<>(); + private Set hashes = new HashSet<>(); private long thresholdMicros; @@ -47,21 +47,21 @@ public class QueryPlanInit { /** * Return true if the query plan should be initiated based on it's hash. */ - public boolean includeHash(long sqlHash) { - return all || hashes.contains(sqlHash); + public boolean includeHash(String hash) { + return all || hashes.contains(hash); } /** * Return the specific hashes that we want to collect query plans on. */ - public Set sqlHashes() { + public Set hashes() { return hashes; } /** * Set the specific hashes that we want to collect query plans on. */ - public void sqlHashes(Set hashes) { + public void hashes(Set hashes) { this.hashes = hashes; } } diff --git a/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java b/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java index 8c88d839e..3012e9e9f 100644 --- a/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java +++ b/ebean-api/src/main/java/io/ebean/metric/TimedMetricStats.java @@ -12,11 +12,6 @@ public interface TimedMetricStats extends MetaTimedMetric { */ void setLocation(String location); - /** - * Additionally set the location hash. - */ - void setLocationHash(long locationHash); - /** * Override the name based on profile location. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java index 878dcc716..0b4c3ea0a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryPlan.java @@ -20,7 +20,7 @@ public interface SpiQueryPlan { /** * The hash of the sql. */ - long getSqlHash(); + String getHash(); /** * The SQL for the query plan. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java index 968e96a98..5274b4ced 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java @@ -120,7 +120,7 @@ class DumpMetrics { appendQueryName(metric, sb); appendCounters(metric, sb); if (dumpHash) { - sb.append("\n sqlHash:").append(metric.sqlHash()); + sb.append("\n hash:").append(metric.hash()); } appendProfileAndSql(metric, sb); out(sb.toString()); @@ -134,7 +134,6 @@ class DumpMetrics { String location = metric.location(); if (dumpLoc && location != null) { sb.append("\n loc:").append(location); - sb.append("\n locHash:").append(metric.locationHash()); } if (dumpSql) { sb.append(" \n\n sql:").append(metric.sql()).append("\n\n"); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java index 07980228f..4bc1e5035 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsData.java @@ -56,7 +56,6 @@ class DumpMetricsData { final MetricData data = create(metric); appendCounters(data, metric); data.setLoc(metric.location()); - data.setLocHash(metric.locationHash()); } private void addCount(MetaCountMetric metric) { @@ -68,11 +67,10 @@ class DumpMetricsData { final MetricData data = create(metric); appendCounters(data, metric); appendLocationAndSql(data, metric); - data.setSqlHash(metric.sqlHash()); + data.setHash(metric.hash()); } private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) { - data.setLocHash(metric.locationHash()); data.setLoc(metric.location()); data.setSql(metric.sql()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java index c116ebd26..90200f79b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java @@ -188,8 +188,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { metricStart(metric); appendTiming(metric); if (isIncludeDetail(metric)) { - keyVal("locHash", metric.locationHash()); - appendExtra("loc", metric.location()); + append("loc", metric.location()); } metricEnd(); } @@ -198,12 +197,11 @@ class DumpMetricsJson implements ServerMetricsAsJson { metricStart(metric); appendTiming(metric); if (withHash) { - keyVal("sqlHash", metric.sqlHash()); - keyVal("locHash", metric.locationHash()); + append("hash", metric.hash()); } if (isIncludeDetail(metric)) { - appendExtra("loc", metric.location()); - appendExtra("sql", metric.sql()); + append("loc", metric.location()); + append("sql", metric.sql()); } metricEnd(); } @@ -212,7 +210,7 @@ class DumpMetricsJson implements ServerMetricsAsJson { return includeExtraAttributes == 2 || includeExtraAttributes == 1 && metric.initialCollection(); } - private void appendExtra(String key, String val) throws IOException { + private void append(String key, String val) throws IOException { if (val != null) { key(key); val(val); @@ -220,13 +218,13 @@ class DumpMetricsJson implements ServerMetricsAsJson { } private void appendTiming(MetaTimedMetric timedMetric) throws IOException { - keyVal("count", timedMetric.count()); - keyVal("total", timedMetric.total()); - keyVal("mean", timedMetric.mean()); - keyVal("max", timedMetric.max()); + append("count", timedMetric.count()); + append("total", timedMetric.total()); + append("mean", timedMetric.mean()); + append("max", timedMetric.max()); } - private void keyVal(String key, long value) throws IOException { + private void append(String key, long value) throws IOException { key(key); val(value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 6a9acd839..8e8b02b6f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1558,7 +1558,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { void queryPlanInit(QueryPlanInit request, List list) { for (CQueryPlan queryPlan : queryPlanCache.values()) { - if (request.includeHash(queryPlan.getSqlHash())) { + if (request.includeHash(queryPlan.getHash())) { queryPlan.queryPlanInit(request.thresholdMicros()); list.add(queryPlan.createMeta(null, null)); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java index 8e8a5ffc0..d7d5b0fd2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/BasicProfileLocation.java @@ -10,12 +10,10 @@ final class BasicProfileLocation implements ProfileLocation { private final String fullLocation; private final String location; private final String label; - private final long hash; BasicProfileLocation(String fullLocation) { this.fullLocation = fullLocation; - this.hash = UtilLocation.hash(fullLocation); - this.location = shortDesc(fullLocation); + this.location = UtilLocation.loc(fullLocation); this.label = UtilLocation.label(location); } @@ -44,11 +42,6 @@ final class BasicProfileLocation implements ProfileLocation { return location; } - @Override - public long hash() { - return hash; - } - @Override public String fullLocation() { return fullLocation; @@ -64,15 +57,4 @@ final class BasicProfileLocation implements ProfileLocation { // do nothing } - private String shortDesc(String location) { - int lastPer = location.lastIndexOf('.'); - if (lastPer > -1) { - lastPer = location.lastIndexOf('.', lastPer - 1); - if (lastPer > -1) { - return location.substring(lastPer + 1); - } - } - return location; - } - } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java index 9c6340ad4..63fa0e11a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java @@ -13,7 +13,6 @@ class DProfileLocation implements ProfileLocation { private String fullLocation; private String location; private String label; - private long hash; private final int lineNumber; private int traceCount; @@ -46,11 +45,10 @@ class DProfileLocation implements ProfileLocation { return false; } final String loc = create(); - final String shortDesc = shortDesc(loc); - label = UtilLocation.label(shortDesc); - location = shortDesc; - fullLocation = loc; - hash = UtilLocation.hash(loc); + final String location = UtilLocation.loc(loc); + this.label = UtilLocation.label(location); + this.location = location; + this.fullLocation = loc; initWith(label); return true; } @@ -69,11 +67,6 @@ class DProfileLocation implements ProfileLocation { return location; } - @Override - public long hash() { - return hash; - } - @Override public String fullLocation() { return fullLocation; @@ -116,20 +109,4 @@ class DProfileLocation implements ProfileLocation { return traceLine.substring(0, traceLine.length() - 1) + ":" + lineNumber + ")"; } } - - private String shortDesc(String location) { - int pos = location.lastIndexOf('('); - if (pos == -1) { - pos = location.length(); - } - - pos = location.lastIndexOf('.', pos); - if (pos > -1) { - pos = location.lastIndexOf('.', pos - 1); - if (pos > -1) { - return location.substring(pos + 1); - } - } - return location; - } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java index 0354bbd0a..67f724051 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.profile; import io.ebean.ProfileLocation; -import io.ebeaninternal.server.util.Checksum; +import io.ebeaninternal.server.util.Md5; class DQueryPlanMeta { @@ -10,7 +10,7 @@ class DQueryPlanMeta { private final ProfileLocation profileLocation; private final String name; private final String sql; - private final long sqlHash; + private final String hash; DQueryPlanMeta(Class type, String label, ProfileLocation profileLocation, String sql) { this.type = type; @@ -22,15 +22,16 @@ class DQueryPlanMeta { name += "_" + label; } this.name = name; - this.sqlHash = Checksum.checksum(sql); + String loc = profileLocation == null ? null : profileLocation.location(); + this.hash = Md5.hash(sql, name, loc); } public Class getType() { return type; } - public long getSqlHash() { - return sqlHash; + public String getHash() { + return hash; } public String getName() { @@ -49,10 +50,6 @@ class DQueryPlanMeta { return (profileLocation == null) ? null : profileLocation.location(); } - public long getLocationHash() { - return (profileLocation == null) ? 0 : profileLocation.hash(); - } - public String getSql() { return sql; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index 41a0658f4..e24da73c7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -59,8 +59,8 @@ class DQueryPlanMetric implements QueryPlanMetric { } @Override - public long sqlHash() { - return meta.getSqlHash(); + public String hash() { + return meta.getHash(); } @Override @@ -83,11 +83,6 @@ class DQueryPlanMetric implements QueryPlanMetric { return meta.getLocation(); } - @Override - public long locationHash() { - return meta.getLocationHash(); - } - @Override public long count() { return stats.count(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java index 82731dfd3..a4243e31a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java @@ -14,7 +14,6 @@ class DTimeMetricStats implements TimedMetricStats { private String name; private String location; - private long locationHash; DTimeMetricStats(String name, boolean collected, long count, long total, long max) { this.name = name; @@ -37,7 +36,6 @@ class DTimeMetricStats implements TimedMetricStats { .append(" max:").append(max); if (location != null) { sb.append(" loc:").append(location); - sb.append(" locHash:").append(locationHash); } return sb.toString(); } @@ -47,11 +45,6 @@ class DTimeMetricStats implements TimedMetricStats { this.location = location; } - @Override - public void setLocationHash(long locationHash) { - this.locationHash = locationHash; - } - @Override public boolean initialCollection() { return !collected; @@ -72,11 +65,6 @@ class DTimeMetricStats implements TimedMetricStats { return location; } - @Override - public long locationHash() { - return locationHash; - } - /** * Return the count of values collected. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java b/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java index 00b388bbd..65ab1ce4d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/profile/UtilLocation.java @@ -1,28 +1,29 @@ package io.ebeaninternal.server.profile; -import io.ebeaninternal.server.util.Checksum; - final class UtilLocation { - /** - * Return a hash of the full description excluding the source line number. - */ - static long hash(String full) { + static String loc(String full) { final int pos = full.lastIndexOf('('); if (pos > -1) { - return Checksum.checksum(full.substring(0, pos)); + return full.substring(0, pos); } else { - return Checksum.checksum(full); + return full; } } - static String label(String shortDescription) { - int pos = shortDescription.indexOf("("); - if (pos == -1) { - return shortDescription; - } else { - return trimInit(shortDescription.substring(0, pos)); + static String label(String location) { + return trimInit(shortDesc(location)); + } + + private static String shortDesc(String location) { + int pos = location.lastIndexOf('.'); + if (pos > -1) { + pos = location.lastIndexOf('.', pos - 1); + if (pos > -1) { + return location.substring(pos + 1); + } } + return location; } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index 770ac522d..de365f253 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -13,7 +13,7 @@ import io.ebeaninternal.api.SpiQueryBindCapture; import io.ebeaninternal.api.SpiQueryPlan; import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.core.timezone.DataTimeZone; -import io.ebeaninternal.server.util.Checksum; +import io.ebeaninternal.server.util.Md5; import io.ebeaninternal.server.util.Str; import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot; import io.ebeaninternal.server.type.DataBind; @@ -56,13 +56,12 @@ public class CQueryPlan implements SpiQueryPlan { private final SpiEbeanServer server; private final ProfileLocation profileLocation; private final String location; - private final long locationHash; private final String label; private final String name; private final CQueryPlanKey planKey; private final boolean rawSql; private final String sql; - private final long sqlHash; + private final String hash; private final String logWhereSql; private final SqlTree sqlTree; @@ -93,7 +92,6 @@ public class CQueryPlan implements SpiQueryPlan { SpiQuery query = request.getQuery(); this.profileLocation = query.getProfileLocation(); this.location = (profileLocation == null) ? null : profileLocation.location(); - this.locationHash = (profileLocation == null) ? 0 : profileLocation.hash(); this.label = query.getPlanLabel(); this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName()); this.asOfTableCount = query.getAsOfTableCount(); @@ -105,7 +103,7 @@ public class CQueryPlan implements SpiQueryPlan { this.stats = new CQueryPlanStats(this); this.dependentTables = sqlTree.dependentTables(); this.bindCapture = initBindCapture(query); - this.sqlHash = Checksum.checksum(sql); + this.hash = Md5.hash(sql, name, location); } /** @@ -118,7 +116,6 @@ public class CQueryPlan implements SpiQueryPlan { SpiQuery query = request.getQuery(); this.profileLocation = query.getProfileLocation(); this.location = (profileLocation == null) ? null : profileLocation.location(); - this.locationHash = (profileLocation == null) ? 0 : profileLocation.hash(); this.label = query.getPlanLabel(); this.name = deriveName(label, query.getType(), request.getBeanDescriptor().getSimpleName()); this.planKey = buildPlanKey(sql, logWhereSql); @@ -131,7 +128,7 @@ public class CQueryPlan implements SpiQueryPlan { this.stats = new CQueryPlanStats(this); this.dependentTables = sqlTree.dependentTables(); this.bindCapture = initBindCaptureRaw(sql, query); - this.sqlHash = Checksum.checksum(sql); + this.hash = Md5.hash(sql, name, location); } private String deriveName(String label, SpiQuery.Type type, String simpleName) { @@ -177,8 +174,8 @@ public class CQueryPlan implements SpiQueryPlan { } @Override - public long getSqlHash() { - return sqlHash; + public String getHash() { + return hash; } @Override @@ -203,10 +200,6 @@ public class CQueryPlan implements SpiQueryPlan { return location; } - public long getLocationHash() { - return locationHash; - } - @Override public void queryPlanInit(long thresholdMicros) { bindCapture.queryPlanInit(thresholdMicros); @@ -214,7 +207,7 @@ public class CQueryPlan implements SpiQueryPlan { @Override public DQueryPlanOutput createMeta(String bind, String planString) { - return new DQueryPlanOutput(getBeanType(), name, sqlHash, sql, profileLocation, bind, planString); + return new DQueryPlanOutput(getBeanType(), name, hash, sql, profileLocation, bind, planString); } public DataReader createDataReader(ResultSet rset) { @@ -261,7 +254,7 @@ public class CQueryPlan implements SpiQueryPlan { private String calcAuditQueryKey() { // rawSql needs to include the MD5 hash of the sql - return rawSql ? planKey.getPartialKey() + "_" + sqlHash : planKey.getPartialKey(); + return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey(); } SqlTree getSqlTree() { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java index ab7dc0b63..ffc749eb9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java @@ -106,11 +106,6 @@ public final class CQueryPlanStats { return queryPlan.getLocation(); } - @Override - public long locationHash() { - return queryPlan.getLocationHash(); - } - @Override public long count() { return metrics.count(); @@ -132,8 +127,8 @@ public final class CQueryPlanStats { } @Override - public long sqlHash() { - return queryPlan.getSqlHash(); + public String hash() { + return queryPlan.getHash(); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java index 3d879362b..1c1a41d07 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DQueryPlanOutput.java @@ -16,14 +16,14 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { private final String sql; private final String bind; private final String plan; - private final long sqlHash; + private final String hash; private long queryTimeMicros; private long captureCount; - DQueryPlanOutput(Class beanType, String label, long sqlHash, String sql, ProfileLocation profileLocation, String bind, String plan) { + DQueryPlanOutput(Class beanType, String label, String hash, String sql, ProfileLocation profileLocation, String bind, String plan) { this.beanType = beanType; this.label = label; - this.sqlHash = sqlHash; + this.hash = hash; this.sql = sql; this.profileLocation = profileLocation; this.bind = bind; @@ -31,8 +31,8 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { } @Override - public long sqlHash() { - return sqlHash; + public String hash() { + return hash; } /** @@ -99,7 +99,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan { @Override public String toString() { - return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + sqlHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan; + return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + hash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan; } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java b/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java new file mode 100644 index 000000000..45df901f0 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/util/Md5.java @@ -0,0 +1,36 @@ +package io.ebeaninternal.server.util; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +public final class Md5 { + + /** + * Return the MD5 hash of the underlying sql. + */ + public static String hash(String... values) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + for (String val : values) { + if (val != null) { + md.update(val.getBytes(StandardCharsets.UTF_8)); + } + } + return digestToHex(md.digest()); + } catch (Exception e) { + throw new RuntimeException("MD5 hashing failed", e); + } + } + + /** + * Convert the digest into a hex value. + */ + private static String digestToHex(byte[] digest) { + StringBuilder sb = new StringBuilder(32); + for (byte aDigest : digest) { + sb.append(Integer.toString((aDigest & 0xff) + 0x100, 16).substring(1)); + } + return sb.toString(); + } + +} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java index 6dfdad9f6..84a53442a 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java @@ -14,7 +14,7 @@ public class BasicProfileLocationTest { assertThat(loc.obtain()).isTrue(); assertThat(loc.fullLocation()).endsWith(":12)"); - assertThat(loc.location()).isEqualTo("NativeMethodAccessorImpl.invoke0(Native Method:12)"); + assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0"); assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0"); } @@ -24,7 +24,7 @@ public class BasicProfileLocationTest { BasicProfileLocation loc = new BasicProfileLocation("com.foo.Bar.all"); assertThat(loc.obtain()).isFalse(); assertThat(loc.fullLocation()).isEqualTo("com.foo.Bar.all"); - assertThat(loc.location()).isEqualTo("Bar.all"); + assertThat(loc.location()).isEqualTo("com.foo.Bar.all"); assertThat(loc.label()).isEqualTo("Bar.all"); } @@ -34,7 +34,7 @@ public class BasicProfileLocationTest { BasicProfileLocation loc = new BasicProfileLocation("foo.Bar.all"); assertThat(loc.obtain()).isFalse(); assertThat(loc.fullLocation()).isEqualTo("foo.Bar.all"); - assertThat(loc.location()).isEqualTo("Bar.all"); + assertThat(loc.location()).isEqualTo("foo.Bar.all"); assertThat(loc.label()).isEqualTo("Bar.all"); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java index 5fcbe90a4..0b26078ff 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/UtilLocationTest.java @@ -9,13 +9,12 @@ public class UtilLocationTest { @Test public void label() { assertThat(UtilLocation.label("foo")).isEqualTo("foo"); - assertThat(UtilLocation.label("ProfileLocationTest$Other.(ProfileLocationTest.java:47)")).isEqualTo("ProfileLocationTest$Other.init"); + assertThat(UtilLocation.label("ProfileLocationTest$Other.")).isEqualTo("ProfileLocationTest$Other.init"); } @Test - public void hash() { - assertThat(UtilLocation.hash("org.foo.MyFoo.doIt(MyFoo.java:12)")).isEqualTo(396279222L); - assertThat(UtilLocation.hash("org.foo.MyFoo.doIt(MyFoo.java:13)")).isEqualTo(396279222L); - assertThat(UtilLocation.hash("org.foo.MyFoo.doIt(MyFoo.java:945)")).isEqualTo(396279222L); + public void loc() { + assertThat(UtilLocation.loc("org.foo.MyFoo.doIt(MyFoo.java:12)")).isEqualTo("org.foo.MyFoo.doIt"); + assertThat(UtilLocation.label("org.foo.MyFoo.doIt")).isEqualTo("MyFoo.doIt"); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java b/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java new file mode 100644 index 000000000..9f0106cae --- /dev/null +++ b/ebean-core/src/test/java/io/ebeaninternal/server/util/Md5Test.java @@ -0,0 +1,53 @@ +package io.ebeaninternal.server.util; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class Md5Test { + + @Test + public void hash() throws Exception { + String content = "some random content we wish to hash"; + String hash1 = Md5.hash(content); + String hash2 = Md5.hash(content); + assertEquals(hash1, hash2); + assertEquals(hash1, "62c20bf679ff56cb746452ab5c88e3ed"); + } + + @Test + public void hashDifferent() throws Exception { + String hash1 = Md5.hash("one"); + String hash2 = Md5.hash("two"); + String hash3 = Md5.hash("onetwo"); + + assertNotEquals(hash1, hash2); + assertNotEquals(hash2, hash3); + assertEquals(hash1, "f97c5d29941bfb1b2fdab0874906ab82"); + } + + @Test + public void hashMulti() { + String hash1 = Md5.hash("one", "two"); + String hash2 = Md5.hash("onetwo"); + + assertEquals(hash1, hash2); + assertEquals(hash1, "5b9164ad6f496d9dee12ec7634ce253f"); + } + + @Test + public void hashMulti_when_null() { + String hash1 = Md5.hash("one", null); + String hash2 = Md5.hash("one"); + + assertEquals(hash1, hash2); + assertEquals(hash1, "f97c5d29941bfb1b2fdab0874906ab82"); + } + + @Test + public void when_null() { + String hash1 = Md5.hash(null, null); + assertEquals(hash1, "d41d8cd98f00b204e9800998ecf8427e"); + } +} diff --git a/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java b/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java index d452b7463..c7b0edfe9 100644 --- a/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java +++ b/ebean-core/src/test/java/org/tests/profile/ProfileLocationTest.java @@ -20,15 +20,13 @@ public class ProfileLocationTest { public void test_obtain() { assertThat(doIt()).isTrue(); assertThat(loc.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:16)"); - assertThat(loc.location()).isEqualTo("ProfileLocationTest.doIt(ProfileLocationTest.java:16)"); + assertThat(loc.location()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt"); assertThat(loc.label()).isEqualTo("ProfileLocationTest.doIt"); - assertThat(loc.hash()).isEqualTo(1867926812L); // same hash even when the line number has changed assertThat(locB.fullLocation()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt(ProfileLocationTest.java:15)"); - assertThat(locB.location()).isEqualTo("ProfileLocationTest.doIt(ProfileLocationTest.java:15)"); + assertThat(locB.location()).isEqualTo("org.tests.profile.ProfileLocationTest.doIt"); assertThat(locB.label()).isEqualTo("ProfileLocationTest.doIt"); - assertThat(locB.hash()).isEqualTo(1867926812L); } @Test @@ -43,7 +41,7 @@ public class ProfileLocationTest { other.hashCode(); assertThat(loc2.label()).isEqualTo("ProfileLocationTest$Other.init"); - assertThat(loc2.location()).isEqualTo("ProfileLocationTest$Other.(ProfileLocationTest.java:52)"); + assertThat(loc2.location()).isEqualTo("org.tests.profile.ProfileLocationTest$Other."); } static class Other { diff --git a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java index 6dfd99422..d36e36b51 100644 --- a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -242,10 +242,9 @@ public class TestCustomerFinder extends BaseTestCase { assertThat(metricsJson).contains("\"name\":\"txn.main\""); assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\""); - assertThat(metricsJson).contains("\"locHash\":3254522637"); - assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:43)\""); + assertThat(metricsJson).contains("\"loc\":\"org.tests.model.basic.finder.CustomerFinder.byNameStatus\""); if (isH2() || isPostgres()) { - assertThat(metricsJson).contains("\"sqlHash\":3634991469"); + assertThat(metricsJson).contains("\"hash\":\"de3affa5b4bff07e19c1c012590dcde6\""); assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,"); } } From 43213ead058f8a648f6ce1a7d2edf54c28a54d47 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 9 Aug 2021 15:10:59 +1200 Subject: [PATCH 68/87] #2291 - ENH: Explicit option for fast-failing or not fast-failing (skipDataSourceCheck config option) --- .../java/io/ebean/config/DatabaseConfig.java | 17 +++++++++++++++++ .../server/core/DefaultContainer.java | 3 +++ .../io/ebean/config/DatabaseConfigTest.java | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java index d1f0346b5..873304c3a 100644 --- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java +++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java @@ -319,6 +319,8 @@ public class DatabaseConfig { */ private ExternalTransactionManager externalTransactionManager; + private boolean skipDataSourceCheck; + /** * The data source (if programmatically provided). */ @@ -1651,6 +1653,20 @@ public class DatabaseConfig { this.autoTuneConfig = autoTuneConfig; } + /** + * Return true if the startup DataSource check should be skipped. + */ + public boolean skipDataSourceCheck() { + return skipDataSourceCheck; + } + + /** + * Set to true to skip the startup DataSource check. + */ + public void setSkipDataSourceCheck(boolean skipDataSourceCheck) { + this.skipDataSourceCheck = skipDataSourceCheck; + } + /** * Return the DataSource. */ @@ -2932,6 +2948,7 @@ public class DatabaseConfig { jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate); jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection); + skipDataSourceCheck = p.getBoolean("skipDataSourceCheck", skipDataSourceCheck); runMigration = p.getBoolean("migration.run", runMigration); ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate); ddlRun = p.getBoolean("ddl.run", ddlRun); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java index cdefcc294..2c8bbfecc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java @@ -255,6 +255,9 @@ public class DefaultContainer implements SpiContainer { } throw new RuntimeException("DataSource not set?"); } + if (config.skipDataSourceCheck()) { + return true; + } try (Connection connection = config.getDataSource().getConnection()) { if (connection.getAutoCommit()) { logger.warn("DataSource [{}] has autoCommit defaulting to true!", config.getName()); diff --git a/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java b/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java index dd132a8e3..368bc9a8f 100644 --- a/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java +++ b/ebean-core/src/test/java/io/ebean/config/DatabaseConfigTest.java @@ -76,6 +76,7 @@ public class DatabaseConfigTest { props.setProperty("loadModuleInfo", "true"); props.setProperty("forUpdateNoKey", "true"); props.setProperty("defaultServer", "false"); + props.setProperty("skipDataSourceCheck", "true"); props.setProperty("queryPlan.enable", "true"); props.setProperty("queryPlan.thresholdMicros", "10000"); @@ -92,6 +93,7 @@ public class DatabaseConfigTest { assertTrue(config.isDbOffline()); assertTrue(config.isAutoReadOnlyDataSource()); assertTrue(config.isAutoLoadModuleInfo()); + assertTrue(config.skipDataSourceCheck()); assertTrue(config.isIdGeneratorAutomatic()); assertFalse(config.getPlatformConfig().isCaseSensitiveCollation()); @@ -155,6 +157,7 @@ public class DatabaseConfigTest { assertTrue(config.isIdGeneratorAutomatic()); assertTrue(config.isDefaultServer()); assertFalse(config.isAutoPersistUpdates()); + assertFalse(config.skipDataSourceCheck()); config.setIdGeneratorAutomatic(false); assertFalse(config.isIdGeneratorAutomatic()); @@ -175,6 +178,8 @@ public class DatabaseConfigTest { assertFalse(config.isAutoLoadModuleInfo()); config.setAutoPersistUpdates(true); assertTrue(config.isAutoPersistUpdates()); + config.setSkipDataSourceCheck(true); + assertTrue(config.skipDataSourceCheck()); } @Test From 8c101fd0a460df5ed5252601483b8f89ac4f783e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 9 Aug 2021 16:48:59 +1200 Subject: [PATCH 69/87] #2268 - Feature request/question: Query execution time for debugging --- .../server/core/AbstractSqlQueryRequest.java | 14 +--- .../server/core/DtoQueryRequest.java | 3 - .../server/core/RelationalQueryRequest.java | 3 - .../io/ebeaninternal/server/query/CQuery.java | 6 +- .../server/query/CQueryEngine.java | 66 ++----------------- .../query/CQueryFetchSingleAttribute.java | 38 ++--------- .../server/query/CQueryRowCount.java | 38 ++--------- .../server/query/CQueryUpdate.java | 32 ++------- 8 files changed, 29 insertions(+), 171 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java index 67844c631..a88f1bbe9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java @@ -23,25 +23,16 @@ import javax.persistence.PersistenceException; public abstract class AbstractSqlQueryRequest implements CancelableQuery { protected final SpiSqlBinding query; - protected final SpiEbeanServer server; - protected SpiTransaction transaction; - private boolean createdTransaction; - protected String sql; - protected ResultSet resultSet; - protected String bindLog = ""; - protected PreparedStatement pstmt; - protected long startNano; - private final ReentrantLock lock = new ReentrantLock(); - + /** * Create the BeanFindRequest. */ @@ -161,7 +152,8 @@ public abstract class AbstractSqlQueryRequest implements CancelableQuery { this.bindLog = binder.bind(bindParams, pstmt, conn); } if (isLogSql()) { - transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")")); + long micros = (System.nanoTime() - startNano) / 1000L; + transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ") --micros(", micros + ")")); } } finally { lock.unlock(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java index 2b1a55f6e..c023f2d0d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java @@ -28,11 +28,8 @@ public final class DtoQueryRequest extends AbstractSqlQueryRequest { private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase(); private final SpiDtoQuery query; - private final DtoQueryEngine queryEngine; - private DtoQueryPlan plan; - private DataReader dataReader; DtoQueryRequest(SpiEbeanServer server, DtoQueryEngine engine, SpiDtoQuery query) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java index a5130ef22..ef3874008 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java @@ -18,11 +18,8 @@ import java.util.function.Predicate; public final class RelationalQueryRequest extends AbstractSqlQueryRequest { private final RelationalQueryEngine queryEngine; - private String[] propertyNames; - private int estimateCapacity; - private int rows; /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java index e7674a31c..7fc636af0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java @@ -541,9 +541,13 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran updateStatistics(); } + long micros() { + return (System.nanoTime() - startNano) / 1000L; + } + private void updateStatistics() { try { - executionTimeMicros = (System.nanoTime() - startNano) / 1000L; + executionTimeMicros = micros(); if (autoTuneProfiling) { profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java index a1a211c0a..03074b43f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java @@ -79,15 +79,10 @@ public class CQueryEngine { private int executeUpdate(OrmQueryRequest request, CQueryUpdate query) { try { int rows = query.execute(); - if (request.isLogSql()) { - String logSql = query.getGeneratedSql(); - logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows)); - request.logSql(logSql); + request.logSql(Str.add(query.getGeneratedSql(), "; --bind(", query.getBindLog(), ") --micros(", query.micros() + ") --rows(", rows + ")")); } - return rows; - } catch (SQLException e) { throw translate(request, query.getBindLog(), query.getGeneratedSql(), e); } @@ -97,7 +92,6 @@ public class CQueryEngine { * Build and execute the findSingleAttributeList query. */ public List findSingleAttributeList(OrmQueryRequest request) { - CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request); request.setCancelableQuery(rcQuery); return findAttributeList(request, rcQuery); @@ -108,14 +102,13 @@ public class CQueryEngine { try { List list = (List) rcQuery.findList(); if (request.isLogSql()) { - logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog()); + logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog(), rcQuery.micros()); } if (request.isLogSummary()) { request.getTransaction().logSummary(rcQuery.getSummary()); } if (request.isQueryCachePut()) { request.addDependentTables(rcQuery.getDependentTables()); - list = Collections.unmodifiableList(list); request.putToQueryCache(list); if (Boolean.FALSE.equals(request.getQuery().isReadOnly())) { @@ -123,7 +116,6 @@ public class CQueryEngine { } } return list; - } catch (SQLException e) { throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e); } @@ -139,10 +131,8 @@ public class CQueryEngine { String msg = "ERROR executing query, bindLog[" + bindLog + "] error[" + StringHelper.removeNewLines(e.getMessage()) + "]"; t.logSummary(msg); } - // ensure 'rollback' is logged if queryOnly transaction t.getConnection(); - // build a decent error message for the exception String m = "Query threw SQLException:" + e.getMessage() + " Bind values:[" + bindLog + "] Query was:" + sql; return dbPlatform.translate(m, e); @@ -152,46 +142,37 @@ public class CQueryEngine { * Build and execute the find Id's query. */ public List findIds(OrmQueryRequest request) { - CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchIdsQuery(request); request.setCancelableQuery(rcQuery); return findAttributeList(request, rcQuery); } - private void logGeneratedSql(OrmQueryRequest request, String sql, String bindLog) { - request.logSql(Str.add(sql, "; --bind(", bindLog, ")")); + private void logGeneratedSql(OrmQueryRequest request, String sql, String bindLog, long micros) { + request.logSql(Str.add(sql, "; --bind(", bindLog, ") --micros(", micros + ")")); } /** * Build and execute the row count query. */ public int findCount(OrmQueryRequest request) { - CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request); request.setCancelableQuery(rcQuery); try { - int count = rcQuery.findCount(); - if (request.isLogSql()) { - logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog()); + logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog(), rcQuery.micros()); } - if (request.isLogSummary()) { request.getTransaction().logSummary(rcQuery.getSummary()); } - if (request.getQuery().isFutureFetch()) { request.getTransaction().end(); } - if (request.isQueryCachePut()) { request.addDependentTables(rcQuery.getDependentTables()); request.putToQueryCache(count); } - return count; - } catch (SQLException e) { throw translate(request, rcQuery.getBindLog(), rcQuery.getGeneratedSql(), e); } @@ -254,9 +235,7 @@ public class CQueryEngine { * Execute the find versions query returning version beans. */ public List> findVersions(OrmQueryRequest request) { - SpiQuery query = request.getQuery(); - String sysPeriodLower = getSysPeriodLower(query); if (query.isVersionsBetween() && !historySupport.isStandardsBased()) { query.where().lt(sysPeriodLower, query.getVersionEnd()); @@ -272,26 +251,21 @@ public class CQueryEngine { if (request.isLogSql()) { logSql(cquery); } - List> versions = cquery.readVersions(); // just order in memory rather than use NULLS LAST as that // is not universally supported, not expect huge list here versions.sort(OrderVersionDesc.INSTANCE); deriveVersionDiffs(versions, request); - if (request.isLogSummary()) { logFindManySummary(cquery); } - if (request.isAuditReads()) { cquery.auditFindMany(); } - return versions; } catch (SQLException e) { throw cquery.createPersistenceException(e); - } finally { if (cquery != null) { cquery.close(); @@ -300,9 +274,7 @@ public class CQueryEngine { } private void deriveVersionDiffs(List> versions, OrmQueryRequest request) { - BeanDescriptor descriptor = request.getBeanDescriptor(); - if (!versions.isEmpty()) { Version current = versions.get(0); if (versions.size() > 1) { @@ -367,10 +339,8 @@ public class CQueryEngine { * Find a list/map/set of beans. */ BeanCollection findMany(OrmQueryRequest request) { - CQuery cquery = queryBuilder.buildQuery(request); request.setCancelableQuery(cquery); - try { if (defaultFetchSizeFindList > 0) { request.setDefaultFetchBuffer(defaultFetchSizeFindList); @@ -380,30 +350,24 @@ public class CQueryEngine { logger.trace("Future fetch already cancelled"); return null; } - if (request.isLogSql()) { logSql(cquery); } - BeanCollection beanCollection = cquery.readCollection(); if (request.isLogSummary()) { logFindManySummary(cquery); } - if (request.isAuditReads()) { cquery.auditFindMany(); } - request.executeSecondaryQueries(false); if (request.isQueryCachePut()) { request.addDependentTables(cquery.getDependentTables()); } - return beanCollection; } catch (SQLException e) { throw cquery.createPersistenceException(e); - } finally { if (cquery != null) { cquery.close(); @@ -416,38 +380,27 @@ public class CQueryEngine { */ @SuppressWarnings("unchecked") public T find(OrmQueryRequest request) { - EntityBean bean = null; - CQuery cquery = queryBuilder.buildQuery(request); request.setCancelableQuery(cquery); - try { cquery.prepareBindExecuteQuery(); - if (request.isLogSql()) { logSql(cquery); } - if (cquery.readBean()) { bean = cquery.next(); } - if (request.isLogSummary()) { logFindBeanSummary(cquery); } - if (request.isAuditReads()) { cquery.auditFind(bean); } - request.executeSecondaryQueries(false); - return (T) bean; - } catch (SQLException e) { throw cquery.createPersistenceException(e); - } finally { cquery.close(); } @@ -457,17 +410,13 @@ public class CQueryEngine { * Log the generated SQL to the transaction log. */ private void logSql(CQuery query) { - - String sql = query.getGeneratedSql(); - sql = Str.add(sql, "; --bind(", query.getBindLog(), ")"); - query.getTransaction().logSql(sql); + query.getTransaction().logSql(Str.add(query.getGeneratedSql(), "; --bind(", query.getBindLog(), ") --micros(", query.micros() + ")")); } /** * Log the FindById summary to the transaction log. */ private void logFindBeanSummary(CQuery q) { - SpiQuery query = q.getQueryRequest().getQuery(); String loadMode = query.getLoadMode(); String loadDesc = query.getLoadDescription(); @@ -504,7 +453,6 @@ public class CQueryEngine { msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros()); msg.append("] rows[").append(q.getLoadedRowDetail()); msg.append("] bind[").append(q.getBindLog()).append("]"); - q.getTransaction().logSummary(msg.toString()); } @@ -512,7 +460,6 @@ public class CQueryEngine { * Log the FindMany to the transaction log. */ private void logFindManySummary(CQuery q) { - SpiQuery query = q.getQueryRequest().getQuery(); String loadMode = query.getLoadMode(); String loadDesc = query.getLoadDescription(); @@ -551,7 +498,6 @@ public class CQueryEngine { msg.append("] rows[").append(q.getLoadedRowDetail()); msg.append("] predicates[").append(q.getLogWhereSql()); msg.append("] bind[").append(q.getBindLog()).append("]"); - q.getTransaction().logSummary(msg.toString()); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java index b68813bd4..a89cd28fa 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryFetchSingleAttribute.java @@ -29,45 +29,19 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab private static final Logger logger = LoggerFactory.getLogger(CQueryFetchSingleAttribute.class); private final CQueryPlan queryPlan; - - /** - * The overall find request wrapper object. - */ private final OrmQueryRequest request; - private final BeanDescriptor desc; - private final SpiQuery query; - - /** - * Where clause predicates. - */ private final CQueryPredicates predicates; - - /** - * The final sql that is generated. - */ private final String sql; - private RsetDataReader dataReader; - - /** - * The statement used to create the resultSet. - */ private PreparedStatement pstmt; - private String bindLog; - private long executionTimeMicros; - private int rowCount; - private final ScalarDataReader reader; - private final boolean containsCounts; - private long profileOffset; - private final ReentrantLock lock = new ReentrantLock(); /** @@ -95,19 +69,20 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab .append("] type[").append(desc.getName()) .append("] predicates[").append(predicates.getLogWhereSql()) .append("] bind[").append(bindLog).append("]"); - return sb.toString(); } + long micros() { + return executionTimeMicros; + } + /** * Execute the query returning the row count. */ List findList() throws SQLException { - long startNano = System.nanoTime(); try { prepareExecute(); - List result = new ArrayList<>(); while (dataReader.next()) { Object value = reader.read(dataReader); @@ -117,16 +92,13 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab result.add(value); rowCount++; } - executionTimeMicros = (System.nanoTime() - startNano) / 1000L; request.slowQueryCheck(executionTimeMicros, rowCount); if (queryPlan.executionTime(executionTimeMicros)) { queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros); } getTransaction().profileEvent(this); - return result; - } finally { close(); } @@ -158,14 +130,12 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Cancelab profileOffset = t.profileOffset(); Connection conn = t.getInternalConnection(); pstmt = conn.prepareStatement(sql); - if (query.getBufferFetchSizeHint() > 0) { pstmt.setFetchSize(query.getBufferFetchSizeHint()); } if (query.getTimeout() > 0) { pstmt.setQueryTimeout(query.getTimeout()); } - bindLog = predicates.bind(pstmt, conn); } finally { lock.unlock(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java index 248fa9058..0c1df9483 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryRowCount.java @@ -22,44 +22,17 @@ import java.util.concurrent.locks.ReentrantLock; class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery { private final CQueryPlan queryPlan; - - /** - * The overall find request wrapper object. - */ private final OrmQueryRequest request; - private final BeanDescriptor desc; - private final SpiQuery query; - - /** - * Where clause predicates. - */ private final CQueryPredicates predicates; - - /** - * The final sql that is generated. - */ private final String sql; - - /** - * The resultSet that is read and converted to objects. - */ private ResultSet rset; - - /** - * The statement used to create the resultSet. - */ private PreparedStatement pstmt; - private String bindLog; - private long executionTimeMicros; - private int rowCount; - private long profileOffset; - private final ReentrantLock lock = new ReentrantLock(); /** @@ -104,11 +77,14 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery { return sql; } + long micros() { + return executionTimeMicros; + } + /** * Execute the query returning the row count. */ public int findCount() throws SQLException { - long startNano = System.nanoTime(); try { SpiTransaction t = getTransaction(); @@ -118,24 +94,19 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery { try { query.checkCancelled(); pstmt = conn.prepareStatement(sql); - if (query.getTimeout() > 0) { pstmt.setQueryTimeout(query.getTimeout()); } - bindLog = predicates.bind(pstmt, conn); } finally { lock.unlock(); } rset = pstmt.executeQuery(); query.checkCancelled(); - if (!rset.next()) { throw new PersistenceException("Expecting 1 row but got none?"); } - rowCount = rset.getInt(1); - executionTimeMicros = (System.nanoTime() - startNano) / 1000L; request.slowQueryCheck(executionTimeMicros, rowCount); if (queryPlan.executionTime(executionTimeMicros)) { @@ -143,7 +114,6 @@ class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuery { } t.profileEvent(this); return rowCount; - } finally { close(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java index eec5d2e6f..302b1bcf0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java @@ -19,36 +19,18 @@ import java.util.concurrent.locks.ReentrantLock; class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery { private final CQueryPlan queryPlan; - private final OrmQueryRequest request; - private final BeanDescriptor desc; - private final SpiQuery query; - - /** - * Where clause predicates. - */ private final CQueryPredicates predicates; - - /** - * The final sql that is generated. - */ private final String sql; - - /** - * The statement used to create the resultSet. - */ private PreparedStatement pstmt; - private String bindLog; - private int rowCount; - private long profileOffset; - + private long executionTimeMicros; private final ReentrantLock lock = new ReentrantLock(); - + /** * Create the Sql select based on the request. */ @@ -80,7 +62,6 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery { * Execute the update or delete statement returning the row count. */ public int execute() throws SQLException { - long startNano = System.nanoTime(); try { SpiTransaction t = getTransaction(); @@ -90,19 +71,16 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery { try { query.checkCancelled(); pstmt = conn.prepareStatement(sql); - if (query.getTimeout() > 0) { pstmt.setQueryTimeout(query.getTimeout()); } - bindLog = predicates.bind(pstmt, conn); } finally { lock.unlock(); } rowCount = pstmt.executeUpdate(); query.checkCancelled(); - - long executionTimeMicros = (System.nanoTime() - startNano) / 1000L; + executionTimeMicros = (System.nanoTime() - startNano) / 1000L; request.slowQueryCheck(executionTimeMicros, rowCount); if (queryPlan.executionTime(executionTimeMicros)) { queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros); @@ -115,6 +93,10 @@ class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery { } } + long micros() { + return executionTimeMicros; + } + private SpiTransaction getTransaction() { return request.getTransaction(); } From 7ed2de917828ea69a6ab5136cc86a80c383f2e5b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Mon, 9 Aug 2021 16:57:43 +1200 Subject: [PATCH 70/87] Update github issue template --- .github/ISSUE_TEMPLATE.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 8575f4580..357f8aefb 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,9 +1,3 @@ - -GITHUB ISSUES ARE STRICTLY CONTROLLED FOR THIS PROJECT. - -Refer to http://ebean-orm.github.io/support for the policies controlling the use of github issues. -Please post issues to the Ebean group https://groups.google.com/forum/#!forum/ebean first. - ## Expected behavior ## Actual behavior From 181dc73d39f5285fb7d02ffbc923d01ff794c4d9 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 9 Aug 2021 09:19:53 +0200 Subject: [PATCH 71/87] Use MD5 for QueryBindHash --- .../java/io/ebeaninternal/api/BindHash.java | 37 +++++ .../java/io/ebeaninternal/api/BindParams.java | 11 +- .../java/io/ebeaninternal/api/HashQuery.java | 8 +- .../io/ebeaninternal/api/SpiExpression.java | 2 +- .../java/io/ebeaninternal/api/SpiQuery.java | 2 +- .../expression/AbstractTextExpression.java | 7 +- .../expression/AllEqualsExpression.java | 8 +- .../expression/ArrayContainsExpression.java | 10 +- .../expression/ArrayIsEmptyExpression.java | 5 +- .../server/expression/BetweenExpression.java | 7 +- .../expression/BetweenPropertyExpression.java | 5 +- .../server/expression/BitwiseExpression.java | 5 +- .../CaseInsensitiveEqualExpression.java | 5 +- .../expression/DefaultExampleExpression.java | 8 +- .../expression/DefaultExpressionList.java | 8 +- .../expression/ExistsQueryExpression.java | 5 +- .../server/expression/IdExpression.java | 5 +- .../server/expression/IdInExpression.java | 8 +- .../server/expression/InExpression.java | 8 +- .../server/expression/InPairsExpression.java | 8 +- .../server/expression/InQueryExpression.java | 5 +- .../server/expression/InRangeExpression.java | 7 +- .../server/expression/IsEmptyExpression.java | 5 +- .../server/expression/JsonPathExpression.java | 7 +- .../server/expression/JunctionExpression.java | 6 +- .../server/expression/LikeExpression.java | 5 +- .../server/expression/LogicExpression.java | 7 +- .../expression/NativeILikeExpression.java | 5 +- .../NestedPathWrapperExpression.java | 5 +- .../server/expression/NoopExpression.java | 4 +- .../server/expression/NotExpression.java | 5 +- .../server/expression/NullExpression.java | 5 +- .../server/expression/RawExpression.java | 8 +- .../server/expression/SimpleExpression.java | 5 +- .../server/querydefn/DefaultOrmQuery.java | 26 ++-- .../server/querydefn/HashCodeBindHash.java | 56 +++++++ .../server/querydefn/MdBindHash.java | 138 ++++++++++++++++++ .../server/expression/RawExpressionTest.java | 12 +- .../server/querydefn/DefaultOrmQueryTest.java | 14 +- .../java/org/tests/cache/TestQueryCache.java | 86 +++++++---- 40 files changed, 436 insertions(+), 137 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java new file mode 100644 index 000000000..d70c5f09d --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java @@ -0,0 +1,37 @@ +package io.ebeaninternal.api; + +/** + * BindHash implementation. + * + * @author Roland Praml, FOCONIS AG + * + */ +public interface BindHash { + + /** + * Update with boolean value. + */ + BindHash update(boolean boolValue); + + /** + * Update with int value. + */ + BindHash update(int intValue); + + /** + * Update with long value. + */ + BindHash update(long longValue); + + /** + * Update with object value. + */ + BindHash update(Object value); + + /** + * finishes the hash. May be used to compute internal state. After finish, no + * update method must be called + */ + void finish(); + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index afb1e44b8..2817c4ba1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -54,12 +54,11 @@ public class BindParams implements Serializable { positionedParameters.clear(); } - public int queryBindHash() { - int hc = namedParameters.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(positionedParameters.size()); for (Param positionedParameter : positionedParameters) { - hc = hc * 92821 + positionedParameter.hashCode(); + positionedParameter.queryBindHash(hash); } - return hc; } /** @@ -422,6 +421,10 @@ public class BindParams implements Serializable { return hc; } + void queryBindHash(BindHash hash) { + hash.update(isInParam).update(isOutParam).update(type).update(inValue); + } + @Override public boolean equals(Object o) { return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java index c14cb88d7..87241aedc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java @@ -7,12 +7,12 @@ public class HashQuery { private final CQueryPlanKey planHash; - private final int bindHash; + private final BindHash bindHash; /** * Create the HashQuery. */ - public HashQuery(CQueryPlanKey planHash, int bindHash) { + public HashQuery(CQueryPlanKey planHash, BindHash bindHash) { this.planHash = planHash; this.bindHash = bindHash; } @@ -25,7 +25,7 @@ public class HashQuery { @Override public int hashCode() { int hc = 92821 * planHash.hashCode(); - hc = 92821 * hc + bindHash; + hc = 92821 * hc + bindHash.hashCode(); return hc; } @@ -39,6 +39,6 @@ public class HashQuery { } HashQuery e = (HashQuery) obj; - return e.bindHash == bindHash && e.planHash.equals(planHash); + return e.bindHash.equals(bindHash) && e.planHash.equals(planHash); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java index 007e9f29f..32c3a3518 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java @@ -56,7 +56,7 @@ public interface SpiExpression extends Expression { /** * Return the hash value for the values that will be bound. */ - int queryBindHash(); + void queryBindHash(BindHash hash); /** * Return true if the expression is the same with respect to bind values. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index 12942e7d6..d8bae7503 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -635,7 +635,7 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod * query). *

*/ - int queryBindHash(); + void queryBindHash(BindHash hash); /** * Identifies queries that are exactly the same including bind variables. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java index d30649149..d377e48dd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -37,9 +38,9 @@ public abstract class AbstractTextExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return 0; - } + public void queryBindHash(BindHash hash) { + // do nothing, only execute against document store + }; @Override public boolean isSameByBind(SpiExpression other) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java index 5a931ec5a..df9e6095b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -122,14 +123,13 @@ class AllEqualsExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { + public void queryBindHash(BindHash hash) { - int hc = 92821; + hash.update(propMap.size()); for (Object value : propMap.values()) { - hc = hc * 92821 + (value == null ? 0 : value.hashCode()); + hash.update(value); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index 8c12bec60..ac6d27d0e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -49,12 +50,11 @@ public class ArrayContainsExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = values[0].hashCode(); - for (int i = 1; i < values.length; i++) { - hc = hc * 92821 + values[i].hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(values.length); + for (int i = 0; i < values.length; i++) { + hash.update(values[i]); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java index 815223dfe..0249acabb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -33,8 +34,8 @@ public class ArrayIsEmptyExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return empty ? 0 : 92821; + public void queryBindHash(BindHash hash) { + hash.update(empty); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java index bfdfa1d52..37a3aab8f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -49,10 +50,8 @@ class BetweenExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = low().hashCode(); - hc = hc * 92821 + high().hashCode(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(low()).update(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java index a50ec888f..440dd5f1f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -95,8 +96,8 @@ class BetweenPropertyExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { - return val().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java index 073ee51df..db1fa686d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -39,8 +40,8 @@ class BitwiseExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return Long.hashCode(flags); + public void queryBindHash(BindHash hash) { + hash.update(flags).update(match); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java index 52e39fbfa..485d1b04f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -69,8 +70,8 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression { } @Override - public int queryBindHash() { - return val().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index aade99f5a..9149923e0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -5,6 +5,7 @@ import io.ebean.LikeType; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; import io.ebean.util.SplitName; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -241,12 +242,11 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio * Return a hash for the actual bind values used. */ @Override - public int queryBindHash() { - int hc = DefaultExampleExpression.class.getName().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(list.size()); for (SpiExpression aList : list) { - hc = hc * 92821 + aList.queryBindHash(); + aList.queryBindHash(hash); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index ab80ae9e5..b5f9c6ec1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -26,6 +26,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -678,12 +679,11 @@ public class DefaultExpressionList implements SpiExpressionList { * Calculate a hash based on the expressions. */ @Override - public int queryBindHash() { - int hash = DefaultExpressionList.class.getName().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(list.size()); for (SpiExpression aList : list) { - hash = hash * 92821 + aList.queryBindHash(); + aList.queryBindHash(hash); } - return hash; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index 32ae2bccc..b3fcf8ce0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiEbeanServer; @@ -91,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress } @Override - public int queryBindHash() { - return subQuery.queryBindHash(); + public void queryBindHash(BindHash hash) { + subQuery.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java index b5280b868..904fbe9be 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -77,8 +78,8 @@ class IdExpression extends NonPrepareExpression implements SpiExpression { } @Override - public int queryBindHash() { - return value.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java index 4c08b24d2..390d0bebb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -133,8 +134,11 @@ public class IdInExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { - return idCollection.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(idCollection.size()); + for (Object elem : idCollection) { + hash.update(elem); + } } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java index 7009a8b44..a5719f71a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -176,12 +177,11 @@ class InExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = 92821; + public void queryBindHash(BindHash hash) { + hash.update(bindValues.size()); for (Object bindValue : bindValues) { - hc = 92821 * hc + bindValue.hashCode(); + hash.update(bindValue); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java index 96cc7c182..a7079ae55 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java @@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Pairs; import io.ebean.Pairs.Entry; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -124,12 +125,11 @@ class InPairsExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = 92821; + public void queryBindHash(BindHash hash) { + hash.update(entries.size()); for (Pairs.Entry entry : entries) { - hc = 92821 * hc + entry.hashCode(); + hash.update(entry.getA()).update(entry.getB()); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 83507af4b..619f7b715 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -72,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor } @Override - public int queryBindHash() { - return subQuery.queryBindHash(); + public void queryBindHash(BindHash hash) { + subQuery.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java index e1d170b43..abe0b7338 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -47,10 +48,8 @@ class InRangeExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = low().hashCode(); - hc = hc * 92821 + high().hashCode(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(low()).update(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java index d3a3a1da6..bb65962b3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -103,8 +104,8 @@ class IsEmptyExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return 1; + public void queryBindHash(BindHash hash) { + // no bind values } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java index f60b125cb..18e0aa924 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -83,10 +84,8 @@ class JsonPathExpression extends AbstractExpression { } @Override - public int queryBindHash() { - int hc = (value == null) ? 0 : value.hashCode(); - hc = (upperValue == null) ? hc : hc * 92821 + upperValue.hashCode(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(value).update(upperValue); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 0aae30886..51eba8f39 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -25,6 +25,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -225,13 +226,12 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public int queryBindHash() { + public void queryBindHash(BindHash hash) { int hc = JunctionExpression.class.getName().hashCode(); List list = exprList.internalList(); for (SpiExpression aList : list) { - hc = hc * 92821 + aList.queryBindHash(); + aList.queryBindHash(hash); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java index 6886203c4..587e6064c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -70,8 +71,8 @@ class LikeExpression extends AbstractValueExpression { } @Override - public int queryBindHash() { - return strValue().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(strValue()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java index 482aac6e8..cbd487b3e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java @@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.Junction; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -168,10 +169,8 @@ abstract class LogicExpression implements SpiExpression { } @Override - public int queryBindHash() { - int hc = expOne.queryBindHash(); - hc = hc * 92821 + expTwo.queryBindHash(); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(expOne).update(expTwo); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java index c3376f656..8c51d2f5d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -54,8 +55,8 @@ class NativeILikeExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return val.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(val); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java index 77f46828b..f3ed1badd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -79,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression { } @Override - public int queryBindHash() { - return delegate.queryBindHash(); + public void queryBindHash(BindHash hash) { + delegate.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java index 28c9e3c78..92180cea1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -74,9 +75,8 @@ class NoopExpression implements SpiExpression { } @Override - public int queryBindHash() { + public void queryBindHash(BindHash hash) { // no bind values - return 0; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java index 60ade47b0..e9df6d856 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java @@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.event.BeanQueryRequest; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -99,8 +100,8 @@ final class NotExpression implements SpiExpression { } @Override - public int queryBindHash() { - return exp.queryBindHash(); + public void queryBindHash(BindHash hash) { + exp.queryBindHash(hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java index 90894dbde..daded43cb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -94,7 +95,7 @@ class NullExpression extends AbstractExpression { } @Override - public int queryBindHash() { - return (notNull ? 1 : 0); + public void queryBindHash(BindHash hash) { + hash.update(notNull); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java index 295ddc75b..721d69b25 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.expression; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -73,12 +74,11 @@ class RawExpression extends NonPrepareExpression { } @Override - public int queryBindHash() { - int hc = sql.hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(values.length); for (Object value : values) { - hc = hc * 92821 + value.hashCode(); + hash.update(value); } - return hc; } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java index 0f8de90ec..e50d9856c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java @@ -5,6 +5,7 @@ import io.ebean.plugin.ExpressionPath; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.NaturalKeyQueryData; import java.io.IOException; @@ -121,8 +122,8 @@ public class SimpleExpression extends AbstractValueExpression { } @Override - public int queryBindHash() { - return value().hashCode(); + public void queryBindHash(BindHash hash) { + hash.update(value()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 96a923637..6f3334ede 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -32,6 +32,8 @@ import io.ebean.bean.PersistenceContext; import io.ebean.event.BeanQueryRequest; import io.ebean.event.readaudit.ReadEvent; import io.ebean.plugin.BeanType; +import io.ebean.plugin.LoadErrorHandler; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.BindParams; import io.ebeaninternal.api.CQueryPlanKey; import io.ebeaninternal.api.CacheIdLookup; @@ -283,6 +285,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { private boolean orderById; + private final String bindHashAlgorithm; + private ProfileLocation profileLocation; public DefaultOrmQuery(BeanDescriptor desc, SpiEbeanServer server, ExpressionFactory expressionFactory) { @@ -291,6 +295,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { this.beanType = desc.getBeanType(); this.server = server; this.orderById = server.getServerConfig().isDefaultOrderById(); + this.bindHashAlgorithm = "MD5"; // TODO: server.getServerConfig().isUseMd5BindHash(); this.disableLazyLoading = server.getServerConfig().isDisableLazyLoading(); this.expressionFactory = expressionFactory; this.detail = new OrmQueryDetail(); @@ -1276,15 +1281,12 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { *

*/ @Override - public int queryBindHash() { - int hc = (id == null ? 0 : id.hashCode()); - hc = hc * 92821 + (whereExpressions == null ? 0 : whereExpressions.queryBindHash()); - hc = hc * 92821 + (havingExpressions == null ? 0 : havingExpressions.queryBindHash()); - hc = hc * 92821 + (bindParams == null ? 0 : bindParams.queryBindHash()); - hc = hc * 92821 + (asOf == null ? 0 : asOf.hashCode()); - hc = hc * 92821 + (versionsStart == null ? 0 : versionsStart.hashCode()); - hc = hc * 92821 + (versionsEnd == null ? 0 : versionsEnd.hashCode()); - return hc; + public void queryBindHash(BindHash hash) { + hash.update(id); + if (whereExpressions != null) whereExpressions.queryBindHash(hash); + if (havingExpressions != null) havingExpressions.queryBindHash(hash); + if (bindParams != null) bindParams.queryBindHash(hash); + hash.update(asOf).update(versionsStart).update(versionsEnd); } /** @@ -1298,8 +1300,10 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - int hc = queryBindHash(); - return new HashQuery(queryPlanKey, hc); + BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); + queryBindHash(hash); + hash.finish(); + return new HashQuery(queryPlanKey, hash); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java new file mode 100644 index 000000000..cc6cd5f13 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java @@ -0,0 +1,56 @@ +package io.ebeaninternal.server.querydefn; + +import java.util.Objects; + +import io.ebeaninternal.api.BindHash; + +/** + * HashCode builder that uses Object.hashCode for computing bind-hashes. + * This is a fast and lightweight implementation, but may produce collisions. + * + * @author Roland Praml, FOCONIS AG + */ +public class HashCodeBindHash implements BindHash { + + int hashCode; + + @Override + public BindHash update(int intValue) { + hashCode = hashCode * 92821 + intValue; + return this; + } + + @Override + public BindHash update(long longValue) { + hashCode = hashCode * 92821 + Long.hashCode(longValue); + return this; + } + + @Override + public BindHash update(boolean boolValue) { + hashCode = hashCode * 92821 + Boolean.hashCode(boolValue); + return this; + } + + @Override + public BindHash update(Object value) { + hashCode = hashCode * 92821 + Objects.hashCode(value); + return this; + } + + @Override + public void finish() { + // nothing to do + } + + @Override + public boolean equals(Object obj) { + return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).hashCode == hashCode; + } + + @Override + public int hashCode() { + return hashCode; + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java new file mode 100644 index 000000000..4791a9c07 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java @@ -0,0 +1,138 @@ +/* + * Licensed Materials - Property of FOCONIS AG + * (C) Copyright FOCONIS AG. + */ + +package io.ebeaninternal.server.querydefn; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.Date; +import java.util.UUID; + +import io.ebeaninternal.api.BindHash; + +/** + * Bind hash that uses a MessageDigest to compute a collision resistent hash. + * + * @author Roland Praml, FOCONIS AG + * + */ +public class MdBindHash implements BindHash { + private MessageDigest md; + private byte[] buffer; + private int hashCode; + + public MdBindHash(String algorithm) { + try { + md = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException nsae) { + throw new InternalError(algorithm + " not supported", nsae); + } + } + + @Override + public BindHash update(int v) { + md.update((byte) (v >>> 24)); + md.update((byte) (v >>> 16)); + md.update((byte) (v >>> 8)); + md.update((byte) (v >>> 0)); + return this; + } + + @Override + public BindHash update(long v) { + md.update((byte) (v >>> 56)); + md.update((byte) (v >>> 48)); + md.update((byte) (v >>> 40)); + md.update((byte) (v >>> 32)); + md.update((byte) (v >>> 24)); + md.update((byte) (v >>> 16)); + md.update((byte) (v >>> 8)); + md.update((byte) (v >>> 0)); + return this; + } + + @Override + public BindHash update(boolean boolValue) { + md.update(boolValue ? (byte) 1 : (byte) 0); + return this; + } + + @Override + public BindHash update(Object value) { + if (value == null) { + md.update((byte) 0); + + // do some special handling for known object types + } else if (value instanceof String) { + md.update(((String) value).getBytes()); + + } else if (value instanceof Long) { + update(((Long) value).longValue()); + + } else if (value instanceof Double) { + double d = ((Double) value).doubleValue(); + update(Double.doubleToLongBits(d)); + + } else if (value instanceof UUID) { + UUID uuid = (UUID) value; + update(uuid.getLeastSignificantBits()); + update(uuid.getMostSignificantBits()); + + } else if (value instanceof Date) { + update(((Date) value).getTime()); + + } else if (value instanceof Instant) { + update(((Instant) value).getEpochSecond()); + update(((Instant) value).getNano()); + + } else if (value instanceof LocalDate) { + update(((LocalDate) value).toEpochDay()); + + } else if (value instanceof LocalTime) { + update(((LocalTime) value).toSecondOfDay()); + update(((LocalTime) value).toNanoOfDay()); + + } else if (value instanceof LocalDateTime) { + update(((LocalDateTime) value).toLocalDate().toEpochDay()); + update(((LocalDateTime) value).toLocalTime().toSecondOfDay()); + update(((LocalDateTime) value).toLocalTime().toNanoOfDay()); + + } else { + // Fall back to hashCode for all other types + updateOther(value); + } + return this; + } + + /** + * Update all other object. May be overridden to handle joda dates. + */ + protected void updateOther(Object value) { + // Fall back to hashCode for all other types + update(value.hashCode()); + } + + @Override + public void finish() { + buffer = md.digest(); + hashCode = Arrays.hashCode(buffer); + md = null; // clear memory + } + + @Override + public boolean equals(Object obj) { + return obj instanceof MdBindHash && Arrays.equals(buffer, ((MdBindHash) obj).buffer); + } + + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 1953f2959..6c608472f 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -3,6 +3,8 @@ package io.ebeaninternal.server.expression; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; +import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.server.querydefn.HashCodeBindHash; public class RawExpressionTest extends BaseExpressionTest { @@ -61,11 +63,17 @@ public class RawExpressionTest extends BaseExpressionTest { } public void assert_queryBindHash_isDifferent(RawExpression exp0, RawExpression exp1) { - assertThat(exp0.queryBindHash()).isNotEqualTo(exp1.queryBindHash()); + assertThat(getHash(exp0)).isNotEqualTo(getHash(exp1)); } public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1) { - assertThat(exp0.queryBindHash()).isEqualTo(exp1.queryBindHash()); + assertThat(getHash(exp0)).isEqualTo(getHash(exp1)); } + private int getHash(RawExpression query) { + BindHash hash = new HashCodeBindHash(); + query.queryBindHash(hash); + hash.finish(); + return hash.hashCode(); + } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index d9e621221..cb1738ba8 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -4,6 +4,7 @@ package io.ebeaninternal.server.querydefn; import io.ebean.BaseTestCase; import io.ebean.CacheMode; import io.ebean.Ebean; +import io.ebeaninternal.api.BindHash; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.core.OrmQueryRequest; import org.junit.Test; @@ -62,7 +63,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isNotEqualTo(q2.createQueryPlanKey()); - assertThat(q1.queryBindHash()).isNotEqualTo(q2.queryBindHash()); + assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); } @Test @@ -73,7 +74,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(q1.queryBindHash()).isNotEqualTo(q2.queryBindHash()); + assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); } @Test @@ -84,7 +85,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(q1.queryBindHash()).isEqualTo(q2.queryBindHash()); + assertThat(getHash(q1)).isEqualTo(getHash(q2)); } @Test @@ -110,4 +111,11 @@ public class DefaultOrmQueryTest extends BaseTestCase { OrmQueryRequest r2 = createQueryRequest(SpiQuery.Type.LIST, q2, null); q2.prepare(r2); } + + private int getHash(DefaultOrmQuery query) { + BindHash hash = new HashCodeBindHash(); + query.queryBindHash(hash); + hash.finish(); + return hash.hashCode(); + } } diff --git a/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java b/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java index f9e732220..4c4d56109 100644 --- a/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java +++ b/ebean-core/src/test/java/org/tests/cache/TestQueryCache.java @@ -3,7 +3,7 @@ package org.tests.cache; import io.ebean.BaseTestCase; import io.ebean.CacheMode; import io.ebean.DB; -import io.ebean.Ebean; +import io.ebean.ExpressionList; import io.ebean.bean.BeanCollection; import io.ebean.cache.ServerCache; import org.ebeantest.LoggedSqlCollector; @@ -14,6 +14,7 @@ import org.tests.model.basic.ResetBasicData; import org.tests.model.cache.EColAB; import java.util.List; +import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; @@ -26,8 +27,7 @@ public class TestQueryCache extends BaseTestCase { new EColAB("02", "10").save(); List list1 = - Ebean.getServer(null) - .find(EColAB.class) + DB.find(EColAB.class) .setUseQueryCache(true) .where() .eq("columnA", "01") @@ -35,8 +35,7 @@ public class TestQueryCache extends BaseTestCase { .findList(); List list2 = - Ebean.getServer(null) - .find(EColAB.class) + DB.find(EColAB.class) .setUseQueryCache(true) .where() .eq("columnA", "02") @@ -57,7 +56,7 @@ public class TestQueryCache extends BaseTestCase { new EColAB("03", "SingleAttribute").save(); new EColAB("03", "SingleAttribute").save(); - List colA_first = Ebean.getServer(null) + List colA_first = DB .find(EColAB.class) .setUseQueryCache(true) .setDistinct(true) @@ -66,7 +65,7 @@ public class TestQueryCache extends BaseTestCase { .eq("columnB", "SingleAttribute") .findSingleAttributeList(); - List colA_Second = Ebean.getServer(null) + List colA_Second = DB .find(EColAB.class) .setUseQueryCache(true) .setDistinct(true) @@ -77,7 +76,7 @@ public class TestQueryCache extends BaseTestCase { assertThat(colA_Second).isSameAs(colA_first); - List colA_NotDistinct = Ebean.getServer(null) + List colA_NotDistinct = DB .find(EColAB.class) .setUseQueryCache(true) .select("columnA") @@ -89,7 +88,7 @@ public class TestQueryCache extends BaseTestCase { // ensure that findCount & findSingleAttribute use different // slots in cache. If not a "Cannot cast List to int" should happen. - int count = Ebean.getServer(null) + int count = DB .find(EColAB.class) .setUseQueryCache(true) .select("columnA") @@ -107,13 +106,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "count") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "count") @@ -126,7 +125,7 @@ public class TestQueryCache extends BaseTestCase { // and now, ensure that we hit the database LoggedSqlCollector.start(); - int count2 = Ebean.find(EColAB.class) + int count2 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.OFF) .where() .eq("columnB", "count") @@ -142,13 +141,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "abc") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "def") @@ -167,13 +166,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "uvw") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.PUT) .where() .eq("columnB", "uvw") @@ -193,13 +192,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - int count0 = Ebean.find(EColAB.class) + int count0 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.PUT) .where() .eq("columnB", "xyz") .findCount(); - int count1 = Ebean.find(EColAB.class) + int count1 = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "xyz") @@ -214,26 +213,26 @@ public class TestQueryCache extends BaseTestCase { @Test @SuppressWarnings("unchecked") - public void test() { + public void testReadOnlyFind() { ResetBasicData.reset(); - ServerCache customerCache = Ebean.getServerCacheManager().getQueryCache(Customer.class); + ServerCache customerCache = DB.getServerCacheManager().getQueryCache(Customer.class); customerCache.clear(); - List list = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() + List list = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() .ilike("name", "Rob").findList(); BeanCollection bc = (BeanCollection) list; Assert.assertTrue(bc.isReadOnly()); Assert.assertFalse(bc.isEmpty()); Assert.assertTrue(!list.isEmpty()); - Assert.assertTrue(Ebean.getBeanState(list.get(0)).isReadOnly()); + Assert.assertTrue(DB.getBeanState(list.get(0)).isReadOnly()); - List list2 = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() + List list2 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where() .ilike("name", "Rob").findList(); - List list2B = Ebean.find(Customer.class).setUseQueryCache(true) + List list2B = DB.find(Customer.class).setUseQueryCache(true) // .setReadOnly(true) .where().ilike("name", "Rob").findList(); @@ -245,7 +244,7 @@ public class TestQueryCache extends BaseTestCase { - List list3 = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where() + List list3 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where() .ilike("name", "Rob").findList(); Assert.assertNotSame(list, list3); @@ -269,13 +268,13 @@ public class TestQueryCache extends BaseTestCase { LoggedSqlCollector.start(); - List colA_first = Ebean.find(EColAB.class) + List colA_first = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "someId") .findIds(); - List colA_second = Ebean.find(EColAB.class) + List colA_second = DB.find(EColAB.class) .setUseQueryCache(CacheMode.ON) .where() .eq("columnB", "someId") @@ -289,7 +288,7 @@ public class TestQueryCache extends BaseTestCase { // and now, ensure that we hit the database LoggedSqlCollector.start(); - colA_second = Ebean.find(EColAB.class) + colA_second = DB.find(EColAB.class) .setUseQueryCache(CacheMode.PUT) .where() .eq("columnB", "someId") @@ -299,4 +298,35 @@ public class TestQueryCache extends BaseTestCase { assertThat(sql).hasSize(1); } + @Test + public void findCountDifferentQueriesBit() { + DB.getDefault().getPluginApi().getServerCacheManager().clearAll(); + differentFindCount(q->q.bitwiseAny("id",1), q->q.bitwiseAny("id",0)); + differentFindCount(q->q.bitwiseAll("id",1), q->q.bitwiseAll("id",0)); + // differentFindCount(q->q.bitwiseNot("id",1), q->q.bitwiseNot("id",0)); NOT 1 == AND 1 = 0 + differentFindCount(q->q.bitwiseAnd("id",1, 0), q->q.bitwiseAnd("id",1, 1)); + + differentFindCount(q->q.bitwiseAnd("id",2, 0), q->q.bitwiseAnd("id",4, 0)); + differentFindCount(q->q.bitwiseAnd("id",2, 1), q->q.bitwiseAnd("id",4, 1)); + // Will produce hash collision + differentFindCount(q->q.bitwiseAnd("id",10, 0), q->q.bitwiseAnd("id",0, 928210)); + + } + + void differentFindCount(Consumer> q0, Consumer> q1) { + LoggedSqlCollector.start(); + + ExpressionList el0 = DB.find(EColAB.class).setUseQueryCache(CacheMode.ON).where(); + q0.accept(el0); + el0.findCount(); + + ExpressionList el1 = DB.find(EColAB.class).setUseQueryCache(CacheMode.ON).where(); + q1.accept(el1); + el1.findCount(); + + List sql = LoggedSqlCollector.stop(); + + assertThat(sql).hasSize(2); // different queries + } + } From e18c190905fa8a2a2051e6bba3447ad1a942408d Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 9 Aug 2021 09:46:49 +0200 Subject: [PATCH 72/87] Fix the test to run with java 8 --- .../server/profile/BasicProfileLocationTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java index 84a53442a..9d99b17ce 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java @@ -14,7 +14,11 @@ public class BasicProfileLocationTest { assertThat(loc.obtain()).isTrue(); assertThat(loc.fullLocation()).endsWith(":12)"); - assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0"); + if (System.getProperty("java.version").startsWith("1.8")) { + assertThat(loc.location()).isEqualTo("sun.reflect.NativeMethodAccessorImpl.invoke0"); + } else { + assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0"); + } assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0"); } From c8ae3c5ef65da0ad043f9a0c302720abd298fda9 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 10 Aug 2021 09:30:25 +0200 Subject: [PATCH 73/87] FIX: Changelog-oldValue did not work for Json mutable properties --- .../io/ebean/bean/EntityBeanIntercept.java | 13 ++++++--- .../org/tests/changelog/TestChangeLog.java | 27 +++++++++++++++++++ .../tests/model/basic/EBasicChangeLog.java | 19 ++++++++++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 78d8e5f8c..527860200 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -395,6 +395,15 @@ public final class EntityBeanIntercept implements Serializable { this.owner._ebean_setEmbeddedLoaded(); this.lazyLoadProperty = -1; this.origValues = null; + // after save, transfer the mutable next values back to mutable info + if (mutableNext != null) { + for (int i = 0; i < mutableNext.length; i++) { + MutableValueNext next = mutableNext[i]; + if (next != null) { + mutableInfo(i, next.info()); + } + } + } this.mutableNext = null; for (int i = 0; i < flags.length; i++) { flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET); @@ -1223,9 +1232,7 @@ public final class EntityBeanIntercept implements Serializable { if (mutableNext == null) { return null; } - final MutableValueNext next = mutableNext[propertyIndex]; - mutableInfo(propertyIndex, next.info()); - return next.content(); + return mutableNext[propertyIndex].content(); } } diff --git a/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java b/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java index 2adbbfc4b..a10a41276 100644 --- a/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java +++ b/ebean-core/src/test/java/org/tests/changelog/TestChangeLog.java @@ -16,10 +16,13 @@ import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.changelog.ChangeSet; import io.ebean.event.changelog.ChangeType; import io.ebean.event.changelog.TxnState; +import io.ebeantest.LoggedSql; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.tests.model.basic.EBasicChangeLog; +import org.tests.model.json.PlainBean; import java.util.ArrayList; import java.util.List; @@ -130,7 +133,31 @@ public class TestChangeLog extends BaseTestCase { assertThat(change.getEvent()).isEqualTo(ChangeType.DELETE); assertThat(change.getData()).isNull(); } + + @Test + public void testWithJsonMutationDetection() { + EBasicChangeLog bean = new EBasicChangeLog(); + bean.setName(null); + bean.setShortDescription("hello"); + PlainBean jsonBean = new PlainBean(); + bean.setPlainBean(jsonBean); + jsonBean.setName("A"); + server.save(bean); + + BeanChange change = firstChange(); + assertThat(change.getEvent()).isEqualTo(ChangeType.INSERT); + + jsonBean.setName("B"); + LoggedSql.start(); + server.save(bean); + assertThat(LoggedSql.stop()).isNotEmpty(); + + change = firstChange(); + assertThat(change.getEvent()).isEqualTo(ChangeType.UPDATE); + assertThat(change.getData()).contains("\"plainBean\":{\"name\":\"B\""); + assertThat(change.getOldData()).contains("\"plainBean\":{\"name\":\"A\""); + } private Database createServer() { DatabaseConfig config = new DatabaseConfig(); diff --git a/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java b/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java index 88e3e4f10..0ac02e749 100644 --- a/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java +++ b/ebean-core/src/test/java/org/tests/model/basic/EBasicChangeLog.java @@ -2,6 +2,7 @@ package org.tests.model.basic; import io.ebean.annotation.Cache; import io.ebean.annotation.ChangeLog; +import io.ebean.annotation.DbJson; import io.ebean.annotation.ReadAudit; import io.ebean.annotation.WhenCreated; import io.ebean.annotation.WhenModified; @@ -12,11 +13,16 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Version; import javax.validation.constraints.Size; + +import org.tests.model.json.PlainBean; + +import static io.ebean.annotation.MutationDetection.SOURCE; + import java.sql.Timestamp; @Cache(enableQueryCache = true) @ReadAudit -@ChangeLog(updatesThatInclude = {"name", "shortDescription"}) +@ChangeLog(updatesThatInclude = {"name", "shortDescription", "plainBean"}) @Entity public class EBasicChangeLog { @@ -46,6 +52,9 @@ public class EBasicChangeLog { @Version Long version; + + @DbJson(length = 500, mutationDetection = SOURCE) // such that we can rebuild old values + PlainBean plainBean; public Long getId() { return id; @@ -118,4 +127,12 @@ public class EBasicChangeLog { public void setVersion(Long version) { this.version = version; } + + public PlainBean getPlainBean() { + return plainBean; + } + + public void setPlainBean(PlainBean plainBean) { + this.plainBean = plainBean; + } } From 3e8f76cd6d054cbc9bb88392c4e3048d8857841b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 21:49:31 +1200 Subject: [PATCH 74/87] Update HashCodeBindHash to use values for equals() --- .../server/querydefn/DefaultOrmQuery.java | 3 ++- .../server/querydefn/HashCodeBindHash.java | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 6f3334ede..f29889a88 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1300,7 +1300,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); + //BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); + BindHash hash = new HashCodeBindHash();// : new MdBindHash(bindHashAlgorithm); queryBindHash(hash); hash.finish(); return new HashQuery(queryPlanKey, hash); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java index cc6cd5f13..9bc9c60fc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java @@ -1,5 +1,7 @@ package io.ebeaninternal.server.querydefn; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import io.ebeaninternal.api.BindHash; @@ -12,28 +14,33 @@ import io.ebeaninternal.api.BindHash; */ public class HashCodeBindHash implements BindHash { - int hashCode; + private final List values = new ArrayList<>(); + private int hashCode; @Override public BindHash update(int intValue) { + values.add(intValue); hashCode = hashCode * 92821 + intValue; return this; } @Override public BindHash update(long longValue) { + values.add(longValue); hashCode = hashCode * 92821 + Long.hashCode(longValue); return this; } @Override public BindHash update(boolean boolValue) { + values.add(boolValue); hashCode = hashCode * 92821 + Boolean.hashCode(boolValue); return this; } @Override public BindHash update(Object value) { + values.add(value); hashCode = hashCode * 92821 + Objects.hashCode(value); return this; } @@ -45,7 +52,7 @@ public class HashCodeBindHash implements BindHash { @Override public boolean equals(Object obj) { - return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).hashCode == hashCode; + return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).values.equals(values); } @Override From ede1ad0a4c0d067bfa2823d0ec24530c7e88254e Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 21:58:41 +1200 Subject: [PATCH 75/87] Fix BindParams equals() and hashCode() --- .../java/io/ebeaninternal/api/BindParams.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index 2817c4ba1..3a81f2f0a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -4,11 +4,7 @@ import io.ebeaninternal.server.persist.MultiValueWrapper; import io.ebeaninternal.server.querydefn.NaturalKeyBindParam; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; /** @@ -414,6 +410,7 @@ public class BindParams implements Serializable { @Override public int hashCode() { int hc = getClass().hashCode(); + hc = hc * 92821 + (encryptionKey ? 0 : 1); hc = hc * 92821 + (isInParam ? 0 : 1); hc = hc * 92821 + (isOutParam ? 0 : 1); hc = hc * 92821 + (type); @@ -421,13 +418,17 @@ public class BindParams implements Serializable { return hc; } - void queryBindHash(BindHash hash) { - hash.update(isInParam).update(isOutParam).update(type).update(inValue); - } - @Override public boolean equals(Object o) { - return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode()); + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Param param = (Param) o; + return encryptionKey == param.encryptionKey && isInParam == param.isInParam && isOutParam == param.isOutParam + && type == param.type && Objects.equals(inValue, param.inValue); + } + + void queryBindHash(BindHash hash) { + hash.update(isInParam).update(isOutParam).update(type).update(inValue); } /** From 2dc48cf08d4519e66bc8bc81b9f31a791407e47d Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:00:46 +1200 Subject: [PATCH 76/87] ArrayContainsExpression equals() to use enhanced for loop --- .../server/expression/ArrayContainsExpression.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index ac6d27d0e..3c200c046 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -52,8 +52,8 @@ public class ArrayContainsExpression extends AbstractExpression { @Override public void queryBindHash(BindHash hash) { hash.update(values.length); - for (int i = 0; i < values.length; i++) { - hash.update(values[i]); + for (Object value : values) { + hash.update(value); } } From ac3edbedf41ef627f5d99f77507dbfac4fc51dd5 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:07:48 +1200 Subject: [PATCH 77/87] Remove MdBindHash, tidy DefaultOrmQuery --- .../server/querydefn/DefaultOrmQuery.java | 67 +-------- .../server/querydefn/MdBindHash.java | 138 ------------------ 2 files changed, 5 insertions(+), 200 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index f29889a88..e4735e32b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1,30 +1,7 @@ package io.ebeaninternal.server.querydefn; -import io.ebean.CacheMode; -import io.ebean.CountDistinctOrder; -import io.ebean.Database; -import io.ebean.DtoQuery; -import io.ebean.Expression; -import io.ebean.ExpressionFactory; -import io.ebean.ExpressionList; -import io.ebean.FetchConfig; -import io.ebean.FetchGroup; -import io.ebean.FetchPath; -import io.ebean.FutureIds; -import io.ebean.FutureList; -import io.ebean.FutureRowCount; -import io.ebean.OrderBy; +import io.ebean.*; import io.ebean.OrderBy.Property; -import io.ebean.PagedList; -import io.ebean.PersistenceContextScope; -import io.ebean.ProfileLocation; -import io.ebean.Query; -import io.ebean.QueryIterator; -import io.ebean.QueryType; -import io.ebean.RawSql; -import io.ebean.Transaction; -import io.ebean.UpdateQuery; -import io.ebean.Version; import io.ebean.bean.CallOrigin; import io.ebean.bean.ObjectGraphNode; import io.ebean.bean.ObjectGraphOrigin; @@ -32,31 +9,10 @@ import io.ebean.bean.PersistenceContext; import io.ebean.event.BeanQueryRequest; import io.ebean.event.readaudit.ReadEvent; import io.ebean.plugin.BeanType; -import io.ebean.plugin.LoadErrorHandler; -import io.ebeaninternal.api.BindHash; -import io.ebeaninternal.api.BindParams; -import io.ebeaninternal.api.CQueryPlanKey; -import io.ebeaninternal.api.CacheIdLookup; -import io.ebeaninternal.api.CacheIdLookupMany; -import io.ebeaninternal.api.CacheIdLookupSingle; -import io.ebeaninternal.api.HashQuery; -import io.ebeaninternal.api.ManyWhereJoins; -import io.ebeaninternal.api.NaturalKeyQueryData; -import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.api.SpiExpression; -import io.ebeaninternal.api.SpiExpressionList; -import io.ebeaninternal.api.SpiExpressionValidation; -import io.ebeaninternal.api.SpiNamedParam; -import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.api.SpiQuerySecondary; -import io.ebeaninternal.api.SpiTransaction; +import io.ebeaninternal.api.*; import io.ebeaninternal.server.autotune.ProfilingListener; import io.ebeaninternal.server.core.SpiOrmQueryRequest; -import io.ebeaninternal.server.deploy.BeanDescriptor; -import io.ebeaninternal.server.deploy.BeanNaturalKey; -import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import io.ebeaninternal.server.deploy.InheritInfo; -import io.ebeaninternal.server.deploy.TableJoin; +import io.ebeaninternal.server.deploy.*; import io.ebeaninternal.server.el.ElPropertyDeploy; import io.ebeaninternal.server.expression.DefaultExpressionList; import io.ebeaninternal.server.expression.IdInExpression; @@ -68,14 +24,7 @@ import io.ebeaninternal.server.transaction.ExternalJdbcTransaction; import javax.persistence.PersistenceException; import java.sql.Connection; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.locks.ReentrantLock; +import java.util.*; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -93,8 +42,6 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy(); - private final ReentrantLock lock = new ReentrantLock(); - private final Class beanType; private final ExpressionFactory expressionFactory; @@ -285,8 +232,6 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { private boolean orderById; - private final String bindHashAlgorithm; - private ProfileLocation profileLocation; public DefaultOrmQuery(BeanDescriptor desc, SpiEbeanServer server, ExpressionFactory expressionFactory) { @@ -295,7 +240,6 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { this.beanType = desc.getBeanType(); this.server = server; this.orderById = server.getServerConfig().isDefaultOrderById(); - this.bindHashAlgorithm = "MD5"; // TODO: server.getServerConfig().isUseMd5BindHash(); this.disableLazyLoading = server.getServerConfig().isDisableLazyLoading(); this.expressionFactory = expressionFactory; this.detail = new OrmQueryDetail(); @@ -1300,8 +1244,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - //BindHash hash = bindHashAlgorithm == null ? new HashCodeBindHash() : new MdBindHash(bindHashAlgorithm); - BindHash hash = new HashCodeBindHash();// : new MdBindHash(bindHashAlgorithm); + BindHash hash = new HashCodeBindHash(); queryBindHash(hash); hash.finish(); return new HashQuery(queryPlanKey, hash); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java deleted file mode 100644 index 4791a9c07..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/MdBindHash.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed Materials - Property of FOCONIS AG - * (C) Copyright FOCONIS AG. - */ - -package io.ebeaninternal.server.querydefn; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.Arrays; -import java.util.Date; -import java.util.UUID; - -import io.ebeaninternal.api.BindHash; - -/** - * Bind hash that uses a MessageDigest to compute a collision resistent hash. - * - * @author Roland Praml, FOCONIS AG - * - */ -public class MdBindHash implements BindHash { - private MessageDigest md; - private byte[] buffer; - private int hashCode; - - public MdBindHash(String algorithm) { - try { - md = MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException nsae) { - throw new InternalError(algorithm + " not supported", nsae); - } - } - - @Override - public BindHash update(int v) { - md.update((byte) (v >>> 24)); - md.update((byte) (v >>> 16)); - md.update((byte) (v >>> 8)); - md.update((byte) (v >>> 0)); - return this; - } - - @Override - public BindHash update(long v) { - md.update((byte) (v >>> 56)); - md.update((byte) (v >>> 48)); - md.update((byte) (v >>> 40)); - md.update((byte) (v >>> 32)); - md.update((byte) (v >>> 24)); - md.update((byte) (v >>> 16)); - md.update((byte) (v >>> 8)); - md.update((byte) (v >>> 0)); - return this; - } - - @Override - public BindHash update(boolean boolValue) { - md.update(boolValue ? (byte) 1 : (byte) 0); - return this; - } - - @Override - public BindHash update(Object value) { - if (value == null) { - md.update((byte) 0); - - // do some special handling for known object types - } else if (value instanceof String) { - md.update(((String) value).getBytes()); - - } else if (value instanceof Long) { - update(((Long) value).longValue()); - - } else if (value instanceof Double) { - double d = ((Double) value).doubleValue(); - update(Double.doubleToLongBits(d)); - - } else if (value instanceof UUID) { - UUID uuid = (UUID) value; - update(uuid.getLeastSignificantBits()); - update(uuid.getMostSignificantBits()); - - } else if (value instanceof Date) { - update(((Date) value).getTime()); - - } else if (value instanceof Instant) { - update(((Instant) value).getEpochSecond()); - update(((Instant) value).getNano()); - - } else if (value instanceof LocalDate) { - update(((LocalDate) value).toEpochDay()); - - } else if (value instanceof LocalTime) { - update(((LocalTime) value).toSecondOfDay()); - update(((LocalTime) value).toNanoOfDay()); - - } else if (value instanceof LocalDateTime) { - update(((LocalDateTime) value).toLocalDate().toEpochDay()); - update(((LocalDateTime) value).toLocalTime().toSecondOfDay()); - update(((LocalDateTime) value).toLocalTime().toNanoOfDay()); - - } else { - // Fall back to hashCode for all other types - updateOther(value); - } - return this; - } - - /** - * Update all other object. May be overridden to handle joda dates. - */ - protected void updateOther(Object value) { - // Fall back to hashCode for all other types - update(value.hashCode()); - } - - @Override - public void finish() { - buffer = md.digest(); - hashCode = Arrays.hashCode(buffer); - md = null; // clear memory - } - - @Override - public boolean equals(Object obj) { - return obj instanceof MdBindHash && Arrays.equals(buffer, ((MdBindHash) obj).buffer); - } - - @Override - public int hashCode() { - return hashCode; - } -} From e1ad210bb088aa7da3a6ee15fe6775742becf784 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:18:33 +1200 Subject: [PATCH 78/87] Tidy JunctionExpression --- .../server/expression/JunctionExpression.java | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 51eba8f39..88614873d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -114,9 +114,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void writeDocQuery(DocQueryContext context) throws IOException { context.startBool(type); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.writeDocQuery(context); + for (SpiExpression expr : exprList.internalList()) { + expr.writeDocQuery(context); } context.endBool(); } @@ -124,9 +123,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void writeDocQueryJunction(DocQueryContext context) throws IOException { context.startBoolGroupList(type); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.writeDocQuery(context); + for (SpiExpression expr : exprList.internalList()) { + expr.writeDocQuery(context); } context.endBoolGroupList(); } @@ -139,18 +137,15 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { - List list = exprList.internalList(); - // get the current state for 'require outer joins' boolean parentOuterJoins = manyWhereJoin.isRequireOuterJoins(); if (type == Type.OR) { // turn on outer joins required for disjunction expressions manyWhereJoin.setRequireOuterJoins(true); } - - for (SpiExpression aList : list) { - aList.containsMany(desc, manyWhereJoin); + for (SpiExpression expr : list) { + expr.containsMany(desc, manyWhereJoin); } if (type == Type.OR && !parentOuterJoins) { // restore state to not forcing outer joins @@ -177,18 +172,14 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void addBindValues(SpiExpressionRequest request) { - - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.addBindValues(request); + for (SpiExpression expr : exprList.internalList()) { + expr.addBindValues(request); } } @Override public void addSql(SpiExpressionRequest request) { - List list = exprList.internalList(); - if (!list.isEmpty()) { request.append(type.prefix()); request.append("("); @@ -205,9 +196,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void prepareExpression(BeanQueryRequest request) { - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.prepareExpression(request); + for (SpiExpression expr : exprList.internalList()) { + expr.prepareExpression(request); } } @@ -227,10 +217,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void queryBindHash(BindHash hash) { - int hc = JunctionExpression.class.getName().hashCode(); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.queryBindHash(hash); + for (SpiExpression expr : exprList.internalList()) { + expr.queryBindHash(hash); } } @@ -275,7 +263,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression return exprList.textCommonTerms(search, options); } - @Override public ExpressionList allEq(Map propertyMap) { return exprList.allEq(propertyMap); @@ -1025,7 +1012,6 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public String nestedPath(BeanDescriptor desc) { - PrepareDocNested.prepare(exprList, desc, type); String nestedPath = exprList.allDocNestedPath; if (nestedPath != null) { From e47b1737a82382093714d07bc3f20f787c63c7f9 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 10 Aug 2021 22:28:31 +1200 Subject: [PATCH 79/87] Add HashCodeBindHashTest --- .../querydefn/HashCodeBindHashTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java new file mode 100644 index 000000000..dea0a2e0c --- /dev/null +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java @@ -0,0 +1,37 @@ +package io.ebeaninternal.server.querydefn; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashCodeBindHashTest { + + @Test + public void update_with_null() { + + HashCodeBindHash hash = new HashCodeBindHash(); + hash.update(1).update(null).update("hello"); + + HashCodeBindHash hash2 = new HashCodeBindHash(); + hash2.update(1).update(null).update("hello"); + + assertThat(hash).isEqualTo(hash2); + } + + @Test + public void notEqual() { + + HashCodeBindHash hash = new HashCodeBindHash(); + hash.update(1).update(null).update("hello"); + + HashCodeBindHash hash2 = new HashCodeBindHash(); + hash2.update(1).update("hello"); + + HashCodeBindHash hash3 = new HashCodeBindHash(); + hash2.update(1).update(null); + + assertThat(hash).isNotEqualTo(hash2); + assertThat(hash).isNotEqualTo(hash3); + assertThat(hash2).isNotEqualTo(hash3); + } +} From 75fa7cb7fddda9d5699354e8c0a298abfc6888bc Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 10 Aug 2021 13:24:13 +0200 Subject: [PATCH 80/87] PushJson/PopJson did not work correctly, if null beans were involved --- .../type/ScalarTypeJsonObjectMapper.java | 4 +- .../org/tests/json/TestDbJson_Jackson3.java | 16 ++++ .../org/tests/model/json/EBasicJsonMulti.java | 81 +++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index 7ae24420f..41cb28d49 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -80,11 +80,11 @@ class ScalarTypeJsonObjectMapper { @Override public Object read(DataReader reader) throws SQLException { String json = reader.getString(); + // pushJson such that we MD5 and store on EntityBeanIntercept later + reader.pushJson(json); if (json == null || json.isEmpty()) { return null; } - // pushJson such that we MD5 and store on EntityBeanIntercept later - reader.pushJson(json); try { return objectReader.readValue(json, deserType); } catch (IOException e) { diff --git a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java index c1cc3c40d..5b58b3108 100644 --- a/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java +++ b/ebean-core/src/test/java/org/tests/json/TestDbJson_Jackson3.java @@ -10,6 +10,7 @@ import io.ebeantest.LoggedSql; import org.junit.Test; import org.tests.model.json.EBasicJsonJackson3; import org.tests.model.json.EBasicJsonList; +import org.tests.model.json.EBasicJsonMulti; import org.tests.model.json.PlainBean; import org.tests.model.json.PlainBeanDirtyAware; @@ -221,6 +222,21 @@ public class TestDbJson_Jackson3 extends BaseTestCase { LoggedSql.stop(); } + + @Test + public void push_pop_test() { + + EBasicJsonMulti bean = new EBasicJsonMulti(); + bean.setPlainValue2(new PlainBeanDirtyAware("x", 42)); + bean.save(); + + bean = DB.find(EBasicJsonMulti.class, bean.getId()); + bean.setPlainValue1(null); // already null + bean.setPlainValue2(null); + bean.setPlainValue3(null); // already null + BeanState state = DB.getBeanState(bean); + assertThat(state.getDirtyValues()).hasSize(1).containsKey("plainValue2"); + } private void expectedSql(int i, String s) { assertThat(LoggedSql.collect().get(i)).contains(s); diff --git a/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java new file mode 100644 index 000000000..23dcd33d9 --- /dev/null +++ b/ebean-core/src/test/java/org/tests/model/json/EBasicJsonMulti.java @@ -0,0 +1,81 @@ +package org.tests.model.json; + +import io.ebean.Model; +import io.ebean.annotation.DbJson; +import io.ebean.annotation.MutationDetection; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; + +import static io.ebean.annotation.MutationDetection.NONE; +import static io.ebean.annotation.MutationDetection.SOURCE; + +@Entity +public class EBasicJsonMulti extends Model { + + @Id + Long id; + + String name; + + @DbJson(length = 500, mutationDetection = SOURCE) + PlainBeanDirtyAware plainValue1; + + @DbJson(length = 500, mutationDetection = SOURCE) + PlainBeanDirtyAware plainValue2; + + @DbJson(length = 500, mutationDetection = SOURCE) + PlainBeanDirtyAware plainValue3; + + @Version + long version; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public PlainBeanDirtyAware getPlainValue1() { + return plainValue1; + } + + public void setPlainValue1(PlainBeanDirtyAware plainValue1) { + this.plainValue1 = plainValue1; + } + + public PlainBeanDirtyAware getPlainValue2() { + return plainValue2; + } + + public void setPlainValue2(PlainBeanDirtyAware plainValue2) { + this.plainValue2 = plainValue2; + } + + public PlainBeanDirtyAware getPlainValue3() { + return plainValue3; + } + + public void setPlainValue3(PlainBeanDirtyAware plainValue3) { + this.plainValue3 = plainValue3; + } + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } +} From 60dfaab22ce4f10539c2c570a8d014d616321efd Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Tue, 10 Aug 2021 15:29:10 +0200 Subject: [PATCH 81/87] Enables select + exists for dynamic formula properties --- .../server/deploy/BeanDescriptor.java | 2 +- .../server/query/SqlTreeBuilder.java | 8 ++--- .../java/org/tests/query/TestQueryAlias.java | 29 +++++++++++++++---- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 8e8b02b6f..055f2605a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -2519,7 +2519,7 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { if (propName.indexOf('(') > -1) { return findSqlTreeFormula(propName, path); } - return _findBeanProperty(propName); + return findProperty(propName); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 239e8a58e..7e165ef57 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -365,11 +365,11 @@ public final class SqlTreeBuilder { * This means it can included individual properties of an embedded bean. *

*/ - private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName) { - STreeProperty p = desc.findProperty(propName); + private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName, String path) { + STreeProperty p = desc.findPropertyWithDynamic(propName, path); if (p == null) { logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it."); - + return; } else if (p instanceof STreePropertyAssoc && p.isEmbedded()) { // if the property is embedded we need to lookup the real column name int pos = propName.indexOf('.'); @@ -383,7 +383,7 @@ public final class SqlTreeBuilder { private void addProperty(SqlTreeProperties selectProps, STreeType desc, OrmQueryProperties queryProps, String propName) { if (subQuery) { - addPropertyToSubQuery(selectProps, desc, propName); + addPropertyToSubQuery(selectProps, desc, propName, queryProps.getPath()); return; } diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java b/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java index de378586c..4e77d49d6 100644 --- a/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java +++ b/ebean-core/src/test/java/org/tests/query/TestQueryAlias.java @@ -1,7 +1,7 @@ package org.tests.query; import io.ebean.BaseTestCase; -import io.ebean.Ebean; +import io.ebean.DB; import io.ebean.Query; import org.junit.Test; import org.tests.model.basic.CKeyParent; @@ -16,11 +16,11 @@ public class TestQueryAlias extends BaseTestCase { ResetBasicData.reset(); - Query sq = Ebean.createQuery(CKeyParent.class) + Query sq = DB.createQuery(CKeyParent.class) .select("id.oneKey").alias("st0") .setAutoTune(false).where().query(); - Query pq = Ebean.find(CKeyParent.class).alias("myt0").where().in("id.oneKey", sq).query(); + Query pq = DB.find(CKeyParent.class).alias("myt0").where().in("id.oneKey", sq).query(); pq.findList(); @@ -36,17 +36,36 @@ public class TestQueryAlias extends BaseTestCase { assertThat(sql).contains("ckey_parent myt0"); assertThat(sql).contains("(myt0.one_key) in (select st0.one_key from ckey_parent st0)"); } + + @Test + public void testExistsWithConcat() { + + ResetBasicData.reset(); + + Query sq = DB.createQuery(CKeyParent.class) + .select("concat(id.oneKey,id.twoKey)").alias("st0") + .setAutoTune(false).where().query(); + + Query pq = DB.find(CKeyParent.class).alias("myt0").where().in("concat(id.oneKey,id.twoKey)", sq).query(); + + pq.findList(); + + String sql = pq.getGeneratedSql(); + + assertThat(sql).contains("ckey_parent myt0"); + assertThat(sql).contains("(concat(myt0.one_key,myt0.two_key)) in (select concat(st0.one_key,st0.two_key) from ckey_parent st0)"); + } @Test public void testNotExists() { ResetBasicData.reset(); - Query sq = Ebean.createQuery(CKeyParent.class) + Query sq = DB.createQuery(CKeyParent.class) .select("id.oneKey").alias("st0") .setAutoTune(false).where().query(); - Query pq = Ebean.find(CKeyParent.class).alias("myt0").where().notIn("id.oneKey", sq).query(); + Query pq = DB.find(CKeyParent.class).alias("myt0").where().notIn("id.oneKey", sq).query(); pq.findList(); From d08f7af1e0e03a3773eee88b82b1698e30ed43d8 Mon Sep 17 00:00:00 2001 From: Thomas Fellner Date: Tue, 10 Aug 2021 18:31:02 +0200 Subject: [PATCH 82/87] update ebean-ddl-generator version in ebean-core --- ebean-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 11aac552a..b99bf98fb 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -72,7 +72,7 @@ io.ebean ebean-ddl-generator - 12.9.4-RC1 + 12.11.0 test From 11e8e3696f7a0c17cefe2a467625f1cb2447d045 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 12:24:36 +1200 Subject: [PATCH 83/87] BindParams does not need encryptionKey in equals/hashCode --- .../src/main/java/io/ebeaninternal/api/BindParams.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index 3a81f2f0a..b3da53833 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -410,8 +410,7 @@ public class BindParams implements Serializable { @Override public int hashCode() { int hc = getClass().hashCode(); - hc = hc * 92821 + (encryptionKey ? 0 : 1); - hc = hc * 92821 + (isInParam ? 0 : 1); +`` hc = hc * 92821 + (isInParam ? 0 : 1); hc = hc * 92821 + (isOutParam ? 0 : 1); hc = hc * 92821 + (type); hc = hc * 92821 + (inValue == null ? 0 : inValue.hashCode()); @@ -423,8 +422,7 @@ public class BindParams implements Serializable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Param param = (Param) o; - return encryptionKey == param.encryptionKey && isInParam == param.isInParam && isOutParam == param.isOutParam - && type == param.type && Objects.equals(inValue, param.inValue); + return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue); } void queryBindHash(BindHash hash) { From fd2e542c0b90433140c41b3434cd0e84873f0643 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 12:43:58 +1200 Subject: [PATCH 84/87] Change BindHash from interface to implementation, Delete unused server persist BindValues --- .../java/io/ebeaninternal/api/BindHash.java | 42 ++++------ .../java/io/ebeaninternal/api/BindParams.java | 2 +- .../server/persist/BindValues.java | 81 ------------------- .../ebeaninternal/server/persist/Binder.java | 27 ------- .../server/querydefn/DefaultOrmQuery.java | 3 +- .../server/querydefn/HashCodeBindHash.java | 63 --------------- .../server/expression/RawExpressionTest.java | 4 +- ...odeBindHashTest.java => BindHashTest.java} | 13 +-- .../server/querydefn/DefaultOrmQueryTest.java | 3 +- 9 files changed, 28 insertions(+), 210 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java rename ebean-core/src/test/java/io/ebeaninternal/server/querydefn/{HashCodeBindHashTest.java => BindHashTest.java} (67%) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java index d70c5f09d..b3ad8e49b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java @@ -1,37 +1,29 @@ package io.ebeaninternal.api; +import java.util.ArrayList; +import java.util.List; + /** * BindHash implementation. - * - * @author Roland Praml, FOCONIS AG - * */ -public interface BindHash { +public class BindHash { - /** - * Update with boolean value. - */ - BindHash update(boolean boolValue); + private final List values = new ArrayList<>(); - /** - * Update with int value. - */ - BindHash update(int intValue); + public BindHash update(Object value) { + values.add(value); + return this; + } - /** - * Update with long value. - */ - BindHash update(long longValue); + @Override + public boolean equals(Object obj) { + return obj instanceof BindHash && ((BindHash) obj).values.equals(values); + } - /** - * Update with object value. - */ - BindHash update(Object value); + @Override + public int hashCode() { + return values.hashCode(); + } - /** - * finishes the hash. May be used to compute internal state. After finish, no - * update method must be called - */ - void finish(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index b3da53833..74e7dfa23 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -410,7 +410,7 @@ public class BindParams implements Serializable { @Override public int hashCode() { int hc = getClass().hashCode(); -`` hc = hc * 92821 + (isInParam ? 0 : 1); + hc = hc * 92821 + (isInParam ? 0 : 1); hc = hc * 92821 + (isOutParam ? 0 : 1); hc = hc * 92821 + (type); hc = hc * 92821 + (inValue == null ? 0 : inValue.hashCode()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java deleted file mode 100644 index 6d71e8d79..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/BindValues.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.ebeaninternal.server.persist; - -import java.util.ArrayList; - -/** - * Holds a list of bind values for binding to a PreparedStatement. - */ -class BindValues { - - private final ArrayList list = new ArrayList<>(); - - /** - * Create with a Binder. - */ - public BindValues() { - } - - /** - * Add a bind value with its JDBC datatype. - * - * @param value the bind value - * @param dbType the type as per java.sql.Types - */ - public void add(Object value, int dbType, String name) { - list.add(new Value(value, dbType, name)); - } - - /** - * List of bind values. - */ - public ArrayList values() { - return list; - } - - /** - * A Value has additionally the JDBC data type. - */ - public static class Value { - - private final Object value; - - private final int dbType; - - private final String name; - - /** - * Create the value. - */ - Value(Object value, int dbType, String name) { - this.value = value; - this.dbType = dbType; - this.name = name; - } - - /** - * Return the type as per java.sql.Types. - */ - public int getDbType() { - return dbType; - } - - /** - * Return the value. - */ - public Object getValue() { - return value; - } - - /** - * Return the property name. - */ - public String getName() { - return name; - } - - @Override - public String toString() { - return String.valueOf(value); - } - } -} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java index 1d94d7a81..600a7e21a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java @@ -77,33 +77,6 @@ public class Binder { return asOfStandardsBased; } - /** - * Bind the values to the Prepared Statement. - */ - public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf) throws SQLException { - String logPrefix = ""; - ArrayList list = bindValues.values(); - for (BindValues.Value bindValue : list) { - Object val = bindValue.getValue(); - int dt = bindValue.getDbType(); - bindObject(dataBind, val, dt); - - if (bindBuf != null) { - bindBuf.append(logPrefix); - if (logPrefix.isEmpty()) { - logPrefix = ", "; - } - bindBuf.append(bindValue.getName()); - bindBuf.append("="); - if (isLob(dt)) { - bindBuf.append("[LOB]"); - } else { - bindBuf.append(val); - } - } - } - } - /** * Bind the parameters to the preparedStatement returning the bind log. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index e4735e32b..865b3184b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1244,9 +1244,8 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - BindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); queryBindHash(hash); - hash.finish(); return new HashQuery(queryPlanKey, hash); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java deleted file mode 100644 index 9bc9c60fc..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/HashCodeBindHash.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.ebeaninternal.server.querydefn; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import io.ebeaninternal.api.BindHash; - -/** - * HashCode builder that uses Object.hashCode for computing bind-hashes. - * This is a fast and lightweight implementation, but may produce collisions. - * - * @author Roland Praml, FOCONIS AG - */ -public class HashCodeBindHash implements BindHash { - - private final List values = new ArrayList<>(); - private int hashCode; - - @Override - public BindHash update(int intValue) { - values.add(intValue); - hashCode = hashCode * 92821 + intValue; - return this; - } - - @Override - public BindHash update(long longValue) { - values.add(longValue); - hashCode = hashCode * 92821 + Long.hashCode(longValue); - return this; - } - - @Override - public BindHash update(boolean boolValue) { - values.add(boolValue); - hashCode = hashCode * 92821 + Boolean.hashCode(boolValue); - return this; - } - - @Override - public BindHash update(Object value) { - values.add(value); - hashCode = hashCode * 92821 + Objects.hashCode(value); - return this; - } - - @Override - public void finish() { - // nothing to do - } - - @Override - public boolean equals(Object obj) { - return obj instanceof HashCodeBindHash && ((HashCodeBindHash) obj).values.equals(values); - } - - @Override - public int hashCode() { - return hashCode; - } - -} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 6c608472f..99c7d7eec 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -4,7 +4,6 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import io.ebeaninternal.api.BindHash; -import io.ebeaninternal.server.querydefn.HashCodeBindHash; public class RawExpressionTest extends BaseExpressionTest { @@ -71,9 +70,8 @@ public class RawExpressionTest extends BaseExpressionTest { } private int getHash(RawExpression query) { - BindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); query.queryBindHash(hash); - hash.finish(); return hash.hashCode(); } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java similarity index 67% rename from ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java rename to ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java index dea0a2e0c..a74f4432f 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/HashCodeBindHashTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java @@ -1,18 +1,19 @@ package io.ebeaninternal.server.querydefn; +import io.ebeaninternal.api.BindHash; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -public class HashCodeBindHashTest { +public class BindHashTest { @Test public void update_with_null() { - HashCodeBindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); hash.update(1).update(null).update("hello"); - HashCodeBindHash hash2 = new HashCodeBindHash(); + BindHash hash2 = new BindHash(); hash2.update(1).update(null).update("hello"); assertThat(hash).isEqualTo(hash2); @@ -21,13 +22,13 @@ public class HashCodeBindHashTest { @Test public void notEqual() { - HashCodeBindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); hash.update(1).update(null).update("hello"); - HashCodeBindHash hash2 = new HashCodeBindHash(); + BindHash hash2 = new BindHash(); hash2.update(1).update("hello"); - HashCodeBindHash hash3 = new HashCodeBindHash(); + BindHash hash3 = new BindHash(); hash2.update(1).update(null); assertThat(hash).isNotEqualTo(hash2); diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index cb1738ba8..999459892 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -113,9 +113,8 @@ public class DefaultOrmQueryTest extends BaseTestCase { } private int getHash(DefaultOrmQuery query) { - BindHash hash = new HashCodeBindHash(); + BindHash hash = new BindHash(); query.queryBindHash(hash); - hash.finish(); return hash.hashCode(); } } From ec7c7048db86a677e85d4d066cd76f77cd1cc23a Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 13:19:44 +1200 Subject: [PATCH 85/87] Refactor rename BindHash to BindValuesKey --- .../java/io/ebeaninternal/api/BindHash.java | 29 -------------- .../java/io/ebeaninternal/api/BindParams.java | 12 +++--- .../io/ebeaninternal/api/BindValuesKey.java | 35 +++++++++++++++++ .../java/io/ebeaninternal/api/HashQuery.java | 12 +++--- .../io/ebeaninternal/api/SpiExpression.java | 2 +- .../java/io/ebeaninternal/api/SpiQuery.java | 2 +- .../expression/AbstractTextExpression.java | 4 +- .../expression/AllEqualsExpression.java | 10 ++--- .../expression/ArrayContainsExpression.java | 8 ++-- .../expression/ArrayIsEmptyExpression.java | 6 +-- .../server/expression/BetweenExpression.java | 6 +-- .../expression/BetweenPropertyExpression.java | 6 +-- .../server/expression/BitwiseExpression.java | 6 +-- .../CaseInsensitiveEqualExpression.java | 6 +-- .../expression/DefaultExampleExpression.java | 10 ++--- .../expression/DefaultExpressionList.java | 10 ++--- .../expression/ExistsQueryExpression.java | 6 +-- .../server/expression/IdExpression.java | 6 +-- .../server/expression/IdInExpression.java | 8 ++-- .../server/expression/InExpression.java | 8 ++-- .../server/expression/InPairsExpression.java | 8 ++-- .../server/expression/InQueryExpression.java | 6 +-- .../server/expression/InRangeExpression.java | 6 +-- .../server/expression/IsEmptyExpression.java | 4 +- .../server/expression/JsonPathExpression.java | 6 +-- .../server/expression/JunctionExpression.java | 6 +-- .../server/expression/LikeExpression.java | 6 +-- .../server/expression/LogicExpression.java | 6 +-- .../expression/NativeILikeExpression.java | 6 +-- .../NestedPathWrapperExpression.java | 6 +-- .../server/expression/NoopExpression.java | 4 +- .../server/expression/NotExpression.java | 6 +-- .../server/expression/NullExpression.java | 6 +-- .../server/expression/RawExpression.java | 8 ++-- .../server/expression/SimpleExpression.java | 6 +-- .../server/querydefn/DefaultOrmQuery.java | 18 ++++----- .../server/expression/RawExpressionTest.java | 14 +++---- .../server/querydefn/BindHashTest.java | 38 ------------------- .../server/querydefn/BindValuesKeyTest.java | 38 +++++++++++++++++++ .../server/querydefn/DefaultOrmQueryTest.java | 16 ++++---- 40 files changed, 204 insertions(+), 202 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java delete mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java create mode 100644 ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java deleted file mode 100644 index b3ad8e49b..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindHash.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.ebeaninternal.api; - -import java.util.ArrayList; -import java.util.List; - -/** - * BindHash implementation. - */ -public class BindHash { - - private final List values = new ArrayList<>(); - - public BindHash update(Object value) { - values.add(value); - return this; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof BindHash && ((BindHash) obj).values.equals(values); - } - - @Override - public int hashCode() { - return values.hashCode(); - } - - -} diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java index 74e7dfa23..64a0a0287 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindParams.java @@ -50,10 +50,10 @@ public class BindParams implements Serializable { positionedParameters.clear(); } - public void queryBindHash(BindHash hash) { - hash.update(positionedParameters.size()); - for (Param positionedParameter : positionedParameters) { - positionedParameter.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + key.add(positionedParameters.size()); + for (Param param : positionedParameters) { + param.queryBindHash(key); } } @@ -425,8 +425,8 @@ public class BindParams implements Serializable { return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue); } - void queryBindHash(BindHash hash) { - hash.update(isInParam).update(isOutParam).update(type).update(inValue); + void queryBindHash(BindValuesKey key) { + key.add(isInParam).add(isOutParam).add(type).add(inValue); } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java new file mode 100644 index 000000000..4a6eac324 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/api/BindValuesKey.java @@ -0,0 +1,35 @@ +package io.ebeaninternal.api; + +import java.util.ArrayList; +import java.util.List; + +/** + * BindValues used for L2 query cache key matching. + *

+ * The equals/hashCode implementation must meet the requirement that the query bind values + * match for L2 query cache hit (given the query plan hash is already a match). + */ +public class BindValuesKey { + + private final List values = new ArrayList<>(); + + /** + * Add a bind value. + */ + public BindValuesKey add(Object value) { + values.add(value); + return this; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof BindValuesKey && ((BindValuesKey) obj).values.equals(values); + } + + @Override + public int hashCode() { + return values.hashCode(); + } + + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java index 87241aedc..faddb7bee 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/HashQuery.java @@ -6,15 +6,14 @@ package io.ebeaninternal.api; public class HashQuery { private final CQueryPlanKey planHash; - - private final BindHash bindHash; + private final BindValuesKey bindValuesKey; /** * Create the HashQuery. */ - public HashQuery(CQueryPlanKey planHash, BindHash bindHash) { + public HashQuery(CQueryPlanKey planHash, BindValuesKey bindValuesKey) { this.planHash = planHash; - this.bindHash = bindHash; + this.bindValuesKey = bindValuesKey; } @Override @@ -25,7 +24,7 @@ public class HashQuery { @Override public int hashCode() { int hc = 92821 * planHash.hashCode(); - hc = 92821 * hc + bindHash.hashCode(); + hc = 92821 * hc + bindValuesKey.hashCode(); return hc; } @@ -37,8 +36,7 @@ public class HashQuery { if (!(obj instanceof HashQuery)) { return false; } - HashQuery e = (HashQuery) obj; - return e.bindHash.equals(bindHash) && e.planHash.equals(planHash); + return e.bindValuesKey.equals(bindValuesKey) && e.planHash.equals(planHash); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java index 32c3a3518..00879e15d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java @@ -56,7 +56,7 @@ public interface SpiExpression extends Expression { /** * Return the hash value for the values that will be bound. */ - void queryBindHash(BindHash hash); + void queryBindHash(BindValuesKey key); /** * Return true if the expression is the same with respect to bind values. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index d8bae7503..2eae4506b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -635,7 +635,7 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod * query). *

*/ - void queryBindHash(BindHash hash); + void queryBindHash(BindValuesKey key); /** * Identifies queries that are exactly the same including bind variables. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java index d377e48dd..2a411675e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -38,7 +38,7 @@ public abstract class AbstractTextExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { // do nothing, only execute against document store }; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java index df9e6095b..be0791a12 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -123,13 +123,11 @@ class AllEqualsExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - - hash.update(propMap.size()); + public void queryBindHash(BindValuesKey key) { + key.add(propMap.size()); for (Object value : propMap.values()) { - hash.update(value); + key.add(value); } - } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index 3c200c046..1ca15f8f7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -50,10 +50,10 @@ public class ArrayContainsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(values.length); + public void queryBindHash(BindValuesKey key) { + key.add(values.length); for (Object value : values) { - hash.update(value); + key.add(value); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java index 0249acabb..5b58be0b4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -34,8 +34,8 @@ public class ArrayIsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(empty); + public void queryBindHash(BindValuesKey key) { + key.add(empty); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java index 37a3aab8f..27e7a5909 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -50,8 +50,8 @@ class BetweenExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(low()).update(high()); + public void queryBindHash(BindValuesKey key) { + key.add(low()).add(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java index 440dd5f1f..cdc199d70 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -96,8 +96,8 @@ class BetweenPropertyExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(val()); + public void queryBindHash(BindValuesKey key) { + key.add(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java index db1fa686d..20119b370 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -40,8 +40,8 @@ class BitwiseExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(flags).update(match); + public void queryBindHash(BindValuesKey key) { + key.add(flags).add(match); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java index 485d1b04f..ecefbe08a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -70,8 +70,8 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(val()); + public void queryBindHash(BindValuesKey key) { + key.add(val()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index 9149923e0..3c0e268b6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -5,7 +5,7 @@ import io.ebean.LikeType; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; import io.ebean.util.SplitName; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -242,10 +242,10 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio * Return a hash for the actual bind values used. */ @Override - public void queryBindHash(BindHash hash) { - hash.update(list.size()); - for (SpiExpression aList : list) { - aList.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + key.add(list.size()); + for (SpiExpression expr : list) { + expr.queryBindHash(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index b5f9c6ec1..6c47467f2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -26,7 +26,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -679,10 +679,10 @@ public class DefaultExpressionList implements SpiExpressionList { * Calculate a hash based on the expressions. */ @Override - public void queryBindHash(BindHash hash) { - hash.update(list.size()); - for (SpiExpression aList : list) { - aList.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + key.add(list.size()); + for (SpiExpression expr : list) { + expr.queryBindHash(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index b3fcf8ce0..9f1d1830d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiEbeanServer; @@ -92,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress } @Override - public void queryBindHash(BindHash hash) { - subQuery.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + subQuery.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java index 904fbe9be..5c20e66b3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -78,8 +78,8 @@ class IdExpression extends NonPrepareExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(value); + public void queryBindHash(BindValuesKey key) { + key.add(value); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java index 390d0bebb..8dbceb419 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -134,10 +134,10 @@ public class IdInExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(idCollection.size()); + public void queryBindHash(BindValuesKey key) { + key.add(idCollection.size()); for (Object elem : idCollection) { - hash.update(elem); + key.add(elem); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java index a5719f71a..bbc2bf7eb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.bean.EntityBean; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -177,10 +177,10 @@ class InExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(bindValues.size()); + public void queryBindHash(BindValuesKey key) { + key.add(bindValues.size()); for (Object bindValue : bindValues) { - hash.update(bindValue); + key.add(bindValue); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java index a7079ae55..6f2aae853 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Pairs; import io.ebean.Pairs.Entry; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -125,10 +125,10 @@ class InPairsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(entries.size()); + public void queryBindHash(BindValuesKey key) { + key.add(entries.size()); for (Pairs.Entry entry : entries) { - hash.update(entry.getA()).update(entry.getB()); + key.add(entry.getA()).add(entry.getB()); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 619f7b715..769f83b84 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -73,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor } @Override - public void queryBindHash(BindHash hash) { - subQuery.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + subQuery.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java index abe0b7338..c8a8dc96f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -48,8 +48,8 @@ class InRangeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(low()).update(high()); + public void queryBindHash(BindValuesKey key) { + key.add(low()).add(high()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java index bb65962b3..4ca943619 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -104,7 +104,7 @@ class IsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java index 18e0aa924..d501068d9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -84,8 +84,8 @@ class JsonPathExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(value).update(upperValue); + public void queryBindHash(BindValuesKey key) { + key.add(value).add(upperValue); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 88614873d..75c17ba19 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -25,7 +25,7 @@ import io.ebean.search.MultiMatch; import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -216,9 +216,9 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { for (SpiExpression expr : exprList.internalList()) { - expr.queryBindHash(hash); + expr.queryBindHash(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java index 587e6064c..355cdfa3c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -71,8 +71,8 @@ class LikeExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(strValue()); + public void queryBindHash(BindValuesKey key) { + key.add(strValue()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java index cbd487b3e..d6f7894e0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.Junction; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -169,8 +169,8 @@ abstract class LogicExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(expOne).update(expTwo); + public void queryBindHash(BindValuesKey key) { + key.add(expOne).add(expTwo); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java index 8c51d2f5d..efb77d37a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.LikeType; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; @@ -55,8 +55,8 @@ class NativeILikeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(val); + public void queryBindHash(BindValuesKey key) { + key.add(val); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java index f3ed1badd..0931486af 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -80,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - delegate.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + delegate.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java index 92180cea1..0a8a67f24 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -75,7 +75,7 @@ class NoopExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { + public void queryBindHash(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java index e9df6d856..4d6a4aba1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.expression; import io.ebean.Expression; import io.ebean.event.BeanQueryRequest; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.NaturalKeyQueryData; import io.ebeaninternal.api.SpiExpression; @@ -100,8 +100,8 @@ final class NotExpression implements SpiExpression { } @Override - public void queryBindHash(BindHash hash) { - exp.queryBindHash(hash); + public void queryBindHash(BindValuesKey key) { + exp.queryBindHash(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java index daded43cb..1ec123fda 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.util.SplitName; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -95,7 +95,7 @@ class NullExpression extends AbstractExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(notNull); + public void queryBindHash(BindValuesKey key) { + key.add(notNull); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java index 721d69b25..080f541b7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java @@ -1,6 +1,6 @@ package io.ebeaninternal.server.expression; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.ManyWhereJoins; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; @@ -74,10 +74,10 @@ class RawExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(values.length); + public void queryBindHash(BindValuesKey key) { + key.add(values.length); for (Object value : values) { - hash.update(value); + key.add(value); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java index e50d9856c..3e0db8707 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java @@ -5,7 +5,7 @@ import io.ebean.plugin.ExpressionPath; import io.ebeaninternal.api.SpiExpression; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.server.el.ElPropertyValue; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.NaturalKeyQueryData; import java.io.IOException; @@ -122,8 +122,8 @@ public class SimpleExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindHash hash) { - hash.update(value()); + public void queryBindHash(BindValuesKey key) { + key.add(value()); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 865b3184b..4508458b8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1225,12 +1225,12 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { *

*/ @Override - public void queryBindHash(BindHash hash) { - hash.update(id); - if (whereExpressions != null) whereExpressions.queryBindHash(hash); - if (havingExpressions != null) havingExpressions.queryBindHash(hash); - if (bindParams != null) bindParams.queryBindHash(hash); - hash.update(asOf).update(versionsStart).update(versionsEnd); + public void queryBindHash(BindValuesKey key) { + key.add(id); + if (whereExpressions != null) whereExpressions.queryBindHash(key); + if (havingExpressions != null) havingExpressions.queryBindHash(key); + if (bindParams != null) bindParams.queryBindHash(key); + key.add(asOf).add(versionsStart).add(versionsEnd); } /** @@ -1244,9 +1244,9 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { public HashQuery queryHash() { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called - BindHash hash = new BindHash(); - queryBindHash(hash); - return new HashQuery(queryPlanKey, hash); + BindValuesKey bindKey = new BindValuesKey(); + queryBindHash(bindKey); + return new HashQuery(queryPlanKey, bindKey); } @Override diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 99c7d7eec..3568a3bf6 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.expression; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; public class RawExpressionTest extends BaseExpressionTest { @@ -62,16 +62,16 @@ public class RawExpressionTest extends BaseExpressionTest { } public void assert_queryBindHash_isDifferent(RawExpression exp0, RawExpression exp1) { - assertThat(getHash(exp0)).isNotEqualTo(getHash(exp1)); + assertThat(bindKey(exp0)).isNotEqualTo(bindKey(exp1)); } public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1) { - assertThat(getHash(exp0)).isEqualTo(getHash(exp1)); + assertThat(bindKey(exp0)).isEqualTo(bindKey(exp1)); } - private int getHash(RawExpression query) { - BindHash hash = new BindHash(); - query.queryBindHash(hash); - return hash.hashCode(); + private BindValuesKey bindKey(RawExpression query) { + BindValuesKey bindValuesKey = new BindValuesKey(); + query.queryBindHash(bindValuesKey); + return bindValuesKey; } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java deleted file mode 100644 index a74f4432f..000000000 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindHashTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.ebeaninternal.server.querydefn; - -import io.ebeaninternal.api.BindHash; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class BindHashTest { - - @Test - public void update_with_null() { - - BindHash hash = new BindHash(); - hash.update(1).update(null).update("hello"); - - BindHash hash2 = new BindHash(); - hash2.update(1).update(null).update("hello"); - - assertThat(hash).isEqualTo(hash2); - } - - @Test - public void notEqual() { - - BindHash hash = new BindHash(); - hash.update(1).update(null).update("hello"); - - BindHash hash2 = new BindHash(); - hash2.update(1).update("hello"); - - BindHash hash3 = new BindHash(); - hash2.update(1).update(null); - - assertThat(hash).isNotEqualTo(hash2); - assertThat(hash).isNotEqualTo(hash3); - assertThat(hash2).isNotEqualTo(hash3); - } -} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java new file mode 100644 index 000000000..1a7d3b39f --- /dev/null +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/BindValuesKeyTest.java @@ -0,0 +1,38 @@ +package io.ebeaninternal.server.querydefn; + +import io.ebeaninternal.api.BindValuesKey; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BindValuesKeyTest { + + @Test + public void update_with_null() { + + BindValuesKey hash = new BindValuesKey(); + hash.add(1).add(null).add("hello"); + + BindValuesKey hash2 = new BindValuesKey(); + hash2.add(1).add(null).add("hello"); + + assertThat(hash).isEqualTo(hash2); + } + + @Test + public void notEqual() { + + BindValuesKey hash = new BindValuesKey(); + hash.add(1).add(null).add("hello"); + + BindValuesKey hash2 = new BindValuesKey(); + hash2.add(1).add("hello"); + + BindValuesKey hash3 = new BindValuesKey(); + hash2.add(1).add(null); + + assertThat(hash).isNotEqualTo(hash2); + assertThat(hash).isNotEqualTo(hash3); + assertThat(hash2).isNotEqualTo(hash3); + } +} diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index 999459892..b3b657c4a 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -4,7 +4,7 @@ package io.ebeaninternal.server.querydefn; import io.ebean.BaseTestCase; import io.ebean.CacheMode; import io.ebean.Ebean; -import io.ebeaninternal.api.BindHash; +import io.ebeaninternal.api.BindValuesKey; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.core.OrmQueryRequest; import org.junit.Test; @@ -63,7 +63,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isNotEqualTo(q2.createQueryPlanKey()); - assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); + assertThat(bindKey(q1)).isNotEqualTo(bindKey(q2)); } @Test @@ -74,7 +74,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(getHash(q1)).isNotEqualTo(getHash(q2)); + assertThat(bindKey(q1)).isNotEqualTo(bindKey(q2)); } @Test @@ -85,7 +85,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { prepare(q1, q2); assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey()); - assertThat(getHash(q1)).isEqualTo(getHash(q2)); + assertThat(bindKey(q1)).isEqualTo(bindKey(q2)); } @Test @@ -112,9 +112,9 @@ public class DefaultOrmQueryTest extends BaseTestCase { q2.prepare(r2); } - private int getHash(DefaultOrmQuery query) { - BindHash hash = new BindHash(); - query.queryBindHash(hash); - return hash.hashCode(); + private BindValuesKey bindKey(DefaultOrmQuery query) { + BindValuesKey key = new BindValuesKey(); + query.queryBindHash(key); + return key; } } From 144a3b54bb51d804712a086a0e1921eceb1faf86 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 13:25:04 +1200 Subject: [PATCH 86/87] Refactor rename method queryBindHash() to queryBindKey() --- .../java/io/ebeaninternal/api/SpiExpression.java | 4 ++-- .../main/java/io/ebeaninternal/api/SpiQuery.java | 8 +++----- .../server/expression/AbstractTextExpression.java | 2 +- .../server/expression/AllEqualsExpression.java | 2 +- .../server/expression/ArrayContainsExpression.java | 2 +- .../server/expression/ArrayIsEmptyExpression.java | 2 +- .../server/expression/BetweenExpression.java | 2 +- .../expression/BetweenPropertyExpression.java | 2 +- .../server/expression/BitwiseExpression.java | 2 +- .../expression/CaseInsensitiveEqualExpression.java | 2 +- .../expression/DefaultExampleExpression.java | 7 ++----- .../server/expression/DefaultExpressionList.java | 7 ++----- .../server/expression/ExistsQueryExpression.java | 4 ++-- .../server/expression/IdExpression.java | 2 +- .../server/expression/IdInExpression.java | 2 +- .../server/expression/InExpression.java | 2 +- .../server/expression/InPairsExpression.java | 2 +- .../server/expression/InQueryExpression.java | 4 ++-- .../server/expression/InRangeExpression.java | 2 +- .../server/expression/IsEmptyExpression.java | 2 +- .../server/expression/JsonPathExpression.java | 2 +- .../server/expression/JunctionExpression.java | 4 ++-- .../server/expression/LikeExpression.java | 2 +- .../server/expression/LogicExpression.java | 2 +- .../server/expression/NativeILikeExpression.java | 2 +- .../expression/NestedPathWrapperExpression.java | 4 ++-- .../server/expression/NoopExpression.java | 2 +- .../server/expression/NotExpression.java | 4 ++-- .../server/expression/NullExpression.java | 2 +- .../server/expression/RawExpression.java | 2 +- .../server/expression/SimpleExpression.java | 2 +- .../server/querydefn/DefaultOrmQuery.java | 14 ++++---------- .../server/expression/RawExpressionTest.java | 2 +- .../server/querydefn/DefaultOrmQueryTest.java | 2 +- 34 files changed, 47 insertions(+), 61 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java index 00879e15d..8fe986259 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiExpression.java @@ -54,9 +54,9 @@ public interface SpiExpression extends Expression { void queryPlanHash(StringBuilder builder); /** - * Return the hash value for the values that will be bound. + * Build the key for bind values of the query. */ - void queryBindHash(BindValuesKey key); + void queryBindKey(BindValuesKey key); /** * Return true if the expression is the same with respect to bind values. diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index 2eae4506b..5ba0b6eeb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -629,13 +629,11 @@ public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCod CQueryPlanKey prepare(SpiOrmQueryRequest request); /** - * Calculate a hash based on the bind values used in the query. + * Build the key for the bind values used in the query (for l2 query cache). *

- * Combined with queryPlanHash() to return getQueryHash (a unique hash for a - * query). - *

+ * Combined with queryPlanHash() to return queryHash (a unique key for a query). */ - void queryBindHash(BindValuesKey key); + void queryBindKey(BindValuesKey key); /** * Identifies queries that are exactly the same including bind variables. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java index 2a411675e..363bcb08d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AbstractTextExpression.java @@ -38,7 +38,7 @@ public abstract class AbstractTextExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { // do nothing, only execute against document store }; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java index be0791a12..56761dcf2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/AllEqualsExpression.java @@ -123,7 +123,7 @@ class AllEqualsExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(propMap.size()); for (Object value : propMap.values()) { key.add(value); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java index 1ca15f8f7..7a9e903f6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayContainsExpression.java @@ -50,7 +50,7 @@ public class ArrayContainsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(values.length); for (Object value : values) { key.add(value); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java index 5b58be0b4..f9cd530c8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ArrayIsEmptyExpression.java @@ -34,7 +34,7 @@ public class ArrayIsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(empty); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java index 27e7a5909..ed3d58d8b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenExpression.java @@ -50,7 +50,7 @@ class BetweenExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(low()).add(high()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java index cdc199d70..55b08380d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BetweenPropertyExpression.java @@ -96,7 +96,7 @@ class BetweenPropertyExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(val()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java index 20119b370..460af37ec 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/BitwiseExpression.java @@ -40,7 +40,7 @@ class BitwiseExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(flags).add(match); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java index ecefbe08a..3a6224d9c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/CaseInsensitiveEqualExpression.java @@ -70,7 +70,7 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(val()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index 3c0e268b6..e6353edd0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -238,14 +238,11 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio builder.append("]"); } - /** - * Return a hash for the actual bind values used. - */ @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(list.size()); for (SpiExpression expr : list) { - expr.queryBindHash(key); + expr.queryBindKey(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 6c47467f2..97899f091 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -675,14 +675,11 @@ public class DefaultExpressionList implements SpiExpressionList { builder.append("]"); } - /** - * Calculate a hash based on the expressions. - */ @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(list.size()); for (SpiExpression expr : list) { - expr.queryBindHash(key); + expr.queryBindKey(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java index 9f1d1830d..4ba939066 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/ExistsQueryExpression.java @@ -92,8 +92,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress } @Override - public void queryBindHash(BindValuesKey key) { - subQuery.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + subQuery.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java index 5c20e66b3..94cab6de7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdExpression.java @@ -78,7 +78,7 @@ class IdExpression extends NonPrepareExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java index 8dbceb419..edf56b443 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IdInExpression.java @@ -134,7 +134,7 @@ public class IdInExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(idCollection.size()); for (Object elem : idCollection) { key.add(elem); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java index bbc2bf7eb..0bc5a4588 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -177,7 +177,7 @@ class InExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(bindValues.size()); for (Object bindValue : bindValues) { key.add(bindValue); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java index 6f2aae853..e5bd27413 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InPairsExpression.java @@ -125,7 +125,7 @@ class InPairsExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(entries.size()); for (Pairs.Entry entry : entries) { key.add(entry.getA()).add(entry.getB()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java index 769f83b84..48647d0c2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InQueryExpression.java @@ -73,8 +73,8 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor } @Override - public void queryBindHash(BindValuesKey key) { - subQuery.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + subQuery.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java index c8a8dc96f..04b242ba8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InRangeExpression.java @@ -48,7 +48,7 @@ class InRangeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(low()).add(high()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java index 4ca943619..d6b878bfa 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/IsEmptyExpression.java @@ -104,7 +104,7 @@ class IsEmptyExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java index d501068d9..1d954dcc1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JsonPathExpression.java @@ -84,7 +84,7 @@ class JsonPathExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(value).add(upperValue); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 75c17ba19..a253bbb2b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -216,9 +216,9 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { for (SpiExpression expr : exprList.internalList()) { - expr.queryBindHash(key); + expr.queryBindKey(key); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java index 355cdfa3c..f40501929 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LikeExpression.java @@ -71,7 +71,7 @@ class LikeExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(strValue()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java index d6f7894e0..a66e2e88a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/LogicExpression.java @@ -169,7 +169,7 @@ abstract class LogicExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(expOne).add(expTwo); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java index efb77d37a..f64933df5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NativeILikeExpression.java @@ -55,7 +55,7 @@ class NativeILikeExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(val); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java index 0931486af..b76bfef4d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NestedPathWrapperExpression.java @@ -80,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { - delegate.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + delegate.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java index 0a8a67f24..6c8d709e7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NoopExpression.java @@ -75,7 +75,7 @@ class NoopExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { // no bind values } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java index 4d6a4aba1..462e60d9e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NotExpression.java @@ -100,8 +100,8 @@ final class NotExpression implements SpiExpression { } @Override - public void queryBindHash(BindValuesKey key) { - exp.queryBindHash(key); + public void queryBindKey(BindValuesKey key) { + exp.queryBindKey(key); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java index 1ec123fda..2808acce7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/NullExpression.java @@ -95,7 +95,7 @@ class NullExpression extends AbstractExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(notNull); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java index 080f541b7..9e1c3e8dc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/RawExpression.java @@ -74,7 +74,7 @@ class RawExpression extends NonPrepareExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(values.length); for (Object value : values) { key.add(value); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java index 3e0db8707..bc23b84f2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/SimpleExpression.java @@ -122,7 +122,7 @@ public class SimpleExpression extends AbstractValueExpression { } @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(value()); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 4508458b8..834abb856 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1218,17 +1218,11 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { } } - /** - * Calculate a hash based on the bind values used in the query. - *

- * Used with queryPlanHash() to get a unique hash for a query. - *

- */ @Override - public void queryBindHash(BindValuesKey key) { + public void queryBindKey(BindValuesKey key) { key.add(id); - if (whereExpressions != null) whereExpressions.queryBindHash(key); - if (havingExpressions != null) havingExpressions.queryBindHash(key); + if (whereExpressions != null) whereExpressions.queryBindKey(key); + if (havingExpressions != null) havingExpressions.queryBindKey(key); if (bindParams != null) bindParams.queryBindHash(key); key.add(asOf).add(versionsStart).add(versionsEnd); } @@ -1245,7 +1239,7 @@ public class DefaultOrmQuery extends AbstractQuery implements SpiQuery { // calculateQueryPlanHash is called just after potential AutoTune tuning // so queryPlanHash is calculated well before this method is called BindValuesKey bindKey = new BindValuesKey(); - queryBindHash(bindKey); + queryBindKey(bindKey); return new HashQuery(queryPlanKey, bindKey); } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java index 3568a3bf6..eb12ff460 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/expression/RawExpressionTest.java @@ -71,7 +71,7 @@ public class RawExpressionTest extends BaseExpressionTest { private BindValuesKey bindKey(RawExpression query) { BindValuesKey bindValuesKey = new BindValuesKey(); - query.queryBindHash(bindValuesKey); + query.queryBindKey(bindValuesKey); return bindValuesKey; } } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java index b3b657c4a..b2b4902ad 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/DefaultOrmQueryTest.java @@ -114,7 +114,7 @@ public class DefaultOrmQueryTest extends BaseTestCase { private BindValuesKey bindKey(DefaultOrmQuery query) { BindValuesKey key = new BindValuesKey(); - query.queryBindHash(key); + query.queryBindKey(key); return key; } } From c267366578b7877313f87569b503e77f331a6d8b Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 11 Aug 2021 15:32:54 +1200 Subject: [PATCH 87/87] Refactor tidy internals - aList -> expr in for loops etc. No functional change. --- .../io/ebeaninternal/api/LoadBeanRequest.java | 13 +------ .../server/core/DefaultServer.java | 12 +++--- .../deploy/meta/DeployBeanDescriptor.java | 6 +-- .../expression/DefaultExampleExpression.java | 16 +++----- .../expression/DefaultExpressionList.java | 38 ++++++++----------- .../server/expression/JunctionExpression.java | 5 +-- 6 files changed, 33 insertions(+), 57 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java b/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java index fef666a63..9d72463a5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/LoadBeanRequest.java @@ -92,9 +92,7 @@ public class LoadBeanRequest extends LoadRequest { * Return the list of Id values for the beans in the lazy load buffer. */ public List getIdList() { - List idList = new ArrayList<>(); - BeanDescriptor desc = loadBuffer.getBeanDescriptor(); for (EntityBeanIntercept ebi : batch) { idList.add(desc.getId(ebi.getOwner())); @@ -106,10 +104,8 @@ public class LoadBeanRequest extends LoadRequest { * Configure the query for lazy loading execution. */ public void configureQuery(SpiQuery query, List idList) { - query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN); query.setPersistenceContext(loadBuffer.getPersistenceContext()); - String mode = isLazy() ? "+lazy" : "+query"; query.setLoadDescription(mode, getDescription()); @@ -117,9 +113,7 @@ public class LoadBeanRequest extends LoadRequest { // cascade the batch size (if set) for further lazy loading query.setLazyLoadBatchSize(getBatchSize()); } - loadBuffer.configureQuery(query, lazyLoadProperty); - if (idList.size() == 1) { query.where().idEq(idList.get(0)); } else { @@ -131,19 +125,16 @@ public class LoadBeanRequest extends LoadRequest { * Load the beans into the L2 cache if that is requested and check for load failures due to deletes. */ public void postLoad(List list) { - Set loadedIds = new HashSet<>(); - BeanDescriptor desc = loadBuffer.getBeanDescriptor(); // collect Ids and maybe load bean cache - for (Object aList : list) { - EntityBean loadedBean = (EntityBean) aList; + for (Object bean : list) { + EntityBean loadedBean = (EntityBean) bean; loadedIds.add(desc.getId(loadedBean)); } if (isLoadCache()) { desc.cacheBeanPutAll(list); } - if (lazyLoadProperty != null) { for (EntityBeanIntercept ebi : batch) { // check if the underlying row in DB was deleted. Mark the bean as 'failed' if diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 2040e3f78..4f0eed00b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -2055,17 +2055,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return transactionManager; } - public void register(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (BeanDescriptor aList : list) { - aList.register(c); + public void register(BeanPersistController controller) { + for (BeanDescriptor desc : beanDescriptorManager.getBeanDescriptorList()) { + desc.register(controller); } } public void deregister(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (BeanDescriptor aList : list) { - aList.deregister(c); + for (BeanDescriptor desc : beanDescriptorManager.getBeanDescriptorList()) { + desc.deregister(c); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 79ca0beb1..f0184948e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -702,13 +702,11 @@ public class DeployBeanDescriptor { } public void sortProperties() { - ArrayList list = new ArrayList<>(propMap.values()); list.sort(PROP_ORDER); - propMap = new LinkedHashMap<>(list.size()); - for (DeployBeanProperty aList : list) { - addBeanProperty(aList); + for (DeployBeanProperty property : list) { + addBeanProperty(property); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java index e6353edd0..cd8a59857 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExampleExpression.java @@ -137,10 +137,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio @Override public void containsMany(BeanDescriptor desc, ManyWhereJoins whereManyJoins) { list = buildExpressions(desc); - if (list != null) { - for (SpiExpression aList : list) { - aList.containsMany(desc, whereManyJoins); - } + for (SpiExpression expr : list) { + expr.containsMany(desc, whereManyJoins); } } @@ -187,8 +185,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio @Override public void validate(SpiExpressionValidation validation) { - for (SpiExpression aList : list) { - aList.validate(validation); + for (SpiExpression expr : list) { + expr.validate(validation); } } @@ -229,10 +227,9 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio */ @Override public void queryPlanHash(StringBuilder builder) { - builder.append("Example["); - for (SpiExpression aList : list) { - aList.queryPlanHash(builder); + for (SpiExpression expr : list) { + expr.queryPlanHash(builder); builder.append(","); } builder.append("]"); @@ -264,7 +261,6 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio * Build the List of expressions. */ private ArrayList buildExpressions(BeanDescriptor beanDescriptor) { - ArrayList list = new ArrayList<>(); addExpressions(list, beanDescriptor, entity, null); return list; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 97899f091..ed3f57e14 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -110,10 +110,8 @@ public class DefaultExpressionList implements SpiExpressionList { * @return A single SpiExpression that has the nestedPath set */ SpiExpression wrap(List list, String nestedPath, Junction.Type type) { - DefaultExpressionList wrapper = new DefaultExpressionList<>(query, expr, null, list, false); wrapper.setAllDocNested(nestedPath); - if (type != null) { return new JunctionExpression<>(type, wrapper); } else { @@ -122,15 +120,15 @@ public class DefaultExpressionList implements SpiExpressionList { } void simplifyEntries() { - for (SpiExpression element : list) { - element.simplify(); + for (SpiExpression expr : list) { + expr.simplify(); } } @Override public void prefixProperty(String path) { - for (SpiExpression exp : list) { - exp.prefixProperty(path); + for (SpiExpression expr : list) { + expr.prefixProperty(path); } } @@ -175,7 +173,6 @@ public class DefaultExpressionList implements SpiExpressionList { context.startNested(allDocNestedPath); } int size = list.size(); - SpiExpression first = list.get(0); boolean explicitBool = first instanceof SpiJunction; boolean implicitBool = !explicitBool && size > 1; @@ -211,7 +208,6 @@ public class DefaultExpressionList implements SpiExpressionList { @Override public void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException { - if (allDocNestedPath != null) { context.startNested(allDocNestedPath); } @@ -228,8 +224,8 @@ public class DefaultExpressionList implements SpiExpressionList { if (idEquals != null) { idEquals.writeDocQuery(context); } - for (SpiExpression aList : list) { - aList.writeDocQuery(context); + for (SpiExpression expr : list) { + expr.writeDocQuery(context); } context.endBool(); } @@ -279,16 +275,15 @@ public class DefaultExpressionList implements SpiExpressionList { */ @Override public void containsMany(BeanDescriptor desc, ManyWhereJoins whereManyJoins) { - - for (SpiExpression aList : list) { - aList.containsMany(desc, whereManyJoins); + for (SpiExpression expr : list) { + expr.containsMany(desc, whereManyJoins); } } @Override public void validate(SpiExpressionValidation validation) { - for (SpiExpression aList : list) { - aList.validate(validation); + for (SpiExpression expr : list) { + expr.validate(validation); } } @@ -631,7 +626,6 @@ public class DefaultExpressionList implements SpiExpressionList { @Override public void addSql(SpiExpressionRequest request) { - for (int i = 0, size = list.size(); i < size; i++) { SpiExpression expression = list.get(i); if (i > 0) { @@ -643,15 +637,15 @@ public class DefaultExpressionList implements SpiExpressionList { @Override public void addBindValues(SpiExpressionRequest request) { - for (SpiExpression aList : list) { - aList.addBindValues(request); + for (SpiExpression expr : list) { + expr.addBindValues(request); } } @Override public void prepareExpression(BeanQueryRequest request) { - for (SpiExpression aList : list) { - aList.prepareExpression(request); + for (SpiExpression expr : list) { + expr.prepareExpression(request); } } @@ -668,8 +662,8 @@ public class DefaultExpressionList implements SpiExpressionList { if (allDocNestedPath != null) { builder.append("path:").append(allDocNestedPath).append(" "); } - for (SpiExpression aList : list) { - aList.queryPlanHash(builder); + for (SpiExpression expr : list) { + expr.queryPlanHash(builder); builder.append(","); } builder.append("]"); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index a253bbb2b..5637fba7f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -207,9 +207,8 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression @Override public void queryPlanHash(StringBuilder builder) { builder.append(type).append("["); - List list = exprList.internalList(); - for (SpiExpression aList : list) { - aList.queryPlanHash(builder); + for (SpiExpression expr : exprList.internalList()) { + expr.queryPlanHash(builder); builder.append(","); } builder.append("]");