Compare commits

...
Author SHA1 Message Date
rbygrave f271ea8a0d [maven-release-plugin] prepare release avaje-ebeanorm-4.4.1 2014-12-01 23:38:42 +13:00
rbygrave 1ced574cd8 bump pom version to 4.4.1-SNAPSHOT 2014-12-01 23:36:41 +13:00
rbygrave 9dd412cd19 #218 - Internal change - modify SequenceIdGenerator to use Long instead of Integer 2014-12-01 23:35:25 +13:00
rbygrave 8237072f5b #217 - ENH: Restore findFutureList() removing its previous deprecated status. Change implementation to use its own PersistenceContext (aka PersistenceContextScope.QUERY) 2014-12-01 23:09:25 +13:00
rbygrave dd30b43d59 #216 - ENH: Add Model.Finder deleteById(id) method 2014-12-01 23:02:58 +13:00
rbygrave 09f8967459 #215 - Remove findPagingList() ... it has been deprecated for a while, please migrate to findPagedList() 2014-12-01 21:52:40 +13:00
rbygrave e3bf3629ee Remove extraneous ServerConfig.enhanceLogLevel which is no longer used 2014-12-01 21:27:27 +13:00
rbygrave b6edd33f9a Fix for #214 - Change default lazyLoadBatchSize from 1 to 10 (in ServerConfig) 2014-12-01 21:16:30 +13:00
rbygrave 3a11a23e01 Fix for #213 - Make PathProperties easier to use - deprecate JsonWriteOptions 2014-12-01 21:10:51 +13:00
rbygrave 370267ab6f Refactor properties bootup, remove GlobalProperties and allow ServerConfig.loadFromProperties() 2014-11-30 17:08:02 +13:00
rbygrave fe65a0ccb9 Change Test - make test more explicit wrt batch lazy loading 2014-11-30 12:55:46 +13:00
rbygrave 2837324a03 Change Test - make test more explicit wrt batch lazy loading 2014-11-30 12:55:16 +13:00
rbygrave a6bd614f87 bump version in readme 2014-11-27 01:20:57 +13:00
rbygrave dfaf4b2f31 [maven-release-plugin] prepare for next development iteration 2014-11-27 01:01:41 +13:00
rbygrave 0f26160e1b [maven-release-plugin] prepare release avaje-ebeanorm-4.3.1 2014-11-27 01:00:53 +13:00
rbygrave cb66aaf66c Change pom to attach javadocs as jar for deploy to central 2014-11-27 00:58:47 +13:00
rbygrave 78b1c05de1 [maven-release-plugin] prepare for next development iteration 2014-11-26 23:54:47 +13:00
rbygrave c70a4f80eb [maven-release-plugin] prepare release avaje-ebeanorm-4.3.0 2014-11-26 23:54:21 +13:00
rbygrave cc788e281f No effective change - javadoc update 2014-11-26 23:53:06 +13:00
rbygrave d55a7b26f8 No effective change - javadoc warnings 2014-11-26 23:08:01 +13:00
rbygrave 2195d7ecbc #211 - ENH: Add support for File as a bean type ... for streaming into/from db 2014-11-26 22:45:33 +13:00
rbygrave 983391af94 #210 - ENH: Add Query.setLazyLoadBatch(int size) method ... for per query control 2014-11-26 20:24:14 +13:00
rbygrave 478053263f #210 - ENH: Add Query.setLazyLoadBatch(int size) method ... for per query control 2014-11-26 20:13:30 +13:00
rbygrave 1cd31e13b9 bump pom to 4.3.0-SNAPSHOT 2014-11-25 23:55:24 +13:00
rbygrave 374d7faae9 bump pom to 4.3.0-SNAPSHOT 2014-11-25 23:54:46 +13:00
rbygrave 6a6cff2236 Fix for #209 - refresh() ... does not refresh OneToMany or ManyToMany properties 2014-11-25 23:40:21 +13:00
rbygrave d5d6ac675b Add support for java.time types for use with @CreatedTimestamp and @UpdatedTimestamp 2014-11-24 20:41:46 +13:00
rbygrave 8ea1f51c7f Remove deprecated Expression.join() methods 2014-11-23 00:53:48 +13:00
rbygrave 6ecb77dcc2 Fix for #206 - Reuse SqlUpdate when binding a list that can vary in size 2014-11-22 23:40:14 +13:00
rbygrave 216806074c Remove PersistenceContextScope.NONE for now 2014-11-22 12:46:40 +13:00
rbygrave 65cb2ae94d #203: ENH: Add Transaction hook ... for preCommit(), postCommit(), postRollback() 2014-11-22 12:14:21 +13:00
rbygrave 6677a6199c Fix for #207: Add support for PeristenceContextScope QUERY and NONE (in addition to the existing TRANSACTION scope) 2014-11-20 00:53:32 +13:00
rbygrave 55eaa0b484 Fix for #204: Revert change from #163 - Add back support for DataSource with autocommit=true 2014-11-19 20:10:50 +13:00
rbygrave 353f662d70 Fix for #205: For JsonContext API wrap checked IOException in runtime JsonIOException 2014-11-19 20:00:31 +13:00
rbygrave c95d76d6f6 Fix for #201: Change BeanPersistListener to use isRegisterFor() method rather than generics type (just like BeanPersistController) 2014-11-19 19:43:34 +13:00
Rob Bygrave e04d6979d1 Merge pull request #202 from ebean-orm/java8
Add support for java.time types, tidy up ScalarTypes
2014-11-19 00:13:37 +13:00
rbygrave b8078083b1 Add MonthDay support, ScalarTypeMonthDay 2014-11-18 23:57:53 +13:00
rbygrave 07cdb69a5b Add Period support via CompoundTypePeriod, rename server/reflect to server/properties 2014-11-18 23:09:26 +13:00
rbygrave 98f6e1e6af Update ScalarTypeMonth and ScalarTypeDayOfWeek 2014-11-18 20:06:13 +13:00
rbygrave fd4d757f87 WIP Java8 types - ScalarTypeDuration etc 2014-11-18 01:20:26 +13:00
rbygrave b71b0fc8e2 WIP Java8 types - ScalarTypeDuration etc 2014-11-17 01:09:43 +13:00
rbygrave 4303bcfd13 No effective change - tidy up on DeployCreateProperties 2014-11-17 00:23:30 +13:00
rbygrave 7f30dc7b1e WIP Java8 types - ScalarTypeDuration etc 2014-11-17 00:05:20 +13:00
rbygrave 4a882b537c #197 - Add Query findEach() findEachWhile() ... as better method names for findVisit(). More consistent with forEach() methods 2014-11-14 21:26:32 +13:00
rbygrave 641385aab5 ScalarTypeLocalDate update - some test ordering issues 2014-11-14 01:01:32 +13:00
rbygrave 33ab7eb7a5 Add ScalarTypeLocalDate 2014-11-13 23:13:51 +13:00
Rob Bygrave f6674f8e3b [maven-release-plugin] prepare for next development iteration 2014-11-13 01:28:18 +13:00
Rob Bygrave 4e8b3f22de [maven-release-plugin] prepare release avaje-ebeanorm-4.2.0 2014-11-13 01:27:58 +13:00
Rob Bygrave 55e43ddc36 Add whitespace for MANIFEST.MF 2014-11-13 01:26:47 +13:00
Rob Bygrave 97d75c06a1 Revert MANIFEST.MF - invalid header 2014-11-13 01:15:47 +13:00
Rob Bygrave adcfa939e4 revert MANIFEST.MF - invalid jackson part 2014-11-13 01:11:07 +13:00
Rob Bygrave 8a4d78a3f5 Fix JSON for ScalarTypePostgresHstore with field name and nulls 2014-11-13 00:49:20 +13:00
Rob Bygrave 04e77d9949 Bump pom to 4.2.0-SNAPSHOT 2014-11-12 23:30:31 +13:00
Rob Bygrave 76e7b8d199 Add to Model the apply(PathProperties) method 2014-11-12 23:06:26 +13:00
Rob Bygrave 00f85a42f8 JSON Refactor - move EJson into text.json package 2014-11-12 22:56:42 +13:00
Rob Bygrave e649a70509 make SimpleTextParser class not public 2014-11-12 22:35:48 +13:00
Rob Bygrave 2ec50d2c71 Add query.apply(PathProperties) 2014-11-12 22:24:37 +13:00
Rob Bygrave eaee3a9c02 JSON refactor - tidy up 2014-11-12 22:10:28 +13:00
Rob Bygrave 221272328c WIP JSON refactor - tidy up ScalarTypes 2014-11-12 00:17:24 +13:00
Rob Bygrave 1677917465 Move from javax.json to jackson-core for parser/generator 2014-11-10 20:31:11 +13:00
Rob Bygrave bfefdbac41 Merge from JSON refactoring branch 2014-11-06 23:32:27 +13:00
Rob Bygrave 58755a51a9 JSON Refactor - compound type support 2014-11-06 23:18:20 +13:00
Rob Bygrave b313809894 WIP json refactor - initial tidy 2014-11-05 23:15:09 +13:00
rbygrave 0f3b1eb812 Tidy up test - TestInfoOneToOne, oneToOne cascade update 2014-10-29 23:45:24 +13:00
rbygrave 306cc4adf4 Add test for oneToOne cascade update 2014-10-29 23:43:11 +13:00
rbygrave ed88978173 No effective change - javadoc tweak on params 2014-10-29 23:41:40 +13:00
rbygrave ae266968b4 [maven-release-plugin] prepare for next development iteration 2014-10-29 00:11:43 +13:00
rbygrave aa86454cc4 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.9 2014-10-29 00:11:20 +13:00
rbygrave c99bec4940 Add First/Second/SuperSecond test 2014-10-29 00:07:25 +13:00
rbygrave 7426274ef6 bump agent to 4.1.9 2014-10-28 23:46:20 +13:00
Rob Bygrave 130b7e3327 Add another self referencing test - SelfParent 2014-10-20 23:06:50 +13:00
Rob Bygrave de201715fc #196 - Add support for using java.net.InetAddress in entity beans 2014-10-20 22:36:18 +13:00
Rob Bygrave 241f6f3d6d #196 - Add support for using java.net.InetAddress in entity beans 2014-10-20 22:31:22 +13:00
Rob Bygrave 1210aca027 Add test for SqlUpdate in various transaction scopes 2014-10-20 20:18:17 +13:00
Rob Bygrave e653c34211 Fix for #188 - ClassPathSearch not searching in running Jar 2014-10-16 23:15:16 +13:00
Rob Bygrave 131750cf85 No effective change - format 2014-10-16 22:58:13 +13:00
Rob Bygrave 17b16fd5d9 Merge pull request #194 from nedge/master
FIX for ManyToOne targetType & OSGI MANIFEST
2014-10-15 19:55:17 +13:00
Eddie Mc Greal 1065a84709 FIX for ManyToOne targetType & OSGI MANIFEST 2014-10-14 08:10:31 +02:00
Rob Bygrave 13791a2e58 Update version in readme 2014-10-08 22:13:48 +13:00
Rob Bygrave 277703d750 [maven-release-plugin] prepare for next development iteration 2014-10-08 21:44:25 +13:00
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 0ceda6406e WIP on JSON refactor 2014-06-28 03:09:10 +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
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
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
891 changed files with 43186 additions and 28578 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.5</version>
<version>4.3.1</version>
</dependency>
+64 -26
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.3.1-RC1</version>
<version>4.4.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -48,6 +48,18 @@
<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>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
@@ -77,13 +89,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,14 +99,14 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>3.2.2</version>
<version>4.1.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.153</version>
<version>1.4.182</version>
<scope>test</scope>
</dependency>
@@ -136,9 +141,16 @@
<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>
@@ -167,23 +179,21 @@
<!-- Enhance the meta beans -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</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.2</version>
<version>4.1.9</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>
@@ -213,9 +223,37 @@
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</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> -->
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</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();
}
File diff suppressed because it is too large Load Diff
+209 -209
View File
@@ -7,6 +7,7 @@ import java.util.Map;
import java.util.Set;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.cache.ServerCacheManager;
@@ -48,18 +49,18 @@ import com.avaje.ebean.text.json.JsonContext;
* Example: Get a EbeanServer
* </p>
*
* <pre class="code">
* <pre>{@code
* // Get access to the Human Resources EbeanServer/Database
* EbeanServer hrServer = Ebean.getServer(&quot;HR&quot;);
* EbeanServer hrServer = Ebean.getServer("HR");
*
*
* // fetch contact 3 from the HR database Contact contact =
* hrServer.find(Contact.class, new Integer(3));
*
* contact.setStatus(&quot;INACTIVE&quot;); ...
* contact.setStatus("INACTIVE"); ...
*
* // save the contact back to the HR database hrServer.save(contact);
* </pre>
* }</pre>
*
* <p>
* <b>EbeanServer has more API than Ebean</b><br/>
@@ -85,9 +86,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>
@@ -124,7 +128,6 @@ public interface EbeanServer {
*/
public MetaInfoManager getMetaInfoManager();
/**
* Return the BeanState for a given entity bean.
* <p>
@@ -188,15 +191,15 @@ public interface EbeanServer {
* moved to {@link #createNamedQuery(Class, String)}.
* </p>
*
* <pre class="code">
* <pre>{@code
* EbeanServer ebeanServer = ... ;
* String q = "find order fetch details where status = :st";
*
* List&lt;Order&gt; newOrders
* List<Order> newOrders
* = ebeanServer.createQuery(Order.class, q)
* .setParameter("st", Order.Status.NEW)
* .findList();
* </pre>
* }</pre>
*
* @param query
* the object query
@@ -204,16 +207,14 @@ public interface EbeanServer {
public <T> Query<T> createQuery(Class<T> beanType, String query);
/**
* Create a query for an entity bean (refer {@link Ebean#createQuery(Class)}
* ).
* Create a query for an entity bean (refer {@link Ebean#createQuery(Class)}).
*
* @see Ebean#createQuery(Class)
*/
public <T> Query<T> createQuery(Class<T> beanType);
/**
* Create a query for a type of entity bean (the same as
* {@link EbeanServer#createQuery(Class)}).
* Create a query for a type of entity bean (the same as {@link EbeanServer#createQuery(Class)}).
*/
public <T> Query<T> find(Class<T> beanType);
@@ -269,8 +270,7 @@ public interface EbeanServer {
public SqlQuery createSqlQuery(String sql);
/**
* Create a named sql query (refer {@link Ebean#createNamedSqlQuery(String)}
* ).
* Create a named sql query (refer {@link Ebean#createNamedSqlQuery(String)}).
* <p>
* The query statement will be defined in a deployment orm xml file.
* </p>
@@ -280,8 +280,7 @@ public interface EbeanServer {
public SqlQuery createNamedSqlQuery(String namedQuery);
/**
* Create a sql update for executing native dml statements (refer
* {@link Ebean#createSqlUpdate(String)}).
* Create a sql update for executing native dml statements (refer {@link Ebean#createSqlUpdate(String)}).
*
* @see Ebean#createSqlUpdate(String)
*/
@@ -293,8 +292,7 @@ public interface EbeanServer {
public CallableSql createCallableSql(String callableSql);
/**
* Create a named sql update (refer {@link Ebean#createNamedSqlUpdate(String)}
* ).
* Create a named sql update (refer {@link Ebean#createNamedSqlUpdate(String)}).
* <p>
* The statement (an Insert Update or Delete statement) will be defined in a
* deployment orm xml file.
@@ -304,6 +302,17 @@ public interface EbeanServer {
*/
public SqlUpdate createNamedSqlUpdate(String namedQuery);
/**
* Register a TransactionCallback on the currently active transaction.
* <p/>
* If there is no currently active transaction then a PersistenceException is thrown.
*
* @param transactionCallback The transaction callback to be registered with the current transaction.
*
* @throws PersistenceException If there is no currently active transaction
*/
public void register(TransactionCallback transactionCallback) throws PersistenceException;
/**
* Create a new transaction that is not held in TransactionThreadLocal.
* <p>
@@ -335,8 +344,7 @@ public interface EbeanServer {
public Transaction beginTransaction(TxIsolation isolation);
/**
* Returns the current transaction or null if there is no current transaction
* in scope.
* Returns the current transaction or null if there is no current transaction in scope.
*/
public Transaction currentTransaction();
@@ -364,15 +372,19 @@ public interface EbeanServer {
* <p>
* Code example:
*
* <pre class="code">
* Ebean.startTransaction(); try { // do some fetching
* and or persisting
* <pre>{@code
* ebeanServer.startTransaction();
* try {
* // do some fetching and or persisting ...
*
* // commit at the end Ebean.commitTransaction();
* // commit at the end
* ebeanServer.commitTransaction();
*
* } finally { // if commit didn't occur then rollback the transaction
* Ebean.endTransaction(); }
* </pre>
* } finally {
* // if commit didn't occur then rollback the transaction
* ebeanServer.endTransaction();
* }
* }</pre>
*
* </p>
*
@@ -410,14 +422,27 @@ 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>{@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);
public <T> T getReference(Class<T> beanType, Object id);
/**
* Return the number of 'top level' or 'root' entities this query should
@@ -428,21 +453,45 @@ 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.
*
* @see Query#findEach(QueryEachConsumer)
*/
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t);
public <T> void findEach(Query<T> query, QueryEachConsumer<T> consumer, 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.
*
* @see Query#findEachWhile(QueryEachWhileConsumer)
*/
public <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction transaction);
/**
* Deprecated in favor of #findEachWhile which is functionally exactly the same
* but has a much better name.
* <p>
* 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.
* </p>
*
* @deprecated
*/
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction transaction);
/**
* Execute a query returning a list of beans.
@@ -473,11 +522,11 @@ public interface EbeanServer {
*
* @param query
* the query to execute the row count on
* @param t
* @param transaction
* 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.
@@ -489,27 +538,29 @@ public interface EbeanServer {
*
* @param query
* the query to execute the fetch Id's on
* @param t
* @param transaction
* 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.
* Execute find list query in a background thread returning a FutureList object.
* <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).
* </p>
*
* execution status (isDone etc) and get the value (with or without a timeout).
* <p>
* This query will execute in it's own PersistenceContext and using its own transaction.
* What that means is that it will not share any bean instances with other queries.
*
*
* @param query
* the query to execute in the background
* @param t
* @param transaction
* the transaction (can be null).
* @return a Future object for the list result of the query
*/
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.
@@ -518,19 +569,35 @@ public interface EbeanServer {
* execution status (isDone etc) and get the value (with or without a
* timeout).
* </p>
*
*
* @param query
* the query to execute in the background
* @param t
* @param transaction
* the transaction (can be null).
* @return a Future object for the list result of the query
*/
public SqlFutureList findFutureList(SqlQuery query, Transaction t);
public SqlFutureList findFutureList(SqlQuery query, Transaction transaction);
/**
* Find using a PagingList with explicit transaction and 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.
@@ -677,7 +744,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.
@@ -704,7 +771,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.
@@ -715,7 +782,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
@@ -784,205 +851,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.
* <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>
* Save all the beans in the collection with an explicit transaction.
*/
public void update(Object bean);
public int save(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Force an update of the non-null properties of the bean with an explicit
* transaction.
* 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.
* </p>
* This is used so that when a bean that is otherwise unmodified is updated the version
* property is updated.
* <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>
* 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>{@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 update(Object bean, Transaction t);
public void markAsDirty(Object bean);
/**
* Force an update using the bean explicitly stating the properties to update.
* 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>
* 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.
* <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">
* <pre>{@code
*
* Customer c = new Customer();
* c.setId(7);
* c.setName(&quot;ModifiedNameNoOCC&quot;);
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName("ModifiedNameNoOCC");
* ebeanServer.update(customer);
*
* // 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);
* }</pre>
*
* // by default the Non-null properties
* // are included in the update
* ebeanServer.update(c);
*
* </pre>
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public void update(Object bean, Set<String> updateProps);
public void update(Object bean) throws OptimisticLockException;
/**
* 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>
* Update a bean additionally specifying a transaction.
*/
public void update(Object bean, Set<String> updateProps, Transaction t);
public void update(Object bean, Transaction t) throws OptimisticLockException;
/**
* 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.
@@ -1154,8 +1145,17 @@ public interface EbeanServer {
public void runCacheWarming(Class<?> beanType);
/**
* Create a JsonContext that will use the default configuration options.
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
public JsonContext createJsonContext();
/**
* Return the JsonContext for reading/writing JSON.
* <p>
* This instance is safe to be used concurrently by multiple threads and this method is cheap to call.
* </p>
*/
public JsonContext json();
}
@@ -1,13 +1,12 @@
package com.avaje.ebean;
import javax.persistence.PersistenceException;
import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.lang.reflect.Constructor;
import java.util.Properties;
/**
* Creates EbeanServer instances.
@@ -26,39 +25,46 @@ 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();
private static BootupEbeanManager bootupEbeanManager;
/**
* Initialise the container with clustering configuration.
*
* Call this prior to creating any EbeanServer instances or alternatively set the
* ContainerConfig on the ServerConfig when creating the first EbeanServer instance.
*/
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
getServerFactory(containerConfig);
}
/**
* Create using ebean.properties to configure the server.
*/
public static EbeanServer create(String name) {
public static synchronized EbeanServer create(String name) {
EbeanServer server = serverFactory.createServer(name);
return server;
// construct based on loading properties files
// and if invoked by Ebean then it handles registration
BootupEbeanManager serverFactory = getServerFactory(null);
return serverFactory.createServer(name);
}
/**
* Create using the ServerConfig object to configure the server.
*/
public static EbeanServer create(ServerConfig config) {
public static synchronized EbeanServer create(ServerConfig config) {
if (config.getName() == null) {
throw new PersistenceException("The name is null (it is required)");
}
EbeanServer server = serverFactory.createServer(config);
EbeanServer server = createInternal(config);
if (config.isDefaultServer()) {
GlobalProperties.setSkipPrimaryServer(true);
PrimaryServer.setSkip(true);
}
if (config.isRegister()) {
Ebean.register(server, config.isDefaultServer());
@@ -67,30 +73,45 @@ public class EbeanServerFactory {
return server;
}
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);
private static EbeanServer createInternal(ServerConfig config) {
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);
return getServerFactory(config.getContainerConfig()).createServer(config);
}
} catch (InterruptedException e) {
String m = "Interrupting debug.start.delay of " + delaySecs;
logger.error(m, e);
}
/**
* Get the BootupEbeanManager initialising it if necessary.
*
* @param containerConfig the configuration controlling clustering communication
*/
private static BootupEbeanManager getServerFactory(ContainerConfig containerConfig) {
if (bootupEbeanManager != null) {
return bootupEbeanManager;
}
if (containerConfig == null) {
// effectively load configuration from ebean.properties
Properties properties = PrimaryServer.getProperties();
containerConfig = new ContainerConfig();
containerConfig.loadFromProperties(properties);
}
bootupEbeanManager = createServerFactory(containerConfig);
return bootupEbeanManager;
}
/**
* Create the container instance using the configuration.
*/
private static BootupEbeanManager createServerFactory(ContainerConfig containerConfig) {
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
String implClassName = System.getProperty("ebean.serverfactory", dflt);
try {
// use a client side implementation?
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
Class<?> cls = Class.forName(implClassName);
Constructor<?> constructor = cls.getConstructor(ContainerConfig.class);
return (BootupEbeanManager) constructor.newInstance(containerConfig);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
@@ -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.
*/
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
@@ -87,6 +89,11 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setOrderBy(String orderBy);
/**
* Apply the path properties to the query replacing the select and fetch clauses.
*/
public Query<T> apply(PathProperties pathProperties);
/**
* Execute the query iterating over the results.
*
@@ -95,9 +102,25 @@ public interface ExpressionList<T> extends Serializable {
public QueryIterator<T> findIterate();
/**
* Execute the query visiting the results.
*
* @see Query#findVisit(QueryResultVisitor)
* Execute the query process the beans one at a time.
*
* @see Query#findEach(QueryEachConsumer)
*/
public void findEach(QueryEachConsumer<T> consumer);
/**
* Execute the query processing the beans one at a time with the ability to
* stop processing before reading all the beans.
*
* @see Query#findEachWhile(QueryEachWhileConsumer)
*/
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
/**
* Deprecated in favor of #findEachWhile which is functionally exactly the same
* but has a much better name.
*
* @deprecated
*/
public void findVisit(QueryResultVisitor<T> visitor);
@@ -186,24 +209,29 @@ public interface ExpressionList<T> extends Serializable {
public FutureList<T> findFutureList();
/**
* Return a PagingList for this query.
* Return a PagedList 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.
* 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>
* 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.
* 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 fetched per Page
*
* The number of beans to return per page.
* @return The PagedList
*/
public PagingList<T> findPagingList(int pageSize);
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Add some filter predicate expressions to the many property.
*/
public ExpressionList<T> filterMany(String prop);
/**
@@ -214,22 +242,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> select(String properties);
/**
* Specify a property (associated bean) to join and <em>fetch</em> including
* all its properties.
*
* @see Query#join(String)
*/
public Query<T> join(String assocProperties);
/**
* Specify a property (associated bean) to join and <em>fetch</em> with its
* specific properties to include (aka partial object).
*
* @see Query#join(String,String)
*/
public Query<T> join(String assocProperty, String assocProperties);
/**
* Set the first row to fetch.
*
@@ -244,14 +256,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,15 +263,6 @@ public interface ExpressionList<T> extends Serializable {
*/
public Query<T> setMapKey(String mapKey);
/**
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}.
* Set a QueryListener for bean by bean processing.
*
* @see Query#setListener(QueryListener)
* @deprecated Migrate to {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set to true to use the query for executing this query.
*
+30 -7
View File
@@ -1,7 +1,11 @@
package com.avaje.ebean;
import javax.persistence.PersistenceException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* FutureList represents the result of a background query execution that will
@@ -15,13 +19,13 @@ import java.util.concurrent.Future;
* A simple example:
* </p>
*
* <pre class="code">
* <pre>{@code
* // create a query to find all orders
* Query&lt;Order&gt; query = Ebean.find(Order.class);
* Query<Order> query = Ebean.find(Order.class);
*
* // execute the query in a background thread
* // immediately returning the futureList
* FutureList&lt;Order&gt; futureList = query.findFutureList();
* FutureList<Order> futureList = query.findFutureList();
*
* // do something else ...
*
@@ -35,13 +39,11 @@ import java.util.concurrent.Future;
*
* if (!futureList.isCancelled()){
* // wait for the query to finish and return the list
* List&lt;Order&gt; list = futureList.get();
* List<Order> list = futureList.get();
* ...
* }
*
* </pre>
*
* @author rbygrave
* }</pre>
*/
public interface FutureList<T> extends Future<List<T>> {
@@ -50,4 +52,25 @@ public interface FutureList<T> extends Future<List<T>> {
*/
public Query<T> getQuery();
/**
* Same as {@link #get()} but wraps InterruptedException and ExecutionException in the
* unchecked PersistenceException.
*
* @return The query list result
*
* @throws PersistenceException when a InterruptedException or ExecutionException occurs.
*/
public List<T> getUnchecked();
/**
* Same as {@link #get(long, java.util.concurrent.TimeUnit)} but wraps InterruptedException
* and ExecutionException in the unchecked PersistenceException.
*
* @return The query list result
*
* @throws TimeoutException if the wait timed out
* @throws PersistenceException if a InterruptedException or ExecutionException occurs.
*/
public List<T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException;
}
+636
View File
@@ -0,0 +1,636 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
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);
}
/**
* Delete a bean by Id.
*/
public void deleteById(I id) {
db().delete(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>
* Equivalent to {@link EbeanServer#find(Class, Object)}
*/
public T byId(I id) {
return db().find(type, id);
}
/**
* Creates an entity reference for this ID.
*
* <p>
* Equivalent 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>
* Equivalent to {@link EbeanServer#filter(Class)}
*/
public Filter<T> filter() {
return db().filter(type);
}
/**
* Creates a query.
* <p>
* Equivalent to {@link EbeanServer#find(Class)}
*/
public Query<T> query() {
return db().find(type);
}
/**
* Creates a query applying the path properties to set the select and fetch clauses.
*/
public Query<T> apply(PathProperties pathProperties) {
return db().find(type).apply(pathProperties);
}
/**
* 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>
* Equivalent to {@link Query#findIds()}
*/
public List<Object> findIds() {
return query().findIds();
}
/**
* Execute the query consuming each bean one at a time.
* <p>
* This is generally used to process large queries where unlike findList
* you do not want to hold all the results in memory at once but instead
* process them one at a time (requiring far less memory).
* </p>
* Equivalent to {@link Query#findEach(QueryEachConsumer)}
*/
public void findEach(QueryEachConsumer<T> consumer) {
query().findEach(consumer);
}
/**
* Execute the query consuming each bean one at a time.
* <p>
* This is similar to #findEach except that you return boolean
* true to continue processing beans and return false to stop
* processing early.
* </p>
* <p>
* This is generally used to process large queries where unlike findList
* you do not want to hold all the results in memory at once but instead
* process them one at a time (requiring far less memory).
* </p>
* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)}
*/
public void findEachWhile(QueryEachWhileConsumer<T> consumer) {
query().findEachWhile(consumer);
}
/**
* Retrieves all entities of the given type.
* <p>
* The same as {@link #all()}
* <p>
* Equivalent to {@link Query#findList()}
*/
public List<T> findList() {
return query().findList();
}
/**
* Returns all the entities of the given type as a set.
* <p>
* Equivalent to {@link Query#findSet()}
*/
public Set<T> findSet() {
return query().findSet();
}
/**
* Retrieves all entities of the given type as a map of objects.
* <p>
* Equivalent 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>
* Equivalent 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>
* Equivalent to {@link Query#findPagedList(int, int)}
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return query().findPagedList(pageIndex, pageSize);
}
/**
* Executes a find row count query in a background thread.
* <p>
* Equivalent to {@link Query#findFutureRowCount()}
*/
public FutureRowCount<T> findFutureRowCount() {
return query().findFutureRowCount();
}
/**
* Returns the total number of entities for this type. *
* <p>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent 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>
* Equivalent to {@link EbeanServer#createQuery(Class, String)}
*/
public Query<T> setQuery(String oql) {
return db().createQuery(type, oql);
}
/**
* Create and return a new query based on the <code>RawSql</code>.
* <p>
* Equivalent 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);
}
}
}
@@ -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);
}
@@ -1,127 +0,0 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* Used to page through a query result rather than fetching all the results in a
* single query.
* <p>
* Has the ability to use background threads to 'fetch ahead' the next page and
* get the total row count.
* </p>
* <p>
* If you are building a stateless web application and not keeping the
* PagingList over multiple requests then there is not much to be gained in
* using PagingList. Instead you can just use {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)}.
* </p>
*
* <p>
* If you are using PagingList is a stateful web application where the
* PagingList is held over multiple requests then PagingList provides the extra
* benefits of
* <ul>
* <li>Fetch ahead - automatically fetching the next page via background query
* execution</li>
* <li>Automatic propagation of the persistence context</li>
* </ul>
* </p>
* <p>
* So with PagingList when you use Page 2 it can automatically fetch Page 3 data
* in the background (using a findFutureList() query). It also automatically
* propagates the persistence context so that all the queries executed by the
* PagingList all use the same persistence context.
* </p>
*
* <pre>
* PagingList&lt;TOne&gt; pagingList =
* Ebean.find(TOne.class)
* .where().gt(&quot;name&quot;, &quot;2&quot;)
* .findPagingList(10);
*
* // get the row count in the background...
* // ... otherwise it is fetched on demand
* // ... when getRowCount() or getPageCount()
* // ... is called
* pagingList.getFutureRowCount();
*
* // get the first page
* Page&lt;TOne&gt; page = pagingList.getPage(0);
*
* // get the beans from the page as a list
* List&lt;TOne&gt; list = page.getList();
* </pre>
*
* @author rbygrave
*
* @param <T>
* the entity bean type
*/
public interface PagingList<T> {
/**
* Refresh will clear all the pages and row count forcing them to be
* re-fetched when next required.
*/
public void refresh();
// public void fetchAll();
// public String? getOrderBy();
// public void setOrderBy(String?);
/**
* By default fetchAhead is true so use this to turn off fetchAhead.
* <p>
* Set this to false if you don't want to fetch ahead using background
* fetching.
* <p>
* If set to true (or left as to default) then the next page is fetched in the
* background as soon as the list is accessed.
* </p>
*/
public PagingList<T> setFetchAhead(boolean fetchAhead);
/**
* Return the Future for getting the total row count.
*/
public Future<Integer> getFutureRowCount();
/**
* Return the data for all the pages in the form of a single List.
* <p>
* Iterating through this list will automatically fire the paging queries as
* required.
* </p>
*/
public List<T> getAsList();
/**
* Return the page size. This is the number of rows per page.
*/
public int getPageSize();
/**
* Return the total row count.
* <p>
* This gets the result from getFutureRowCount and will wait until that query
* has completed.
* </p>
*/
public int getTotalRowCount();
/**
* Return the total page count.
* <p>
* This is based on the total row count. This will wait until the row count
* has returned if it has not already.
* </p>
*/
public int getTotalPageCount();
/**
* Return the page for a given page position (starting at 0).
*/
public Page<T> getPage(int i);
}
@@ -0,0 +1,35 @@
package com.avaje.ebean;
/**
* Defines the scope for PersistenceContext.
* <p/>
* Ebean has traditionally used Transaction scope for the PersistenceContext. This is used to change the scope to
* use (by default) and explicitly set the scope to use for an individual query.
*
* @see com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)
* @see com.avaje.ebean.Query#setPersistenceContextScope(PersistenceContextScope)
*/
public enum PersistenceContextScope {
/**
* PersistenceContext is scoped to the transaction.
* <p/>
* If a transaction spans 2 or more queries that fetch the same bean in terms of same type
* and same Id value then they share the same bean instance.
* <p/>
* You may want to change to use QUERY scope when you want a query executing in a transaction to effectively
* ignore beans that have already been loaded (by other queries in the same transaction) and instead get a
* 'fresh copy' of the bean.
*/
TRANSACTION,
/**
* PersistenceContext is scoped to the query.
* <p/>
* This means that for this query running in an existing transaction then it will effectively ignore any beans
* that have already been queried/loaded by prior queries in the same transaction.
* <p/>
* You may use QUERY scope on a query that is executed in a transaction and you want to get a 'fresh copy' of the bean.
*/
QUERY
}
@@ -0,0 +1,55 @@
package com.avaje.ebean;
import com.avaje.ebean.config.PropertyMap;
import java.util.Properties;
/**
* Provides singleton state for the default server.
* <p/>
* Intended for internal use as part of bootup, construction, registration of the default server.
*/
class PrimaryServer {
private static Properties globalProperties;
private static String defaultServerName;
private static boolean skip;
/**
* Set whether to skip automatically creating the primary server.
*/
static synchronized void setSkip(boolean skip) {
PrimaryServer.skip = skip;
}
/**
* Return true to skip automatically creating the primary server.
*/
static synchronized boolean isSkip() {
return skip;
}
/**
* Return the default server name.
*/
static synchronized String getPrimaryServerName() {
getProperties();
return defaultServerName;
}
/**
* Return the default configuration Properties.
*/
static synchronized Properties getProperties() {
if (globalProperties == null) {
globalProperties = PropertyMap.defaultProperties();
}
defaultServerName = globalProperties.getProperty("datasource.default");
if (defaultServerName == null) {
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
}
return globalProperties;
}
}
+124 -95
View File
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@@ -269,26 +271,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.
*/
@@ -319,6 +301,21 @@ public interface Query<T> extends Serializable {
*/
public Query<T> copy();
/**
* Specify the PersistenceContextScope to use for this query.
* <p/>
* When this is not set the 'default' configured on {@link com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)}
* is used - this value defaults to {@link com.avaje.ebean.PersistenceContextScope#TRANSACTION}.
* <p/>
* Note that the same persistence Context is used for subsequent lazy loading and query join queries.
* <p/>
* Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for
* queries executed as #findIterate, #findEach, #findEachWhile.
*
* @param scope The scope to use for this query and subsequent lazy loading.
*/
public Query<T> setPersistenceContextScope(PersistenceContextScope scope);
/**
* Return the ExpressionFactory used by this query.
*/
@@ -343,22 +340,32 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setAutofetch(boolean autofetch);
/**
* Set the default lazy loading batch size to use.
* <p>
* When lazy loading is invoked on beans loaded by this query then this sets the
* batch size used to load those beans.
*
* @param lazyLoadBatchSize the number of beans to lazy load in a single batch
*/
public Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize);
/**
* Explicitly set a comma delimited list of the properties to fetch on the
* 'main' entity bean (aka partial object). Note that '*' means all
* properties.
*
*
* <pre class="code">
* Query&lt;Customer&gt; query = Ebean.createQuery(Customer.class);
*
*
* // Only fetch the customer id, name and status.
* // This is described as a &quot;Partial Object&quot;
* query.select(&quot;name, status&quot;);
* query.where(&quot;lower(name) like :custname&quot;).setParameter(&quot;custname&quot;, &quot;rob%&quot;);
*
*
* List&lt;Customer&gt; customerList = query.findList();
* </pre>
*
*
* @param fetchProperties
* the properties to fetch for this bean (* = all properties).
*/
@@ -433,6 +440,14 @@ public interface Query<T> extends Serializable {
*/
public Query<T> fetch(String path, FetchConfig joinConfig);
/**
* Apply the path properties replacing the select and fetch clauses.
* <p>
* This is typically used when the PathProperties is applied to both the query and the JSON output.
* </p>
*/
public Query<T> apply(PathProperties pathProperties);
/**
* Execute the query returning the list of Id's.
* <p>
@@ -456,37 +471,89 @@ public interface Query<T> extends Serializable {
*/
public QueryIterator<T> findIterate();
/**
* This is deprecated in favor of #findEachWhile.
* <p>
* This is functionally exactly the same as #findEachWhile. It is
* replaced by findEachWhile because the method name is much better.
* </p>
*
* @param visitor
* the visitor used to process the queried beans.
*
* @deprecated
*/
public void findVisit(QueryResultVisitor<T> visitor);
/**
* Execute the query processing the beans one at a time.
* <p>
* This method is appropriate to process very large query results as the
* beans are consumed one at a time and do not need to be held in memory
* (unlike #findList #findSet etc)
* </p>
* <p>
* Compared with #findEachWhile this will always process all the beans where as
* #findEachWhile provides a way to stop processing the query result early before
* all the beans have been read.
* </p>
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the QueryEachConsumer (SAM) interface which is better suited to use
* with Java8 closures.
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* });
* </pre>
*
* @param consumer
* the consumer used to process the queried beans.
*/
public void findEach(QueryEachConsumer<T> consumer);
/**
* Execute the query using callbacks to a visitor to process the resulting
* beans one at a time.
* <p>
* Similar to findIterate() this query method does not require all the result
* beans to be all held in memory at once and as such is useful for processing
* large queries.
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the QueryEachWhileConsumer (SAM) interface which is better suited to use
* with Java8 closures.
* </p>
*
*
* <pre class="code">
*
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit(new QueryResultVisitor&lt;Customer&gt;() {
*
* public boolean accept(Customer customer) {
*
* query.findEachWhile((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* return true;
* }
*
* // return true to continue processing or false to stop
* return (customer.getId() < 40);
* });
* </pre>
*
* @param visitor
* the visitor used to process the queried beans.
*
* @param consumer
* the consumer used to process the queried beans.
*/
public void findVisit(QueryResultVisitor<T> visitor);
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
/**
* Execute the query returning the list of objects.
@@ -609,33 +676,34 @@ 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).
* This query will execute in it's own PersistenceContext and using its own transaction.
* What that means is that it will not share any bean instances with other queries.
* </p>
*
*
* @return a Future object for the list result of the query
*/
public FutureList<T> findFutureList();
/**
* Return a PagingList for this query.
* Return a PagedList 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.
* 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>
* 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.
* 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 fetched per Page
*
* The number of beans to return per page.
* @return The PagedList
*/
public PagingList<T> findPagingList(int pageSize);
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Set a named bind parameter. Named parameters have a colon to prefix the
@@ -683,36 +751,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setParameter(int position, Object value);
/**
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
* <p>
* Set a listener to process the query on a row by row basis.
* </p>
* <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>
* @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
*/
public Query<T> setListener(QueryListener<T> queryListener);
/**
* Set the Id value to query. This is used with findUnique().
* <p>
@@ -986,13 +1024,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>
@@ -1072,16 +1103,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();
}
@@ -0,0 +1,41 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* });
* </pre>
*
* @param <T>
* the type of entity bean being queried.
*/
public interface QueryEachConsumer<T> {
/**
* Process the bean.
*
* @param bean
* the entity bean to process
*/
public void accept(T bean);
}
@@ -0,0 +1,45 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
* <p/>
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findEachWhile((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
*
* // return true to continue processing or false to stop
* return (customer.getId() < 40);
* });
* </pre>
*
* @param <T> the type of entity bean being queried.
*/
public interface QueryEachWhileConsumer<T> {
/**
* Process the bean and return true if you want to continue processing more
* beans. Return false if you want to stop processing further.
*
* @param bean the entity bean to process
* @return true to continue processing more beans or false to stop.
*/
public boolean accept(T bean);
}
@@ -1,43 +0,0 @@
package com.avaje.ebean;
/**
* Deprecated, please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
* <p>
* Provides a mechanism for processing a query one bean at a time.
* </p>
* <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
* @deprecated Please migrate to using {@link #findIterate()} or
* {@link #findVisit(QueryResultVisitor)}
*/
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;
}
}
@@ -1,6 +1,6 @@
package com.avaje.ebean;
public class SimpleTextParser {
class SimpleTextParser {
private final String oql;
private final char[] chars;
@@ -10,9 +10,7 @@ public class SimpleTextParser {
private String word;
private String lowerWord;
private int openParenthesisCount;
public SimpleTextParser(String oql) {
SimpleTextParser(String oql) {
this.oql = oql;
this.chars = oql.toCharArray();
this.eof = oql.length();
@@ -22,10 +20,6 @@ public class SimpleTextParser {
return pos;
}
public String getOql() {
return oql;
}
public String getWord() {
return word;
}
@@ -104,7 +98,7 @@ public class SimpleTextParser {
private void moveToClose() {
pos++;
openParenthesisCount = 0;
int openParenthesisCount = 0;
for (; pos < eof; pos++) {
char c = chars[pos];
@@ -143,11 +137,7 @@ public class SimpleTextParser {
if (isOperator(c)) {
return !isOperator;
}
if (c == '(') {
return true;
}
return isOperator;
return c == '(' || isOperator;
}
private boolean isOperator(char c) {
@@ -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();
@@ -91,6 +91,11 @@ public interface SqlUpdate {
*/
public String getSql();
/**
* Return the generated sql that has named parameters converted to positioned parameters.
*/
public String getGeneratedSql();
/**
* Return the timeout used to execute this statement.
*/
@@ -35,12 +35,10 @@ public interface Transaction extends Closeable {
*/
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();
/**
* Register a TransactionCallback with this transaction.
*/
public void register(TransactionCallback callback);
/**
* Return true if this transaction is read only.
@@ -0,0 +1,34 @@
package com.avaje.ebean;
/**
* Provides a callback that can be registered with a Transaction.
* <p/>
* The callback methods are called just prior to and after the transaction performs a commit or rollback.
* <p/>
* A typical use of TransactionCallback would be to clean up non-transactional resources like files. For example,
* when processing files on postCommit/postRollback clean up the associated files. As another example when
* on postCommit of a delete remove associated resources from the file system or remote service.
*/
public interface TransactionCallback {
/**
* Perform processing just prior to the transaction commit.
*/
void preCommit();
/**
* Perform processing just after the transaction commit.
*/
void postCommit();
/**
* Perform processing just prior to the transaction rollback.
*/
void preRollback();
/**
* Perform processing just after the transaction rollback.
*/
void postRollback();
}
@@ -0,0 +1,42 @@
package com.avaje.ebean;
/**
* Adapter that can be extended for easier implementation of TransactionCallback.
* <p/>
* Provides 'no operation' implementation for each of the TransactionCallback methods. It is expected that this
* class is extended and override the methods you need to.
*/
public abstract class TransactionCallbackAdapter implements TransactionCallback {
/**
* Perform processing just prior to the transaction commit.
*/
@Override
public void preCommit() {
// do nothing - override as necessary
}
/**
* Perform processing just after the transaction commit.
*/
@Override
public void postCommit() {
// do nothing - override as necessary
}
/**
* Perform processing just prior to the transaction rollback.
*/
@Override
public void preRollback() {
// do nothing - override as necessary
}
/**
* Perform processing just after the transaction rollback.
*/
@Override
public void postRollback() {
// do nothing - override as necessary
}
}
+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,24 +31,29 @@ public interface BeanCollection<E> extends Serializable {
ALL
}
/**
* Reset the collection back to an empty state ready for reloading.
* <p>
* This is done as part of bean refresh.
*/
public void reset(EntityBean ownerBean, String propertyName);
/**
* Return true if the collection is empty and untouched. Used to detect if a
* collection was 'cleared' deliberately or just un-initialised.
*/
public boolean isEmptyAndUntouched();
/**
* Return the bean that owns this collection.
*/
public Object getOwnerBean();
public EntityBean getOwnerBean();
/**
* Return the bean property name this collection represents.
*/
public String getPropertyName();
/**
* Return the index position of this collection in the lazy/query loader.
* <p>
* Used for batch loading of collections.
* </p>
*/
public int getLoaderIndex();
/**
* Check after the lazy load that the underlying collection is not null
* (handle case where join to many not outer).
@@ -75,30 +77,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.
*/
@@ -129,11 +107,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public void internalAdd(Object bean);
/**
* Returns the underlying List Set or Map object.
*/
public Object getActualCollection();
/**
* Return the number of elements in the List Set or Map.
*/
@@ -158,34 +131,6 @@ public interface BeanCollection<E> extends Serializable {
*/
public Collection<?> getActualEntries();
/**
* 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();
/**
* 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);
/**
* return true if there are real rows held. Return false is this is using
* Deferred fetch to lazy load the rows and the rows have not yet been
@@ -12,5 +12,5 @@ public interface BeanCollectionAdd {
/**
* Add a loaded bean to the collection.
*/
public void addBean(Object bean);
public void addBean(EntityBean bean);
}
@@ -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
@@ -1,7 +1,6 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Objects;
/**
* Identifies a unique node of an object graph.
@@ -70,7 +69,7 @@ public final class ObjectGraphNode implements Serializable {
public int hashCode() {
int hc = 31 * originQueryPoint.hashCode();
hc = 31 * hc + Objects.hashCode(path);
hc = 31 * hc + (path == null ? 0 : path.hashCode());
return hc;
}
@@ -83,7 +82,7 @@ public final class ObjectGraphNode implements Serializable {
}
ObjectGraphNode e = (ObjectGraphNode) obj;
return Objects.equals(e.path, path)
return ((e.path == path) || (e.path != null && e.path.equals(path)))
&& e.originQueryPoint.equals(originQueryPoint);
}
}
@@ -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,30 +35,15 @@ 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 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 String propertyName;
protected ModifyHolder<E> modifyHolder;
@@ -70,30 +52,30 @@ 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.
*/
public AbstractBeanCollection() {
this.ownerBean = null;
this.propertyName = null;
}
/**
* 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;
}
@@ -101,10 +83,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
return propertyName;
}
public int getLoaderIndex() {
return loaderIndex;
}
public ExpressionList<?> getFilterMany() {
return filterMany;
}
@@ -128,7 +106,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 +139,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,12 +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 void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.list = null;
this.touched = false;
}
@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);
}
@@ -75,16 +90,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);
}
}
@@ -115,13 +138,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
return list;
}
/**
* Returns the underlying list.
*/
public Object getActualCollection() {
return list;
}
/**
* Return true if the underlying list is populated.
*/
@@ -138,7 +154,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 ");
@@ -148,7 +164,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();
@@ -257,7 +272,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);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.map = null;
this.touched = false;
}
public boolean isEmptyAndUntouched() {
return !touched && (map == null || map.isEmpty());
}
@SuppressWarnings("unchecked")
public void internalPut(Object key, Object bean) {
if (map == null) {
@@ -83,16 +98,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);
}
}
@@ -131,15 +154,8 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
/**
* Returns the underlying map.
*/
public Object getActualCollection() {
return map;
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder(50);
sb.append("BeanMap ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -149,7 +165,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();
@@ -209,7 +224,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public boolean isEmpty() {
init();
initAsUntouched();
return map.isEmpty();
}
@@ -8,12 +8,15 @@ 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 {
private static final long serialVersionUID = 1L;
/**
* The underlying Set implementation.
*/
@@ -33,12 +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);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.set = null;
this.touched = false;
}
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);
}
@@ -83,16 +98,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);
}
}
@@ -128,7 +151,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 ");
@@ -138,7 +161,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();
@@ -217,7 +239,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
public boolean isEmpty() {
init();
initAsUntouched();
return set.isEmpty();
}
@@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory;
/**
* Provides some base implementation for NamingConventions.
*
*
* @author emcgreal
*/
public abstract class AbstractNamingConvention implements NamingConvention {
@@ -41,7 +41,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
/** Used to trim off extra prefix for M2M. */
protected int rhsPrefixLength = 3;
protected boolean useForeignKeyPrefix = true;
protected boolean useForeignKeyPrefix;
/**
* Construct with a sequence format and useForeignKeyPrefix setting.
@@ -53,12 +53,12 @@ public abstract class AbstractNamingConvention implements NamingConvention {
/**
* Construct with a sequence format.
*
* @param sequenceFormat
* the sequence format
*
* @param sequenceFormat the sequence format
*/
public AbstractNamingConvention(String sequenceFormat) {
this.sequenceFormat = sequenceFormat;
this.useForeignKeyPrefix = true;
}
/**
@@ -125,10 +125,9 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* The format should include "{table}". When generating the sequence name
* {table} is replaced with the actual table name.
* </p>
*
* @param sequenceFormat
* string containing "{table}" which is replaced with the actual
* table name to generate the sequence name.
*
* @param sequenceFormat string containing "{table}" which is replaced with the actual
* table name to generate the sequence name.
*/
public void setSequenceFormat(String sequenceFormat) {
this.sequenceFormat = sequenceFormat;
@@ -173,7 +172,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* This first checks for the @Table annotation and if not present uses the
* naming convention to define the table name.
* </p>
*
*
* @see #getTableNameFromAnnotation(Class)
* @see #getTableNameByConvention(Class)
*/
@@ -240,8 +239,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
if (t != null && !isEmpty(t.name())) {
// Note: empty catalog and schema are converted to null
// Only need to convert quoted identifiers from annotations
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()),
quoteIdentifiers(t.name()));
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
}
// No annotation
@@ -279,4 +277,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
}
return false;
}
/**
* Load settings from properties.
*/
@Override
public void loadFromProperties(PropertiesWrapper properties) {
useForeignKeyPrefix = properties.getBoolean("namingConvention.useForeignKeyPrefix", useForeignKeyPrefix);
sequenceFormat = properties.get("namingConvention.sequenceFormat", sequenceFormat);
schema = properties.get("namingConvention.schema", schema);
}
}
@@ -155,14 +155,6 @@ public class AutofetchConfig {
return logDirectory;
}
/**
* Return the log directory substituting any expressions such as
* ${catalina.base} etc.
*/
public String getLogDirectoryWithEval() {
return GlobalProperties.evaluateExpressions(logDirectory);
}
/**
* Set the directory to put the autofetch log in.
*/
@@ -234,22 +226,20 @@ public class AutofetchConfig {
/**
* Load the settings from the properties file.
*/
public void loadSettings(GlobalProperties.PropertySource p) {
public void loadSettings(PropertiesWrapper p) {
logDirectory = p.get("autofetch.logDirectory", null);
queryTuning = p.getBoolean("autofetch.querytuning", false);
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", false);
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", false);
logDirectory = p.get("autofetch.logDirectory", logDirectory);
queryTuning = p.getBoolean("autofetch.querytuning", queryTuning);
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", queryTuningAddVersion);
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", garbageCollectionOnShutdown);
profiling = p.getBoolean("autofetch.profiling", false);
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", AutofetchMode.DEFAULT_ONIFEMPTY);
profiling = p.getBoolean("autofetch.profiling", profiling);
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode);
profilingMin = p.getInt("autofetch.profiling.min", 1);
profilingBase = p.getInt("autofetch.profiling.base", 10);
profilingMin = p.getInt("autofetch.profiling.min", profilingMin);
profilingBase = p.getInt("autofetch.profiling.base", profilingBase);
String rate = p.get("autofetch.profiling.rate", "0.05");
profilingRate = Double.parseDouble(rate);
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", 60);
profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate);
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency);
}
}
@@ -1,50 +0,0 @@
package com.avaje.ebean.config;
import com.avaje.ebean.config.GlobalProperties.PropertySource;
/**
* Helper to read server specific properties from ebean.properties.
*/
class ConfigPropertyMap implements PropertySource {
private final String serverName;
public ConfigPropertyMap(String serverName) {
this.serverName = serverName;
}
public String getServerName() {
return serverName;
}
public String get(String key, String defaultValue) {
String namedKey = "ebean." + serverName + "." + key;
String inheritKey = "ebean." + key;
String value = GlobalProperties.get(namedKey, null);
if (value == null) {
value = GlobalProperties.get(inheritKey, null);
}
if (value == null) {
return defaultValue;
} else {
return value;
}
}
public int getInt(String key, int defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Integer.parseInt(value);
}
public boolean getBoolean(String key, boolean defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Boolean.parseBoolean(value);
}
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
String level = get(key, defaultValue.name());
return Enum.valueOf(enumType, level.toUpperCase());
}
}
@@ -0,0 +1,464 @@
package com.avaje.ebean.config;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* Configuration for the container that holds the EbeanServer instances.
* <p>
* Provides configuration for cluster communication (if clustering is used). The cluster communication is
* used to invalidate appropriate parts of the L2 cache across the cluster.
*/
public class ContainerConfig {
/**
* Communication mode used for clustering.
*/
public enum ClusterMode {
/**
* No clustering.
*/
NONE,
/**
* Use Multicast networking for cluster wide communication.
*/
MULTICAST,
/**
* Use TCP Sockets for cluster wide communication.
*/
SOCKET
}
/**
* The cluster mode to use.
*/
ClusterMode mode = ClusterMode.NONE;
/**
* Configuration if using TCP sockets for clustering communication.
*/
SocketConfig socketConfig = new SocketConfig();
/**
* Configuration if using Multicast for clustering communication.
*/
MulticastConfig multicastConfig = new MulticastConfig();
// -------------------------------------------------------------------------------------------
// MulticastConfig
/**
* The configuration for clustering using Multicast networking.
*/
public static class MulticastConfig {
int managerSleepMillis = 80;
int lastSendTimeFreqSecs = 300;//5mins
int lastStatusTimeFreqSecs = 600;//10mins
int maxResendOutgoingAttempts = 200;
int maxResendIncomingRequests = 50;
int listenPort;
String listenAddress;
int sendPort;
String sendAddress;
// Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507
int maxSendPacketSize = 1500;
// Whether to send packets even when there are no other members online
boolean sendWithNoMembers = true;
// When multiple instances are on same box you need to broadcast back locally
boolean disableLoopback;
int listenTimeToLive = -1;
int listenTimeout = 1000;
int listenBufferSize = 65500;
// For multihomed environment the address the listener should bind to
String listenBindAddress;
/**
* Return the manager sleep millis.
*/
public int getManagerSleepMillis() {
return managerSleepMillis;
}
/**
* Set the manager sleep millis.
*/
public void setManagerSleepMillis(int managerSleepMillis) {
this.managerSleepMillis = managerSleepMillis;
}
/**
* Return the last send time frequency.
*/
public int getLastSendTimeFreqSecs() {
return lastSendTimeFreqSecs;
}
/**
* Set the last send time frequency.
*/
public void setLastSendTimeFreqSecs(int lastSendTimeFreqSecs) {
this.lastSendTimeFreqSecs = lastSendTimeFreqSecs;
}
/**
* Return the last status time frequency.
*/
public int getLastStatusTimeFreqSecs() {
return lastStatusTimeFreqSecs;
}
/**
* Set the last status time frequency.
*/
public void setLastStatusTimeFreqSecs(int lastStatusTimeFreqSecs) {
this.lastStatusTimeFreqSecs = lastStatusTimeFreqSecs;
}
/**
* Return the maximum number of times we will try to re-send a given packet before giving up sending
*/
public int getMaxResendOutgoingAttempts() {
return maxResendOutgoingAttempts;
}
/**
* Set the maximum retry attempts for outgoing messages.
*/
public void setMaxResendOutgoingAttempts(int maxResendOutgoingAttempts) {
this.maxResendOutgoingAttempts = maxResendOutgoingAttempts;
}
/**
* Return the maximum number of times we will ask for a packet to be resent to us before giving up asking.
*/
public int getMaxResendIncomingRequests() {
return maxResendIncomingRequests;
}
/**
* Set the maximum retry attempts for incoming messages.
*/
public void setMaxResendIncomingRequests(int maxResendIncomingRequests) {
this.maxResendIncomingRequests = maxResendIncomingRequests;
}
/**
* Return the listen port.
*/
public int getListenPort() {
return listenPort;
}
/**
* Set the listen port.
*/
public void setListenPort(int port) {
this.listenPort = port;
}
/**
* Return the listen address.
*/
public String getListenAddress() {
return listenAddress;
}
/**
* Set the listen address.
*/
public void setListenAddress(String listenAddress) {
this.listenAddress = listenAddress;
}
/**
* Return the send port.
*/
public int getSendPort() {
return sendPort;
}
/**
* Set the send port.
*/
public void setSendPort(int sendPort) {
this.sendPort = sendPort;
}
/**
* Return the send address.
*/
public String getSendAddress() {
return sendAddress;
}
/**
* Set the send address.
*/
public void setSendAddress(String sendAddress) {
this.sendAddress = sendAddress;
}
/**
* Return the maximum send packet size.
*/
public int getMaxSendPacketSize() {
return maxSendPacketSize;
}
/**
* Set the maximum send packet size. Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507.
*/
public void setMaxSendPacketSize(int maxSendPacketSize) {
this.maxSendPacketSize = maxSendPacketSize;
}
/**
* Return true if send messages when no other members in the cluster are up.
*/
public boolean isSendWithNoMembers() {
return sendWithNoMembers;
}
/**
* Set true if send messages when no other members in the cluster are up.
*/
public void setSendWithNoMembers(boolean sendWithNoMembers) {
this.sendWithNoMembers = sendWithNoMembers;
}
/**
* Return true if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
*/
public boolean isDisableLoopback() {
return disableLoopback;
}
/**
* Set if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
*/
public void setDisableLoopback(boolean disableLoopback) {
this.disableLoopback = disableLoopback;
}
/**
* Return the listen time to live.
*/
public int getListenTimeToLive() {
return listenTimeToLive;
}
/**
* Set the listen time to live.
*/
public void setListenTimeToLive(int listenTimeToLive) {
this.listenTimeToLive = listenTimeToLive;
}
/**
* Return the listen timeout.
*/
public int getListenTimeout() {
return listenTimeout;
}
/**
* set the listen timeout.
*/
public void setListenTimeout(int listenTimeout) {
this.listenTimeout = listenTimeout;
}
/**
* Return the listen buffer size.
*/
public int getListenBufferSize() {
return listenBufferSize;
}
/**
* Set the listen buffer size.
*/
public void setListenBufferSize(int listenBufferSize) {
this.listenBufferSize = listenBufferSize;
}
/**
* Return the listener bind address (optional). For multihomed environment the address the listener should bind to.
*/
public String getListenBindAddress() {
return listenBindAddress;
}
/**
* Set the listener bind address (optional). For multihomed environment the address the listener should bind to.
*/
public void setListenBindAddress(String listenBindAddress) {
this.listenBindAddress = listenBindAddress;
}
}
// -------------------------------------------------------------------------------------------
// SocketConfig
/**
* Configuration for clustering using TCP sockets.
* <p>
* This is good for when there are relatively small number of cluster members.
*/
public static class SocketConfig {
/**
* This local server in host:port format.
*/
String localHostPort;
/**
* All the cluster members in host:port format.
*/
List<String> members = new ArrayList<String>();
/**
* core threads for the associated thread pool.
*/
int coreThreads = 2;
/**
* Max threads for the associated thread pool.
*/
int maxThreads = 16;
String threadPoolName = "EbeanCluster";
/**
* Return the host and port for this server instance.
*/
public String getLocalHostPort() {
return localHostPort;
}
/**
* Set the host and port for this server instance.
*/
public void setLocalHostPort(String localHostPort) {
this.localHostPort = localHostPort;
}
/**
* Return all the host and port for all the members of the cluster.
*/
public List<String> getMembers() {
return members;
}
/**
* Set all the host and port for all the members of the cluster.
*/
public void setMembers(List<String> members) {
this.members = members;
}
/**
* Return the number of core threads to use.
*/
public int getCoreThreads() {
return coreThreads;
}
/**
* Set the number of core threads to use.
*/
public void setCoreThreads(int coreThreads) {
this.coreThreads = coreThreads;
}
/**
* Return the number of max threads to use.
*/
public int getMaxThreads() {
return maxThreads;
}
/**
* Set the number of max threads to use.
*/
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
}
/**
* Return the thread pool name.
*/
public String getThreadPoolName() {
return threadPoolName;
}
/**
* Set the thread pool name.
*/
public void setThreadPoolName(String threadPoolName) {
this.threadPoolName = threadPoolName;
}
}
// -------------------------------------------------------------------------------------------
// Members
/**
* Load the settings from properties.
*/
public void loadFromProperties(Properties properties) {
//TODO
}
/**
* Return the cluster mode.
*/
public ClusterMode getMode() {
return mode;
}
/**
* Set the cluster mode.
*/
public void setMode(ClusterMode mode) {
this.mode = mode;
}
/**
* Return the socket communication configuration.
*/
public SocketConfig getSocketConfig() {
return socketConfig;
}
/**
* Set the socket communication configuration.
*/
public void setSocketConfig(SocketConfig socketConfig) {
this.socketConfig = socketConfig;
}
/**
* Return the multicast communication configuration.
*/
public MulticastConfig getMulticastConfig() {
return multicastConfig;
}
/**
* Set the multicast communication configuration.
*/
public void setMulticastConfig(MulticastConfig multicastConfig) {
this.multicastConfig = multicastConfig;
}
}
@@ -2,6 +2,7 @@ package com.avaje.ebean.config;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.util.StringHelper;
@@ -32,6 +33,8 @@ public class DataSourceConfig {
private int isolationLevel = Transaction.READ_COMMITTED;
private boolean autoCommit;
private String heartbeatSql;
private int heartbeatFreqSecs = 30;
@@ -131,6 +134,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.
@@ -432,47 +449,63 @@ public class DataSourceConfig {
this.customProperties = customProperties;
}
/**
* Load the settings by reading the ebean.properties file.
*
* @param serverName name of the server
*/
public void loadSettings(String serverName) {
loadSettingsCustomPrefix("datasource." + serverName + ".", new GlobalProperties.DelegatedGlobalPropertySource(serverName));
loadSettings(new PropertiesWrapper("datasource", serverName, PropertyMap.defaultProperties()));
}
/**
* Load the settings from ebean.properties.
* Load the settings from the properties supplied.
* <p>
* You can use this when you have your own properties to use for configuration.
* </p>
*
* @param properties the properties to configure the datasource
* @param serverName the name of the specific datasource (optional)
*/
public void loadSettingsCustomPrefix(String prefix, GlobalProperties.PropertySource properties) {
public void loadSettings(Properties properties, String serverName) {
PropertiesWrapper dbProps = new PropertiesWrapper("datasource", serverName, properties);
loadSettings(dbProps);
}
this.username = properties.get(prefix + "username", null);
this.password = properties.get(prefix + "password", null);
/**
* Load the settings from the PropertiesWrapper.
*/
public void loadSettings(PropertiesWrapper properties) {
String dbDriver = properties.get(prefix + "databaseDriver", null);
this.driver = properties.get(prefix + "driver", dbDriver);
username = properties.get("username", username);
password = properties.get("password", password);
driver = properties.get("driver", properties.get("databaseDriver", driver));
url = properties.get("url", properties.get("databaseUrl", url));
String dbUrl = properties.get(prefix + "databaseUrl", null);
this.url = properties.get(prefix + "url", dbUrl);
autoCommit = properties.getBoolean("autoCommit", autoCommit);
captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace);
maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize);
leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes);
maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs);
trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs);
maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes);
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", 720);
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
minConnections = properties.getInt("minConnections", minConnections);
maxConnections = properties.getInt("maxConnections", maxConnections);
pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize);
cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize);
this.minConnections = properties.getInt(prefix + "minConnections", 0);
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
this.pstmtCacheSize = properties.getInt(prefix + "pstmtCacheSize", 20);
this.cstmtCacheSize = properties.getInt(prefix + "cstmtCacheSize", 20);
waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis);
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
heartbeatSql = properties.get("heartbeatSql", heartbeatSql);
heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds);
poolListener = properties.get("poolListener", poolListener);
offline = properties.getBoolean("offline", offline);
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);
String isoLevel = properties.get(prefix + "isolationlevel", "READ_COMMITTED");
String isoLevel = properties.get("isolationlevel", getTransactionIsolationLevel(isolationLevel));
this.isolationLevel = getTransactionIsolationLevel(isoLevel);
String customProperties = properties.get(prefix + "customProperties", null);
String customProperties = properties.get("customProperties", null);
if (customProperties != null && customProperties.length() > 0) {
Map<String, String> custProps = StringHelper.delimitedToMap(customProperties, ";", "=");
this.customProperties = custProps;
@@ -481,7 +514,21 @@ public class DataSourceConfig {
}
/**
* return the isolation level for a given string description.
* Return the isolation level description from the associated Connection int value.
*/
public String getTransactionIsolationLevel(int level) {
switch (level) {
case Connection.TRANSACTION_NONE : return "NONE";
case Connection.TRANSACTION_READ_COMMITTED : return "READ_COMMITTED";
case Connection.TRANSACTION_READ_UNCOMMITTED : return "READ_UNCOMMITTED";
case Connection.TRANSACTION_REPEATABLE_READ : return "REPEATABLE_READ";
case Connection.TRANSACTION_SERIALIZABLE : return "SERIALIZABLE";
default: throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
}
}
/**
* Return the isolation level for a given string description.
*/
public int getTransactionIsolationLevel(String level) {
level = level.toUpperCase();
@@ -505,6 +552,6 @@ public class DataSourceConfig {
return Connection.TRANSACTION_SERIALIZABLE;
}
throw new RuntimeException("Transaction Isolaction level [" + level + "] is not known.");
throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
}
}
@@ -1,211 +0,0 @@
package com.avaje.ebean.config;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import com.avaje.ebean.util.ClassUtil;
/**
* Provides access to properties loaded from the ebean.properties file.
*/
public final class GlobalProperties {
private static volatile PropertyMap globalMap;
private static boolean skipPrimaryServer;
/**
* Set whether to skip automatically creating the primary server.
*/
public static synchronized void setSkipPrimaryServer(boolean skip) {
skipPrimaryServer = skip;
}
/**
* Return true to skip automatically creating the primary server.
*/
public static synchronized boolean isSkipPrimaryServer() {
return skipPrimaryServer;
}
/**
* Parse the string replacing any expressions like ${catalina.base}.
* <p>
* This will evaluate expressions using first environment variables, than java
* system variables and lastly properties in ebean.properties - in that order.
* </p>
* <p>
* Expressions start with "${" and end with "}".
* </p>
*/
public static String evaluateExpressions(String val) {
return getPropertyMap().eval(val);
}
/**
* Parse and evaluate any expressions that have not already been evaluated.
*/
public static synchronized void evaluateExpressions() {
getPropertyMap().evaluateProperties();
}
/**
* In a servlet container environment this will additionally look in WEB-INF
* for the ebean.properties file.
*/
public static synchronized void setServletContext(ServletContext servletContext) {
PropertyMapLoader.setServletContext(servletContext);
}
/**
* Return the ServletContext (if setup in a servlet container environment).
*/
public static synchronized ServletContext getServletContext() {
return PropertyMapLoader.getServletContext();
}
private static void initPropertyMap() {
String fileName = System.getenv("EBEAN_PROPS_FILE");
if (fileName == null) {
fileName = System.getProperty("ebean.props.file");
if (fileName == null) {
fileName = "ebean.properties";
}
}
globalMap = PropertyMapLoader.load(null, fileName);
if (globalMap == null) {
// ebean.properties file was not found... but that
// is ok because we are likely doing programmatic config
globalMap = new PropertyMap();
}
String loaderCn = globalMap.get("ebean.properties.loader");
if (loaderCn != null) {
// a Runnable that can be used to customise the initialisation
// of the GlobalProperties
try {
Runnable r = (Runnable) ClassUtil.newInstance(loaderCn);
r.run();
} catch (Exception e) {
String m = "Error creating or running properties loader " + loaderCn;
throw new RuntimeException(m, e);
}
}
}
/**
* Return the property map loading it if required.
*/
private static synchronized PropertyMap getPropertyMap() {
if (globalMap == null) {
initPropertyMap();
}
return globalMap;
}
/**
* Return a String property with a default value.
*/
public static synchronized String get(String key, String defaultValue) {
return getPropertyMap().get(key, defaultValue);
}
/**
* Return a int property with a default value.
*/
public static synchronized int getInt(String key, int defaultValue) {
return getPropertyMap().getInt(key, defaultValue);
}
/**
* Return a boolean property with a default value.
*/
public static synchronized boolean getBoolean(String key, boolean defaultValue) {
return getPropertyMap().getBoolean(key, defaultValue);
}
/**
* Set a property return the previous value. This will evaluate any
* expressions in the value.
*/
public static synchronized String put(String key, String value) {
return getPropertyMap().putEval(key, value);
}
/**
* Set a Map of key value properties.
*/
public static synchronized void putAll(Map<String, String> keyValueMap) {
for (Entry<String, String> e : keyValueMap.entrySet()) {
getPropertyMap().putEval(e.getKey(), e.getValue());
}
}
public static PropertySource getPropertySource(String name) {
return new ConfigPropertyMap(name);
}
public static interface PropertySource {
/**
* Return the name of the server. This is also the dataSource name.
*/
public String getServerName();
/**
* Get a property. This will prepend "ebean" and the server name to lookup
* the value.
*/
public String get(String key, String defaultValue);
public int getInt(String key, int defaultValue);
public boolean getBoolean(String key, boolean defaultValue);
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue);
}
public static class DelegatedGlobalPropertySource implements PropertySource {
private String serverName;
public DelegatedGlobalPropertySource(String serverName) {
this.serverName = serverName;
}
@Override
public String getServerName() {
return serverName;
}
@Override
public String get(String key, String defaultValue) {
return GlobalProperties.get(key, defaultValue);
}
@Override
public int getInt(String key, int defaultValue) {
return GlobalProperties.getInt(key, defaultValue);
}
@Override
public boolean getBoolean(String key, boolean defaultValue) {
return GlobalProperties.getBoolean(key, defaultValue);
}
@Override
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
String level = get(key, defaultValue.name());
return Enum.valueOf(enumType, level.toUpperCase());
}
}
}
@@ -0,0 +1,29 @@
package com.avaje.ebean.config;
/**
* Configuration for JSON features.
*/
public abstract class JsonConfig {
/**
* Defined the format used for DateTime types.
*/
public enum DateTime {
/**
* Format as epoch millis.
*/
MILLIS,
/**
* Format as epoch with nanos.
*/
NANOS,
/**
* Format as ISO-8601 date format.
*/
ISO8601
}
}
@@ -112,4 +112,9 @@ public interface NamingConvention {
*/
public boolean isUseForeignKeyPrefix();
/**
* Load setting from properties.
*/
public void loadFromProperties(PropertiesWrapper properties);
}
@@ -0,0 +1,148 @@
package com.avaje.ebean.config;
import java.util.Properties;
public class PropertiesWrapper {
protected final Properties properties;
protected final String prefix;
protected final String serverName;
protected final PropertyMap propertyMap;
/**
* Construct with a prefix, serverName and properties.
*/
public PropertiesWrapper(String prefix, String serverName, Properties properties) {
this.serverName = serverName;
this.prefix = prefix;
this.propertyMap = PropertyMapLoader.load(null, properties);
this.properties = propertyMap.asProperties();
}
/**
* Construct without prefix of serverName.
*/
public PropertiesWrapper(Properties properties) {
this(null, null, properties);
}
/**
* Internal copy constructor when changing prefix.
*/
protected PropertiesWrapper(String prefix, String serverName, PropertyMap propertyMap, Properties properties) {
this.serverName = serverName;
this.prefix = prefix;
this.propertyMap = propertyMap;
this.properties = properties;
}
/**
* Return a PropertiesWrapper instance with a different prefix but same underlying properties.
* <p/>
* Used when wanting to use "datasource" as the prefix rather than "ebean".
* <p/>
* The returning instance should only be used in a read only fashion.
*/
public PropertiesWrapper withPrefix(String prefix) {
return new PropertiesWrapper(prefix, serverName, propertyMap, properties);
}
/**
* Return the serverName (optional).
*/
public String getServerName() {
return serverName;
}
/**
* Return as Properties with lower case keys and after evaluation and additional properties loading has occurred.
* <p>
* Ebean has historically ignored the case of keys hence returning the Properties with all the keys lower cased.
* </p>
*/
public Properties asPropertiesLowerCase() {
return properties;
}
/**
* Get a property with no default value.
*/
public String get(String key) {
return get(key, null);
}
/**
* Get a property with a default value.
* <p>
* This performs a search using the prefix and server name (if supplied) to search for the property
* value in order based on:
* <pre>{@code
* prefix.serverName.key
* prefix.key
* key
* }</pre>
* </p>
*/
public String get(String key, String defaultValue) {
String value = null;
if (serverName != null && prefix != null) {
value = propertyMap.get(prefix + "." + serverName + "." + key, null);
}
if (value == null && prefix != null) {
value = propertyMap.get(prefix + "." + key, null);
}
if (value == null) {
value = propertyMap.get(key, null);
}
return value == null ? defaultValue : value;
}
/**
* Return a double property value.
*/
public double getDouble(String key, double defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Double.parseDouble(value);
}
/**
* Return an int property value.
*/
public int getInt(String key, int defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Integer.parseInt(value);
}
/**
* Return a long property value.
*/
public long getLong(String key, long defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Long.parseLong(value);
}
/**
* Return a boolean property value.
*/
public boolean getBoolean(String key, boolean defaultValue) {
String value = get(key, String.valueOf(defaultValue));
return Boolean.parseBoolean(value);
}
/**
* Return a Enum property value.
*/
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
String level = get(key, defaultValue.name());
return Enum.valueOf(enumType, level.toUpperCase());
}
}
@@ -1,25 +1,43 @@
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.Properties;
import java.util.Set;
/**
* A map like structure of properties.
* <p/>
* Handles evaluation of expressions like ${home} and provides convenience methods for int, long and boolean.
*/
final class PropertyMap implements Serializable {
public final class PropertyMap implements Serializable {
private static final long serialVersionUID = 1L;
private LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
public static Properties defaultProperties() {
PropertyMap propertyMap = PropertyMapLoader.loadGlobalProperties();
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
}
public String toString() {
return map.toString();
}
/**
* Return as standard Properties.
*/
public Properties asProperties() {
Properties properties = new Properties();
for (Entry<String, String> e : entrySet()) {
properties.put(e.getKey(), e.getValue());
}
return properties;
}
/**
* Go through all the properties and evaluate any expressions that have not
* been resolved.
@@ -36,10 +54,16 @@ final class PropertyMap implements Serializable {
}
}
/**
* Returns the value with expressions like ${home} evaluated using system properties and environment variables.
*/
public synchronized String eval(String val) {
return PropertyExpression.eval(val, this);
}
/**
* Return the boolean property value with a given default.
*/
public synchronized boolean getBoolean(String key, boolean defaultValue) {
String value = get(key);
if (value == null) {
@@ -49,6 +73,9 @@ final class PropertyMap implements Serializable {
}
}
/**
* Return the int property value with a given default.
*/
public synchronized int getInt(String key, int defaultValue) {
String value = get(key);
if (value == null) {
@@ -58,37 +85,69 @@ final class PropertyMap implements Serializable {
}
}
/**
* Return the long property value with a given default.
*/
public synchronized long getLong(String key, long defaultValue) {
String value = get(key);
if (value == null) {
return defaultValue;
} else {
return Long.parseLong(value);
}
}
/**
* Return the string property value with a given default.
*/
public synchronized String get(String key, String defaultValue) {
String value = map.get(key.toLowerCase());
return value == null ? defaultValue : value;
}
/**
* Return the property value returning null if there is no value defined.
*/
public synchronized String get(String key) {
return map.get(key.toLowerCase());
}
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();
put(entry.getKey(), entry.getValue());
/**
* Put all evaluating any expressions in the values.
*/
public synchronized void putEvalAll(Map<String, String> keyValueMap) {
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
putEval(entry.getKey(), entry.getValue());
}
}
synchronized String putEval(String key, String value) {
/**
* Put a single key value evaluating any expressions in the value.
*/
public synchronized String putEval(String key, String value) {
value = PropertyExpression.eval(value, this);
return map.put(key.toLowerCase(), value);
}
synchronized String put(String key, String value) {
/**
* Put a single key value with no expression evaluation.
*/
public synchronized String put(String key, String value) {
return map.put(key.toLowerCase(), value);
}
synchronized String remove(String key) {
/**
* Remove an entry.
*/
public synchronized String remove(String key) {
return map.remove(key.toLowerCase());
}
synchronized Set<Entry<String, String>> entrySet() {
/**
* Return the entries.
*/
public synchronized Set<Entry<String, String>> entrySet() {
return map.entrySet();
}
@@ -3,40 +3,28 @@ 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.io.*;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
/**
* Helper used to load the PropertyMap.
* Helper used to load the ebean.properties into a PropertyMap.
*/
final class PropertyMapLoader {
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
private static ServletContext servletContext;
public static PropertyMap loadGlobalProperties() {
/**
* Return the servlet context when in a web environment.
*/
public static ServletContext getServletContext() {
return servletContext;
}
String fileName = System.getenv("EBEAN_PROPS_FILE");
if (fileName == null) {
fileName = System.getProperty("ebean.props.file");
if (fileName == null) {
fileName = "ebean.properties";
}
}
/**
* Set the ServletContext for when ebean.properties is in WEB-INF in a web
* application environment.
*/
public static void setServletContext(ServletContext servletContext) {
PropertyMapLoader.servletContext = servletContext;
return load(null, fileName);
}
/**
@@ -66,24 +54,27 @@ final class PropertyMapLoader {
* @param in
* the InputStream of the properties file to load.
*/
private static PropertyMap load(PropertyMap p, InputStream in) {
public static PropertyMap load(PropertyMap p, InputStream in) {
Properties props = new Properties();
try {
props.load(in);
in.close();
return load(p, props);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static PropertyMap load(PropertyMap p, Properties props) {
if (p == null) {
p = new PropertyMap();
}
// 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) {
@@ -121,18 +112,6 @@ final class PropertyMapLoader {
throw new NullPointerException("fileName is null?");
}
if (servletContext == null) {
logger.debug("No servletContext so not looking in WEB-INF for " + fileName);
} else {
// first look in WEB-INF ...
InputStream in = servletContext.getResourceAsStream("/WEB-INF/" + fileName);
if (in != null) {
logger.debug(fileName + " found in WEB-INF");
return in;
}
}
try {
File f = new File(fileName);
@@ -1,25 +1,21 @@
package com.avaje.ebean.config;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.PersistenceContextScope;
import com.avaje.ebean.annotation.Encrypted;
import com.avaje.ebean.cache.ServerCacheFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.GlobalProperties.PropertySource;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
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.event.*;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.util.ClassUtil;
import com.fasterxml.jackson.core.JsonFactory;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* The configuration used for creating a EbeanServer.
@@ -34,9 +30,9 @@ import com.avaje.ebean.util.ClassUtil;
* classes and listeners etc.
* </p>
*
* <pre class="code">
* <pre>{@code
* ServerConfig c = new ServerConfig();
* c.setName(&quot;ordh2&quot;);
* c.setName("ordh2");
*
* // read the ebean.properties and load
* // those settings into this serverConfig object
@@ -47,18 +43,18 @@ import com.avaje.ebean.util.ClassUtil;
* c.setDdlRun(true);
*
* // add any classes found in the app.data package
* c.addPackage(&quot;app.data&quot;);
* c.addPackage("app.data");
*
* // add the names of Jars that contain entities
* c.addJar(&quot;myJarContainingEntities.jar&quot;);
* c.addJar(&quot;someOtherJarContainingEntities.jar&quot;);
* c.addJar("myJarContainingEntities.jar");
* c.addJar("someOtherJarContainingEntities.jar");
*
* // register as the 'Default' server
* c.setDefaultServer(true);
*
* EbeanServer server = EbeanServerFactory.create(c);
*
* </pre>
* }</pre>
*
* @see EbeanServerFactory
*
@@ -66,24 +62,19 @@ import com.avaje.ebean.util.ClassUtil;
* @author rbygrave
*/
public class ServerConfig {
/** The Constant DEFAULT_QUERY_BATCH_SIZE. Default: 100 */
private final static int DEFAULT_QUERY_BATCH_SIZE = 100;
/**
* The EbeanServer name.
*/
private String name;
private ContainerConfig containerConfig;
/**
* The resource directory.
*/
private String resourceDirectory;
/**
* The enhance log level. Used with subclass generation.
*/
private int enhanceLogLevel;
/**
* Set to true to register this EbeanServer with the Ebean singleton.
*/
@@ -112,13 +103,29 @@ public class ServerConfig {
*/
private List<String> searchJars = new ArrayList<String>();
/** The autofetch config. */
/**
* Class name of a classPathReader implementation.
*/
private String classPathReaderClassName;
/**
* Config controlling the autofetch behaviour.
*/
private AutofetchConfig autofetchConfig = new AutofetchConfig();
/** The database platform name. */
/**
* The JSON format used for DateTime types. Default to millis.
*/
private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.MILLIS;
/**
* The database platform name. Used to imply a DatabasePlatform to use.
*/
private String databasePlatformName;
/** The database platform. */
/**
* The database platform.
*/
private DatabasePlatform databasePlatform;
/**
@@ -130,11 +137,17 @@ public class ServerConfig {
private int persistBatchSize = 20;
/** The default batch size for lazy loading */
private int lazyLoadBatchSize = 1;
/**
* The default batch size for lazy loading
*/
private int lazyLoadBatchSize = 10;
/** The query batch size. */
private int queryBatchSize = -1;
/**
* The default batch size for 'query joins'.
*/
private int queryBatchSize = 100;
private boolean eagerFetchLobs;
private boolean ddlGenerate;
@@ -152,29 +165,61 @@ 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. */
private NamingConvention namingConvention;
/**
* The naming convention.
*/
private NamingConvention namingConvention = new UnderscoreNamingConvention();
/** 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<?>>();
private List<BeanPersistListener> persistListeners = new ArrayList<BeanPersistListener>();
private List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>();
private List<BulkTableEventListener> bulkTableEventListeners = new ArrayList<BulkTableEventListener>();
private List<ServerConfigStartup> configStartupListeners = new ArrayList<ServerConfigStartup>();
@@ -192,17 +237,86 @@ public class ServerConfig {
private ServerCacheManager serverCacheManager;
private boolean collectQueryStatsByNode;
private boolean collectQueryStatsByNode = true;
private boolean collectQueryOrigins = true;
private boolean collectQueryOrigins;
/**
* Construct a Server Configuration for programmatically creating an
* EbeanServer.
* The default PersistenceContextScope used if one is not explicitly set on a query.
*/
private PersistenceContextScope persistenceContextScope = PersistenceContextScope.TRANSACTION;
private JsonFactory jsonFactory;
private boolean localTimeWithNanos;
private boolean durationWithNanos;
private int maxCallStack = 5;
private boolean transactionRollbackOnChecked = true;
private boolean registerJmxMBeans = true;
// configuration for the background executor service (thread pool)
private int backgroundExecutorSchedulePoolSize = 1;
private int backgroundExecutorCorePoolSize = 1;
private int backgroundExecutorMaxPoolSize = 8;
private int backgroundExecutorIdleSecs = 60;
private int backgroundExecutorShutdownSecs = 30;
// defaults for the L2 bean caching
private int cacheWarmingDelay = 30;
private int cacheMaxSize = 10000;
private int cacheMaxIdleTime = 600;
private int cacheMaxTimeToLive = 60*60*6;
// defaults for the L2 query caching
private int queryCacheMaxSize = 1000;
private int queryCacheMaxIdleTime = 600;
private int queryCacheMaxTimeToLive = 60*60*6;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
public ServerConfig() {
}
/**
* Return the Jackson JsonFactory to use.
* <p>
* If not set a default implmentation will be used.
*/
public JsonFactory getJsonFactory() {
return jsonFactory;
}
/**
* Set the Jackson JsonFactory to use.
* <p>
* If not set a default implmentation will be used.
*/
public void setJsonFactory(JsonFactory jsonFactory) {
this.jsonFactory = jsonFactory;
}
/**
* Return the JSON format used for DateTime types.
*/
public JsonConfig.DateTime getJsonDateTime() {
return jsonDateTime;
}
/**
* Set the JSON format to use for DateTime types.
*/
public void setJsonDateTime(JsonConfig.DateTime jsonDateTime) {
this.jsonDateTime = jsonDateTime;
}
/**
* Return the name of the EbeanServer.
@@ -218,6 +332,26 @@ public class ServerConfig {
this.name = name;
}
/**
* Return the container / clustering configuration.
* <p/>
* The container holds all the EbeanServer instances and provides clustering communication
* services to all the EbeanServer instances.
*/
public ContainerConfig getContainerConfig() {
return containerConfig;
}
/**
* Set the container / clustering configuration.
* <p/>
* The container holds all the EbeanServer instances and provides clustering communication
* services to all the EbeanServer instances.
*/
public void setContainerConfig(ContainerConfig containerConfig) {
this.containerConfig = containerConfig;
}
/**
* Return true if this server should be registered with the Ebean singleton
* when it is created.
@@ -274,15 +408,6 @@ public class ServerConfig {
return persistBatching;
}
/**
* Use isPersistBatching() instead.
*
* @deprecated
*/
public boolean isUsePersistBatching() {
return persistBatching;
}
/**
* Set to true if you what to use JDBC batching for persisting and deleting
* beans.
@@ -431,17 +556,231 @@ public class ServerConfig {
}
/**
* Return the log level used for "subclassing" enhancement.
* Return true if LOB's should default to fetch eager.
* By default this is set to false and LOB's must be explicitly fetched.
*/
public int getEnhanceLogLevel() {
return enhanceLogLevel;
public boolean isEagerFetchLobs() {
return eagerFetchLobs;
}
/**
* Set the log level used for "subclassing" enhancement.
* Set to true if you want LOB's to be fetch eager by default.
* By default this is set to false and LOB's must be explicitly fetched.
*/
public void setEnhanceLogLevel(int enhanceLogLevel) {
this.enhanceLogLevel = enhanceLogLevel;
public void setEagerFetchLobs(boolean eagerFetchLobs) {
this.eagerFetchLobs = eagerFetchLobs;
}
/**
* Return the max call stack to use for origin location.
*/
public int getMaxCallStack() {
return maxCallStack;
}
/**
* Set the max call stack to use for origin location.
*/
public void setMaxCallStack(int maxCallStack) {
this.maxCallStack = maxCallStack;
}
/**
* Return true if transactions should rollback on checked exceptions.
*/
public boolean isTransactionRollbackOnChecked() {
return transactionRollbackOnChecked;
}
/**
* Set to true if transactions should by default rollback on checked exceptions.
*/
public void setTransactionRollbackOnChecked(boolean transactionRollbackOnChecked) {
this.transactionRollbackOnChecked = transactionRollbackOnChecked;
}
/**
* Return true if the server should register JMX MBeans.
*/
public boolean isRegisterJmxMBeans() {
return registerJmxMBeans;
}
/**
* Set if the server should register JMX MBeans.
*/
public void setRegisterJmxMBeans(boolean registerJmxMBeans) {
this.registerJmxMBeans = registerJmxMBeans;
}
/**
* Return the Background executor schedule pool size. Defaults to 1.
*/
public int getBackgroundExecutorSchedulePoolSize() {
return backgroundExecutorSchedulePoolSize;
}
/**
* Set the Background executor schedule pool size.
*/
public void setBackgroundExecutorSchedulePoolSize(int backgroundExecutorSchedulePoolSize) {
this.backgroundExecutorSchedulePoolSize = backgroundExecutorSchedulePoolSize;
}
/**
* Return the Background executor core pool size.
*/
public int getBackgroundExecutorCorePoolSize() {
return backgroundExecutorCorePoolSize;
}
/**
* Set the Background executor core pool size.
*/
public void setBackgroundExecutorCorePoolSize(int backgroundExecutorCorePoolSize) {
this.backgroundExecutorCorePoolSize = backgroundExecutorCorePoolSize;
}
/**
* Return the Background executor max pool size.
*/
public int getBackgroundExecutorMaxPoolSize() {
return backgroundExecutorMaxPoolSize;
}
/**
* Set the Background executor max pool size.
*/
public void setBackgroundExecutorMaxPoolSize(int backgroundExecutorMaxPoolSize) {
this.backgroundExecutorMaxPoolSize = backgroundExecutorMaxPoolSize;
}
/**
* Return the Background executor idle seconds.
*/
public int getBackgroundExecutorIdleSecs() {
return backgroundExecutorIdleSecs;
}
/**
* Set the Background executor idle seconds.
*/
public void setBackgroundExecutorIdleSecs(int backgroundExecutorIdleSecs) {
this.backgroundExecutorIdleSecs = backgroundExecutorIdleSecs;
}
/**
* Return the Background executor shutdown seconds. This is the time allowed for the pool to shutdown nicely
* before it is forced shutdown.
*/
public int getBackgroundExecutorShutdownSecs() {
return backgroundExecutorShutdownSecs;
}
/**
* Set the Background executor shutdown seconds. This is the time allowed for the pool to shutdown nicely
* before it is forced shutdown.
*/
public void setBackgroundExecutorShutdownSecs(int backgroundExecutorShutdownSecs) {
this.backgroundExecutorShutdownSecs = backgroundExecutorShutdownSecs;
}
/**
* Return the cache warming delay in seconds.
*/
public int getCacheWarmingDelay() {
return cacheWarmingDelay;
}
/**
* Set the cache warming delay in seconds.
*/
public void setCacheWarmingDelay(int cacheWarmingDelay) {
this.cacheWarmingDelay = cacheWarmingDelay;
}
/**
* Return the L2 cache default max size.
*/
public int getCacheMaxSize() {
return cacheMaxSize;
}
/**
* Set the L2 cache default max size.
*/
public void setCacheMaxSize(int cacheMaxSize) {
this.cacheMaxSize = cacheMaxSize;
}
/**
* Return the L2 cache default max idle time in seconds.
*/
public int getCacheMaxIdleTime() {
return cacheMaxIdleTime;
}
/**
* Set the L2 cache default max idle time in seconds.
*/
public void setCacheMaxIdleTime(int cacheMaxIdleTime) {
this.cacheMaxIdleTime = cacheMaxIdleTime;
}
/**
* Return the L2 cache default max time to live in seconds.
*/
public int getCacheMaxTimeToLive() {
return cacheMaxTimeToLive;
}
/**
* Set the L2 cache default max time to live in seconds.
*/
public void setCacheMaxTimeToLive(int cacheMaxTimeToLive) {
this.cacheMaxTimeToLive = cacheMaxTimeToLive;
}
/**
* Return the L2 query cache default max size.
*/
public int getQueryCacheMaxSize() {
return queryCacheMaxSize;
}
/**
* Set the L2 query cache default max size.
*/
public void setQueryCacheMaxSize(int queryCacheMaxSize) {
this.queryCacheMaxSize = queryCacheMaxSize;
}
/**
* Return the L2 query cache default max idle time in seconds.
*/
public int getQueryCacheMaxIdleTime() {
return queryCacheMaxIdleTime;
}
/**
* Set the L2 query cache default max idle time in seconds.
*/
public void setQueryCacheMaxIdleTime(int queryCacheMaxIdleTime) {
this.queryCacheMaxIdleTime = queryCacheMaxIdleTime;
}
/**
* Return the L2 query cache default max time to live in seconds.
*/
public int getQueryCacheMaxTimeToLive() {
return queryCacheMaxTimeToLive;
}
/**
* Set the L2 query cache default max time to live in seconds.
*/
public void setQueryCacheMaxTimeToLive(int queryCacheMaxTimeToLive) {
this.queryCacheMaxTimeToLive = queryCacheMaxTimeToLive;
}
/**
@@ -544,6 +883,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.
@@ -764,6 +1117,58 @@ 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;
}
/**
* Return true if LocalTime should be persisted with nanos precision.
*/
public boolean isLocalTimeWithNanos() {
return localTimeWithNanos;
}
/**
* Set to true if LocalTime should be persisted with nanos precision.
* <p>
* Otherwise it is persisted using java.sql.Time which is seconds precision.
* </p>
*/
public void setLocalTimeWithNanos(boolean localTimeWithNanos) {
this.localTimeWithNanos = localTimeWithNanos;
}
/**
* Return true if Duration should be persisted with nanos precision (SQL DECIMAL).
* <p>
* Otherwise it is persisted with second precision (SQL INTEGER).
* </p>
*/
public boolean isDurationWithNanos() {
return durationWithNanos;
}
/**
* Set to true if Duration should be persisted with nanos precision (SQL DECIMAL).
* <p>
* Otherwise it is persisted with second precision (SQL INTEGER).
* </p>
*/
public void setDurationWithNanos(boolean durationWithNanos) {
this.durationWithNanos = durationWithNanos;
}
/**
* Set to true to run the DDL generation on startup.
*/
@@ -895,6 +1300,23 @@ public class ServerConfig {
this.searchJars = searchJars;
}
/**
* Return the class name of a classPathReader implementation.
*/
public String getClassPathReaderClassName() {
return classPathReaderClassName;
}
/**
* Set the class name of a classPathReader implementation.
*
* Refer to server.util.ClassPathReader, this should really by a plugin but doing this for now
* to be relatively compatible with current implementation.
*/
public void setClassPathReaderClassName(String classPathReaderClassName) {
this.classPathReaderClassName = classPathReaderClassName;
}
/**
* Set the list of classes (entities, listeners, scalarTypes etc) that should
* be used for this server.
@@ -931,8 +1353,23 @@ 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.
*/
@@ -1085,14 +1522,14 @@ public class ServerConfig {
* all the BeanPersistListener instances.
* </p>
*/
public void add(BeanPersistListener<?> beanPersistListener) {
public void add(BeanPersistListener beanPersistListener) {
persistListeners.add(beanPersistListener);
}
/**
* Return the BeanPersistListener instances.
*/
public List<BeanPersistListener<?>> getPersistListeners() {
public List<BeanPersistListener> getPersistListeners() {
return persistListeners;
}
@@ -1131,49 +1568,60 @@ public class ServerConfig {
* BeanPersistListener instances one at a time.
* </p>
*/
public void setPersistListeners(List<BeanPersistListener<?>> persistListeners) {
public void setPersistListeners(List<BeanPersistListener> persistListeners) {
this.persistListeners = persistListeners;
}
/**
* Load the settings from the ebean.properties file.
* Return the default PersistenceContextScope to be used if one is not explicitly set on a query.
* <p/>
* The PersistenceContextScope can specified on each query via {@link com.avaje.ebean
* .Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)}. If it
* is not set on the query this default scope is used.
*
* @see com.avaje.ebean.Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)
*/
public PersistenceContextScope getPersistenceContextScope() {
// if somehow null return TRANSACTION scope
return persistenceContextScope == null ? PersistenceContextScope.TRANSACTION : persistenceContextScope;
}
/**
* Set the PersistenceContext scope to be used if one is not explicitly set on a query.
* <p/>
* This defaults to {@link PersistenceContextScope#TRANSACTION}.
* <p/>
* The PersistenceContextScope can specified on each query via {@link com.avaje.ebean
* .Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)}. If it
* is not set on the query this scope is used.
*
* @see com.avaje.ebean.Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)
*/
public void setPersistenceContextScope(PersistenceContextScope persistenceContextScope) {
this.persistenceContextScope = persistenceContextScope;
}
/**
* Load settings from ebean.properties.
*/
public void loadFromProperties() {
ConfigPropertyMap p = new ConfigPropertyMap(name);
loadFromProperties(PropertyMap.defaultProperties());
}
/**
* Load the settings from the given properties
*/
public void loadFromProperties(Properties properties) {
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
loadSettings(p);
}
/**
* Return a PropertySource for this server.
*/
public PropertySource getPropertySource() {
return GlobalProperties.getPropertySource(name);
}
/**
* Return a configuration property using a default value.
*/
public String getProperty(String propertyName, String defaultValue) {
PropertySource p = new ConfigPropertyMap(name);
return p.get(propertyName, defaultValue);
}
/**
* Return a configuration property.
*/
public String getProperty(String propertyName) {
return getProperty(propertyName, null);
}
@SuppressWarnings("unchecked")
private <T> T createInstance(PropertySource p, Class<T> type, String key) {
private <T> T createInstance(PropertiesWrapper p, Class<T> pluginType, String key) {
String classname = p.get(key, null);
if (classname == null) {
return null;
}
return (T) ClassUtil.newInstance(classname);
return classname == null ? null : (T) ClassUtil.newInstance(classname);
}
/**
@@ -1183,37 +1631,37 @@ public class ServerConfig {
*
* @param p - The defined property source passed to load settings
*/
protected void loadDataSourceSettings(PropertySource p) {
dataSourceConfig.loadSettings(p.getServerName());
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.withPrefix("datasource"));
}
/**
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
*
* @param p
*/
protected void loadAutofetchConfig(PropertySource p) {
protected void loadAutofetchSettings(PropertiesWrapper p) {
autofetchConfig.loadSettings(p);
}
/**
* Load the configuration settings from the properties file.
*/
protected void loadSettings(PropertySource p) {
protected void loadSettings(PropertiesWrapper p) {
if (namingConvention != null) {
namingConvention.loadFromProperties(p);
}
if (autofetchConfig == null) {
autofetchConfig = new AutofetchConfig();
}
loadAutofetchConfig(p);
loadAutofetchSettings(p);
if (dataSourceConfig == null) {
dataSourceConfig = new DataSourceConfig();
}
loadDataSourceSettings(p);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
namingConvention = createNamingConvention(p);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager");
@@ -1228,52 +1676,66 @@ public class ServerConfig {
searchJars = getSearchJarsPackages(jarsProp);
}
String packagesProp = p.get("search.packages", p.get("packages", null));
if (packages != null) {
String packagesProp = p.get("search.packages", p.get("packages", null));
packages = getSearchJarsPackages(packagesProp);
}
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
boolean batchMode = p.getBoolean("batch.mode", false);
updateChangesOnly = p.getBoolean("updateChangesOnly", updateChangesOnly);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", updatesDeleteMissingChildren);
updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren);
boolean batchMode = p.getBoolean("batch.mode", persistBatching);
persistBatching = p.getBoolean("persistBatching", batchMode);
int batchSize = p.getInt("batch.size", 20);
int batchSize = p.getInt("batch.size", persistBatchSize);
persistBatchSize = p.getInt("persistBatchSize", batchSize);
dataSourceJndiName = p.get("dataSourceJndiName", null);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", 20);
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
databasePlatformName = p.get("databasePlatformName", null);
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope","TRANSACTION"));
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", uuidStoreAsBinary);
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
ddlGenerate = p.getBoolean("ddl.generate", false);
ddlRun = p.getBoolean("ddl.run", false);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
String jsonDateTimeFormat = p.get("jsonDateTime", null);
if (jsonDateTimeFormat != null) {
jsonDateTime = JsonConfig.DateTime.valueOf(jsonDateTimeFormat);
} else {
jsonDateTime = JsonConfig.DateTime.MILLIS;
}
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
classes = getClasses(p);
}
private NamingConvention createNamingConvention(PropertySource p) {
private NamingConvention createNamingConvention(PropertiesWrapper properties) {
NamingConvention nc = createInstance(p, NamingConvention.class, "namingconvention");
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention");
if (nc == null) {
return null;
}
if (nc instanceof AbstractNamingConvention) {
AbstractNamingConvention anc = (AbstractNamingConvention) nc;
String v = p.get("namingConvention.useForeignKeyPrefix", null);
String v = properties.get("namingConvention.useForeignKeyPrefix", null);
if (v != null) {
boolean useForeignKeyPrefix = Boolean.valueOf(v);
anc.setUseForeignKeyPrefix(useForeignKeyPrefix);
}
String sequenceFormat = p.get("namingConvention.sequenceFormat", null);
String sequenceFormat = properties.get("namingConvention.sequenceFormat", null);
if (sequenceFormat != null) {
anc.setSequenceFormat(sequenceFormat);
}
@@ -1284,20 +1746,20 @@ public class ServerConfig {
/**
* Build the list of classes from the comma delimited string.
*
* @param p
* the p
* @param properties
* the properties
*
* @return the classes
*/
private ArrayList<Class<?>> getClasses(PropertySource p) {
private List<Class<?>> getClasses(PropertiesWrapper properties) {
String classNames = p.get("classes", null);
String classNames = properties.get("classes", null);
if (classNames == null) {
return null;
}
ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
List<Class<?>> classes = new ArrayList<Class<?>>();
String[] split = classNames.split("[ ,;]");
for (int i = 0; i < split.length; i++) {
@@ -1327,5 +1789,4 @@ public class ServerConfig {
}
return hitList;
}
}
@@ -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.
*
@@ -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,42 +15,67 @@ 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";
protected String columnAliasPrefix = "c";
protected String tableAliasPlaceHolder = "${ta}";
/**
* Use a BackTick ` at the beginning and end of table or column names that you
* want to use quoted identifiers for. The backticks get converted to the
@@ -57,6 +83,11 @@ 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;
@@ -139,6 +170,34 @@ public class DatabasePlatform {
return dbDdlSyntax;
}
/**
* Return the column alias prefix.
*/
public String getColumnAliasPrefix() {
return columnAliasPrefix;
}
/**
* Set the column alias prefix.
*/
public void setColumnAliasPrefix(String columnAliasPrefix) {
this.columnAliasPrefix = columnAliasPrefix;
}
/**
* Return the table alias placeholder.
*/
public String getTableAliasPlaceHolder() {
return tableAliasPlaceHolder;
}
/**
* Set the table alias placeholder.
*/
public void setTableAliasPlaceHolder(String tableAliasPlaceHolder) {
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
}
/**
* Return the close quote for quoted identifiers.
*
@@ -299,4 +358,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;
}
}
@@ -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);
@@ -1,7 +1,6 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
@@ -14,14 +13,13 @@ public class H2Platform extends DatabasePlatform {
super();
this.name = "h2";
this.dbEncrypt = new H2DbEncrypt();
// like ? escape'' not working in the latest version H2 so just using no
// escape clause for now noting that backslash is an escape char for like in H2
this.likeClause = "like ?";
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
boolean useIdentity = GlobalProperties.getBoolean("ebean.h2platform.useIdentity", false);
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
this.dbIdentity.setIdType(idType);
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -1,11 +1,9 @@
package com.avaje.ebean.config.dbplatform;
import java.sql.Types;
import com.avaje.ebean.BackgroundExecutor;
import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import java.sql.Types;
/**
* H2 specific platform.
@@ -16,14 +14,7 @@ public class HsqldbPlatform extends DatabasePlatform {
super();
this.name = "hsqldb";
this.dbEncrypt = new H2DbEncrypt();
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
boolean useIdentity = GlobalProperties.getBoolean("ebean.hsqldb.useIdentity", true);
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
this.dbIdentity.setIdType(idType);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -17,22 +17,21 @@ 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;
}
if (maxRows > 0 || firstRow > 0) {
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ").append(maxRows);
sb.append(" ").append(LIMIT).append(" ").append(maxRows);
if (firstRow > 0) {
sb.append(" ").append(OFFSET).append(" ");
sb.append(firstRow);
@@ -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();
@@ -1,7 +1,5 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.GlobalProperties;
/**
* Oracle encryption support.
*
@@ -48,12 +46,19 @@ import com.avaje.ebean.config.GlobalProperties;
public class Oracle10DbEncrypt extends AbstractDbEncrypt {
/**
* Constructs the Oracle10DbEncrypt.
* Constructs the Oracle10DbEncrypt with default encrypt and decrypt stored procedures.
*/
public Oracle10DbEncrypt() {
this("eb_encrypt", "eb_decrypt");
}
String encryptfunction = GlobalProperties.get("ebean.oracle.encryptfunction", "eb_encrypt");
String decryptfunction = GlobalProperties.get("ebean.oracle.decryptfunction", "eb_decrypt");
/**
* Constructs the Oracle10DbEncrypt specifying encrypt and decrypt stored procedures.
*
* @param encryptfunction the encrypt stored procedure
* @param decryptfunction the decrypt stored procedure
*/
public Oracle10DbEncrypt(String encryptfunction, String decryptfunction) {
this.varcharEncryptFunction = new OraVarcharFunction(encryptfunction, decryptfunction);
this.dateEncryptFunction = new OraDateFunction(encryptfunction, decryptfunction);
@@ -0,0 +1,67 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
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);
this.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;
}
}
@@ -1,43 +1,48 @@
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.
* 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);
if (colAlias == null) {
// Postgres requires the "as" keyword for column alias
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
}
this.columnAliasPrefix = "as c";
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);
}
@@ -40,7 +40,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
protected final BackgroundExecutor backgroundExecutor;
protected final ArrayList<Integer> idList = new ArrayList<Integer>(50);
protected final ArrayList<Long> idList = new ArrayList<Long>(50);
protected int batchSize;
@@ -117,7 +117,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
if (idList.size() == 0) {
loadMoreIds(batchSize, t);
}
Integer nextId = idList.remove(0);
Long nextId = idList.remove(0);
if (batchSize > 1) {
if (idList.size() <= batchSize / 2) {
@@ -158,7 +158,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
protected void loadMoreIds(final int numberToLoad, Transaction t) {
ArrayList<Integer> newIds = getMoreIds(numberToLoad, t);
ArrayList<Long> newIds = getMoreIds(numberToLoad, t);
if (logger.isDebugEnabled()) {
logger.debug("... seq:" + seqName + " loaded:" + numberToLoad + " ids:" + newIds);
@@ -174,11 +174,11 @@ public abstract class SequenceIdGenerator implements IdGenerator {
/**
* Get more Id's by executing a query and reading the Id's returned.
*/
protected ArrayList<Integer> getMoreIds(int loadSize, Transaction t) {
protected ArrayList<Long> getMoreIds(int loadSize, Transaction t) {
String sql = getSql(loadSize);
ArrayList<Integer> newIds = new ArrayList<Integer>(loadSize);
ArrayList<Long> newIds = new ArrayList<Long>(loadSize);
boolean useTxnConnection = t != null;
@@ -191,12 +191,10 @@ public abstract class SequenceIdGenerator implements IdGenerator {
pstmt = c.prepareStatement(sql);
rset = pstmt.executeQuery();
while (rset.next()) {
int val = rset.getInt(1);
newIds.add(Integer.valueOf(val));
newIds.add(rset.getLong(1));
}
if (newIds.size() == 0) {
String m = "Always expecting more than 1 row from " + sql;
throw new PersistenceException(m);
throw new PersistenceException("Always expecting more than 1 row from " + sql);
}
return newIds;
@@ -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 {
@@ -0,0 +1,78 @@
package com.avaje.ebean.event;
import java.util.Set;
/**
* Provides a base implementation of BeanPersistListener.
* <p>
* Objects extending this should override the methods then are interested in.
* The default inserted() updated() and deleted() methods return false and as such
* means other servers in the cluster are not notified.
* </p>
*/
public abstract class AbstractBeanPersistListener implements BeanPersistListener {
/**
* Notified that a bean has been inserted locally. Return true if you want the
* cluster to be notified of the event.
*
* @param bean The bean that was inserted.
*/
@Override
public boolean inserted(Object bean) {
return false;
}
/**
* Notified that a bean has been updated locally. Return true if you want the
* cluster to be notified of the event.
*
* @param bean The bean that was updated.
* @param updatedProperties The properties that were modified by this update.
*/
@Override
public boolean updated(Object bean, Set<String> updatedProperties) {
return false;
}
/**
* Notified that a bean has been deleted locally. Return true if you want the
* cluster to be notified of the event.
*
* @param bean The bean that was deleted.
*/
@Override
public boolean deleted(Object bean) {
return false;
}
/**
* Notify that a bean was inserted on another node of the cluster.
*
* @param id the id value of the inserted bean
*/
@Override
public void remoteInsert(Object id) {
// do nothing
}
/**
* Notify that a bean was updated on another node of the cluster.
*
* @param id the id value of the updated bean.
*/
@Override
public void remoteUpdate(Object id) {
// do nothing
}
/**
* Notify that a bean was deleted on another node of the cluster.
*
* @param id the id value of the deleted bean.
*/
@Override
public void remoteDelete(Object id) {
// do nothing
}
}
@@ -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,10 +33,17 @@ 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> {
public interface BeanPersistListener {
/**
* Return true if this BeanPersistListener should be registered for events
* on this entity type.
*/
public boolean isRegisterFor(Class<?> cls);
/**
* Notified that a bean has been inserted locally. Return true if you want the
@@ -43,7 +52,7 @@ public interface BeanPersistListener<T> {
* @param bean
* The bean that was inserted.
*/
public boolean inserted(T bean);
public boolean inserted(Object bean);
/**
* Notified that a bean has been updated locally. Return true if you want the
@@ -52,9 +61,9 @@ 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);
public boolean updated(Object bean, Set<String> updatedProperties);
/**
* Notified that a bean has been deleted locally. Return true if you want the
@@ -63,7 +72,7 @@ public interface BeanPersistListener<T> {
* @param bean
* The bean that was deleted.
*/
public boolean deleted(T bean);
public boolean deleted(Object bean);
/**
* Notify that a bean was inserted on another node of the cluster.
@@ -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 long exeCount;
private final long totalBeanLoaded;
private final long totalMicros;
public QueryStats(String path, long exeCount, long totalBeanLoaded, long 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 long getExeCount() {
return exeCount;
}
/**
* The total number of beans loaded by the query.
*/
public long getTotalBeanLoaded() {
return totalBeanLoaded;
}
/**
* The total time in microseconds of the queries.
*/
public long 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 + "]";
}
}
@@ -19,9 +19,6 @@ import com.avaje.ebean.Query;
* properties and applying that to both what to fetch (ORM query) and what to
* render (JAX-RS JSON / XML).
* </p>
*
* @author rbygrave
*
*/
public class PathProperties {
@@ -53,6 +53,9 @@ class PathPropertiesParser {
case '(':
return currentWord();
default:
if (pos == 1) {
return "";
}
}
} while (pos < eof);
throw new RuntimeException("Hit EOF while reading sectionTitle from " + startPos);
@@ -91,6 +94,10 @@ class PathPropertiesParser {
}
} while (pos < eof);
if (startPos < pos) {
String currentWord = source.substring(startPos, pos);
currentPathProps.addProperty(currentWord);
}
}
private void addSubpath() {
@@ -1,7 +1,7 @@
package com.avaje.ebean.text;
/**
* An exception occured typically in processing CSV, JSON or XML.
* An exception occurred typically in processing CSV, JSON or XML.
*
* @author rbygrave
*/
@@ -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.
@@ -0,0 +1,115 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
/**
* Utility that converts between JSON content and simple java Maps/Lists.
*/
public class EJson {
/**
* Write the nested Map/List as json.
*/
public static String write(Object object) throws IOException {
return EJsonWriter.write(object);
}
/**
* Write the nested Map/List as json to the writer.
*/
public static void write(Object object, Writer writer) throws IOException {
EJsonWriter.write(object, writer);
}
/**
* Write the nested Map/List as json to the jsonGenerator.
*/
public static void write(Object object, JsonGenerator jsonGenerator) throws IOException {
EJsonWriter.write(object, jsonGenerator);
}
/**
* Parse the json and return as a Map.
*/
public static Map<String,Object> parseObject(String json) throws IOException {
return EJsonReader.parseObject(json);
}
/**
* Parse the json and return as a Map taking a reader.
*/
public static Map<String,Object> parseObject(Reader reader) throws IOException {
return EJsonReader.parseObject(reader);
}
/**
* Parse the json and return as a Map taking a JsonParser.
*/
public static Map<String,Object> parseObject(JsonParser parser) throws IOException {
return EJsonReader.parseObject(parser);
}
/**
* Parse the json and return as a Map taking a JsonParser and a starting token.
* <p>
* Used when the first token is checked to see if the value is null prior to calling this.
* </p>
*/
public static Map<String,Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
return EJsonReader.parseObject(parser, token);
}
/**
* Parse the json and return as a List.
* @throws IOException
*/
public static List<Object> parseList(String json) throws IOException {
return EJsonReader.parseList(json);
}
/**
* Parse the json and return as a List taking a Reader.
* @throws IOException
*/
public static List<Object> parseList(Reader reader) throws IOException {
return EJsonReader.parseList(reader);
}
/**
* Parse the json and return as a List taking a JsonParser.
*/
public static List<Object> parseList(JsonParser parser) throws IOException {
return EJsonReader.parseList(parser);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(String json) throws IOException {
return EJsonReader.parse(json);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(Reader reader) throws IOException {
return EJsonReader.parse(reader);
}
/**
* Parse the json and return as a List or Map.
*/
public static Object parse(JsonParser parser) throws IOException {
return EJsonReader.parse(parser);
}
}
@@ -0,0 +1,282 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
class EJsonReader {
static JsonFactory json = new JsonFactory();
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(String json) throws IOException {
return (Map<String, Object>) parse(json);
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(Reader reader) throws IOException {
return (Map<String, Object>) parse(reader);
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(JsonParser parser) throws IOException {
return (Map<String, Object>) parse(parser);
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
return (Map<String, Object>)parse(parser, token);
}
@SuppressWarnings("unchecked")
static List<Object> parseList(String json) throws IOException {
return (List<Object>) parse(json);
}
@SuppressWarnings("unchecked")
static List<Object> parseList(Reader reader) throws IOException {
return (List<Object>) parse(reader);
}
@SuppressWarnings("unchecked")
static List<Object> parseList(JsonParser parser) throws IOException {
return (List<Object>) parse(parser);
}
static Object parse(String json) throws IOException {
return parse(new StringReader(json));
}
static Object parse(Reader reader) throws IOException {
return parse(json.createParser(reader));
}
static Object parse(JsonParser parser) throws IOException {
return parse(parser, null);
}
static Object parse(JsonParser parser, JsonToken token) throws IOException {
return new EJsonReader(parser).parseJson(token);
}
private final JsonParser parser;
private int depth;
private Stack stack;
private Context currentContext;
EJsonReader(JsonParser parser) {
this.parser = parser;
}
private void startArray() {
depth++;
stack.push(currentContext);
currentContext = new ArrayContext();
}
private void startObject() {
depth++;
stack.push(currentContext);
currentContext = new ObjectContext();
}
private void endArray() {
end();
}
private void endObject() {
end();
}
private void end() {
depth--;
if (!stack.isEmpty()) {
currentContext = stack.pop(currentContext);
}
}
private void setValue(Object value) {
currentContext.setValue(value);
}
private void setValueNull() {
currentContext.setValueNull();
}
private Object parseJson(JsonToken token) throws IOException {
if (token == null) {
// no initial token so expect to read START_OBJECT or similar
token = parser.nextToken();
if (JsonToken.VALUE_NULL == token) {
return null;
}
}
stack = new Stack();
// it is a object or array, process the first JsonToken
processJsonToken(token);
// process the rest of the object or array
while (depth > 0) {
token = parser.nextToken();
processJsonToken(token);
}
return currentContext.getValue();
}
/**
* Process the JsonToken for objects and arrays.
*/
private void processJsonToken(JsonToken token) throws IOException {
switch (token) {
case START_ARRAY:
startArray();
break;
case START_OBJECT:
startObject();
break;
case FIELD_NAME:
currentContext.setKey(parser.getCurrentName());
break;
case VALUE_STRING:
setValue(parser.getValueAsString());
break;
case VALUE_NUMBER_INT:
setValue(parser.getLongValue());
break;
case VALUE_NUMBER_FLOAT:
setValue(parser.getDecimalValue());
break;
case VALUE_TRUE:
setValue(Boolean.TRUE);
break;
case VALUE_FALSE:
setValue(Boolean.FALSE);
break;
case VALUE_NULL:
setValueNull();
break;
case END_OBJECT:
endObject();
break;
case END_ARRAY:
endArray();
break;
default:
break;
}
}
private static final class Stack {
private Context head;
private void push(Context context) {
if (context != null) {
context.next = head;
head = context;
}
}
private Context pop(Context endingContext) {
if (head == null) {
throw new NoSuchElementException();
}
Context temp = head;
head = head.next;
temp.popContext(endingContext);
return temp;
}
private boolean isEmpty() {
return head == null;
}
}
private static abstract class Context {
Context next;
abstract void popContext(Context temp);
abstract Object getValue();
abstract void setKey(String key);
abstract void setValue(Object value);
abstract void setValueNull();
}
private static class ObjectContext extends Context {
private final Map<String, Object> map = new LinkedHashMap<String, Object>();
private String key;
public void popContext(Context temp) {
setValue(temp.getValue());
}
Object getValue() {
return map;
}
void setKey(String key) {
this.key = key;
}
void setValue(Object value) {
map.put(key, value);
}
void setValueNull() {
map.put(key, null);
}
}
private static class ArrayContext extends Context {
private final List<Object> values = new ArrayList<Object>();
public void popContext(Context temp) {
values.add(temp.getValue());
}
Object getValue() {
return values;
}
void setValue(Object value) {
values.add(value);
}
void setValueNull() {
// ignore
}
void setKey(String key) {
// not expected
}
}
}
@@ -0,0 +1,209 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
class EJsonWriter {
/**
* Base jsonFactory implementation used when it is not passed in.
*/
static JsonFactory jsonFactory = new JsonFactory();
static String write(Object object) throws IOException {
StringWriter writer = new StringWriter(200);
write(object, writer);
return writer.toString();
}
static void write(Object object, Writer writer) throws IOException {
JsonGenerator generator = jsonFactory.createGenerator(writer);
write(object, generator);
generator.close();
}
static void write(Object object, JsonGenerator jsonGenerator) {
new EJsonWriter(jsonGenerator).writeJson(object);
}
private final JsonGenerator jsonGenerator;
private EJsonWriter(JsonGenerator jsonGenerator) {
this.jsonGenerator = jsonGenerator;
}
private void writeJson(Object object) {
writeJson(null, object);
}
@SuppressWarnings("unchecked")
private void writeJson(String name, Object object) {
try {
if (object == null) {
writeNull(name);
} else if (object instanceof Map) {
writeMap(name, (Map<Object, Object>) object);
} else if (object instanceof Collection) {
writeCollection(name, (Collection<Object>) object);
} else if (object instanceof Boolean) {
writeBoolean(name, (Boolean) object);
} else if (object instanceof Number) {
writeNumber(name, (Number) object);
} else if (object instanceof Date) {
writeDate(name, (Date) object);
} else if (object instanceof String) {
writeString(name, (String) object);
} else if (object instanceof Map.Entry<?, ?>) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
writeJson(entry.getKey().toString(), entry.getValue());
} else {
writeString(name, object.toString());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void writeBoolean(String name, Boolean object) throws IOException {
if (name == null) {
jsonGenerator.writeBoolean(object);
} else {
jsonGenerator.writeBooleanField(name, object);
}
}
private void writeDate(String name, Date object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber(object.getTime());
} else {
jsonGenerator.writeNumberField(name, object.getTime());
}
}
private void writeNumber(String name, Number object) throws IOException {
if (object instanceof Long) {
writeLong(name, object);
} else if (object instanceof Integer) {
writeInteger(name, object);
} else if (object instanceof Double) {
writeDouble(name, object);
} else if (object instanceof BigDecimal) {
writeBigDecimal(name, object);
} else if (object instanceof BigInteger) {
writeBigInteger(name, object);
} else {
writeGeneralNumber(name, object);
}
}
private void writeGeneralNumber(String name, Number object) throws IOException {
writeBigDecimal(name, new BigDecimal(object.toString()));
}
private void writeBigDecimal(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((BigDecimal) object);
} else {
jsonGenerator.writeNumberField(name, (BigDecimal) object);
}
}
private void writeBigInteger(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((BigInteger) object);
} else {
jsonGenerator.writeNumberField(name, object.longValue());
}
}
private void writeDouble(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((Double) object);
} else {
jsonGenerator.writeNumberField(name, (Double) object);
}
}
private void writeLong(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((Long) object);
} else {
jsonGenerator.writeNumberField(name, (Long) object);
}
}
private void writeInteger(String name, Number object) throws IOException {
if (name == null) {
jsonGenerator.writeNumber((Integer) object);
} else {
jsonGenerator.writeNumberField(name, (Integer) object);
}
}
private void writeNull(String name) throws IOException {
if (name == null) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeNullField(name);
}
}
private void writeString(String name, String object) throws IOException {
if (name == null) {
jsonGenerator.writeString(object);
} else {
jsonGenerator.writeStringField(name, object);
}
}
private void writeCollection(String name, Collection<Object> collection) throws IOException {
if (name != null) {
jsonGenerator.writeFieldName(name);
}
jsonGenerator.writeStartArray();
for (Object object : collection) {
writeJson(null, object);
}
jsonGenerator.writeEndArray();
}
private void writeMap(String name, Map<Object, Object> map) throws IOException {
if (name != null) {
jsonGenerator.writeFieldName(name);
}
jsonGenerator.writeStartObject();
Set<Entry<Object, Object>> entrySet = map.entrySet();
for (Entry<Object, Object> entry : entrySet) {
writeJson(entry.getKey().toString(), entry.getValue());
}
jsonGenerator.writeEndObject();
}
}
@@ -1,5 +1,9 @@
package com.avaje.ebean.text.json;
import com.avaje.ebean.text.PathProperties;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
@@ -7,125 +11,136 @@ import java.util.List;
/**
* Converts objects to and from JSON format.
*
* @author rbygrave
*/
public interface JsonContext {
/**
* Convert json string input into a Bean of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> T toBean(Class<T> rootType, String json);
public <T> T toBean(Class<T> rootType, String json) throws JsonIOException;
/**
* Convert json reader input into a Bean of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> T toBean(Class<T> rootType, Reader json);
/**
* Convert json string input into a Bean of a specific type with options.
*/
public <T> T toBean(Class<T> rootType, String json, JsonReadOptions options);
/**
* Convert json reader input into a Bean of a specific type with options.
*/
public <T> T toBean(Class<T> rootType, Reader json, JsonReadOptions options);
public <T> T toBean(Class<T> rootType, Reader json) throws JsonIOException;
/**
* Convert json string input into a list of beans of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> List<T> toList(Class<T> rootType, String json);
/**
* Convert json string input into a list of beans of a specific type with
* options.
*/
public <T> List<T> toList(Class<T> rootType, String json, JsonReadOptions options);
public <T> List<T> toList(Class<T> rootType, String json) throws JsonIOException;
/**
* Convert json reader input into a list of beans of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
public <T> List<T> toList(Class<T> rootType, Reader json);
/**
* Convert json reader input into a list of beans of a specific type with
* options.
*/
public <T> List<T> toList(Class<T> rootType, Reader json, JsonReadOptions options);
public <T> List<T> toList(Class<T> rootType, Reader json) throws JsonIOException;
/**
* Use the genericType to determine if this should be converted into a List or
* bean.
*
* @throws JsonIOException When IOException occurs
*/
public Object toObject(Type genericType, Reader json, JsonReadOptions options);
public Object toObject(Type genericType, Reader json) throws JsonIOException;
/**
* Use the genericType to determine if this should be converted into a List or
* bean.
*
* @throws JsonIOException When IOException occurs
*/
public Object toObject(Type genericType, String json, JsonReadOptions options);
public Object toObject(Type genericType, String json) throws JsonIOException;
/**
* Write the bean or collection in JSON format to the writer with default
* options.
*
* @param o
* the bean or collection of beans to write
* @param writer
* used to write the json output to
* Return the bean or collection as JSON string.
*
* @throws JsonIOException When IOException occurs
*/
public void toJsonWriter(Object o, Writer writer);
public String toJson(Object value) throws JsonIOException;
/**
* With additional pretty output option.
* Write the bean or collection in JSON format to the writer.
*
* @throws JsonIOException When IOException occurs
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty);
public void toJson(Object value, Writer writer) throws JsonIOException;
/**
* With additional options to specify JsonValueAdapter and
* JsonWriteBeanVisitor's.
*
* @param o
* the bean or collection of beans to write
* @param writer
* used to write the json output to
* @param options
* additional options to control the JSON output
* Write the bean or collection to the JsonGenerator.
*
* @throws JsonIOException When IOException occurs
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options);
public void toJson(Object value, JsonGenerator generator) throws JsonIOException;
/**
* With additional JSONP callback function.
* Return the bean or collection as JSON string using PathProperties.
*
* @throws JsonIOException When IOException occurs
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options,
String callback);
public String toJson(Object value, PathProperties pathProperties) throws JsonIOException;
/**
* Convert a bean or collection to json string using default options.
* Write the bean or collection as json to the writer using the PathProperties.
*/
public String toJsonString(Object o);
public void toJson(Object value, Writer writer, PathProperties pathProperties) throws JsonIOException;
/**
* Convert a bean or collection to json string with pretty format using
* default options.
* Write the bean or collection to the JsonGenerator using the PathProperties.
*/
public String toJsonString(Object o, boolean pretty);
public void toJson(Object value, JsonGenerator generator, PathProperties pathProperties) throws JsonIOException;
/**
* Convert a bean or collection to json string using options.
* Deprecated in favour of using PathProperties by itself.
* Write json to the JsonGenerator using the JsonWriteOptions.
*
* @deprecated
*/
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options);
public void toJson(Object value, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException;
/**
* Convert a bean or collection to json string using a JSONP callback.
* Deprecated in favour of using PathProperties by itself.
* With additional options.
*
* @throws JsonIOException When IOException occurs
*
* @deprecated
*/
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback);
public void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException;
/**
* Return true if the type is known as an Entity or Xml type or a List Set or
* Map of known bean types.
* Deprecated in favour of using PathProperties by itself.
* Convert a bean or collection to json string.
*
* @throws JsonIOException When IOException occurs
*
* @deprecated
*/
public String toJson(Object value, JsonWriteOptions options) throws JsonIOException;
/**
* Return true if the type is known as an Entity bean or a List Set or
* Map of entity beans.
*/
public boolean isSupportedType(Type genericType);
/**
* Create and return a new JsonGenerator for the given writer.
*
* @throws JsonIOException When IOException occurs
*/
public JsonGenerator createGenerator(Writer writer) throws JsonIOException;
/**
* Create and return a new JsonParser for the given reader.
*
* @throws JsonIOException When IOException occurs
*/
public JsonParser createParser(Reader reader) throws JsonIOException;
}
@@ -1,39 +0,0 @@
package com.avaje.ebean.text.json;
/**
* Marker interface for all the Raw JSON types.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public interface JsonElement {
/**
* Return true if this is a JSON primitive type (null, boolean, number or
* string).
*/
public boolean isPrimitive();
/**
* Return the string value of this primitive JSON element.
* <p>
* This can not be used for JsonElementObject or JsonElementArray.
* </p>
*/
public String toPrimitiveString();
public Object eval(String exp);
public int evalInt(String exp);
public String evalString(String exp);
public boolean evalBoolean(String exp);
}
@@ -1,112 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.ArrayList;
import java.util.List;
/**
* JSON Array element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementArray implements JsonElement {
private final List<JsonElement> values = new ArrayList<JsonElement>();
public List<JsonElement> getValues() {
return values;
}
public void add(JsonElement value) {
values.add(value);
}
public String toString() {
return values.toString();
}
public boolean isPrimitive() {
return false;
}
public String toPrimitiveString() {
return null;
}
private String[] split(String exp) {
int pos = exp.indexOf('.');
if (pos == -1) {
return new String[] { exp, null };
}
String exp0 = exp.substring(0, pos);
String exp1 = exp.substring(pos + 1);
return new String[] { exp0, exp1 };
}
public Object eval(String exp) {
String[] e = split(exp);
return eval(e[0], e[1]);
}
public int evalInt(String exp) {
String[] e = split(exp);
return evalInt(e[0], e[1]);
}
public String evalString(String exp) {
String[] e = split(exp);
return evalString(e[0], e[1]);
}
public boolean evalBoolean(String exp) {
// TODO Auto-generated method stub
return false;
}
private Object eval(String exp0, String exp1) {
if ("size".equals(exp0)) {
return values.size();
}
if ("isEmpty".equals(exp0)) {
return values.isEmpty();
}
int idx = Integer.parseInt(exp0);
JsonElement element = values.get(idx);
return element.eval(exp1);
}
private int evalInt(String exp0, String exp1) {
if ("size".equals(exp0)) {
return values.size();
}
if ("isEmpty".equals(exp0)) {
return values.isEmpty() ? 1 : 0;
}
int idx = Integer.parseInt(exp0);
JsonElement element = values.get(idx);
return element.evalInt(exp1);
}
private String evalString(String exp0, String exp1) {
if ("size".equals(exp0)) {
return String.valueOf(values.size());
}
if ("isEmpty".equals(exp0)) {
return String.valueOf(values.isEmpty());
}
int idx = Integer.parseInt(exp0);
JsonElement element = values.get(idx);
return element.evalString(exp1);
}
public String getString() {
return toString();
}
}
@@ -1,63 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON boolean element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementBoolean implements JsonElement {
public static final JsonElementBoolean TRUE = new JsonElementBoolean(true);
public static final JsonElementBoolean FALSE = new JsonElementBoolean(false);
private final Boolean value;
private JsonElementBoolean(Boolean value) {
this.value = value;
}
public Boolean getValue() {
return value;
}
public String toString() {
return Boolean.toString(value);
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return value.toString();
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on boolean");
}
return value;
}
public int evalInt(String exp) {
return value ? 1 : 0;
}
public String evalString(String exp) {
return toString();
}
public boolean evalBoolean(String exp) {
return value;
}
}
@@ -1,57 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON null element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementNull implements JsonElement {
public static final JsonElementNull NULL = new JsonElementNull();
private JsonElementNull() {
}
public String getValue() {
return "null";
}
public String toString() {
return "json null";
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return null;
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on null");
}
return null;
}
public int evalInt(String exp) {
return 0;
}
public String evalString(String exp) {
return null;
}
public boolean evalBoolean(String exp) {
return false;
}
}
@@ -1,67 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON number element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementNumber implements JsonElement {
private final String value;
public JsonElementNumber(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String toString() {
return value;
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return value;
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Double.parseDouble(value);
}
public int evalInt(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Integer.parseInt(value);
}
public String evalString(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return value;
}
public boolean evalBoolean(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Boolean.parseBoolean(value);
}
}
@@ -1,108 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* JSON Object element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementObject implements JsonElement {
private final Map<String, JsonElement> map = new LinkedHashMap<String, JsonElement>();
public void put(String key, JsonElement value) {
map.put(key, value);
}
private String[] split(String exp) {
int pos = exp.indexOf('.');
if (pos == -1) {
return new String[] { exp, null };
}
String exp0 = exp.substring(0, pos);
String exp1 = exp.substring(pos + 1);
return new String[] { exp0, exp1 };
}
public Object eval(String exp) {
String[] e = split(exp);
return eval(e[0], e[1]);
}
public int evalInt(String exp) {
String[] e = split(exp);
return evalInt(e[0], e[1]);
}
public String evalString(String exp) {
if (exp == null) {
return map.toString();
}
String[] e = split(exp);
return evalString(e[0], e[1]);
}
public boolean evalBoolean(String exp) {
String[] e = split(exp);
return evalBoolean(e[0], e[1]);
}
private Object eval(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? null : e.eval(exp1);
}
private int evalInt(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? 0 : e.evalInt(exp1);
}
private String evalString(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? "" : e.evalString(exp1);
}
private boolean evalBoolean(String exp0, String exp1) {
JsonElement e = map.get(exp0);
return e == null ? false : e.evalBoolean(exp1);
}
public JsonElement get(String key) {
return map.get(key);
}
public JsonElement getValue(String key) {
return map.get(key);
}
public Set<String> keySet() {
return map.keySet();
}
public Set<Map.Entry<String, JsonElement>> entrySet() {
return map.entrySet();
}
public String toString() {
return map.toString();
}
public boolean isPrimitive() {
return false;
}
public String toPrimitiveString() {
return null;
}
}
@@ -1,71 +0,0 @@
package com.avaje.ebean.text.json;
/**
* JSON string element.
* <p>
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
* The JSON elements that are not mapped to a bean property are made available
* to the JsonReadBeanVisitor.
* </p>
*
* @see JsonReadBeanVisitor
*
* @author rbygrave
*/
public class JsonElementString implements JsonElement {
private final String value;
public JsonElementString(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String toString() {
return value;
}
public boolean isPrimitive() {
return true;
}
public String toPrimitiveString() {
return value;
}
public Object eval(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return value;
}
public int evalInt(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return 0;
}
}
public String evalString(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return value;
}
public boolean evalBoolean(String exp) {
if (exp != null) {
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
}
return Boolean.parseBoolean(exp);
}
}
@@ -0,0 +1,24 @@
package com.avaje.ebean.text.json;
/**
* Unchecked exception thrown when an IOException occurs in json processing.
* <p>
* Typically wraps the checked IOException.
* </p>
*/
public class JsonIOException extends RuntimeException {
/**
* Construct with an underlying cause.
*/
public JsonIOException(Throwable cause) {
super(cause);
}
/**
* Construct with a message.
*/
public JsonIOException(String message) {
super(message);
}
}
@@ -1,34 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.Map;
/**
* Provides for some custom handling of json content as it is read.
* <p>
* This visit method is called after all the known properties of the bean have
* been processed. Any JSON elements that could not be mapped to known bean
* properties are available in the unmapped Map.
* </p>
*
* @author rbygrave
*
* @param <T>
* The type of entity bean
*/
public interface JsonReadBeanVisitor<T> {
/**
* Visit the bean that has just been processed.
* <p>
* This provides a method of customising the bean and processing any custom
* JSON content.
* </p>
*
* @param bean
* the bean being processed
* @param unmapped
* Map of any JSON elements that didn't map to known bean properties
*/
public void visit(T bean, Map<String, JsonElement> unmapped);
}
@@ -1,71 +0,0 @@
package com.avaje.ebean.text.json;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Provides the ability to customise the reading of JSON content.
* <p>
* You can optionally provide a custom JsonValueAdapter to handle specific
* formatting for Date and DateTime types.
* </p>
* <p>
* You can optionally register JsonReadBeanVisitors to customise the processing
* of the beans as they are processed and handle any custom JSON elements that
* could not be mapped to bean properties.
* </p>
*
* @author rbygrave
*
*/
public class JsonReadOptions {
protected JsonValueAdapter valueAdapter;
protected Map<String, JsonReadBeanVisitor<?>> visitorMap;
/**
* Default constructor.
*/
public JsonReadOptions() {
this.visitorMap = new LinkedHashMap<String, JsonReadBeanVisitor<?>>();
}
/**
* Return the JsonValueAdapter.
*/
public JsonValueAdapter getValueAdapter() {
return valueAdapter;
}
/**
* Return the map of JsonReadBeanVisitor's.
*/
public Map<String, JsonReadBeanVisitor<?>> getVisitorMap() {
return visitorMap;
}
/**
* Set a JsonValueAdapter for custom DateTime and Date formatting.
*/
public JsonReadOptions setValueAdapter(JsonValueAdapter valueAdapter) {
this.valueAdapter = valueAdapter;
return this;
}
/**
* Register a JsonReadBeanVisitor for the root level.
*/
public JsonReadOptions addRootVisitor(JsonReadBeanVisitor<?> visitor) {
return addVisitor(null, visitor);
}
/**
* Register a JsonReadBeanVisitor for a given path.
*/
public JsonReadOptions addVisitor(String path, JsonReadBeanVisitor<?> visitor) {
visitorMap.put(path, visitor);
return this;
}
}
@@ -1,61 +0,0 @@
/**
* Copyright (C) 2009 Authors
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebean.text.json;
import java.sql.Timestamp;
/**
* Allows you to customise the Date and Timestamp formats.
* <p>
* There is not a standard JSON format for Date or Timestamp types. By default
* Ebean uses ISO8601 "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and "yyyy-MM-dd".
* </p>
* <p>
* Note that Ebean will convert Joda types to either of the Date or Timestamp
* types and back for you.
* </p>
*
* @see JsonReadOptions
*
* @author rbygrave
*/
public interface JsonValueAdapter {
/**
* Convert the Date to json string.
*/
public String jsonFromDate(java.sql.Date date);
/**
* Convert the DateTime to json string.
*/
public String jsonFromTimestamp(java.sql.Timestamp date);
/**
* Parse the JSON string into a Date.
*/
public java.sql.Date jsonToDate(String jsonDate);
/**
* Parse the JSON DateTime into a Timestamp.
*/
public Timestamp jsonToTimestamp(String jsonDateTime);
}
@@ -1,33 +0,0 @@
package com.avaje.ebean.text.json;
/**
* Allows for customising the JSON write processing.
* <p>
* You can use this to add raw JSON content via {@link JsonWriter}.
* </p>
* <p>
* You register a JsonWriteBeanVisitor with {@link JsonWriteOptions}.
* </p>
*
* @author rbygrave
*
* @param <T>
* the type of entity bean
*
* @see JsonWriteOptions
*/
public interface JsonWriteBeanVisitor<T> {
/**
* Visit the bean that has just been writing it's content to JSON. You can
* write your own additional JSON content to the JsonWriter if you wish.
*
* @param bean
* the bean that has been writing it's content
* @param jsonWriter
* the JsonWriter which you can append custom json content to if you
* wish.
*/
public void visit(T bean, JsonWriter jsonWriter);
}
@@ -1,225 +1,42 @@
package com.avaje.ebean.text.json;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.text.PathProperties;
/**
* Deprecated in favour of just using PathProperties.
*
* Provides options for customising the JSON write process.
* <p>
* You can optionally provide a custom JsonValueAdapter to handle specific
* formatting for Date and DateTime types.
* </p>
* <p>
* You can optionally register JsonWriteBeanVisitors to customise the processing
* of the beans as they are processed and <strong>add raw JSON
* elements</strong>.
* </p>
* <p>
* You can explicitly state which properties to include in the JSON output for
* the root level and each path.
* </p>
*
* <pre class="code">
* // find some customers ...
*
* List&lt;Customer&gt; list = Ebean.find(Customer.class).select(&quot;id, name, status, shippingAddress&quot;)
* .fetch(&quot;billingAddress&quot;,
* &quot;line1, city&quot;).fetch(&quot;billingAddress.country&quot;, &quot;*&quot;).fetch(&quot;contacts&quot;, &quot;firstName,email&quot;)
* .order().desc(&quot;id&quot;)
* .findList();
*
* JsonContext json = Ebean.createJsonContext();
*
* JsonWriteOptions writeOptions = new JsonWriteOptions();
* writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor&lt;Customer&gt;() {
*
* public void visit(Customer bean, JsonWriter ctx) {
* System.out.println(&quot;write visit customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummyCust&quot;, &quot;34&quot;);
* ctx.appendKeyValue(&quot;smallCustObject&quot;, &quot;{\&quot;a\&quot;:34,\&quot;b\&quot;:\&quot;asdasdasd\&quot;}&quot;);
* }
* });
*
* writeOptions.setPathProperties(&quot;contacts&quot;, &quot;firstName,id&quot;);
* writeOptions.setPathVisitor(&quot;contacts&quot;, new JsonWriteBeanVisitor&lt;Contact&gt;() {
*
* public void visit(Contact bean, JsonWriter ctx) {
* System.out.println(&quot;write additional custom json on customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummy&quot;, &quot; 3400&quot; + bean.getId() + &quot;&quot;);
* ctx.appendKeyValue(&quot;smallObject&quot;, &quot;{\&quot;contactA\&quot;:34,\&quot;contactB\&quot;:\&quot;banana\&quot;}&quot;);
* }
*
* });
*
* // output as a JSON string with pretty formatting
* String s = json.toJsonString(list, true, writeOptions);
*
* </pre>
*
* @see JsonContext#toList(Class, String, JsonReadOptions)
*
* @author rbygrave
*
* @deprecated
*/
public class JsonWriteOptions {
protected String callback;
protected JsonValueAdapter valueAdapter;
protected Map<String, JsonWriteBeanVisitor<?>> visitorMap;
protected PathProperties pathProperties;
/**
* Parse and return a PathProperties from nested string format like
* (a,b,c(d,e),f(g)) where "c" is a path containing "d" and "e" and "f" is a
* path containing "g" and the root path contains "a","b","c" and "f".
*
* @see com.avaje.ebean.text.PathProperties#parse(String)
*/
public static JsonWriteOptions parsePath(String pathProperties) {
PathProperties p = PathProperties.parse(pathProperties);
return pathProperties(PathProperties.parse(pathProperties));
}
/**
* Construct JsonWriteOptions with the given pathProperties.
*/
public static JsonWriteOptions pathProperties(PathProperties pathProperties) {
JsonWriteOptions o = new JsonWriteOptions();
o.setPathProperties(p);
o.setPathProperties(pathProperties);
return o;
}
/**
* This creates and returns a copy of these options.
* <p>
* Note that it assumes that the JsonWriteBeanVisitor (if defined) are
* immutable and any JsonWriteBeanVisitor instances are shared between the
* original and the copy.
* </p>
*/
public JsonWriteOptions copy() {
JsonWriteOptions copy = new JsonWriteOptions();
copy.callback = callback;
copy.valueAdapter = valueAdapter;
copy.pathProperties = pathProperties;
if (visitorMap != null) {
copy.visitorMap = new HashMap<String, JsonWriteBeanVisitor<?>>(visitorMap);
}
return copy;
}
/**
* Return a JSONP callback function.
*/
public String getCallback() {
return callback;
}
/**
* Set a JSONP callback function.
*/
public JsonWriteOptions setCallback(String callback) {
this.callback = callback;
return this;
}
/**
* Return the JsonValueAdapter.
*/
public JsonValueAdapter getValueAdapter() {
return valueAdapter;
}
/**
* Set a JsonValueAdapter for custom DateTime and Date formatting.
*/
public JsonWriteOptions setValueAdapter(JsonValueAdapter valueAdapter) {
this.valueAdapter = valueAdapter;
return this;
}
/**
* Register a JsonWriteBeanVisitor for the root level.
*/
public JsonWriteOptions setRootPathVisitor(JsonWriteBeanVisitor<?> visitor) {
return setPathVisitor(null, visitor);
}
/**
* Register a JsonWriteBeanVisitor for the given path.
*/
public JsonWriteOptions setPathVisitor(String path, JsonWriteBeanVisitor<?> visitor) {
if (visitorMap == null) {
visitorMap = new HashMap<String, JsonWriteBeanVisitor<?>>();
}
visitorMap.put(path, visitor);
return this;
}
/**
* Set the properties to include in the JSON output for the given path.
*
* @param propertiesToInclude
* The set of properties to output
*/
public JsonWriteOptions setPathProperties(String path, Set<String> propertiesToInclude) {
if (pathProperties == null) {
pathProperties = new PathProperties();
}
pathProperties.put(path, propertiesToInclude);
return this;
}
/**
* Set the properties to include in the JSON output for the given path.
*
* @param propertiesToInclude
* Comma delimited list of properties to output
*/
public JsonWriteOptions setPathProperties(String path, String propertiesToInclude) {
return setPathProperties(path, parseProps(propertiesToInclude));
}
/**
* Set the properties to include in the JSON output for the root level.
*
* @param propertiesToInclude
* Comma delimited list of properties to output
*/
public JsonWriteOptions setRootPathProperties(String propertiesToInclude) {
return setPathProperties(null, parseProps(propertiesToInclude));
}
/**
* Set the properties to include in the JSON output for the root level.
*
* @param propertiesToInclude
* The set of properties to output
*/
public JsonWriteOptions setRootPathProperties(Set<String> propertiesToInclude) {
return setPathProperties(null, propertiesToInclude);
}
private Set<String> parseProps(String propertiesToInclude) {
LinkedHashSet<String> props = new LinkedHashSet<String>();
String[] split = propertiesToInclude.split(",");
for (int i = 0; i < split.length; i++) {
String s = split[i].trim();
if (s.length() > 0) {
props.add(s);
}
}
return props;
}
/**
* Return the Map of registered JsonWriteBeanVisitor's by path.
*/
public Map<String, JsonWriteBeanVisitor<?>> getVisitorMap() {
return visitorMap;
}
/**
* Set the Map of properties to include by path.
*/
@@ -1,28 +0,0 @@
package com.avaje.ebean.text.json;
/**
* The JSON Writer made available to JsonWriteBeanVisitor's so that you can
* append your own JSON content into the output.
*
* @see JsonWriteBeanVisitor
* @see JsonWriteOptions#setRootPathVisitor(JsonWriteBeanVisitor)
* @see JsonWriteOptions#setPathVisitor(String, JsonWriteBeanVisitor)
*
* @author rbygrave
*/
public interface JsonWriter {
/**
* Use this to append some custom content into the JSON output.
*
* @param key
* the json key
*
* @param rawJsonValue
* raw json value
*/
public void appendRawValue(String key, String rawJsonValue);
public void appendQuoteEscapeValue(String key, String rawJsonValue);
}
@@ -19,31 +19,10 @@
* .order().desc(&quot;id&quot;)
* .findList();
*
* JsonContext json = Ebean.createJsonContext();
*
* JsonWriteOptions writeOptions = new JsonWriteOptions();
* writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor&lt;Customer&gt;() {
*
* public void visit(Customer bean, JsonWriter ctx) {
* System.out.println(&quot;write visit customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummyCust&quot;, &quot;34&quot;);
* ctx.appendKeyValue(&quot;smallCustObject&quot;, &quot;{\&quot;a\&quot;:34,\&quot;b\&quot;:\&quot;asdasdasd\&quot;}&quot;);
* }
* });
*
* writeOptions.setPathProperties(&quot;contacts&quot;, &quot;firstName,id&quot;);
* writeOptions.setPathVisitor(&quot;contacts&quot;, new JsonWriteBeanVisitor&lt;Contact&gt;() {
*
* public void visit(Contact bean, JsonWriter ctx) {
* System.out.println(&quot;write additional custom json on customer: &quot; + bean);
* ctx.appendKeyValue(&quot;dummy&quot;, &quot; 3400&quot; + bean.getId() + &quot;&quot;);
* ctx.appendKeyValue(&quot;smallObject&quot;, &quot;{\&quot;contactA\&quot;:34,\&quot;contactB\&quot;:\&quot;banana\&quot;}&quot;);
* }
*
* });
*
* // output as a JSON string with pretty formatting
* String s = json.toJsonString(list, true, writeOptions);
* JsonContext json = Ebean.json();
*
* // output as a JSON string
* String jsonOutput = json.toJson(list);
*
* </pre>
*/

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