Compare commits

...
Author SHA1 Message Date
Rob Bygrave 637cbc6489 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.8 2014-10-08 21:44:15 +13:00
Rob Bygrave 5d09ca16d6 Fix for #192 - Backslash literal in LIKE expression - Postgres (and H2, MySql) behaviour 2014-10-08 21:42:39 +13:00
Rob Bygrave 6ed1f25e22 [maven-release-plugin] prepare for next development iteration 2014-09-10 00:02:41 +12:00
Rob Bygrave b8ae443074 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.7 2014-09-10 00:02:31 +12:00
Rob Bygrave ab9762bbd9 Fix for #190 - javax.persistence.PersistenceException: Server [null] was not found? at EntityBeanIntercept.loadBean(EntityBeanIntercept.java:693) 2014-09-10 00:00:49 +12:00
Rob Bygrave 2c90b239cd [maven-release-plugin] prepare for next development iteration 2014-08-22 00:45:30 +12:00
Rob Bygrave 13806767a3 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.6 2014-08-22 00:45:20 +12:00
Rob Bygrave 40e43b9d5f update agent and maven plugin versions to 4.1.6 2014-08-22 00:44:08 +12:00
Rob Bygrave 29f1e63146 Fix for #186 - Fix for Lazy load when fetched optional ManyToOne bean is null in database 2014-08-22 00:41:12 +12:00
rbygrave 5cf008b521 Fix for #182 Modify pom removing provided scope from slf4j-api dependency 2014-08-12 23:30:41 +12:00
rbygrave 3e788d4598 [maven-release-plugin] prepare for next development iteration 2014-08-01 21:43:53 +12:00
rbygrave 8a29454893 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.5 2014-08-01 21:43:31 +12:00
rbygrave 62c083a6ea Add LongMaxUpdater, change CQueryPlanStats to use LongMaxUpdater 2014-07-29 23:48:38 +12:00
rbygrave 7f8e4be396 Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2014-07-29 23:44:38 +12:00
rbygrave 2ad39974e8 No effective change - code format and javadoc tidy 2014-07-29 23:44:03 +12:00
Rob Bygrave 664e95d87b Merge pull request #180 from rvbiljouw/master
Index annotation for generating single-column indexes in DDL
2014-07-29 22:45:39 +12:00
Rick van Biljouw 5cf668f65b Disallow Index annotation on fields that have a relationship annotation (OneToMany, ManyToOne or OneToOne) 2014-07-28 19:44:25 +01:00
Rick van Biljouw 31f56621ff Added @Index annotation to generate single-column indexes 2014-07-28 18:43:50 +01:00
rbygrave 11a6cd04ea Update README with latest version 2014-07-26 00:12:41 +12:00
rbygrave fdb573bfe2 Fix for #177 - EbeanServer.refresh() not loading/refreshing @Lob property unless it is annotated with @Basic(fetch=FetchType.EAGER) 2014-07-26 00:09:26 +12:00
rbygrave ae0e1fe691 No effective change - remove some incorrect javadoc 2014-07-25 23:21:14 +12:00
rbygrave 0511e01273 [maven-release-plugin] prepare for next development iteration 2014-07-24 19:45:56 +12:00
rbygrave f3bd38536c [maven-release-plugin] prepare release avaje-ebeanorm-4.1.4 2014-07-24 19:45:30 +12:00
rbygrave 139a558165 Add test TestPropertyChangeListener 2014-07-21 22:59:31 +12:00
rbygrave 53b40f7ce0 Update .gitignore for h2 db directory 2014-07-21 22:32:05 +12:00
rbygrave e50656871d Fix for #175 - Change to use System.getProperty("ebean.serverfactory")
rather than GlobalProperties
2014-07-21 22:31:01 +12:00
rbygrave f7ece0f13c Fix for #174 - Incorrect SQL generated with syntax error at the "order by" clause 2014-07-21 21:49:31 +12:00
rbygrave 562c6fafc9 Update maven-enhancer to 4.1.5, improve test - TestNotEnhancedMappedSuper 2014-07-19 19:41:57 +12:00
rbygrave 63e9f603ec Code cleanup - move old iterator/while loops to for loops 2014-07-19 19:10:13 +12:00
rbygrave 0c4fc4eea3 Fix for #173 - Ability to use a MappedSuperClass without being enhanced as long as it doesn't have persistent fields 2014-07-19 17:49:03 +12:00
rbygrave 2dca1bdf5b Fix for #172 - Regression: @Where predicates not applied in eager fetch (but are on lazy load) 2014-07-18 23:35:14 +12:00
rbygrave 6403a83e0e No effective change - format only 2014-07-18 22:52:49 +12:00
Rob Bygrave b57b91ffef [maven-release-plugin] prepare for next development iteration 2014-07-16 23:18:02 +12:00
Rob Bygrave 4ba5702211 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.3 2014-07-16 23:17:06 +12:00
Rob Bygrave 2e7aeae004 Bump version in README 2014-07-16 23:14:52 +12:00
rbygrave 3f2676ab49 Fix for #168 - ENH: DataSource - add support for configuring autocommit=true 2014-07-16 21:58:32 +12:00
rbygrave 3f29acb25c Fix for #169 - Caused by: java.lang.ClassCastException: org.example.Order cannot be cast to org.example.Customer 2014-07-15 23:09:08 +12:00
rbygrave 6d1a41f979 Fix for #167 - Ebean try to do update child nodes instead of inserting them on cascading save resulting in OptimisticLockException 2014-07-15 22:05:56 +12:00
Rob Bygrave aee46955a7 Add test which saves parent and child using same pk value 2014-07-10 23:27:18 +12:00
rbygrave 38a93b9814 Update README with new version 2014-07-10 22:41:38 +12:00
rbygrave 5f2ad0dc62 [maven-release-plugin] prepare for next development iteration 2014-07-10 21:41:01 +12:00
rbygrave ece7f481bc [maven-release-plugin] prepare release avaje-ebeanorm-4.1.2 2014-07-10 21:40:39 +12:00
rbygrave fa4d0380e5 Add extra test - TestQueryRowCountWithMany 2014-07-10 21:29:44 +12:00
rbygrave 713ba4889d Clean up of test for #162 2014-07-10 21:23:45 +12:00
rbygrave 0543745e18 Test case for #162 - no issue reproduced, no assert failing 2014-07-10 19:48:06 +12:00
rbygrave 4e2bc6cce6 Fix for #166 - Incorrect join with @OneToOne optional on one side and
NOT optional on other side
2014-07-10 03:05:52 +12:00
rbygrave f2d422ff5a Test case for #165 - Incorrect join used when query language with where
expression has a many property and ends with a foreign key column
2014-07-10 01:20:51 +12:00
rbygrave 176197513d No effective change - format only 2014-07-09 23:51:19 +12:00
rbygrave 1e3035c558 Fix for #164 - Incorrect join used when where expression has a many
property and ends with a foreign key column
2014-07-09 23:31:15 +12:00
rbygrave 17f7143447 Fix for #163 - Remove detection of autocommit=true from JdbcTransaction 2014-07-09 22:24:28 +12:00
rbygrave 394dfc96d1 Update Test - cleanup and add clarification test to TestManyWhereJoin 2014-07-09 22:23:31 +12:00
Rob Bygrave 452ce78c3d Fix for #161 - DDL Generation - @OneToOne (foreign key column / JoinColumn) should default to having a unique constraint on it 2014-07-08 23:23:46 +12:00
Rob Bygrave 4cb13760c2 Fix for #160 - DDL generation throwing NPE at com.avaje.ebeaninternal.server.ddl.CreateTableVisitor.isDbColumnWritten(CreateTableVisitor.java:60) 2014-07-08 23:04:27 +12:00
Rob Bygrave 5cc2eff0d4 Fix for #158 - Add support for using setDistinct (by excluding id property from generated sql) 2014-07-07 23:29:48 +12:00
Rob Bygrave af9a01be77 No effective change - update test 2014-07-05 23:08:09 +12:00
Rob Bygrave cceb7c035c Fix for #159 - Refactor - cleanup clode when closing query only transaction + change default option 2014-07-05 22:10:53 +12:00
Rob Bygrave 1ddc103582 Fix for #157 - Regression for @OrderBy on a @ManyToMany property due to change from issue 154 2014-07-02 23:56:42 +12:00
Rob Bygrave b973cc3b86 No effective change - imports of Assert in tests + formatting 2014-07-02 23:29:54 +12:00
Rob Bygrave 10f0ea9bae No effective change - code format and tidy up 2014-07-02 23:10:53 +12:00
Rob Bygrave c49dd54500 Improve exception throw in BeanReflectProperties when _ebean_props not found 2014-07-02 21:23:28 +12:00
Rob Bygrave bf0944bb2a [maven-release-plugin] prepare for next development iteration 2014-06-30 22:06:04 +12:00
Rob Bygrave 59ba6244c1 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.1 2014-06-30 22:05:43 +12:00
Rob Bygrave f88524b34c Fix for #156 - NPE in DDL Generation when Embedded bean as column with unique constraint 2014-06-30 22:03:31 +12:00
Rob Bygrave 6838b4df4f Fix for #155 Add markAsDirty() method 2014-06-27 23:08:36 +12:00
Rob Bygrave 5db7ae504e [maven-release-plugin] prepare for next development iteration 2014-06-26 19:39:13 +12:00
Rob Bygrave 723ae6bffc [maven-release-plugin] prepare release avaje-ebeanorm-4.1.0 2014-06-26 19:38:48 +12:00
Rob Bygrave 33b173dd55 Bump pom to 4.1.0-SNAPSHOT 2014-06-26 19:37:15 +12:00
Rob Bygrave 7966d74eb3 Just tweaking some very common string appending with buffer size prediction 2014-06-26 01:15:48 +12:00
Rob Bygrave 28cb2e210d Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2014-06-26 00:37:40 +12:00
Rob Bygrave 1109d412e7 Fix for #154 - @OrderBy on a @OneToMany property is not used when lazy loading 2014-06-26 00:37:31 +12:00
Rob Bygrave 3e33ece65a No effective change - add protected modified 2014-06-26 00:12:55 +12:00
Rob Bygrave 1dd200fba2 Merge pull request #153 from waffle-iron/master
waffle.io Badge
2014-06-25 09:16:10 +12:00
Making GitHub Delicious. f0ea977f91 add waffle.io badge 2014-06-24 15:13:51 -06:00
Rob Bygrave e830d8efc7 Additional test in TestOrderByWithMany 2014-06-24 00:17:11 +12:00
Rob Bygrave 0b2ba2a82e Fix for #151 - IllegalArgumentException using enum in filter - toBeanType() throws 2014-06-24 00:04:32 +12:00
Rob Bygrave df526b8f3c Fix for #149 - OneToMany with nested ManyToMany deletion bug 2014-06-23 23:34:22 +12:00
Rob Bygrave 8fe1f32e78 No effective change - code reformat on PersistRequestBean 2014-06-21 00:03:28 +12:00
Rob Bygrave c5ea93fb8e Fix for #150 - Stateless update - property changed in BeanPersistAdapter
preUpdate not included in update
2014-06-20 23:49:29 +12:00
Rob Bygrave dd83bc4d26 Test BeanPersistAdapter with stateless update 2014-06-19 02:03:59 +12:00
Rob Bygrave 9b4b7a1a01 Opps, part of #108 2014-06-19 02:03:14 +12:00
Rob Bygrave 548fa745d6 Fix for #108 - ENH (443) : Postgres HStore support 2014-06-19 01:35:07 +12:00
Rob Bygrave 7d83e3f49e Fix for #97 - ENH: (406) Add support for Postgres Serial type 2014-06-13 23:48:45 +12:00
Rob Bygrave 974284bf8a Addition to fix for #139 - DDL constraint generation when table with schema 2014-06-13 02:38:14 +12:00
Rob Bygrave b446f00cd8 Addition to fix for #139 - DDL constraint generation when table with schema 2014-06-12 23:48:40 +12:00
Rob Bygrave a4095b4ae5 Fix for #142 - ENH: Add support for using RawSql with user supplied ResultSet 2014-06-12 21:11:14 +12:00
Rob Bygrave af6dbd0dbe Fix for #140 - Add some support for RawSql findRowCount, remove BeanCollection.hasMoreRows() 2014-06-10 23:52:05 +12:00
Rob Bygrave db17ddd069 No effective change - tidy up test for findPagedList 2014-06-10 23:10:27 +12:00
Rob Bygrave b6b85225e7 Fix for #139 - DDL constraint generation when table with schema 2014-06-10 21:13:16 +12:00
Rob Bygrave b57abfa732 Update README.md 2014-05-30 03:28:25 +12:00
Rob Bygrave 4a4927b1f3 Update README.md 2014-05-30 03:27:38 +12:00
Rob Bygrave 6ac34bec97 Update README.md 2014-05-30 03:26:59 +12:00
Rob Bygrave 82725e6a98 [maven-release-plugin] prepare for next development iteration 2014-05-30 03:07:28 +12:00
Rob Bygrave a744c8b375 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.4 2014-05-30 03:07:04 +12:00
Rob Bygrave 5e2a34de39 removing maven-javadoc-plugin section from pom as it breaks oss sonatype
deployment
2014-05-30 03:05:16 +12:00
Rob Bygrave 8a9ab39a01 Fix for #138 Issue with lazy loading after a findIterate query 2014-05-30 02:50:51 +12:00
Rob Bygrave 515256abfd pom - javadoc + remove unused enhancement section 2014-05-30 01:36:20 +12:00
Rob Bygrave 84e5a6145e Fix for #137 - transaction.setBatchFlushOnQuery(false) not working for
some queries
2014-05-30 01:35:19 +12:00
Rob Bygrave 1f5618d0e3 Fix for #136 - Minor - not log extra SQL into log when using jdbc batch
mode
2014-05-30 01:32:44 +12:00
Rob Bygrave b1de821817 Fix for #135 Make Model abstract, change db() and db(server) to be
static methods.
2014-05-30 01:30:57 +12:00
Rob Bygrave f4dceb97b0 [maven-release-plugin] prepare for next development iteration 2014-05-27 22:28:35 +12:00
Rob Bygrave ad79f175da [maven-release-plugin] prepare release avaje-ebeanorm-4.0.3 2014-05-27 22:28:09 +12:00
Rob Bygrave a4b145bc2d Updated Model - javadoc and cleanup 2014-05-27 22:19:41 +12:00
Rob Bygrave 5491b27528 Fix for #131 - Memory issue (Hard refs) when using findIterate 2014-05-27 20:35:53 +12:00
Rob Bygrave 9211c7e6ab Update Model - insert,update,delete to other server, db(), db(server)
and javadoc tweaks
2014-05-26 01:18:17 +12:00
Rob Bygrave 5bcb6cd116 Cleanup of Model and Finder 2014-05-25 02:33:08 +12:00
Rob Bygrave 534f2c8b07 Initial cleanup of Model and Finder 2014-05-25 02:22:36 +12:00
Rob Bygrave 7ad390934e Add protected method on Ebean to specifically support mocking out the
default server.
2014-05-25 01:59:34 +12:00
Rob Bygrave 2a651c38e3 Initial add of Model and Finder 2014-05-25 00:27:32 +12:00
James RoperandRob Bygrave ddd30f9ea0 Backport to JDK 6
Replaced all uses of ju.Objects.hashCode and ju.Objects.equals with a
copy of their implementations inlined into the code.

Also changed source/target for compiler plugin.

To test, I compiled my own avaje launchagent against 6, and changed to
use that in the pom, compiled/tested the whole project using JDK 7,
then ran mvn surefire:test using JDK 6 - running surefire:test ensures
that mvn doesn't try to recompile everything against 6, since that's not
possible because of some of the delegate classes having delegate methods
to JDK 7 jdbc classes.
2014-05-24 22:51:05 +12:00
Rob Bygrave 01d41b7eea Update README.md 2014-05-19 00:26:21 +12:00
Rob Bygrave 4cdea33073 [maven-release-plugin] prepare for next development iteration 2014-05-18 23:19:06 +12:00
Rob Bygrave 33d7e10ad8 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.2 2014-05-18 23:18:42 +12:00
Rob Bygrave 2201f9220e Change version in pom for 4.0.2 release 2014-05-18 23:17:17 +12:00
Rob Bygrave b8743a5ef2 Fix for #85 - Invalid query when ORDER BY with DISTINCT 2014-05-18 23:00:28 +12:00
Rob Bygrave b587aa8172 No effective change - touch up test assert 2014-05-18 22:04:37 +12:00
Rob Bygrave c37fa83675 No effective change - update test with better assert 2014-05-18 21:51:14 +12:00
Rob Bygrave 08602afcff Fix for #37 - disjunction expression should not produce inner join - left outer join instead 2014-05-18 21:17:19 +12:00
Rob Bygrave 5b98d82f58 No effective change - reformat 2014-05-17 23:53:23 +12:00
Rob Bygrave 22cd5b0e82 Fix for #128 storing UUID as binary 2014-05-17 21:52:51 +12:00
Rob Bygrave 93855478dd Fix for #128 storing UUID as binary 2014-05-17 21:52:18 +12:00
Rob Bygrave 59b86d5b19 Fix for #128 - UUID stored as binary(16) rather than varchar(40) 2014-05-17 21:51:18 +12:00
Rob Bygrave 27afb9ee2e Merge pull request #41 from corux/master
Unit test for BUG 402 / #37
2014-05-17 01:31:21 +12:00
Rob Bygrave a49cd42a0c Fix for #119 - Add support for @PrePersist, @PostPersist, @PostLoad 2014-05-17 01:24:32 +12:00
Rob Bygrave 8c8442ec58 Fix for #112 - ENH (411) : RawSql to support discriminator column mapping. 2014-05-17 00:11:44 +12:00
Rob Bygrave 41f3173adf Add PagedList was part of #96 2014-05-16 01:41:56 +12:00
Rob Bygrave 1a4192b40f Add another embedded bean l2 cache test 2014-05-15 23:57:20 +12:00
Rob Bygrave fffda7c789 Test and additional fix for #56 - Using findIterate with MySQL streams
the entire result set at once
2014-05-15 22:08:23 +12:00
Rob Bygrave b7f6596bcb Test case for #109 - Ebean magically adding +1 hour for a datetime that is inside the hour of a daylight saving change. This issue does not reproduce. 2014-05-15 00:18:03 +12:00
Rob Bygrave 1ea49dd206 Test for #110 - BeanSet of sub type with inheritance has null listen mode 2014-05-15 00:07:25 +12:00
Rob Bygrave 32e4f114b4 No effective change - cleanup of imports on test code 2014-05-14 23:47:46 +12:00
Rob Bygrave fcbedda597 Fix for #102 - When using @Transient along with @Formula I'm not seeing any values in the field. 2014-05-14 23:35:15 +12:00
Rob Bygrave 2055349d76 Fix for #103 DDL creation doesn't handle nullable attribute of @JoinColumn 2014-05-14 23:21:31 +12:00
Rob Bygrave 44ee64cde5 Fix for #57 - Embedded Entities with Autofetch Exception, plus some additional tests 2014-05-14 23:13:14 +12:00
Rob Bygrave 9f916998c9 Test and Fix for #127 Exception using findIds() with inheritance
SINGLE_TABLE
2014-05-14 21:10:52 +12:00
Rob Bygrave 2ddef37632 Merge pull request #58 from mbell697/autofetch-embedded
Failing test case for issue #57
2014-05-01 00:21:59 +12:00
Rob Bygrave 538ebdc899 Fix for #122 Allow the global row limit for queries to be configurable ( CQuery.GLOBAL_ROW_LIMIT) 2014-05-01 00:12:48 +12:00
Rob Bygrave d6e0d661fb no effective change - javadoc update 2014-05-01 00:11:59 +12:00
Rob Bygrave 289deb397a Embedded beans support in l2 cache - initial work 2014-04-30 01:49:20 +12:00
Rob Bygrave 68b4aab837 Add test for old (427) : Binding enum on find throws No ScalarType registered 2014-04-30 00:08:23 +12:00
Rob Bygrave 563293f7a9 Test case and fix for #113 (395) : On save @OneToOne does not cascade parent IDs to child node 2014-04-30 00:01:54 +12:00
Rob Bygrave 5049694fd3 Add test for old 408 bug - does not reproduce, not sure when fixed at this point 2014-04-29 23:19:03 +12:00
Rob Bygrave a9c8d1960c Add test - self referencing relationships: ResourceFile 2014-04-29 22:25:14 +12:00
Rob Bygrave 066b409a65 No effective change -Add test: Truck, Car, Wheel plus test code cleanup 2014-04-29 22:00:07 +12:00
Rob Bygrave 54ad2e0ad0 remove the Object[] transient from the test 2014-04-29 21:21:44 +12:00
Rob Bygrave 77f7bc2a7e Fix for #111 - Ebean doesn't honor @Transient for non-primitive array properties 2014-04-29 21:20:42 +12:00
Rob Bygrave f773d74597 Test for #105 - did not reproduce - Ebean is trying to delete new
objects added to the list of an entity and then removed without being
persisted before
2014-04-29 20:42:51 +12:00
Rob Bygrave 90b23c4985 Bump version of agent and mavenenhancer 2014-04-29 20:36:00 +12:00
Rob Bygrave 60b4ad410b [maven-release-plugin] prepare for next development iteration 2014-04-27 01:59:10 +12:00
Rob Bygrave 56215aafa3 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.1-RC1 2014-04-27 01:58:45 +12:00
Rob Bygrave 5ba66e4d78 #96 - marking findPagingList() as deprecated 2014-04-27 01:52:44 +12:00
Rob Bygrave 0eb7139426 Fix for #95 - Deprecating - Query findFutureList() and SqlQuery findFutureList() 2014-04-27 01:44:26 +12:00
Rob Bygrave d16d92db54 Test case and Fix for #67 - Bug - @AttributeOverrides annotation for attribute definition in entity does not work for embedded primary keys 2014-04-27 00:57:29 +12:00
Rob Bygrave dde59db411 Test case for #75 - Persistence Context Cache not cleared when updating an entity in a running transaction. Actually the test case does not fail. 2014-04-27 00:05:00 +12:00
Rob Bygrave 1a13fc9742 Fixes for #94 - Additional API - added update(Collection beans); and insert(Collection beans); and #93 - ebean.properties defaultDeleteMissingChildren moved to updatesDeleteMissingChildren 2014-04-26 23:43:08 +12:00
Rob Bygrave e1ec0bcdfe Test and Fix for #39 - TestOnCascadeDeleteChildrenWithCompositeKeys from hei1233212000, with fix included 2014-04-25 22:34:03 +12:00
Rob Bygrave 27803afe64 Test and Fix for #39 - TestOnCascadeDeleteChildrenWithCompositeKeys from hei1233212000, with fix included 2014-04-25 22:32:33 +12:00
Rob Bygrave c011dab3e2 Change to use released 4.1.0 version of agent and maven enhancer 2014-04-25 19:58:41 +12:00
Rob Bygrave fb7abb44e0 Add a test for enhancement equals() and hashCode() 2014-04-25 19:23:56 +12:00
Rob Bygrave 14034ffaa0 Change to use enhancement for creating new entity bean instances 2014-04-25 15:54:07 +12:00
Rob Bygrave 75bcbc06e1 #53 - Add test for protected constructor 2014-04-25 15:19:50 +12:00
Rob Bygrave bc75736780 merge of develop-v4 2014-04-25 14:36:06 +12:00
Rob Bygrave ed0fe7fd57 Caching changes and refactoring, includes Fix for Wobu - TestCacheCollectionIds test case 2014-04-25 03:06:22 +12:00
wbuchnerandRob Bygrave 696dabc658 Add Wobu TestCacheDelete test case for cached beans 2014-04-24 02:42:28 +12:00
Wolfgang BuchnerandRob Bygrave 89f7d2bace Test and fix for TestStatelessUpdate.testStatelessRecursiveUpdateWithVersionField() 2014-04-24 02:34:01 +12:00
Rob Bygrave 4c4e084e43 WIP update/refactoring on caching 2014-04-24 01:57:52 +12:00
Wolfgang BuchnerandRob Bygrave 2dccb818a2 unit test for: update ManyToMany collection cache on updating cached beans 2014-04-23 21:19:06 +12:00
Rob Bygrave a7df7da984 Fix #23 - Covers: unit tests for: stateless updates which fails when executing without... 2014-04-23 21:16:30 +12:00
Wolfgang BuchnerandRob Bygrave b012720a5e unit test for: ensuring many relations won't be deleted on a partial stateless update when using BeanPersistAdapter 2014-04-23 03:29:10 +12:00
Rob Bygrave 1451ec0084 Fix for TestStatelessUpdate.testWithoutChangesAndVersionColumnAndIgnoreNullValues 2014-04-23 03:27:39 +12:00
Wolfgang BuchnerandRob Bygrave 0d2101ca7c unit tests for: stateless updates which fails when executing without changes 2014-04-23 03:05:16 +12:00
Wolfgang BuchnerandRob Bygrave a22496a57d added intelliJ files to gitignore 2014-04-23 03:00:00 +12:00
Rob Bygrave 654b10154e Fix for #83 - Bug: Stateless update cascading to OneToOne or ManyToOne incorrectly tries to INSERT rather than UPDATE 2014-04-23 02:31:08 +12:00
Rob Bygrave f4357c2767 Add test for reference on bean with constructor setting properties 2014-04-23 02:01:27 +12:00
Rob Bygrave 0d1c9e2883 Fix for #91 Stateless Update API Change, #90 Removed BeanState.setReference(), #89 Remove support for multiple @Id properties 2014-04-23 01:51:27 +12:00
Rob Bygrave 5e2d592c2a v4 refactor for diff() support 2014-04-21 12:34:51 +12:00
Rob Bygrave 2344f57f84 #88 - v4 - Breaking API Change - BeanPersistRequest.getOldValues() replaced with ... Map<String,ValuePair> getUpdatedValues() 2014-04-20 22:52:02 +12:00
Rob Bygrave fc628d336d Fix for #87 - v4 - Remove Query setBackgroundFetchAfter() feature 2014-04-20 19:30:40 +12:00
Rob Bygrave 18d9c53ce3 Fix for #86 - v4 - Remove deprecated QueryListener. You must convert to findIterate() or findVisit() 2014-04-20 17:18:13 +12:00
Rob Bygrave 811d298355 WIP - rename EntityBeanIntercept lazy loading property methods 2014-04-20 17:01:36 +12:00
Rob Bygrave f7a37537db WIP update api 2014-04-20 16:57:14 +12:00
Rob Bygrave 0c9d95619b [maven-release-plugin] prepare for next development iteration 2014-04-03 23:19:03 +13:00
Rob Bygrave 3b4836cad9 [maven-release-plugin] prepare release avaje-ebeanorm-3.3.2 2014-04-03 23:18:50 +13:00
Rob Bygrave 6048d97aab Update version in README 2014-04-03 23:17:24 +13:00
Rob Bygrave 16d32b408e Merge branch 'master' of https://github.com/ebean-orm/avaje-ebeanorm 2014-04-03 23:15:01 +13:00
Rob Bygrave 0435ab0991 Fix for #84 Fix for classpath searching with maven surefire / jarfile with manifest 2014-04-03 23:14:55 +13:00
rob 95617d1bea Update README with pom version 2014-04-03 09:12:28 +13:00
Rob Bygrave 96e4b4640f Merge in v4 code changes 2014-04-03 03:47:55 +13:00
Rob Bygrave a3c8ed88ca Update pom to 4.0.1-RC1-SNAPSHOT 2014-04-03 02:19:16 +13:00
Rob Bygrave 38d0e7642e [maven-release-plugin] prepare for next development iteration 2014-04-02 21:59:57 +13:00
Rob Bygrave e4306ce1ab [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1 2014-04-02 21:59:46 +13:00
Rob Bygrave 444b656780 Update pom to 3.3.1 2014-04-02 21:58:13 +13:00
Rob Bygrave fcbba23441 Test case for #83 - Bug: Stateless update cascading to OneToOne or
ManyToOne incorrectly tries to INSERT rather than UPDATE
2014-04-02 21:44:41 +13:00
Rob Bygrave 18961047a1 Fix for #80 - Exposing discriminator value in entity bean throws
exception when updating or deleting
2014-03-31 22:49:44 +13:00
Rob Bygrave 34347fc3e1 Fix for #77 - NPE when lazy loading on a OneToMany that is not a leaf 2014-03-31 22:06:10 +13:00
Rob Bygrave 189e0a2ea9 Part of fix for #79 - moving deactivate() and notifyCommit() into finally block 2014-03-21 21:54:47 +13:00
Rob Bygrave 182e027521 Further changes for #79 - returning/cleanup of bad connection 2014-03-21 02:42:41 +13:00
Rob Bygrave f85fa2c21d Fix for #79 - Rollback in case of connection closed - deactivate() not called when connection.rollback() fails 2014-03-20 20:14:14 +13:00
Rob Bygrave 5a5f479568 Merge pull request #81 from rvowles/master
assume that if includes are specified then turn default includes off
2014-03-16 15:02:28 +13:00
Rob Bygrave 9ca2486c9c Change defaultJarMatch back to default to false (as per previous release) 2014-03-16 14:31:21 +13:00
Richard Vowles 656b239925 assume that if includes are specified then turn default includes off 2014-03-16 13:11:51 +13:00
Rob Bygrave 5d46904341 Fix for introduced issue with sub-query expression 2014-03-16 00:05:43 +13:00
Rob Bygrave f0dd069ed2 Fix for #70 - @PrivateOwned makes Ebean try to remove non persistent objects from DB and throws OptimisticLockException 2014-03-15 13:48:56 +13:00
Rob Bygrave 5bd454bdc0 Merge pull request #76 from icaromuniz/master
Adding test case TestPrivateOwnedIgnoreTransientOrphan related to issue #70
2014-03-15 13:14:17 +13:00
Rob Bygrave eeebaf9a08 Fix for #78 - Wrong collection used in excludeJar 2014-03-15 12:53:58 +13:00
Rob Bygrave 636a148509 Fix for #79 - Rollback in case of connection closed 2014-03-15 12:45:44 +13:00
Icaro Muniz 170cd6051e Adding test case TestPrivateOwnedIgnoreTransientOrphan 2014-02-26 09:23:35 -03:00
Rob Bygrave 522fc2384c [maven-release-plugin] prepare for next development iteration 2014-01-31 02:12:20 +13:00
Rob Bygrave 6f214590aa [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1-RC2 2014-01-31 02:12:07 +13:00
Rob Bygrave 0014ea3501 Remove unused Lucene references including Query.UserIndex and associated methods 2014-01-31 01:26:12 +13:00
Rob Bygrave db3140512f set specific packages on dynamically loaded agent for tests 2014-01-31 00:59:48 +13:00
Rob Bygrave 8bfa331c61 Cleanup of expressions after FilterExprPath now only needed on FilterExpressionList 2014-01-31 00:48:59 +13:00
Rob Bygrave 456a1d8608 Remove AutoFetch query statistics meta entity beans, Remove Xml bean parsing (for json etc) 2014-01-31 00:22:38 +13:00
Rob Bygrave 695d3374a8 Default classPath scan include all jars (hopefully better default
behaviour)
2014-01-31 00:17:11 +13:00
Rob Bygrave 5488d054bf [maven-release-plugin] prepare for next development iteration 2014-01-30 23:25:57 +13:00
Rob Bygrave 4d72273dc3 [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1-RC1 2014-01-30 23:25:44 +13:00
Rob Bygrave cb9a5d6709 Change pom version 3.3.1-RC1 2014-01-30 23:23:53 +13:00
Rob Bygrave 1b8aa682c6 Change classPath scan to support searching manifest (maven surefire classpath scanning) 2014-01-30 23:21:50 +13:00
Rob Bygrave ef39bfcf14 Merge branch 'many-lazy-load' 2014-01-30 22:32:50 +13:00
Rob Bygrave 749f8d0d45 Change *ToMany lazy loading to not use the parent entity/table 2014-01-30 22:27:52 +13:00
Rob Bygrave 2fd221bbb5 Removed old XML code that never got into main code 2014-01-23 00:55:09 +13:00
Rob Bygrave ee96c4afa6 Fix for Issue 72 - Bug - models.Role cannot be cast to java.util.Map [error] at com.avaje.ebeaninternal.server.persist.DefaultPersister.saveAssocManyDetails(DefaultPersister.java:877) 2014-01-23 00:50:09 +13:00
Rob Bygrave 87a948223c Merge branch 'dbpool-logging' 2014-01-22 01:09:19 +13:00
Rob Bygrave acca803a80 Add MetaObjectGraphNodeStats and MetaQueryPlanOriginCount etc 2014-01-22 01:05:22 +13:00
Rob Bygrave 470b27efe1 Mark QueryListener deprecated in favor of findIterate() and findVisit() 2014-01-21 19:41:46 +13:00
Rob Bygrave f287205421 Refactor of query plan hash - add HashQueryPlan, HashQuery etc 2014-01-21 00:48:38 +13:00
Rob Bygrave 6bd8c2bd05 Add MetaInfoManager API, remove MetaQueryStatistic entity bean, refactor query execution stats collection 2014-01-19 22:49:18 +13:00
Rob Bygrave 05dcdf162b Refactor internals of batch lazy loading and query fetches 2014-01-18 23:35:42 +13:00
Rob Bygrave e97e77e2e0 Add reading/setting of namingconvention.schema from ebean.properties 2014-01-17 00:31:03 +13:00
Rob Bygrave e685e2f4ac Updated maven version in README 2014-01-16 23:16:00 +13:00
Rob Bygrave c6019bf3c5 Fix for issue 46 - Persistence Context Cache not cleared when deleting a collection of entities in a running transaction 2014-01-16 23:09:57 +13:00
Rob Bygrave 1a9a4c957c Fix for issue 49 - Query parsing error for SELECT something * 1.5 AS something_else 2014-01-16 21:48:27 +13:00
Rob Bygrave bd57a3b9c8 Merge pull request #65 from yuba/master
make Transaction interface extends Closeable
2014-01-16 00:18:33 -08:00
Rob Bygrave 21a71c3a4f Fix for Issue 62 - Query.setId should not return all results for null input Object 2014-01-16 21:10:13 +13:00
Rob Bygrave f66439a62e Fix for Issue 40 - Expr does not have a betweenProperties(...) method. 2014-01-16 21:02:19 +13:00
Rob Bygrave 73452a5f68 Fix for issue 68 - findRowCount sql error when using a @Formula property in the where clause 2014-01-16 20:36:23 +13:00
Rob Bygrave 5f4789f9ba Support heart beat timeout, tidy up free buffer 2014-01-16 00:28:00 +13:00
yuba eaac874ac0 make Transaction interface extends Closeable
To avoid method name conflict, JtaTransaction#close was renamed to
closeConnection()
2013-12-27 14:00:00 +09:00
Rob Bygrave 4900a460d1 Fix for DataSourcePool trim idle connections not firing frequently
enough
2013-12-14 21:24:26 +13:00
Rob Bygrave d16f33d26b Modified DataSource pool - cleanup of reset and statistics collection 2013-12-12 20:57:50 +13:00
Rob Bygrave 664f08fd6f [maven-release-plugin] prepare for next development iteration 2013-11-25 21:06:09 +13:00
Rob Bygrave 39416e933e [maven-release-plugin] prepare release avaje-ebeanorm-3.2.5 2013-11-25 21:06:00 +13:00
Rob Bygrave 74b118e8f0 Merge pull request #59 from andersha/db2
support db2 and SqlLimiter for db2
2013-11-21 03:19:10 -08:00
AndersHA 43853b0357 cleanup db2 platform 2013-11-20 13:54:01 +01:00
AndersHA f62bc9a35f support db2 and write sqllimiter for db2 2013-11-20 11:07:23 +01:00
Mark Bell d8391ba90c Test embedded fetch also as this throws the same exception 2013-11-11 13:23:53 -05:00
Mark Bell c4d41ecb00 Failing test case for auto fetch + embedded exception 2013-11-11 13:13:46 -05:00
Rob Bygrave 9d8fe97de0 Adjusted Fix for Issue 56 - Using findIterate with MySQL streams the
entire result set at once.  This makes the fix specific to the
DatabasePlatform and findIterate() or findVisit().
2013-11-07 23:58:15 +13:00
Rob Bygrave b896e55af1 Fix for Issue 56 - Using findIterate with MySQL streams the entire
result set at once
2013-11-07 23:07:39 +13:00
Rob Bygrave bd75f2cf77 Merge pull request #55 from rvowles/master
Add support for bang paths
2013-11-07 01:15:15 -08:00
Rob Bygrave 4f33fe087e Merge pull request #47 from romming/BUG420
Thanks!!
2013-11-07 00:08:54 -08:00
Rob Bygrave ccf94bf1a4 Merge pull request #51 from mollstam/limitbug
Invalid SQL when firstRow set but not maxRows
2013-11-06 23:16:08 -08:00
Richard Vowles 0acabf820e Add support for bang paths 2013-11-02 20:07:35 +13:00
Tobias 1330fe8fb6 Generate valid SQL when firstRow is set but not maxRows 2013-10-07 16:29:23 +02:00
Rob Bygrave 09ea5217dc [maven-release-plugin] prepare for next development iteration 2013-09-11 16:26:44 +12:00
Rob Bygrave 93304949e8 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.4 2013-09-11 16:26:36 +12:00
Rob Bygrave 1a292275d8 Json Timestamp parsing - Add support for long values 2013-09-11 14:20:36 +12:00
Christian Romming 236fb5541b Fixed whitespace in test. 2013-08-28 23:15:20 +02:00
Christian Romming 1cafea20c6 Test and fix for BUG 420. 2013-08-28 23:06:37 +02:00
Rob Bygrave f71eac5f1e [maven-release-plugin] prepare for next development iteration 2013-08-02 12:18:45 +12:00
Rob Bygrave 3f9c3c1db5 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.3 2013-08-02 12:18:36 +12:00
Rob Bygrave b4df899314 Move agent dependency to 3.2.2 (Fix for Issue 44 @Transactional
handling)
2013-08-02 12:16:41 +12:00
Rob Bygrave 9c7f62e8b1 Allow more flexibility with JSON marshalling of non-entity beans 2013-08-02 12:16:00 +12:00
Rob Bygrave bf8574833a Remove Thread UncaughtExceptionHandler as part of fix for pull/44
@Transactional not rolling back for uncaught RuntimeException - This
change is not required for 44 but is nice cleanup as
UncaughtExceptionHandler is no used.
2013-08-02 12:14:17 +12:00
Rob Bygrave 6f1bb5a9d7 Merge pull request #44 from hei1233212000/master
added a test case to show the issue that the user record does NOT rolback when there is an exception thrown
2013-08-01 16:45:15 -07:00
harry.chan 2f7b4e3a24 added a test case to show the issue that the user record does NOT
rolback when there is an exception thrown
2013-08-01 11:42:38 +08:00
Rob Bygrave 44d95dc06c Fix for Issue 43 - Support for JSON unmarshalling of @XmlRootElement
beans.
2013-07-29 23:24:30 +12:00
Robin Bygrave 6f579ba890 [maven-release-plugin] prepare for next development iteration 2013-07-12 21:10:49 +12:00
Tobias Wallura 39f5284a76 unit test for issue #37 2013-07-10 10:24:03 +02:00
Robin Bygrave 4ec62a5772 initial rework for new enhancement 2013-06-19 20:36:24 +12:00
Robin Bygrave eb1eafe98c WIP - Initial enhancement API change 2013-05-01 14:46:55 +12:00
701 changed files with 29623 additions and 20316 deletions
+8
View File
@@ -6,4 +6,12 @@
target/
logs/
log/
/db
/mydb.db
# Intellij project files
*.iml
*.ipr
*.iws
.idea/
+4 -6
View File
@@ -1,14 +1,12 @@
avaje-ebeanorm-server
=====================
The internal server implementation of EbeanORM API
[![Stories in Ready](https://badge.waffle.io/ebean-orm/avaje-ebeanorm.png?label=ready&title=Ready)](https://waffle.io/ebean-orm/avaje-ebeanorm)
avaje-ebeanorm
==============
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.2.1</version>
<version>4.1.4</version>
</dependency>
+64 -25
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.2.2</version>
<version>4.1.8</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -48,6 +48,12 @@
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
@@ -77,13 +83,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-agentloader</artifactId>
@@ -94,7 +93,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>3.2.1</version>
<version>4.1.6</version>
<scope>test</scope>
</dependency>
@@ -129,14 +128,28 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.15</version>
<version>5.1.27</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-integration</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
@@ -147,29 +160,34 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Enhance the meta beans -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>3.2.1</version>
<version>4.1.6</version>
<executions>
<execution>
<id>main</id>
<phase>process-classes</phase>
<configuration>
<classSource>target/classes</classSource>
<packages>com.avaje.ebean.meta.**</packages>
<transformArgs>debug=1</transformArgs>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
<!-- Not going to enhance Model bean -->
<execution>
<id>test</id>
<phase>process-test-classes</phase>
@@ -200,8 +218,29 @@
</configuration>
</plugin>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<doctitle>Ebean 4</doctitle>
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>site</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
</project>
+7 -11
View File
@@ -1,6 +1,7 @@
package com.avaje.ebean;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.Set;
/**
@@ -46,6 +47,11 @@ public interface BeanState {
*/
public Set<String> getChangedProps();
/**
* Return a map of the updated properties and their new and old values.
*/
public Map<String,ValuePair> getDirtyValues();
/**
* Return true if the bean is readOnly.
* <p>
@@ -69,16 +75,6 @@ public interface BeanState {
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Advanced - Used to programmatically build a reference object.
* <p>
* You can create a new EntityBean (
* {@link EbeanServer#createEntityBean(Class)}, set its Id property and then
* call this setReference() method.
* </p>
*/
public void setReference();
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
@@ -90,5 +86,5 @@ public interface BeanState {
* the properties that where loaded or null for a fully loaded entity
* bean.
*/
public void setLoaded(Set<String> loadedProperties);
public void setLoaded();
}
@@ -1,7 +1,6 @@
package com.avaje.ebean;
import java.util.ArrayList;
import java.util.Arrays;
import javax.persistence.PersistenceException;
@@ -57,6 +56,9 @@ final class DRawSqlColumnsParser {
split = tmp.toArray(new String[tmp.size()]);
}
if (split.length == 0) {
throw new PersistenceException("Huh? Not expecting length=0 when parsing column " + colInfo);
}
if (split.length == 1) {
// default to column the same name as the property
return new ColumnMapping.Column(indexPos++, split[0], null);
@@ -64,17 +66,18 @@ final class DRawSqlColumnsParser {
if (split.length == 2) {
return new ColumnMapping.Column(indexPos++, split[0], split[1]);
}
if (split.length == 3) {
if (!split[1].equalsIgnoreCase("as")) {
String msg = "Expecting AS keyword parsing column " + colInfo;
throw new PersistenceException(msg);
}
return new ColumnMapping.Column(indexPos++, split[0], split[2]);
// Ok, we now expect/require the AS keyword and it should be the
// second to last word in the colInfo content
if (!split[split.length - 2].equalsIgnoreCase("as")) {
throw new PersistenceException("Expecting AS keyword as second to last word when parsing column " + colInfo);
}
String msg = "Expecting Max 3 words parsing column " + colInfo + ". Got "
+ Arrays.toString(split);
throw new PersistenceException(msg);
// build back the 'column formula' that precedes the AS keyword
StringBuilder sb = new StringBuilder();
sb.append(split[0]);
for (int i = 1; i < split.length-2; i++) {
sb.append(" ").append(split[i]);
}
return new ColumnMapping.Column(indexPos++, sb.toString(), split[split.length - 1]);
}
private int nextComma() {
+86 -58
View File
@@ -5,20 +5,20 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This Ebean object is effectively a singleton that holds a map of registered
@@ -214,14 +214,13 @@ public final class Ebean {
* Register a server so we can get it by its name.
*/
private void register(EbeanServer server, boolean isPrimaryServer) {
registerWithName(server.getName(), server, isPrimaryServer);
}
private void registerWithName(String name, EbeanServer server, boolean isPrimaryServer) {
synchronized (monitor) {
concMap.put(server.getName(), server);
EbeanServer existingServer = syncMap.put(server.getName(), server);
if (existingServer != null) {
String msg = "Existing EbeanServer [" + server.getName() + "] is being replaced?";
logger.warn(msg);
}
concMap.put(name, server);
syncMap.put(name, server);
if (isPrimaryServer) {
primaryServer = server;
}
@@ -286,6 +285,15 @@ public final class Ebean {
serverMgr.register(server, isPrimaryServer);
}
/**
* Backdoor for registering a mock implementation of EbeanServer as the default server.
*/
protected static EbeanServer mock(String name, EbeanServer server, boolean isPrimaryServer) {
EbeanServer originalPrimaryServer = serverMgr.primaryServer;
serverMgr.registerWithName(name, server, isPrimaryServer);
return originalPrimaryServer;
}
/**
* Return the next identity value for a given bean type.
* <p>
@@ -454,69 +462,89 @@ public final class Ebean {
}
/**
* Force an update using the bean updating the non-null properties.
* Insert the bean. This is useful when you set the Id property on a bean and
* want to explicitly insert it.
*/
public static void insert(Object bean) {
serverMgr.getPrimaryServer().insert(bean);
}
/**
* Insert a collection of beans.
*/
public static void insert(Collection<?> beans) {
serverMgr.getPrimaryServer().insert(beans);
}
/**
* Marks the entity bean as dirty.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link Ebean#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* This is used so that when a bean that is otherwise unmodified is updated with the version
* property updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
*
* Customer customer = Ebean.find(Customer, id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* Ebean.markAsDirty(customer);
* Ebean.save(customer);
*
* </pre>
*/
public static void markAsDirty(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().markAsDirty(bean);
}
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
* <p>
* <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
* update().You can create a new instance and set some of its properties programmatically for via
* JSON/XML marshalling etc. This is described as a 'stateless update'.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
* that are in the database but are not in the collection are deleted.
* </p>
* <p>
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
* controls if only the changed properties are included in the update or if all the loaded
* properties are included instead.
* </p>
*
* <pre class="code">
*
* Customer c = new Customer();
* c.setId(7);
* c.setName(&quot;ModifiedNameNoOCC&quot;);
*
* // generally you should set the version property
* // so that Optimistic Concurrency Checking is used.
* // If a version property is not set then no Optimistic
* // Concurrency Checking occurs for the update
* // c.setLastUpdate(lastUpdateTime);
*
* // by default the Non-null properties
* // are included in the update
* Ebean.update(c);
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName(&quot;ModifiedNameNoOCC&quot;);
* ebeanServer.update(customer);
*
* </pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public static void update(Object bean) {
public static void update(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().update(bean);
}
/**
* Force an update using the bean explicitly stating the properties to update.
* <p>
* If you don't specify explicit properties to use in the update then the
* non-null properties are included in the update.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
*
* @param bean
* The bean holding the values to be included in the update.
* @param updateProps
* the explicit set of properties to include in the update (can be
* null).
* Update the beans in the collection.
*/
public static void update(Object bean, Set<String> updateProps) {
serverMgr.getPrimaryServer().update(bean, updateProps);
public static void update(Collection<?> beans) throws OptimisticLockException {
serverMgr.getPrimaryServer().update(beans);
}
/**
@@ -529,8 +557,8 @@ public final class Ebean {
/**
* Save all the beans from a Collection.
*/
public static int save(Collection<?> c) throws OptimisticLockException {
return save(c.iterator());
public static int save(Collection<?> beans) throws OptimisticLockException {
return serverMgr.getPrimaryServer().save(beans);
}
/**
+149 -175
View File
@@ -11,6 +11,7 @@ import javax.persistence.OptimisticLockException;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
@@ -84,9 +85,12 @@ import com.avaje.ebean.text.json.JsonContext;
public interface EbeanServer {
/**
* Shutdown the EbeanServer.
* Shutdown the EbeanServer programmatically.
* <p>
* If the under underlying DataSource is the EbeanORM implementation then you
* This method is not normally required. Ebean registers a shutdown hook and shuts down cleanly.
* </p>
* <p>
* If the under underlying DataSource is the Ebean implementation then you
* also have the option of shutting down the DataSource and deregistering the
* JDBC driver.
* </p>
@@ -117,6 +121,13 @@ public interface EbeanServer {
*/
public ExpressionFactory getExpressionFactory();
/**
* Return the MetaInfoManager which is used to get meta data from the EbeanServer
* such as query execution statistics.
*/
public MetaInfoManager getMetaInfoManager();
/**
* Return the BeanState for a given entity bean.
* <p>
@@ -402,12 +413,25 @@ public interface EbeanServer {
public <T> T find(Class<T> beanType, Object uid);
/**
* Get a reference Object (see {@link Ebean#getReference(Class, Object)}.
* Get a reference bean (see {@link Ebean#getReference(Class, Object)}.
* <p>
* This will not perform a query against the database.
* </p>
* <pre class="code">
* Product product = Ebean.getReference(Product.class, 1);
*
* @see Ebean#getReference(Class, Object)
* // You can get the id without causing a fetch/lazy load
* Integer productId = product.getId();
*
* // If you try to get any other property a fetch/lazy loading will occur
* // This will cause a query to execute...
* String name = product.getName();
* </pre>
*
* @param beanType
* the type of entity bean
* @param id
* the id value
*/
public <T> T getReference(Class<T> beanType, Object uid);
@@ -420,21 +444,21 @@ public interface EbeanServer {
/**
* Return the Id values of the query as a List.
*/
public <T> List<Object> findIds(Query<T> query, Transaction t);
public <T> List<Object> findIds(Query<T> query, Transaction transaction);
/**
* Return a QueryIterator for the query. This is similar to findVisit in that
* not all the result beans need to be held in memory at the same time and as
* such is go for processing large queries.
*/
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t);
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
/**
* Execute the query visiting the results. This is similar to findIterate in
* that not all the result beans need to be held in memory at the same time
* and as such is go for processing large queries.
*/
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t);
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction transaction);
/**
* Execute a query returning a list of beans.
@@ -469,7 +493,7 @@ public interface EbeanServer {
* the transaction (can be null).
* @return a Future object for the row count query
*/
public <T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction t);
public <T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction transaction);
/**
* Execute find Id's query in a background thread.
@@ -485,7 +509,7 @@ public interface EbeanServer {
* the transaction (can be null).
* @return a Future object for the list of Id's
*/
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t);
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
/**
* Execute find list query in a background thread.
@@ -500,8 +524,9 @@ public interface EbeanServer {
* @param t
* the transaction (can be null).
* @return a Future object for the list result of the query
* @deprecated
*/
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t);
public <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
/**
* Execute find list SQL query in a background thread.
@@ -516,13 +541,37 @@ public interface EbeanServer {
* @param t
* the transaction (can be null).
* @return a Future object for the list result of the query
* @deprecated
*/
public SqlFutureList findFutureList(SqlQuery query, Transaction t);
public SqlFutureList findFutureList(SqlQuery query, Transaction transaction);
/**
* Find using a PagingList with explicit transaction and pageSize.
* Deprecated in favour of findPagedList().
* @deprecated
*/
public <T> PagingList<T> findPagingList(Query<T> query, Transaction transaction, int pageSize);
/**
* Return a PagedList for this query.
* <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
*/
public <T> PagingList<T> findPagingList(Query<T> query, Transaction t, int pageSize);
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize);
/**
* Execute the query returning a set of entity beans.
@@ -669,7 +718,7 @@ public interface EbeanServer {
/**
* Save all the beans in the collection.
*/
public int save(Collection<?> it) throws OptimisticLockException;
public int save(Collection<?> beans) throws OptimisticLockException;
/**
* Delete the bean.
@@ -696,7 +745,7 @@ public interface EbeanServer {
/**
* Delete the bean given its type and id with an explicit transaction.
*/
public int delete(Class<?> beanType, Object id, Transaction t);
public int delete(Class<?> beanType, Object id, Transaction transaction);
/**
* Delete several beans given their type and id values.
@@ -707,7 +756,7 @@ public interface EbeanServer {
* Delete several beans given their type and id values with an explicit
* transaction.
*/
public void delete(Class<?> beanType, Collection<?> ids, Transaction t);
public void delete(Class<?> beanType, Collection<?> ids, Transaction transaction);
/**
* Execute a SQL Update Delete or Insert statement using the current
@@ -776,205 +825,129 @@ public interface EbeanServer {
/**
* Insert or update a bean with an explicit transaction.
*/
public void save(Object bean, Transaction t) throws OptimisticLockException;
public void save(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Save all the beans in the iterator with an explicit transaction.
*/
public int save(Iterator<?> it, Transaction t) throws OptimisticLockException;
public int save(Iterator<?> it, Transaction transaction) throws OptimisticLockException;
/**
* Force an update using the bean.
* Save all the beans in the collection with an explicit transaction.
*/
public int save(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Marks the entity bean as dirty.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* This is used so that when a bean that is otherwise unmodified is updated the version
* property is updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
*
* Customer customer = ebeanServer.find(Customer, id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* ebeanServer.markAsDirty(customer);
* ebeanServer.save(customer);
*
* </pre>
*/
public void markAsDirty(Object bean);
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
* <p>
* <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
* update().You can create a new instance and set some of its properties programmatically for via
* JSON/XML marshalling etc. This is described as a 'stateless update'.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
* that are in the database but are not in the collection are deleted.
* </p>
* <p>
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
* controls if only the changed properties are included in the update or if all the loaded
* properties are included instead.
* </p>
*
* <pre class="code">
*
* Customer c = new Customer();
* c.setId(7);
* c.setName(&quot;ModifiedNameNoOCC&quot;);
*
* // generally you should set the version property
* // so that Optimistic Concurrency Checking is used.
* // If a version property is not set then no Optimistic
* // Concurrency Checking occurs for the update
* // c.setLastUpdate(lastUpdateTime);
*
* // by default the Non-null properties
* // are included in the update
* ebeanServer.update(c);
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName(&quot;ModifiedNameNoOCC&quot;);
* ebeanServer.update(customer);
*
* </pre>
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public void update(Object bean);
public void update(Object bean) throws OptimisticLockException;
/**
* Force an update of the non-null properties of the bean with an explicit
* transaction.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
* Update a bean additionally specifying a transaction.
*/
public void update(Object bean, Transaction t);
public void update(Object bean, Transaction t) throws OptimisticLockException;
/**
* Force an update using the bean explicitly stating the properties to update.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
*
* <pre class="code">
*
* Customer c = new Customer();
* c.setId(7);
* c.setName(&quot;ModifiedNameNoOCC&quot;);
*
* // generally you should set the version property
* // so that Optimistic Concurrency Checking is used.
* // If a version property is not set then no Optimistic
* // Concurrency Checking occurs for the update
* // c.setLastUpdate(lastUpdateTime);
*
* // by default the Non-null properties
* // are included in the update
* ebeanServer.update(c);
*
* </pre>
*/
public void update(Object bean, Set<String> updateProps);
/**
* Force an update of the specified properties of the bean with an explicit
* transaction.
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* <p>
* For updates against beans that have not been fetched (say built from JSON
* or XML) this will treat deleteMissingChildren=true and will delete any
* 'missing children'. Refer to
* {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
* </p>
*/
public void update(Object bean, Set<String> updateProps, Transaction t);
/**
* Force an update additionally specifying whether to 'deleteMissingChildren'
* when the update cascades to a OneToMany or ManyToMany.
* <p>
* By default the deleteMissingChildren is true and it is assumed that when
* cascade saving a O2M or M2M relationship that the relationship is 'fully
* loaded' and any child beans that are no longer on the relationship will be
* deleted.
* </p>
* <p>
* You can use this method to FORCE an update to occur (even on a bean that
* has not been fetched but say built from JSON or XML). When
* {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
* insert or an update based on the state of the bean. Using this method will
* force an update to occur.
* </p>
* <p>
* It is expected that this method is most useful in stateless REST services
* or web applications where you have the values you wish to update but no
* existing bean.
* </p>
* Update a bean additionally specifying a transaction and the deleteMissingChildren setting.
*
* @param bean
* the bean to update
* @param updateProps
* optionally you can specify the properties to update (can be null).
* @param t
* optionally you can specify the transaction to use (can be null).
* @param transaction
* the transaction to use (can be null).
* @param deleteMissingChildren
* specify false if you do not want 'missing children' of a OneToMany
* or ManyToMany to be automatically deleted.
* @param updateNullProperties
* specify true if by default you want properties with null values to
* be included in the update and false if those properties should be
* treated as 'unloaded' and excluded from the update. This only
* takes effect if the updateProps is null.
*/
public void update(Object bean, Set<String> updateProps, Transaction t,
boolean deleteMissingChildren, boolean updateNullProperties);
public void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException;
/**
* Force the bean to be saved with an explicit insert.
* <p>
* Typically you would use save() and let Ebean determine if the bean should
* be inserted or updated. This can be useful when you are transferring data
* between databases and want to explicitly insert a bean into a different
* database that it came from.
* </p>
* Update a collection of beans. If there is no current transaction one is created and used to
* update all the beans in the collection.
*/
public void update(Collection<?> beans) throws OptimisticLockException;
/**
* Update a collection of beans with an explicit transaction.
*/
public void update(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Insert the bean.
*/
public void insert(Object bean);
/**
* Force the bean to be saved with an explicit insert.
* <p>
* Typically you would use save() and let Ebean determine if the bean should
* be inserted or updated. This can be useful when you are transferring data
* between databases and want to explicitly insert a bean into a different
* database that it came from.
* </p>
* Insert the bean with a transaction.
*/
public void insert(Object bean, Transaction t);
/**
* Insert a collection of beans. If there is no current transaction one is created and used to
* insert all the beans in the collection.
*/
public void insert(Collection<?> beans);
/**
* Insert a collection of beans with an explicit transaction.
*/
public void insert(Collection<?> beans, Transaction t);
/**
* Delete the associations (from the intersection table) of a ManyToMany given
* the owner bean and the propertyName of the ManyToMany collection.
@@ -1150,4 +1123,5 @@ public interface EbeanServer {
*/
public JsonContext createJsonContext();
}
@@ -6,8 +6,6 @@ import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates EbeanServer instances.
@@ -26,14 +24,9 @@ import org.slf4j.LoggerFactory;
* methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
* convenient way of using the 'default/primary' EbeanServer.
* </p>
*
* @author Rob Bygrave
*
*/
public class EbeanServerFactory {
private static final Logger logger = LoggerFactory.getLogger(EbeanServerFactory.class);
private static BootupEbeanManager serverFactory = createServerFactory();
/**
@@ -69,27 +62,10 @@ public class EbeanServerFactory {
private static BootupEbeanManager createServerFactory() {
// String d___ =
// com.avaje.ebean.server.core.DefaultServerFactory.class.getName();
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
String implClassName = GlobalProperties.get("ebean.serverfactory", dflt);
String implClassName = System.getProperty("ebean.serverfactory", dflt);
int delaySecs = GlobalProperties.getInt("ebean.start.delay", 0);
if (delaySecs > 0) {
try {
// perhaps useful to delay the startup to give time to
// attach a debugger when running in a server like tomcat.
String m = "Ebean sleeping " + delaySecs + " seconds due to ebean.start.delay";
logger.info(m);
Thread.sleep(delaySecs * 1000);
} catch (InterruptedException e) {
String m = "Interrupting debug.start.delay of " + delaySecs;
logger.error(m, e);
}
}
try {
// use a client side implementation?
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
} catch (Exception ex) {
throw new RuntimeException(ex);
+8
View File
@@ -73,6 +73,14 @@ public class Expr {
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
}
/**
* Between - value between two given properties.
*/
public static Expression between(String lowProperty, String highProperty, Object value) {
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
}
/**
* Greater Than - property greater than the given value.
*/
@@ -36,11 +36,6 @@ import java.util.Map;
*/
public interface ExpressionFactory {
/**
* Return the language for this expression factory.
*/
public String getLang();
/**
* Equal To - property equal to the given value.
*/
@@ -200,10 +200,34 @@ public interface ExpressionList<T> extends Serializable {
*
* @param pageSize
* the number of beans fetched per Page
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <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
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Add some filter predicate expressions to the many property.
*/
public ExpressionList<T> filterMany(String prop);
/**
@@ -244,14 +268,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setMaxRows(int maxRows);
/**
* Set the number of rows after which the fetching should continue in a
* background thread.
*
* @see Query#setBackgroundFetchAfter(int)
*/
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the name of the property which values become the key of a map.
*
@@ -259,13 +275,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setMapKey(String mapKey);
/**
* Set a QueryListener for bean by bean processing.
*
* @see Query#setListener(QueryListener)
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set to true to use the query for executing this query.
*
+20 -15
View File
@@ -24,7 +24,7 @@ import java.io.Serializable;
*
* <pre class="code">
* // Normal fetch join results in a single SQL query
* List&lt;Order&gt; list = Ebean.find(Order.class).join(&quot;details&quot;).findList();
* List&lt;Order&gt; list = Ebean.find(Order.class).fetch(&quot;details&quot;).findList();
*
* // Find Orders join details using a single SQL query
* </pre>
@@ -51,8 +51,8 @@ import java.io.Serializable;
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new JoinConfig().query())
* .fetch(&quot;customer&quot;, new JoinConfig().query(5))
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .fetch(&quot;customer&quot;, new FetchConfig().queryFirst(5))
* .findList();
*
* // query 1) find order
@@ -70,7 +70,7 @@ import java.io.Serializable;
* .select(&quot;status, shipDate&quot;)
* .fetch(&quot;details&quot;, &quot;quantity, price&quot;, new FetchConfig().query())
* .fetch(&quot;details.product&quot;, &quot;sku, name&quot;)
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().query(10))
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().queryFirst(5))
* .fetch(&quot;customer.contacts&quot;)
* .fetch(&quot;customer.shippingAddress&quot;)
* .findList();
@@ -96,13 +96,13 @@ import java.io.Serializable;
* <pre class="code">
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;customer&quot;, new FetchConfig().query(3).lazy(10))
* .fetch(&quot;customer&quot;, new FetchConfig().query(10).lazy(5))
* .findList();
*
* // query 1) find order
* // query 2) find customer where id in (?,?,?) // first 3 customers
* // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
* // .. then if lazy loading of customers is invoked
* // .. use a batch size of 10 to load the customers
* // .. use a batch size of 5 to load the customers
*
* </pre>
*
@@ -128,8 +128,8 @@ import java.io.Serializable;
* // .. use a batch size of 5 to load the customers
*
* find customer (name)
* fetch contact (contactName, phone, email)
* fetch shippingAddress (*)
* fetch customer.contacts (contactName, phone, email)
* fetch customer.shippingAddress (*)
* where id in (?,?,?,?,?)
*
* </pre>
@@ -159,6 +159,7 @@ public class FetchConfig implements Serializable {
*/
public FetchConfig lazy() {
this.lazyBatchSize = 0;
this.queryAll = false;
return this;
}
@@ -170,11 +171,12 @@ public class FetchConfig implements Serializable {
*/
public FetchConfig lazy(int lazyBatchSize) {
this.lazyBatchSize = lazyBatchSize;
this.queryAll = false;
return this;
}
/**
* Specify that this path should be loaded as a separate query (rather than as
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
* This will use the default batch size for separate query which is 100.
@@ -187,14 +189,15 @@ public class FetchConfig implements Serializable {
}
/**
* Specify that this path should be loaded as a separate query (rather than as
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
* </p>
* <p>
* This will load all beans on this path eagerly.
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
* is also used.
* </p>
*
* @param queryBatchSize
@@ -202,12 +205,14 @@ public class FetchConfig implements Serializable {
*/
public FetchConfig query(int queryBatchSize) {
this.queryBatchSize = queryBatchSize;
this.queryAll = true;
// queryAll true as long as a lazy batch size has not already been set
this.queryAll = (lazyBatchSize == -1);
return this;
}
/**
* Similar to {@link #query(int)} but only fetches the first batch.
* Eagerly fetch the first batch of beans on this path.
* This is similar to {@link #query(int)} but only fetches the first batch.
* <p>
* If there are more parent beans than the batch size then they will not be
* loaded eagerly but instead use lazy loading.
@@ -228,7 +233,7 @@ public class FetchConfig implements Serializable {
public int getLazyBatchSize() {
return lazyBatchSize;
}
/**
* Return the batch size for separate query load.
*/
+594
View File
@@ -0,0 +1,594 @@
package com.avaje.ebean;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.persistence.MappedSuperclass;
/**
* A MappedSuperclass base class that provides convenience methods for inserting, updating and
* deleting beans.
*
* <p>
* By having your entity beans extend this it provides a 'Active Record' style programming model for
* Ebean users.
*
* <p>
* Note that there is a avaje-ebeanorm-mocker project that enables you to use Mockito or similar
* tools to still mock out the underlying 'default EbeanServer' for testing purposes.
*
* <p>
* You may choose not use this Model mapped superclass if you don't like the 'Active Record' style
* or if you believe it 'pollutes' your entity beans.
*
* <p>
* If you choose to use the Model mapped superclass you will probably also chose to additionally add
* a {@link Finder} as a public static field to complete the active record pattern and provide a
* relatively nice clean way to write queries.
*/
@MappedSuperclass
public abstract class Model {
/**
* Return the underlying 'default' EbeanServer.
*
* <p>
* This provides full access to the API such as explicit transaction demarcation etc.
*
* <p>
* Example:
* <pre class="code">
* Transaction transaction = Customer.db().beginTransaction();
* try {
*
* // turn off cascade persist for this transaction
* transaction.setPersistCascade(false);
*
* // extra control over jdbc batching for this transaction
* transaction.setBatchGetGeneratedKeys(false);
* transaction.setBatchMode(true);
* transaction.setBatchSize(20);
*
* Customer customer = new Customer();
* customer.setName(&quot;Roberto&quot;);
* customer.save();
*
* Customer otherCustomer = new Customer();
* otherCustomer.setName(&quot;Franko&quot;);
* otherCustomer.save();
*
* transaction.commit();
*
* } finally {
* transaction.end();
* }
*
* </pre>
*/
public static EbeanServer db() {
return Ebean.getServer(null);
}
/**
* Return a named EbeanServer that is typically different to the default server.
*
* <p>
* If you are using multiple databases then each database has a name and maps to a single
* EbeanServer. You can use this method to get an EbeanServer for another database.
*
* @param server
* The name of the EbeanServer. If this is null then the default EbeanServer is returned.
*/
public static EbeanServer db(String server) {
return Ebean.getServer(server);
}
/**
* Marks the entity bean as dirty.
* <p>
* This is used so that when a bean that is otherwise unmodified is updated the version
* property is updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
*
* Customer customer = Customer.find.byId(id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* customer.markAsDirty();
* customer.save();
*
* </pre>
*/
public void markAsDirty() {
db().markAsDirty(this);
}
/**
* Insert or update this entity depending on its state.
*
* <p>
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
* insert or an update based on that.
*/
public void save() {
db().save(this);
}
/**
* Update this entity.
*/
public void update() {
db().update(this);
}
/**
* Insert this entity.
*/
public void insert() {
db().insert(this);
}
/**
* Delete this entity.
*/
public void delete() {
db().delete(this);
}
/**
* Perform an update using this entity against the specified server.
*/
public void update(String server) {
db(server).update(this);
}
/**
* Perform an insert using this entity against the specified server.
*/
public void insert(String server) {
db(server).insert(this);
}
/**
* Perform a delete using this entity against the specified server.
*/
public void delete(String server) {
db(server).delete(this);
}
/**
* Refreshes this entity from the database.
*/
public void refresh() {
db().refresh(this);
}
/**
* Helper object for performing queries.
*
* <p>
* Typically a Finder is defined as a public static field on an entity bean class to provide a
* nice way to write queries.
*
* @param I
* The Id type. This is most often a {@link Long} but is also often a {@link UUID} or
* {@link String}.
*
* @param T
* The bean type
*/
public static class Finder<I, T> {
private final Class<I> idType;
private final Class<T> type;
private final String serverName;
/**
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>.
*
* <p>
* Typically you use this constructor to have a static "find" field on each entity bean.
*/
public Finder(Class<I> idType, Class<T> type) {
this(null, idType, type);
}
/**
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>, using a
* specific EbeanServer.
*
* <p>
* Typically you don't need to use this method.
*/
public Finder(String serverName, Class<I> idType, Class<T> type) {
this.type = type;
this.idType = idType;
this.serverName = serverName;
}
/**
* Return the underlying 'default' EbeanServer.
*
* <p>
* This provides full access to the API such as explicit transaction demarcation etc.
*
*/
public EbeanServer db() {
return Ebean.getServer(serverName);
}
/**
* Return typically a different EbeanServer to the default.
* <p>
* This is equivilent to {@link Ebean#getServer(String)}
*
* @param server
* The name of the EbeanServer. If this is null then the default EbeanServer is
* returned.
*/
public EbeanServer db(String server) {
return Ebean.getServer(server);
}
/**
* Creates a Finder for the named EbeanServer.
*
* <p>
* Create and return a new Finder for a different server.
*/
public Finder<I, T> on(String server) {
return new Finder<I, T>(server, idType, type);
}
// Does not exist yet but I think should
// public void deleteById(I id) {
// return db().deleteById(type, id);
// }
/**
* Retrieves all entities of the given type.
*
* <p>
* This is the same as (synonym for) {@link #findList()}
*/
public List<T> all() {
return findList();
}
/**
* Retrieves an entity by ID.
*
* <p>
* Equivilent to {@link EbeanServer#find(Class, Object)}
*/
public T byId(I id) {
return db().find(type, id);
}
/**
* Creates an entity reference for this ID.
*
* <p>
* Equivilent to {@link EbeanServer#getReference(Class, Object)}
*/
public T ref(I id) {
return db().getReference(type, id);
}
/**
* Creates a filter for sorting and filtering lists of entities locally without going back to
* the database.
* <p>
* Equivilent to {@link EbeanServer#filter(Class)}
*/
public Filter<T> filter() {
return db().filter(type);
}
/**
* Creates a query.
* <p>
* Equivilent to {@link EbeanServer#find(Class)}
*/
public Query<T> query() {
return db().find(type);
}
/**
* Returns the next identity value.
*
* @see EbeanServer#nextId(Class)
*/
@SuppressWarnings("unchecked")
public I nextId() {
return (I) db().nextId(type);
}
/**
* Executes a query and returns the results as a list of IDs.
* <p>
* Equivilent to {@link Query#findIds()}
*/
public List<Object> findIds() {
return query().findIds();
}
/**
* Retrieves all entities of the given type.
* <p>
* The same as {@link #all()}
* <p>
* Equivilent to {@link Query#findList()}
*/
public List<T> findList() {
return query().findList();
}
/**
* Returns all the entities of the given type as a set.
* <p>
* Equivilent to {@link Query#findSet()}
*/
public Set<T> findSet() {
return query().findSet();
}
/**
* Retrieves all entities of the given type as a map of objects.
* <p>
* Equivilent to {@link Query#findMap()}
*/
public Map<?, T> findMap() {
return query().findMap();
}
/**
* Executes the query and returns the results as a map of the objects specifying the map key
* property.
* <p>
* Equivilent to {@link Query#findMap(String, Class)}
*/
public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) {
return query().findMap(keyProperty, keyType);
}
/**
* Return a PagedList of all entities of the given type (use where() to specify predicates as
* needed).
* <p>
* Equivilent 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>
* Equivilent to {@link Query#findFutureRowCount()}
*/
public FutureRowCount<T> findFutureRowCount() {
return query().findFutureRowCount();
}
/**
* Returns the total number of entities for this type. *
* <p>
* Equivilent to {@link Query#findRowCount()}
*/
public int findRowCount() {
return query().findRowCount();
}
/**
* Returns the <code>ExpressionFactory</code> used by this query.
*/
public ExpressionFactory getExpressionFactory() {
return query().getExpressionFactory();
}
/**
* Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean,
* to load a partial object.
* <p>
* Equivilent to {@link Query#select(String)}
*/
public Query<T> select(String fetchProperties) {
return query().select(fetchProperties);
}
/**
* Specifies a path to load including all its properties.
* <p>
* Equivilent to {@link Query#fetch(String)}
*/
public Query<T> fetch(String path) {
return query().fetch(path);
}
/**
* Additionally specifies a <code>FetchConfig</code> to specify a 'query join' and/or define the
* lazy loading query.
* <p>
* Equivilent to {@link Query#fetch(String, FetchConfig)}
*/
public Query<T> fetch(String path, FetchConfig joinConfig) {
return query().fetch(path, joinConfig);
}
/**
* Specifies a path to fetch with a specific list properties to include, to load a partial
* object.
* <p>
* Equivilent to {@link Query#fetch(String, String)}
*/
public Query<T> fetch(String path, String fetchProperties) {
return query().fetch(path, fetchProperties);
}
/**
* Additionally specifies a <code>FetchConfig</code> to use a separate query or lazy loading to
* load this path.
* <p>
* Equivilent to {@link Query#fetch(String, String, FetchConfig)}
*/
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) {
return query().fetch(assocProperty, fetchProperties, fetchConfig);
}
/**
* Adds expressions to the <code>where</code> clause with the ability to chain on the
* <code>ExpressionList</code>.
* <p>
* Equivilent to {@link Query#where()}
*/
public ExpressionList<T> where() {
return query().where();
}
/**
* Returns the <code>order by</code> clause so that you can append an ascending or descending
* property to the <code>order by</code> clause.
* <p>
* This is exactly the same as {@link #orderBy}.
* <p>
* Equivilent to {@link Query#order()}
*/
public OrderBy<T> order() {
return query().order();
}
/**
* Sets the <code>order by</code> clause, replacing the existing <code>order by</code> clause if
* there is one.
* <p>
* This is exactly the same as {@link #orderBy(String)}.
*/
public Query<T> order(String orderByClause) {
return query().order(orderByClause);
}
/**
* Returns the <code>order by</code> clause so that you can append an ascending or descending
* property to the <code>order by</code> clause.
* <p>
* This is exactly the same as {@link #order}.
* <p>
* Equivilent to {@link Query#orderBy()}
*/
public OrderBy<T> orderBy() {
return query().orderBy();
}
/**
* Set the <code>order by</code> clause replacing the existing <code>order by</code> clause if
* there is one.
* <p>
* This is exactly the same as {@link #order(String)}.
*/
public Query<T> orderBy(String orderByClause) {
return query().orderBy(orderByClause);
}
/**
* Sets the first row to return for this query.
* <p>
* Equivilent to {@link Query#setFirstRow(int)}
*/
public Query<T> setFirstRow(int firstRow) {
return query().setFirstRow(firstRow);
}
/**
* Sets the maximum number of rows to return in the query.
* <p>
* Equivilent to {@link Query#setMaxRows(int)}
*/
public Query<T> setMaxRows(int maxRows) {
return query().setMaxRows(maxRows);
}
/**
* Sets the ID value to query.
*
* <p>
* Use this to perform a find byId query but with additional control over the query such as
* using select and fetch to control what parts of the object graph are returned.
* <p>
* Equivilent to {@link Query#setId(Object)}
*/
public Query<T> setId(Object id) {
return query().setId(id);
}
/**
* Create and return a new query using the OQL.
* <p>
* Equivilent 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>
* Equivilent to {@link Query#setRawSql(RawSql)}
*/
public Query<T> setRawSql(RawSql rawSql) {
return query().setRawSql(rawSql);
}
/**
* Create a query with explicit 'Autofetch' use.
*/
public Query<T> setAutofetch(boolean autofetch) {
return query().setAutofetch(autofetch);
}
/**
* Create a query with the select with "for update" specified.
*
* <p>
* This will typically create row level database locks on the selected rows.
*/
public Query<T> setForUpdate(boolean forUpdate) {
return query().setForUpdate(forUpdate);
}
/**
* Create a query specifying whether the returned beans will be read-only.
*/
public Query<T> setReadOnly(boolean readOnly) {
return query().setReadOnly(readOnly);
}
/**
* Create a query specifying if the beans should be loaded into the L2 cache.
*/
public Query<T> setLoadBeanCache(boolean loadBeanCache) {
return query().setLoadBeanCache(loadBeanCache);
}
/**
* Create a query specifying if the L2 bean cache should be used.
*/
public Query<T> setUseCache(boolean useBeanCache) {
return query().setUseCache(useBeanCache);
}
/**
* Create a query specifying if the L2 query cache should be used.
*/
public Query<T> setUseQueryCache(boolean useQueryCache) {
return query().setUseQueryCache(useQueryCache);
}
}
}
+47 -22
View File
@@ -15,8 +15,6 @@ import java.util.List;
* Typically you will not construct an OrderBy yourself but use one that exists
* on the Query object.
* </p>
*
* @author rbygrave
*/
public final class OrderBy<T> implements Serializable {
@@ -24,7 +22,7 @@ public final class OrderBy<T> implements Serializable {
private transient Query<T> query;
private List<Property> list;
private final List<Property> list;
/**
* Create an empty OrderBy with no associated query.
@@ -32,6 +30,10 @@ public final class OrderBy<T> implements Serializable {
public OrderBy() {
this.list = new ArrayList<Property>(2);
}
private OrderBy(List<Property> list) {
this.list = list;
}
/**
* Create an orderBy parsing the order by clause.
@@ -81,6 +83,17 @@ public final class OrderBy<T> implements Serializable {
return query;
}
/**
* Return a copy of this OrderBy with the path trimmed.
*/
public OrderBy<T> copyWithTrim(String path) {
List<Property> newList = new ArrayList<Property>(list.size());
for (int i = 0; i < list.size(); i++) {
newList.add(list.get(i).copyWithTrim(path));
}
return new OrderBy<T>(newList);
}
/**
* Return the properties for this OrderBy.
*/
@@ -153,31 +166,23 @@ public final class OrderBy<T> implements Serializable {
@Override
public boolean equals(Object obj) {
if (obj instanceof OrderBy<?>) {
if (obj == this) {
return true;
}
OrderBy<?> other = (OrderBy<?>) obj;
return hashCode() == other.hashCode();
if (obj == this) {
return true;
}
return false;
}
@Override
public int hashCode() {
return hash();
if (!(obj instanceof OrderBy<?>)) {
return false;
}
OrderBy<?> e = (OrderBy<?>) obj;
return e.list.equals(list);
}
/**
* Return a hash value for this OrderBy. This can be to determine logical
* equality for OrderBy clauses.
*/
public int hash() {
int hc = OrderBy.class.getName().hashCode();
for (int i = 0; i < list.size(); i++) {
hc = hc * 31 + list.get(i).hash();
}
return hc;
public int hashCode() {
return list.hashCode();
}
/**
@@ -196,11 +201,31 @@ public final class OrderBy<T> implements Serializable {
this.ascending = ascending;
}
protected int hash() {
/**
* Return a copy of this Property with the path trimmed.
*/
public Property copyWithTrim(String path) {
return new Property(property.substring(path.length() + 1), ascending);
}
public int hashCode() {
int hc = property.hashCode();
hc = hc * 31 + (ascending ? 0 : 1);
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Property)) {
return false;
}
Property e = (Property) obj;
return e.ascending == ascending
&& e.property.equals(property);
}
public String toString() {
return toStringFormat();
@@ -0,0 +1,80 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* Represents a page of results.
* <p>
* The benefit of using PagedList over just using the normal Query with
* {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} is that it additionally wraps
* functionality that can call {@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 <T>
* the entity bean type
*
* @see Query#findPagedList(int, int)
*/
public interface PagedList<T> {
/**
* Initiate the loading of the total row count in the background.
*/
public void loadRowCount();
/**
* Return the Future row count. You might get this if you wish to cancel the total row count query
* or specify a timeout for that query.
*/
public Future<Integer> getFutureRowCount();
/**
* Return the list of entities for this page.
*/
public List<T> getList();
/**
* Return the total row count for all pages.
*/
public int getTotalRowCount();
/**
* Return the total number of pages based on the page size and total row count.
*/
public int getTotalPageCount();
/**
* Return the index position of this page. Zero based.
*/
public int getPageIndex();
/**
* Return true if there is a next page.
*/
public boolean hasNext();
/**
* Return true if there is a previous page.
*/
public boolean hasPrev();
/**
* Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
* last row and Z the total row count.
*
* @param to
* String to put between the first and last row
* @param of
* String to put between the last row and the total row count
*
* @return String of the format XtoYofZ.
*/
public String getDisplayXtoYofZ(String to, String of);
}
+28 -75
View File
@@ -269,26 +269,6 @@ import java.util.Set;
*/
public interface Query<T> extends Serializable {
/**
* How this query should use (or not) the Lucene Index if one is defined for
* the bean type.
*/
public enum UseIndex {
NO, DEFAULT, YES_IDS, YES_OBJECTS
}
/**
* Explicitly specify how this query should use a Lucene Index if one is
* defined for this bean type.
*/
public Query<T> setUseIndex(UseIndex useIndex);
/**
* Return the setting for how this query should use a Lucene Index if one is
* defined for this bean type.
*/
public UseIndex getUseIndex();
/**
* Return the RawSql that was set to use for this query.
*/
@@ -609,34 +589,42 @@ public interface Query<T> extends Serializable {
/**
* Execute find list query in a background thread.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a
* timeout).
* Deprecated with a view to simplifying internals.
* </p>
*
* @return a Future object for the list result of the query
* @deprecated
*/
public FutureList<T> findFutureList();
/**
* Return a PagingList for this query.
* <p>
* This can be used to break up a query into multiple queries to fetch the
* data a page at a time.
* </p>
* <p>
* This typically works by using a query per page and setting
* {@link Query#setFirstRow(int)} and and {@link Query#setMaxRows(int)} on the
* query. This usually would translate into SQL that uses limit offset, rownum
* or row_number function to limit the result set.
* </p>
*
* @param pageSize
* the number of beans fetched per Page
* This is being deprecated in favour of the simplier {@link Query#findPagedList(int, int)}.
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <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
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Set a named bind parameter. Named parameters have a colon to prefix the
* name.
@@ -683,32 +671,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setParameter(int position, Object value);
/**
* Set a listener to process the query on a row by row basis.
* <p>
* Use this when you want to process a large query and do not want to hold the
* entire query result in memory.
* </p>
* <p>
* It this case the rows are not loaded into the persistence context and
* instead are processed by the query listener.
* </p>
*
* <pre class="code">
* QueryListener&lt;Order&gt; listener = ...;
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
*
* // set the listener that will process each order one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list (emptyList) will be empty ...
* List&lt;Order&gt; emtyList = query.findList();
* </pre>
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set the Id value to query. This is used with findUnique().
* <p>
@@ -982,13 +944,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setMaxRows(int maxRows);
/**
* Set the rows after which fetching should continue in a background thread.
*
* @param backgroundFetchAfter
*/
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the property to use as keys for a map.
* <p>
@@ -1068,16 +1023,14 @@ public interface Query<T> extends Serializable {
*/
public String getGeneratedSql();
/**
* Return the total hits matched for a lucene text search query.
*/
public int getTotalHits();
/**
* executed the select with "for update" which should lock the record
* "on read"
*/
public Query<T> setForUpdate(boolean forUpdate);
/**
* Return true if this query has forUpdate set.
*/
public boolean isForUpdate();
}
@@ -1,38 +0,0 @@
package com.avaje.ebean;
/**
* Provides a mechanism for processing a query one bean at a time.
* <p>
* This is useful when the query will return a large number of results and you
* want to process the beans one at a time rather than whole all of the beans in
* memory at once.
* </p>
*
* <pre class="code">
* QueryListener&lt;Order&gt; listener = ...;
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
*
* // set the listener that will process each order one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list will be empty ... so don't bother assigning it
* query.findList();
* </pre>
*
* @param <T>
* the type of entity bean
*/
public interface QueryListener<T> {
/**
* Process the bean that has just been read.
* <p>
* This bean will not be added to the List Set or Map and nor will it be put
* into the PersistenceContext. This is what makes this a good way to process
* a large result set (which could normally use a lot of memory).
* </p>
*/
public void process(T bean);
}
+51 -3
View File
@@ -1,6 +1,7 @@
package com.avaje.ebean;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -135,18 +136,33 @@ import com.avaje.ebean.util.CamelCaseHelper;
* Note that lazy loading also works with object graphs built with RawSql.
* </p>
*
* @author rbygrave
*
*/
public final class RawSql implements Serializable {
private static final long serialVersionUID = 1L;
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
protected RawSql(Sql sql, ColumnMapping columnMapping) {
/**
* Construct with a ResultSet and properties that the columns map to.
* <p>
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
* <p>
* When a query executes this RawSql object then it will close the resultSet.
*/
public RawSql(ResultSet resultSet, String... propertyNames) {
this.resultSet = resultSet;
this.sql = null;
this.columnMapping = new ColumnMapping(propertyNames);
}
protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) {
this.resultSet = resultSet;
this.sql = sql;
this.columnMapping = columnMapping;
}
@@ -158,6 +174,14 @@ public final class RawSql implements Serializable {
return sql;
}
/**
* Return the resultSet if this is a ResultSet based RawSql.
*/
public ResultSet getResultSet() {
return resultSet;
}
/**
* Return the column mapping for the SQL columns to bean properties.
*/
@@ -169,6 +193,9 @@ public final class RawSql implements Serializable {
* Return the hash for this query.
*/
public int queryHash() {
if (resultSet != null) {
return 31 * columnMapping.queryHash();
}
return 31 * sql.queryHash() + columnMapping.queryHash();
}
@@ -329,6 +356,7 @@ public final class RawSql implements Serializable {
private final LinkedHashMap<String, Column> dbColumnMap;
private final Map<String, String> propertyMap;
private final Map<String, Column> propertyColumnMap;
private final boolean parsed;
@@ -364,6 +392,26 @@ public final class RawSql implements Serializable {
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<String, Column>();
}
/**
* Construct for ResultSet use.
*/
protected ColumnMapping(String... propertyNames) {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
//this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<String, Column>();
int hc = 31;
int pos = 0;
for (String prop : propertyNames) {
hc = 31 * hc + prop.hashCode();
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
}
propertyColumnMap = dbColumnMap;
this.queryHashCode = hc;
}
/**
* Construct an immutable ColumnMapping based on collected information.
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import java.sql.ResultSet;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.Sql;
@@ -10,8 +12,6 @@ import com.avaje.ebean.RawSql.Sql;
* named query.
* </p>
*
* @author rbygrave
*
* @see RawSql
*/
public class RawSqlBuilder {
@@ -21,10 +21,23 @@ public class RawSqlBuilder {
*/
public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
/**
* Create and return a RawSql object based on the resultSet and list of properties the columns in
* the resultSet map to.
* <p>
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
*/
public static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
return new RawSql(resultSet, propertyNames);
}
/**
* Return an unparsed RawSqlBuilder. Unlike a parsed one this query can not be
* modified - so no additional WHERE or HAVING expressions can be added to
@@ -58,9 +71,17 @@ public class RawSqlBuilder {
return new RawSqlBuilder(sql2, mapping);
}
private RawSqlBuilder(ResultSet resultSet, ColumnMapping columnMapping) {
this.resultSet = resultSet;
this.columnMapping = columnMapping;
this.sql = null;
}
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
this.sql = sql;
this.columnMapping = columnMapping;
this.resultSet = null;
}
/**
@@ -92,7 +113,7 @@ public class RawSqlBuilder {
* has been defined.
*/
public RawSql create() {
return new RawSql(sql, columnMapping.createImmutableCopy());
return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
}
/**
@@ -101,4 +122,6 @@ public class RawSqlBuilder {
protected Sql getSql() {
return sql;
}
}
@@ -79,6 +79,7 @@ public interface SqlQuery extends Serializable {
* </p>
*
* @return a Future object for the list result of the query
* @deprecated
*/
public SqlFutureList findFutureList();
@@ -3,12 +3,13 @@ package com.avaje.ebean;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import java.io.Closeable;
import java.sql.Connection;
/**
* The Transaction object. Typically representing a JDBC or JTA transaction.
*/
public interface Transaction {
public interface Transaction extends Closeable {
/**
* Read Committed transaction isolation. Same as
@@ -34,13 +35,6 @@ public interface Transaction {
*/
public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
// /**
// * You can call this after the transaction commit to wait for any changes to
// * Lucene indexes to be made and committed. Note that generally this could
// * be some time so it is not expected that you call this method generally.
// */
// public void waitForIndexUpdates();
/**
* Return true if this transaction is read only.
*/
+26 -10
View File
@@ -5,30 +5,46 @@ package com.avaje.ebean;
*/
public class ValuePair {
final Object value1;
private final Object newValue;
final Object value2;
private final Object oldValue;
public ValuePair(Object value1, Object value2) {
this.value1 = value1;
this.value2 = value2;
public ValuePair(Object newValue, Object oldValue) {
this.newValue = newValue;
this.oldValue = oldValue;
}
/**
* Return the first value.
* Return the new value.
*/
public Object getNewValue() {
return newValue;
}
/**
* Return the old value.
*/
public Object getOldValue() {
return oldValue;
}
/**
* Return the new value.
*/
@Deprecated
public Object getValue1() {
return value1;
return newValue;
}
/**
* Return the second value.
* Return the old value.
*/
@Deprecated
public Object getValue2() {
return value2;
return oldValue;
}
public String toString() {
return value1 + "," + value2;
return newValue + "," + oldValue;
}
}
@@ -6,7 +6,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.avaje.ebean.Query;
import com.avaje.ebean.Query.UseIndex;
/**
* Specify the default cache use specific entity type.
@@ -56,9 +55,4 @@ public @interface CacheStrategy {
*/
String warmingQuery() default "";
/**
* Default setting for using a text index if it has been defined on this bean
* type.
*/
UseIndex useIndex() default UseIndex.DEFAULT;
};
@@ -0,0 +1,15 @@
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;
/**
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnHstore {
}
@@ -13,10 +13,5 @@ public enum ConcurrencyMode {
/**
* Use a version column.
*/
VERSION,
/**
* Use all the columns (except Lobs).
*/
ALL
VERSION
}
@@ -0,0 +1,24 @@
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 annotation for declaring an index on a single column.
*
* @author rvbiljouw
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Index {
/**
* Name of the index
*
* @return index name
*/
String value() default "";
}
@@ -55,21 +55,6 @@ import com.avaje.ebean.TxType;
* Ebean.save(order);
* }
* </pre>
*
* <p>
* During development and testing you can set a debug level which will log the
* transaction begin, commit and rollback events so that you can easily confirm
* it is behaving as you would expect.
* </p>
*
* <pre class="code">
* ## in ebean.properties file
*
* ## Log transaction begins and ends etc
* ## (0=NoLogging 1=minimal ... 9=logAll)
* ebean.debug.transaction=3
*
* </pre>
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@@ -3,11 +3,8 @@ package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.Query;
/**
* Lazy loading capable Maps, Lists and Sets.
@@ -34,10 +31,16 @@ public interface BeanCollection<E> extends Serializable {
ALL
}
/**
* Return true if the collection is empty and untouched. Used to detect if a
* collection was 'cleared' deliberately or just un-initialised.
*/
public boolean isEmptyAndUntouched();
/**
* Return the bean that owns this collection.
*/
public Object getOwnerBean();
public EntityBean getOwnerBean();
/**
* Return the bean property name this collection represents.
@@ -75,30 +78,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public void setFilterMany(ExpressionList<?> filterMany);
/**
* Set when this collection is being loaded via a background thread.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void setBackgroundFetch(Future<Integer> future);
/**
* Wait for the fetch to complete with a given timeout.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void backgroundFetchWait(long wait, TimeUnit timeUnit);
/**
* Wait for the fetch to complete.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void backgroundFetchWait();
/**
* Set a listener to be notified when the BeanCollection is first touched.
*/
@@ -145,41 +124,18 @@ public interface BeanCollection<E> extends Serializable {
public boolean isEmpty();
/**
* Returns the underlying details as an iterator.
* <p>
* Note that for maps this returns the entrySet as we need the keys of the
* map.
* </p>
* Returns the underlying collection of beans from the Set, Map or List.
*/
public Collection<E> getActualDetails();
/**
* Set to true if maxRows was hit and there are actually more rows available.
* Returns the underlying entries so for Maps this is a collection of
* Map.Entry.
* <p>
* Can be used by client code that is paging through results using
* setFirstRow() setMaxRows(). If this returns true then the client can
* display a 'next' button etc.
* For maps this returns the entrySet as we need the keys of the map.
* </p>
*/
public boolean hasMoreRows();
/**
* Set to true when maxRows is hit but there are actually more rows available.
* This is set so that client code knows that there is more data available.
*/
public void setHasMoreRows(boolean hasMoreRows);
/**
* Returns true if the fetch has finished. False if the fetch is continuing in
* a background thread.
*/
public boolean isFinishedFetch();
/**
* Set to true when a fetch has finished. Used when a fetch continues in the
* background.
*/
public void setFinishedFetch(boolean finishedFetch);
public Collection<?> getActualEntries();
/**
* return true if there are real rows held. Return false is this is using
@@ -12,5 +12,5 @@ public interface BeanCollectionAdd {
/**
* Add a loaded bean to the collection.
*/
public void addBean(Object bean);
public void addBean(EntityBean bean);
}
@@ -1,6 +1,7 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Arrays;
/**
* Represent the call stack (stack trace elements).
@@ -34,6 +35,25 @@ public final class CallStack implements Serializable {
}
this.pathHash = enc(hc);
}
public int hashCode() {
int hc = 0;
for (int i = 0; i < callStack.length; i++) {
hc = 31 * hc + callStack[i].hashCode();
}
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof CallStack)) {
return false;
}
CallStack e = (CallStack) obj;
return Arrays.equals(callStack, e.callStack);
}
/**
* Return the first element of the call stack.
@@ -14,6 +14,10 @@ import java.io.Serializable;
*/
public interface EntityBean extends Serializable {
public String[] _ebean_getPropertyNames();
public String _ebean_getPropertyName(int pos);
/**
* Return the enhancement marker value.
* <p>
@@ -79,31 +83,19 @@ public interface EntityBean extends Serializable {
*/
public Object _ebean_createCopy();
/**
* Return the fields in their index order.
*/
public String[] _ebean_getFieldNames();
/**
* Set the value of a field of an entity bean of this type.
* <p>
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading and oldValues creation.
* </p>
*
* @param fieldIndex
* the index of the field
* @param entityBean
* the entityBean of this type to modify
* @param value
* the value to set
*/
public void _ebean_setField(int fieldIndex, Object entityBean, Object value);
public void _ebean_setField(int fieldIndex, Object value);
/**
* Set the field value with interception.
*/
public void _ebean_setFieldIntercept(int fieldIndex, Object entityBean, Object value);
public void _ebean_setFieldIntercept(int fieldIndex, Object value);
/**
* Return the value of a field from an entity bean of this type.
@@ -111,17 +103,12 @@ public interface EntityBean extends Serializable {
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading.
* </p>
*
* @param fieldIndex
* the index of the field
* @param entityBean
* the entityBean to get the value from
*/
public Object _ebean_getField(int fieldIndex, Object entityBean);
public Object _ebean_getField(int fieldIndex);
/**
* Return the field value with interception.
*/
public Object _ebean_getFieldIntercept(int fieldIndex, Object entityBean);
public Object _ebean_getFieldIntercept(int fieldIndex);
}
File diff suppressed because it is too large Load Diff
@@ -64,6 +64,25 @@ public final class ObjectGraphNode implements Serializable {
}
public String toString() {
return "origin:" + originQueryPoint + " " + ":" + path + ":" + path;
return "origin:" + originQueryPoint + " path[" + path+"]";
}
public int hashCode() {
int hc = 31 * originQueryPoint.hashCode();
hc = 31 * hc + (path == null ? 0 : path.hashCode());
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ObjectGraphNode)) {
return false;
}
ObjectGraphNode e = (ObjectGraphNode) obj;
return ((e.path == path) || (e.path != null && e.path.equals(path)))
&& e.originQueryPoint.equals(originQueryPoint);
}
}
@@ -14,17 +14,20 @@ import java.io.Serializable;
*/
public final class ObjectGraphOrigin implements Serializable {
private static final long serialVersionUID = 410937765287968707L;
private static final long serialVersionUID = 410937765287968708L;
private final CallStack callStack;
private final String key;
private final String beanType;
private final int queryHash;
private final String key;
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
this.callStack = callStack;
this.beanType = beanType;
this.queryHash = queryHash;
this.key = callStack.getOriginKey(queryHash);
}
@@ -55,7 +58,27 @@ public final class ObjectGraphOrigin implements Serializable {
}
public String toString() {
return key + " " + beanType + " " + callStack.getFirstStackTraceElement();
return "key["+ key + "] type[" + beanType + "] " + callStack.getFirstStackTraceElement()+" ";
}
public int hashCode() {
int hc = 31 * callStack.hashCode();
hc = 31 * hc + beanType.hashCode();
hc = 31 * hc + queryHash;
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ObjectGraphOrigin)) {
return false;
}
ObjectGraphOrigin e = (ObjectGraphOrigin) obj;
return e.queryHash == queryHash
&& e.beanType.equals(beanType)
&& e.callStack.equals(callStack);
}
}
@@ -29,6 +29,13 @@ public interface PersistenceContext {
*/
public Object get(Class<?> beanType, Object uid);
/**
* Get the bean from the persistence context also checked to see if it had
* been previously deleted (if so then you also can't hit the L2 cache to
* fetch the bean for this particular persistence context).
*/
public WithOption getWithOption(Class<?> beanType, Object uid);
/**
* Clear all the references.
*/
@@ -44,9 +51,60 @@ public interface PersistenceContext {
*/
public void clear(Class<?> beanType, Object uid);
/**
* Clear the reference as a result of an entity being deleted.
*/
public void deleted(Class<?> beanType, Object id);
/**
* Return the number of beans of the given type in the persistence context.
*/
public int size(Class<?> beanType);
/**
* Wrapper on a bean to also indicate if a bean has been deleted.
* <p>
* If a bean has been deleted then for the same persistence context is should
* not be able to be fetched from persistence context or L2 cache.
* </p>
*/
public static class WithOption {
/**
* The bean was previously deleted from this persistence context (can't hit
* L2 cache).
*/
public static WithOption DELETED = new WithOption(true);
private final boolean deleted;
private final Object bean;
private WithOption(boolean deleted) {
this.deleted = true;
this.bean = null;
}
/**
* The bean exists in the persistence context (and not been previously deleted).
*/
public WithOption(Object bean) {
this.deleted = false;
this.bean = bean;
}
/**
* Return true if the bean was deleted. This means you can't hit the L2
* cache.
*/
public boolean isDeleted() {
return deleted;
}
/**
* Return the bean (from the persistence context).
*/
public Object getBean() {
return bean;
}
}
}
@@ -1,8 +1,6 @@
package com.avaje.ebean.common;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.persistence.PersistenceException;
@@ -12,7 +10,6 @@ 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 com.avaje.ebean.bean.EntityBeanIntercept;
/**
* Base class for List Set and Map implementations of BeanCollection.
@@ -38,31 +35,16 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected transient BeanCollectionTouched beanCollectionTouched;
protected transient Future<Integer> fetchFuture;
/**
* The owning bean (used for lazy fetch).
*/
protected final Object ownerBean;
protected final EntityBean ownerBean;
/**
* The name of this property in the owning bean (used for lazy fetch).
*/
protected final String propertyName;
/**
* Can be false when a background thread is used to continue the fetch the
* rows. It will set this to true when it is finished. If no background thread
* is used then this should already be true.
*/
protected boolean finishedFetch = true;
/**
* Flag set to true if rows are limited by firstRow maxRows and more rows
* exist. For use by client to enable 'next' for paging.
*/
protected boolean hasMoreRows;
protected ModifyHolder<E> modifyHolder;
protected ModifyListenMode modifyListenMode;
@@ -70,6 +52,12 @@ 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.
*/
@@ -81,19 +69,15 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
/**
* Used to create deferred fetch proxy.
*/
public AbstractBeanCollection(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
public AbstractBeanCollection(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
this.loader = loader;
this.ebeanServerName = loader.getName();
this.ownerBean = ownerBean;
this.propertyName = propertyName;
if (ownerBean instanceof EntityBean) {
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
this.readOnly = ebi.isReadOnly();
}
this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
}
public Object getOwnerBean() {
public EntityBean getOwnerBean() {
return ownerBean;
}
@@ -128,7 +112,14 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
checkEmptyLazyLoad();
}
protected void touched() {
/**
* 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);
@@ -154,66 +145,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.readOnly = readOnly;
}
/**
* Set to true if maxRows was hit and there are actually more rows available.
* <p>
* Can be used by client code that is paging through results using
* setFirstRow() setMaxRows(). If this returns true then the client can
* display a 'next' button etc.
* </p>
*/
public boolean hasMoreRows() {
return hasMoreRows;
}
/**
* Set to true when maxRows is hit but there are actually more rows available.
* This is set so that client code knows that there is more data available.
*/
public void setHasMoreRows(boolean hasMoreRows) {
this.hasMoreRows = hasMoreRows;
}
/**
* Returns true if the fetch has finished. False if the fetch is continuing in
* a background thread.
*/
public boolean isFinishedFetch() {
return finishedFetch;
}
/**
* Set to true when a fetch has finished. Used when a fetch continues in the
* background.
*/
public void setFinishedFetch(boolean finishedFetch) {
this.finishedFetch = finishedFetch;
}
public void setBackgroundFetch(Future<Integer> fetchFuture) {
this.fetchFuture = fetchFuture;
}
public void backgroundFetchWait(long wait, TimeUnit timeUnit) {
if (fetchFuture != null) {
try {
fetchFuture.get(wait, timeUnit);
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
public void backgroundFetchWait() {
if (fetchFuture != null) {
try {
fetchFuture.get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
protected void checkReadOnly() {
if (readOnly) {
String msg = "This collection is in ReadOnly mode";
@@ -10,13 +10,15 @@ import java.util.ListIterator;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* List capable of lazy loading.
*/
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>,
BeanCollectionAdd {
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
private static final long serialVersionUID = 1L;
/**
* The underlying List implementation.
*/
@@ -40,17 +42,25 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
/**
* Used to create deferred fetch proxy.
*/
public BeanList(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
public BeanList(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
@Override
public boolean isEmptyAndUntouched() {
return !touched && (list == null || list.isEmpty());
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
public void addBean(EntityBean bean) {
list.add((E) bean);
}
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
if (list == null) {
list = new ArrayList<E>();
}
list.add((E) bean);
}
@@ -72,16 +82,24 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
list = new ArrayList<E>();
}
}
touched();
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();
touched(setTouched);
}
}
@@ -106,6 +124,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public Collection<E> getActualDetails() {
return list;
}
@Override
public Collection<?> getActualEntries() {
return list;
}
/**
* Returns the underlying list.
@@ -130,7 +153,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanList ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -140,7 +163,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
} else {
sb.append("size[").append(list.size()).append("] ");
sb.append("hasMoreRows[").append(hasMoreRows).append("] ");
sb.append("list").append(list).append("");
}
return sb.toString();
@@ -249,7 +271,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
public boolean isEmpty() {
init();
initAsUntouched();
return list.isEmpty();
}
@@ -8,12 +8,15 @@ import java.util.Map;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* Map capable of lazy loading.
*/
public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Map<K, E> {
private static final long serialVersionUID = 1L;
/**
* The underlying map implementation.
*/
@@ -33,10 +36,22 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
this(new LinkedHashMap<K, E>());
}
public BeanMap(BeanCollectionLoader ebeanServer, Object ownerBean, String propertyName) {
public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) {
super(ebeanServer, ownerBean, propertyName);
}
public boolean isEmptyAndUntouched() {
return !touched && (map == null || map.isEmpty());
}
@SuppressWarnings("unchecked")
public void internalPut(Object key, Object bean) {
if (map == null) {
map = new LinkedHashMap<K, E>();
}
map.put((K)key, (E)bean);
}
public void internalAdd(Object bean) {
throw new RuntimeException("Not allowed for map");
}
@@ -75,16 +90,24 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
map = new LinkedHashMap<K, E>();
}
}
touched();
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();
touched(setTouched);
}
}
@@ -104,14 +127,23 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
/**
* Returns the map entrySet iterator.
* Returns the collection of beans (map values).
*/
public Collection<E> getActualDetails() {
return map.values();
}
/**
* Returns the map entrySet.
* <p>
* This is because the key values may need to be set against the details (so
* they don't need to be set twice).
* </p>
*/
public Collection<E> getActualDetails() {
return map.values();
public Collection<?> getActualEntries() {
return map.entrySet();
}
/**
@@ -122,7 +154,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanMap ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -132,7 +164,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" map").append(map);
}
return sb.toString();
@@ -192,7 +223,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public boolean isEmpty() {
init();
initAsUntouched();
return map.isEmpty();
}
@@ -8,12 +8,14 @@ import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* Set capable of lazy loading.
*/
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>,
BeanCollectionAdd {
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
private static final long serialVersionUID = 1L;
/**
* The underlying Set implementation.
@@ -34,17 +36,24 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
this(new LinkedHashSet<E>());
}
public BeanSet(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
public boolean isEmptyAndUntouched() {
return !touched && (set == null || set.isEmpty());
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
public void addBean(EntityBean bean) {
set.add((E) bean);
}
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
if (set == null) {
set = new LinkedHashSet<E>();
}
set.add((E) bean);
}
@@ -81,16 +90,24 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
set = new LinkedHashSet<E>();
}
}
touched();
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();
touched(setTouched);
}
}
@@ -113,6 +130,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
return set;
}
@Override
public Collection<?> getActualEntries() {
return set;
}
/**
* Returns the underlying set.
*/
@@ -121,7 +143,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanSet ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -131,7 +153,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
} else {
sb.append("size[").append(set.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" set").append(set);
}
return sb.toString();
@@ -210,7 +231,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
public boolean isEmpty() {
init();
initAsUntouched();
return set.isEmpty();
}
@@ -32,29 +32,37 @@ public class DataSourceConfig {
private int isolationLevel = Transaction.READ_COMMITTED;
private boolean autoCommit;
private String heartbeatSql;
private int heartbeatFreqSecs = 30;
private int heartbeatTimeoutSeconds = 3;
private boolean captureStackTrace;
private int maxStackTraceSize = 5;
private int leakTimeMinutes = 30;
private int maxInactiveTimeSecs = 900;
private int maxInactiveTimeSecs = 720;
private int maxAgeMinutes = 0;
private int trimPoolFreqSecs = 59;
private int pstmtCacheSize = 20;
private int cstmtCacheSize = 20;
private int waitTimeoutMillis = 1000;
private String poolListener;
private boolean offline;
Map<String, String> customProperties;
protected Map<String, String> customProperties;
/**
* Return the connection URL.
@@ -125,6 +133,20 @@ public class DataSourceConfig {
public void setIsolationLevel(int isolationLevel) {
this.isolationLevel = isolationLevel;
}
/**
* Return autoCommit setting.
*/
public boolean isAutoCommit() {
return autoCommit;
}
/**
* Set to true to turn on autoCommit.
*/
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
/**
* Return the minimum number of connections the pool should maintain.
@@ -194,6 +216,20 @@ public class DataSourceConfig {
public void setHeartbeatFreqSecs(int heartbeatFreqSecs) {
this.heartbeatFreqSecs = heartbeatFreqSecs;
}
/**
* Return the heart beat timeout in seconds.
*/
public int getHeartbeatTimeoutSeconds() {
return heartbeatTimeoutSeconds;
}
/**
* Set the heart beat timeout in seconds.
*/
public void setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) {
this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds;
}
/**
* Return true if a stack trace should be captured when obtaining a connection
@@ -309,6 +345,23 @@ public class DataSourceConfig {
return maxInactiveTimeSecs;
}
/**
* Return the maximum age a connection is allowed to be before it is closed.
* <p>
* This can be used to close really old connections.
* </p>
*/
public int getMaxAgeMinutes() {
return maxAgeMinutes;
}
/**
* Set the maximum age a connection can be in minutes.
*/
public void setMaxAgeMinutes(int maxAgeMinutes) {
this.maxAgeMinutes = maxAgeMinutes;
}
/**
* Set the time in seconds a connection can be idle after which it can be
* trimmed from the pool.
@@ -321,6 +374,25 @@ public class DataSourceConfig {
this.maxInactiveTimeSecs = maxInactiveTimeSecs;
}
/**
* Return the minimum time gap between pool trim checks.
* <p>
* This defaults to 59 seconds meaning that the pool trim check will run every
* minute assuming the heart beat check runs every 30 seconds.
* </p>
*/
public int getTrimPoolFreqSecs() {
return trimPoolFreqSecs;
}
/**
* Set the minimum trim gap between pool trim checks.
*/
public void setTrimPoolFreqSecs(int trimPoolFreqSecs) {
this.trimPoolFreqSecs = trimPoolFreqSecs;
}
/**
* Return the pool listener.
*/
@@ -359,7 +431,7 @@ public class DataSourceConfig {
public void setOffline(boolean offline) {
this.offline = offline;
}
/**
* Return a map of custom properties for the jdbc driver connection.
*/
@@ -388,18 +460,19 @@ public class DataSourceConfig {
this.username = properties.get(prefix + "username", null);
this.password = properties.get(prefix + "password", null);
String v;
String dbDriver = properties.get(prefix + "databaseDriver", null);
this.driver = properties.get(prefix + "driver", dbDriver);
v = properties.get(prefix + "databaseDriver", null);
this.driver = properties.get(prefix + "driver", v);
v = properties.get(prefix + "databaseUrl", null);
this.url = properties.get(prefix + "url", v);
String dbUrl = properties.get(prefix + "databaseUrl", null);
this.url = properties.get(prefix + "url", dbUrl);
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 900);
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 720);
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
this.minConnections = properties.getInt(prefix + "minConnections", 0);
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
@@ -409,6 +482,7 @@ public class DataSourceConfig {
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
this.heartbeatSql = properties.get(prefix + "heartbeatSql", null);
this.heartbeatTimeoutSeconds = properties.getInt(prefix + "heartbeatTimeoutSeconds", 3);
this.poolListener = properties.get(prefix + "poolListener", null);
this.offline = properties.getBoolean(prefix + "offline", false);
@@ -1,11 +1,10 @@
package com.avaje.ebean.config;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
/**
* A map like structure of properties.
@@ -68,9 +67,8 @@ final class PropertyMap implements Serializable {
}
synchronized void putAll(Map<String, String> keyValueMap) {
Iterator<Entry<String, String>> it = keyValueMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@@ -1,20 +1,18 @@
package com.avaje.ebean.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper used to load the PropertyMap.
*/
@@ -81,9 +79,7 @@ final class PropertyMapLoader {
}
// put values in initially without any evaluation
Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Object> entry = it.next();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = ((String) entry.getKey()).toLowerCase();
String val = ((String) entry.getValue());
if (val != null) {
@@ -18,6 +18,7 @@ import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.BulkTableEventListener;
import com.avaje.ebean.event.ServerConfigStartup;
import com.avaje.ebean.event.TransactionEventListener;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.util.ClassUtil;
/**
@@ -65,7 +66,10 @@ import com.avaje.ebean.util.ClassUtil;
* @author rbygrave
*/
public class ServerConfig {
/** The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100 */
/**
* The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100
*/
private final static int DEFAULT_QUERY_BATCH_SIZE = 100;
/**
@@ -111,13 +115,19 @@ public class ServerConfig {
*/
private List<String> searchJars = new ArrayList<String>();
/** The autofetch config. */
/**
* Config controlling the autofetch behaviour.
*/
private AutofetchConfig autofetchConfig = new AutofetchConfig();
/** The database platform name. */
/**
* The database platform name. Used to imply a DatabasePlatform to use.
*/
private String databasePlatformName;
/** The database platform. */
/**
* The database platform.
*/
private DatabasePlatform databasePlatform;
/**
@@ -129,10 +139,14 @@ public class ServerConfig {
private int persistBatchSize = 20;
/** The default batch size for lazy loading */
/**
* The default batch size for lazy loading
*/
private int lazyLoadBatchSize = 1;
/** The query batch size. */
/**
* The query batch size.
*/
private int queryBatchSize = -1;
private boolean ddlGenerate;
@@ -151,26 +165,58 @@ public class ServerConfig {
*/
private PstmtDelegate pstmtDelegate;
/** The data source. */
/**
* The data source (if programmatically provided).
*/
private DataSource dataSource;
/** The data source config. */
/**
* The data source config.
*/
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
/** The data source jndi name. */
/**
* Set to true if the DataSource uses autoCommit.
* <p>
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
private boolean autoCommitMode;
/**
* The data source JNDI name if using a JNDI DataSource.
*/
private String dataSourceJndiName;
/** The database boolean true. */
/**
* The database boolean true value (typically either 1, T, or Y).
*/
private String databaseBooleanTrue;
/** The database boolean false. */
/**
* The database boolean false value (typically either 0, F or N).
*/
private String databaseBooleanFalse;
/** The naming convention. */
/**
* The naming convention.
*/
private NamingConvention namingConvention;
/** The update changes only. */
/**
* Behaviour of update to include on the change properties.
*/
private boolean updateChangesOnly = true;
/**
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
*/
private boolean updatesDeleteMissingChildren = true;
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40).
*/
private boolean uuidStoreAsBinary;
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
private List<BeanPersistListener<?>> persistListeners = new ArrayList<BeanPersistListener<?>>();
@@ -191,6 +237,10 @@ public class ServerConfig {
private ServerCacheManager serverCacheManager;
private boolean collectQueryStatsByNode;
private boolean collectQueryOrigins;
/**
* Construct a Server Configuration for programmatically creating an
* EbeanServer.
@@ -539,6 +589,20 @@ public class ServerConfig {
public void setDataSourceJndiName(String dataSourceJndiName) {
this.dataSourceJndiName = dataSourceJndiName;
}
/**
* Return true if autoCommit mode is on. This indicates to Ebean to use autoCommit friendly Transactions and TransactionManager.
*/
public boolean isAutoCommitMode() {
return autoCommitMode;
}
/**
* Set to true if autoCommit mode is on and Ebean should use autoCommit friendly Transactions and TransactionManager.
*/
public void setAutoCommitMode(boolean autoCommitMode) {
this.autoCommitMode = autoCommitMode;
}
/**
* Return a value used to represent TRUE in the database.
@@ -759,6 +823,21 @@ public class ServerConfig {
this.dbEncrypt = dbEncrypt;
}
/**
* Return true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public boolean isUuidStoreAsBinary() {
return uuidStoreAsBinary;
}
/**
* Set to true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public void setUuidStoreAsBinary(boolean uuidStoreAsBinary) {
this.uuidStoreAsBinary = uuidStoreAsBinary;
}
/**
* Set to true to run the DDL generation on startup.
*/
@@ -926,6 +1005,66 @@ public class ServerConfig {
public void setUpdateChangesOnly(boolean updateChangesOnly) {
this.updateChangesOnly = updateChangesOnly;
}
/**
* Return true if updates by default delete missing children when cascading save to a OneToMany or
* ManyToMany. When not set this defaults to true.
*/
public boolean isUpdatesDeleteMissingChildren() {
return updatesDeleteMissingChildren;
}
/**
* Set if updates by default delete missing children when cascading save to a OneToMany or
* ManyToMany. When not set this defaults to true.
*/
public void setUpdatesDeleteMissingChildren(boolean updatesDeleteMissingChildren) {
this.updatesDeleteMissingChildren = updatesDeleteMissingChildren;
}
/**
* Return true if the ebeanServer should collection query statistics by ObjectGraphNode.
*/
public boolean isCollectQueryStatsByNode() {
return collectQueryStatsByNode;
}
/**
* Set to true to collection query execution statistics by ObjectGraphNode.
* <p>
* These statistics can be used to highlight code/query 'origin points' that result in lots of lazy loading.
* </p>
* <p>
* It is considered safe/fine to have this set to true for production.
* </p>
* <p>
* This information can be later retrieved via {@link MetaInfoManager}.
* </p>
* @see MetaInfoManager
*/
public void setCollectQueryStatsByNode(boolean collectQueryStatsByNode) {
this.collectQueryStatsByNode = collectQueryStatsByNode;
}
/**
* Return true if query plans should also collect their 'origins'. This means for a given query plan you
* can identify the code/origin points where this query resulted from including lazy loading origins.
*/
public boolean isCollectQueryOrigins() {
return collectQueryOrigins;
}
/**
* Set to true if query plans should collect their 'origin' points. This means for a given query plan you
* can identify the code/origin points where this query resulted from including lazy loading origins.
* <p>
* This information can be later retrieved via {@link MetaInfoManager}.
* </p>
* @see MetaInfoManager
*/
public void setCollectQueryOrigins(boolean collectQueryOrigins) {
this.collectQueryOrigins = collectQueryOrigins;
}
/**
* Returns the resource directory.
@@ -1163,6 +1302,7 @@ public class ServerConfig {
loadDataSourceSettings(p);
autoCommitMode = p.getBoolean("autoCommitMode", false);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
namingConvention = createNamingConvention(p);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
@@ -1183,7 +1323,13 @@ public class ServerConfig {
packages = getSearchJarsPackages(packagesProp);
}
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", true);
updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren);
boolean batchMode = p.getBoolean("batch.mode", false);
persistBatching = p.getBoolean("persistBatching", batchMode);
@@ -1196,6 +1342,7 @@ public class ServerConfig {
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
databasePlatformName = p.get("databasePlatformName", null);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
@@ -57,6 +57,13 @@ public final class TableName {
this.name = split[len - 1];
}
/**
* Parse a qualifiedTableName that might include a catalog and schema and just return the table name.
*/
public static String parse(String qualifiedTableName) {
return new TableName(qualifiedTableName).getName();
}
public String toString() {
return getQualifiedName();
}
@@ -121,6 +128,18 @@ public final class TableName {
return buffer.toString();
}
/**
* Append a catalog and schema prefix if they exist to the string builder.
*/
public void appendCatalogAndSchema(StringBuilder buffer) {
if (catalog != null) {
buffer.append(catalog).append(".");
}
if (schema != null) {
buffer.append(schema).append(".");
}
}
/**
* Checks if is table name is valid i.e. it has at least a name.
*
@@ -17,29 +17,16 @@ public class DB2Platform extends DatabasePlatform {
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead for H2
this.sqlLimiter = new Db2SqlLimiter();
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsSequence(true);
this.openQuote = "\"";
this.closeQuote = "\"";
booleanDbType = Types.INTEGER;
dbTypeMap.put(Types.BOOLEAN, new DbType("smallint default 0"));
dbTypeMap.put(Types.INTEGER, new DbType("integer"));
dbTypeMap.put(Types.BIGINT, new DbType("bigint"));
dbTypeMap.put(Types.REAL, new DbType("float"));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.SMALLINT, new DbType("smallint"));
booleanDbType = Types.BOOLEAN;
dbTypeMap.put(Types.REAL, new DbType("real"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 15));
this.dbDdlSyntax.setIdentity("generated by default as identity");
// this.dbDdlSyntax.setDropIfExists("if exists");
// this.dbDdlSyntax.setDisableReferentialIntegrity("SET REFERENTIAL_INTEGRITY FALSE");
// this.dbDdlSyntax.setEnableReferentialIntegrity("SET REFERENTIAL_INTEGRITY TRUE");
// this.dbDdlSyntax.setForeignKeySuffix("on delete restrict on update restrict");
}
/**
@@ -6,6 +6,7 @@ import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,40 +15,61 @@ import org.slf4j.LoggerFactory;
*/
public class DatabasePlatform {
/** The Constant logger. */
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatform.class);
/** The open quote used by quoted identifiers. */
/**
* The open quote used by quoted identifiers.
*/
protected String openQuote = "\"";
/** The close quote used by quoted identifiers. */
/**
* The close quote used by quoted identifiers.
*/
protected String closeQuote = "\"";
/** For limit/offset, row_number etc limiting of SQL queries. */
/**
* For limit/offset, row_number etc limiting of SQL queries.
*/
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
/** Mapping of JDBC to Database types. */
/**
* Mapping of JDBC to Database types.
*/
protected DbTypeMap dbTypeMap = new DbTypeMap();
/** DB specific DDL syntax. */
/**
* DB specific DDL syntax.
*/
protected DbDdlSyntax dbDdlSyntax = new DbDdlSyntax();
/** Defines DB identity/sequence features. */
/**
* Defines DB identity/sequence features.
*/
protected DbIdentity dbIdentity = new DbIdentity();
/** The JDBC type to map booleans to (by default). */
/**
* The JDBC type to map booleans to (by default).
*/
protected int booleanDbType = Types.BOOLEAN;
/** The JDBC type to map Blob to. */
/**
* The JDBC type to map Blob to.
*/
protected int blobDbType = Types.BLOB;
/** The JDBC type to map Clob to. */
/**
* The JDBC type to map Clob to.
*/
protected int clobDbType = Types.CLOB;
/** For Oracle treat empty strings as null. */
/**
* For Oracle treat empty strings as null.
*/
protected boolean treatEmptyStringsAsNull;
/** The name. */
/**
* The database platform name.
*/
protected String name = "generic";
/**
@@ -57,12 +79,23 @@ public class DatabasePlatform {
*/
private static final char BACK_TICK = '`';
/**
* The like clause. Can be overridden to disable default escape character.
*/
protected String likeClause = "like ?";
protected DbEncrypt dbEncrypt;
protected boolean idInExpandedForm;
protected boolean selectCountWithAlias;
/**
* If set then use the FORWARD ONLY hint when creating ResultSets for
* findIterate() and findVisit().
*/
protected boolean forwardOnlyHintOnFindIterate;
/**
* Instantiates a new database platform.
*/
@@ -199,6 +232,24 @@ public class DatabasePlatform {
return idInExpandedForm;
}
/**
* Return true if the ResultSet TYPE_FORWARD_ONLY Hint should be used on
* findIterate() and findVisit() PreparedStatements.
* <p>
* This specifically is required for MySql when processing large results.
* </p>
*/
public boolean isForwardOnlyHintOnFindIterate() {
return forwardOnlyHintOnFindIterate;
}
/**
* Set to true if the ResultSet TYPE_FORWARD_ONLY Hint should be used by default on findIterate PreparedStatements.
*/
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
}
/**
* Return the DB identity/sequence features for this platform.
*
@@ -275,4 +326,13 @@ public class DatabasePlatform {
return sql;
}
/**
* Returns the like clause used by this database platform.
* <p>
* This may include an escape clause to disable a default escape character.
*/
public String getLikeClause() {
return likeClause;
}
}
@@ -0,0 +1,20 @@
package com.avaje.ebean.config.dbplatform;
public class Db2SqlLimiter implements SqlLimiter {
@Override
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(512);
sb.append("select ");
sb.append(request.getDbSql());
int maxRows = request.getMaxRows();
if (maxRows > 0) {
sb.append(" ").append(NEW_LINE).append("FETCH FIRST ").append(maxRows).append(" ROWS ONLY");
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, false);
}
}
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.TableName;
/**
* Used to support DB specific syntax for DDL generation.
*/
@@ -9,7 +11,7 @@ public class DbDdlSyntax {
private boolean inlinePrimaryKeyConstraint = false;
private boolean addOneToOneUniqueContraint = false;
private boolean addOneToOneUniqueContraint = true;
private int maxConstraintNameLength = 32;
@@ -43,6 +45,17 @@ public class DbDdlSyntax {
return pk;
}
/**
* Return the column definition for an identity column.
*/
public String getIdentityColumnDefn(String columnDefn) {
String identity = getIdentity();
if (identity != null && identity.length() > 0) {
return columnDefn+" "+identity;
}
return columnDefn;
}
/**
* Return the identity clause for DB's that have identities.
*/
@@ -248,26 +261,29 @@ public class DbDdlSyntax {
this.inlinePrimaryKeyConstraint = inlinePrimaryKeyConstraint;
}
/**
* Builds and returns a fully index name.
*/
public String getIndexName(String table, String propName, int ixCount) {
StringBuilder buffer = new StringBuilder();
buffer.append("ix_");
buffer.append(table);
buffer.append("_");
buffer.append(propName);
StringBuilder buffer = new StringBuilder(30);
buffer.append("ix_").append(TableName.parse(table));
buffer.append("_").append(propName);
addSuffix(buffer, ixCount);
return buffer.toString();
}
/**
* Builds and returns a fully qualified foreign key constraint name.
*/
public String getForeignKeyName(String table, String propName, int fkCount) {
StringBuilder buffer = new StringBuilder();
buffer.append("fk_");
buffer.append(table);
buffer.append("_");
buffer.append(propName);
StringBuilder buffer = new StringBuilder(30);
buffer.append("fk_").append(TableName.parse(table));
buffer.append("_").append(propName);
addSuffix(buffer, fkCount);
@@ -276,15 +292,10 @@ public class DbDdlSyntax {
/**
* Adds the suffix.
*
* @param buffer
* the buffer
* @param count
* the count
*/
protected void addSuffix(StringBuilder buffer, int count) {
final String suffixNr = Integer.toString(count);
final int suffixLen = suffixNr.length() + 1;
String suffixNr = Integer.toString(count);
int suffixLen = suffixNr.length() + 1;
if (buffer.length() + suffixLen > maxConstraintNameLength) {
buffer.setLength(maxConstraintNameLength - suffixLen);
@@ -14,6 +14,7 @@ public class H2Platform extends DatabasePlatform {
super();
this.name = "h2";
this.dbEncrypt = new H2DbEncrypt();
this.likeClause = "like ? escape''";
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
@@ -17,27 +17,25 @@ public class LimitOffsetSqlLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(512);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(50 + dbSql.length());
sb.append("select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
int firstRow = request.getFirstRow();
int maxRows = request.getMaxRows();
if (maxRows > 0) {
maxRows = maxRows + 1;
}
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ");
if (maxRows > 0) {
sb.append(maxRows);
}
if (firstRow > 0) {
sb.append(" ").append(OFFSET).append(" ");
sb.append(firstRow);
if (maxRows > 0 || firstRow > 0) {
sb.append(" ").append(LIMIT).append(" ").append(maxRows);
if (firstRow > 0) {
sb.append(" ").append(OFFSET).append(" ");
sb.append(firstRow);
}
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
@@ -26,9 +26,7 @@ public class MsSqlServer2005SqlLimiter implements SqlLimiter {
int lastRow = request.getMaxRows();
if (lastRow > 0) {
// fetch 1 more than we return so that
// we know if more rows are available
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
if (firstRow < 1) {
@@ -20,6 +20,7 @@ public class MySqlPlatform extends DatabasePlatform {
public MySqlPlatform() {
super();
this.name = "mysql";
this.likeClause = "like ? escape''";
this.selectCountWithAlias = true;
this.dbEncrypt = new MySqlDbEncrypt();
@@ -31,6 +32,7 @@ public class MySqlPlatform extends DatabasePlatform {
this.openQuote = "`";
this.closeQuote = "`";
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
dbTypeMap.put(Types.BIT, new DbType("tinyint(1) default 0"));
@@ -0,0 +1,73 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
/**
* Postgres v8.3 specific platform.
* <p>
* No support for getGeneratedKeys.
* </p>
*/
public class Postgres8Platform extends DatabasePlatform {
public Postgres8Platform() {
super();
this.name = "postgres";
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
if (colAlias == null) {
// Postgres requires the "as" keyword for column alias
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
}
this.openQuote = "\"";
this.closeQuote = "\"";
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
dbTypeMap.put(Types.BINARY, new DbType("bytea", false));
dbTypeMap.put(Types.VARBINARY, new DbType("bytea", false));
dbTypeMap.put(Types.BLOB, new DbType("bytea", false));
dbTypeMap.put(Types.CLOB, new DbType("text"));
dbTypeMap.put(Types.LONGVARBINARY, new DbType("bytea", false));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
dbDdlSyntax.setDropTableCascade("cascade");
dbDdlSyntax.setDropIfExists("if exists");
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
String seqName, int batchSize) {
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
}
@Override
protected String withForUpdate(String sql) {
return sql + " for update";
}
}
@@ -0,0 +1,25 @@
package com.avaje.ebean.config.dbplatform;
public class PostgresDdlSyntax extends DbDdlSyntax {
/**
* Map bigint, integer and smallint into their equivilent serial types.
*/
@Override
public String getIdentityColumnDefn(String columnDefn) {
//smallserial, serial and bigserial
if ("bigint".equalsIgnoreCase(columnDefn)) {
return "bigserial";
}
if ("integer".equalsIgnoreCase(columnDefn)) {
return "serial";
}
if ("smallint".equalsIgnoreCase(columnDefn)) {
return "smallserial";
}
return columnDefn;
}
}
@@ -4,27 +4,38 @@ import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
/**
* Postgres v8.3 specific platform.
* Postgres v9 specific platform.
* <p>
* No support for getGeneratedKeys.
* Uses serial types and getGeneratedKeys.
* </p>
*/
public class PostgresPlatform extends DatabasePlatform {
/**
* Unique jdbc type id defined for hstore type.
*/
public static final int TYPE_HSTORE = 4001;
public PostgresPlatform() {
super();
this.name = "postgres";
this.likeClause = "like ? escape''";
this.dbDdlSyntax = new PostgresDdlSyntax();
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
// Use Identity and getGeneratedKeys
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
@@ -36,8 +47,8 @@ public class PostgresPlatform extends DatabasePlatform {
this.openQuote = "\"";
this.closeQuote = "\"";
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(TYPE_HSTORE, new DbType("hstore"));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
@@ -15,7 +15,7 @@ public class RowNumberSqlLimiter implements SqlLimiter {
*/
private static final String ROW_NUMBER_AS = ") as rn, ";
final String rowNumberWindowAlias;
private final String rowNumberWindowAlias;
/**
* Specify the name of the rowNumberWindowAlias.
@@ -30,16 +30,18 @@ public class RowNumberSqlLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
sb.append("select * from (").append(NEW_LINE);
sb.append("select * from ( ");
sb.append("select ");
if (request.isDistinct()) {
@@ -50,9 +52,9 @@ public class RowNumberSqlLimiter implements SqlLimiter {
sb.append(request.getDbOrderBy());
sb.append(ROW_NUMBER_AS);
sb.append(request.getDbSql());
sb.append(dbSql);
sb.append(NEW_LINE).append(") ");
sb.append(" ) ");
sb.append(rowNumberWindowAlias);
sb.append(" where ");
if (firstRow > 0) {
@@ -35,38 +35,40 @@ public class RownumSqlLimiter implements SqlLimiter {
// :MAX_ROW_TO_FETCH )
// where rnum >= :MIN_ROW_TO_FETCH;
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
sb.append("select * ").append(NEW_LINE).append("from ( ");
sb.append("select * from ( ");
sb.append("select ");
if (useFirstRowsHint && request.getMaxRows() > 0) {
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows() + 1).append(") */ ");
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
}
sb.append("rownum ").append(rnum).append(", a.* ").append(NEW_LINE);
sb.append(" from (");//
sb.append("rownum ").append(rnum).append(", a.* ");
sb.append(" from (");
sb.append(" select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
sb.append(NEW_LINE).append(" ) a ");
sb.append(NEW_LINE).append(" ) a ");
if (lastRow > 0) {
sb.append(NEW_LINE).append(" where rownum <= ").append(lastRow);
sb.append(" where rownum <= ").append(lastRow);
}
sb.append(NEW_LINE).append(" ) ");
sb.append(" ) ");
if (firstRow > 0) {
sb.append(NEW_LINE).append("where ");
sb.append(" where ");
sb.append(rnum).append(" > ").append(firstRow);
}
@@ -8,15 +8,12 @@ public class SqlAnywhereLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int maxRows = request.getMaxRows();
if (maxRows > 0) {
// fetch 1 more than we return so that
// we know if more rows are available
maxRows = maxRows + 1;
}
/*
* SELECT TOP xx START AT xx ... FROM ...
@@ -31,7 +28,7 @@ public class SqlAnywhereLimiter implements SqlLimiter {
if (firstRow > 0) {
sb.append("start at ").append(firstRow + 1).append(" ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
@@ -5,8 +5,6 @@ import com.avaje.ebean.Query;
/**
* The request object for the query that can have sql limiting applied to it
* (such as a LIMIT OFFSET clause).
*
* @author rob
*/
public interface SqlLimitRequest {
@@ -5,7 +5,7 @@ import java.util.Set;
/**
* Used to enhance or override the default bean persistence mechanism.
* <p>
* Note that if want to totally change the finding, you need to use a BeanFinder
* Note that if want to totally change the finding, you need to use a BeanQueryAdapter
* rather than using postLoad().
* </p>
* <p>
@@ -2,6 +2,8 @@ package com.avaje.ebean.event;
import java.util.Set;
import com.avaje.ebean.config.ServerConfig;
/**
* Listens for committed bean events.
* <p>
@@ -31,8 +33,9 @@ import java.util.Set;
* </p>
* <p>
* A BeanPersistListener is either found automatically via class path search or
* can be added programmatically via ServerConfiguration.addEntity().
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}.
* </p>
* @see ServerConfig#add(BeanPersistListener)
*/
public interface BeanPersistListener<T> {
@@ -52,7 +55,7 @@ public interface BeanPersistListener<T> {
* @param bean
* The bean that was updated.
* @param updatedProperties
* the properties on the bean that where updated
* The properties that were modified by this update.
*/
public boolean updated(T bean, Set<String> updatedProperties);
@@ -1,9 +1,11 @@
package com.avaje.ebean.event;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.ValuePair;
/**
* Holds the information available for a bean persist (insert, update or
@@ -41,12 +43,8 @@ public interface BeanPersistRequest<T> {
public T getBean();
/**
* Returns a bean containing the original values prior to the bean being
* modified.
* <p>
* This is for updates only.
* </p>
* Returns a map of the properties that have changed and their new and old values.
*/
public T getOldValues();
public Map<String,ValuePair> getUpdatedValues();
}
@@ -1,228 +0,0 @@
package com.avaje.ebean.meta;
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
import com.avaje.ebean.bean.ObjectGraphOrigin;
/**
* Statistics collected by AutoFetch profiling.
*/
@Entity
public class MetaAutoFetchStatistic implements Serializable {
private static final long serialVersionUID = -6640406753257176803L;
@Id
private String id;
private ObjectGraphOrigin origin;
private String beanType;
private int counter;
@Transient
private List<QueryStats> queryStats;
@Transient
private List<NodeUsageStats> nodeUsageStats;
public MetaAutoFetchStatistic() {
}
public MetaAutoFetchStatistic(ObjectGraphOrigin origin, int counter, List<QueryStats> queryStats,
List<NodeUsageStats> nodeUsageStats) {
this.origin = origin;
this.beanType = origin == null ? null : origin.getBeanType();
this.id = origin == null ? null : origin.getKey();
this.counter = counter;
this.queryStats = queryStats;
this.nodeUsageStats = nodeUsageStats;
}
/**
* This is the query point key.
*/
public String getId() {
return id;
}
/**
* Return the bean type.
*/
public String getBeanType() {
return beanType;
}
/**
* Return the query point.
*/
public ObjectGraphOrigin getOrigin() {
return origin;
}
/**
* Return the number of profiled queries the statistics is based on.
*/
public int getCounter() {
return counter;
}
/**
* Return the query execution statistics.
*/
public List<QueryStats> getQueryStats() {
return queryStats;
}
/**
* Return the node usage statistics.
*/
public List<NodeUsageStats> getNodeUsageStats() {
return nodeUsageStats;
}
/**
* FIXME: This will likely be deprecated in favour of a separate object graph
* cost.
*/
public static class QueryStats implements Serializable {
private static final long serialVersionUID = -5517935732867671387L;
private final String path;
private final int exeCount;
private final int totalBeanLoaded;
private final int totalMicros;
public QueryStats(String path, int exeCount, int totalBeanLoaded, int totalMicros) {
this.path = path;
this.exeCount = exeCount;
this.totalBeanLoaded = totalBeanLoaded;
this.totalMicros = totalMicros;
}
/**
* Return the path. This is empty string for the origin query and otherwise
* the path for the associated lazy loading queries.
*/
public String getPath() {
return path;
}
/**
* The number of queries executed.
*/
public int getExeCount() {
return exeCount;
}
/**
* The total number of beans loaded by the query.
*/
public int getTotalBeanLoaded() {
return totalBeanLoaded;
}
/**
* The total time in microseconds of the queries.
*/
public int getTotalMicros() {
return totalMicros;
}
public String toString() {
long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount;
return "queryExe path[" + path + "] count[" + exeCount + "] totalBeansLoaded["
+ totalBeanLoaded + "] avgMicros[" + avgMicros + "] totalMicros[" + totalMicros
+ "]";
}
}
/**
* Collects usages statistics for a given node in the object graph.
*/
public static class NodeUsageStats implements Serializable {
private static final long serialVersionUID = 1786787832374844739L;
private final String path;
private final int profileCount;
private final int profileUsedCount;
private final String[] usedProperties;
public NodeUsageStats(String path, int profileCount, int profileUsedCount,
String[] usedProperties) {
this.path = path == null ? "" : path;
this.profileCount = profileCount;
this.profileUsedCount = profileUsedCount;
this.usedProperties = usedProperties;
}
/**
* Return the path. This is empty string for the origin and otherwise the
* path for the associated nodes.
*/
public String getPath() {
return path;
}
/**
* The number of profiled beans for this node.
*/
public int getProfileCount() {
return profileCount;
}
/**
* The number of profiled beans that where actually used for this node.
* <p>
* The difference between profiled and used could show uneven traversal of
* the object graph. UI paging through results means the traversal for the
* first x beans can be much higher than the last x beans.
* </p>
*/
public int getProfileUsedCount() {
return profileUsedCount;
}
/**
* The properties used at this node.
*/
public String[] getUsedProperties() {
return usedProperties;
}
/**
* Return the properties as a Set rather than an Array.
*/
public Set<String> getUsedPropertiesSet() {
LinkedHashSet<String> s = new LinkedHashSet<String>();
for (int i = 0; i < usedProperties.length; i++) {
s.add(usedProperties[i]);
}
return s;
}
public String toString() {
return "path[" + path + "] profileCount[" + profileCount + "] used[" + profileUsedCount
+ "] props" + Arrays.toString(usedProperties);
}
}
}
@@ -1,119 +0,0 @@
package com.avaje.ebean.meta;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import com.avaje.ebean.bean.ObjectGraphOrigin;
/**
* "Tuned fetch" information used by AutoFetch.
* <p>
* Note that the queryPoint is effectively the Id field for this bean.
* </p>
* <p>
* The queryPoint identifies both the query and call stack.
* </p>
*/
@Entity
public class MetaAutoFetchTunedQueryInfo implements Serializable {
private static final long serialVersionUID = 3119991928889170215L;
@Id
private String id;
private String beanType;
/**
* The profile query point (call stack and query).
*/
private ObjectGraphOrigin origin;
/**
* The tuned query details with joins and properties.
*/
private String tunedDetail;
/**
* The number of times profiling has been collected for this query point.
*/
private int profileCount;
/**
* The number of queries tuned by this info.
*/
private int tunedCount;
private long lastTuneTime;
public MetaAutoFetchTunedQueryInfo() {
}
public MetaAutoFetchTunedQueryInfo(final ObjectGraphOrigin origin, String tunedDetail,
int profileCount, int tunedCount, long lastTuneTime) {
this.origin = origin;
this.beanType = origin == null ? null : origin.getBeanType();
this.id = origin == null ? null : origin.getKey();
this.tunedDetail = tunedDetail;
this.profileCount = profileCount;
this.tunedCount = tunedCount;
this.lastTuneTime = lastTuneTime;
}
/**
* Return the query point key.
*/
public String getId() {
return id;
}
/**
* Return the type of bean this is tuned for.
*/
public String getBeanType() {
return beanType;
}
/**
* Return the query point.
*/
public ObjectGraphOrigin getOrigin() {
return origin;
}
/**
* The tuned query detail in string form.
*/
public String getTunedDetail() {
return tunedDetail;
}
/**
* The number of profiled queries the tuned query is based on.
*/
public int getProfileCount() {
return profileCount;
}
/**
* Return the number of queries tuned.
*/
public int getTunedCount() {
return tunedCount;
}
/**
* Return the time of the last tune (that changed the query).
*/
public long getLastTuneTime() {
return lastTuneTime;
}
public String toString() {
return "origin[" + origin + "] query[" + tunedDetail + "] profileCount[" + profileCount + "]";
}
}
@@ -0,0 +1,17 @@
package com.avaje.ebean.meta;
import java.util.List;
public interface MetaBeanInfo {
/**
* Collect the current query plan statistics return the non-empty statistics.
*/
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
/**
* Collect the current query plan statistics return all the statistics (include query plans that haven't had query executions).
*/
public List<MetaQueryPlanStatistic> collectAllQueryPlanStatistics(boolean reset);
}
@@ -0,0 +1,42 @@
package com.avaje.ebean.meta;
import java.util.List;
/**
* Provides access to the meta data in EbeanServer such as query execution statistics.
*/
public interface MetaInfoManager {
/**
* Return the MetaBeanInfo for a bean type.
*/
public MetaBeanInfo getMetaBeanInfo(Class<?> beanClass);
/**
* Return all the MetaBeanInfo.
*/
public List<MetaBeanInfo> getMetaBeanInfoList();
/**
* Collect and return the query plan statistics for all the beans.
* <p>
* Note that this excludes the query plan statistics where there has been no
* executions (since the last collection with reset).
* </p>
*/
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
/**
* Collect and return the ObjectGraphNode statistics.
* <p>
* These show query executions based on an origin point and relative path.
* This is used to look at the amount of lazy loading occurring for a given
* query origin point and highlight potential for tuning a query.
* </p>
*
* @param reset
* Set to true to reset the underlying statistics after collection.
*/
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset);
}
@@ -0,0 +1,41 @@
package com.avaje.ebean.meta;
import com.avaje.ebean.bean.ObjectGraphNode;
/**
* Statistics for query execution based on object graph origin and paths.
* <p>
* These statistics can be used to identify origin queries that result in lots
* of lazy loading.
* </p>
*
* @see MetaInfoManager#collectNodeStatistics(boolean)
*/
public interface MetaObjectGraphNodeStats {
/**
* Return the ObjectGraphNode which has the origin point and relative path.
*/
public ObjectGraphNode getNode();
/**
* Return the startTime of statistics collection.
*/
public long getStartTime();
/**
* Return the total count of queries executed for this node.
*/
public long getCount();
/**
* Return the total time of queries executed for this node.
*/
public long getTotalTime();
/**
* Return the total beans loaded by queries for this node.
*/
public long getTotalBeans();
}
@@ -0,0 +1,33 @@
package com.avaje.ebean.meta;
import com.avaje.ebean.bean.ObjectGraphNode;
/**
* Holds a query 'origin' point and count for the number of queries executed for
* this 'origin'.
* <p>
* This basically points to the bit of original code and query that results in
* this query directly or via lazy loading.
* </p>
*
* @see MetaQueryPlanStatistic
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
*/
public interface MetaQueryPlanOriginCount {
/**
* The 'origin' and path which this query belongs to.
* <p>
* For lazy loading queries this points to the original query and associated
* navigation path that resulted in this query being executed.
* </p>
*/
public ObjectGraphNode getObjectGraphNode();
/**
* The number of times a query was fired for this node since the counter was
* last reset.
*/
public long getCount();
}
@@ -0,0 +1,91 @@
package com.avaje.ebean.meta;
import java.util.List;
/**
* Query execution statistics Meta data.
*
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
*/
public interface MetaQueryPlanStatistic {
/**
* Return the bean type this query plan is for.
*/
public Class<?> getBeanType();
/**
* Return true if this query plan was tuned by Autofetch.
*/
public boolean isAutofetchTuned();
/**
* Return a string representation of the query plan hash.
*/
public String getQueryPlanHash();
/**
* Return the sql executed.
*/
public String getSql();
/**
* Return the total number of queries executed.
*/
public long getExecutionCount();
/**
* Return the total number of beans loaded by the queries.
* <p>
* This excludes background fetching.
* </p>
*/
public long getTotalLoadedBeans();
/**
* Return the total time taken by executions of this query.
*/
public long getTotalTimeMicros();
/**
* Return the max execution time for this query.
*/
public long getMaxTimeMicros();
/**
* Return the time collection started (or was last reset).
*/
public long getCollectionStart();
/**
* Return the time of the last query executed using this plan.
*/
public long getLastQueryTime();
/**
* Return the average query execution time in microseconds.
* <p>
* This excludes background fetching.
* </p>
*/
public long getAvgTimeMicros();
/**
* Return the average number of bean loaded per query.
* <p>
* This excludes background fetching.
* </p>
*/
public long getAvgLoadedBeans();
/**
* Return the 'origin' points and paths that resulted in the query being
* executed and the associated number of times the query was executed via that
* path.
* <p>
* This includes direct and lazy loading paths.
* </p>
*/
public List<MetaQueryPlanOriginCount> getOrigins();
}
@@ -1,172 +0,0 @@
package com.avaje.ebean.meta;
import java.io.Serializable;
import javax.persistence.Entity;
/**
* Query execution statistics Meta data.
*/
@Entity
public class MetaQueryStatistic implements Serializable {
private static final long serialVersionUID = -8746524372894472583L;
boolean autofetchTuned;
String beanType;
/**
* The original query plan hash (calculated prior to autofetch tuning).
*/
int origQueryPlanHash;
/**
* The final query plan hash (calculated after to autofetch tuning).
*/
int finalQueryPlanHash;
String sql;
int executionCount;
int totalLoadedBeans;
int totalTimeMicros;
long collectionStart;
long lastQueryTime;
int avgTimeMicros;
int avgLoadedBeans;
public MetaQueryStatistic() {
}
/**
* Create a MetaQueryStatistic.
*/
public MetaQueryStatistic(boolean autofetchTuned, String beanType, int plan, String sql,
int executionCount, int totalLoadedBeans, int totalTimeMicros, long collectionStart,
long lastQueryTime) {
this.autofetchTuned = autofetchTuned;
this.beanType = beanType;
this.finalQueryPlanHash = plan;
this.sql = sql;
this.executionCount = executionCount;
this.totalLoadedBeans = totalLoadedBeans;
this.totalTimeMicros = totalTimeMicros;
this.collectionStart = collectionStart;
this.lastQueryTime = lastQueryTime;
this.avgTimeMicros = executionCount == 0 ? 0 : totalTimeMicros / executionCount;
this.avgLoadedBeans = executionCount == 0 ? 0 : totalLoadedBeans / executionCount;
}
public String toString() {
return "type=" + beanType + " tuned:" + autofetchTuned + " origHash=" + origQueryPlanHash
+ " count=" + executionCount + " avgMicros=" + getAvgTimeMicros();
}
/**
* Return true if this query plan was built for Autofetch tuned queries.
*/
public boolean isAutofetchTuned() {
return autofetchTuned;
}
/**
* Return the original query plan hash (calculated prior to autofetch tuning).
* <p>
* This will return 0 if there is no autofetch profiling or tuning on this
* query.
* </p>
*/
public int getOrigQueryPlanHash() {
return origQueryPlanHash;
}
/**
* Return the queryPlanHash value. This is unique for a given query plan.
*/
public int getFinalQueryPlanHash() {
return finalQueryPlanHash;
}
/**
* Return the bean type.
*/
public String getBeanType() {
return beanType;
}
/**
* Return the sql executed.
*/
public String getSql() {
return sql;
}
/**
* Return the total number of queries executed.
*/
public int getExecutionCount() {
return executionCount;
}
/**
* Return the total number of beans loaded by the queries.
* <p>
* This excludes background fetching.
* </p>
*/
public int getTotalLoadedBeans() {
return totalLoadedBeans;
}
/**
* Return the number of times this query was executed.
*/
public int getTotalTimeMicros() {
return totalTimeMicros;
}
/**
* Return the time collection started.
*/
public long getCollectionStart() {
return collectionStart;
}
/**
* Return the time of the last query executed using this plan.
*/
public long getLastQueryTime() {
return lastQueryTime;
}
/**
* Return the average query execution time in microseconds.
* <p>
* This excludes background fetching.
* </p>
*/
public int getAvgTimeMicros() {
return avgTimeMicros;
}
/**
* Return the average number of bean loaded per query.
* <p>
* This excludes background fetching.
* </p>
*/
public int getAvgLoadedBeans() {
return avgLoadedBeans;
}
}
@@ -0,0 +1,4 @@
/**
* Meta data that can be retrieved for the EbeanServer.
*/
package com.avaje.ebean.meta;
@@ -1,17 +0,0 @@
<html>
<head>
<title>Entity Beans for getting "Meta" data from Ebean</title>
</head>
<body>
Entity Beans for getting "Meta" data from Ebean
<p>
You can query these entity beans to get "meta" data from Ebean.
This includes things like query execution statistics.
</p>
<pre class="code">
// fetch the meta data that controls autoFetch query tuning
Query<MetaAutoFetchTunedFetch> query = Ebean.createQuery(MetaAutoFetchTunedFetch.class);
List<MetaAutoFetchTunedFetch> list = query.findList();
</pre>
</body>
</html>
@@ -32,7 +32,6 @@ import com.avaje.ebean.text.StringParser;
* csvReader.addDateTime(&quot;anniversary&quot;, &quot;dd-MMM-yyyy&quot;);
* csvReader.addProperty(&quot;billingAddress.line1&quot;);
* csvReader.addProperty(&quot;billingAddress.city&quot;);
* csvReader.addReference(&quot;billingAddress.country.code&quot;);
*
* csvReader.process(reader);
*
@@ -41,8 +40,6 @@ import com.avaje.ebean.text.StringParser;
* }
* </pre>
*
* @author rbygrave
*
* @param <T>
* the entity bean type
*/
@@ -129,13 +126,6 @@ public interface CsvReader<T> {
*/
public void addProperty(String propertyName);
/**
* Define the next property to be a reference. This effectively means it
* represents a foreign key. For example, with an Address object a Country
* Code could be a reference.
*/
public void addReference(String propertyName);
/**
* Define the next property and use a custom StringParser to convert the
* string content into the appropriate type for the property.
@@ -3,8 +3,7 @@ package com.avaje.ebeaninternal.api;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -14,22 +13,16 @@ import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
/**
* Parameters used for binding to a statement.
* <p>
* Used by FindByNativeSql and UpdateSql to support ordered and named
* parameters. Note that you can use either ordered OR named parameters.
* Supports ordered or named parameters.
* </p>
*/
public class BindParams implements Serializable {
private static final long serialVersionUID = 4541081933302086285L;
private ArrayList<Param> positionedParameters = new ArrayList<Param>();
private List<Param> positionedParameters = new ArrayList<Param>();
private HashMap<String, Param> namedParameters = new HashMap<String, Param>();
/**
* Need to create a hash when binding collection values (for in clauses).
*/
private int queryPlanHash = 1;
private Map<String, Param> namedParameters = new LinkedHashMap<String, Param>();
/**
* This is the sql. For named parameters this is the sql after the named
@@ -38,6 +31,40 @@ public class BindParams implements Serializable {
*/
private String preparedSql;
public BindParams() {
}
public int queryBindHash() {
int hc = namedParameters.hashCode();
for (int i = 0; i < positionedParameters.size(); i++) {
hc = hc * 31 + positionedParameters.get(i).hashCode();
}
return hc;
}
/**
* Return the hash that should be included with the query plan.
* <p>
* This is to handle binding collections to in clauses. The number of values
* in the collection effects the query (number of bind values) and so must be
* taken into account when calculating the query hash.
* </p>
*/
public void buildQueryPlanHash(HashQueryPlanBuilder builder) {
int hc = 31;
for (Param param : positionedParameters) {
hc = hc * 31 + param.queryBindCount();
}
for (Map.Entry<String, Param> entry : namedParameters.entrySet()) {
hc = hc * 31 + entry.getKey().hashCode();
hc = hc * 31 + entry.getValue().queryBindCount();
}
int bindCount = positionedParameters.size() + namedParameters.size();
builder.add(hc).bind(bindCount);
}
/**
* Return a deep copy of the BindParams.
*/
@@ -46,45 +73,12 @@ public class BindParams implements Serializable {
for (Param p : positionedParameters) {
copy.positionedParameters.add(p.copy());
}
Iterator<Entry<String, Param>> it = namedParameters.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Param> entry = (Map.Entry<String, Param>) it.next();
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
for (Entry<String, Param> entry : namedParameters.entrySet()) {
copy.namedParameters.put(entry.getKey(), entry.getValue().copy());
}
return copy;
}
public int queryBindHash() {
int hc = namedParameters.hashCode();
for (int i = 0; i < positionedParameters.size(); i++) {
hc = hc * 31 + positionedParameters.get(i).hashCode();
}
return hc;
}
public int hashCode() {
int hc = getClass().hashCode();
hc = hc * 31 + namedParameters.hashCode();
for (int i = 0; i < positionedParameters.size(); i++) {
hc = hc * 31 + positionedParameters.get(i).hashCode();
}
hc = hc * 31 + (preparedSql == null ? 0 : preparedSql.hashCode());
return hc;
}
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o == this) {
return true;
}
if (o instanceof BindParams) {
return hashCode() == o.hashCode();
}
return false;
}
/**
* Return true if there are no bind parameters.
*/
@@ -131,10 +125,8 @@ public class BindParams implements Serializable {
* Set an In Out parameter using position.
*/
public void setParameter(int position, Object value, int outType) {
addToQueryPlanHash(String.valueOf(position), value);
Param p = getParam(position);
Param p = getParam(position);
p.setInValue(value);
p.setOutType(outType);
}
@@ -144,10 +136,8 @@ public class BindParams implements Serializable {
* must use setNullParameter.
*/
public void setParameter(int position, Object value) {
addToQueryPlanHash(String.valueOf(position), value);
Param p = getParam(position);
Param p = getParam(position);
p.setInValue(value);
}
@@ -182,10 +172,8 @@ public class BindParams implements Serializable {
* Set a named In Out parameter.
*/
public void setParameter(String name, Object value, int outType) {
addToQueryPlanHash(name, value);
Param p = getParam(name);
Param p = getParam(name);
p.setInValue(value);
p.setOutType(outType);
}
@@ -203,48 +191,22 @@ public class BindParams implements Serializable {
*/
public Param setParameter(String name, Object value) {
addToQueryPlanHash(name, value);
Param p = getParam(name);
Param p = getParam(name);
p.setInValue(value);
return p;
}
/**
* For binding collections calculate a hash to be used for the query plan.
*/
private void addToQueryPlanHash(String name, Object value){
if (value != null){
if (value instanceof Collection<?>){
queryPlanHash = queryPlanHash * 31 + name.hashCode();
queryPlanHash = queryPlanHash * 31 + ((Collection<?>)value).size();
}
}
}
/**
* Return the hash that should be included with the query plan.
* <p>
* This is to handle binding collections to in clauses. The number
* of values in the collection effects the query (number of bind values)
* and so must be taken into account when calculating the query hash.
* </p>
*/
public int getQueryPlanHash() {
return queryPlanHash;
}
/**
* Set an encryption key as a bind value.
* <p>
* Needs special treatment as the value should not be included in a log.
* </p>
*/
public Param setEncryptionKey(String name, Object value) {
Param p = getParam(name);
p.setEncryptionKey(value);
return p;
}
/**
* Set an encryption key as a bind value.
* <p>
* Needs special treatment as the value should not be included in a log.
* </p>
*/
public Param setEncryptionKey(String name, Object value) {
Param p = getParam(name);
p.setEncryptionKey(value);
return p;
}
/**
* Register the named parameter as an Out parameter.
@@ -300,9 +262,9 @@ public class BindParams implements Serializable {
*/
public static final class OrderedList {
final List<Param> paramList;
private final List<Param> paramList;
final StringBuilder preparedSql;
private final StringBuilder preparedSql;
public OrderedList() {
this(new ArrayList<Param>());
@@ -373,6 +335,16 @@ public class BindParams implements Serializable {
public Param() {
}
public int queryBindCount() {
if (inValue == null) {
return 0;
}
if (inValue instanceof Collection<?>){
return ((Collection<?>)inValue).size();
}
return 1;
}
/**
* Create a deep copy of the Param.
*/
@@ -448,14 +420,14 @@ public class BindParams implements Serializable {
this.isInParam = true;
}
/**
* Set an encryption key (which can not be logged).
*/
public void setEncryptionKey(Object in) {
this.inValue = in;
this.isInParam = true;
this.encryptionKey = true;
}
/**
* Set an encryption key (which can not be logged).
*/
public void setEncryptionKey(Object in) {
this.inValue = in;
this.isInParam = true;
this.encryptionKey = true;
}
/**
* Specify that the In parameter is NULL and the specific type that it
@@ -506,12 +478,12 @@ public class BindParams implements Serializable {
this.textLocation = textLocation;
}
/**
* If true do not include this value in a transaction log.
*/
public boolean isEncryptionKey() {
return encryptionKey;
}
/**
* If true do not include this value in a transaction log.
*/
public boolean isEncryptionKey() {
return encryptionKey;
}
}
}
@@ -0,0 +1,51 @@
package com.avaje.ebeaninternal.api;
/**
* A hash key for a query including both the query plan and bind values.
*/
public class HashQuery {
private final HashQueryPlan planHash;
private final int bindHash;
/**
* Create the HashQuery.
*/
public HashQuery(HashQueryPlan planHash, int bindHash) {
this.planHash = planHash;
this.bindHash = bindHash;
}
/**
* Return the query plan hash.
*/
public HashQueryPlan getPlanHash() {
return planHash;
}
/**
* Return the bind values hash.
*/
public int getBindHash() {
return bindHash;
}
public int hashCode() {
int hc = 31 * planHash.hashCode();
hc = 31 * hc + bindHash;
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof HashQuery)) {
return false;
}
HashQuery e = (HashQuery) obj;
return e.bindHash == bindHash && e.planHash.equals(planHash);
}
}
@@ -0,0 +1,44 @@
package com.avaje.ebeaninternal.api;
/**
* A hash for a query plan.
*/
public class HashQueryPlan {
private final String rawSql;
private final int planHash;
private final int bindCount;
public HashQueryPlan(String rawSql, int planHash, int bindCount) {
this.rawSql = rawSql;
this.planHash = planHash;
this.bindCount = bindCount;
}
public String toString() {
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
}
public int hashCode() {
int hc = planHash;
hc = hc * 31 + bindCount;
hc = hc * 31 + (rawSql == null ? 0 : rawSql.hashCode());
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof HashQueryPlan)) {
return false;
}
HashQueryPlan e = (HashQueryPlan) obj;
return e.planHash == planHash
&& e.bindCount == bindCount
&& ((e.rawSql == rawSql) || (e.rawSql != null && e.rawSql.equals(rawSql)));
}
}
@@ -0,0 +1,78 @@
package com.avaje.ebeaninternal.api;
/**
* Used to build HashQueryPlan instances.
*/
public class HashQueryPlanBuilder {
private int planHash;
private int bindCount;
private String rawSql;
public HashQueryPlanBuilder() {
this.planHash = 31;
}
public String toString() {
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
}
/**
* Add a class to the hash calculation.
*/
public HashQueryPlanBuilder add(Class<?> cls) {
planHash = planHash * 31 + cls.getName().hashCode();
return this;
}
/**
* Add an object to the hash calculation.
*/
public HashQueryPlanBuilder add(Object object) {
planHash = planHash * 31 + (object == null ? 0 : object.hashCode());
return this;
}
/**
* Add an integer to the hash calculation.
*/
public HashQueryPlanBuilder add(int hashValue) {
planHash = planHash * 31 + (hashValue);
return this;
}
/**
* Add a boolean to the hash calculation.
*/
public HashQueryPlanBuilder add(boolean booleanValue) {
planHash = planHash * 31 + (booleanValue ? 31 : 0);
return this;
}
/**
* Add a number to the bind count for the hash.
*/
public HashQueryPlanBuilder bind(int extraBindCount) {
bindCount += extraBindCount;
return this;
}
/**
* Add raw sql to the hash.
*/
public HashQueryPlanBuilder addRawSql(String rawSql) {
this.rawSql = rawSql;
return this;
}
/**
* Build and return the calculated HashQueryPlan.
*/
public HashQueryPlan build() {
return new HashQueryPlan(rawSql, planHash, bindCount);
}
}
@@ -0,0 +1,24 @@
package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
* A buffer of beans for batch lazy loading and secondary query loading.
*/
public interface LoadBeanBuffer {
public List<EntityBeanIntercept> getBatch();
public BeanDescriptor<?> getBeanDescriptor();
public PersistenceContext getPersistenceContext();
public String getFullPath();
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
}
@@ -1,39 +1,10 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
* Controls the loading of ManyToOne and OneToOne relationships.
*
* @author rbygrave
*/
public interface LoadBeanContext extends LoadSecondaryQuery {
/**
* Configure the query to load beans for this node/path.
*/
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
/**
* Return the full path of this node from the root object.
*/
public String getFullPath();
/**
* Return the persistence context used for all queries
* related to this object graph.
*/
public PersistenceContext getPersistenceContext();
/**
* Return the BeanDescriptor for beans for this node.
*/
public BeanDescriptor<?> getBeanDescriptor();
/**
* Return the batchSize used for lazy loading beans.
*/
public int getBatchSize();
}
@@ -2,62 +2,61 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading ManyToOne and OneToOne relationships.
*/
public class LoadBeanRequest extends LoadRequest {
private final List<EntityBeanIntercept> batch;
private final LoadBeanContext loadContext;
private final String lazyLoadProperty;
private final boolean loadCache;
public LoadBeanRequest(LoadBeanContext loadContext, List<EntityBeanIntercept> batch,
Transaction transaction, int batchSize, boolean lazy, String lazyLoadProperty, boolean loadCache) {
super(transaction, batchSize, lazy);
this.loadContext = loadContext;
this.batch = batch;
this.lazyLoadProperty = lazyLoadProperty;
this.loadCache = loadCache;
}
public boolean isLoadCache() {
return loadCache;
}
private final List<EntityBeanIntercept> batch;
public String getDescription() {
String fullPath = loadContext.getFullPath();
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
+ batch.size();
return s;
}
private final LoadBeanBuffer LoadBuffer;
/**
* Return the batch of beans to actually load.
*/
public List<EntityBeanIntercept> getBatch() {
return batch;
}
private final String lazyLoadProperty;
/**
* Return the load context.
*/
public LoadBeanContext getLoadContext() {
return loadContext;
}
private final boolean loadCache;
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, boolean lazy, String lazyLoadProperty, boolean loadCache) {
this(LoadBuffer, null, lazy, lazyLoadProperty, loadCache);
}
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
super(parentRequest, lazy);
this.LoadBuffer = LoadBuffer;
this.batch = LoadBuffer.getBatch();
this.lazyLoadProperty = lazyLoadProperty;
this.loadCache = loadCache;
}
public boolean isLoadCache() {
return loadCache;
}
public String getDescription() {
return "path:" + LoadBuffer.getFullPath() + " batch:" + batch.size();
}
/**
* Return the batch of beans to actually load.
*/
public List<EntityBeanIntercept> getBatch() {
return batch;
}
/**
* Return the load context.
*/
public LoadBeanBuffer getLoadContext() {
return LoadBuffer;
}
/**
* Return the property that invoked the lazy loading.
*/
public String getLazyLoadProperty() {
return lazyLoadProperty;
}
/**
* Return the property that invoked the lazy loading.
*/
public String getLazyLoadProperty() {
return lazyLoadProperty;
}
}
@@ -0,0 +1,30 @@
package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
/**
* A buffer of bean collections for batch lazy loading and secondary query loading.
*/
public interface LoadManyBuffer {
public List<BeanCollection<?>> getBatch();
public BeanPropertyAssocMany<?> getBeanProperty();
public ObjectGraphNode getObjectGraphNode();
public BeanDescriptor<?> getBeanDescriptor();
public PersistenceContext getPersistenceContext();
public String getFullPath();
public void configureQuery(SpiQuery<?> query);
}
@@ -1,54 +1,9 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
/**
* Controls the loading of OneToMany and ManyToMany relationships.
*
* @author rbygrave
*/
public interface LoadManyContext extends LoadSecondaryQuery {
/**
* Configure the query to load beans for this node/path.
*/
public void configureQuery(SpiQuery<?> query);
/**
* Return the full path of this node from the root object.
*/
public String getFullPath();
/**
* Return the node location for this node/path.
*/
public ObjectGraphNode getObjectGraphNode();
/**
* Return the persistence context used for all queries
* related to this object graph.
*/
public PersistenceContext getPersistenceContext();
/**
* Return the batchSize used for lazy loading beans.
*/
public int getBatchSize();
/**
* Return the BeanDescriptor for beans for this node.
*/
public BeanDescriptor<?> getBeanDescriptor();
/**
* Return the associated Many bean property.
*/
public BeanPropertyAssocMany<?> getBeanProperty();
}
@@ -2,73 +2,71 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
* Request for loading Associated Many Beans.
*/
public class LoadManyRequest extends LoadRequest {
private final List<BeanCollection<?>> batch;
private final List<BeanCollection<?>> batch;
private final LoadManyBuffer loadContext;
private final LoadManyContext loadContext;
private final boolean onlyIds;
private final boolean onlyIds;
private final boolean loadCache;
public LoadManyRequest(LoadManyContext loadContext,
List<BeanCollection<?>> batch, Transaction transaction,
int batchSize, boolean lazy, boolean onlyIds, boolean loadCache) {
private final boolean loadCache;
super(transaction, batchSize, lazy);
this.loadContext = loadContext;
this.batch = batch;
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
public LoadManyRequest(LoadManyBuffer loadContext, int batchSize, boolean lazy,boolean onlyIds, boolean loadCache) {
this(loadContext, null, batchSize, lazy, onlyIds, loadCache);
}
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, int batchSize, boolean lazy,
boolean onlyIds, boolean loadCache) {
public String getDescription() {
String fullPath = loadContext.getFullPath();
String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
+ batch.size();
return s;
}
super(parentRequest, lazy);
this.loadContext = loadContext;
this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
public String getDescription() {
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
}
/**
* Return the load context.
*/
public LoadManyContext getLoadContext() {
return loadContext;
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such
* as clear() and removeAll() where it generally makes sense to
* only fetch the Id values as the other property information is
* not used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
* generally makes sense to only fetch the Id values as the other property information is not
* used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
}
@@ -1,26 +1,36 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
*/
public abstract class LoadRequest {
protected final boolean lazy;
protected final OrmQueryRequest<?> parentRequest;
protected final int batchSize;
protected final Transaction transaction;
protected final Transaction transaction;
protected final boolean lazy;
public LoadRequest(Transaction transaction, int batchSize, boolean lazy) {
public LoadRequest(OrmQueryRequest<?> parentRequest, boolean lazy) {
this.transaction = transaction;
this.batchSize = batchSize;
this.parentRequest = parentRequest;
this.transaction = parentRequest == null ? null : parentRequest.getTransaction();
this.lazy = lazy;
}
/**
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
* true. This is for testing purposes to confirm the secondary query executes etc.
*/
public void logSecondaryQuery(SpiQuery<?> query) {
if (parentRequest != null && parentRequest.isLogSecondaryQuery()) {
parentRequest.getQuery().logSecondaryQuery(query);
}
}
/**
* Return true if this is a lazy load and false if it is a secondary query.
*/
@@ -28,13 +38,6 @@ public abstract class LoadRequest {
return lazy;
}
/**
* Return the requested batch size.
*/
public int getBatchSize() {
return batchSize;
}
/**
* Return the transaction to use if this is a secondary query.
* <p>
@@ -1,13 +1,15 @@
package com.avaje.ebeaninternal.api;
import java.io.Serializable;
import java.util.Set;
import java.util.Collection;
import java.util.TreeMap;
import java.util.TreeSet;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Holds the joins needs to support the many where predicates.
@@ -17,7 +19,31 @@ public class ManyWhereJoins implements Serializable {
private static final long serialVersionUID = -6490181101871795417L;
private final TreeSet<String> joins = new TreeSet<String>();
private final TreeMap<String,PropertyJoin> joins = new TreeMap<String,PropertyJoin>();
private StringBuilder formulaProperties = new StringBuilder();
private boolean formulaWithJoin;
/**
* 'Mode' indicating that joins added while this is true are required to be outer joins.
*/
private boolean requireOuterJoins;
/**
* Return the current 'mode' indicating if outer joins are currently required or not.
*/
public boolean isRequireOuterJoins() {
return requireOuterJoins;
}
/**
* Set the 'mode' to be that joins added are required to be outer joins.
* This is set during the evaluation of disjunction predicates.
*/
public void setRequireOuterJoins(boolean requireOuterJoins) {
this.requireOuterJoins = requireOuterJoins;
}
/**
* Add a many where join.
@@ -27,13 +53,15 @@ public class ManyWhereJoins implements Serializable {
String join = elProp.getElPrefix();
BeanProperty p = elProp.getBeanProperty();
if (p instanceof BeanPropertyAssocMany<?>){
join = addManyToJoin(join, p.getName());
join = addManyToJoin(join, p.getName());
}
if (join != null){
joins.add(join);
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
joins.add(join+"."+secondaryTableJoinPrefix);
addJoin(join);
if (p != null) {
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
addJoin(join+"."+secondaryTableJoinPrefix);
}
}
addParentJoins(join);
}
@@ -54,11 +82,16 @@ public class ManyWhereJoins implements Serializable {
private void addParentJoins(String join) {
String[] split = SplitName.split(join);
if (split[0] != null){
joins.add(split[0]);
addJoin(split[0]);
addParentJoins(split[0]);
}
}
private void addJoin(String property) {
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
joins.put(property, new PropertyJoin(property, joinType));
}
/**
* Return true if there are no extra many where joins.
*/
@@ -69,8 +102,52 @@ public class ManyWhereJoins implements Serializable {
/**
* Return the set of many where joins.
*/
public Set<String> getJoins() {
return joins;
public Collection<PropertyJoin> getPropertyJoins() {
return joins.values();
}
/**
* Return the set of property names for the many where joins.
*/
public TreeSet<String> getPropertyNames() {
TreeSet<String> propertyNames = new TreeSet<String>();
for (PropertyJoin join : joins.values()) {
propertyNames.add(join.getProperty());
}
return propertyNames;
}
/**
* In findRowCount query found a formula property with a join clause so building a select clause
* specifically for the findRowCount query.
*/
public void addFormulaWithJoin(String propertyName) {
if (formulaWithJoin) {
formulaProperties.append(",");
} else {
formulaProperties = new StringBuilder();
formulaWithJoin = true;
}
formulaProperties.append(propertyName);
}
public boolean isHasMany() {
return formulaWithJoin || !joins.isEmpty();
}
/**
* Return true if the findRowCount query just needs the id property in the select clause.
*/
public boolean isSelectId() {
return !formulaWithJoin;
}
/**
* Return the formula properties to build the select clause for a findRowCount query.
*/
public String getFormulaProperties() {
return formulaProperties.toString();
}
}
@@ -0,0 +1,39 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join required for a given property and whether than needs to be an outer join.
*/
public class PropertyJoin {
/**
* The property name.
*/
private final String property;
/**
* Set to true if the property needs to be an outer join.
*/
private final SqlJoinType joinType;
public PropertyJoin(String property, SqlJoinType joinType) {
this.property = property;
this.joinType = joinType;
}
/**
* Return the property that should be joined.
*/
public String getProperty() {
return property;
}
/**
* Return true if this join is required to be an outer join.
*/
public SqlJoinType getSqlJoinType() {
return joinType;
}
}
@@ -1,6 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import com.avaje.ebean.TxScope;
@@ -45,9 +44,6 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
*/
private final ArrayList<Class<? extends Throwable>> rollbackFor;
private final UncaughtExceptionHandler originalUncaughtHandler;
/**
* Flag set when a rollback has occurred.
*/
@@ -65,11 +61,6 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
Thread t = Thread.currentThread();
originalUncaughtHandler = t.getUncaughtExceptionHandler();
t.setUncaughtExceptionHandler(this);
}
/**
@@ -81,13 +72,8 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
// rollback transaction if required
caughtThrowable(e);
// reinstate suspended transaction and
// original uncaughtExceptionHandler if required
// reinstate suspended transaction
onFinally();
if (originalUncaughtHandler != null){
originalUncaughtHandler.uncaughtException(thread, e);
}
}
/**
@@ -110,11 +96,7 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
* Also reinstate the suspended transaction if there was one.
*/
public void onFinally() {
try {
if (originalUncaughtHandler != null){
Thread.currentThread().setUncaughtExceptionHandler(originalUncaughtHandler);
}
try {
if (!rolledBack && created) {
transaction.commit();
}
@@ -9,6 +9,8 @@ import com.avaje.ebean.TxScope;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanLoader;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.core.PstmtBatch;
@@ -30,15 +32,14 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
public void shutdownManaged();
/**
* Return true if DeleteMissingChildren defaults to true for stateless
* updates.
* Return true if query origins should be collected.
*/
public boolean isDefaultDeleteMissingChildren();
public boolean isCollectQueryOrigins();
/**
* Return true if UpdateNullProperties defaults to true for stateless updates.
* Return the server configuration.
*/
public boolean isDefaultUpdateNullProperties();
public ServerConfig getServerConfig();
/**
* Return the DatabasePlatform for this server.
@@ -187,4 +188,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
*/
public boolean isSupportedType(java.lang.reflect.Type genericType);
/**
* Collect query statistics by ObjectGraphNode. Used for Lazy loading reporting.
*/
public void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
}
@@ -10,13 +10,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public interface SpiExpression extends Expression {
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent
* join clause.
* </p>
*/
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
/**
@@ -26,7 +25,7 @@ public interface SpiExpression extends Expression {
* from an AutoFetch perspective and get different tuning.
* </p>
*/
public int queryAutoFetchHash();
public void queryAutoFetchHash(HashQueryPlanBuilder builder);
/**
* Calculate a hash value for the expression.
@@ -37,7 +36,7 @@ public interface SpiExpression extends Expression {
* case the query execution plan can be reused.
* </p>
*/
public int queryPlanHash(BeanQueryRequest<?> request);
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
/**
* Return the hash value for the values that will be bound.
@@ -1,13 +1,12 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
public interface SpiExpressionFactory extends ExpressionFactory {
/**
* Create another expression factory with a given sub path.
*/
public ExpressionFactory createExpressionFactory(FilterExprPath prefix);
/**
* Create another expression factory with a given sub path.
*/
public ExpressionFactory createExpressionFactory();
}
@@ -18,24 +18,23 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
*/
public List<SpiExpression> getUnderlyingList();
/**
* Trim the path for filterMany() expressions.
*/
public void trimPath(int prefixTrim);
/**
* Return a copy of the ExpressionList with the path trimmed for filterMany() expressions.
*/
public SpiExpressionList<?> trimPath(int prefixTrim);
/**
* Restore the ExpressionFactory after deserialisation.
*/
public void setExpressionFactory(ExpressionFactory expr);
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent
* join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
/**
* Return true if this list is empty.
@@ -62,10 +61,10 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
*/
public ArrayList<Object> buildBindValues(SpiExpressionRequest request);
/**
* Calculate a hash based on the expressions but excluding the actual bind
* values.
*/
public int queryPlanHash(BeanQueryRequest<?> request);
/**
* Calculate a hash based on the expressions but excluding the actual bind
* values.
*/
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
}
@@ -44,9 +44,14 @@ public interface SpiExpressionRequest {
* Return the ordered list of bind values for all expressions in this request.
*/
public ArrayList<Object> getBindValues();
/**
* Increments the parameter index and returns that value.
*/
public int nextParameter();
/**
* Increments the parameter index and returns that value.
*/
public int nextParameter();
/**
* Append a DB Like clause.
*/
public void appendLike();
}
@@ -6,7 +6,6 @@ import java.util.List;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryListener;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.EntityBean;
@@ -15,6 +14,7 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
@@ -79,11 +79,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
SUBQUERY
}
/**
* Set total hits when querying against lucene.
*/
public void setTotalHits(int totalHits);
/**
* Return true if select all properties was used to ensure the property
@@ -153,11 +148,23 @@ public interface SpiQuery<T> extends Query<T> {
*/
public String getLoadMode();
/**
* This becomes a lazy loading query for a many relationship.
*/
public void setLazyLoadForParents(List<Object> parentIds, BeanPropertyAssocMany<?> many);
/**
* Return the lazy loading 'many' property.
*/
public BeanPropertyAssocMany<?> getLazyLoadForParentsProperty();
/**
* Return the list of parent Id's for lazy loading.
*/
public List<Object> getLazyLoadForParentIds();
/**
* Set the load mode (+lazy or +query) and the load description.
*
* @param loadMode
* @param loadDescription
*/
public void setLoadDescription(String loadMode, String loadDescription);
@@ -253,10 +260,25 @@ public interface SpiQuery<T> extends Query<T> {
*/
public Boolean isAutofetch();
// /**
// * Return explicit forUpdate setting or null.
// */
// public boolean isForUpdate();
/**
* Set to true if you want to capture executed secondary queries.
*/
public void setLogSecondaryQuery(boolean logSecondaryQuery);
/**
* Return true if executed secondary queries should be captured.
*/
public boolean isLogSecondaryQuery();
/**
* Return the list of secondary queries that were executed.
*/
public List<SpiQuery<?>> getLoggedSecondaryQueries();
/**
* Log an executed secondary query.
*/
public void logSecondaryQuery(SpiQuery<?> query);
/**
* If return null then no autoFetch profiling for this query. If a
@@ -341,7 +363,7 @@ public interface SpiQuery<T> extends Query<T> {
* tuning/modifying the query.
* </p>
*/
public int queryAutofetchHash();
public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder);
/**
* Identifies queries that are the same bar the bind variables.
@@ -354,7 +376,7 @@ public interface SpiQuery<T> extends Query<T> {
* Excludes the actual bind values (as they don't effect the query plan).
* </p>
*/
public int queryPlanHash(BeanQueryRequest<?> request);
public HashQueryPlan queryPlanHash(BeanQueryRequest<?> request);
/**
* Calculate a hash based on the bind values used in the query.
@@ -368,7 +390,7 @@ public interface SpiQuery<T> extends Query<T> {
/**
* Identifies queries that are exactly the same including bind variables.
*/
public int queryHash();
public HashQuery queryHash();
/**
* Return true if this is a query based on a SqlSelect rather than
@@ -501,12 +523,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
public String getMapKey();
/**
* Return the number of rows after which fetching should occur in a
* background thread.
*/
public int getBackgroundFetchAfter();
/**
* Return the maximum number of rows to return in the query.
*/
@@ -516,12 +532,24 @@ public interface SpiQuery<T> extends Query<T> {
* Return the index of the first row to return in the query.
*/
public int getFirstRow();
/**
* Internally set by Ebean when this query must use the DISTINCT keyword.
* <p>
* This does not exclude/remove the use of the id property.
*/
public Query<T> setSqlDistinct(boolean sqlDistinct);
/**
* return true if this query uses DISTINCT.
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
*/
public boolean isDistinctQuery();
/**
* Return true if this query has been specified by a user to use DISTINCT.
*/
public boolean isDistinct();
/**
* Set default select clauses where none have been explicitly defined.
*/
@@ -537,19 +565,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
public Object getId();
/**
* Return the queryListener.
*/
public QueryListener<T> getListener();
/**
* Return true if this query should use its own transaction.
* <p>
* This is true for background fetching and when using QueryListener.
* </p>
*/
public boolean createOwnTransaction();
/**
* Set the generated sql for debug purposes.
*
@@ -15,6 +15,11 @@ import com.avaje.ebeaninternal.server.persist.BatchControl;
*/
public interface SpiTransaction extends Transaction {
/**
* End the transaction when had query only use.
*/
public void endQueryOnly();
/**
* Return the string prefix with the transactin id and label used in logging.
*/
@@ -1,9 +1,9 @@
package com.avaje.ebeaninternal.api;
import java.sql.SQLException;
import java.util.Set;
import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.persist.dml.DmlHandler;
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
@@ -21,20 +21,20 @@ import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
*/
public interface SpiUpdatePlan {
/**
* Return true if the set clause has no columns.
* <p>
* Can occur when the only columns updated have a updatable=false in their
* deployment.
* </p>
*/
public boolean isEmptySetClause();
/**
* Return true if the set clause has no columns.
* <p>
* Can occur when the only columns updated have a updatable=false in their
* deployment.
* </p>
*/
public boolean isEmptySetClause();
/**
* Bind given the request and bean. The bean could be the oldValues bean
* when binding a update or delete where clause with ALL concurrency mode.
*/
public void bindSet(DmlHandler bind, Object bean) throws SQLException;
public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException;
/**
* Return the time this plan was created.
@@ -66,10 +66,10 @@ public interface SpiUpdatePlan {
*/
public Bindable getSet();
/**
* Return the properties that where changed and should be included in the
* update statement.
*/
public Set<String> getProperties();
// /**
// * Return the properties that where changed and should be included in the
// * update statement.
// */
// public Set<String> getProperties();
}
@@ -224,7 +224,7 @@ public interface AutoFetchManager extends NodeUsageListener {
* @param micros
* the query executing time in microseconds
*/
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros);
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros);
/**
@@ -61,6 +61,7 @@ public class AutoFetchManagerFactory {
FileInputStream fi = new FileInputStream(autoFetchFile);
ObjectInputStream ois = new ObjectInputStream(fi);
AutoFetchManager profListener = (AutoFetchManager) ois.readObject();
ois.close();
logger.info("AutoFetch deserialized from file ["+autoFetchFile.getAbsolutePath()+"]");
@@ -527,7 +527,7 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable {
* query in which case the parentNode will be null, or a lazy loading query
* resulting from traversal of the object graph.
*/
public void collectQueryInfo(ObjectGraphNode node, int beans, int micros) {
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
if (node != null){
ObjectGraphOrigin origin = node.getOriginQueryPoint();
@@ -1,19 +1,14 @@
package com.avaje.ebeaninternal.server.autofetch;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.avaje.ebean.bean.NodeUsageCollector;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
import com.avaje.ebean.meta.MetaAutoFetchStatistic.QueryStats;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.text.PathProperties.Props;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -53,30 +48,6 @@ public class Statistics implements Serializable {
}
}
public MetaAutoFetchStatistic createPublicMeta() {
synchronized (monitor) {
StatisticsQuery[] sourceQueryStats = queryStatsMap.values().toArray(new StatisticsQuery[queryStatsMap.size()]);
List<QueryStats> destQueryStats = new ArrayList<QueryStats>(sourceQueryStats.length);
// copy the query statistics
for (int i = 0; i < sourceQueryStats.length; i++) {
destQueryStats.add(sourceQueryStats[i].createPublicMeta());
}
StatisticsNodeUsage[] sourceNodeUsage = nodeUsageMap.values().toArray(new StatisticsNodeUsage[nodeUsageMap.size()]);
List<NodeUsageStats> destNodeUsage = new ArrayList<NodeUsageStats>(sourceNodeUsage.length);
// copy the node usage statistics
for (int i = 0; i < sourceNodeUsage.length; i++) {
destNodeUsage.add(sourceNodeUsage[i].createPublicMeta());
}
return new MetaAutoFetchStatistic(origin, counter, destQueryStats, destNodeUsage);
}
}
/**
* Return the number of times the root query has executed.
* <p>
@@ -127,7 +98,7 @@ public class Statistics implements Serializable {
}
public void collectQueryInfo(ObjectGraphNode node, int beansLoaded, int micros) {
public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) {
synchronized (monitor) {
String key = node.getPath();
@@ -5,16 +5,16 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.bean.NodeUsageCollector;
import com.avaje.ebean.meta.MetaAutoFetchStatistic.NodeUsageStats;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SplitName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Collects usages statistics for a given node in the object graph.
@@ -44,13 +44,6 @@ public class StatisticsNodeUsage implements Serializable {
this.queryTuningAddVersion = queryTuningAddVersion;
}
public NodeUsageStats createPublicMeta() {
synchronized(monitor){
String[] usedProps = aggregateUsed.toArray(new String[aggregateUsed.size()]);
return new NodeUsageStats(path, profileCount, profileUsedCount, usedProps);
}
}
public void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc) {
synchronized(monitor){
@@ -93,9 +86,9 @@ public class StatisticsNodeUsage implements Serializable {
}
if ((modified || queryTuningAddVersion) && desc != null) {
BeanProperty[] versionProps = desc.propertiesVersion();
if (versionProps.length > 0) {
pathProps.addToPath(path, versionProps[0].getName());
BeanProperty versionProp = desc.getVersionProperty();
if (versionProp != null) {
pathProps.addToPath(path, versionProp.getName());
}
}
}

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