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/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/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/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; 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; - } } 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