From b581c893ecc595283bde87bb3d96d1ba6b0ea3ff Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 7 Nov 2016 19:47:34 +1300 Subject: [PATCH 1/5] No effective change - tidy up test conversation User entity bean --- .../avaje/tests/model/converstation/User.java | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/src/test/java/com/avaje/tests/model/converstation/User.java b/src/test/java/com/avaje/tests/model/converstation/User.java index 350fa74bc..d0c99f028 100644 --- a/src/test/java/com/avaje/tests/model/converstation/User.java +++ b/src/test/java/com/avaje/tests/model/converstation/User.java @@ -1,25 +1,22 @@ package com.avaje.tests.model.converstation; +import com.avaje.ebean.annotation.History; +import com.avaje.ebean.annotation.HistoryExclude; +import com.avaje.tests.model.BaseModel; + import javax.persistence.Entity; import javax.persistence.ManyToOne; import javax.persistence.Table; -import com.avaje.ebean.annotation.History; -import com.avaje.ebean.annotation.HistoryExclude; -import com.avaje.ebean.annotation.WhenModified; -import com.avaje.tests.model.BaseModel; - -import java.sql.Timestamp; - @History @Entity -@Table(name="c_user") +@Table(name = "c_user") public class User extends BaseModel { boolean inactive; - + String name; - + String email; @HistoryExclude @@ -28,12 +25,9 @@ public class User extends BaseModel { @ManyToOne Group group; - @WhenModified - Timestamp whenModified; - public User() { } - + public boolean isInactive() { return inactive; } @@ -74,13 +68,4 @@ public class User extends BaseModel { this.passwordHash = passwordHash; } - @Override - public Timestamp getWhenModified() { - return whenModified; - } - - @Override - public void setWhenModified(Timestamp whenModified) { - this.whenModified = whenModified; - } } From d05b59343235c419785e5c8f9ae76d65a55e2f4f Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 7 Nov 2016 19:50:05 +1300 Subject: [PATCH 2/5] #862 - ENH: Change ServerConfig such that if H2 is the DataSource/DatabasePlatform then automatically it sets up for testing (ddl generation) --- .../com/avaje/ebean/config/ServerConfig.java | 34 +++++++++++++++++++ .../server/core/DefaultContainer.java | 2 +- ...nServerFactory_ServerConfigStart_Test.java | 1 + .../avaje/ebean/config/ServerConfigTest.java | 2 ++ .../build/ModelBuild_compoundKeyTest.java | 1 + .../ModelBuild_explicitSequencesTest.java | 1 + .../event/BeanPersistControllerTest.java | 1 + .../avaje/ebean/event/BeanPostLoadTest.java | 1 + .../transaction/TestAutoCommitDataSource.java | 1 + src/test/resources/ebean.properties | 4 +-- src/test/resources/logback-test.xml | 8 ++--- 11 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 14e2e6085..d750c654b 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -77,6 +77,12 @@ public class ServerConfig { */ private String name = "db"; + /** + * When false (default) H2 automatically uses DDL generate and run + * (i.e. assumes we are running tests using in memory h2). + */ + private boolean h2ProductionMode; + /** * Typically configuration type objects that are passed by this ServerConfig * to plugins. For example - IgniteConfiguration passed to Ignite plugin. @@ -509,6 +515,27 @@ public class ServerConfig { this.name = name; } + /** + * Return true if H2 should be used in production mode. + *

+ * Otherwise it is assumed we are using H2 for testing and DDL generate and run is turned on. + *

+ */ + public boolean isH2ProductionMode() { + return h2ProductionMode; + } + + /** + * Set to true for H2 to be used in production mode. + *

+ * Do this when we want to use H2 and not have the DDL generation and run automatically turned on. + * Otherwise it is assumed we are using H2 for testing purposes. + *

+ */ + public void setH2ProductionMode(boolean h2ProductionMode) { + this.h2ProductionMode = h2ProductionMode; + } + /** * Return the container / clustering configuration. *

@@ -1444,6 +1471,12 @@ public class ServerConfig { */ public void setDatabasePlatform(DatabasePlatform databasePlatform) { this.databasePlatform = databasePlatform; + if (!h2ProductionMode && databasePlatform != null && databasePlatform.isPlatform(Platform.H2)) { + // we are using H2 to run tests so turn on DDL generation and run + this.ddlGenerate = true; + this.ddlRun = true; + this.ddlCreateOnly = true; + } } /** @@ -2460,6 +2493,7 @@ public class ServerConfig { jsonDateTime = JsonConfig.DateTime.MILLIS; } + h2ProductionMode = p.getBoolean("h2ProductionMode", h2ProductionMode); ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate); ddlRun = p.getBoolean("ddl.run", ddlRun); ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java index 009513583..38e781d70 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -240,7 +240,7 @@ public class DefaultContainer implements SpiContainer { DatabasePlatform db = factory.create(config); db.configure(config); config.setDatabasePlatform(db); - logger.info("DatabasePlatform name:" + config.getName() + " platform:" + db.getName()); + logger.info("DatabasePlatform name:{} platform:{}", config.getName(), db.getName()); } } diff --git a/src/test/java/com/avaje/ebean/EbeanServerFactory_ServerConfigStart_Test.java b/src/test/java/com/avaje/ebean/EbeanServerFactory_ServerConfigStart_Test.java index 3f9bdbed5..e30a39682 100644 --- a/src/test/java/com/avaje/ebean/EbeanServerFactory_ServerConfigStart_Test.java +++ b/src/test/java/com/avaje/ebean/EbeanServerFactory_ServerConfigStart_Test.java @@ -18,6 +18,7 @@ public class EbeanServerFactory_ServerConfigStart_Test { config.setName("h2"); config.loadFromProperties(); config.setName("h2other"); + config.setH2ProductionMode(true); config.setDdlGenerate(false); config.setDdlRun(false); config.setDefaultServer(false); diff --git a/src/test/java/com/avaje/ebean/config/ServerConfigTest.java b/src/test/java/com/avaje/ebean/config/ServerConfigTest.java index 202caca0f..01c1b4029 100644 --- a/src/test/java/com/avaje/ebean/config/ServerConfigTest.java +++ b/src/test/java/com/avaje/ebean/config/ServerConfigTest.java @@ -34,9 +34,11 @@ public class ServerConfigTest { props.setProperty("jdbcFetchSizeFindList", "43"); props.setProperty("backgroundExecutorShutdownSecs", "98"); props.setProperty("backgroundExecutorSchedulePoolSize", "4"); + props.setProperty("h2ProductionMode", "true"); serverConfig.loadFromProperties(props); + assertTrue(serverConfig.isH2ProductionMode()); assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatch()); assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatchOnCascade()); assertEquals(ServerConfig.DbUuid.BINARY, serverConfig.getDbUuid()); diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java index 9a08860ef..9e5452c67 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -31,6 +31,7 @@ public class ModelBuild_compoundKeyTest extends BaseTestCase { config.setName("h2"); config.loadFromProperties(); config.setName("h2other"); + config.setH2ProductionMode(true); config.setDdlGenerate(false); config.setDdlRun(false); config.setDefaultServer(false); diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_explicitSequencesTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_explicitSequencesTest.java index 49fc3ada9..fa3fe09e7 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_explicitSequencesTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_explicitSequencesTest.java @@ -25,6 +25,7 @@ public class ModelBuild_explicitSequencesTest extends BaseTestCase { config.setName("h2"); config.loadFromProperties(); config.setName("h2other"); + config.setH2ProductionMode(true); config.setDdlGenerate(false); config.setDdlRun(false); config.setDefaultServer(false); diff --git a/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java b/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java index 761634eb5..c1a6b02f6 100644 --- a/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java +++ b/src/test/java/com/avaje/ebean/event/BeanPersistControllerTest.java @@ -73,6 +73,7 @@ public class BeanPersistControllerTest { ServerConfig config = new ServerConfig(); config.setName("h2ebasicver"); + config.setH2ProductionMode(true); config.loadFromProperties(); config.setDdlGenerate(true); config.setDdlRun(true); diff --git a/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java b/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java index 1dabfa7d1..7cbe11ee0 100644 --- a/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java +++ b/src/test/java/com/avaje/ebean/event/BeanPostLoadTest.java @@ -52,6 +52,7 @@ public class BeanPostLoadTest extends BaseTestCase { config.setName("h2ebasicver"); config.loadFromProperties(); + config.setH2ProductionMode(true); config.setDdlGenerate(true); config.setDdlRun(true); diff --git a/src/test/java/com/avaje/tests/transaction/TestAutoCommitDataSource.java b/src/test/java/com/avaje/tests/transaction/TestAutoCommitDataSource.java index 7365b352a..f03d17d49 100644 --- a/src/test/java/com/avaje/tests/transaction/TestAutoCommitDataSource.java +++ b/src/test/java/com/avaje/tests/transaction/TestAutoCommitDataSource.java @@ -43,6 +43,7 @@ public class TestAutoCommitDataSource extends BaseTestCase { ServerConfig config = new ServerConfig(); config.setName("h2autocommit"); + config.setH2ProductionMode(true); config.loadFromProperties(); config.setDataSource(pool); config.setDefaultServer(false); diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 8eb6e2842..8ca08996f 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -15,8 +15,8 @@ ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager #ebean.autotune.profiling=true #ebean.autotune.profilingUpdateFrequency=5 -ebean.ddl.generate=true -ebean.ddl.run=true +#ebean.ddl.generate=true +#ebean.ddl.run=true datasource.default=h2 #ebean.persistBatch=NONE diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 44bfd40d3..ecacef46d 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -78,10 +78,10 @@ - - - - + + + + From 686593e9e87e36611ea54cf7c30aed833b1aa3e4 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 7 Nov 2016 21:36:19 +1300 Subject: [PATCH 3/5] No effective change - tidy up test Order entity bean --- src/test/java/com/avaje/tests/model/basic/Order.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/java/com/avaje/tests/model/basic/Order.java b/src/test/java/com/avaje/tests/model/basic/Order.java index 13b91815f..8db2097d4 100644 --- a/src/test/java/com/avaje/tests/model/basic/Order.java +++ b/src/test/java/com/avaje/tests/model/basic/Order.java @@ -47,11 +47,6 @@ public class Order implements Serializable { COMPLETE } - public Order() { - - } - - @Id Integer id; From 7a8d1b85a9b941ae1fbe468caeefa914bed6c8a0 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 7 Nov 2016 21:57:03 +1300 Subject: [PATCH 4/5] #866 - Fix RawSql mapping such that it automatically maps foreign key columns --- .../server/query/CQueryBuilder.java | 16 ++++- .../server/query/CQueryPlanRawSql.java | 16 ++++- .../tests/rawsql/TestRawSqlMasterDetail.java | 69 ++++++++++++++++++- 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java index e83847952..fa039444b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java @@ -363,8 +363,15 @@ class CQueryBuilder { RawSql.ColumnMapping.Column column = it.next(); String propertyName = column.getPropertyName(); if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) { - ElPropertyValue el = descriptor.getElGetValue(propertyName); + if (el == null && propertyName.endsWith("Id")) { + // try default naming convention for foreign key columns + String foreignIdPath = assocOneIdPath(propertyName); + el = descriptor.getElGetValue(foreignIdPath); + if (el != null) { + propertyName = foreignIdPath; + } + } if (el == null) { throw new PersistenceException("Property [" + propertyName + "] not found on " + descriptor.getFullName()); } else { @@ -404,6 +411,13 @@ class CQueryBuilder { return new SqlTreeBuilder(request, predicates, detail, rawNoId).build(); } + /** + * Return a path for a foreign key property using the default naming convention. + */ + private String assocOneIdPath(String propertyName) { + return propertyName.substring(0, propertyName.length() - 2) + ".id"; + } + /** * Return the SQL response with row limiting (when not an update statement). */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java index 11ce4a076..2f0ec4a51 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java @@ -35,7 +35,13 @@ class CQueryPlanRawSql extends CQueryPlan { // set the resultSet index positions for the property expressions for (int i = 0; i < chain.size(); i++) { - indexPositions[i] = 1 + columnMapping.getIndexPosition(chain.get(i)); + String logicalPropertyPath = chain.get(i); + int mappedPosition = columnMapping.getIndexPosition(logicalPropertyPath); + if (mappedPosition == -1 && logicalPropertyPath.endsWith(".id")) { + // try a automatically mapped foreign key + mappedPosition = columnMapping.getIndexPosition(foreignKeyPath(logicalPropertyPath)); + } + indexPositions[i] = 1 + mappedPosition; } // check and handle the case where a discriminator column for @@ -51,4 +57,12 @@ class CQueryPlanRawSql extends CQueryPlan { return indexPositions; } + + /** + * Return the path for a foreign key column that was automatically mapped. + */ + private String foreignKeyPath(String logicalPropertyPath) { + // trim the .id and replace with Id ... to reverse the auto fk mapping earlier + return logicalPropertyPath.substring(0, logicalPropertyPath.length() - 3) + "Id"; + } } diff --git a/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java b/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java index 801fa3f80..1a1e7639f 100644 --- a/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java +++ b/src/test/java/com/avaje/tests/rawsql/TestRawSqlMasterDetail.java @@ -4,6 +4,7 @@ import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.RawSql; import com.avaje.ebean.RawSqlBuilder; +import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.EBasic; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.OrderDetail; @@ -13,7 +14,7 @@ import org.junit.Test; import java.util.List; import java.util.Random; -import static org.assertj.core.api.StrictAssertions.assertThat; +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; @@ -54,6 +55,72 @@ public class TestRawSqlMasterDetail extends BaseTestCase { } + @Test + public void testForeignKeyColumn() { + + ResetBasicData.reset(); + + // billing_address_id fk column is automatically + // mapped to the logical path: billingAddress.id + + String rs = "select c.id, c.name, c.billing_address_id, c.updtime "+ + "from o_customer c " + + "order by c.id"; + + RawSql rawSql = RawSqlBuilder.parse(rs).create(); + + List customers = Ebean.find(Customer.class) + .setRawSql(rawSql) + .findList(); + + assertThat(customers).isNotEmpty(); + } + + @Test + public void testSimpleNestedColumn() { + + ResetBasicData.reset(); + + + String rs = "select c.id, c.name, c.billing_address_id, ba.line_1, ba.city, c.updtime "+ + "from o_customer c " + + " left join o_address ba on ba.id = c.billing_address_id "+ + "order by c.id"; + + RawSql rawSql = RawSqlBuilder.parse(rs) + .tableAliasMapping("ba", "billingAddress") + .create(); + + List customers = Ebean.find(Customer.class) + .setRawSql(rawSql) + .findList(); + + assertThat(customers).isNotEmpty(); + } + + @Test + public void testDoubleJoinColumn() { + + ResetBasicData.reset(); + + String rs = "select c.id, c.name, c.billing_address_id, ba.line_1, ba.city, c.updtime, sa.id, sa.line_1, sa.city "+ + "from o_customer c " + + " left join o_address ba on ba.id = c.billing_address_id "+ + " left join o_address sa on sa.id = c.shipping_address_id "+ + "order by c.id"; + + RawSql rawSql = RawSqlBuilder.parse(rs) + .tableAliasMapping("ba", "billingAddress") + .tableAliasMapping("sa", "shippingAddress") + .create(); + + List customers = Ebean.find(Customer.class) + .setRawSql(rawSql) + .findList(); + + assertThat(customers).isNotEmpty(); + } + @Test public void testWithTableAliasMapping() { From 08939721152b7e1701c077797ef9da4c832c88ea Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 7 Nov 2016 22:00:33 +1300 Subject: [PATCH 5/5] Fix for running CI tests as Postgres (where we need to explicitly turn on DDL generation and running for the tests). --- src/test/resources/ebean.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 8ca08996f..8eb6e2842 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -15,8 +15,8 @@ ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager #ebean.autotune.profiling=true #ebean.autotune.profilingUpdateFrequency=5 -#ebean.ddl.generate=true -#ebean.ddl.run=true +ebean.ddl.generate=true +ebean.ddl.run=true datasource.default=h2 #ebean.persistBatch=NONE