Compare commits

..
Author SHA1 Message Date
Robin Bygrave 9ea58572de [maven-release-plugin] prepare release avaje-ebeanorm-7.14.1 2016-06-17 22:27:04 +12:00
Robin Bygrave 35d82c9104 Bump pom to 7.14.1-SNAPSHOT 2016-06-17 22:26:06 +12:00
Robin Bygrave 31aae38616 #742 - ENH: DocStoreConfig ... read System properties for isGenerateMapping(), isDropCreate() and isCreate() 2016-06-17 22:25:11 +12:00
Robin Bygrave f144b53651 #741 - ENH: Add DocumentStore indexSettings() method ... for setting ElasticSearch refresh_interval 2016-06-17 22:21:22 +12:00
Robin Bygrave f47cd7e7c2 Add extra-dll.xml example for .... ENH: For @Index add support for index types (like Postgres GIN) #707 ... 2016-06-17 15:04:12 +12:00
Robin Bygrave 8765cb8356 No effective change - reformat code 2016-06-17 13:24:49 +12:00
Robin Bygrave 30abe75a96 Travis - enable caching .m2 2016-06-17 13:19:01 +12:00
Robin Bygrave 164e6c71c1 Travis - stop caching .m2 2016-06-17 13:15:42 +12:00
Robin Bygrave dfa6353850 #740 - ClassCastException: java.util.ArrayList cannot be cast to com.avaje.ebean.bean.BeanCollection at com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany.resetMany(BeanPropertyAssocMany.java:273) 2016-06-17 12:30:57 +12:00
Robin Bygrave d88ff2a3b2 #739 - UpdateQuery with idEq() expression gives wrong update statement (still has table alias) 2016-06-17 11:34:47 +12:00
Robin Bygrave 713ae75464 No effective change - reformat 2016-06-17 11:03:47 +12:00
Rob BygraveandGitHub 710fb0e947 Merge pull request #738 from DevFactory/staging/using-isempty-fix-1
Refactor: Use Collection.isEmpty() instead of size() comparison to 0 etc
2016-06-15 20:55:02 +12:00
Rob BygraveandGitHub cb0cf97f3f Merge pull request #736 from DevFactory/staging/make-public-static-fields-constant-fix-1
Updating "public static" fields to add final modifier
2016-06-15 20:46:37 +12:00
Artyom Melnikov 07edb2c81f Using Collection.isEmpty() to test for emptiness 2016-06-14 15:21:23 +03:00
Artyom Melnikov 066effa5bf "public static" fields should be constant 2016-06-14 14:18:18 +03:00
Robin Bygrave bb5ca6c56e Bump agent in pom to 4.11.1 2016-06-14 22:24:50 +12:00
Robin Bygrave a64a020e36 #734 - Add ServerConfig.skipCacheAfterWrite & transaction.setSkipCache(boolean) ... to control L2 Bean cache use during transaction 2016-06-10 17:59:37 +12:00
Robin Bygrave c24ee53462 [maven-release-plugin] prepare for next development iteration 2016-06-08 14:40:18 +12:00
Robin Bygrave 06e4bdca0e [maven-release-plugin] prepare release avaje-ebeanorm-7.13.1 2016-06-08 14:39:56 +12:00
Robin Bygrave 3dbf35aca4 Bump pom version to 7.13.1-SNAPSHOT 2016-06-08 14:38:47 +12:00
Robin Bygrave cd56b3ca3c #726 - L2 cache returns stale data -> change such that when using "local L2 caching" the changes to L2 cache propagate in foreground 2016-06-08 14:38:08 +12:00
Robin Bygrave 353175626d #733 - RawSqlBuilder.columnMapping() not working using DB column alias ... java.lang.IllegalArgumentException: DB Column [detail_count] not found in mapping. 2016-06-07 17:10:37 +12:00
Robin Bygrave 1a3acb86df #732 - NPE in ScalarTypeArrayList.read(ScalarTypeArrayList.java:89) ... 2016-06-03 08:09:04 +12:00
Robin Bygrave 1304646cce #731 - ENH: Add support for using ebean.mf manifest file for finding/defining packages to search for entities 2016-06-02 17:35:06 +12:00
Robin Bygrave b3828572fd No effective change - javadoc for ServerConfig 2016-06-02 14:08:00 +12:00
Robin Bygrave eddd48f8ad #730 - Remove unused ServerConfig.classPathReaderClassName ... as replaced by org.avaje.classpath.scanner.ClassPathScanner plugin (in 7.8.1) 2016-06-02 14:03:30 +12:00
Robin Bygrave 9797502ef2 #729 - Remove unused ServerConfig.addJar(...); serverConfig.setJars(...); ... as "search jars" is no longer used in classpath scanning (since 7.8.1) 2016-06-02 13:55:14 +12:00
Robin Bygrave daba7a11b4 No effective change - remove unused code (old ClassPathReader etc) 2016-06-01 20:53:07 +12:00
Robin Bygrave 92caf69cb3 #728 - Remove Query.where(String) ... migrate to where().raw(...) 2016-06-01 19:54:02 +12:00
Robin Bygrave 71f70c01ae #727 - ENH: Add endOr(), endAnd(), endNot() ... to ExpressionList as synonym for endJunction() 2016-06-01 16:03:30 +12:00
Robin Bygrave 74d7652044 [maven-release-plugin] prepare for next development iteration 2016-05-30 17:28:05 +12:00
Robin Bygrave d7ee748f31 [maven-release-plugin] prepare release avaje-ebeanorm-7.12.2 2016-05-30 17:27:43 +12:00
Robin Bygrave 2442eb017a #725 - L2 cache not updating CacheManyId's entry when Lazy loading on a ToMany relationship, after a prior update, when other ToMany is a vanilla type (like ArrayList) 2016-05-30 17:01:20 +12:00
Robin Bygrave 37ea56ad11 #724 - NPE when using L2 caching configured with @Cache with queryCaching=true and beanCaching=false 2016-05-30 16:56:04 +12:00
Robin Bygrave 067a8b784c No effective change - add test with where().raw() containing disjunction etc 2016-05-24 09:28:29 +12:00
Robin Bygrave b18922df79 [maven-release-plugin] prepare for next development iteration 2016-05-23 22:34:46 +12:00
Robin Bygrave 70cce1094e [maven-release-plugin] prepare release avaje-ebeanorm-7.12.1 2016-05-23 22:34:24 +12:00
Robin Bygrave 44c69dec72 Bump pom to 7.12.1-SNAPSHOT 2016-05-23 22:33:10 +12:00
Robin Bygrave 6c7074e288 #120 - ENH: Add support for using where() on bulk Update statements 2016-05-23 22:24:07 +12:00
Robin Bygrave 1c162b9b18 #723 - Breaking API - Change BeanPersistListener to return void rather than boolean for inserted(), updated() and deleted() 2016-05-23 10:31:24 +12:00
Robin Bygrave 676f0d7a24 #722 - NotifyOfCommit failed. L2 Cache potentially not notified. - NullPointerException at BeanDescriptorCacheHelp.java:299 2016-05-22 20:20:23 +12:00
Robin Bygrave 46e674db57 #721 - Remove EbeanServer saveManyToManyAssociations() and deleteManyToManyAssociations() 2016-05-20 23:42:02 +12:00
Robin Bygrave 0e1aacdeee #720 - Remove EbeanServer saveAssociation(Object ownerBean, String propertyName); ... migrate to just save the bean 2016-05-20 23:24:37 +12:00
Robin Bygrave 7268890332 #719 - Remove deprecated Query.includeSoftDeletes() ... migrate to setIncludeSoftDeletes() 2016-05-20 23:08:28 +12:00
Robin Bygrave b2a3f0c99b #718 - Remove findPagedList(pageIndex, pageSize) ... migrate to setFirstRows().setMaxRows().findPagedList() 2016-05-20 23:04:35 +12:00
Robin Bygrave 662be92a16 #717 - Remove findIterate() ... migrate to findEach() or findEachWhile() 2016-05-20 22:38:07 +12:00
Robin Bygrave 11d623cb69 #716 - Remove createNamedQuery() and createQuery(Class<T> beanType, String query) ... use criteria API, query beans and RawSql 2016-05-20 22:13:58 +12:00
Robin Bygrave a20822a9e1 #715 - isNull() expression on @OneToMany or @ManyToMany produces incorrect SQL - should translate this to be same as isEmpty() and result in exists subquery 2016-05-20 17:53:11 +12:00
Rob Bygrave 0510304c0f Merge pull request #714 from VictorJimenezKwast/master
Added tests for query isNull expressions
2016-05-20 15:39:32 +12:00
Victor Jimenez Kwast fb2488e3b8 Added tests for query isNull expressions 2016-05-20 00:21:33 +02:00
Robin Bygrave 5959dcc7db [maven-release-plugin] prepare for next development iteration 2016-05-19 22:47:42 +12:00
Robin Bygrave 3656091d1f [maven-release-plugin] prepare release avaje-ebeanorm-7.11.4 2016-05-19 22:47:24 +12:00
Robin Bygrave 5122ea4b4d #416 - RawSql issue with inheritance - PersistenceException: Inheritance type for discriminator value 2016-05-19 22:44:55 +12:00
Robin Bygrave db8dd48e53 #513 - Column Constraint for Enum types should cover all Enum values in an inheritance hierachy 2016-05-19 20:55:16 +12:00
Robin Bygrave 4606c7c99f No effective change - format and imports 2016-05-19 19:58:00 +12:00
Robin Bygrave e3a7c3040b #540 - Add tests for ... RawSqlBuilder columnMapping is confused by commas in COALESCE (actually fixed in 6.14.1) 2016-05-19 09:35:39 +12:00
Robin Bygrave cb51ea4238 #713 - DB Migration - When change is from Not Null to Null ... this migration is duplicated in subsequent migration generation 2016-05-18 21:04:36 +12:00
Robin Bygrave a1a48dac2c #712 - Refactor: Rename DbMigrationConfig dbUser to dbUsername 2016-05-18 20:50:18 +12:00
Robin Bygrave b8d9273152 No effective change - reduce logging in logback-test.xml 2016-05-18 17:29:39 +12:00
Robin Bygrave 026f0e161f #711 - Refactor: Extract DB Migration runner as separate module (called avaje-dbmigration) 2016-05-18 17:27:54 +12:00
Robin Bygrave 75a2633590 #710 - Behavior change - query with invalid fetch path (without properties) now throws PersistenceException 2016-05-18 12:34:16 +12:00
Robin Bygrave 362ceea79b #709 - Refactor: BeanCollection internals, rename method isEmptyAndUntouched() to isSkipSave(), remove 'touched' flag 2016-05-18 11:19:01 +12:00
Robin Bygrave 1c9b974427 #708 - Refactor - Remove BeanCollectionTouched interface 2016-05-18 10:13:03 +12:00
Robin Bygrave f181e3946f No effective change - format code 2016-05-18 09:36:26 +12:00
Robin Bygrave 6df145b4fa No effective change - format code 2016-05-18 09:35:26 +12:00
Robin Bygrave a4cb116d4f No effective change - format code 2016-05-18 09:34:20 +12:00
Robin Bygrave 3161664d58 No effective change - additional test for constructor enhancement (from agent https://github.com/ebean-orm/avaje-ebeanorm-agent/issues/31) 2016-05-17 16:41:58 +12:00
Robin Bygrave 4b3f63f6e0 Update pom - update agent version to 4.10.1 2016-05-17 16:40:51 +12:00
Robin Bygrave d89b1b0367 #511 Add Test (that is clearer with better asserts) 2016-05-17 08:19:13 +12:00
Robin Bygrave 17433bb70b [maven-release-plugin] prepare for next development iteration 2016-05-16 16:58:02 +12:00
Robin Bygrave fb6ea92ae9 [maven-release-plugin] prepare release avaje-ebeanorm-7.11.3 2016-05-16 16:57:39 +12:00
Robin Bygrave 08b0a6350d #706 - NPE when using @DbArray with null values 2016-05-16 16:54:18 +12:00
Robin Bygrave bd330b4c0e No effective change - additional tests for @DbArray inserting nulls 2016-05-16 16:30:41 +12:00
Robin Bygrave ec234f1f39 #568 - ENH: Add ability to turn of getGeneratedKeys with @Transactional annotation (Also needs agent update) 2016-05-16 15:50:11 +12:00
Robin Bygrave dd28b28095 #704 - Update javadoc for @Cache and @Query.setUseCache() 2016-05-13 16:30:43 +12:00
Robin Bygrave 2a5be6db3d No effective change - sys out for CI server - fix PG like clause in test 2016-05-13 16:23:19 +12:00
Robin Bygrave 545f40703e No effective change - sys out for CI server 2016-05-13 16:18:17 +12:00
Robin Bygrave 35d0481644 No effective change - sys out for CI server 2016-05-13 16:05:13 +12:00
Robin Bygrave 2282ff3c6e #704 - Internal refactor to use excludeBeanCache in DefaultOrmQuery 2016-05-13 15:57:20 +12:00
Robin Bygrave d1d4bf1da8 #704 - Internal refactor to use excludeBeanCache in DefaultOrmQuery 2016-05-13 15:56:45 +12:00
Robin Bygrave 8c6653605d #704 - Update tests for 704 removing the use of Query.setUseCache(true) which is redundant 2016-05-13 14:31:03 +12:00
Robin Bygrave e344b5c0a1 #703 - Refactor - When batch lazy loading hits the L2 bean cache and misses initially, it checks that initial bean again (and should skip the second attempt) 2016-05-13 14:12:28 +12:00
Robin Bygrave 0e5859716b #702 - IllegalStateException: No bean cache enabled error ... when updating bean that is not using caching (but is related to a bean with @Cache) 2016-05-13 12:52:27 +12:00
Robin Bygrave 90a794fca5 No effective change - sys out for CI server - add ReadAuditQueryPlan equals/hashCode for CI issue 2016-05-13 11:06:26 +12:00
Robin Bygrave 1d6e480402 No effective change - sys out for CI server 2016-05-13 10:55:36 +12:00
Robin Bygrave 54706302df #701 - DB Migration - incorrect alter column DDL when removing not null constraint (Postgres specific) 2016-05-13 09:07:43 +12:00
Robin Bygrave 4a59cf4c3d No effective change - sys out for CI server 2016-05-12 21:31:15 +12:00
Robin Bygrave 8cb8f9eabf #278 - SqlQuery setFirstRow fail - modify SQL using LIMIT/OFFSET etc to support firstRows/maxRows 2016-05-12 20:59:52 +12:00
Robin Bygrave 58a36e6b67 No effective change - cleanup comment 2016-05-12 19:07:52 +12:00
Robin Bygrave 064d282f7b [maven-release-plugin] prepare for next development iteration 2016-05-12 17:43:22 +12:00
Robin Bygrave e83ec42bbc [maven-release-plugin] prepare release avaje-ebeanorm-7.11.2 2016-05-12 17:43:03 +12:00
Robin Bygrave 409720d854 #700 - Postgres ARRAY and JSON expressions cast Long to ::integer rather than ::bigint 2016-05-12 17:38:15 +12:00
Robin Bygrave 1d8bb8c800 #700 - Postgres ARRAY and JSON expressions cast Long to ::integer rather than ::bigint 2016-05-12 17:22:40 +12:00
Robin Bygrave ad3b9b2f84 No effective change - sys out for CI server 2016-05-12 17:09:55 +12:00
Robin Bygrave b8ecbd507e #522 - DDL - DB migration diff does not include DDL to add comment 2016-05-12 16:52:22 +12:00
Robin Bygrave 45453bbcd8 No effective change - include the search packages in the classpath scan INFO logging message 2016-05-12 16:17:33 +12:00
Robin Bygrave 44dfab2ace No effective change - sys out for CI server 2016-05-12 14:47:35 +12:00
Robin Bygrave 4ab5cdf841 #698 - History DDL generation for Postgres does not respect non-public schema 2016-05-12 14:32:38 +12:00
Robin Bygrave b384cb4043 #699 - DB Migration not generating sql to alter check constraints 2016-05-12 13:44:30 +12:00
Robin Bygrave 2a323f2256 #697 - Refactor - tidy objectMapper detection in DefaultTypeManager 2016-05-10 13:00:04 +12:00
Robin Bygrave 8cfba1015f No effective change - remove incorrect comment 2016-05-10 11:17:04 +12:00
Robin Bygrave bd6ff5d855 [maven-release-plugin] prepare for next development iteration 2016-05-10 00:04:08 +12:00
445 changed files with 8209 additions and 11449 deletions
+11 -5
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>7.11.1</version>
<version>7.14.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -37,7 +37,7 @@
<scm>
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
<tag>avaje-ebeanorm-7.11.1</tag>
<tag>avaje-ebeanorm-7.14.1</tag>
</scm>
<dependencies>
@@ -78,6 +78,12 @@
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-dbmigration</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -162,7 +168,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>4.9.2</version>
<version>4.11.1</version>
<scope>test</scope>
</dependency>
@@ -237,14 +243,14 @@
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>4.9.2</version>
<version>4.11.1</version>
<executions>
<execution>
<id>test</id>
<phase>process-test-classes</phase>
<configuration>
<classSource>target/test-classes</classSource>
<packages>com.avaje.tests.**</packages>
<!--<packages>com.avaje.tests.**</packages>-->
<transformArgs>debug=1</transformArgs>
</configuration>
<goals>
@@ -5,6 +5,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* Document storage operations.
@@ -205,6 +206,39 @@ public interface DocumentStore {
*/
void createIndex(String indexName, String alias);
/**
* Modify the settings on an index.
* <p>
* For example, this can be used be used to set elasticSearch refresh_interval
* on an index before a bulk update.
* </p>
* <pre>{@code
*
* // refresh_interval -1 ... disable refresh while bulk loading
*
* Map<String,Object> settings = new LinkedHashMap<>();
* settings.put("refresh_interval", "-1");
*
* documentStore.indexSettings("product", settings);
*
* }</pre>
*
* <pre>{@code
*
* // refresh_interval 1s ... restore after bulk loading
*
* Map<String,Object> settings = new LinkedHashMap<>();
* settings.put("refresh_interval", "1s");
*
* documentStore.indexSettings("product", settings);
*
* }</pre>
*
* @param indexName the name of the index to update settings on
* @param settings the settings to set on the index
*/
void indexSettings(String indexName, Map<String, Object> settings);
/**
* Copy the index to a new index.
* <p>
+22 -207
View File
@@ -639,58 +639,6 @@ public final class Ebean {
return serverMgr.getDefaultServer().saveAll(beans);
}
/**
* Delete the associations (from the intersection table) of a ManyToMany given
* the owner bean and the propertyName of the ManyToMany collection.
* <p>
* Typically these deletions occur automatically when persisting a ManyToMany
* collection and this provides a way to invoke those deletions directly.
* </p>
*
* @return the number of associations deleted (from the intersection table).
*/
public static int deleteManyToManyAssociations(Object ownerBean, String propertyName) {
return serverMgr.getDefaultServer().deleteManyToManyAssociations(ownerBean, propertyName);
}
/**
* Save the associations of a ManyToMany given the owner bean and the
* propertyName of the ManyToMany collection.
* <p>
* Typically the saving of these associations (inserting into the intersection
* table) occurs automatically when persisting a ManyToMany. This provides a
* way to invoke those insertions directly.
* </p>
* <p>
* You can use this when the collection is new and in this case all the
* entries in the collection are treated as additions are result in inserts
* into the intersection table.
* </p>
*/
public static void saveManyToManyAssociations(Object ownerBean, String propertyName) {
serverMgr.getDefaultServer().saveManyToManyAssociations(ownerBean, propertyName);
}
/**
* Save the associated collection or bean given the property name.
* <p>
* This is similar to performing a save cascade on a specific property
* manually/programmatically.
* </p>
* <p>
* Note that you can turn on/off cascading for a transaction via
* {@link Transaction#setPersistCascade(boolean)}
* </p>
*
* @param ownerBean
* the bean instance holding the property we want to save
* @param propertyName
* the property we want to save
*/
public static void saveAssociation(Object ownerBean, String propertyName) {
serverMgr.getDefaultServer().saveAssociation(ownerBean, propertyName);
}
/**
* Delete the bean.
* <p>
@@ -932,19 +880,6 @@ public final class Ebean {
return serverMgr.getDefaultServer().createSqlQuery(sql);
}
/**
* Create a named sql query.
* <p>
* The query statement will be defined in a deployment orm xml file.
* </p>
*
* @param namedQuery
* the name of the query
*/
public static SqlQuery createNamedSqlQuery(String namedQuery) {
return serverMgr.getDefaultServer().createNamedSqlQuery(namedQuery);
}
/**
* Create a sql update for executing native dml statements.
* <p>
@@ -954,10 +889,6 @@ public final class Ebean {
* <p>
* See {@link SqlUpdate} for example usage.
* </p>
* <p>
* Where possible it would be expected practice to put the statement in a orm
* xml file (named update) and use {@link #createNamedSqlUpdate(String)} .
* </p>
*/
public static SqlUpdate createSqlUpdate(String sql) {
return serverMgr.getDefaultServer().createSqlUpdate(sql);
@@ -972,144 +903,6 @@ public final class Ebean {
return serverMgr.getDefaultServer().createCallableSql(sql);
}
/**
* Create a named sql update.
* <p>
* The statement (an Insert Update or Delete statement) will be defined in a
* deployment orm xml file.
* </p>
*
* <pre>{@code
*
* // Use a namedQuery
* UpdateSql update = Ebean.createNamedSqlUpdate("update.topic.count");
*
* update.setParameter("count", 1);
* update.setParameter("topicId", 50);
*
* int modifiedCount = update.execute();
*
* }</pre>
*/
public static SqlUpdate createNamedSqlUpdate(String namedQuery) {
return serverMgr.getDefaultServer().createNamedSqlUpdate(namedQuery);
}
/**
* Return a named Query that will have defined fetch paths, predicates etc.
* <p>
* The query is created from a statement that will be defined in a deployment
* orm xml file or NamedQuery annotations. The query will typically already
* define fetch paths, predicates, order by clauses etc so often you will just
* need to bind required parameters and then execute the query.
* </p>
*
* <pre>{@code
*
* // example
* Query<Order> query = Ebean.createNamedQuery(Order.class, "new.for.customer");
* query.setParameter("customerId", 23);
* List<Order> newOrders = query.findList();
*
* }</pre>
*
* @param beanType
* the class of entity to be fetched
* @param namedQuery
* the name of the query
*/
public static <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
return serverMgr.getDefaultServer().createNamedQuery(beanType, namedQuery);
}
/**
* Create a query using the query language.
* <p>
* Note that you are allowed to add additional clauses using where() as well
* as use fetch() and setOrderBy() after the query has been created.
* </p>
* <p>
* Note that this method signature used to map to named queries and that has
* moved to {@link #createNamedQuery(Class, String)}.
* </p>
*
* <pre>{@code
*
* String q = "find order fetch details where status = :st";
*
* List<Order> newOrders = Ebean.>findOrder.class, q)
* .setParameter("st", Order.Status.NEW)
* .findList();
*
* }</pre>
*
* @param query
* the object query
*/
public static <T> Query<T> createQuery(Class<T> beanType, String query) {
return serverMgr.getDefaultServer().createQuery(beanType, query);
}
/**
* Create a named orm update. The update statement is specified via the
* NamedUpdate annotation.
* <p>
* The orm update differs from the SqlUpdate in that it uses the bean name and
* bean property names rather than table and column names.
* </p>
* <p>
* Note that named update statements can be specified in raw sql (with column
* and table names) or using bean name and bean property names. This can be
* specified with the isSql flag.
* </p>
* <p>
* Example named updates:
* </p>
*
* <pre>{@code
* package app.data;
*
* import ...
*
* @NamedUpdates(value = {
* @NamedUpdate( name = "setTitle",
* isSql = false,
* notifyCache = false,
* update = "update topic set title = :title, postCount = :postCount where id = :id"),
* @NamedUpdate( name = "setPostCount",
* notifyCache = false,
* update = "update f_topic set post_count = :postCount where id = :id"),
* @NamedUpdate( name = "incrementPostCount",
* notifyCache = false,
* isSql = false,
* update = "update Topic set postCount = postCount + 1 where id = :id") })
* @Entity
* @Table(name = "f_topic")
* public class Topic { ...
*
* }</pre>
*
* <p>
* Example using a named update:
* </p>
*
* <pre>{@code
*
* Update<Topic> update = Ebean.createNamedUpdate(Topic.class, "setPostCount");
* update.setParameter("postCount", 10);
* update.setParameter("id", 3);
*
* int rows = update.execute();
* System.out.println("rows updated: " + rows);
*
* }</pre>
*/
public static <T> Update<T> createNamedUpdate(Class<T> beanType, String namedUpdate) {
return serverMgr.getDefaultServer().createNamedUpdate(beanType, namedUpdate);
}
/**
* Create a orm update where you will supply the insert/update or delete
* statement (rather than using a named one that is already defined using the
@@ -1226,6 +1019,28 @@ public final class Ebean {
return serverMgr.getDefaultServer().find(beanType);
}
/**
* Create an Update query to perform a bulk update.
* <p>
* <pre>{@code
*
* int rows = Ebean.update(Customer.class)
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* @param beanType The type of entity bean to update
* @param <T> The type of entity bean
* @return The update query to use
*/
public static <T> UpdateQuery<T> update(Class<T> beanType) {
return serverMgr.getDefaultServer().update(beanType);
}
/**
* Create a filter for sorting and filtering lists of entities locally without
* going back to the database.
File diff suppressed because it is too large Load Diff
@@ -135,16 +135,17 @@ public interface ExpressionList<T> {
* optimal depending on the database platform.
* </p>
*
* @return the number of beans/rows that were deleted.
* @return the number of rows that were deleted.
*/
int delete();
/**
* Execute the query iterating over the results.
*
* @see Query#findIterate()
* Execute as a update query.
*
* @return the number of rows that were updated.
* @see UpdateQuery
*/
QueryIterator<T> findIterate();
int update();
/**
* Execute the query process the beans one at a time.
@@ -253,27 +254,6 @@ public interface ExpressionList<T> {
*/
FutureList<T> findFutureList();
/**
* Return a PagedList for this query using pageIndex and pageSize.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number
* function to limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Return a PagedList for this query using firstRow and maxRows.
* <p>
@@ -945,7 +925,7 @@ public interface ExpressionList<T> {
* This is exactly the same as conjunction();
* </p>
* <p>
* Use endJunction() to end the AND junction.
* Use endAnd() or endJunction() to end the AND junction.
* </p>
* <p>
* Note that a where() clause defaults to an AND junction so
@@ -963,11 +943,11 @@ public interface ExpressionList<T> {
* .and() // nested and
* .startsWith("name", "r")
* .eq("anniversary", onAfter)
* .endJunction() // end AND junction
* .endAnd()
* .and()
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endJunction() // end AND junction
* .endAnd()
* .orderBy().asc("name")
* .findList();
* }</pre>
@@ -979,7 +959,7 @@ public interface ExpressionList<T> {
* This is exactly the same as disjunction();
*
* <p>
* Use endJunction() to end the OR junction.
* Use endOr() or endJunction() to end the OR junction.
* </p>
*
* <pre>{@code
@@ -993,11 +973,11 @@ public interface ExpressionList<T> {
* .and()
* .startsWith("name", "r")
* .eq("anniversary", onAfter)
* .endJunction() // end AND junction
* .endAnd()
* .and()
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endJunction() // end AND junction
* .endAnd()
* .orderBy().asc("name")
* .findList();
*
@@ -1008,7 +988,7 @@ public interface ExpressionList<T> {
/**
* Return a list of expressions that will be wrapped by NOT.
* <p>
* Use endJunction() to end expressions being added to the
* Use endNot() or endJunction() to end expressions being added to the
* NOT expression list.
* </p>
*
@@ -1018,7 +998,7 @@ public interface ExpressionList<T> {
* .not()
* .gt("id", 1)
* .eq("anniversary", onAfter)
* .endJunction() // end the not expressions
* .endNot()
*
* }</pre>
*
@@ -1032,7 +1012,7 @@ public interface ExpressionList<T> {
* .not()
* .gt("id", 1)
* .eq("anniversary", onAfter)
* .endJunction() // end the not expressions
* .endNot()
* .orderBy()
* .asc("name")
* .findList();
@@ -1105,4 +1085,19 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> endJunction();
/**
* End a AND junction - synonym for endJunction().
*/
ExpressionList<T> endAnd();
/**
* End a AND junction - synonym for endJunction().
*/
ExpressionList<T> endOr();
/**
* End a NOT junction - synonym for endJunction().
*/
ExpressionList<T> endNot();
}
-19
View File
@@ -674,16 +674,6 @@ public abstract class Model {
return query().findMap(keyProperty, keyType);
}
/**
* Return a PagedList of all entities of the given type (use where() to specify predicates as
* needed).
* <p>
* Equivalent to {@link Query#findPagedList(int, int)}
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return query().findPagedList(pageIndex, pageSize);
}
/**
* Executes a find row count query in a background thread.
* <p>
@@ -843,15 +833,6 @@ public abstract class Model {
return query().setId(id);
}
/**
* Create and return a new query using the OQL.
* <p>
* Equivalent to {@link EbeanServer#createQuery(Class, String)}
*/
public Query<T> setQuery(String oql) {
return db().createQuery(type, oql);
}
/**
* Create and return a new query based on the <code>RawSql</code>.
* <p>
+1 -10
View File
@@ -84,7 +84,7 @@ import java.util.concurrent.Future;
* @param <T>
* the entity bean type
*
* @see Query#findPagedList(int, int)
* @see Query#findPagedList()
*/
public interface PagedList<T> {
@@ -181,15 +181,6 @@ public interface PagedList<T> {
*/
int getTotalPageCount();
/**
* Return the index position of this page. Zero based.
* <p>
* Note that if firstRows/maxRows is used rather than pageIndex/pageSize then
* this always returns 0.
* </p>
*/
int getPageIndex();
/**
* Return the page size used for this query. This is the same value as maxRows used by the query.
*/
+15 -132
View File
@@ -301,12 +301,6 @@ public interface Query<T> {
*/
Query<T> asDraft();
/**
* Deprecated in favour of setIncludeSoftDeletes().
*/
@Deprecated
Query<T> includeSoftDeletes();
/**
* Cancel the query execution if supported by the underlying database and
* driver.
@@ -547,25 +541,6 @@ public interface Query<T> {
*/
List<Object> findIds();
/**
* Execute the query iterating over the results.
* <p>
* Remember that with {@link QueryIterator} you must call
* {@link QueryIterator#close()} when you have finished iterating the results
* (typically in a finally block).
* </p>
* <p>
* findEach() and findEachWhile() are preferred to findIterate() as they ensure
* the jdbc statement and resultSet are closed at the end of the iteration.
* </p>
* <p>
* This query will execute against the EbeanServer that was used to create it.
* </p>
*/
QueryIterator<T> findIterate();
/**
* Execute the query processing the beans one at a time.
* <p>
@@ -788,6 +763,11 @@ public interface Query<T> {
*/
int delete();
/**
* Execute the UpdateQuery returning the number of rows updated.
*/
int update();
/**
* Return the count of entities this query should return.
* <p>
@@ -831,52 +811,6 @@ public interface Query<T> {
*/
FutureList<T> findFutureList();
/**
* Return a PagedList for this query using pageIndex and pageSize.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
* limit the result set.
* </p>
*
* <h4>Example: typical use including total row count</h4>
* <pre>{@code
*
* // We want to find the first 100 new orders
* // ... 0 means first page
* // ... page size is 100
*
* PagedList<Order> pagedList
* = ebeanServer.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findPagedList(0, 100);
*
* // Optional: initiate the loading of the total
* // row count in a background thread
* pagedList.loadRowCount();
*
* // fetch and return the list in the foreground thread
* List<Order> orders = pagedList.getList();
*
* // get the total row count (from the future)
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Return a PagedList for this query using firstRow and maxRows.
* <p>
@@ -983,40 +917,6 @@ public interface Query<T> {
*/
Object getId();
/**
* Add additional clause(s) to the where clause.
* <p>
* This typically contains named parameters which will need to be set via
* {@link #setParameter(String, Object)}.
* </p>
*
* <pre>{@code
*
* Query<Order> query = ebeanServer.createQuery(Order.class, "top");
* ...
* if (...) {
* query.where("status = :status and lower(customer.name) like :custName");
* query.setParameter("status", Order.NEW);
* query.setParameter("custName", "rob%");
* }
*
* }</pre>
*
* <p>
* Internally the addToWhereClause string is processed by removing named
* parameters (replacing them with ?) and by converting logical property names
* to database column names (with table alias). The rest of the string is left
* as is and it is completely acceptable and expected for the addToWhereClause
* string to include sql functions and columns.
* </p>
*
* @param addToWhereClause
* the clause to append to the where clause which typically contains
* named parameters.
* @return The query object
*/
Query<T> where(String addToWhereClause);
/**
* Add a single Expression to the where clause returning the query.
*
@@ -1127,29 +1027,6 @@ public interface Query<T> {
*/
ExpressionList<T> having();
/**
* Add additional clause(s) to the having clause.
* <p>
* This typically contains named parameters which will need to be set via
* {@link #setParameter(String, Object)}.
* </p>
*
* <pre>{@code
*
* List<ReportOrder> query =
* ebeanServer.find(ReportOrder.class)
* .having("score > :min").setParameter("min", 1)
* .findList();
*
* }</pre>
*
* @param addToHavingClause
* the clause to append to the having clause which typically contains
* named parameters.
* @return The query object
*/
Query<T> having(String addToHavingClause);
/**
* Add an expression to the having clause returning the query.
* <p>
@@ -1304,13 +1181,19 @@ public interface Query<T> {
Query<T> setMapKey(String mapKey);
/**
* Set this to true to use the bean cache.
* Set this to false to not use the bean cache.
* <p>
* If the query result is in cache then by default this same instance is
* returned. In this sense it should be treated as a read only object graph.
* By default "find by id" and "find by natural key" will use the bean cache
* when bean caching is enabled. Setting this to false means that the query
* will not use the bean cache and instead hit the database.
* </p>
* <p>
* In the case of other queries (findList(), findEach() etc) then setting this to
* false beans that the if lazy loading is invoked that lazy loading will not try
* to use the bean cache.
* </p>
*/
Query<T> setUseCache(boolean useBeanCache);
Query<T> setUseCache(boolean useCache);
/**
* Set this to true to use the query cache.
+10 -10
View File
@@ -404,7 +404,7 @@ public final class RawSql implements Serializable {
this.dbColumnMap = new LinkedHashMap<String, Column>();
for (int i = 0; i < columns.size(); i++) {
Column c = columns.get(i);
dbColumnMap.put(c.getDbColumn(), c);
dbColumnMap.put(c.getDbColumnKey(), c);
}
}
@@ -500,8 +500,7 @@ public final class RawSql implements Serializable {
} else {
Column column = dbColumnMap.get(dbColumn);
if (column == null) {
String msg = "DB Column [" + dbColumn + "] not found in mapping. Expecting one of ["
+ dbColumnMap.keySet() + "]";
String msg = "DB Column [" + dbColumn + "] not found in mapping. Expecting one of [" + dbColumnMap.keySet() + "]";
throw new IllegalArgumentException(msg);
}
column.setPropertyName(propertyName);
@@ -657,6 +656,14 @@ public final class RawSql implements Serializable {
return indexPos;
}
/**
* Return the DB column alias if specified otherwise DB column.
* This is used as the key for mapping a column to a logical property.
*/
public String getDbColumnKey() {
return (dbAlias != null) ? dbAlias : dbColumn;
}
/**
* Return the DB column name including table alias (if it has one).
*/
@@ -664,13 +671,6 @@ public final class RawSql implements Serializable {
return dbColumn;
}
/**
* Return the DB column alias (if it has one).
*/
public String getDbAlias() {
return dbAlias;
}
/**
* Return the bean property this column is mapped to.
*/
+69 -3
View File
@@ -3,6 +3,7 @@ package com.avaje.ebean;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.config.DocStoreConfig;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.config.ServerConfig;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
@@ -125,13 +126,78 @@ public interface Transaction extends Closeable {
* Set to true when you want all loaded properties to be included in the update
* (rather than just the changed properties).
* <p>
* You might set this when using JDBC batch in order to get multiple updates
* with slightly different sets of changed properties into the same statement
* and hence better JDBC batch performance.
* You might set this when using JDBC batch in order to get multiple updates
* with slightly different sets of changed properties into the same statement
* and hence better JDBC batch performance.
* </p>
*/
void setUpdateAllLoadedProperties(boolean updateAllLoadedProperties);
/**
* Set if the L2 cache should be skipped for "find by id" and "find by natural key" queries.
* <p>
* By default {@link ServerConfig#isSkipCacheAfterWrite()} is true and that means that for
* "find by id" and "find by natural key" queries which normally hit L2 bean cache automatically
* - will not do so after a persist/write on the transaction.
* </p>
* <p>
* This method provides explicit control over whether "find by id" and "find by natural key"
* will skip the L2 bean cache or not (regardless of whether the transaction is considered "read only").
* </p>
* <p>
* Refer to {@link ServerConfig#setSkipCacheAfterWrite(boolean)} for configuring the default behavior
* for using the L2 bean cache in transactions spanning multiple query/persist requests.
* </p>
*
* <pre>{@code
*
* // assume Customer has L2 bean caching enabled ...
*
* Transaction transaction = Ebean.beginTransaction();
* try {
*
* // this uses L2 bean cache as the transaction
* // ... is considered "query only" at this point
* Customer.find.byId(42);
*
* // transaction no longer "query only" once
* // ... a bean has been saved etc
* Ebean.save(someBean);
*
* // will NOT use L2 bean cache as the transaction
* // ... is no longer considered "query only"
* Customer.find.byId(55);
*
*
*
* // explicit control - please use L2 bean cache
*
* transaction.setSkipCache(false);
* Customer.find.byId(77); // hit the l2 bean cache
*
*
* // explicit control - please don't use L2 bean cache
*
* transaction.setSkipCache(true);
* Customer.find.byId(99); // skips l2 bean cache
*
*
* } finally {
* transaction.end();
* }
*
* }</pre>
*
* @see ServerConfig#isSkipCacheAfterWrite()
*/
void setSkipCache(boolean skipCache);
/**
* Return true if the L2 cache should be skipped. More accurately if true then find by id
* and find by natural key queries should NOT automatically use the L2 bean cache.
*/
boolean isSkipCache();
/**
* Turn on or off statement batching. Statement batching can be transparent
* for drivers and databases that support getGeneratedKeys. Otherwise you may
@@ -36,6 +36,8 @@ public final class TxScope {
int batchSize;
boolean skipGeneratedKeys;
boolean readOnly;
ArrayList<Class<? extends Throwable>> rollbackFor;
@@ -188,6 +190,21 @@ public final class TxScope {
return this;
}
/**
* Set if the transaction should skip reading generated keys for inserts.
*/
public TxScope setSkipGeneratedKeys() {
this.skipGeneratedKeys = true;
return this;
}
/**
* Return true if getGeneratedKeys should be skipped for this transaction.
*/
public boolean isSkipGeneratedKeys() {
return skipGeneratedKeys;
}
/**
* Return if the transaction should be treated as read only.
*/
@@ -0,0 +1,160 @@
package com.avaje.ebean;
/**
* An update query typically intended to perform a bulk update of many rows that match the query.
* <p>
* Also note that you can also just use a raw SQL update via {@link SqlUpdate} which is pretty light and simple.
* This UpdateQuery is more for the cases where we want to build the where expression of the update using the
* {@link ExpressionList} "Criteria API" that is used with a normal ORM query.
* </p>
*
* <h4>Example: Simple update</h4>
*
* <pre>{@code
*
* int rows = ebeanServer
* .update(Customer.class)
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
* <pre>{@code sql
*
* update o_customer set status=?, updtime=? where id > ?
*
* }</pre>
*
* <p>
* Note that if the where() clause contains a join then the SQL update changes to use a
* <code> WHERE ID IN () </code> form.
* </p>
*
* <h4>Example: Update with a JOIN</h4>
* <p>
* In this example the expression <code>.eq("billingAddress.country", nz)</code> requires a join
* to the address table.
* </p>
*
* <pre>{@code
*
* int rows = ebeanServer
* .update(Customer.class)
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .eq("status", Customer.Status.NEW)
* .eq("billingAddress.country", nz)
* .gt("id", 1000)
* .update();
* }</pre>
*
* <pre>{@code sql
*
* update o_customer set status=?, updtime=?
* where id in (
* select t0.id c0
* from o_customer t0
* left outer join o_address t1 on t1.id = t0.billing_address_id
* where t0.status = ?
* and t1.country_code = ?
* and t0.id > ? )
*
* }</pre>
*
* @param <T> The type of entity bean being updated
*
* @see SqlUpdate
*/
public interface UpdateQuery<T> {
/**
* Set the value of a property.
*
*
* <pre>{@code
*
* int rows = ebeanServer
* .update(Customer.class)
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* @param property The bean property to be set
* @param value The value to set the property to
*/
UpdateQuery<T> set(String property, Object value);
/**
* Set the property to be null.
*
* <pre>{@code
*
* int rows = ebeanServer
* .update(Customer.class)
* .setNull("notes")
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* @param property The property to be set to null.
*/
UpdateQuery<T> setNull(String property);
/**
*
* Set using a property expression that does not need any bind values.
* <p>
* The property expression typically contains database functions.
* </p>
*
* <pre>{@code
*
* int rows = ebeanServer
* .update(Customer.class)
* .setRaw("status = coalesce(status, 'A')")
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* @param propertyExpression A property expression
*/
UpdateQuery<T> setRaw(String propertyExpression);
/**
* Set using a property expression that can contain <code>?</code> bind value placeholders.
* <p>
* For each <code>?</code> in the property expression there should be a matching bind value supplied.
* </p>
* <pre>{@code
*
* int rows = ebeanServer
* .update(Customer.class)
* .setRaw("status = coalesce(status, ?)", Customer.Status.ACTIVE)
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* @param propertyExpression A raw property expression
* @param values The values to bind with the property expression
*/
UpdateQuery<T> setRaw(String propertyExpression, Object... values);
/**
* Return the query expression list to add predicates to.
*/
ExpressionList<T> where();
}
@@ -28,6 +28,11 @@ public @interface Cache {
* set this to false on a bean type that we want to use query caching but no
* bean caching (and this is expected to be a rare case).
* </p>
* <p>
* When bean caching is enabled by default "find by id" and "find by unique natural key"
* queries will try to use the bean cache. We use {@link com.avaje.ebean.Query#setUseCache(boolean)}
* with <code>false</code> for the case when we do NOT want to use the bean cache.
* </p>
*/
boolean enableBeanCache() default true;
@@ -1,37 +0,0 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An Update statement for a particular entity bean type.
* <p>
* The update can either be a sql insert,update or delete statement with tables
* and columns etc or the equivalent statement but with table names and columns
* expressed as bean types and bean properties.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedUpdate {
/**
* The name of the update.
*/
String name();
/**
* The insert, update or delete statement.
*/
String update();
/**
* Set this to false if you do not want the cache to be notified. If true the
* cache will invalidate appropriate objects from the cache (after a
* successful transaction commit).
*/
boolean notifyCache() default true;
}
@@ -1,20 +0,0 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Holds an array of named Update statements for a particular entity bean type.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedUpdates {
/**
* An array of named updates.
*/
NamedUpdate[] value();
}
@@ -90,6 +90,16 @@ public @interface Transactional {
*/
int batchSize() default 0;
/**
* Set to false when we want to skip getting generatedKeys.
* <p>
* This is typically used in the case of large batch inserts where we get a
* performance benefit from not calling getGeneratedKeys (as we are going to
* insert a lot of rows and have no need for the Id values after the insert).
* </p>
*/
boolean getGeneratedKeys() default true;
/**
* The transaction isolation level this transaction should have.
* <p>
@@ -54,10 +54,12 @@ public interface BeanCollection<E> extends Serializable {
void reset(EntityBean ownerBean, String propertyName);
/**
* Return true if the collection is empty and untouched. Used to detect if a
* collection was 'cleared' deliberately or just un-initialised.
* Return true if the collection is uninitialised or is empty without any held modifications.
* <p>
* Returning true means can safely skip cascade save for this bean collection.
* </p>
*/
boolean isEmptyAndUntouched();
boolean isSkipSave();
/**
* Return the bean that owns this collection.
@@ -92,11 +94,6 @@ public interface BeanCollection<E> extends Serializable {
*/
void setFilterMany(ExpressionList<?> filterMany);
/**
* Set a listener to be notified when the BeanCollection is first touched.
*/
void setBeanCollectionTouched(BeanCollectionTouched notify);
/**
* Return true if the collection has been registered with the batch loading context.
*/
@@ -1,20 +0,0 @@
package com.avaje.ebean.bean;
/**
* Used to specify a listener to be notified when a BeanCollection is first
* used.
* <p>
* To use this you can set a BeanCollectionTouched onto a BeanCollection before
* it has been used. When the BeanCollection is first used by the client code
* then the BeanCollectionTouched is notified. It can only be notified once.
* </p>
*
* @author rbygrave
*/
public interface BeanCollectionTouched {
/**
* Notify the listener that the bean collection has been used.
*/
void notifyTouched(BeanCollection<?> c);
}
@@ -5,6 +5,15 @@ package com.avaje.ebean.cache;
*/
public interface ServerCacheManager {
/**
* Return true if the L2 caching is local.
* <p>
* Local L2 caching means that the cache updates should occur in foreground
* rather than background processing.
* </p>
*/
boolean isLocalL2Caching();
/**
* Return true if there is an active bean cache for this type of bean.
*/
@@ -1,16 +1,14 @@
package com.avaje.ebean.common;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.EntityBean;
import javax.persistence.PersistenceException;
import java.util.Set;
/**
* Base class for List Set and Map implementations of BeanCollection.
*
@@ -36,8 +34,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected String ebeanServerName;
protected transient BeanCollectionTouched beanCollectionTouched;
/**
* The owning bean (used for lazy fetch).
*/
@@ -55,12 +51,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected boolean modifyRemoveListening;
protected boolean modifyListening;
/**
* Flag used to tell if empty collections have been cleared etc or just
* uninitialised.
*/
protected boolean touched;
/**
* Constructor not non-lazy loading collection.
*/
@@ -109,25 +99,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
checkEmptyLazyLoad();
}
/**
* Set touched. If setFlag is false then typically an isEmpty() call and still
* considering that to be untouched.
*/
protected void touched(boolean setFlag) {
if (setFlag) {
touched = true;
}
if (beanCollectionTouched != null) {
// only call this once
beanCollectionTouched.notifyTouched(this);
beanCollectionTouched = null;
}
}
public void setBeanCollectionTouched(BeanCollectionTouched notify) {
this.beanCollectionTouched = notify;
}
public boolean isRegisteredWithLoadContext() {
return registeredWithLoadContext;
}
@@ -219,4 +190,11 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
return modifyHolder.getModifyRemovals();
}
}
/**
* Return true if there are underlying additions or removals.
*/
public boolean holdsModifications() {
return modifyHolder != null && modifyHolder.hasModifications();
}
}
@@ -52,12 +52,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.list = null;
this.touched = false;
}
@Override
public boolean isEmptyAndUntouched() {
return !touched && (list == null || list.isEmpty());
public boolean isSkipSave() {
return list == null || (list.isEmpty() && !holdsModifications());
}
@SuppressWarnings("unchecked")
@@ -109,24 +108,14 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
list = new ArrayList<E>();
}
}
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (list == null) {
lazyLoadCollection(false);
}
touched(setTouched);
}
}
@@ -296,7 +285,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
public boolean isEmpty() {
initAsUntouched();
init();
return list.isEmpty();
}
@@ -45,11 +45,10 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.map = null;
this.touched = false;
}
public boolean isEmptyAndUntouched() {
return !touched && (map == null || map.isEmpty());
public boolean isSkipSave() {
return map == null || (map.isEmpty() && !holdsModifications());
}
@Override
@@ -125,24 +124,14 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
map = new LinkedHashMap<K, E>();
}
}
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (map == null) {
lazyLoadCollection(false);
}
touched(setTouched);
}
}
@@ -249,7 +238,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public boolean isEmpty() {
initAsUntouched();
init();
return map.isEmpty();
}
@@ -46,11 +46,10 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.set = null;
this.touched = false;
}
public boolean isEmptyAndUntouched() {
return !touched && (set == null || set.isEmpty());
public boolean isSkipSave() {
return set == null || (set.isEmpty() && !holdsModifications());
}
@SuppressWarnings("unchecked")
@@ -117,24 +116,14 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
set = new LinkedHashSet<E>();
}
}
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (set == null) {
lazyLoadCollection(true);
}
touched(setTouched);
}
}
@@ -260,7 +249,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
public boolean isEmpty() {
initAsUntouched();
init();
return set.isEmpty();
}
@@ -71,4 +71,11 @@ class ModifyHolder<E> implements Serializable {
Set<E> getModifyRemovals() {
return modifyDeletions;
}
/**
* Return true if there additions or removals.
*/
boolean hasModifications() {
return !modifyDeletions.isEmpty() || !modifyAdditions.isEmpty();
}
}
@@ -3,6 +3,8 @@ package com.avaje.ebean.config;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.dbplatform.DbPlatformName;
import com.avaje.ebean.dbmigration.DbMigration;
import org.avaje.dbmigration.MigrationConfig;
import org.avaje.dbmigration.MigrationRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -97,7 +99,7 @@ public class DbMigrationConfig {
/**
* DB user used to run the DB migration.
*/
protected String dbUser;
protected String dbUsername;
/**
* DB password used to run the DB migration.
@@ -242,14 +244,6 @@ public class DbMigrationConfig {
this.name = name;
}
/**
* Set the model, rollback and drop paths to be empty such that all the migration files are generated
* into a single directory.
*/
public void singleDirectory() {
this.modelPath = "";
}
/**
* Return the table name that holds the migration run details
* (used by DB Migration runner only).
@@ -323,22 +317,22 @@ public class DbMigrationConfig {
}
/**
* Return the DB user to use for running DB migrations.
* Return the DB username to use for running DB migrations.
*/
public String getDbUser() {
public String getDbUsername() {
// environment properties take precedence
String user = readEnvironment("ddl.migration.user");
if (user != null) {
return user;
}
return dbUser;
return dbUsername;
}
/**
* Set the DB user to use for running DB migrations.
* Set the DB username to use for running DB migrations.
*/
public void setDbUser(String dbUser) {
this.dbUser = dbUser;
public void setDbUsername(String dbUsername) {
this.dbUsername = dbUsername;
}
/**
@@ -365,11 +359,7 @@ public class DbMigrationConfig {
public void loadSettings(PropertiesWrapper properties, String serverName) {
migrationPath = properties.get("migration.migrationPath", migrationPath);
if (properties.getBoolean("migration.singleDirectory", false)) {
singleDirectory();
} else {
modelPath = properties.get("migration.modelPath", modelPath);
}
modelPath = properties.get("migration.modelPath", modelPath);
applyPrefix = properties.get("migration.applyPrefix", applyPrefix);
applySuffix = properties.get("migration.applySuffix", applySuffix);
modelSuffix = properties.get("migration.modelSuffix", modelSuffix);
@@ -386,9 +376,9 @@ public class DbMigrationConfig {
metaTable = properties.get("migration.metaTable", metaTable);
runPlaceholders = properties.get("migration.placeholders", runPlaceholders);
String adminUser = properties.get("datasource."+serverName+".username", dbUser);
String adminUser = properties.get("datasource."+serverName+".username", dbUsername);
adminUser = properties.get("datasource."+serverName+".adminusername", adminUser);
dbUser = properties.get("migration.dbuser", adminUser);
dbUsername = properties.get("migration.dbusername", adminUser);
String adminPwd = properties.get("datasource."+serverName+".password", dbPassword);
adminPwd = properties.get("datasource."+serverName+".adminpassword", adminPwd);
@@ -494,4 +484,20 @@ public class DbMigrationConfig {
return val == null || val.trim().isEmpty();
}
/**
* Create the MigrationRunner to run migrations if necessary.
*/
public MigrationRunner createRunner(ClassLoader classLoader) {
MigrationConfig runnerConfig = new MigrationConfig();
runnerConfig.setMetaTable(metaTable);
runnerConfig.setApplySuffix(applySuffix);
runnerConfig.setMigrationPath(migrationPath);
runnerConfig.setRunPlaceholderMap(runPlaceholderMap);
runnerConfig.setRunPlaceholders(runPlaceholders);
runnerConfig.setDbUsername(getDbUsername());
runnerConfig.setDbPassword(getDbPassword());
runnerConfig.setClassLoader(classLoader);
return new MigrationRunner(runnerConfig);
}
}
@@ -83,7 +83,6 @@ public class DocStoreConfig {
if (systemValue != null) {
return systemValue;
}
return url;
}
@@ -100,6 +99,10 @@ public class DocStoreConfig {
* Return true if Ebean should generate mapping files on server startup.
*/
public boolean isGenerateMapping() {
String systemValue = System.getProperty("ebean.docstore.generateMapping");
if (systemValue != null) {
return Boolean.parseBoolean(systemValue);
}
return generateMapping;
}
@@ -114,6 +117,10 @@ public class DocStoreConfig {
* Return true if the document store should recreate mapped indexes.
*/
public boolean isDropCreate() {
String systemValue = System.getProperty("ebean.docstore.dropCreate");
if (systemValue != null) {
return Boolean.parseBoolean(systemValue);
}
return dropCreate;
}
@@ -129,6 +136,10 @@ public class DocStoreConfig {
* This is only used if dropCreate is false.
*/
public boolean isCreate() {
String systemValue = System.getProperty("ebean.docstore.create");
if (systemValue != null) {
return Boolean.parseBoolean(systemValue);
}
return create;
}
@@ -47,23 +47,16 @@ import java.util.ServiceLoader;
* </p>
*
* <pre>{@code
*
* ServerConfig c = new ServerConfig();
* c.setName("ordh2");
* c.setName("db");
*
* // read the ebean.properties and load
* // those settings into this serverConfig object
* c.loadFromProperties();
*
* // generate DDL and run it
* c.setDdlGenerate(true);
* c.setDdlRun(true);
*
* // add any classes found in the app.data package
* c.addPackage("app.data");
*
* // add the names of Jars that contain entities
* c.addJar("myJarContainingEntities.jar");
* c.addJar("someOtherJarContainingEntities.jar");
* c.addPackage("com.myapp.domain");
*
* // register as the 'Default' server
* c.setDefaultServer(true);
@@ -128,17 +121,6 @@ public class ServerConfig {
*/
private List<String> packages = new ArrayList<String>();
/**
* The names of Jar files that are searched for entities and other interesting
* classes. Only used when classes is empty/not explicitly specified.
*/
private List<String> searchJars = new ArrayList<String>();
/**
* Class name of a classPathReader implementation.
*/
private String classPathReaderClassName;
/**
* Configuration for the ElasticSearch integration.
*/
@@ -238,6 +220,11 @@ public class ServerConfig {
private String ddlSeedSql;
/**
* When true L2 bean cache use is skipped after a write has occurred on a transaction.
*/
private boolean skipCacheAfterWrite = true;
private boolean useJtaTransactionManager;
/**
@@ -1790,67 +1777,6 @@ public class ServerConfig {
this.packages = packages;
}
/**
* Add the name of a Jar to search for entities via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
* <p>
* If you are using ebean.properties you can specify jars to search by setting
* a ebean.search.jars property.
* </p>
*
* <pre>{@code
* # EBean will search through classes for entities, but will not search jar files
* # unless you tell it to do so, for performance reasons. Set this value to a
* # comma-delimited list of jar files you want ebean to search.
* ebean.search.jars=example.jar
* }</pre>
*/
public void addJar(String jarName) {
if (searchJars == null) {
searchJars = new ArrayList<String>();
}
searchJars.add(jarName);
}
/**
* Return packages to search for entities via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
*/
public List<String> getJars() {
return searchJars;
}
/**
* Set the names of Jars to search for entities via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
*/
public void setJars(List<String> searchJars) {
this.searchJars = searchJars;
}
/**
* Return the class name of a classPathReader implementation.
*/
public String getClassPathReaderClassName() {
return classPathReaderClassName;
}
/**
* Set the class name of a classPathReader implementation.
* <p>
* Refer to server.util.ClassPathReader, this should really by a plugin but doing this for now
* to be relatively compatible with current implementation.
*/
public void setClassPathReaderClassName(String classPathReaderClassName) {
this.classPathReaderClassName = classPathReaderClassName;
}
/**
* Set the list of classes (entities, listeners, scalarTypes etc) that should
* be used for this server.
@@ -1874,6 +1800,66 @@ public class ServerConfig {
return classes;
}
/**
* Return true if L2 bean cache should be skipped once writes have occurred on a transaction.
* <p>
* This defaults to true and means that for "find by id" and "find by natural key"
* queries that normally hit L2 bean cache automatically will not do so after a write/persist
* on the transaction.
* </p>
*
* <pre>{@code
*
* // assume Customer has L2 bean caching enabled ...
*
* Transaction transaction = Ebean.beginTransaction();
* try {
*
* // this uses L2 bean cache as the transaction
* // ... is considered "query only" at this point
* Customer.find.byId(42);
*
* // transaction no longer "query only" once
* // ... a bean has been saved etc
* Ebean.save(someBean);
*
* // will NOT use L2 bean cache as the transaction
* // ... is no longer considered "query only"
* Customer.find.byId(55);
*
*
*
* // explicit control - please use L2 bean cache
*
* transaction.setSkipCache(false);
* Customer.find.byId(77); // hit the l2 bean cache
*
*
* // explicit control - please don't use L2 bean cache
*
* transaction.setSkipCache(true);
* Customer.find.byId(99); // skips l2 bean cache
*
*
* } finally {
* transaction.end();
* }
*
* }</pre>
*
* @see com.avaje.ebean.Transaction#setSkipCache(boolean)
*/
public boolean isSkipCacheAfterWrite() {
return skipCacheAfterWrite;
}
/**
* Set to false when we still want to hit the cache after a write has occurred on a transaction.
*/
public void setSkipCacheAfterWrite(boolean skipCacheAfterWrite) {
this.skipCacheAfterWrite = skipCacheAfterWrite;
}
/**
* Return true to only update changed properties.
*/
@@ -2382,12 +2368,6 @@ public class ServerConfig {
serverCachePlugin = createInstance(p, ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
serverCacheManager = createInstance(p, ServerCacheManager.class, "serverCacheManager", serverCacheManager);
cacheWarmingDelay = p.getInt("cacheWarmingDelay", cacheWarmingDelay);
classPathReaderClassName = p.get("classpathreader");
String jarsProp = p.get("search.jars", p.get("jars", null));
if (jarsProp != null) {
searchJars = getSearchJarsPackages(jarsProp);
}
if (packages != null) {
String packagesProp = p.get("search.packages", p.get("packages", null));
@@ -2397,6 +2377,7 @@ public class ServerConfig {
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
skipCacheAfterWrite = p.getBoolean("skipCacheAfterWrite", skipCacheAfterWrite);
updateAllPropertiesInBatch = p.getBoolean("updateAllPropertiesInBatch", updateAllPropertiesInBatch);
updateChangesOnly = p.getBoolean("updateChangesOnly", updateChangesOnly);
@@ -0,0 +1,24 @@
package com.avaje.ebean.config.dbplatform;
/**
* Adds ANSI based OFFSET FETCH NEXT clauses to a SQL query.
*/
public class BasicSqlAnsiLimiter implements BasicSqlLimiter {
@Override
public String limit(String dbSql, int firstRow, int maxRows) {
StringBuilder sb = new StringBuilder(50 + dbSql.length());
sb.append(dbSql);
if (firstRow > 0) {
sb.append(" ").append("offset");
sb.append(" ").append(firstRow).append(" rows");
}
if (maxRows > 0) {
sb.append(" fetch next ").append(maxRows).append(" rows only");
}
return sb.toString();
}
}
@@ -0,0 +1,36 @@
package com.avaje.ebean.config.dbplatform;
/**
* Adds LIMIT OFFSET clauses to a SQL query.
*/
public class BasicSqlLimitOffset implements BasicSqlLimiter {
/**
* LIMIT keyword.
*/
private static final String LIMIT = "limit";
/**
* OFFSET keyword.
*/
private static final String OFFSET = "offset";
@Override
public String limit(String dbSql, int firstRow, int maxRows) {
StringBuilder sb = new StringBuilder(50 + dbSql.length());
sb.append(dbSql);
if (maxRows > 0) {
sb.append(" ").append(LIMIT);
sb.append(" ").append(maxRows);
}
if (firstRow > 0) {
sb.append(" ").append(OFFSET).append(" ");
sb.append(firstRow);
}
return sb.toString();
}
}
@@ -0,0 +1,12 @@
package com.avaje.ebean.config.dbplatform;
/**
* Simple SQL limiter for use with SqlQuery.
*/
public interface BasicSqlLimiter {
/**
* Add basic offset/limit clause to SqlQuery query.
*/
String limit(String dbSql, int firstRow, int maxRows);
}
@@ -71,6 +71,11 @@ public class DatabasePlatform {
*/
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
/**
* Limit/offset support for SqlQuery only.
*/
protected BasicSqlLimiter basicSqlLimiter = new BasicSqlLimitOffset();
/**
* Mapping of JDBC to Database types.
*/
@@ -454,6 +459,13 @@ public class DatabasePlatform {
return sqlLimiter;
}
/**
* Return the BasicSqlLimiter for limit/offset of SqlQuery queries.
*/
public BasicSqlLimiter getBasicSqlLimiter() {
return basicSqlLimiter;
}
/**
* Set the DB TRUE literal (from the registered boolean ScalarType)
*/
@@ -18,6 +18,7 @@ public class OraclePlatform extends DatabasePlatform {
this.maxConstraintNameLength = 30;
this.dbEncrypt = new OracleDbEncrypt();
this.sqlLimiter = new RownumSqlLimiter();
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
this.platformDdl = new Oracle10Ddl(this);
this.historySupport = new OracleDbHistorySupport();
@@ -114,7 +114,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
public Object nextId(Transaction t) {
synchronized (monitor) {
if (idList.size() == 0) {
if (idList.isEmpty()) {
loadMoreIds(batchSize, t);
}
Long nextId = idList.remove(0);
@@ -193,7 +193,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
while (rset.next()) {
newIds.add(rset.getLong(1));
}
if (newIds.size() == 0) {
if (newIds.isEmpty()) {
throw new PersistenceException("Always expecting more than 1 row from " + sql);
}
@@ -1,8 +1,7 @@
package com.avaje.ebean.config.dbplatform;
/**
* Use top xx and start at xx to limit sql results. Based on
* MsSqlServer2005SqlLimiter and LimitOffsetSqlLimiter
* Use top xx and start at xx to limit sql results.
*/
public class SqlAnywhereLimiter implements SqlLimiter {
@@ -5,6 +5,7 @@ import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.model.CurrentModel;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import org.avaje.dbmigration.ddl.DdlRunner;
import javax.persistence.PersistenceException;
import java.io.File;
@@ -1,110 +0,0 @@
package com.avaje.ebean.dbmigration;
import javax.persistence.PersistenceException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
/**
* Parses string content into separate SQL/DDL statements.
*/
public class DdlParser {
/**
* Break up the sql in reader into a list of statements using the semi-colon and $$ delimiters;
*/
public List<String> parse(StringReader reader) {
try {
BufferedReader br = new BufferedReader(reader);
StatementsSeparator statements = new StatementsSeparator();
String s;
while ((s = br.readLine()) != null) {
s = s.trim();
statements.nextLine(s);
}
return statements.statements;
} catch (IOException e) {
throw new PersistenceException(e);
}
}
/**
* Local utility used to detect the end of statements / separate statements.
* This is often just the semicolon character but for trigger/procedures this
* detects the $$ demarcation used in the history DDL generation for MySql and
* Postgres.
*/
static class StatementsSeparator {
ArrayList<String> statements = new ArrayList<String>();
boolean trimDelimiter;
boolean inDbProcedure;
StringBuilder sb = new StringBuilder();
void lineContainsDollars(String line) {
if (inDbProcedure) {
if (trimDelimiter) {
line = line.replace("$$","");
}
endOfStatement(line);
} else {
// MySql style delimiter needs to be trimmed/removed
trimDelimiter = line.equals("delimiter $$");
if (!trimDelimiter) {
sb.append(line).append(" ");
}
}
inDbProcedure = !inDbProcedure;
}
void endOfStatement(String line) {
// end of Db procedure
sb.append(line);
statements.add(sb.toString().trim());
sb = new StringBuilder();
}
void nextLine(String line) {
if (line.contains("$$")) {
lineContainsDollars(line);
return;
}
if (sb.length() == 0 && (line.isEmpty() || line.startsWith("--"))) {
// ignore leading empty lines and sql comments
return;
}
if (inDbProcedure) {
sb.append(line).append(" ");
return;
}
int semiPos = line.indexOf(';');
if (semiPos == -1) {
sb.append(line).append(" ");
} else if (semiPos == line.length() - 1) {
// semicolon at end of line
endOfStatement(line);
} else {
// semicolon in middle of line
String preSemi = line.substring(0, semiPos);
endOfStatement(preSemi);
sb.append(line.substring(semiPos + 1));
}
}
}
}
@@ -1,115 +0,0 @@
package com.avaje.ebean.dbmigration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Runs DDL scripts.
*/
public class DdlRunner {
protected static final Logger logger = LoggerFactory.getLogger(DdlRunner.class);
private DdlParser ddlParser = new DdlParser();
private final String scriptName;
private final boolean expectErrors;
/**
* Construct with a script name (for logging) and flag indicating if errors are expected.
*/
public DdlRunner(boolean expectErrors, String scriptName) {
this.expectErrors = expectErrors;
this.scriptName = scriptName;
}
/**
* Parse the content into sql statements and execute them in a transaction.
*/
public int runAll(String content, Connection connection) throws SQLException {
List<String> statements = ddlParser.parse(new StringReader(content));
return runStatements(statements, connection);
}
/**
* Execute all the statements in a single transaction.
*/
private int runStatements(List<String> statements, Connection connection) throws SQLException {
List<String> noDuplicates = new ArrayList<String>();
for (String statement : statements) {
if (!noDuplicates.contains(statement)) {
noDuplicates.add(statement);
}
}
logger.info("Executing {} - {} statements", scriptName, noDuplicates.size());
for (int i = 0; i < noDuplicates.size(); i++) {
String xOfy = (i + 1) + " of " + noDuplicates.size();
runStatement(expectErrors, xOfy, noDuplicates.get(i), connection);
}
return noDuplicates.size();
}
/**
* Execute the statement.
*/
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) throws SQLException {
PreparedStatement pstmt = null;
try {
// trim and remove trailing ; or /
stmt = stmt.trim();
if (stmt.endsWith(";")) {
stmt = stmt.substring(0, stmt.length() - 1);
} else if (stmt.endsWith("/")) {
stmt = stmt.substring(0, stmt.length() - 1);
}
if (logger.isDebugEnabled()) {
logger.debug("executing " + oneOf + " " + getSummary(stmt));
}
pstmt = c.prepareStatement(stmt);
pstmt.execute();
} catch (SQLException e) {
if (expectErrors) {
logger.debug(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage());
} else {
String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]";
throw new SQLException(msg, e);
}
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
logger.error("Error closing pstmt", e);
}
}
}
}
private String getSummary(String s) {
if (s.length() > 80) {
return s.substring(0, 80).trim() + "...";
}
return s;
}
}
@@ -1,111 +0,0 @@
package com.avaje.ebean.dbmigration;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.DbMigrationConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.runner.LocalMigrationResource;
import com.avaje.ebean.dbmigration.runner.LocalMigrationResources;
import com.avaje.ebean.dbmigration.runner.MigrationTable;
import com.avaje.ebeaninternal.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* Runs the DB migration typically on application start.
*/
public class MigrationRunner {
public static final Logger logger = LoggerFactory.getLogger("org.avaje.ebean.DbMigration");
private final EbeanServer server;
private final ServerConfig config;
private final DbMigrationConfig migrationConfig;
public MigrationRunner(EbeanServer server, DbMigrationConfig migrationConfig) {
this.server = server;
this.config = server.getPluginApi().getServerConfig();
this.migrationConfig = migrationConfig;
}
/**
* Run the migrations if there are any that need running.
*/
public void run() {
LocalMigrationResources resources = new LocalMigrationResources(config, migrationConfig);
if (!resources.readResources()) {
logger.debug("no migrations to check");
return;
}
String migrationUser = migrationConfig.getDbUser();
String migrationPwd = migrationConfig.getDbPassword();
if (migrationUser == null) {
throw new IllegalStateException("No DB migration user specified (to run the db migration) ?");
}
DataSource dataSource = server.getPluginApi().getDataSource();
if (dataSource == null) {
throw new IllegalStateException("No dataSource when trying to run migration? "
+"Maybe trying to generate DBMigration when ebean.migration.run=true is set? "
+"Perhaps need to set ebean.migration.run=false in test-ebean.properties?");
}
Connection connection;
try {
connection = dataSource.getConnection(migrationUser, migrationPwd);
} catch (SQLException e) {
throw new IllegalArgumentException("Error trying to connect to database using DB Migration user [" + migrationUser + "]", e);
}
try {
logger.debug("using db user [{}] to run migrations ...");
connection.setAutoCommit(false);
runMigrations(resources, connection);
connection.commit();
} catch (Exception e) {
JdbcClose.rollback(connection);
throw new RuntimeException(e);
} finally {
JdbcClose.close(connection);
}
}
/**
* Run all the migrations as needed.
*/
private void runMigrations(LocalMigrationResources resources, Connection connection) throws SQLException, IOException {
MigrationTable table = new MigrationTable(server, migrationConfig, connection);
table.createIfNeeded();
// get the migrations in version order
List<LocalMigrationResource> localVersions = resources.getVersions();
logger.info("local migrations:{} existing migrations:{}", localVersions.size(), table.size());
LocalMigrationResource priorVersion = null;
// run migrations in order
for (int i = 0; i < localVersions.size(); i++) {
LocalMigrationResource localVersion = localVersions.get(i);
if (!table.shouldRun(localVersion, priorVersion)) {
break;
}
priorVersion = localVersion;
connection.commit();
}
}
}
@@ -5,6 +5,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.platform.BaseTableDdl;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import com.avaje.ebean.dbmigration.migration.AddColumn;
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
import com.avaje.ebean.dbmigration.migration.AddTableComment;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.ChangeSet;
import com.avaje.ebean.dbmigration.migration.CreateIndex;
@@ -37,6 +38,8 @@ public class BaseDdlHandler implements DdlHandler {
generate(writer, (CreateTable) change);
} else if (change instanceof DropTable) {
generate(writer, (DropTable) change);
} else if (change instanceof AddTableComment) {
generate(writer, (AddTableComment) change);
} else if (change instanceof CreateIndex) {
generate(writer, (CreateIndex) change);
} else if (change instanceof DropIndex) {
@@ -70,6 +73,11 @@ public class BaseDdlHandler implements DdlHandler {
tableDdl.generate(writer, dropTable);
}
@Override
public void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException {
tableDdl.generate(writer, addTableComment);
}
@Override
public void generate(DdlWrite writer, AddColumn addColumn) throws IOException {
tableDdl.generate(writer, addColumn);
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.ddlgeneration;
import com.avaje.ebean.dbmigration.migration.AddColumn;
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
import com.avaje.ebean.dbmigration.migration.AddTableComment;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.ChangeSet;
import com.avaje.ebean.dbmigration.migration.CreateIndex;
@@ -14,6 +15,7 @@ import com.avaje.ebean.dbmigration.migration.DropTable;
import java.io.IOException;
/**
* DDL generation interface.
*/
public interface DdlHandler {
@@ -23,6 +25,8 @@ public interface DdlHandler {
void generate(DdlWrite writer, DropTable dropTable) throws IOException;
void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException;
void generate(DdlWrite writer, AddColumn addColumn) throws IOException;
void generate(DdlWrite writer, DropColumn dropColumn) throws IOException;
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.ddlgeneration;
import com.avaje.ebean.dbmigration.migration.AddColumn;
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
import com.avaje.ebean.dbmigration.migration.AddTableComment;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.CreateIndex;
import com.avaje.ebean.dbmigration.migration.CreateTable;
@@ -42,6 +43,11 @@ public interface TableDdl {
*/
void generate(DdlWrite writer, DropColumn dropColumn) throws IOException;
/**
* Write the AddTableComment change.
*/
void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException;
/**
* Write the AddHistoryTable change.
*/
@@ -10,6 +10,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.TableDdl;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.IndexSet;
import com.avaje.ebean.dbmigration.migration.AddColumn;
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
import com.avaje.ebean.dbmigration.migration.AddTableComment;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.Column;
import com.avaje.ebean.dbmigration.migration.CreateIndex;
@@ -554,6 +555,13 @@ public class BaseTableDdl implements TableDdl {
}
}
@Override
public void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException {
if (hasValue(addTableComment.getComment())) {
platformDdl.addTableComment(writer.apply(), addTableComment.getName(), addTableComment.getComment());
}
}
/**
* Add add column DDL.
*/
@@ -634,7 +642,16 @@ public class BaseTableDdl implements TableDdl {
if (hasValue(alterColumn.getUniqueOneToOne())) {
alterColumnAddUniqueOneToOneConstraint(writer, alterColumn);
}
if (hasValue(alterColumn.getComment())) {
alterColumnComment(writer, alterColumn);
}
boolean alterCheckConstraint = hasValue(alterColumn.getCheckConstraint());
if (alterCheckConstraint) {
// drop constraint before altering type etc
dropCheckConstraint(writer, alterColumn);
}
boolean alterBaseAttributes = false;
if (hasValue(alterColumn.getType())) {
alterColumnType(writer, alterColumn);
@@ -648,10 +665,17 @@ public class BaseTableDdl implements TableDdl {
alterColumnNotnull(writer, alterColumn);
alterBaseAttributes = true;
}
if (alterBaseAttributes) {
alterColumnBaseAttributes(writer, alterColumn);
}
if (alterCheckConstraint) {
// add constraint last (after potential type change)
addCheckConstraint(writer, alterColumn);
}
}
private void alterColumnComment(DdlWrite writer, AlterColumn alterColumn) throws IOException {
platformDdl.addColumnComment(writer.apply(), alterColumn.getTableName(), alterColumn.getColumnName(), alterColumn.getComment());
}
/**
@@ -705,6 +729,22 @@ public class BaseTableDdl implements TableDdl {
}
}
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
String ddl = platformDdl.alterTableDropConstraint(alter.getTableName(), alter.getCheckConstraintName());
if (hasValue(ddl)) {
writer.apply().append(ddl).endOfStatement();
}
}
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
String ddl = platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint());
if (hasValue(ddl)) {
writer.apply().append(ddl).endOfStatement();
}
}
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
String ddl = platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull());
@@ -131,11 +131,11 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
}
protected String historyTableName(String baseTableName) {
return normalise(baseTableName) + historySuffix;
return baseTableName + historySuffix;
}
protected String procedureName(String baseTableName) {
return normalise(baseTableName) + "_history_version";
return baseTableName + "_history_version";
}
protected String triggerName(String baseTableName) {
@@ -38,8 +38,13 @@ public class MySqlDdl extends PlatformDdl {
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
public String alterTableDropConstraint(String tableName, String constraintName) {
// drop constraint not supported
return null;
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
// can't alter itself - done in alterColumnBaseAttributes()
return null;
}
@@ -67,8 +67,12 @@ public class PlatformDdl {
protected String alterColumn = "alter column";
protected String dropConstraint = "drop constraint";
protected String dropUniqueConstraint = "drop constraint";
protected String addConstraint = "add constraint";
protected String columnSetType = "";
protected String columnSetDefault = "set default";
@@ -327,12 +331,19 @@ public class PlatformDdl {
}
/**
* Drop a unique constraint from the table.
* Drop a unique constraint from the table (Sometimes this is an index).
*/
public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) {
return "alter table " + tableName + " " + dropUniqueConstraint + " " + uniqueConstraintName;
}
/**
* Drop a unique constraint from the table.
*/
public String alterTableDropConstraint(String tableName, String constraintName) {
return "alter table " + tableName + " " + dropConstraint + " " + constraintName;
}
/**
* Add a unique constraint to the table.
* <p>
@@ -377,6 +388,14 @@ public class PlatformDdl {
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix;
}
/**
* Alter table adding the check constraint.
*/
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
return "alter table " + tableName + " " + addConstraint + " " + checkConstraintName + " " + checkConstraint;
}
/**
* Return true if the default value is the special DROP DEFAULT value.
*/
@@ -13,6 +13,7 @@ public class PostgresDdl extends PlatformDdl {
this.dropTableCascade = " cascade";
this.columnSetType = "type ";
this.alterTableIfExists = "if exists ";
this.columnSetNull = "drop not null";
}
/**
@@ -0,0 +1,87 @@
package com.avaje.ebean.dbmigration.migration;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="comment" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "addTableComment")
public class AddTableComment {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "comment", required = true)
protected String comment;
/**
* Gets the value of the name property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getName() {
return name;
}
/**
* Sets the value of the name property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setName(String value) {
this.name = value;
}
/**
* Gets the value of the comment property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getComment() {
return comment;
}
/**
* Sets the value of the comment property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setComment(String value) {
this.comment = value;
}
}
@@ -26,6 +26,7 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attribute name="currentDefaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="notnull" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="currentNotnull" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="historyExclude" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="checkConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="checkConstraintName" type="{http://www.w3.org/2001/XMLSchema}string" />
@@ -68,6 +69,8 @@ public class AlterColumn {
protected Boolean notnull;
@XmlAttribute(name = "currentNotnull")
protected Boolean currentNotnull;
@XmlAttribute(name = "comment")
protected String comment;
@XmlAttribute(name = "historyExclude")
protected Boolean historyExclude;
@XmlAttribute(name = "checkConstraint")
@@ -309,6 +312,30 @@ public class AlterColumn {
this.currentNotnull = value;
}
/**
* Gets the value of the comment property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getComment() {
return comment;
}
/**
* Sets the value of the comment property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setComment(String value) {
this.comment = value;
}
/**
* Gets the value of the historyExclude property.
*
@@ -52,6 +52,7 @@ public class ChangeSet {
@XmlElement(name = "createTable", type = CreateTable.class),
@XmlElement(name = "dropTable", type = DropTable.class),
@XmlElement(name = "renameTable", type = RenameTable.class),
@XmlElement(name = "addTableComment", type = AddTableComment.class),
@XmlElement(name = "addHistoryTable", type = AddHistoryTable.class),
@XmlElement(name = "dropHistoryTable", type = DropHistoryTable.class),
@XmlElement(name = "addColumn", type = AddColumn.class),
@@ -98,6 +99,7 @@ public class ChangeSet {
* {@link CreateTable }
* {@link DropTable }
* {@link RenameTable }
* {@link AddTableComment }
* {@link AddHistoryTable }
* {@link DropHistoryTable }
* {@link AddColumn }
@@ -101,6 +101,14 @@ public class ObjectFactory {
return new DefaultTablespace();
}
/**
* Create an instance of {@link AddTableComment }
*
*/
public AddTableComment createAddTableComment() {
return new AddTableComment();
}
/**
* Create an instance of {@link RenameTable }
*
@@ -260,7 +260,7 @@ public class MColumn {
return c;
}
private boolean different(String val1, String val2) {
protected static boolean different(String val1, String val2) {
return (val1 == null) ? val2 != null : !val1.equals(val2);
}
@@ -268,6 +268,10 @@ public class MColumn {
return val != null && !val.isEmpty();
}
private boolean hasValue(Boolean val) {
return val != null;
}
private AlterColumn getAlterColumn(String tableName, boolean tableWithHistory) {
if (alterColumn == null) {
alterColumn = new AlterColumn();
@@ -314,10 +318,13 @@ public class MColumn {
alter.setDefaultValue(newColumn.defaultValue);
}
}
if (different(comment, newColumn.comment)) {
AlterColumn alter = getAlterColumn(tableName, tableWithHistory);
alter.setComment(newColumn.comment);
}
if (different(checkConstraint, newColumn.checkConstraint)) {
AlterColumn alter = getAlterColumn(tableName, tableWithHistory);
if (hasValue(checkConstraint)) {
if (hasValue(checkConstraint) && !hasValue(newColumn.checkConstraint)) {
alter.setDropCheckConstraint(checkConstraintName);
}
if (hasValue(newColumn.checkConstraint)) {
@@ -393,6 +400,9 @@ public class MColumn {
if (hasValue(alterColumn.getType())) {
type = alterColumn.getType();
}
if (hasValue(alterColumn.isNotnull())) {
notnull = alterColumn.isNotnull();
}
if (hasValue(alterColumn.getDefaultValue())) {
defaultValue = alterColumn.getDefaultValue();
}
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.model;
import com.avaje.ebean.dbmigration.migration.AddColumn;
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
import com.avaje.ebean.dbmigration.migration.AddTableComment;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.Column;
import com.avaje.ebean.dbmigration.migration.CreateTable;
@@ -285,6 +286,13 @@ public class MTable {
if (addColumn != null) {
modelDiff.addAddColumn(addColumn);
}
if (MColumn.different(comment, newTable.comment)) {
AddTableComment addTableComment = new AddTableComment();
addTableComment.setName(name);
addTableComment.setComment(newTable.comment);
modelDiff.addTableComment(addTableComment);
}
}
/**
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.model;
import com.avaje.ebean.dbmigration.migration.AddColumn;
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
import com.avaje.ebean.dbmigration.migration.AddTableComment;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.ChangeSet;
import com.avaje.ebean.dbmigration.migration.ChangeSetType;
@@ -237,4 +238,11 @@ public class ModelDiff {
public void addCreateIndex(CreateIndex createIndex) {
applyChanges.add(createIndex);
}
/**
* Add a table comment to the 'apply' changes.
*/
public void addTableComment(AddTableComment addTableComment) {
applyChanges.add(addTableComment);
}
}
@@ -11,12 +11,13 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated
@@ -249,9 +250,13 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setUnique(determineUniqueConstraintName(col.getName()));
indexSetAdd(col.getName());
}
String checkConstraint = p.getDbConstraintExpression();
if (checkConstraint != null) {
col.setCheckConstraint(checkConstraint);
Set<String> checkConstraintValues = p.getDbCheckConstraintValues();
if (checkConstraintValues != null) {
if (beanDescriptor.hasInheritance()) {
InheritInfo inheritInfo = beanDescriptor.getInheritInfo();
inheritInfo.appendCheckConstraintValues(p.getName(), checkConstraintValues);
}
col.setCheckConstraint(buildCheckConstraint(p.getDbColumn(), checkConstraintValues));
col.setCheckConstraintName(determineCheckConstraintName(col.getName()));
}
@@ -268,6 +273,22 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
table.addColumn(col);
}
/**
* Build the check constraint clause given the db column and values.
*/
private String buildCheckConstraint(String dbColumn, Set<String> checkConstraintValues) {
StringBuilder sb = new StringBuilder();
sb.append("check ( ").append(dbColumn).append(" in (");
int count = 0;
for (String value : checkConstraintValues) {
if (count++ > 0) {
sb.append(",");
}
sb.append(value);
}
sb.append("))");
return sb.toString();
}
private void indexSetAdd(String column) {
indexSet.add(column);
@@ -1,32 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.zip.CRC32;
/**
* Calculates the checksum for the given string content.
*/
public class Checksum {
/**
* Returns the checksum of this string.
*/
public static int calculate(String str) {
final CRC32 crc32 = new CRC32();
BufferedReader bufferedReader = new BufferedReader(new StringReader(str));
try {
String line;
while ((line = bufferedReader.readLine()) != null) {
crc32.update(line.getBytes("UTF-8"));
}
} catch (IOException e) {
throw new RuntimeException("Failed to calculate checksum", e);
}
return (int) crc32.getValue();
}
}
@@ -1,101 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import com.avaje.ebean.dbmigration.model.MigrationVersion;
import org.avaje.classpath.scanner.Resource;
/**
* A DB migration resource (DDL script with version).
*/
public class LocalMigrationResource implements Comparable<LocalMigrationResource> {
/**
* Code for repeatable migrations.
*/
private static final String REPEAT_TYPE = "R";
/**
* Code for version migrations.
*/
private static final String VERSION_TYPE = "V";
private final MigrationVersion version;
private final String location;
private final Resource resource;
/**
* Construct with version and resource.
*/
public LocalMigrationResource(MigrationVersion version, String location, Resource resource) {
this.version = version;
this.location = location;
this.resource = resource;
}
public String toString() {
return version.toString();
}
/**
* Return true if the underlying version is "repeatable".
*/
public boolean isRepeatable() {
return version.isRepeatable();
}
/**
* Return the "key" that identifies the migration.
*/
public String key() {
if (isRepeatable()) {
return version.getComment().toLowerCase();
} else {
return version.normalised();
}
}
/**
* Return the migration comment.
*/
public String getComment() {
String comment = version.getComment();
return (comment == null || comment.isEmpty()) ? "-" : comment;
}
/**
* Default ordering by version.
*/
@Override
public int compareTo(LocalMigrationResource o) {
return version.compareTo(o.version);
}
/**
* Return the underlying migration version.
*/
public MigrationVersion getVersion() {
return version;
}
/**
* Return the resource location.
*/
public String getLocation() {
return location;
}
/**
* Return the content for the migration apply ddl script.
*/
public String getContent() {
return resource.loadAsString("UTF-8");
}
/**
* Return the type code ("R" or "V") for this migration.
*/
public String getType() {
return isRepeatable() ? REPEAT_TYPE : VERSION_TYPE;
}
}
@@ -1,91 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import com.avaje.ebean.config.DbMigrationConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.model.MigrationVersion;
import org.avaje.classpath.scanner.Resource;
import org.avaje.classpath.scanner.ResourceFilter;
import org.avaje.classpath.scanner.Scanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Loads the DB migration resources and sorts them into execution order.
*/
public class LocalMigrationResources {
private static final Logger logger = LoggerFactory.getLogger(LocalMigrationResources.class);
private final ServerConfig serverConfig;
private final DbMigrationConfig migrationConfig;
private final List<LocalMigrationResource> versions = new ArrayList<LocalMigrationResource>();
/**
* Construct with configuration options.
*/
public LocalMigrationResources(ServerConfig serverConfig, DbMigrationConfig migrationConfig) {
this.serverConfig = serverConfig;
this.migrationConfig = migrationConfig;
}
/**
* Read all the migration resources (SQL scripts) returning true if there are versions.
*/
public boolean readResources() {
String migrationPath = migrationConfig.getMigrationPath();
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
Scanner scanner = new Scanner(classLoader);
List<Resource> resourceList = scanner.scanForResources(migrationPath, new Match(migrationConfig));
logger.debug("resources: {}", resourceList);
for (Resource resource : resourceList) {
String filename = resource.getFilename();
if (filename.endsWith(migrationConfig.getApplySuffix())) {
int pos = filename.lastIndexOf(migrationConfig.getApplySuffix());
String mainName = filename.substring(0, pos);
MigrationVersion migrationVersion = MigrationVersion.parse(mainName);
LocalMigrationResource res = new LocalMigrationResource(migrationVersion, resource.getLocation(), resource);
versions.add(res);
}
}
Collections.sort(versions);
return !versions.isEmpty();
}
/**
* Return the list of migration resources in version order.
*/
public List<LocalMigrationResource> getVersions() {
return versions;
}
/**
* Filter used to find the migration scripts.
*/
private static class Match implements ResourceFilter {
private final DbMigrationConfig migrationConfig;
Match(DbMigrationConfig migrationConfig) {
this.migrationConfig = migrationConfig;
}
@Override
public boolean isMatch(String name) {
return name.endsWith(migrationConfig.getApplySuffix());
}
}
}
@@ -1,106 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.SqlUpdate;
import java.sql.Timestamp;
/**
* Bean holding migration execution details stored in the migration table.
*/
class MigrationMetaRow {
private int id;
private String type;
private String version;
private String comment;
private int checksum;
private Timestamp runOn;
private String runBy;
private long runTime;
/**
* Construct for inserting into table.
*/
MigrationMetaRow(int id, String type, String version, String comment, int checksum, String runBy, Timestamp runOn, long runTime) {
this.id = id;
this.type = type;
this.version = version;
this.checksum = checksum;
this.comment = comment;
this.runBy = runBy;
this.runOn = runOn;
this.runTime = runTime;
}
/**
* Construct from the SqlRow (read from table).
*/
MigrationMetaRow(SqlRow row) {
id = row.getInteger("id");
type = row.getString("mtype");
version = row.getString("mversion");
comment = row.getString("mcomment");
checksum = row.getInteger("mchecksum");
runOn = row.getTimestamp("run_on");
runBy = row.getString("run_by");
runTime = row.getLong("run_time");
}
public String toString() {
return "id:" + id + " type:" + type + " runVersion:" + version + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
}
/**
* Return the id for this migration.
*/
int getId() {
return id;
}
/**
* Return the normalised version for this migration.
*/
String getVersion() {
return version;
}
/**
* Return the checksum for this migration.
*/
int getChecksum() {
return checksum;
}
/**
* Bind to the insert statement.
*/
void bindInsert(SqlUpdate insert) {
insert.setParameter(1, id);
insert.setParameter(2, type);
insert.setParameter(3, "SUCCESS");
insert.setParameter(4, version);
insert.setParameter(5, comment);
insert.setParameter(6, checksum);
insert.setParameter(7, runOn);
insert.setParameter(8, runBy);
insert.setParameter(9, runTime);
}
/**
* Return the SQL insert given the table migration meta data is stored in.
*/
static String insertSql(String table) {
return "insert into " + table
+ " (id, mtype, mstatus, mversion, mcomment, mchecksum, run_on, run_by, run_time)"
+ " values (?,?,?,?,?,?,?,?,?)";
}
}
@@ -1,30 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import com.avaje.ebean.dbmigration.DdlRunner;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Runs the DDL migration scripts.
*/
public class MigrationScriptRunner {
private final Connection connection;
/**
* Construct with a given connection.
*/
public MigrationScriptRunner(Connection connection) {
this.connection = connection;
}
/**
* Execute all the DDL statements in the script.
*/
int runScript(boolean expectErrors, String content, String scriptName) throws SQLException {
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
return runner.runAll(content, connection);
}
}
@@ -1,259 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.config.DbMigrationConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.dbmigration.MigrationRunner;
import com.avaje.ebean.plugin.SpiServer;
import com.avaje.ebeaninternal.server.transaction.ExternalJdbcTransaction;
import com.avaje.ebeaninternal.util.IOUtils;
import org.slf4j.Logger;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Manages the migration table.
*/
public class MigrationTable {
private static final Logger logger = MigrationRunner.logger;
private final Connection connection;
private final EbeanServer server;
private final DatabasePlatform databasePlatform;
private final String catalog;
private final String schema;
private final String table;
private final ServerConfig serverConfig;
private final String envUserName;
private final Timestamp runOn = new Timestamp(System.currentTimeMillis());
private final ScriptTransform scriptTransform;
private final String insertSql;
private final LinkedHashMap<String, MigrationMetaRow> migrations;
private MigrationMetaRow lastMigration;
/**
* Construct with server, configuration and jdbc connection (DB admin user).
*/
public MigrationTable(EbeanServer server, DbMigrationConfig migrationConfig, Connection connection) {
this.connection = connection;
this.server = server;
this.migrations = new LinkedHashMap<String, MigrationMetaRow>();
SpiServer pluginApi = server.getPluginApi();
this.serverConfig = pluginApi.getServerConfig();
this.databasePlatform = pluginApi.getDatabasePlatform();
this.catalog = null;
this.schema = null;
this.table = migrationConfig.getMetaTable();
this.insertSql = MigrationMetaRow.insertSql(table);
this.scriptTransform = createScriptTransform(migrationConfig);
this.envUserName = System.getProperty("user.name");
}
/**
* Return the number of migrations in the DB migration table.
*/
public int size() {
return migrations.size();
}
/**
* Create the ScriptTransform for placeholder key/value replacement.
*/
private ScriptTransform createScriptTransform(DbMigrationConfig config) {
Map<String, String> map = PlaceholderBuilder.build(config.getRunPlaceholders(), config.getRunPlaceholderMap());
return new ScriptTransform(map);
}
/**
* Create the table is it does not exist.
*/
public void createIfNeeded() throws SQLException, IOException {
if (!tableExists(connection)) {
createTable(connection);
}
ExternalJdbcTransaction t = new ExternalJdbcTransaction(connection);
SqlQuery sqlQuery = server.createSqlQuery("select * from " + table + " order by id for update");
List<SqlRow> metaRows = server.findList(sqlQuery, t);
for (SqlRow row : metaRows) {
MigrationMetaRow metaRow = new MigrationMetaRow(row);
addMigration(metaRow.getVersion(), metaRow);
}
}
private void createTable(Connection connection) throws IOException, SQLException {
String script = ScriptTransform.table(table, getCreateTableScript());
MigrationScriptRunner run = new MigrationScriptRunner(connection);
run.runScript(false, script, "create migration table");
}
/**
* Return the create table script.
*/
private String getCreateTableScript() throws IOException {
// supply a script to override the default table create script
String script = readResource("migration-support/create-table.sql");
if (script == null) {
// no, just use the default script
script = readResource("migration-support/default-create-table.sql");
}
return script;
}
private String readResource(String location) throws IOException {
Enumeration<URL> resources = serverConfig.getClassLoadConfig().getResources(location);
if (resources.hasMoreElements()) {
URL url = resources.nextElement();
return IOUtils.readUtf8(url.openStream());
}
return null;
}
/**
* Return true if the table exists.
*/
private boolean tableExists(Connection connection) throws SQLException {
return databasePlatform.tableExists(connection, catalog, schema, table);
}
/**
* Return true if the migration ran successfully and false if the migration failed.
*/
public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) throws SQLException {
if (priorVersion != null && !localVersion.isRepeatable()) {
if (!migrationExists(priorVersion)) {
logger.error("Migration {} requires prior migration {} which has not been run", localVersion.getVersion(), priorVersion.getVersion());
return false;
}
}
MigrationMetaRow existing = migrations.get(localVersion.key());
return runMigration(localVersion, existing);
}
/**
* Run the migration script.
*
* @param local The local migration resource
* @param existing The information for this migration existing in the table
*
* @return True if the migrations should continue
*/
private boolean runMigration(LocalMigrationResource local, MigrationMetaRow existing) throws SQLException {
String script = convertScript(local.getContent());
int checksum = Checksum.calculate(script);
if (existing != null) {
boolean matchChecksum = (existing.getChecksum() == checksum);
if (!local.isRepeatable()) {
if (!matchChecksum) {
logger.error("Checksum mismatch on migration {}", local.getLocation());
}
return true;
} else if (matchChecksum) {
logger.trace("... skip unchanged repeatable migration {}", local.getLocation());
return true;
}
}
runMigration(local, script, checksum);
return true;
}
/**
* Run a migration script as new migration or update on existing repeatable migration.
*/
private void runMigration(LocalMigrationResource local, String script, int checksum) throws SQLException {
logger.debug("run migration {}", local.getLocation());
long start = System.currentTimeMillis();
MigrationScriptRunner run = new MigrationScriptRunner(connection);
run.runScript(false, script, "run migration version: " + local.getVersion());
long exeMillis = System.currentTimeMillis() - start;
// insert new migration row
SqlUpdate insert = server.createSqlUpdate(insertSql);
MigrationMetaRow metaRow = createMetaRow(local, checksum, exeMillis);
metaRow.bindInsert(insert);
server.execute(insert, new ExternalJdbcTransaction(connection));
addMigration(local.key(), metaRow);
}
/**
* Create the MigrationMetaRow for this migration.
*/
private MigrationMetaRow createMetaRow(LocalMigrationResource migration, int checksum, long exeMillis) {
int nextId = 1;
if (lastMigration != null) {
nextId = lastMigration.getId() + 1;
}
String type = migration.getType();
String runVersion = migration.key();
String comment = migration.getComment();
return new MigrationMetaRow(nextId, type, runVersion, comment, checksum, envUserName, runOn, exeMillis);
}
/**
* Return true if the migration exists.
*/
private boolean migrationExists(LocalMigrationResource priorVersion) {
return migrations.containsKey(priorVersion.key());
}
/**
* Apply the placeholder key/value replacement on the script.
*/
private String convertScript(String script) {
return scriptTransform.transform(script);
}
/**
* Register the successfully executed migration (to allow dependant scripts to run).
*/
private void addMigration(String key, MigrationMetaRow metaRow) {
lastMigration = metaRow;
if (metaRow.getVersion() == null) {
throw new IllegalStateException("No runVersion in db migration table row? " + metaRow);
}
migrations.put(key, metaRow);
}
}
@@ -1,53 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import java.util.HashMap;
import java.util.Map;
/**
* Joins placeholder map and comma/equals delimited string.
*/
class PlaceholderBuilder {
private final Map<String,String> map = new HashMap<String,String>();
/**
* Create with raw comma and equals delimited pairs plus map of key value pairs.
*/
public static Map<String,String> build(String commaDelimited, Map<String,String> placeholders) {
PlaceholderBuilder builder = new PlaceholderBuilder();
builder.add(commaDelimited);
builder.add(placeholders);
return builder.map;
}
private PlaceholderBuilder() {
}
/**
* Add a comma and equals delimited string to parse for key value pairs.
*/
public void add(String commaDelimited) {
if (commaDelimited != null) {
String[] split = commaDelimited.split("[,;]");
for (String keyValue : split) {
String[] pair = keyValue.split("=");
if (pair.length == 2) {
map.put(pair[0].trim(), pair[1].trim());
}
}
}
}
/**
* Add a map of key value placeholder pairs.
*/
public void add(Map<String,String> placeholders) {
if (placeholders != null) {
map.putAll(placeholders);
}
}
}
@@ -1,47 +0,0 @@
package com.avaje.ebean.dbmigration.runner;
import java.util.HashMap;
import java.util.Map;
/**
* Transforms a SQL script given a map of key/value substitutions.
*/
class ScriptTransform {
/**
* Transform just ${table} with the table name.
*/
public static String table(String tableName, String script) {
return script.replace("${table}", tableName);
}
private final Map<String,String> placeholders = new HashMap<String, String>();
ScriptTransform(Map<String,String> map) {
for (Map.Entry<String, String> entry : map.entrySet()) {
placeholders.put(wrapKey(entry.getKey()), entry.getValue());
}
}
private String wrapKey(String key) {
return "${"+key+"}";
}
/**
* Return true if this contains no placeholders.
*/
boolean isEmpty() {
return placeholders.isEmpty();
}
/**
* Transform the script replacing placeholders in the form <code>${key}</code> with <code>value</code>.
*/
String transform(String source) {
for (Map.Entry<String, String> entry : placeholders.entrySet()) {
source = source.replace(entry.getKey(), entry.getValue());
}
return source;
}
}
@@ -19,8 +19,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
* @param bean The bean that was inserted.
*/
@Override
public boolean inserted(Object bean) {
return false;
public void inserted(Object bean) {
}
/**
@@ -31,8 +30,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
* @param updatedProperties The properties that were modified by this update.
*/
@Override
public boolean updated(Object bean, Set<String> updatedProperties) {
return false;
public void updated(Object bean, Set<String> updatedProperties) {
}
/**
@@ -42,8 +40,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
* @param bean The bean that was deleted.
*/
@Override
public boolean deleted(Object bean) {
return false;
public void deleted(Object bean) {
}
}
@@ -1,9 +1,9 @@
package com.avaje.ebean.event;
import java.util.Set;
import com.avaje.ebean.config.ServerConfig;
import java.util.Set;
/**
* Listens for committed bean events.
* <p>
@@ -13,22 +13,18 @@ import com.avaje.ebean.config.ServerConfig;
* the listener is notified of the event.
* </p>
* <p>
* For a cluster these events may need to be broadcast. Each of the inserted(),
* updated() and deleted() methods return true if you want those events to be
* broadcast to the other members of a cluster (the id values are broadcast). If
* these methods return false then the events are not broadcast.
* </p>
* <p>
* It is worth noting that BeanPersistListener is different in three main ways
* It is worth noting that BeanPersistListener is different in two main ways
* from BeanPersistController postXXX methods.
* <ul>
* <li>BeanPersistListener only sees successfully committed events.
* BeanPersistController pre and post methods occur before the commit or a
* rollback and will see events that are later rolled back</li>
* <li>BeanPersistListener runs in a background thread and will not effect the
* response time of the actual persist where as BeanPersistController code will</li>
* <li>BeanPersistListener can be notified of events from other servers in a
* cluster.</li>
* <li>
* BeanPersistListener only sees successfully committed events.
* BeanPersistController pre and post methods occur before the commit or a
* rollback and will see events that are later rolled back
* </li>
* <li>
* BeanPersistListener runs in a background thread and will not effect the
* response time of the actual persist where as BeanPersistController code will
* </li>
* </ul>
* </p>
* <p>
@@ -46,32 +42,29 @@ public interface BeanPersistListener {
boolean isRegisterFor(Class<?> cls);
/**
* Notified that a bean has been inserted locally. Return true if you want the
* cluster to be notified of the event.
* Notified that a bean has been inserted.
*
* @param bean
* The bean that was inserted.
*/
boolean inserted(Object bean);
void inserted(Object bean);
/**
* Notified that a bean has been updated locally. Return true if you want the
* cluster to be notified of the event.
* Notified that a bean has been updated.
*
* @param bean
* The bean that was updated.
* @param updatedProperties
* The properties that were modified by this update.
*/
boolean updated(Object bean, Set<String> updatedProperties);
void updated(Object bean, Set<String> updatedProperties);
/**
* Notified that a bean has been deleted locally. Return true if you want the
* cluster to be notified of the event.
* Notified that a bean has been deleted.
*
* @param bean
* The bean that was deleted.
*/
boolean deleted(Object bean);
void deleted(Object bean);
}
@@ -74,4 +74,23 @@ public class ReadAuditQueryPlan {
public void setSql(String sql) {
this.sql = sql;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ReadAuditQueryPlan that = (ReadAuditQueryPlan) o;
if (!beanType.equals(that.beanType)) return false;
if (!queryKey.equals(that.queryKey)) return false;
return sql.equals(that.sql);
}
@Override
public int hashCode() {
int result = beanType.hashCode();
result = 31 * result + queryKey.hashCode();
result = 31 * result + sql.hashCode();
return result;
}
}
@@ -1,49 +1,49 @@
package com.avaje.ebeaninternal.api;
import java.util.ArrayList;
import com.avaje.ebean.TxScope;
import com.avaje.ebean.config.PersistBatch;
import java.util.ArrayList;
/**
* Used internally to handle the scoping of transactions for methods.
*/
public class ScopeTrans implements Thread.UncaughtExceptionHandler {
private static final int OPCODE_ATHROW = 191;
private static final int OPCODE_ATHROW = 191;
private final SpiTransactionScopeManager scopeMgr;
private final SpiTransactionScopeManager scopeMgr;
/**
* The suspended transaction (can be null).
*/
private final SpiTransaction suspendedTransaction;
/**
* The suspended transaction (can be null).
*/
private final SpiTransaction suspendedTransaction;
/**
* The transaction in scope (can be null).
*/
private final SpiTransaction transaction;
/**
* The transaction in scope (can be null).
*/
private final SpiTransaction transaction;
/**
* If true by default rollback on Checked exceptions.
*/
private final boolean rollbackOnChecked;
/**
* If true by default rollback on Checked exceptions.
*/
private final boolean rollbackOnChecked;
/**
* True if the transaction was created and hence should be committed
* on finally if it hasn't already been rolled back.
*/
private final boolean created;
/**
* True if the transaction was created and hence should be committed
* on finally if it hasn't already been rolled back.
*/
private final boolean created;
/**
* Explicit set of Exceptions that DO NOT cause a rollback to occur.
*/
private final ArrayList<Class<? extends Throwable>> noRollbackFor;
/**
* Explicit set of Exceptions that DO NOT cause a rollback to occur.
*/
private final ArrayList<Class<? extends Throwable>> noRollbackFor;
/**
* Explicit set of Exceptions that DO cause a rollback to occur.
*/
private final ArrayList<Class<? extends Throwable>> rollbackFor;
/**
* Explicit set of Exceptions that DO cause a rollback to occur.
*/
private final ArrayList<Class<? extends Throwable>> rollbackFor;
private PersistBatch restoreBatch;
@@ -51,29 +51,32 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
private int restoreBatchSize;
/**
* Flag set when a rollback has occurred.
*/
private boolean rolledBack;
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) {
private Boolean restoreBatchGeneratedKeys;
this.rollbackOnChecked = rollbackOnChecked;
this.created = created;
this.transaction = transaction;
this.suspendedTransaction = suspendedTransaction;
this.scopeMgr = scopeMgr;
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
/**
* Flag set when a rollback has occurred.
*/
private boolean rolledBack;
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) {
this.rollbackOnChecked = rollbackOnChecked;
this.created = created;
this.transaction = transaction;
this.suspendedTransaction = suspendedTransaction;
this.scopeMgr = scopeMgr;
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
if (transaction != null) {
if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
restoreBatch = transaction.getBatch();
restoreBatchOnCascade = transaction.getBatchOnCascade();
restoreBatchSize = transaction.getBatchSize();
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
}
if (txScope.isBatchSet()) {
transaction.setBatch(txScope.getBatch());
@@ -84,70 +87,74 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
if (txScope.isBatchSizeSet()) {
transaction.setBatchSize(txScope.getBatchSize());
}
if (txScope.isSkipGeneratedKeys()) {
transaction.setBatchGetGeneratedKeys(false);
}
}
}
}
/**
* Return the current/active transaction.
*/
protected SpiTransaction getTransaction() {
return transaction;
}
/**
* Return the current/active transaction.
*/
protected SpiTransaction getTransaction() {
return transaction;
}
/**
* Called when the Thread catches any uncaught exception.
* For example, an unexpected NullPointerException or Error.
*/
public void uncaughtException(Thread thread, Throwable e) {
// rollback transaction if required
caughtThrowable(e);
// reinstate suspended transaction
onFinally();
}
/**
* Returned via RETURN or expected Exception from the method.
* @param returnOrThrowable the return value or Throwable
* @param opCode indicates
*/
public void onExit(Object returnOrThrowable, int opCode) {
if (opCode == OPCODE_ATHROW){
// exited with a Throwable
caughtThrowable((Throwable)returnOrThrowable);
}
onFinally();
}
/**
* Commit if the transaction exists and has not already been rolled back.
* Also reinstate the suspended transaction if there was one.
*/
public void onFinally() {
/**
* Called when the Thread catches any uncaught exception.
* For example, an unexpected NullPointerException or Error.
*/
public void uncaughtException(Thread thread, Throwable e) {
try {
if (!rolledBack) {
commitTransaction();
}
} finally {
restoreSuspended();
}
}
// rollback transaction if required
caughtThrowable(e);
protected void restoreSuspended() {
if (suspendedTransaction != null){
// reinstate suspended transaction
onFinally();
}
/**
* Returned via RETURN or expected Exception from the method.
*
* @param returnOrThrowable the return value or Throwable
* @param opCode indicates
*/
public void onExit(Object returnOrThrowable, int opCode) {
if (opCode == OPCODE_ATHROW) {
// exited with a Throwable
caughtThrowable((Throwable) returnOrThrowable);
}
onFinally();
}
/**
* Commit if the transaction exists and has not already been rolled back.
* Also reinstate the suspended transaction if there was one.
*/
public void onFinally() {
try {
if (!rolledBack) {
commitTransaction();
}
} finally {
restoreSuspended();
}
}
protected void restoreSuspended() {
if (suspendedTransaction != null) {
// put the previously suspended transaction
// back onto the ThreadLocal or equivalent
scopeMgr.replace(suspendedTransaction);
}
}
}
protected void commitTransaction() {
if (created) {
protected void commitTransaction() {
if (created) {
transaction.commit();
} else {
if (restoreBatch != null) {
@@ -159,78 +166,81 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
if (restoreBatchSize > 0) {
transaction.setBatchSize(restoreBatchSize);
}
if (restoreBatchGeneratedKeys != null) {
transaction.setBatchGetGeneratedKeys(restoreBatchGeneratedKeys);
}
}
}
}
/**
* An Error was caught and this ALWAYS causes a rollback to occur.
* Returns the error and this should be thrown by the calling code.
*/
public Error caughtError(Error e) {
rollback(e);
return e;
}
/**
* An Exception was caught and may or may not cause a rollback to occur.
* Returns the exception and this should be thrown by the calling code.
*/
public <T extends Throwable> T caughtThrowable(T e) {
if (isRollbackThrowable(e)) {
rollback(e);
}
return e;
}
/**
* An Error was caught and this ALWAYS causes a rollback to occur.
* Returns the error and this should be thrown by the calling code.
*/
public Error caughtError(Error e) {
rollback(e);
return e;
}
protected void rollback(Throwable e) {
if (transaction != null && transaction.isActive()) {
// transaction is null for NOT_SUPPORTED and sometimes SUPPORTS
// and Inactive (already rolled back) if nested REQUIRED
transaction.rollback(e);
}
rolledBack = true;
}
/**
* An Exception was caught and may or may not cause a rollback to occur.
* Returns the exception and this should be thrown by the calling code.
*/
public <T extends Throwable> T caughtThrowable(T e) {
/**
* Return true if this throwable should cause a rollback to occur.
*/
private boolean isRollbackThrowable(Throwable e) {
if (isRollbackThrowable(e)) {
rollback(e);
}
return e;
}
if (e instanceof Error){
return true;
}
if (noRollbackFor != null){
for (int i = 0; i < noRollbackFor.size(); i++) {
if (noRollbackFor.get(i).equals(e.getClass())) {
// explicit no rollback for this one
return false;
}
}
}
protected void rollback(Throwable e) {
if (transaction != null && transaction.isActive()) {
// transaction is null for NOT_SUPPORTED and sometimes SUPPORTS
// and Inactive (already rolled back) if nested REQUIRED
transaction.rollback(e);
}
rolledBack = true;
}
/**
* Return true if this throwable should cause a rollback to occur.
*/
private boolean isRollbackThrowable(Throwable e) {
if (e instanceof Error) {
return true;
}
if (noRollbackFor != null) {
for (int i = 0; i < noRollbackFor.size(); i++) {
if (noRollbackFor.get(i).equals(e.getClass())) {
// explicit no rollback for this one
return false;
}
}
}
if (rollbackFor != null) {
for (int i = 0; i < rollbackFor.size(); i++) {
if (rollbackFor.get(i).equals(e.getClass())) {
// explicit rollback for this one
return true;
}
}
}
if (e instanceof RuntimeException) {
return true;
} else {
// checked exceptions...
// EJB defaults this to false which is not intuitive IMO
// Ebean makes this configurable (default to true)
return rollbackOnChecked;
}
}
if (rollbackFor != null){
for (int i = 0; i < rollbackFor.size(); i++) {
if (rollbackFor.get(i).equals(e.getClass())) {
// explicit rollback for this one
return true;
}
}
}
if (e instanceof RuntimeException) {
return true;
} else {
// checked exceptions...
// EJB defaults this to false which is not intuitive IMO
// Ebean makes this configurable (default to true)
return rollbackOnChecked;
}
}
}
@@ -105,6 +105,16 @@ public class ScopedTransaction implements SpiTransaction {
transaction.logSummary(msg);
}
@Override
public void setSkipCache(boolean skipCache) {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
}
@Override
public void addBeanChange(BeanChange beanChange) {
transaction.addBeanChange(beanChange);
@@ -230,6 +240,11 @@ public class ScopedTransaction implements SpiTransaction {
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
}
@Override
public Boolean getBatchGetGeneratedKeys() {
return transaction.getBatchGetGeneratedKeys();
}
@Override
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
@@ -5,7 +5,6 @@ import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PersistenceContextScope;
import com.avaje.ebean.Query;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
@@ -19,6 +18,7 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmUpdateProperties;
import java.sql.Timestamp;
import java.util.List;
@@ -92,6 +92,11 @@ public interface SpiQuery<T> extends Query<T> {
* Delete query.
*/
DELETE,
/**
* Update query.
*/
UPDATE,
}
enum TemporalMode {
@@ -226,18 +231,6 @@ public interface SpiQuery<T> extends Query<T> {
List<String> getSoftDeletePredicates();
/**
* Return a listener that wants to be notified when the bean collection is
* first used.
*/
BeanCollectionTouched getBeanCollectionTouched();
/**
* Set a listener to be notified when the bean collection has been touched
* (when the list/set/map is first used).
*/
void setBeanCollectionTouched(BeanCollectionTouched notify);
/**
* Set the list of Id's that is being populated.
* <p>
@@ -449,11 +442,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
void setUsageProfiling(boolean usageProfiling);
/**
* Return the query name.
*/
String getName();
/**
* Prepare the query which prepares sub-query expressions and calculates
* and returns the query plan key.
@@ -477,12 +465,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
HashQuery queryHash();
/**
* Return true if this is a query based on a SqlSelect rather than
* generated.
*/
boolean isSqlSelect();
/**
* Return true if this is a RawSql query.
*/
@@ -493,12 +475,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
OrderBy<T> getOrderBy();
/**
* Return additional where clause. This should be added to any where clause
* that was part of the original query.
*/
String getAdditionalWhere();
/**
* Can return null if no expressions where added to the where clause.
*/
@@ -514,21 +490,21 @@ public interface SpiQuery<T> extends Query<T> {
*/
SpiExpressionList<T> getTextExpression();
/**
* Return additional having clause. Where raw String expressions are added
* to having clause rather than Expression objects.
*/
String getAdditionalHaving();
/**
* Returns true if either firstRow or maxRows has been set.
*/
boolean hasMaxRowsOrFirstRow();
/**
* Return true if this query should use/check the bean cache.
* Return true if the bean cache should be exclude for query or lazy loading.
*/
Boolean isUseBeanCache();
boolean isExcludeBeanCache();
/**
* Return true if this query should use the bean cache.
* It is not skipped and bean caching is supported.
*/
boolean isUseBeanCache();
/**
* Return true if this query should use/check the query cache.
@@ -634,11 +610,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
void setDefaultSelectClause();
/**
* Return the where clause from a parsed string query.
*/
String getRawWhereClause();
/**
* Set the generated sql for debug purposes.
*/
@@ -695,4 +666,9 @@ public interface SpiQuery<T> extends Query<T> {
*/
Set<String> validate(BeanType<T> desc);
/**
* Return the properties for an update query.
*/
OrmUpdateProperties getUpdateProperties();
}
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.sql.Connection;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.PersistenceContext;
@@ -12,6 +9,9 @@ import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import java.sql.Connection;
import java.util.List;
/**
* Extends Transaction with additional API required on server.
* <p>
@@ -125,6 +125,11 @@ public interface SpiTransaction extends Transaction {
*/
int getBatchSize();
/**
* Return the getGeneratedKeys setting for this transaction.
*/
Boolean getBatchGetGeneratedKeys();
/**
* Modify and return the current 'depth' of the transaction.
* <p>
@@ -20,10 +20,13 @@ public class DefaultServerCacheManager implements ServerCacheManager {
private final DefaultCacheHolder collectionIdsCache;
private final boolean localL2Caching;
/**
* Create with a cache factory and default cache options.
*/
public DefaultServerCacheManager(ServerCacheFactory cacheFactory, ServerCacheOptions defaultBeanOptions, ServerCacheOptions defaultQueryOptions) {
public DefaultServerCacheManager(boolean localL2Caching, ServerCacheFactory cacheFactory, ServerCacheOptions defaultBeanOptions, ServerCacheOptions defaultQueryOptions) {
this.localL2Caching = localL2Caching;
this.beanCache = new DefaultCacheHolder(cacheFactory, defaultBeanOptions);
this.queryCache = new DefaultCacheHolder(cacheFactory, defaultQueryOptions);
this.naturalKeyCache = new DefaultCacheHolder(cacheFactory, defaultBeanOptions);
@@ -34,7 +37,11 @@ public class DefaultServerCacheManager implements ServerCacheManager {
* Construct when l2 cache is disabled.
*/
public DefaultServerCacheManager() {
this(new DefaultServerCacheFactory(), new ServerCacheOptions(), new ServerCacheOptions());
this(true, new DefaultServerCacheFactory(), new ServerCacheOptions(), new ServerCacheOptions());
}
public boolean isLocalL2Caching() {
return localL2Caching;
}
/**
@@ -49,7 +56,9 @@ public class DefaultServerCacheManager implements ServerCacheManager {
queryCache.clearCache(beanName);
}
/**
* Clear all caches.
*/
public void clearAll() {
beanCache.clearAll();
queryCache.clearAll();
@@ -57,7 +66,6 @@ public class DefaultServerCacheManager implements ServerCacheManager {
collectionIdsCache.clearAll();
}
public ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName) {
return collectionIdsCache.getCache(beanType.getName() + "." + propertyName, ServerCacheType.COLLECTION_IDS);
}
@@ -87,5 +95,4 @@ public class DefaultServerCacheManager implements ServerCacheManager {
return beanCache.isCaching(beanType.getName());
}
}
@@ -10,7 +10,7 @@ public class CacheOptions {
/**
* Instance when no caching is used.
*/
public static CacheOptions NO_CACHING = new CacheOptions();
public static final CacheOptions NO_CACHING = new CacheOptions();
private final boolean enableBeanCache;
private final boolean enableQueryCache;
@@ -281,7 +281,7 @@ public class DefaultBeanLoader {
}
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
SpiQuery<?> query = server.createQuery(desc.getBeanType());
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
if (draft) {
query.asDraft();
@@ -17,6 +17,8 @@ import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.cache.DefaultServerCachePlugin;
import com.avaje.ebeaninternal.server.cache.DefaultServerCacheManager;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.bootup.BootupClassPathSearch;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import org.avaje.datasource.DataSourceAlertFactory;
import org.avaje.datasource.DataSourceConfig;
@@ -114,8 +116,7 @@ public class DefaultContainer implements SpiContainer {
SpiBackgroundExecutor executor = createBackgroundExecutor(serverConfig);
ServerCacheManager cacheManager = getCacheManager(online, serverConfig, executor);
XmlConfig xmlConfig = new XmlConfigLoader(null).load();
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, executor, serverConfig, bootupClasses);
InternalConfiguration c = new InternalConfiguration(clusterManager, cacheManager, executor, serverConfig, bootupClasses);
DefaultServer server = new DefaultServer(c, cacheManager);
@@ -169,6 +170,7 @@ public class DefaultContainer implements SpiContainer {
queryOptions.setMaxIdleSecs(serverConfig.getQueryCacheMaxIdleTime());
queryOptions.setMaxSecsToLive(serverConfig.getQueryCacheMaxTimeToLive());
boolean localL2Caching = false;
ServerCachePlugin plugin = serverConfig.getServerCachePlugin();
if (plugin == null) {
ServiceLoader<ServerCachePlugin> cacheFactories = ServiceLoader.load(ServerCachePlugin.class);
@@ -178,13 +180,14 @@ public class DefaultContainer implements SpiContainer {
plugin = iterator.next();
logger.debug("using ServerCacheFactory {}", plugin.getClass());
} else {
// use the built in default
// use the built in default l2 caching which is local cache based
localL2Caching = true;
plugin = new DefaultServerCachePlugin();
}
}
ServerCacheFactory factory = plugin.create(serverConfig, executor);
return new DefaultServerCacheManager(factory, beanOptions, queryOptions);
return new DefaultServerCacheManager(localL2Caching, factory, beanOptions, queryOptions);
}
/**
@@ -193,20 +196,20 @@ public class DefaultContainer implements SpiContainer {
*/
private BootupClasses getBootupClasses(ServerConfig serverConfig) {
BootupClasses bootupClasses = getBootupClasses1(serverConfig);
bootupClasses.addIdGenerators(serverConfig.getIdGenerators());
bootupClasses.addPersistControllers(serverConfig.getPersistControllers());
bootupClasses.addPostLoaders(serverConfig.getPostLoaders());
bootupClasses.addFindControllers(serverConfig.getFindControllers());
bootupClasses.addTransactionEventListeners(serverConfig.getTransactionEventListeners());
bootupClasses.addPersistListeners(serverConfig.getPersistListeners());
bootupClasses.addQueryAdapters(serverConfig.getQueryAdapters());
bootupClasses.addServerConfigStartup(serverConfig.getServerConfigStartupListeners());
bootupClasses.addChangeLogInstances(serverConfig);
BootupClasses bootup = getBootupClasses1(serverConfig);
bootup.addIdGenerators(serverConfig.getIdGenerators());
bootup.addPersistControllers(serverConfig.getPersistControllers());
bootup.addPostLoaders(serverConfig.getPostLoaders());
bootup.addFindControllers(serverConfig.getFindControllers());
bootup.addTransactionEventListeners(serverConfig.getTransactionEventListeners());
bootup.addPersistListeners(serverConfig.getPersistListeners());
bootup.addQueryAdapters(serverConfig.getQueryAdapters());
bootup.addServerConfigStartup(serverConfig.getServerConfigStartupListeners());
bootup.addChangeLogInstances(serverConfig);
// run any ServerConfigStartup instances
bootupClasses.runServerConfigStartup(serverConfig);
return bootupClasses;
bootup.runServerConfigStartup(serverConfig);
return bootup;
}
/**
@@ -215,7 +218,7 @@ public class DefaultContainer implements SpiContainer {
private BootupClasses getBootupClasses1(ServerConfig serverConfig) {
List<Class<?>> entityClasses = serverConfig.getClasses();
if (serverConfig.isDisableClasspathSearch() || (entityClasses != null && entityClasses.size() > 0)) {
if (serverConfig.isDisableClasspathSearch() || (entityClasses != null && !entityClasses.isEmpty())) {
// use classes we explicitly added via configuration
return new BootupClasses(entityClasses);
}
@@ -14,7 +14,6 @@ import com.avaje.ebean.config.EncryptKeyManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.dbmigration.DdlGenerator;
import com.avaje.ebean.dbmigration.MigrationRunner;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
@@ -39,9 +38,6 @@ import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.DNativeQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.el.ElFilter;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
@@ -57,6 +53,7 @@ import com.avaje.ebeaninternal.server.query.QueryFutureRowCount;
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery;
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate;
import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery;
import com.avaje.ebeaninternal.server.querydefn.DefaultUpdateQuery;
import com.avaje.ebeaninternal.server.text.csv.TCsvReader;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
@@ -65,6 +62,7 @@ import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.util.ParamTypeHelper;
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
import org.avaje.dbmigration.MigrationRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -344,10 +342,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
DbMigrationConfig migrationConfig = serverConfig.getMigrationConfig();
if (migrationConfig != null) {
migrationConfig.generateOnStart(this);
}
if (migrationConfig.isRunMigration()) {
new MigrationRunner(this, migrationConfig).run();
if (migrationConfig.isRunMigration()) {
// classLoader used to load resources
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
MigrationRunner runner = migrationConfig.createRunner(classLoader);
runner.run(serverConfig.getDataSource());
}
}
}
@@ -835,7 +836,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (sortByClause == null) {
throw new NullPointerException("sortByClause is null");
}
if (list.size() == 0) {
if (list.isEmpty()) {
// don't need to sort an empty list
return;
}
@@ -848,25 +849,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
beanDescriptor.sort(list, sortByClause);
}
public <T> Query<T> createQuery(Class<T> beanType) throws PersistenceException {
return createQuery(beanType, null);
}
public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) throws PersistenceException {
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
if (desc == null) {
throw new PersistenceException("Is " + beanType.getName() + " an Entity Bean? BeanDescriptor not found?");
}
DeployNamedQuery deployQuery = desc.getNamedQuery(namedQuery);
if (deployQuery == null) {
throw new PersistenceException("named query " + namedQuery + " was not found for " + desc.getFullName());
}
// this will parse the query
return new DefaultOrmQuery<T>(desc, this, expressionFactory, deployQuery);
}
@Override
public <T> Set<String> validateQuery(Query<T> query) {
@@ -894,42 +876,20 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return new TCsvReader<T>(this, descriptor);
}
public <T> UpdateQuery<T> update(Class<T> beanType) {
return new DefaultUpdateQuery<T>(createQuery(beanType));
}
public <T> Query<T> find(Class<T> beanType) {
return createQuery(beanType);
}
public <T> Query<T> createQuery(Class<T> beanType, String query) {
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType) {
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
if (desc == null) {
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
}
switch (desc.getEntityType()) {
case SQL:
if (query != null) {
throw new PersistenceException("You must used Named queries for this Entity " + desc.getFullName());
}
// use the "default" SqlSelect
DeployNamedQuery defaultSqlSelect = desc.getNamedQuery("default");
return new DefaultOrmQuery<T>(desc, this, expressionFactory, defaultSqlSelect);
default:
return new DefaultOrmQuery<T>(desc, this, expressionFactory, query);
}
}
public <T> Update<T> createNamedUpdate(Class<T> beanType, String namedUpdate) {
BeanDescriptor<?> desc = getBeanDescriptor(beanType);
if (desc == null) {
String m = beanType.getName() + " is NOT an Entity Bean registered with this server?";
throw new PersistenceException(m);
}
DeployNamedUpdate deployUpdate = desc.getNamedUpdate(namedUpdate);
if (deployUpdate == null) {
throw new PersistenceException("named update " + namedUpdate + " was not found for " + desc.getFullName());
}
return new DefaultOrmUpdate<T>(beanType, this, desc.getBaseTable(), deployUpdate);
return new DefaultOrmQuery<T>(desc, this, expressionFactory);
}
public <T> Update<T> createUpdate(Class<T> beanType, String ormUpdate) {
@@ -946,14 +906,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return new DefaultRelationalQuery(this, sql);
}
public SqlQuery createNamedSqlQuery(String namedQuery) {
DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery);
if (nq == null) {
throw new PersistenceException("SqlQuery " + namedQuery + " not found.");
}
return new DefaultRelationalQuery(this, nq.getQuery());
}
public SqlUpdate createSqlUpdate(String sql) {
return new DefaultSqlUpdate(this, sql);
}
@@ -962,14 +914,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return new DefaultCallableSql(this, sql);
}
public SqlUpdate createNamedSqlUpdate(String namedQuery) {
DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery);
if (nq == null) {
throw new PersistenceException("SqlUpdate " + namedQuery + " not found.");
}
return new DefaultSqlUpdate(this, nq.getQuery());
}
public <T> T find(Class<T> beanType, Object uid) {
return find(beanType, uid, null);
@@ -1047,8 +991,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
if (!desc.calculateUseCache(query.isUseBeanCache())) {
// not using bean cache
if (!query.isUseBeanCache() || (t != null && t.isSkipCache())) {
return null;
}
@@ -1098,26 +1041,32 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
public <T> T findUnique(Query<T> query, Transaction t) {
public <T> T findUnique(Query<T> query, Transaction transaction) {
Object id = query.getId();
if (id != null) {
// actually a find by Id query
return findId(query, t);
return findId(query, transaction);
}
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
BeanDescriptor<T> desc = spiQuery.getBeanDescriptor();
id = desc.cacheNaturalKeyIdLookup(spiQuery);
if (id != null) {
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, id);
if (bean != null) {
return bean;
SpiTransaction t = (SpiTransaction) transaction;
if (t == null) {
t = getCurrentServerTransaction();
}
if (t == null || !t.isSkipCache()) {
id = desc.cacheNaturalKeyIdLookup(spiQuery);
if (id != null) {
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, id);
if (bean != null) {
return bean;
}
}
}
// a query that is expected to return either 0 or 1 rows
// a query that is expected to return either 0 or 1 beans
List<T> list = findList(query, t);
return extractUnique(list);
}
@@ -1221,6 +1170,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
public <T> int update(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.UPDATE, query, t);
try {
request.initTransIfRequired();
request.markNotQueryOnly();
return request.update();
} finally {
request.endTransIfRequired();
}
}
public <T> FutureRowCount<T> findFutureRowCount(Query<T> q, Transaction t) {
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
@@ -1278,12 +1239,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return queryFuture;
}
@Override
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
}
@Override
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
@@ -1311,7 +1266,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
request.initTransIfRequired();
request.findEach(consumer);
// no try finally - findVisit guarantee's cleanup of the transaction if required
// no try finally - findEach guarantee's cleanup of the transaction if required
}
public <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction t) {
@@ -1324,21 +1279,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
request.initTransIfRequired();
request.findEachWhile(consumer);
// no try finally - findVisit guarantee's cleanup of the transaction if required
}
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
try {
request.initTransIfRequired();
return request.findIterate();
} catch (RuntimeException ex) {
request.endTransIfRequired();
throw ex;
}
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
}
@Override
@@ -1617,90 +1558,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return (EntityBean)bean;
}
/**
* Delete the associations (from the intersection table) of a ManyToMany given
* the owner bean and the propertyName of the ManyToMany collection.
* <p>
* This returns the number of associations deleted.
* </p>
*/
public int deleteManyToManyAssociations(Object ownerBean, String propertyName) {
return deleteManyToManyAssociations(ownerBean, propertyName, null);
}
/**
* Delete the associations (from the intersection table) of a ManyToMany given
* the owner bean and the propertyName of the ManyToMany collection.
* <p>
* This returns the number of associations deleted.
* </p>
*/
public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
EntityBean owner = checkEntityBean(ownerBean);
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
int rc = persister.deleteManyToManyAssociations(owner, propertyName, trans);
wrap.commitIfCreated();
return rc;
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
/**
* Save the associations of a ManyToMany given the owner bean and the
* propertyName of the ManyToMany collection.
*/
public void saveManyToManyAssociations(Object ownerBean, String propertyName) {
saveManyToManyAssociations(ownerBean, propertyName, null);
}
/**
* Save the associations of a ManyToMany given the owner bean and the
* propertyName of the ManyToMany collection.
*/
public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
EntityBean owner = checkEntityBean(ownerBean);
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
persister.saveManyToManyAssociations(owner, propertyName, trans);
wrap.commitIfCreated();
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
public void saveAssociation(Object ownerBean, String propertyName) {
saveAssociation(ownerBean, propertyName, null);
}
public void saveAssociation(Object ownerBean, String propertyName, Transaction t) {
EntityBean owner = checkEntityBean(ownerBean);
TransWrapper wrap = initTransIfRequired(t);
try {
SpiTransaction trans = wrap.transaction;
persister.saveAssociation(owner, propertyName, trans);
wrap.commitIfCreated();
} catch (RuntimeException e) {
wrap.rollbackIfCreated();
throw e;
}
}
@Override
public int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException {
return saveAllInternal(beans.iterator(), transaction);
@@ -22,18 +22,17 @@ import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener;
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.core.timezone.CloneDataTimeZone;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import com.avaje.ebeaninternal.server.core.timezone.NoDataTimeZone;
import com.avaje.ebeaninternal.server.core.timezone.SimpleDataTimeZone;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
import org.avaje.datasource.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -56,6 +55,7 @@ import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import com.avaje.ebeanservice.docstore.none.NoneDocStoreFactory;
import com.fasterxml.jackson.core.JsonFactory;
import org.avaje.datasource.DataSourcePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -79,8 +79,6 @@ public class InternalConfiguration {
private final DeployInherit deployInherit;
private final DeployOrmXml deployOrmXml;
private final TypeManager typeManager;
private final DataTimeZone dataTimeZone;
@@ -103,8 +101,6 @@ public class InternalConfiguration {
private final SpiBackgroundExecutor backgroundExecutor;
private final XmlConfig xmlConfig;
private final JsonFactory jsonFactory;
private final DocStoreFactory docStoreFactory;
@@ -114,13 +110,12 @@ public class InternalConfiguration {
*/
private final List<Plugin> plugins = new ArrayList<Plugin>();
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
public InternalConfiguration(ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses) {
this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class));
this.jsonFactory = serverConfig.getJsonFactory();
this.xmlConfig = xmlConfig;
this.clusterManager = clusterManager;
this.backgroundExecutor = backgroundExecutor;
this.cacheManager = cacheManager;
@@ -131,7 +126,6 @@ public class InternalConfiguration {
this.expressionFactory = initExpressionFactory(serverConfig, databasePlatform);
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
this.deployOrmXml = new DeployOrmXml();
this.deployInherit = new DeployInherit(bootupClasses);
this.deployCreateProperties = new DeployCreateProperties(typeManager);
@@ -264,10 +258,6 @@ public class InternalConfiguration {
return new DJsonContext(server, jsonFactory, typeManager);
}
public XmlConfig getXmlConfig() {
return xmlConfig;
}
public AutoTuneService createAutoTuneService(SpiEbeanServer server) {
return AutoTuneServiceFactory.create(server, serverConfig);
}
@@ -316,10 +306,6 @@ public class InternalConfiguration {
return deployInherit;
}
public DeployOrmXml getDeployOrmXml() {
return deployOrmXml;
}
public DeployCreateProperties getDeployCreateProperties() {
return deployCreateProperties;
}
@@ -352,15 +338,16 @@ public class InternalConfiguration {
*/
public TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
boolean localL2 = cacheManager.isLocalL2Caching();
if (serverConfig.isExplicitTransactionBeginMode()) {
return new ExplicitTransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
return new ExplicitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
}
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
return new AutoCommitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
}
return new TransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
return new TransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
}
/**
@@ -1,6 +1,5 @@
package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.Version;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.BeanIdList;
@@ -43,4 +42,9 @@ public interface OrmQueryEngine {
* Execute the query as a delete statement.
*/
<T> int delete(OrmQueryRequest<T> request);
/**
* Execute the query as a update statement.
*/
<T> int update(OrmQueryRequest<T> request);
}
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.PersistenceContextScope;
import com.avaje.ebean.QueryEachConsumer;
import com.avaje.ebean.QueryEachWhileConsumer;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.Version;
import com.avaje.ebean.bean.BeanCollection;
@@ -174,14 +173,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
}
}
/**
* Return true if this is a query using generated sql. If false this query
* will use raw sql (Entity bean based on raw sql select).
*/
public boolean isSqlSelect() {
return query.isSqlSelect() && query.getRawSql() == null;
}
/**
* Return the PersistenceContext used for this request.
*/
@@ -295,6 +286,13 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return queryEngine.delete(this);
}
/**
* Execute the query as a update.
*/
public int update() {
return queryEngine.update(this);
}
/**
* Execute the query as findById.
*/
@@ -425,7 +423,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
}
public boolean isUseBeanCache() {
return beanDescriptor.calculateUseCache(query.isUseBeanCache());
return query.isUseBeanCache();
}
/**
@@ -305,7 +305,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Set the cache notify status.
*/
private void setNotifyCache() {
this.notifyCache = beanDescriptor.isCacheNotify(publish);
this.notifyCache = beanDescriptor.isCacheNotify(type, publish);
}
/**
@@ -35,26 +35,6 @@ public interface Persister {
*/
void save(EntityBean entityBean, Transaction t);
/**
* Save the associations of a ManyToMany given the owner bean and the
* propertyName of the ManyToMany collection.
*/
void saveManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t);
/**
* Save an association (OneToMany, ManyToOne, OneToOne or ManyToMany).
*
* @param parentBean the bean that owns the association.
* @param propertyName the name of the property to save.
* @param t the transaction to use.
*/
void saveAssociation(EntityBean parentBean, String propertyName, Transaction t);
/**
* Delete the associations of a ManyToMany given the owner bean and the property name of the ManyToMany.
*/
int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t);
/**
* Delete a bean given it's type and id value.
* <p>
@@ -24,29 +24,18 @@ public class PostgresCast {
// for exists and not-exists expressions
return "";
}
if (isIntegerType(value)) {
if (value instanceof Integer) {
return asArray ? "::integer[]" : "::integer";
}
if (isNumberType(value)) {
if (value instanceof Long) {
return asArray ? "::bigint[]" : "::bigint";
}
if (value instanceof Number) {
return asArray ? "::decimal[]" : "::decimal";
}
if (isBooleanType(value)) {
if (value instanceof Boolean) {
return asArray ? "::boolean[]" : "::boolean";
}
return "";
}
private static boolean isBooleanType(Object value) {
return (value instanceof Boolean);
}
private static boolean isIntegerType(Object value) {
return (value instanceof Integer) || (value instanceof Long);
}
private static boolean isNumberType(Object value) {
return (value instanceof Number);
}
}
@@ -1,4 +1,4 @@
package com.avaje.ebean;
package com.avaje.ebeaninternal.server.core;
import java.util.Iterator;
@@ -31,8 +31,6 @@ import java.util.List;
*/
public final class RelationalQueryRequest {
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit", "1000000"));
private static final Logger logger = LoggerFactory.getLogger(RelationalQueryRequest.class);
private final SpiSqlQuery query;
@@ -212,7 +210,18 @@ public final class RelationalQueryRequest {
// convert any named parameters if required
sql = BindParamsParser.parse(bindParams, sql);
}
this.sql = sql;
this.sql = limitOffset(sql);
}
private String limitOffset(String sql) {
int firstRow = query.getFirstRow();
int maxRows = query.getMaxRows();
if (firstRow > 0 || maxRows > 0) {
return ebeanServer.getDatabasePlatform().getBasicSqlLimiter()
.limit(query.getQuery(), firstRow, maxRows);
}
return sql;
}
/**
@@ -250,13 +259,6 @@ public final class RelationalQueryRequest {
}
/**
* Return the maxRows allowed to fetch.
*/
public int getMaxRows() {
return query.getMaxRows() >= 1 ? query.getMaxRows() : GLOBAL_ROW_LIMIT;
}
/**
* Return the SQL executed for this query.
*/
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.QueryEachConsumer;
import com.avaje.ebean.QueryEachWhileConsumer;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.Version;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiQuery;
@@ -53,6 +52,11 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
*/
int delete();
/**
* Execute the query as a update.
*/
int update();
/**
* Execute the query as findById.
*/
@@ -1,64 +0,0 @@
package com.avaje.ebeaninternal.server.core;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
/**
* Holds the orm.xml and ebean-orm.xml deployment information.
*
* @author rbygrave
*/
public class XmlConfig {
private final List<Dnode> ebeanOrmXml;
private final List<Dnode> ormXml;
private final List<Dnode> allXml;
public XmlConfig(List<Dnode> ormXml, List<Dnode> ebeanOrmXml){
this.ormXml = ormXml;
this.ebeanOrmXml = ebeanOrmXml;
this.allXml = new ArrayList<Dnode>(ormXml.size() + ebeanOrmXml.size());
allXml.addAll(ormXml);
allXml.addAll(ebeanOrmXml);
}
public List<Dnode> getEbeanOrmXml() {
return ebeanOrmXml;
}
public List<Dnode> getOrmXml() {
return ormXml;
}
public List<Dnode> find(List<Dnode> entityXml, String element) {
ArrayList<Dnode> hits = new ArrayList<Dnode>();
for (int i = 0; i < entityXml.size(); i++) {
hits.addAll(entityXml.get(i).findAll(element, 1));
}
return hits;
}
/**
* Find the deployment xml for a given entity.
* <p>
* This searches all the orm.xml and ebean-orm.xml files.
* </p>
*/
public List<Dnode> findEntityXml(String className) {
ArrayList<Dnode> hits = new ArrayList<Dnode>(2);
for (Dnode ormXml : allXml) {
Dnode entityMappings = ormXml.find("entity-mappings");
List<Dnode> entities = entityMappings.findAll("entity", "class", className, 1);
if (entities.size() == 1) {
hits.add(entities.get(0));
}
}
return hits;
}
}
@@ -1,71 +0,0 @@
package com.avaje.ebeaninternal.server.core;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/**
* Used to read the orm.xml and ebean-orm.xml configuration files.
*
* @author rbygrave
* @author Richard Vowles - http://plus.google.com/RichardVowles
*/
public class XmlConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class);
private final ClassLoader classLoader;
public XmlConfigLoader(ClassLoader classLoader) {
if (classLoader == null) {
classLoader = getClass().getClassLoader();
}
this.classLoader = classLoader;
}
public XmlConfig load() {
List<Dnode> ormXml = search("META-INF/orm.xml");
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
return new XmlConfig(ormXml, ebeanOrmXml);
}
public List<Dnode> search(String resourceName) {
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
try {
Enumeration<URL> resources = classLoader.getResources(resourceName);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
InputStream is = url.openStream();
processInputStream(xmlList, is);
is.close();
}
} catch (IOException e) {
logger.error("Unable to find resources {}", resourceName);
}
return xmlList;
}
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
DnodeReader reader = new DnodeReader();
Dnode xmlDoc = reader.parseXml(is);
is.close();
xmlList.add(xmlDoc);
}
}
@@ -1,6 +1,7 @@
package com.avaje.ebeaninternal.server.core;
package com.avaje.ebeaninternal.server.core.bootup;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.core.ClassPathScanners;
import org.avaje.classpath.scanner.ClassPathScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -10,10 +11,12 @@ import java.util.*;
/**
* Searches for interesting classes such as Entities, Embedded and ScalarTypes.
*/
class BootupClassPathSearch {
public class BootupClassPathSearch {
private static final Logger logger = LoggerFactory.getLogger(BootupClassPathSearch.class);
private static final String EBEAN_MF = "META-INF/ebean.mf";
private final List<String> packages;
private final List<ClassPathScanner> scanners;
@@ -28,7 +31,11 @@ class BootupClassPathSearch {
}
private BootupClassPathSearch(ServerConfig serverConfig) {
this.packages = serverConfig.getPackages();
// find packages defined in META-INF/ebean.mf resources
Set<String> mfPackages = ManifestReader.readManifests(serverConfig.getClassLoadConfig().getClassLoader(), EBEAN_MF);
this.packages = DistillPackages.distill(serverConfig.getPackages(), mfPackages);
this.scanners = ClassPathScanners.find(serverConfig);
}
@@ -42,7 +49,7 @@ class BootupClassPathSearch {
long st = System.currentTimeMillis();
for (ClassPathScanner finder : this.scanners) {
if (packages != null && packages.size() > 0) {
if (packages != null && !packages.isEmpty()) {
for (String packageName : packages) {
finder.scanForClasses(packageName, bc);
}
@@ -53,7 +60,7 @@ class BootupClassPathSearch {
}
long searchTime = System.currentTimeMillis() - st;
logger.info("Classpath search entities[{}] searchTime [{}]", bc.getEntities().size(), searchTime);
logger.info("Classpath search entities[{}] searchTime[{}] in packages[{}]", bc.getEntities().size(), searchTime, packages);
return bc;
} catch (Exception ex) {
@@ -1,4 +1,4 @@
package com.avaje.ebeaninternal.server.core;
package com.avaje.ebeaninternal.server.core.bootup;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.IdGenerator;
@@ -17,7 +17,6 @@ import com.avaje.ebean.event.changelog.ChangeLogRegister;
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
import org.avaje.classpath.scanner.ClassFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +34,7 @@ import java.util.List;
* Interesting classes for a EbeanServer such as Embeddable, Entity,
* ScalarTypes, Finders, Listeners and Controllers.
*/
public class BootupClasses implements ClassPathSearchMatcher, ClassFilter {
public class BootupClasses implements ClassFilter {
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
@@ -0,0 +1,48 @@
package com.avaje.ebeaninternal.server.core.bootup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeSet;
/**
* Distill packages into distinct top level packages for searching.
*/
class DistillPackages {
/**
* Distill the list of packages into distinct top level packages.
*/
static List<String> distill(Collection<String> packages, Collection<String> mfPackages) {
// sort into natural order
TreeSet<String> treeSet = new TreeSet<String>();
treeSet.addAll(packages);
treeSet.addAll(mfPackages);
List<String> distilled = new ArrayList<String>();
// build the distilled list
for (String pack : treeSet) {
if (notAlreadyContained(distilled, pack)) {
distilled.add(pack);
}
}
return distilled;
}
/**
* Return true if the package is not already contained in the distilled list.
*/
private static boolean notAlreadyContained(List<String> distilled, String pack) {
for (int i = 0; i < distilled.size(); i++) {
if (pack.startsWith(distilled.get(i))) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,84 @@
package com.avaje.ebeaninternal.server.core.bootup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
/**
* Reads all the META-INF/ebean.mf resources with the package locations of entity beans.
*/
class ManifestReader {
private static final Logger logger = LoggerFactory.getLogger(ManifestReader.class);
private final Set<String> packageSet = new HashSet<String>();
/**
* Read the packages from ebean.mf manifest files found as resources.
*/
static Set<String> readManifests(ClassLoader classLoader, String resourcePath) {
return new ManifestReader().read(classLoader, resourcePath);
}
/**
* Read all the specific manifest files and return the set of packages containing type query beans.
*/
private Set<String> read(ClassLoader classLoader, String resourcePath) {
try {
Enumeration<URL> resources = classLoader.getResources(resourcePath);
while (resources.hasMoreElements()) {
InputStream is = resources.nextElement().openStream();
try {
read(new Manifest(is));
} finally {
close(is);
}
}
} catch (IOException e) {
logger.warn("Error reading META-INF/ebean.mf manifest resources", e);
}
return packageSet;
}
private void close(InputStream is) {
try {
is.close();
} catch (IOException e) {
logger.warn("Error closing manifest InputStream", e);
}
}
/**
* Read the packages from the manifest.
*/
private void read(Manifest manifest) throws IOException {
Attributes attributes = manifest.getMainAttributes();
String packages = attributes.getValue("packages");
if (packages != null) {
add(packages);
}
}
/**
* Collect each individual package splitting by delimiters.
*/
private void add(String packages) {
String[] split = packages.split(",|;| ");
for (int i = 0; i <split.length; i++) {
String pkg = split[i].trim();
if (!pkg.isEmpty()) {
packageSet.add(pkg);
}
}
}
}
@@ -16,7 +16,6 @@ public class SimpleDataTimeZone implements DataTimeZone {
@Override
public Calendar getTimeZone() {
// return null so Calendar is not used
return zone;
}
}
@@ -21,6 +21,7 @@ abstract class AssocOneHelp {
this.property = property;
this.target = property.targetDescriptor;
}
/**
* Effectively skip reading (the jdbc resultSet as already in the persistence context etc).
*/
@@ -10,56 +10,69 @@ import javax.persistence.CascadeType;
*/
public class BeanCascadeInfo {
/**
* should delete cascade.
*/
boolean delete;
/**
* Should save cascade.
*/
boolean save;
private boolean delete;
public void setTypes(CascadeType[] types) {
for (int i = 0; i < types.length; i++) {
setType(types[i]);
}
private boolean save;
private boolean refresh;
public void setTypes(CascadeType[] types) {
for (int i = 0; i < types.length; i++) {
setType(types[i]);
}
private void setType(CascadeType type) {
if (type.equals(CascadeType.ALL)){
save = true;
delete = true;
}
if (type.equals(CascadeType.REMOVE)){
delete = true;
}
if (type.equals(CascadeType.PERSIST)){
save = true;
}
if (type.equals(CascadeType.MERGE)){
save = true;
}
}
/**
* Return true if delete should cascade.
*/
public boolean isDelete() {
return delete;
}
/**
* Set to true if delete should cascade.
*/
public void setDelete(boolean delete) {
this.delete = delete;
}
/**
* Return true if save should cascade.
*/
public boolean isSave() {
return save;
}
private void setType(CascadeType type) {
switch (type) {
case ALL:
save = true;
delete = true;
refresh = true;
break;
case REMOVE:
delete = true;
break;
case REFRESH:
refresh = true;
break;
case PERSIST:
save = true;
break;
case MERGE:
save = true;
break;
default:
throw new IllegalStateException("Unexpected CascadeType " + type);
}
}
/**
* Return true if refresh should cascade.
*/
public boolean isRefresh() {
return refresh;
}
/**
* Return true if delete should cascade.
*/
public boolean isDelete() {
return delete;
}
/**
* Set to true if delete should cascade.
*/
public void setDelete(boolean delete) {
this.delete = delete;
}
/**
* Return true if save should cascade.
*/
public boolean isSave() {
return save;
}
/**
* Set cascade save and delete settings.
@@ -16,48 +16,48 @@ import java.io.IOException;
*/
public interface BeanCollectionHelp<T> {
/**
* Set the EbeanServer that owns the configuration.
*/
void setLoader(BeanCollectionLoader loader);
/**
* Return the mechanism to add beans to the underlying collection.
* <p>
* For Map's this needs to take the mapKey.
* </p>
*/
BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey);
/**
* Set the EbeanServer that owns the configuration.
*/
void setLoader(BeanCollectionLoader loader);
/**
* Return the mechanism to add beans to the underlying collection.
* <p>
* For Map's this needs to take the mapKey.
* </p>
*/
BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey);
/**
* Create an empty collection of the correct type without a parent bean.
*/
BeanCollection<T> createEmptyNoParent();
/**
* Create an empty collection of the correct type.
*/
BeanCollection<T> createEmpty(EntityBean bean);
/**
* Create an empty collection of the correct type.
*/
BeanCollection<T> createEmpty(EntityBean bean);
/**
* Add a bean to the List Set or Map.
*/
void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck);
/**
* Add a bean to the List Set or Map.
*/
void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck);
/**
* Create a lazy loading proxy for a List Set or Map.
*/
BeanCollection<T> createReference(EntityBean parentBean);
/**
* Create a lazy loading proxy for a List Set or Map.
*/
BeanCollection<T> createReference(EntityBean parentBean);
/**
* Refresh the List Set or Map.
*/
void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean);
/**
* Apply the new refreshed BeanCollection to the appropriate property of the parent bean.
*/
void refresh(BeanCollection<?> bc, EntityBean parentBean);
/**
* Refresh the List Set or Map.
*/
void refresh(EbeanServer server, Query<?> query, Transaction t, EntityBean parentBean);
/**
* Apply the new refreshed BeanCollection to the appropriate property of the parent bean.
*/
void refresh(BeanCollection<?> bc, EntityBean parentBean);
/**
* Write the collection out as json.
@@ -13,42 +13,42 @@ public class BeanCollectionHelpFactory {
static final BeanSetHelp SET_HELP = new BeanSetHelp();
/**
* Create the helper based on the many property.
*/
public static <T> BeanCollectionHelp<T> create(BeanPropertyAssocMany<T> manyProperty) {
/**
* Create the helper based on the many property.
*/
public static <T> BeanCollectionHelp<T> create(BeanPropertyAssocMany<T> manyProperty) {
ManyType manyType = manyProperty.getManyType();
switch (manyType) {
case LIST:
return new BeanListHelp<T>(manyProperty);
case SET:
return new BeanSetHelp<T>(manyProperty);
case MAP:
return new BeanMapHelp<T>(manyProperty);
default:
throw new RuntimeException("Invalid type "+manyType);
}
}
ManyType manyType = manyProperty.getManyType();
switch (manyType) {
case LIST:
return new BeanListHelp<T>(manyProperty);
case SET:
return new BeanSetHelp<T>(manyProperty);
case MAP:
return new BeanMapHelp<T>(manyProperty);
default:
throw new RuntimeException("Invalid type " + manyType);
}
}
@SuppressWarnings("unchecked")
public static <T> BeanCollectionHelp<T> create(OrmQueryRequest<T> request) {
public static <T> BeanCollectionHelp<T> create(OrmQueryRequest<T> request) {
SpiQuery.Type manyType = request.getQuery().getType();
if (manyType.equals(SpiQuery.Type.LIST)) {
return LIST_HELP;
} else if (manyType.equals(SpiQuery.Type.SET)) {
return SET_HELP;
} else {
BeanDescriptor<T> target = request.getBeanDescriptor();
String mapKey = request.getQuery().getMapKey();
return new BeanMapHelp<T>(target, mapKey);
}
}
SpiQuery.Type manyType = request.getQuery().getType();
if (manyType.equals(SpiQuery.Type.LIST)){
return LIST_HELP;
} else if (manyType.equals(SpiQuery.Type.SET)) {
return SET_HELP;
} else {
BeanDescriptor<T> target = request.getBeanDescriptor();
String mapKey = request.getQuery().getMapKey();
return new BeanMapHelp<T>(target, mapKey);
}
}
}
@@ -119,7 +119,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
for (int i = 0; i < propertyLength; i++) {
if (fromEbi.isLoadedProperty(i) ) {
if (fromEbi.isLoadedProperty(i)) {
BeanProperty property = getBeanProperty(names[i]);
if (!toEbi.isLoadedProperty(i)) {
Object val = property.getValue(bean);
@@ -228,9 +228,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
*/
private final BeanDescriptorMap owner;
private final String[] properties;
/**
* Intercept pre post on insert,update, and delete .
*/
@@ -276,7 +276,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Derived list of properties that are used for version concurrency checking.
*/
private final BeanProperty versionProperty;
private final int versionPropertyIndex;
private final BeanProperty whenModifiedProperty;
@@ -287,7 +287,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Properties that are initialised in the constructor need to be 'unloaded' to support partial object queries.
*/
private final int[] unloadProperties;
/**
* Properties local to this type (not from a super type).
*/
@@ -298,7 +298,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
*/
private final BeanProperty[] propertiesMutable;
private final BeanPropertyAssocOne<?> unidirectional;
/**
@@ -347,10 +347,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
*/
private final String fullName;
private final Map<String, DeployNamedQuery> namedQueries;
private final Map<String, DeployNamedUpdate> namedUpdates;
/**
* Flag used to determine if saves can be skipped.
*/
@@ -362,7 +358,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private boolean deleteRecurseSkippable;
private final EntityBean prototypeEntityBean;
private final IdBinder idBinder;
private String idBinderInLHSSql;
@@ -371,6 +367,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private String deleteByIdSql;
private String deleteByIdInSql;
private String whereIdInSql;
private String softDeleteByIdSql;
private String softDeleteByIdInSql;
@@ -414,9 +411,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.beanType = deploy.getBeanType();
this.rootBeanType = PersistenceContextUtil.root(beanType);
this.prototypeEntityBean = createPrototypeEntityBean(beanType);
this.namedQueries = deploy.getNamedQueries();
this.namedUpdates = deploy.getNamedUpdates();
this.inheritInfo = deploy.getInheritInfo();
@@ -505,7 +499,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
// Check if there are no cascade delete associated beans (also subject to
// change in initialiseOther()).
deleteRecurseSkippable = (0 == (propertiesOneExportedDelete.length + propertiesOneImportedDelete.length + propertiesManyDelete.length));
// object used to handle Id values
this.idBinder = owner.createIdBinder(idProperty);
this.whenModifiedProperty = findWhenModifiedProperty();
@@ -517,7 +511,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.versionPropertyIndex = -1;
this.unloadProperties = new int[0];
this.propertiesIndex = new BeanProperty[0];
} else {
EntityBeanIntercept ebi = prototypeEntityBean._ebean_getIntercept();
this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName());
@@ -535,40 +529,40 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* These properties need to be unloaded when populating beans for queries.
*/
private int[] derivePropertiesToUnload(EntityBean prototypeEntityBean) {
boolean[] loaded = prototypeEntityBean._ebean_getIntercept().getLoaded();
int[] props = new int[loaded.length];
int pos = 0;
// collect the positions of the properties initialised in the default constructor.
for (int i = 0; i < loaded.length; i++) {
if (loaded[i]) {
props[pos++] = i;
}
}
if (pos == 0) {
// nothing set in the constructor
return new int[0];
}
// populate a smaller/minimal array
int[] unload = new int[pos];
System.arraycopy(props, 0, unload, 0, pos);
return unload;
}
/**
* Create an entity bean that is used as a prototype/factory to create new instances.
* Create an entity bean that is used as a prototype/factory to create new instances.
*/
private EntityBean createPrototypeEntityBean(Class<T> beanType) {
if (Modifier.isAbstract(beanType.getModifiers())) {
return null;
}
return null;
}
try {
return (EntityBean) beanType.newInstance();
} catch (Exception e) {
throw new IllegalStateException("Error trying to create the prototypeEntityBean for "+beanType, e);
throw new IllegalStateException("Error trying to create the prototypeEntityBean for " + beanType, e);
}
}
@@ -614,9 +608,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* These properties need to be initialised prior to the association properties
* as they are used to get the imported and exported properties.
* </p>
*
* @param withHistoryTables map populated if @History is supported on this entity bean
*/
public void initialiseId(Map<String, String> withHistoryTables, Map<String,String> draftTables) {
public void initialiseId(Map<String, String> withHistoryTables, Map<String, String> draftTables) {
if (logger.isTraceEnabled()) {
logger.trace("BeanDescriptor initialise " + fullName);
@@ -650,9 +645,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Initialise the exported and imported parts for associated properties.
*
* @param asOfTableMap the map of base tables to associated 'with history' tables
* @param asOfTableMap the map of base tables to associated 'with history' tables
* @param asOfViewSuffix the suffix added to the table name to derive the 'with history' view name
* @param draftTableMap the map of base tables to associated 'draft' tables.
* @param draftTableMap the map of base tables to associated 'draft' tables.
*/
public void initialiseOther(Map<String, String> asOfTableMap, String asOfViewSuffix, Map<String, String> draftTableMap) {
@@ -694,7 +689,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
String idEqualsSql = idBinder.getBindIdSql(null);
deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql;
deleteByIdInSql = "delete from " + baseTable + " where " + idBinderInLHSSqlNoAlias + " ";
whereIdInSql = " where " + idBinderInLHSSqlNoAlias + " ";
deleteByIdInSql = "delete from " + baseTable + whereIdInSql;
if (softDelete) {
softDeleteByIdSql = "update " + baseTable + " set " + getSoftDeleteDbSet() + " where " + idEqualsSql;
@@ -703,14 +699,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
softDeleteByIdSql = null;
softDeleteByIdInSql = null;
}
if (!isEmbedded()) {
// parse every named update up front into sql dml
for (DeployNamedUpdate namedUpdate : namedUpdates.values()) {
DeployUpdateParser parser = new DeployUpdateParser(this);
namedUpdate.initialise(parser);
}
}
}
/**
@@ -722,10 +710,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
propertiesMany[i].initialisePostTarget();
}
if (inheritInfo != null && !inheritInfo.isRoot()) {
docStoreAdapter = (DocStoreBeanAdapter<T>)inheritInfo.getRoot().desc().docStoreAdapter();
docStoreAdapter = (DocStoreBeanAdapter<T>) inheritInfo.getRoot().desc().docStoreAdapter();
}
docMapping = docStoreAdapter.createDocMapping();
docStoreAdapter.registerPaths();
cacheHelp.deriveNotifyFlags();
}
public void initInheritInfo() {
@@ -765,12 +754,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
PersistRequest.Type type = request.getType();
switch (type) {
case INSERT:
return changeLogFilter.includeInsert(request) ? insertBeanChange(request): null;
return changeLogFilter.includeInsert(request) ? insertBeanChange(request) : null;
case UPDATE:
case SOFT_DELETE:
return changeLogFilter.includeUpdate(request) ? updateBeanChange(request): null;
return changeLogFilter.includeUpdate(request) ? updateBeanChange(request) : null;
case DELETE:
return changeLogFilter.includeDelete(request) ? deleteBeanChange(request) :null;
return changeLogFilter.includeDelete(request) ? deleteBeanChange(request) : null;
default:
throw new IllegalStateException("Unhandled request type " + type);
}
@@ -810,6 +799,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
/**
* Return the "where id in" sql (for use with UpdateQuery).
*/
public String getWhereIdInSql() {
return whereIdInSql;
}
/**
* Return the "delete by id" sql.
*/
@@ -866,10 +862,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
public boolean calculateUseCache(Boolean queryUseCache) {
return (queryUseCache != null) ? queryUseCache : isBeanCaching();
}
/**
* Return the cache options.
*/
@@ -968,7 +960,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
mapping.add(new DocPropertyMapping(discCol, DocPropertyType.INTEGER));
}
}
for (BeanProperty prop: propertiesNonTransient) {
for (BeanProperty prop : propertiesNonTransient) {
prop.docStoreMapping(mapping, prefix);
}
if (inheritInfo != null) {
@@ -1059,12 +1051,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Return true if the persist request needs to notify the cache.
*/
public boolean isCacheNotify(boolean publish) {
public boolean isCacheNotify(PersistRequest.Type type, boolean publish) {
if (draftable && !publish) {
// no caching when editing draft beans
return false;
}
return cacheHelp.isCacheNotify();
return cacheHelp.isCacheNotify(type);
}
/**
@@ -1130,7 +1122,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
public CachedBeanData cacheEmbeddedBeanExtract(EntityBean bean) {
return cacheHelp.beanExtractData(this, bean);
}
/**
* Load the embedded bean (taking into account inheritance).
*/
@@ -1186,7 +1178,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
public void cacheHandleDeleteById(Object id) {
cacheHelp.beanCacheRemove(id);
}
/**
* Returns true if it managed to populate/load the bean from the cache.
*/
@@ -1339,15 +1331,15 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
return collectQueryPlanStatisticsInternal(reset, false);
}
@Override
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset) {
return collectQueryPlanStatisticsInternal(reset, false);
}
public List<MetaQueryPlanStatistic> collectQueryPlanStatisticsInternal(boolean reset, boolean collectAll) {
List<MetaQueryPlanStatistic> list = new ArrayList<MetaQueryPlanStatistic>(queryPlanCache.size());
for (CQueryPlan queryPlan : queryPlanCache.values()) {
for (CQueryPlan queryPlan : queryPlanCache.values()) {
Snapshot snapshot = queryPlan.getSnapshot(reset);
if (collectAll || snapshot.getExecutionCount() > 0) {
list.add(snapshot);
@@ -1422,7 +1414,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Return true if delete can use a single SQL statement.
*
* <p>
* This implies cascade delete does not continue depth wise and that this is no
* associated L2 bean caching.
*/
@@ -1535,28 +1527,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return idBinder.getBindValues(idValue);
}
/**
* Return a named query.
*/
public DeployNamedQuery getNamedQuery(String name) {
return namedQueries.get(name);
}
public void addNamedQuery(DeployNamedQuery deployNamedQuery) {
namedQueries.put(deployNamedQuery.getName(), deployNamedQuery);
}
/**
* Return a named update.
*/
public DeployNamedUpdate getNamedUpdate(String name) {
return namedUpdates.get(name);
}
@Override
@SuppressWarnings("unchecked")
public T createBean() {
return (T)createEntityBean();
return (T) createEntityBean();
}
/**
@@ -1565,8 +1539,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
@SuppressWarnings("unchecked")
public EntityBean createEntityBean() {
try {
EntityBean bean = (EntityBean)prototypeEntityBean._ebean_newInstance();
EntityBean bean = (EntityBean) prototypeEntityBean._ebean_newInstance();
if (unloadProperties.length > 0) {
// 'unload' any properties initialised in the default constructor
EntityBeanIntercept ebi = bean._ebean_getIntercept();
@@ -1575,7 +1549,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
return bean;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
@@ -1657,7 +1631,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]);
} else {
throw new RuntimeException("Error getting BeanDescriptor for path " + path + " from " + getFullName());
throw new PersistenceException("Invalid path " + path + " from " + getFullName());
}
}
@@ -1833,7 +1807,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
public Object convertIdFromJson(Object idValue) {
return idBinder.convertIdFromJson(idValue);
}
/**
* Return the default order by that may need to be added if a many property is
* included in the query.
@@ -1898,7 +1872,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Register all the assoc many properties on this bean that are not populated with the load context.
* <p>
* This provides further lazy loading via the load context.
* This provides further lazy loading via the load context.
* </p>
*/
public void lazyLoadRegister(String prefix, EntityBeanIntercept ebi, EntityBean bean, LoadContext loadContext) {
@@ -1957,9 +1931,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Return a Comparator for local sorting of lists.
*
* @param sortByClause
* list of property names with optional ASC or DESC suffix.
*
* @param sortByClause list of property names with optional ASC or DESC suffix.
*/
@SuppressWarnings("unchecked")
private ElComparator<T> createComparator(String sortByClause) {
@@ -2052,7 +2025,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
ElPropertyDeploy fk = elDeployCache.get(propName);
if (fk != null && fk instanceof BeanFkeyProperty) {
// propertyDeploy chain for foreign key column
return ((BeanFkeyProperty)fk).create(chain.getExpression(), chain.isContainsMany());
return ((BeanFkeyProperty) fk).create(chain.getExpression(), chain.isContainsMany());
}
}
@@ -2114,9 +2087,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
*/
public void resetManyProperties(Object dbBean) {
EntityBean bean = (EntityBean)dbBean;
EntityBean bean = (EntityBean) dbBean;
for (int i = 0; i < propertiesMany.length; i++) {
propertiesMany[i].resetMany(bean);
if (propertiesMany[i].isCascadeRefresh()) {
propertiesMany[i].resetMany(bean);
}
}
}
@@ -2361,10 +2336,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
*/
public String getBaseTable(SpiQuery.TemporalMode mode) {
switch (mode) {
case DRAFT: return draftTable;
case VERSIONS: return baseTableVersionsBetween;
case AS_OF: return baseTableAsOf;
default: return baseTable;
case DRAFT:
return draftTable;
case VERSIONS:
return baseTableVersionsBetween;
case AS_OF:
return baseTableAsOf;
default:
return baseTable;
}
}
@@ -2579,19 +2558,17 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Return true if this bean should be inserted rather than updated.
*
* @param ebi
* The entity bean intercept
* @param insertMode
* true if the 'root request' was an insert rather than an update
*
* @param ebi The entity bean intercept
* @param insertMode true if the 'root request' was an insert rather than an update
*/
public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) {
if (ebi.isLoaded()) {
// must be an update as the bean is loaded
return false;
}
if (idProperty.isEmbedded()) {
// not using Id generator so just base on isLoaded()
return !ebi.isLoaded();
@@ -2603,11 +2580,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
// same as the 'root request'
return insertMode;
}
public boolean isReference(EntityBeanIntercept ebi) {
return ebi.isReference() || hasIdPropertyOnly(ebi);
}
public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) {
return ebi.hasIdOnly(idPropertyIndex);
}
@@ -2656,13 +2633,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
}
public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) {
if (!hasVersionProperty(ebi)) {
return ConcurrencyMode.NONE;
} else {
return concurrencyMode;
return concurrencyMode;
}
}
@@ -2748,7 +2725,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
query.order(rawSql.getSql().getOrderBy());
}
query.order().asc(idProperty.getName());
} else if (!orderBy.containsProperty(idProperty.getName())){
} else if (!orderBy.containsProperty(idProperty.getName())) {
query.order().asc(idProperty.getName());
}
}
@@ -2880,8 +2857,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, null);
}
}
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, key);
}
@@ -2889,11 +2866,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWriteProperties(writeJson, bean);
}
public T jsonRead(ReadJson jsonRead, String path) throws IOException {
return jsonHelp.jsonRead(jsonRead, path);
}
protected T jsonReadObject(ReadJson jsonRead, String path) throws IOException {
return jsonHelp.jsonReadObject(jsonRead, path);
}
@@ -14,6 +14,7 @@ import com.avaje.ebeaninternal.server.cache.CachedBeanDataFromBean;
import com.avaje.ebeaninternal.server.cache.CachedBeanDataToBean;
import com.avaje.ebeaninternal.server.cache.CachedManyIds;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
@@ -28,11 +29,13 @@ import java.util.Map;
/**
* Helper for BeanDescriptor that manages the bean, query and collection caches.
*
*
* @param <T> The entity bean type
*/
final class BeanDescriptorCacheHelp<T> {
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorCacheHelp.class);
private static final Logger queryLog = LoggerFactory.getLogger("org.avaje.ebean.cache.QUERY");
private static final Logger beanLog = LoggerFactory.getLogger("org.avaje.ebean.cache.BEAN");
private static final Logger manyLog = LoggerFactory.getLogger("org.avaje.ebean.cache.COLL");
@@ -52,7 +55,7 @@ final class BeanDescriptorCacheHelp<T> {
private final Class<?> beanType;
private final String cacheName;
private final BeanPropertyAssocOne<?>[] propertiesOneImported;
private final String naturalKeyProperty;
@@ -60,8 +63,18 @@ final class BeanDescriptorCacheHelp<T> {
private final ServerCache naturalKeyCache;
private final ServerCache queryCache;
/**
* Set to true if all persist changes need to notify the cache.
*/
private boolean cacheNotifyOnAll;
/**
* Set to true if delete changes need to notify cache.
*/
private boolean cacheNotifyOnDelete;
BeanDescriptorCacheHelp(BeanDescriptor<T> desc, ServerCacheManager cacheManager, CacheOptions cacheOptions,
boolean cacheSharableBeans, BeanPropertyAssocOne<?>[] propertiesOneImported) {
boolean cacheSharableBeans, BeanPropertyAssocOne<?>[] propertiesOneImported) {
this.desc = desc;
this.beanType = desc.rootBeanType;
@@ -75,7 +88,7 @@ final class BeanDescriptorCacheHelp<T> {
if (!cacheOptions.isEnableQueryCache()) {
this.queryCache = null;
} else {
this.queryCache = cacheManager.getQueryCache(beanType);
this.queryCache = cacheManager.getQueryCache(beanType);
}
if (cacheOptions.isEnableBeanCache()) {
@@ -91,6 +104,43 @@ final class BeanDescriptorCacheHelp<T> {
}
}
/**
* Derive the cache notify flags.
*/
public void deriveNotifyFlags() {
cacheNotifyOnAll = (beanCache != null || queryCache != null);
cacheNotifyOnDelete = !cacheNotifyOnAll && isNotifyOnDeletes();
if (logger.isDebugEnabled()) {
if (isBeanCaching() || isQueryCaching() || cacheNotifyOnAll || cacheNotifyOnDelete) {
String notifyMode = cacheNotifyOnAll ? "All" : (cacheNotifyOnDelete ? "Delete" : "None");
logger.debug("l2 caching on {} - beanCaching:{} queryCaching:{} notifyMode:{} ",
desc.getFullName(), isBeanCaching(), isQueryCaching(), notifyMode);
}
}
}
/**
* Return true if there is an imported bi-directional relationship to a bea
* that does have bean caching enabled.
*/
private boolean isNotifyOnDeletes() {
for (int i = 0; i < propertiesOneImported.length; i++) {
if (propertiesOneImported[i].isCacheNotify()) {
return true;
}
}
return false;
}
/**
* Return true if the persist request needs to notify the cache.
*/
boolean isCacheNotify(PersistRequest.Type type) {
return cacheNotifyOnAll
|| cacheNotifyOnDelete && (type == PersistRequest.Type.DELETE || type == PersistRequest.Type.DELETE_PERMANENT);
}
/**
* Return true if there is currently query caching for this type of bean.
*/
@@ -105,22 +155,6 @@ final class BeanDescriptorCacheHelp<T> {
return beanCache != null;
}
/**
* Return true if the persist request needs to notify the cache.
*/
boolean isCacheNotify() {
if (isBeanCaching() || isQueryCaching()) {
return true;
}
for (int i = 0; i < propertiesOneImported.length; i++) {
if (propertiesOneImported[i].getTargetDescriptor().isBeanCaching()) {
return true;
}
}
return false;
}
CacheOptions getCacheOptions() {
return cacheOptions;
}
@@ -206,7 +240,7 @@ final class BeanDescriptorCacheHelp<T> {
manyLog.trace(" GET {}({}).{} - cache miss", cacheName, parentId, propertyName);
}
} else if (manyLog.isDebugEnabled()) {
manyLog.debug(" GET {}({}).{} - hit", cacheName, parentId, propertyName);
manyLog.debug(" GET {}({}).{} - hit", cacheName, parentId, propertyName);
}
return entry;
}
@@ -221,7 +255,7 @@ final class BeanDescriptorCacheHelp<T> {
// not in cache so return unsuccessful
return false;
}
Object ownerBean = bc.getOwnerBean();
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
PersistenceContext persistenceContext = ebi.getPersistenceContext();
@@ -244,7 +278,9 @@ final class BeanDescriptorCacheHelp<T> {
void manyPropPut(BeanPropertyAssocMany<?> many, Object details, Object parentId) {
CachedManyIds entry = createManyIds(many, details);
cachePutManyIds(parentId, many.getName(), entry);
if (entry != null) {
cachePutManyIds(parentId, many.getName(), entry);
}
}
void cachePutManyIds(Object parentId, String manyName, CachedManyIds entry) {
@@ -262,6 +298,9 @@ final class BeanDescriptorCacheHelp<T> {
List<Object> idList = new ArrayList<Object>();
Collection<?> actualDetails = BeanCollectionUtil.getActualEntries(details);
if (actualDetails == null) {
return null;
}
for (Object bean : actualDetails) {
idList.add(targetDescriptor.getId((EntityBean) bean));
}
@@ -287,7 +326,7 @@ final class BeanDescriptorCacheHelp<T> {
// try to lookup the id using the natural key
Object id = naturalKeyCache.get(keyBindParam.getValue());
if (natLog.isTraceEnabled() && id != null) {
if (natLog.isTraceEnabled()) {
natLog.trace(" LOOKUP {}({}) - id:{}", cacheName, keyBindParam.getValue(), id);
}
return id;
@@ -311,13 +350,13 @@ final class BeanDescriptorCacheHelp<T> {
// Not using a loadContext for beans coming out of L2 cache
// so that means no batch lazy loading for these beans
EntityBean entityBean = (EntityBean)bean;
EntityBean entityBean = (EntityBean) bean;
EntityBeanIntercept ebi = entityBean._ebean_getIntercept();
ebi.setPersistenceContext(context);
Object id = desc.getId(entityBean);
desc.contextPut(context, id, bean);
}
/**
* Return the beanCache creating it if necessary.
*/
@@ -327,7 +366,7 @@ final class BeanDescriptorCacheHelp<T> {
}
return beanCache;
}
/**
* Clear the bean cache.
*/
@@ -383,7 +422,7 @@ final class BeanDescriptorCacheHelp<T> {
CachedBeanData beanCacheGetData(Object id) {
return (CachedBeanData) getBeanCache().get(id);
}
T beanCacheGet(Object id, Boolean readOnly, PersistenceContext context) {
T bean = beanCacheGetInternal(id, readOnly, context);
if (bean != null) {
@@ -391,7 +430,7 @@ final class BeanDescriptorCacheHelp<T> {
}
return bean;
}
/**
* Return a bean from the bean cache.
*/
@@ -418,7 +457,7 @@ final class BeanDescriptorCacheHelp<T> {
}
}
return (T)loadBean(id, readOnly, data, context);
return (T) loadBean(id, readOnly, data, context);
}
/**
@@ -539,7 +578,7 @@ final class BeanDescriptorCacheHelp<T> {
}
/**
* Add appropriate cache changes to support delete.
* Add appropriate cache changes to support delete by id.
*/
void handleDelete(Object id, CacheChangeSet changeSet) {
if (beanCache != null) {
@@ -549,7 +588,7 @@ final class BeanDescriptorCacheHelp<T> {
}
/**
* Add appropriate cache changes to support delete.
* Add appropriate cache changes to support delete bean.
*/
void handleDelete(Object id, PersistRequestBean<T> deleteRequest, CacheChangeSet changeSet) {
queryCacheClear(changeSet);
@@ -581,16 +620,23 @@ final class BeanDescriptorCacheHelp<T> {
queryCacheClear(changeSet);
if (beanCache == null) {
// query caching only
return;
}
List<BeanPropertyAssocMany<?>> manyCollections = updateRequest.getUpdatedManyCollections();
if (manyCollections != null) {
for (int i = 0; i < manyCollections.size(); i++) {
BeanPropertyAssocMany<?> many = manyCollections.get(i);
Object details = many.getValue(updateRequest.getEntityBean());
CachedManyIds entry = createManyIds(many, details);
changeSet.addManyPut(desc, many.getName(), id, entry);
if (entry != null) {
changeSet.addManyPut(desc, many.getName(), id, entry);
}
}
}
// check if the bean itself was updated
if (!updateRequest.isUpdatedManysOnly()) {
@@ -8,7 +8,7 @@ import java.util.List;
/**
* Helper for BeanDescriptor that manages draft entity beans.
*
*
* @param <T> The entity bean type
*/
public final class BeanDescriptorDraftHelp<T> {
@@ -47,7 +47,7 @@ public final class BeanDescriptorDraftHelp<T> {
*/
public boolean draftReset(T draftBean) {
EntityBean draftEntityBean = (EntityBean)draftBean;
EntityBean draftEntityBean = (EntityBean) draftBean;
if (draftDirty != null) {
// set @DraftDirty property to false
@@ -72,11 +72,11 @@ public final class BeanDescriptorDraftHelp<T> {
public T publish(T draftBean, T liveBean) {
if (liveBean == null) {
liveBean = (T)desc.createEntityBean();
liveBean = (T) desc.createEntityBean();
}
EntityBean draft = (EntityBean)draftBean;
EntityBean live = (EntityBean)liveBean;
EntityBean draft = (EntityBean) draftBean;
EntityBean live = (EntityBean) liveBean;
BeanProperty idProperty = desc.getIdProperty();
if (idProperty != null) {
@@ -2,9 +2,6 @@ package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Model;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.cache.ServerCacheManager;
@@ -15,20 +12,20 @@ import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbHistorySupport;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebean.event.changelog.ChangeLogFilter;
import com.avaje.ebean.event.changelog.ChangeLogListener;
import com.avaje.ebean.event.changelog.ChangeLogPrepare;
import com.avaje.ebean.event.changelog.ChangeLogRegister;
import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.core.InternalConfiguration;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.XmlConfig;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded;
@@ -46,8 +43,6 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.deploy.parse.ReadAnnotations;
import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties;
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
@@ -109,8 +104,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final DeployCreateProperties createProperties;
private final DeployOrmXml deployOrmXml;
private final BeanManagerFactory beanManagerFactory;
private final ServerConfig serverConfig;
@@ -163,8 +156,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final IdBinderFactory idBinderFactory;
private final XmlConfig xmlConfig;
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
private final boolean eagerFetchLobs;
@@ -190,7 +181,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.serverName = InternString.intern(serverConfig.getName());
this.cacheManager = config.getCacheManager();
this.docStoreFactory = config.getDocStoreFactory();
this.xmlConfig = config.getXmlConfig();
this.dbSequenceBatchSize = serverConfig.getDatabaseSequenceBatchSize();
this.backgroundExecutor = config.getBackgroundExecutor();
this.dataSource = serverConfig.getDataSource();
@@ -207,7 +197,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.namingConvention = serverConfig.getNamingConvention();
this.dbIdentity = config.getDatabasePlatform().getDbIdentity();
this.deplyInherit = config.getDeployInherit();
this.deployOrmXml = config.getDeployOrmXml();
this.deployUtil = config.getDeployUtil();
this.beanManagerFactory = new BeanManagerFactory(config.getDatabasePlatform());
@@ -316,10 +305,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
readEntityBeanTable();
readEntityDeploymentAssociations();
readInheritedIdGenerators();
// creates the BeanDescriptors
readEntityRelationships();
readRawSqlQueries();
List<BeanDescriptor<?>> list = new ArrayList<BeanDescriptor<?>>(descMap.values());
Collections.sort(list, beanDescComparator);
@@ -555,10 +542,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return beanManagerMap.get(beanClassName);
}
public DNativeQuery getNativeQuery(String name) {
return deployOrmXml.getNativeQuery(name);
}
/**
* Create the BeanControllers, BeanFinders and BeanListeners.
*/
@@ -679,25 +662,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return new BeanTable(beanTable, this);
}
/**
* Parse the named Raw Sql queries using BeanDescriptor.
*/
private void readRawSqlQueries() {
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
DeployBeanDescriptor<?> deployDesc = info.getDescriptor();
BeanDescriptor<?> desc = getBeanDescriptor(deployDesc.getBeanType());
for (DRawSqlMeta rawSqlMeta : deployDesc.getRawSqlMeta()) {
if (rawSqlMeta.getQuery() != null) {
DeployNamedQuery nq = new DRawSqlSelectBuilder(namingConvention, desc, rawSqlMeta).parse();
desc.addNamedQuery(nq);
}
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void readEntityRelationships() {
@@ -830,7 +794,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
if (matchSet.size() == 0) {
if (matchSet.isEmpty()) {
// this is a unidirectional relationship
// ... that is no matching property on the 'detail' bean
return false;
@@ -1148,8 +1112,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
readAnnotations.readAssociations(info, this);
readXml(desc);
if (EntityType.SQL == desc.getEntityType()) {
desc.setBaseTable(null, null, null);
}
@@ -1271,127 +1233,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
private void readXml(DeployBeanDescriptor<?> deployDesc) {
List<Dnode> eXml = xmlConfig.findEntityXml(deployDesc.getFullName());
readXmlRawSql(deployDesc, eXml);
Dnode entityXml = deployOrmXml.findEntityDeploymentXml(deployDesc.getFullName());
if (entityXml != null) {
readXmlNamedQueries(deployDesc, entityXml);
readXmlSql(deployDesc, entityXml);
}
}
/**
* Read sql-select (FUTURE: additionally sql-insert, sql-update, sql-delete).
* If found this entity bean is based on raw sql.
*/
private void readXmlSql(DeployBeanDescriptor<?> deployDesc, Dnode entityXml) {
List<Dnode> sqlSelectList = entityXml.findAll("sql-select", entityXml.getLevel() + 1);
for (int i = 0; i < sqlSelectList.size(); i++) {
Dnode sqlSelect = sqlSelectList.get(i);
readSqlSelect(deployDesc, sqlSelect);
}
}
private String findContent(Dnode node, String nodeName) {
Dnode found = node.find(nodeName);
if (found != null) {
return found.getNodeContent();
} else {
return null;
}
}
private void readSqlSelect(DeployBeanDescriptor<?> deployDesc, Dnode sqlSelect) {
String name = sqlSelect.getStringAttr("name", "default");
String extend = sqlSelect.getStringAttr("extend", null);
String queryDebug = sqlSelect.getStringAttr("debug", null);
boolean debug = (queryDebug != null && queryDebug.equalsIgnoreCase("true"));
// the raw sql select
String query = findContent(sqlSelect, "query");
String where = findContent(sqlSelect, "where");
String having = findContent(sqlSelect, "having");
String columnMapping = findContent(sqlSelect, "columnMapping");
DRawSqlMeta m = new DRawSqlMeta(name, extend, query, debug, where, having, columnMapping);
deployDesc.add(m);
}
private void readXmlRawSql(DeployBeanDescriptor<?> deployDesc, List<Dnode> entityXml) {
List<Dnode> rawSqlQueries = xmlConfig.find(entityXml, "raw-sql");
for (int i = 0; i < rawSqlQueries.size(); i++) {
Dnode rawSqlDnode = rawSqlQueries.get(i);
String name = rawSqlDnode.getAttribute("name");
if (isEmpty(name)) {
throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing name attribute");
}
Dnode queryNode = rawSqlDnode.find("query");
if (queryNode == null) {
throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing query element");
}
String sql = queryNode.getNodeContent();
if (isEmpty(sql)) {
throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " has empty sql in the query element?");
}
List<Dnode> columnMappings = rawSqlDnode.findAll("columnMapping", 1);
RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql);
for (int j = 0; j < columnMappings.size(); j++) {
Dnode cm = columnMappings.get(j);
String column = cm.getAttribute("column");
String property = cm.getAttribute("property");
rawSqlBuilder.columnMapping(column, property);
}
RawSql rawSql = rawSqlBuilder.create();
DeployNamedQuery namedQuery = new DeployNamedQuery(name, rawSql);
deployDesc.add(namedQuery);
}
}
private boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
}
/**
* Read named queries for this bean type.
*/
private void readXmlNamedQueries(DeployBeanDescriptor<?> deployDesc, Dnode entityXml) {
// look for named-query...
List<Dnode> namedQueries = entityXml.findAll("named-query", 1);
for (Dnode namedQueryXml : namedQueries) {
String name = namedQueryXml.getAttribute("name");
Dnode query = namedQueryXml.find("query");
if (query == null) {
logger.warn("orm.xml " + deployDesc.getFullName() + " named-query missing query element?");
} else {
String oql = query.getNodeContent();
// TODO: QueryHints not read from xml yet
if (name == null || oql == null) {
logger.warn("orm.xml " + deployDesc.getFullName() + " named-query has no query content?");
} else {
// add the named query
DeployNamedQuery q = new DeployNamedQuery(name, oql, null);
deployDesc.add(q);
}
}
}
}
private BeanPropertyInfoFactory createReflectionFactory() {
return new EnhanceBeanPropertyInfoFactory();
@@ -2,18 +2,18 @@ package com.avaje.ebeaninternal.server.deploy;
public class BeanEmbeddedMeta {
final BeanProperty[] properties;
public BeanEmbeddedMeta(BeanProperty[] properties) {
this.properties = properties;
}
/**
* Return the properties with over ridden mapping information.
*/
public BeanProperty[] getProperties() {
return properties;
}
final BeanProperty[] properties;
public BeanEmbeddedMeta(BeanProperty[] properties) {
this.properties = properties;
}
/**
* Return the properties with over ridden mapping information.
*/
public BeanProperty[] getProperties() {
return properties;
}
}
@@ -12,41 +12,41 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
*/
public class BeanEmbeddedMetaFactory {
/**
* Create BeanProperties for embedded beans using the deployment specific DB column name and table alias.
*/
public static BeanEmbeddedMeta create(BeanDescriptorMap owner, DeployBeanPropertyAssocOne<?> prop) {
/**
* Create BeanProperties for embedded beans using the deployment specific DB column name and table alias.
*/
public static BeanEmbeddedMeta create(BeanDescriptorMap owner, DeployBeanPropertyAssocOne<?> prop) {
// we can get a BeanDescriptor for an Embedded bean
// and know that it is NOT recursive, as Embedded beans are
// only allow to hold simple scalar types...
BeanDescriptor<?> targetDesc = owner.getBeanDescriptor(prop.getTargetType());
if (targetDesc == null){
String msg = "Could not find BeanDescriptor for "+prop.getTargetType()
+". Perhaps the EmbeddedId class is not registered?";
throw new PersistenceException(msg);
}
// we can get a BeanDescriptor for an Embedded bean
// and know that it is NOT recursive, as Embedded beans are
// only allow to hold simple scalar types...
BeanDescriptor<?> targetDesc = owner.getBeanDescriptor(prop.getTargetType());
if (targetDesc == null) {
String msg = "Could not find BeanDescriptor for " + prop.getTargetType()
+ ". Perhaps the EmbeddedId class is not registered?";
throw new PersistenceException(msg);
}
// deployment override information (column names)
Map<String, String> propColMap = prop.getDeployEmbedded().getPropertyColumnMap();
// deployment override information (column names)
Map<String, String> propColMap = prop.getDeployEmbedded().getPropertyColumnMap();
BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar();
BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar();
BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length];
BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length];
for (int i = 0; i < sourceProperties.length; i++) {
for (int i = 0; i < sourceProperties.length; i++) {
String propertyName = sourceProperties[i].getName();
String dbColumn = propColMap.get(propertyName);
if (dbColumn == null) {
// dbColumn not overridden so take original
dbColumn = sourceProperties[i].getDbColumn();
}
String propertyName = sourceProperties[i].getName();
String dbColumn = propColMap.get(propertyName);
if (dbColumn == null) {
// dbColumn not overridden so take original
dbColumn = sourceProperties[i].getDbColumn();
}
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn);
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
}
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn);
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
}
return new BeanEmbeddedMeta(embeddedProperties);
}
return new BeanEmbeddedMeta(embeddedProperties);
}
}

Some files were not shown because too many files have changed in this diff Show More