Compare commits

..
Author SHA1 Message Date
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
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
534 changed files with 23926 additions and 13666 deletions
+1
View File
@@ -6,6 +6,7 @@
target/
logs/
log/
/db
/mydb.db
+2 -4
View File
@@ -1,14 +1,12 @@
[![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
==============
Main EbeanORM artifact
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.3.2</version>
<version>4.1.8</version>
</dependency>
+57 -24
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.0.1-RC1</version>
<version>4.2.0</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,7 +99,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>4.1.0</version>
<version>4.1.9</version>
<scope>test</scope>
</dependency>
@@ -174,23 +179,21 @@
<!-- Enhance the meta beans -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>4.1.0</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>
@@ -220,9 +223,39 @@
</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>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>site</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
</project>
+50 -10
View File
@@ -214,14 +214,13 @@ public final class Ebean {
* Register a server so we can get it by its name.
*/
private void register(EbeanServer server, boolean isPrimaryServer) {
registerWithName(server.getName(), server, isPrimaryServer);
}
private void registerWithName(String name, EbeanServer server, boolean isPrimaryServer) {
synchronized (monitor) {
concMap.put(server.getName(), server);
EbeanServer existingServer = syncMap.put(server.getName(), server);
if (existingServer != null) {
String msg = "Existing EbeanServer [" + server.getName() + "] is being replaced?";
logger.warn(msg);
}
concMap.put(name, server);
syncMap.put(name, server);
if (isPrimaryServer) {
primaryServer = server;
}
@@ -286,6 +285,15 @@ public final class Ebean {
serverMgr.register(server, isPrimaryServer);
}
/**
* Backdoor for registering a mock implementation of EbeanServer as the default server.
*/
protected static EbeanServer mock(String name, EbeanServer server, boolean isPrimaryServer) {
EbeanServer originalPrimaryServer = serverMgr.primaryServer;
serverMgr.registerWithName(name, server, isPrimaryServer);
return originalPrimaryServer;
}
/**
* Return the next identity value for a given bean type.
* <p>
@@ -467,7 +475,31 @@ public final class Ebean {
public static void insert(Collection<?> beans) {
serverMgr.getPrimaryServer().insert(beans);
}
/**
* Marks the entity bean as dirty.
* <p>
* This is used so that when a bean that is otherwise unmodified is updated with the version
* property updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
*
* <pre class="code">
*
* Customer customer = Ebean.find(Customer, id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* Ebean.markAsDirty(customer);
* Ebean.save(customer);
*
* </pre>
*/
public static void markAsDirty(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().markAsDirty(bean);
}
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
@@ -1351,10 +1383,18 @@ public final class Ebean {
}
/**
* Create a JsonContext that will use the default configuration options.
* Return the JsonContext for reading/writing JSON.
*/
public static JsonContext json() {
return serverMgr.getPrimaryServer().json();
}
/**
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
public static JsonContext createJsonContext() {
return serverMgr.getPrimaryServer().createJsonContext();
return json();
}
}
+57 -7
View File
@@ -433,7 +433,7 @@ public interface EbeanServer {
* @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
@@ -489,7 +489,7 @@ 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
*/
@@ -505,7 +505,7 @@ 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
*/
@@ -521,7 +521,7 @@ public interface EbeanServer {
*
* @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
* @deprecated
@@ -538,7 +538,7 @@ public interface EbeanServer {
*
* @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
* @deprecated
@@ -547,10 +547,32 @@ public interface EbeanServer {
/**
* Find using a PagingList with explicit transaction and pageSize.
* @deprecated
* Deprecated in favour of findPagedList().
* @deprecated
*/
public <T> PagingList<T> findPagingList(Query<T> query, Transaction transaction, int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number
* function to limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize);
/**
* Execute the query returning a set of entity beans.
* <p>
@@ -815,6 +837,28 @@ public interface EbeanServer {
*/
public int save(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* 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 = ebeanServer.find(Customer, id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
* ebeanServer.markAsDirty(customer);
* ebeanServer.save(customer);
*
* </pre>
*/
public void markAsDirty(Object bean);
/**
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
* use this update() method rather than save().
@@ -1075,8 +1119,14 @@ 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.
*/
public JsonContext json();
}
@@ -6,8 +6,6 @@ import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates EbeanServer instances.
@@ -26,14 +24,9 @@ import org.slf4j.LoggerFactory;
* methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
* convenient way of using the 'default/primary' EbeanServer.
* </p>
*
* @author Rob Bygrave
*
*/
public class EbeanServerFactory {
private static final Logger logger = LoggerFactory.getLogger(EbeanServerFactory.class);
private static BootupEbeanManager serverFactory = createServerFactory();
/**
@@ -69,27 +62,10 @@ public class EbeanServerFactory {
private static BootupEbeanManager createServerFactory() {
// String d___ =
// com.avaje.ebean.server.core.DefaultServerFactory.class.getName();
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
String implClassName = GlobalProperties.get("ebean.serverfactory", dflt);
String implClassName = System.getProperty("ebean.serverfactory", dflt);
int delaySecs = GlobalProperties.getInt("ebean.start.delay", 0);
if (delaySecs > 0) {
try {
// perhaps useful to delay the startup to give time to
// attach a debugger when running in a server like tomcat.
String m = "Ebean sleeping " + delaySecs + " seconds due to ebean.start.delay";
logger.info(m);
Thread.sleep(delaySecs * 1000);
} catch (InterruptedException e) {
String m = "Interrupting debug.start.delay of " + delaySecs;
logger.error(m, e);
}
}
try {
// use a client side implementation?
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
} catch (Exception ex) {
throw new RuntimeException(ex);
@@ -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.
*
@@ -200,10 +207,34 @@ public interface ExpressionList<T> extends Serializable {
*
* @param pageSize
* the number of beans fetched per Page
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number
* function to limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Add some filter predicate expressions to the many property.
*/
public ExpressionList<T> filterMany(String prop);
/**
@@ -218,7 +249,8 @@ public interface ExpressionList<T> extends Serializable {
* Specify a property (associated bean) to join and <em>fetch</em> including
* all its properties.
*
* @see Query#join(String)
* @see Query#fetch(String)
* @deprecated
*/
public Query<T> join(String assocProperties);
@@ -226,7 +258,8 @@ public interface ExpressionList<T> extends Serializable {
* 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)
* @see Query#fetch(String,String)
* @deprecated
*/
public Query<T> join(String assocProperty, String assocProperties);
+603
View File
@@ -0,0 +1,603 @@
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);
}
// Does not exist yet but I think should
// public void deleteById(I id) {
// return db().deleteById(type, id);
// }
/**
* Retrieves all entities of the given type.
*
* <p>
* This is the same as (synonym for) {@link #findList()}
*/
public List<T> all() {
return findList();
}
/**
* Retrieves an entity by ID.
*
* <p>
* 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();
}
/**
* 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);
}
+33 -17
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;
@@ -413,6 +415,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>
@@ -589,9 +599,7 @@ public interface Query<T> extends Serializable {
/**
* Execute find list query in a background thread.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a
* timeout).
* Deprecated with a view to simplifying internals.
* </p>
*
* @return a Future object for the list result of the query
@@ -600,25 +608,33 @@ public interface Query<T> extends Serializable {
public FutureList<T> findFutureList();
/**
* Return a PagingList for this query.
* <p>
* This can be used to break up a query into multiple queries to fetch the
* data a page at a time.
* </p>
* <p>
* This typically works by using a query per page and setting
* {@link Query#setFirstRow(int)} and and {@link Query#setMaxRows(int)} on the
* query. This usually would translate into SQL that uses limit offset, rownum
* or row_number function to limit the result set.
* </p>
*
* @param pageSize
* the number of beans fetched per Page
* This is being deprecated in favour of the simplier {@link Query#findPagedList(int, int)}.
*
* @deprecated
*/
public PagingList<T> findPagingList(int pageSize);
/**
* Return a PagedList for this query.
* <p>
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
* </p>
* <p>
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
* limit the result set.
* </p>
*
* @param pageIndex
* The zero based index of the page.
* @param pageSize
* The number of beans to return per page.
* @return The PagedList
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Set a named bind parameter. Named parameters have a colon to prefix the
* name.
+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) {
@@ -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 {
}
@@ -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)
@@ -137,22 +137,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);
/**
* 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
@@ -39,8 +39,6 @@ public final class EntityBeanIntercept implements Serializable {
private transient PersistenceContext persistenceContext;
private transient BeanLoader beanLoader;
private int beanLoaderIndex;
private String ebeanServerName;
@@ -182,6 +180,13 @@ public final class EntityBeanIntercept implements Serializable {
public Object getEmbeddedOwner() {
return embeddedOwner;
}
/**
* Return the property index (for the parent) of this embedded bean.
*/
public int getEmbeddedOwnerIndex() {
return embeddedOwnerIndex;
}
/**
* Special case for a OneToOne, Set the parent bean (by relationship). This is
@@ -193,34 +198,22 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Return the index position for batch loading via BeanLoader.
* Set the BeanLoader with PersistenceContext.
*/
public int getBeanLoaderIndex() {
return beanLoaderIndex;
}
/**
* Set Lazy Loading by ebeanServerName.
* <p>
* This is for reference beans created by themselves.
* </p>
*/
public void setBeanLoaderByServerName(String ebeanServerName) {
this.beanLoaderIndex = 0;
this.beanLoader = null;
this.ebeanServerName = ebeanServerName;
}
/**
* Set the BeanLoader for general lazy loading.
*/
public void setBeanLoader(int index, BeanLoader beanLoader, PersistenceContext ctx) {
this.beanLoaderIndex = index;
public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
this.beanLoader = beanLoader;
this.persistenceContext = ctx;
this.ebeanServerName = beanLoader.getName();
}
/**
* Set the BeanLoader.
*/
public void setBeanLoader(BeanLoader beanLoader) {
this.beanLoader = beanLoader;
this.ebeanServerName = beanLoader.getName();
}
public boolean isFullyLoadedBean() {
return fullyLoadedBean;
}
@@ -444,6 +437,9 @@ public final class EntityBeanIntercept implements Serializable {
return -1;
}
/**
* Return the property name for the given property.
*/
public String getProperty(int propertyIndex) {
if (propertyIndex == -1) {
return null;
@@ -451,18 +447,38 @@ public final class EntityBeanIntercept implements Serializable {
return owner._ebean_getPropertyName(propertyIndex);
}
/**
* Return the number of properties.s
*/
public int getPropertyLength() {
return owner._ebean_getPropertyNames().length;
}
/**
* Set the property to be treated as unloaded. Used for properties initialised in default
* constructor.
*/
public void setPropertyUnloaded(int propertyIndex) {
loadedProps[propertyIndex] = false;
}
/**
* Set the property to be loaded.
*/
public void setLoadedProperty(int propertyIndex) {
loadedProps[propertyIndex] = true;
}
/**
* Return true if the property is loaded.
*/
public boolean isLoadedProperty(int propertyIndex) {
return loadedProps[propertyIndex];
}
/**
* Return true if the property is considered changed.
*/
public boolean isChangedProperty(int propertyIndex) {
return (changedProps != null && changedProps[propertyIndex]);
}
@@ -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);
}
}
@@ -45,12 +45,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
*/
protected final String propertyName;
/**
* Flag set to true if rows are limited by firstRow maxRows and more rows
* exist. For use by client to enable 'next' for paging.
*/
protected boolean hasMoreRows;
protected ModifyHolder<E> modifyHolder;
protected ModifyListenMode modifyListenMode;
@@ -151,26 +145,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.readOnly = readOnly;
}
/**
* Set to true if maxRows was hit and there are actually more rows available.
* <p>
* Can be used by client code that is paging through results using
* setFirstRow() setMaxRows(). If this returns true then the client can
* display a 'next' button etc.
* </p>
*/
public boolean hasMoreRows() {
return hasMoreRows;
}
/**
* Set to true when maxRows is hit but there are actually more rows available.
* This is set so that client code knows that there is more data available.
*/
public void setHasMoreRows(boolean hasMoreRows) {
this.hasMoreRows = hasMoreRows;
}
protected void checkReadOnly() {
if (readOnly) {
String msg = "This collection is in ReadOnly mode";
@@ -153,7 +153,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanList ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -163,7 +163,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
} else {
sb.append("size[").append(list.size()).append("] ");
sb.append("hasMoreRows[").append(hasMoreRows).append("] ");
sb.append("list").append(list).append("");
}
return sb.toString();
@@ -154,7 +154,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanMap ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -164,7 +164,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" map").append(map);
}
return sb.toString();
@@ -143,7 +143,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
public String toString() {
StringBuffer sb = new StringBuffer();
StringBuffer sb = new StringBuffer(50);
sb.append("BeanSet ");
if (isReadOnly()) {
sb.append("readOnly ");
@@ -153,7 +153,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
} else {
sb.append("size[").append(set.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" set").append(set);
}
return sb.toString();
@@ -32,6 +32,8 @@ public class DataSourceConfig {
private int isolationLevel = Transaction.READ_COMMITTED;
private boolean autoCommit;
private String heartbeatSql;
private int heartbeatFreqSecs = 30;
@@ -131,6 +133,20 @@ public class DataSourceConfig {
public void setIsolationLevel(int isolationLevel) {
this.isolationLevel = isolationLevel;
}
/**
* Return autoCommit setting.
*/
public boolean isAutoCommit() {
return autoCommit;
}
/**
* Set to true to turn on autoCommit.
*/
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
/**
* Return the minimum number of connections the pool should maintain.
@@ -450,6 +466,7 @@ public class DataSourceConfig {
String dbUrl = properties.get(prefix + "databaseUrl", null);
this.url = properties.get(prefix + "url", dbUrl);
this.autoCommit = properties.getBoolean(prefix + "autoCommit", false);
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
@@ -1,11 +1,10 @@
package com.avaje.ebean.config;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
/**
* A map like structure of properties.
@@ -68,9 +67,8 @@ final class PropertyMap implements Serializable {
}
synchronized void putAll(Map<String, String> keyValueMap) {
Iterator<Entry<String, String>> it = keyValueMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@@ -1,20 +1,18 @@
package com.avaje.ebean.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper used to load the PropertyMap.
*/
@@ -81,9 +79,7 @@ final class PropertyMapLoader {
}
// put values in initially without any evaluation
Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Object> entry = it.next();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = ((String) entry.getKey()).toLowerCase();
String val = ((String) entry.getValue());
if (val != null) {
@@ -20,6 +20,7 @@ import com.avaje.ebean.event.ServerConfigStartup;
import com.avaje.ebean.event.TransactionEventListener;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.util.ClassUtil;
import com.fasterxml.jackson.core.JsonFactory;
/**
* The configuration used for creating a EbeanServer.
@@ -175,6 +176,13 @@ public class ServerConfig {
*/
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
/**
* 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.
*/
@@ -204,6 +212,12 @@ public class ServerConfig {
* 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<?>>();
@@ -228,6 +242,8 @@ public class ServerConfig {
private boolean collectQueryOrigins;
private JsonFactory jsonFactory;
/**
* Construct a Server Configuration for programmatically creating an
* EbeanServer.
@@ -235,6 +251,24 @@ public class ServerConfig {
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 name of the EbeanServer.
@@ -576,6 +610,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.
@@ -796,6 +844,21 @@ public class ServerConfig {
this.dbEncrypt = dbEncrypt;
}
/**
* Return true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public boolean isUuidStoreAsBinary() {
return uuidStoreAsBinary;
}
/**
* Set to true if UUID should be stored as binary(16) (as opposed to varchar(40)).
*/
public void setUuidStoreAsBinary(boolean uuidStoreAsBinary) {
this.uuidStoreAsBinary = uuidStoreAsBinary;
}
/**
* Set to true to run the DDL generation on startup.
*/
@@ -1260,6 +1323,7 @@ public class ServerConfig {
loadDataSourceSettings(p);
autoCommitMode = p.getBoolean("autoCommitMode", false);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", false);
namingConvention = createNamingConvention(p);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
@@ -1282,7 +1346,7 @@ public class ServerConfig {
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", true);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", true);
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", true);
@@ -1299,6 +1363,7 @@ public class ServerConfig {
databaseBooleanTrue = p.get("databaseBooleanTrue", null);
databaseBooleanFalse = p.get("databaseBooleanFalse", null);
databasePlatformName = p.get("databasePlatformName", null);
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", false);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1);
queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE);
@@ -57,6 +57,13 @@ public final class TableName {
this.name = split[len - 1];
}
/**
* Parse a qualifiedTableName that might include a catalog and schema and just return the table name.
*/
public static String parse(String qualifiedTableName) {
return new TableName(qualifiedTableName).getName();
}
public String toString() {
return getQualifiedName();
}
@@ -121,6 +128,18 @@ public final class TableName {
return buffer.toString();
}
/**
* Append a catalog and schema prefix if they exist to the string builder.
*/
public void appendCatalogAndSchema(StringBuilder buffer) {
if (catalog != null) {
buffer.append(catalog).append(".");
}
if (schema != null) {
buffer.append(schema).append(".");
}
}
/**
* Checks if is table name is valid i.e. it has at least a name.
*
@@ -6,6 +6,7 @@ import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,40 +15,61 @@ import org.slf4j.LoggerFactory;
*/
public class DatabasePlatform {
/** The Constant logger. */
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatform.class);
/** The open quote used by quoted identifiers. */
/**
* The open quote used by quoted identifiers.
*/
protected String openQuote = "\"";
/** The close quote used by quoted identifiers. */
/**
* The close quote used by quoted identifiers.
*/
protected String closeQuote = "\"";
/** For limit/offset, row_number etc limiting of SQL queries. */
/**
* For limit/offset, row_number etc limiting of SQL queries.
*/
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
/** Mapping of JDBC to Database types. */
/**
* Mapping of JDBC to Database types.
*/
protected DbTypeMap dbTypeMap = new DbTypeMap();
/** DB specific DDL syntax. */
/**
* DB specific DDL syntax.
*/
protected DbDdlSyntax dbDdlSyntax = new DbDdlSyntax();
/** Defines DB identity/sequence features. */
/**
* Defines DB identity/sequence features.
*/
protected DbIdentity dbIdentity = new DbIdentity();
/** The JDBC type to map booleans to (by default). */
/**
* The JDBC type to map booleans to (by default).
*/
protected int booleanDbType = Types.BOOLEAN;
/** The JDBC type to map Blob to. */
/**
* The JDBC type to map Blob to.
*/
protected int blobDbType = Types.BLOB;
/** The JDBC type to map Clob to. */
/**
* The JDBC type to map Clob to.
*/
protected int clobDbType = Types.CLOB;
/** For Oracle treat empty strings as null. */
/**
* For Oracle treat empty strings as null.
*/
protected boolean treatEmptyStringsAsNull;
/** The name. */
/**
* The database platform name.
*/
protected String name = "generic";
/**
@@ -57,6 +79,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;
@@ -299,4 +326,13 @@ public class DatabasePlatform {
return sql;
}
/**
* Returns the like clause used by this database platform.
* <p>
* This may include an escape clause to disable a default escape character.
*/
public String getLikeClause() {
return likeClause;
}
}
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.TableName;
/**
* Used to support DB specific syntax for DDL generation.
*/
@@ -9,7 +11,7 @@ public class DbDdlSyntax {
private boolean inlinePrimaryKeyConstraint = false;
private boolean addOneToOneUniqueContraint = false;
private boolean addOneToOneUniqueContraint = true;
private int maxConstraintNameLength = 32;
@@ -43,6 +45,17 @@ public class DbDdlSyntax {
return pk;
}
/**
* Return the column definition for an identity column.
*/
public String getIdentityColumnDefn(String columnDefn) {
String identity = getIdentity();
if (identity != null && identity.length() > 0) {
return columnDefn+" "+identity;
}
return columnDefn;
}
/**
* Return the identity clause for DB's that have identities.
*/
@@ -248,26 +261,29 @@ public class DbDdlSyntax {
this.inlinePrimaryKeyConstraint = inlinePrimaryKeyConstraint;
}
/**
* Builds and returns a fully index name.
*/
public String getIndexName(String table, String propName, int ixCount) {
StringBuilder buffer = new StringBuilder();
buffer.append("ix_");
buffer.append(table);
buffer.append("_");
buffer.append(propName);
StringBuilder buffer = new StringBuilder(30);
buffer.append("ix_").append(TableName.parse(table));
buffer.append("_").append(propName);
addSuffix(buffer, ixCount);
return buffer.toString();
}
/**
* Builds and returns a fully qualified foreign key constraint name.
*/
public String getForeignKeyName(String table, String propName, int fkCount) {
StringBuilder buffer = new StringBuilder();
buffer.append("fk_");
buffer.append(table);
buffer.append("_");
buffer.append(propName);
StringBuilder buffer = new StringBuilder(30);
buffer.append("fk_").append(TableName.parse(table));
buffer.append("_").append(propName);
addSuffix(buffer, fkCount);
@@ -276,15 +292,10 @@ public class DbDdlSyntax {
/**
* Adds the suffix.
*
* @param buffer
* the buffer
* @param count
* the count
*/
protected void addSuffix(StringBuilder buffer, int count) {
final String suffixNr = Integer.toString(count);
final int suffixLen = suffixNr.length() + 1;
String suffixNr = Integer.toString(count);
int suffixLen = suffixNr.length() + 1;
if (buffer.length() + suffixLen > maxConstraintNameLength) {
buffer.setLength(maxConstraintNameLength - suffixLen);
@@ -14,6 +14,7 @@ public class H2Platform extends DatabasePlatform {
super();
this.name = "h2";
this.dbEncrypt = new H2DbEncrypt();
this.likeClause = "like ? escape''";
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
@@ -17,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();
@@ -0,0 +1,73 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
/**
* Postgres v8.3 specific platform.
* <p>
* No support for getGeneratedKeys.
* </p>
*/
public class Postgres8Platform extends DatabasePlatform {
public Postgres8Platform() {
super();
this.name = "postgres";
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
if (colAlias == null) {
// Postgres requires the "as" keyword for column alias
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
}
this.openQuote = "\"";
this.closeQuote = "\"";
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
dbTypeMap.put(Types.BINARY, new DbType("bytea", false));
dbTypeMap.put(Types.VARBINARY, new DbType("bytea", false));
dbTypeMap.put(Types.BLOB, new DbType("bytea", false));
dbTypeMap.put(Types.CLOB, new DbType("text"));
dbTypeMap.put(Types.LONGVARBINARY, new DbType("bytea", false));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
dbDdlSyntax.setDropTableCascade("cascade");
dbDdlSyntax.setDropIfExists("if exists");
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
String seqName, int batchSize) {
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
}
@Override
protected String withForUpdate(String sql) {
return sql + " for update";
}
}
@@ -0,0 +1,25 @@
package com.avaje.ebean.config.dbplatform;
public class PostgresDdlSyntax extends DbDdlSyntax {
/**
* Map bigint, integer and smallint into their equivilent serial types.
*/
@Override
public String getIdentityColumnDefn(String columnDefn) {
//smallserial, serial and bigserial
if ("bigint".equalsIgnoreCase(columnDefn)) {
return "bigserial";
}
if ("integer".equalsIgnoreCase(columnDefn)) {
return "serial";
}
if ("smallint".equalsIgnoreCase(columnDefn)) {
return "smallserial";
}
return columnDefn;
}
}
@@ -4,27 +4,38 @@ import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.GlobalProperties;
import javax.sql.DataSource;
import java.sql.Types;
/**
* Postgres v8.3 specific platform.
* Postgres v9 specific platform.
* <p>
* No support for getGeneratedKeys.
* Uses serial types and getGeneratedKeys.
* </p>
*/
public class PostgresPlatform extends DatabasePlatform {
/**
* Unique jdbc type id defined for hstore type.
*/
public static final int TYPE_HSTORE = 4001;
public PostgresPlatform() {
super();
this.name = "postgres";
this.likeClause = "like ? escape''";
this.dbDdlSyntax = new PostgresDdlSyntax();
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
// Use Identity and getGeneratedKeys
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
@@ -36,8 +47,8 @@ public class PostgresPlatform extends DatabasePlatform {
this.openQuote = "\"";
this.closeQuote = "\"";
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(TYPE_HSTORE, new DbType("hstore"));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
@@ -15,7 +15,7 @@ public class RowNumberSqlLimiter implements SqlLimiter {
*/
private static final String ROW_NUMBER_AS = ") as rn, ";
final String rowNumberWindowAlias;
private final String rowNumberWindowAlias;
/**
* Specify the name of the rowNumberWindowAlias.
@@ -30,16 +30,18 @@ public class RowNumberSqlLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
sb.append("select * from (").append(NEW_LINE);
sb.append("select * from ( ");
sb.append("select ");
if (request.isDistinct()) {
@@ -50,9 +52,9 @@ public class RowNumberSqlLimiter implements SqlLimiter {
sb.append(request.getDbOrderBy());
sb.append(ROW_NUMBER_AS);
sb.append(request.getDbSql());
sb.append(dbSql);
sb.append(NEW_LINE).append(") ");
sb.append(" ) ");
sb.append(rowNumberWindowAlias);
sb.append(" where ");
if (firstRow > 0) {
@@ -35,38 +35,40 @@ public class RownumSqlLimiter implements SqlLimiter {
// :MAX_ROW_TO_FETCH )
// where rnum >= :MIN_ROW_TO_FETCH;
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow = lastRow + firstRow + 1;
lastRow = lastRow + firstRow;
}
sb.append("select * ").append(NEW_LINE).append("from ( ");
sb.append("select * from ( ");
sb.append("select ");
if (useFirstRowsHint && request.getMaxRows() > 0) {
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows() + 1).append(") */ ");
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
}
sb.append("rownum ").append(rnum).append(", a.* ").append(NEW_LINE);
sb.append(" from (");//
sb.append("rownum ").append(rnum).append(", a.* ");
sb.append(" from (");
sb.append(" select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
sb.append(NEW_LINE).append(" ) a ");
sb.append(NEW_LINE).append(" ) a ");
if (lastRow > 0) {
sb.append(NEW_LINE).append(" where rownum <= ").append(lastRow);
sb.append(" where rownum <= ").append(lastRow);
}
sb.append(NEW_LINE).append(" ) ");
sb.append(" ) ");
if (firstRow > 0) {
sb.append(NEW_LINE).append("where ");
sb.append(" where ");
sb.append(rnum).append(" > ").append(firstRow);
}
@@ -8,15 +8,12 @@ public class SqlAnywhereLimiter implements SqlLimiter {
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(500);
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int maxRows = request.getMaxRows();
if (maxRows > 0) {
// fetch 1 more than we return so that
// we know if more rows are available
maxRows = maxRows + 1;
}
/*
* SELECT TOP xx START AT xx ... FROM ...
@@ -31,7 +28,7 @@ public class SqlAnywhereLimiter implements SqlLimiter {
if (firstRow > 0) {
sb.append("start at ").append(firstRow + 1).append(" ");
}
sb.append(request.getDbSql());
sb.append(dbSql);
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
@@ -5,8 +5,6 @@ import com.avaje.ebean.Query;
/**
* The request object for the query that can have sql limiting applied to it
* (such as a LIMIT OFFSET clause).
*
* @author rob
*/
public interface SqlLimitRequest {
@@ -5,7 +5,7 @@ import java.util.Set;
/**
* Used to enhance or override the default bean persistence mechanism.
* <p>
* Note that if want to totally change the finding, you need to use a BeanFinder
* Note that if want to totally change the finding, you need to use a BeanQueryAdapter
* rather than using postLoad().
* </p>
* <p>
@@ -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
*/
@@ -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,131 +1,99 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.List;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
/**
* Converts objects to and from JSON format.
*
* @author rbygrave
*/
public interface JsonContext {
/**
* Convert json string input into a Bean of a specific type.
*/
public <T> T toBean(Class<T> rootType, String json);
public <T> T toBean(Class<T> rootType, String json) throws IOException;
/**
* Convert json reader input into a Bean of a specific type.
*/
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 IOException;
/**
* Convert json string input into a list of beans of a specific type.
*/
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 IOException;
/**
* Convert json reader input into a list of beans of a specific type.
* @throws IOException
*/
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 IOException;
/**
* Use the genericType to determine if this should be converted into a List or
* bean.
*/
public Object toObject(Type genericType, Reader json, JsonReadOptions options);
public Object toObject(Type genericType, Reader json) throws IOException;
/**
* Use the genericType to determine if this should be converted into a List or
* bean.
*/
public Object toObject(Type genericType, String json, JsonReadOptions options);
public Object toObject(Type genericType, String json) throws IOException;
/**
* Write the bean or collection in JSON format to the writer with default
* options.
*
* @param o
* @param value
* the bean or collection of beans to write
* @param writer
* used to write the json output to
*/
public void toJsonWriter(Object o, Writer writer);
/**
* With additional pretty output option.
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty);
public void toJson(Object value, Writer writer) throws IOException;
/**
* With additional options to specify JsonValueAdapter and
* JsonWriteBeanVisitor's.
*
* @param o
* @param value
* 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
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options);
/**
* With additional JSONP callback function.
*/
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options,
String callback);
public void toJson(Object value, Writer writer, JsonWriteOptions options) throws IOException;
/**
* Convert a bean or collection to json string using default options.
*/
public String toJsonString(Object o);
public String toJson(Object value) throws IOException;
/**
* Convert a bean or collection to json string with pretty format using
* default options.
* Convert a bean or collection to json string.
*/
public String toJsonString(Object o, boolean pretty);
public String toJson(Object value, JsonWriteOptions options) throws IOException;
/**
* Convert a bean or collection to json string using options.
*/
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options);
/**
* Convert a bean or collection to json string using a JSONP callback.
*/
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback);
/**
* Return true if the type is known as an Entity or Xml type or a List Set or
* Map of known bean types.
* 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.
*/
public JsonGenerator createGenerator(Writer writer) throws IOException;
/**
* Create and return a new JsonParser for the given reader.
*/
public JsonParser createParser(Reader reader) throws IOException;
}
@@ -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);
}
}
@@ -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,78 +1,23 @@
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;
/**
* 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);
* String s = json.toJson(list, true, writeOptions);
*
* </pre>
*
* @see JsonContext#toList(Class, String, JsonReadOptions)
*
* @author rbygrave
*
*/
public class JsonWriteOptions {
protected String callback;
protected JsonValueAdapter valueAdapter;
protected Map<String, JsonWriteBeanVisitor<?>> visitorMap;
protected PathProperties pathProperties;
/**
@@ -88,138 +33,6 @@ public class JsonWriteOptions {
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>
*/
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.util.Objects;
/**
* A hash for a query plan.
*/
@@ -26,7 +24,7 @@ public class HashQueryPlan {
public int hashCode() {
int hc = planHash;
hc = hc * 31 + bindCount;
hc = hc * 31 + Objects.hashCode(rawSql);
hc = hc * 31 + (rawSql == null ? 0 : rawSql.hashCode());
return hc;
}
@@ -41,6 +39,6 @@ public class HashQueryPlan {
HashQueryPlan e = (HashQueryPlan) obj;
return e.planHash == planHash
&& e.bindCount == bindCount
&& Objects.equals(e.rawSql, rawSql);
&& ((e.rawSql == rawSql) || (e.rawSql != null && e.rawSql.equals(rawSql)));
}
}
@@ -1,7 +1,5 @@
package com.avaje.ebeaninternal.api;
import java.util.Objects;
/**
* Used to build HashQueryPlan instances.
*/
@@ -33,7 +31,7 @@ public class HashQueryPlanBuilder {
* Add an object to the hash calculation.
*/
public HashQueryPlanBuilder add(Object object) {
planHash = planHash * 31 + Objects.hashCode(object);
planHash = planHash * 31 + (object == null ? 0 : object.hashCode());
return this;
}
@@ -2,8 +2,8 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading ManyToOne and OneToOne relationships.
@@ -18,10 +18,12 @@ public class LoadBeanRequest extends LoadRequest {
private final boolean loadCache;
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, Transaction transaction, boolean lazy, String lazyLoadProperty,
boolean loadCache) {
super(transaction, lazy);
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, boolean lazy, String lazyLoadProperty, boolean loadCache) {
this(LoadBuffer, null, lazy, lazyLoadProperty, loadCache);
}
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
super(parentRequest, lazy);
this.LoadBuffer = LoadBuffer;
this.batch = LoadBuffer.getBatch();
this.lazyLoadProperty = lazyLoadProperty;
@@ -2,69 +2,71 @@ package com.avaje.ebeaninternal.api;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
* Request for loading Associated Many Beans.
*/
public class LoadManyRequest extends LoadRequest {
private final List<BeanCollection<?>> batch;
private final List<BeanCollection<?>> batch;
private final LoadManyBuffer loadContext;
private final LoadManyBuffer loadContext;
private final boolean onlyIds;
private final boolean onlyIds;
private final boolean loadCache;
public LoadManyRequest(LoadManyBuffer loadContext, Transaction transaction, int batchSize, boolean lazy,
private final boolean loadCache;
public LoadManyRequest(LoadManyBuffer loadContext, int batchSize, boolean lazy,boolean onlyIds, boolean loadCache) {
this(loadContext, null, batchSize, lazy, onlyIds, loadCache);
}
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, int batchSize, boolean lazy,
boolean onlyIds, boolean loadCache) {
super(transaction, lazy);
this.loadContext = loadContext;
this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
super(parentRequest, lazy);
this.loadContext = loadContext;
this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
public String getDescription() {
return "path:" + loadContext.getFullPath() + " size:"+ batch.size();
}
public String getDescription() {
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
/**
* Return the batch of collections to actually load.
*/
public List<BeanCollection<?>> getBatch() {
return batch;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return the load context.
*/
public LoadManyBuffer getLoadContext() {
return loadContext;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such
* as clear() and removeAll() where it generally makes sense to
* only fetch the Id values as the other property information is
* not used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if lazy loading should only load the id values.
* <p>
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
* generally makes sense to only fetch the Id values as the other property information is not
* used.
* </p>
*/
public boolean isOnlyIds() {
return onlyIds;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
/**
* Return true if we should load the Collection ids into the cache.
*/
public boolean isLoadCache() {
return loadCache;
}
}
@@ -1,22 +1,36 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* Request for loading Associated One Beans.
*/
public abstract class LoadRequest {
protected final boolean lazy;
protected final OrmQueryRequest<?> parentRequest;
protected final Transaction transaction;
protected final Transaction transaction;
public LoadRequest(Transaction transaction, boolean lazy) {
protected final boolean lazy;
this.transaction = transaction;
public LoadRequest(OrmQueryRequest<?> parentRequest, boolean lazy) {
this.parentRequest = parentRequest;
this.transaction = parentRequest == null ? null : parentRequest.getTransaction();
this.lazy = lazy;
}
/**
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
* true. This is for testing purposes to confirm the secondary query executes etc.
*/
public void logSecondaryQuery(SpiQuery<?> query) {
if (parentRequest != null && parentRequest.isLogSecondaryQuery()) {
parentRequest.getQuery().logSecondaryQuery(query);
}
}
/**
* Return true if this is a lazy load and false if it is a secondary query.
*/
@@ -1,13 +1,15 @@
package com.avaje.ebeaninternal.api;
import java.io.Serializable;
import java.util.Set;
import java.util.Collection;
import java.util.TreeMap;
import java.util.TreeSet;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Holds the joins needs to support the many where predicates.
@@ -17,12 +19,32 @@ public class ManyWhereJoins implements Serializable {
private static final long serialVersionUID = -6490181101871795417L;
private final TreeSet<String> joins = new TreeSet<String>();
private final TreeMap<String,PropertyJoin> joins = new TreeMap<String,PropertyJoin>();
private StringBuilder formulaProperties = new StringBuilder();
private boolean formulaWithJoin;
/**
* 'Mode' indicating that joins added while this is true are required to be outer joins.
*/
private boolean requireOuterJoins;
/**
* Return the current 'mode' indicating if outer joins are currently required or not.
*/
public boolean isRequireOuterJoins() {
return requireOuterJoins;
}
/**
* Set the 'mode' to be that joins added are required to be outer joins.
* This is set during the evaluation of disjunction predicates.
*/
public void setRequireOuterJoins(boolean requireOuterJoins) {
this.requireOuterJoins = requireOuterJoins;
}
/**
* Add a many where join.
*/
@@ -31,13 +53,15 @@ public class ManyWhereJoins implements Serializable {
String join = elProp.getElPrefix();
BeanProperty p = elProp.getBeanProperty();
if (p instanceof BeanPropertyAssocMany<?>){
join = addManyToJoin(join, p.getName());
join = addManyToJoin(join, p.getName());
}
if (join != null){
joins.add(join);
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
joins.add(join+"."+secondaryTableJoinPrefix);
addJoin(join);
if (p != null) {
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
addJoin(join+"."+secondaryTableJoinPrefix);
}
}
addParentJoins(join);
}
@@ -58,11 +82,16 @@ public class ManyWhereJoins implements Serializable {
private void addParentJoins(String join) {
String[] split = SplitName.split(join);
if (split[0] != null){
joins.add(split[0]);
addJoin(split[0]);
addParentJoins(split[0]);
}
}
private void addJoin(String property) {
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
joins.put(property, new PropertyJoin(property, joinType));
}
/**
* Return true if there are no extra many where joins.
*/
@@ -73,10 +102,22 @@ public class ManyWhereJoins implements Serializable {
/**
* Return the set of many where joins.
*/
public Set<String> getJoins() {
return joins;
public Collection<PropertyJoin> getPropertyJoins() {
return joins.values();
}
/**
* Return the set of property names for the many where joins.
*/
public TreeSet<String> getPropertyNames() {
TreeSet<String> propertyNames = new TreeSet<String>();
for (PropertyJoin join : joins.values()) {
propertyNames.add(join.getProperty());
}
return propertyNames;
}
/**
* In findRowCount query found a formula property with a join clause so building a select clause
* specifically for the findRowCount query.
@@ -0,0 +1,39 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Represents a join required for a given property and whether than needs to be an outer join.
*/
public class PropertyJoin {
/**
* The property name.
*/
private final String property;
/**
* Set to true if the property needs to be an outer join.
*/
private final SqlJoinType joinType;
public PropertyJoin(String property, SqlJoinType joinType) {
this.property = property;
this.joinType = joinType;
}
/**
* Return the property that should be joined.
*/
public String getProperty() {
return property;
}
/**
* Return true if this join is required to be an outer join.
*/
public SqlJoinType getSqlJoinType() {
return joinType;
}
}
@@ -10,13 +10,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public interface SpiExpression extends Expression {
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent
* join clause.
* </p>
*/
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
/**
@@ -1,13 +1,12 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
public interface SpiExpressionFactory extends ExpressionFactory {
/**
* Create another expression factory with a given sub path.
*/
public ExpressionFactory createExpressionFactory();//FilterExprPath prefix);
/**
* Create another expression factory with a given sub path.
*/
public ExpressionFactory createExpressionFactory();
}
@@ -44,9 +44,14 @@ public interface SpiExpressionRequest {
* Return the ordered list of bind values for all expressions in this request.
*/
public ArrayList<Object> getBindValues();
/**
* Increments the parameter index and returns that value.
*/
public int nextParameter();
/**
* Increments the parameter index and returns that value.
*/
public int nextParameter();
/**
* Append a DB Like clause.
*/
public void appendLike();
}
@@ -260,10 +260,25 @@ public interface SpiQuery<T> extends Query<T> {
*/
public Boolean isAutofetch();
// /**
// * Return explicit forUpdate setting or null.
// */
// public boolean isForUpdate();
/**
* Set to true if you want to capture executed secondary queries.
*/
public void setLogSecondaryQuery(boolean logSecondaryQuery);
/**
* Return true if executed secondary queries should be captured.
*/
public boolean isLogSecondaryQuery();
/**
* Return the list of secondary queries that were executed.
*/
public List<SpiQuery<?>> getLoggedSecondaryQueries();
/**
* Log an executed secondary query.
*/
public void logSecondaryQuery(SpiQuery<?> query);
/**
* If return null then no autoFetch profiling for this query. If a
@@ -517,12 +532,24 @@ public interface SpiQuery<T> extends Query<T> {
* Return the index of the first row to return in the query.
*/
public int getFirstRow();
/**
* Internally set by Ebean when this query must use the DISTINCT keyword.
* <p>
* This does not exclude/remove the use of the id property.
*/
public Query<T> setSqlDistinct(boolean sqlDistinct);
/**
* return true if this query uses DISTINCT.
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
*/
public boolean isDistinctQuery();
/**
* Return true if this query has been specified by a user to use DISTINCT.
*/
public boolean isDistinct();
/**
* Set default select clauses where none have been explicitly defined.
*/
@@ -15,6 +15,11 @@ import com.avaje.ebeaninternal.server.persist.BatchControl;
*/
public interface SpiTransaction extends Transaction {
/**
* End the transaction when had query only use.
*/
public void endQueryOnly();
/**
* Return the string prefix with the transactin id and label used in logging.
*/
@@ -15,10 +15,20 @@ public class CachedBeanDataFromBean {
Object[] data = new Object[desc.getPropertyCount()];
boolean[] loaded = new boolean[desc.getPropertyCount()];
BeanProperty idProperty = desc.getIdProperty();
if (idProperty != null) {
int propertyIndex = idProperty.getPropertyIndex();
if (ebi.isLoadedProperty(propertyIndex)) {
// extract the id property value
data[propertyIndex] = idProperty.getCacheDataValue(bean);
loaded[propertyIndex] = true;
}
}
BeanProperty[] props = desc.propertiesNonMany();
Object naturalKey = null;
// extract all the non-many properties
for (int i = 0; i < props.length; i++) {
BeanProperty prop = props[i];
if (ebi.isLoadedProperty(prop.getPropertyIndex())) {
@@ -13,19 +13,17 @@ public class CachedBeanDataToBean {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
BeanProperty idProperty = desc.getIdProperty();
if (idProperty != null) {
// load the id property
loadProperty(bean, cacheBeanData, ebi, idProperty);
}
// load the non-many properties
BeanProperty[] props = desc.propertiesNonMany();
for (int i = 0; i < props.length; i++) {
BeanProperty prop = props[i];
int propertyIndex = prop.getPropertyIndex();
if (cacheBeanData.isLoaded(propertyIndex)) {
if (ebi.isLoadedProperty(propertyIndex)) {
// already loaded (lazy load on partially loaded bean)
} else {
Object data = cacheBeanData.getData(propertyIndex);
prop.setCacheDataValue(bean, data);
}
}
loadProperty(bean, cacheBeanData, ebi, props[i]);
}
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
@@ -38,4 +36,17 @@ public class CachedBeanDataToBean {
return true;
}
private static void loadProperty(EntityBean bean, CachedBeanData cacheBeanData, EntityBeanIntercept ebi, BeanProperty prop) {
int propertyIndex = prop.getPropertyIndex();
if (cacheBeanData.isLoaded(propertyIndex)) {
if (ebi.isLoadedProperty(propertyIndex)) {
// already loaded (lazy load on partially loaded bean)
} else {
Object data = cacheBeanData.getData(propertyIndex);
prop.setCacheDataValue(bean, data);
}
}
}
}
@@ -1,7 +1,6 @@
package com.avaje.ebeaninternal.server.cache;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import com.avaje.ebean.annotation.CacheTuning;
@@ -89,9 +88,7 @@ public class DefaultCacheHolder {
}
public void clearAll() {
Iterator<ServerCache> it = concMap.values().iterator();
while (it.hasNext()) {
ServerCache serverCache = it.next();
for (ServerCache serverCache : concMap.values()) {
serverCache.clear();
}
}
@@ -111,9 +111,7 @@ public class DefaultServerCache implements ServerCache {
int hc = reset ? removedHitCount.getAndSet(0) : removedHitCount.get();
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
for (CacheEntry cacheEntry : map.values()) {
hc += cacheEntry.getHitCount(reset);
}
@@ -268,10 +266,6 @@ public class DefaultServerCache implements ServerCache {
return map.size();
}
private Iterator<CacheEntry> cacheEntries() {
return map.values().iterator();
}
/**
* The task used to periodically trim the cache.
*/
@@ -296,7 +290,7 @@ public class DefaultServerCache implements ServerCache {
long idleExpire = System.currentTimeMillis() - (maxIdleSecs*1000);
long ttlExpire = System.currentTimeMillis() - (maxSecsToLive*1000);
Iterator<CacheEntry> it = cacheEntries();
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
if (maxIdleSecs > 0 && idleExpire > cacheEntry.getLastAccessTime()) {
@@ -8,6 +8,7 @@ import java.sql.Types;
import java.util.Calendar;
import java.util.UUID;
import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary;
/**
* Default implementation of TypeConverter.
@@ -192,6 +193,9 @@ public final class BasicTypeConverter implements Serializable {
if (value instanceof String) {
return UUID.fromString((String) value);
}
if (value instanceof byte[]) {
return ScalarTypeUUIDBinary.convertFromBytes((byte[])value);
}
return (UUID) value;
}
@@ -32,8 +32,6 @@ public abstract class BeanRequest {
protected boolean createdTransaction;
protected boolean readOnly;
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
this.ebeanServer = ebeanServer;
this.serverName = ebeanServer.getName();
@@ -62,29 +60,19 @@ public abstract class BeanRequest {
if (transaction == null || !transaction.isActive()) {
// create an implicit transaction to execute this query
transaction = ebeanServer.createServerTransaction(false, -1);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
//if (readOnlyTransaction) {
// readOnly = true;
// transaction.setReadOnly(true);
//}
createdTransaction = true;
}
}
}
/**
* Commit this transaction if it was created for this request.
*/
public void commitTransIfRequired() {
if (createdTransaction) {
if (readOnly) {
transaction.rollback();
} else {
transaction.commit();
}
}
}
/**
* Commit this transaction if it was created for this request.
*/
public void commitTransIfRequired() {
if (createdTransaction) {
transaction.commit();
}
}
/**
* Rollback the transaction if it was created for this request.
@@ -128,17 +116,17 @@ public abstract class BeanRequest {
return transaction.getInternalConnection();
}
/**
* Return true if SQL should be logged for this transaction.
*/
public boolean isLogSql() {
return transaction.isLogSql();
}
/**
* Return true if SUMMARY information should be logged for this transaction.
*/
public boolean isLogSummary() {
return transaction.isLogSummary();
}
/**
* Return true if SQL should be logged for this transaction.
*/
public boolean isLogSql() {
return transaction.isLogSql();
}
/**
* Return true if SUMMARY information should be logged for this transaction.
*/
public boolean isLogSummary() {
return transaction.isLogSummary();
}
}
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.core;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.persistence.Embeddable;
@@ -11,6 +10,7 @@ import javax.persistence.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.ServerConfig;
@@ -65,7 +65,9 @@ public class BootupClasses implements ClassPathSearchMatcher {
public BootupClasses(List<Class<?>> list) {
if (list != null) {
process(list.iterator());
for (Class<?> cls : list) {
isMatch(cls);
}
}
}
@@ -83,13 +85,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
this.serverConfigStartupList.addAll(parent.serverConfigStartupList);
}
private void process(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> cls = it.next();
isMatch(cls);
}
}
/**
* Create a copy of this object so that classes can be added to it.
*/
@@ -297,13 +292,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
return beanListenerList;
}
public void add(Iterator<Class<?>> it) {
while (it.hasNext()) {
Class<?> clazz = it.next();
isMatch(clazz);
}
}
public boolean isMatch(Class<?> cls) {
if (isEmbeddable(cls)) {
@@ -53,16 +53,16 @@ public class DatabasePlatformFactory {
private DatabasePlatform byDatabaseName(String dbName) throws SQLException {
dbName = dbName.toLowerCase();
if (dbName.equals("postgres83")) {
if (dbName.equals("postgres") || dbName.equals("postgres9")) {
return new PostgresPlatform();
}
if (dbName.equals("postgres8") || dbName.equals("postgres83")) {
return new Postgres8Platform();
}
if (dbName.equals("oracle9")) {
return new Oracle9Platform();
}
if (dbName.equals("oracle10")) {
return new Oracle10Platform();
}
if (dbName.equals("oracle")) {
if (dbName.equals("oracle") || dbName.equals("oracle10")) {
return new Oracle10Platform();
}
if (dbName.equals("sqlserver2005")) {
@@ -23,6 +23,8 @@ import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
@@ -107,7 +109,19 @@ public class DefaultBeanLoader {
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
String orderBy = many.getLazyFetchOrderBy();
if (orderBy != null) {
query.orderBy(orderBy);
}
String extraWhere = many.getExtraWhere();
if (extraWhere != null) {
// replace special ${ta} placeholder with the base table alias
// which is always t0 and add the extra where clause
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
query.where().raw(ew);
}
query.setLazyLoadForParents(idList, many);
many.addWhereParentIdIn(query, idList);
@@ -139,6 +153,9 @@ public class DefaultBeanLoader {
desc.cacheManyPropPut(many, bc, parentId);
}
}
// log the query (for testing secondary queries)
loadRequest.logSecondaryQuery(query);
}
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
@@ -321,22 +338,33 @@ public class DefaultBeanLoader {
// necessary but allow processing to continue until it is accessed by client code
ebis[i].checkLazyLoadFailure();
}
// log the query (for testing secondary queries)
loadRequest.logSecondaryQuery(query);
}
public void refresh(EntityBean bean) {
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN);
refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1);
}
public void loadBean(EntityBeanIntercept ebi) {
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN);
refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1);
}
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode) {
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) {
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
if (EntityType.EMBEDDED == desc.getEntityType()) {
// lazy loading on an embedded bean property
EntityBean embeddedOwner = (EntityBean)ebi.getEmbeddedOwner();
int ownerIndex = ebi.getEmbeddedOwnerIndex();
refreshBeanInternal(embeddedOwner, mode, ownerIndex);
}
Object id = desc.getId(bean);
if (pc == null) {
@@ -348,7 +376,7 @@ public class DefaultBeanLoader {
}
}
if (ebi != null) {
if (embeddedOwnerIndex == -1) {
if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
// lazy loading and the bean cache is active
if (desc.cacheBeanLoad((EntityBean)bean, ebi, id)) {
@@ -365,6 +393,11 @@ public class DefaultBeanLoader {
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
}
if (embeddedOwnerIndex > -1) {
String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex);
query.select("id,"+embeddedBeanPropertyName);
}
// don't collect autoFetch usage profiling information
// as we just copy the data out of these fetched beans
// and put the data into the original bean
@@ -373,14 +406,22 @@ public class DefaultBeanLoader {
query.setMode(mode);
query.setId(id);
// make sure the query doesn't use the cache
if (mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
if (embeddedOwnerIndex > -1 || mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
// make sure the query doesn't use the cache
query.setUseCache(false);
}
if (ebi != null && ebi.isReadOnly()) {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) {
// explicitly state to load all properties on REFRESH.
// Lobs default to fetch lazy so this forces lobs to be
// included in a 'refresh' query
query.select("*");
}
Object dbBean = query.findUnique();
if (dbBean == null) {
@@ -30,6 +30,7 @@ import com.avaje.ebean.Filter;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.PagedList;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryIterator;
@@ -64,7 +65,6 @@ import com.avaje.ebean.meta.MetaBeanInfo;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonElement;
import com.avaje.ebeaninternal.api.LoadBeanRequest;
import com.avaje.ebeaninternal.api.LoadManyRequest;
import com.avaje.ebeaninternal.api.ScopeTrans;
@@ -96,6 +96,7 @@ import com.avaje.ebeaninternal.server.query.CallableQueryIds;
import com.avaje.ebeaninternal.server.query.CallableQueryList;
import com.avaje.ebeaninternal.server.query.CallableQueryRowCount;
import com.avaje.ebeaninternal.server.query.CallableSqlQueryList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagedList;
import com.avaje.ebeaninternal.server.query.LimitOffsetPagingQuery;
import com.avaje.ebeaninternal.server.query.QueryFutureIds;
import com.avaje.ebeaninternal.server.query.QueryFutureList;
@@ -284,6 +285,13 @@ public final class DefaultServer implements SpiEbeanServer {
ddlGenerator = (DdlGenerator)plugin;
}
}
if (ddlGenerator == null) {
// ServiceLoader not finding ddlGenerator (typically OSGi)
ddlGenerator = new DdlGenerator();
spiPlugins.add(ddlGenerator);
ddlGenerator.setup(this, this.getDatabasePlatform(), config.getServerConfig());
}
ebeanPlugins = Collections.unmodifiableList(spiPlugins);
}
@@ -1110,7 +1118,7 @@ public final class DefaultServer implements SpiEbeanServer {
// predicates on *ToMany properties
if (query.initManyWhereJoins()) {
// we need a sql distinct now
query.setDistinct(true);
query.setSqlDistinct(true);
}
boolean allowOneManyFetch = true;
@@ -1196,18 +1204,12 @@ public final class DefaultServer implements SpiEbeanServer {
}
SpiOrmQueryRequest<T> request = createQueryRequest(desc, spiQuery, t);
try {
request.initTransIfRequired();
return (T) request.findId();
T bean = (T) request.findId();
} finally {
request.endTransIfRequired();
return bean;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1254,15 +1256,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
Set<T> set = (Set<T>) request.findSet();
return (Set<T>) request.findSet();
} finally {
request.endTransIfRequired();
return set;
} catch (RuntimeException ex) {
// String stackTrace = throwablePrinter.print(ex);
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1278,15 +1275,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
Map<?, T> map = (Map<?, T>) request.findMap();
return (Map<?, T>) request.findMap();
} finally {
request.endTransIfRequired();
return map;
} catch (RuntimeException ex) {
// String stackTrace = throwablePrinter.print(ex);
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1301,14 +1293,10 @@ public final class DefaultServer implements SpiEbeanServer {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ROWCOUNT, query, t);
try {
request.initTransIfRequired();
int rowCount = request.findRowCount();
return request.findRowCount();
} finally {
request.endTransIfRequired();
return rowCount;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1324,14 +1312,10 @@ public final class DefaultServer implements SpiEbeanServer {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ID_LIST, query, t);
try {
request.initTransIfRequired();
List<Object> list = request.findIds();
return request.findIds();
} finally {
request.endTransIfRequired();
return list;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1419,6 +1403,12 @@ public final class DefaultServer implements SpiEbeanServer {
return new LimitOffsetPagingQuery<T>(this, spiQuery, pageSize);
}
@Override
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
}
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
@@ -1426,10 +1416,8 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
request.findVisit(visitor);
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
} finally {
// do nothing - findVisit garuntee's cleanup of the transaction if required
}
}
@@ -1440,10 +1428,9 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
return request.findIterate();
// request.endTransIfRequired();
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
request.endTransIfRequired();
throw ex;
}
}
@@ -1460,14 +1447,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
List<T> list = request.findList();
return request.findList();
} finally {
request.endTransIfRequired();
return list;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1510,14 +1493,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
List<SqlRow> list = request.findList();
return request.findList();
} finally {
request.endTransIfRequired();
return list;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1527,14 +1506,10 @@ public final class DefaultServer implements SpiEbeanServer {
try {
request.initTransIfRequired();
Set<SqlRow> set = request.findSet();
return request.findSet();
} finally {
request.endTransIfRequired();
return set;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1543,14 +1518,10 @@ public final class DefaultServer implements SpiEbeanServer {
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
try {
request.initTransIfRequired();
Map<?, SqlRow> map = request.findMap();
return request.findMap();
} finally {
request.endTransIfRequired();
return map;
} catch (RuntimeException ex) {
request.rollbackTransIfRequired();
throw ex;
}
}
@@ -1568,6 +1539,16 @@ public final class DefaultServer implements SpiEbeanServer {
persister.save(checkEntityBean(bean), t);
}
@Override
public void markAsDirty(Object bean) {
if (bean instanceof EntityBean == false) {
throw new IllegalArgumentException("This bean is not an EntityBean?");
}
// mark the bean as dirty (so that an update will not get skipped)
((EntityBean)bean)._ebean_getIntercept().setDirty(true);
}
/**
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
*/
@@ -1981,10 +1962,6 @@ public final class DefaultServer implements SpiEbeanServer {
if (typeInfo == null) {
return false;
}
Class<?> beanType = typeInfo.getBeanType();
if (JsonElement.class.isAssignableFrom(beanType)) {
return true;
}
return getBeanDescriptor(typeInfo.getBeanType()) != null;
}
@@ -2104,11 +2081,18 @@ public final class DefaultServer implements SpiEbeanServer {
return new CallStack(finalTrace);
}
public JsonContext createJsonContext() {
@Override
public JsonContext json() {
// immutable thread safe so return shared instance
return jsonContext;
}
@Override
public JsonContext createJsonContext() {
return json();
}
@Override
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
@@ -1,22 +1,3 @@
/**
* Copyright (C) 2006 Robin Bygrave
*
* 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.ebeaninternal.server.core;
import java.sql.Connection;
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.core;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -9,8 +11,6 @@ import com.avaje.ebean.config.ExternalTransactionManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonValueAdapter;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
@@ -22,6 +22,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -30,7 +31,7 @@ import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
@@ -38,6 +39,7 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
import com.avaje.ebeaninternal.server.type.TypeManager;
import com.fasterxml.jackson.core.JsonFactory;
/**
* Used to extend the ServerConfig with additional objects used to configure and
@@ -87,10 +89,13 @@ public class InternalConfiguration {
private final XmlConfig xmlConfig;
private final JsonFactory jsonFactory;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
this.jsonFactory = serverConfig.getJsonFactory();
this.xmlConfig = xmlConfig;
this.pstmtBatch = pstmtBatch;
this.clusterManager = clusterManager;
@@ -113,8 +118,7 @@ public class InternalConfiguration {
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
this.transactionManager = new TransactionManager(clusterManager, backgroundExecutor,
serverConfig, beanDescriptorManager, this.getBootupClasses());
this.transactionManager = createTransactionManager();
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
@@ -131,19 +135,38 @@ public class InternalConfiguration {
}
}
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
private TransactionManager createTransactionManager() {
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
/**
* Return true if autoCommit mode is on.
*/
private boolean isAutoCommitMode() {
if (serverConfig.isAutoCommitMode()) {
// explicitly set
return true;
}
DataSource dataSource = serverConfig.getDataSource();
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
// We know the DataSourcePool is using autoCommit
return true;
}
return false;
}
public JsonContext createJsonContext(SpiEbeanServer server) {
String s = serverConfig.getProperty("json.pretty", "false");
boolean dfltPretty = "true".equalsIgnoreCase(s);
s = serverConfig.getProperty("json.jsonValueAdapter", null);
JsonValueAdapter va = new DefaultJsonValueAdapter();
if (s != null) {
va = (JsonValueAdapter) ClassUtil.newInstance(s, this.getClass());
}
return new DJsonContext(server, va, dfltPretty);
return new DJsonContext(server, jsonFactory);
}
public XmlConfig getXmlConfig() {
@@ -0,0 +1,266 @@
package com.avaje.ebeaninternal.server.core;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.ExternalTransactionManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine;
import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
<<<<<<< HEAD
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
=======
>>>>>>> json-refactor
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
import com.avaje.ebeaninternal.server.type.TypeManager;
/**
* Used to extend the ServerConfig with additional objects used to configure and
* construct an EbeanServer.
*/
public class InternalConfiguration {
private static final Logger logger = LoggerFactory.getLogger(InternalConfiguration.class);
private final ServerConfig serverConfig;
private final BootupClasses bootupClasses;
private final DeployInherit deployInherit;
private final ResourceManager resourceManager;
private final DeployOrmXml deployOrmXml;
private final TypeManager typeManager;
private final Binder binder;
private final DeployCreateProperties deployCreateProperties;
private final DeployUtil deployUtil;
private final BeanDescriptorManager beanDescriptorManager;
private final TransactionManager transactionManager;
private final TransactionScopeManager transactionScopeManager;
private final CQueryEngine cQueryEngine;
private final ClusterManager clusterManager;
private final ServerCacheManager cacheManager;
private final ExpressionFactory expressionFactory;
private final SpiBackgroundExecutor backgroundExecutor;
private final PstmtBatch pstmtBatch;
private final XmlConfig xmlConfig;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
this.xmlConfig = xmlConfig;
this.pstmtBatch = pstmtBatch;
this.clusterManager = clusterManager;
this.backgroundExecutor = backgroundExecutor;
this.cacheManager = cacheManager;
this.serverConfig = serverConfig;
this.bootupClasses = bootupClasses;
this.expressionFactory = new DefaultExpressionFactory();
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
this.binder = new Binder(typeManager);
this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig);
this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource());
this.deployInherit = new DeployInherit(bootupClasses);
this.deployCreateProperties = new DeployCreateProperties(typeManager);
this.deployUtil = new DeployUtil(typeManager, serverConfig);
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
this.transactionManager = createTransactionManager();
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
externalTransactionManager = new JtaTransactionManager();
}
if (externalTransactionManager != null) {
externalTransactionManager.setTransactionManager(transactionManager);
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
} else {
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
}
}
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
private TransactionManager createTransactionManager() {
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
}
/**
* Return true if autoCommit mode is on.
*/
private boolean isAutoCommitMode() {
if (serverConfig.isAutoCommitMode()) {
// explicitly set
return true;
}
DataSource dataSource = serverConfig.getDataSource();
if (dataSource instanceof DataSourcePool && ((DataSourcePool)dataSource).getAutoCommit()) {
// We know the DataSourcePool is using autoCommit
return true;
}
return false;
}
public JsonContext createJsonContext(SpiEbeanServer server) {
return new DJsonContext(server);
}
public XmlConfig getXmlConfig() {
return xmlConfig;
}
public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
}
public RelationalQueryEngine createRelationalQueryEngine() {
return new DefaultRelationalQueryEngine(binder, serverConfig.getDatabaseBooleanTrue());
}
public OrmQueryEngine createOrmQueryEngine() {
return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine);
}
public Persister createPersister(SpiEbeanServer server) {
return new DefaultPersister(server, binder, beanDescriptorManager, pstmtBatch);
}
public PstmtBatch getPstmtBatch() {
return pstmtBatch;
}
public ServerCacheManager getCacheManager() {
return cacheManager;
}
public BootupClasses getBootupClasses() {
return bootupClasses;
}
public DatabasePlatform getDatabasePlatform() {
return serverConfig.getDatabasePlatform();
}
public ServerConfig getServerConfig() {
return serverConfig;
}
public ExpressionFactory getExpressionFactory() {
return expressionFactory;
}
public TypeManager getTypeManager() {
return typeManager;
}
public Binder getBinder() {
return binder;
}
public BeanDescriptorManager getBeanDescriptorManager() {
return beanDescriptorManager;
}
public DeployInherit getDeployInherit() {
return deployInherit;
}
public ResourceManager getResourceManager() {
return resourceManager;
}
public DeployOrmXml getDeployOrmXml() {
return deployOrmXml;
}
public DeployCreateProperties getDeployCreateProperties() {
return deployCreateProperties;
}
public DeployUtil getDeployUtil() {
return deployUtil;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public TransactionScopeManager getTransactionScopeManager() {
return transactionScopeManager;
}
public CQueryEngine getCQueryEngine() {
return cQueryEngine;
}
public ClusterManager getClusterManager() {
return clusterManager;
}
public SpiBackgroundExecutor getBackgroundExecutor() {
return backgroundExecutor;
}
}
@@ -29,6 +29,7 @@ import com.avaje.ebeaninternal.server.deploy.DeployPropertyParserMap;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.query.CQueryPlan;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Wraps the objects involved in executing a Query.
@@ -43,18 +44,18 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private final BeanFinder<T> finder;
private final LoadContext graphContext;
private final Boolean readOnly;
private final RawSql rawSql;
private LoadContext loadContext;
private PersistenceContext persistenceContext;
private HashQuery cacheKey;
private HashQueryPlan queryPlanHash;
/**
* Create the InternalQueryRequest.
*/
@@ -68,13 +69,19 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
this.queryEngine = queryEngine;
this.query = query;
this.readOnly = query.isReadOnly();
}
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
graphContext.registerSecondaryQueries(query);
/**
* Return the database platform like clause.
*/
@Override
public String getDBLikeClause() {
return ebeanServer.getDatabasePlatform().getLikeClause();
}
public void executeSecondaryQueries(int defaultQueryBatch) {
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
loadContext.executeSecondaryQueries(this, defaultQueryBatch);
}
/**
@@ -86,7 +93,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* </p>
*/
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
return graphContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
return loadContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
}
/**
@@ -107,7 +114,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Return the graph context for this query.
*/
public LoadContext getGraphContext() {
return graphContext;
return loadContext;
}
/**
@@ -165,10 +172,20 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
createdTransaction = true;
}
}
// initialise the persistenceContext and loadContext
this.persistenceContext = getPersistenceContext(query, transaction);
this.graphContext.setPersistenceContext(persistenceContext);
this.loadContext = new DLoadContext(this);
this.loadContext.registerSecondaryQueries(query);
}
/**
* For iterate queries reset the persistenceContext and loadContext.
*/
public void flushPersistenceContextOnIterate() {
persistenceContext = new DefaultPersistenceContext();
loadContext.setPersistenceContext(persistenceContext);
}
/**
* Get the TransactionContext either explicitly set on the query or
* transaction scoped.
@@ -185,14 +202,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* It ends the query only transaction.
* </p>
*/
public void endTransIfRequired() {
if (createdTransaction) {
// we can rollback as readOnly transaction
transaction.rollback();
transaction.endQueryOnly();
}
}
@@ -354,13 +369,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Log the SQL if the logLevel is appropriate.
*/
public void logSql(String sql) {
if (transaction.isLogSql()) {
transaction.logSql(sql);
}
transaction.logSql(sql);
}
public void flushPersistenceContextOnIterate() {
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
/**
* Return true if the request wants to log the secondary queries (test purpose).
*/
public boolean isLogSecondaryQuery() {
return query.isLogSecondaryQuery();
}
}
@@ -34,106 +34,101 @@ import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap;
*/
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T> {
private final BeanManager<T> beanManager;
private final BeanManager<T> beanManager;
private final BeanDescriptor<T> beanDescriptor;
private final BeanDescriptor<T> beanDescriptor;
private final BeanPersistListener<T> beanPersistListener;
private final BeanPersistListener<T> beanPersistListener;
/**
* For per post insert update delete control.
*/
private final BeanPersistController controller;
/**
* For per post insert update delete control.
*/
private final BeanPersistController controller;
/**
/**
* The bean being persisted.
*/
private final T bean;
private final EntityBean entityBean;
/**
* The associated intercept.
*/
private final EntityBeanIntercept intercept;
private final T bean;
/**
* The parent bean for unidirectional save.
*/
private final Object parentBean;
private final EntityBean entityBean;
private final boolean dirty;
/**
* The associated intercept.
*/
private final EntityBeanIntercept intercept;
private ConcurrencyMode concurrencyMode;
/**
* The parent bean for unidirectional save.
*/
private final Object parentBean;
/**
* The unique id used for logging summary.
*/
private Object idValue;
private final boolean dirty;
/**
* Hash value used to handle cascade delete both ways in a relationship.
*/
private Integer beanHash;
private ConcurrencyMode concurrencyMode;
private boolean notifyCache;
/**
* The unique id used for logging summary.
*/
private Object idValue;
private boolean deleteMissingChildren;
private final Set<String> dirtyPropertyNames;
/**
* Hash value used to handle cascade delete both ways in a relationship.
*/
private Integer beanHash;
private boolean notifyCache;
private boolean deleteMissingChildren;
/**
* Flag used to detect when only many properties where updated via a cascade. Used to ensure
* appropriate caches are updated in that case.
*/
private boolean updatedManysOnly;
/**
* Many properties that were cascade saved (and hence might need caches updated later).
*/
private boolean updatedManysOnly;
/**
* Many properties that were cascade saved (and hence might need caches updated later).
*/
private List<BeanPropertyAssocMany<?>> updatedManys;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr,
SpiTransaction t, PersistExecute persistExecute, PersistRequest.Type type) {
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) {
super(server, t, persistExecute);
this.entityBean = (EntityBean)bean;
super(server, t, persistExecute);
this.entityBean = (EntityBean) bean;
this.intercept = entityBean._ebean_getIntercept();
this.beanManager = mgr;
this.beanDescriptor = mgr.getBeanDescriptor();
this.beanPersistListener = beanDescriptor.getPersistListener();
if (PersistRequest.Type.DETERMINE != type) {
this.type = type;
} else {
// determine mode during cascade save (supporting stateless update)
this.type = beanDescriptor.isInsertMode(intercept) ? Type.INSERT : Type.UPDATE;
this.beanManager = mgr;
this.beanDescriptor = mgr.getBeanDescriptor();
this.beanPersistListener = beanDescriptor.getPersistListener();
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
this.type = type;
if (saveRecurse) {
this.persistCascade = t.isPersistCascade();
}
if (this.type == Type.UPDATE && intercept.isNew() ) {
// 'stateless update' - set bean up for doing update
intercept.setNewBeanForUpdate();
if (this.type == Type.UPDATE) {
if (intercept.isNew()) {
// 'stateless update' - set loaded properties as dirty
intercept.setNewBeanForUpdate();
}
// Mark Mutable scalar properties (like Hstore) as dirty where necessary
beanDescriptor.checkMutableProperties(intercept);
}
// derive the set of property names now as we will pass them to a beanPersistListener later
this.dirtyPropertyNames = (beanPersistListener == null) ? null : intercept.getDirtyPropertyNames();
this.bean = bean;
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept);
this.dirty = intercept.isDirty();
}
this.dirty = intercept.isDirty();
}
/**
* Return true if this is an insert request.
*/
/**
* Return true if this is an insert request.
*/
public boolean isInsert() {
return Type.INSERT == type;
}
@Override
@Override
public Set<String> getLoadedProperties() {
return intercept.getLoadedPropertyNames();
}
@@ -148,18 +143,18 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return intercept.getDirtyValues();
}
public boolean isNotify(TransactionEvent txnEvent) {
this.notifyCache = beanDescriptor.isCacheNotify();
return notifyCache || isNotifyPersistListener();
}
public boolean isNotify(TransactionEvent txnEvent) {
this.notifyCache = beanDescriptor.isCacheNotify();
return notifyCache || isNotifyPersistListener();
}
public boolean isNotifyPersistListener() {
return beanPersistListener != null;
}
public boolean isNotifyPersistListener() {
return beanPersistListener != null;
}
/**
* Notify/Update the local L2 cache after the transaction has successfully committed.
*/
/**
* Notify/Update the local L2 cache after the transaction has successfully committed.
*/
public void notifyCache() {
if (notifyCache) {
switch (type) {
@@ -178,414 +173,416 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
beanPersistMap.add(beanDescriptor, type, idValue);
}
beanPersistMap.add(beanDescriptor, type, idValue);
}
public boolean notifyLocalPersistListener() {
if (beanPersistListener == null) {
return false;
public boolean notifyLocalPersistListener() {
if (beanPersistListener == null) {
return false;
} else {
switch (type) {
case INSERT:
return beanPersistListener.inserted(bean);
} else {
switch (type) {
case INSERT:
return beanPersistListener.inserted(bean);
case UPDATE:
return beanPersistListener.updated(bean, dirtyPropertyNames);
case UPDATE:
return beanPersistListener.updated(bean, intercept.getDirtyPropertyNames());
case DELETE:
return beanPersistListener.deleted(bean);
case DELETE:
return beanPersistListener.deleted(bean);
default:
return false;
}
}
}
default:
return false;
}
}
}
public boolean isParent(Object o) {
return o == parentBean;
}
public boolean isParent(Object o) {
return o == parentBean;
}
/**
* Return true if this bean has been already been persisted
* (inserted or updated) in this transaction.
*/
public boolean isRegisteredBean() {
return transaction.isRegisteredBean(bean);
}
public void unRegisterBean() {
transaction.unregisterBean(bean);
}
/**
* The hash used to register the bean with the transaction.
* <p>
* Takes into account the class type and id value.
* </p>
*/
private Integer getBeanHash() {
if (beanHash == null) {
Object id = beanDescriptor.getId(entityBean);
int hc = 31 * bean.getClass().getName().hashCode();
if (id != null) {
hc += id.hashCode();
}
beanHash = Integer.valueOf(hc);
}
return beanHash;
}
public void registerDeleteBean() {
Integer hash = getBeanHash();
transaction.registerDeleteBean(hash);
}
public void unregisterDeleteBean() {
Integer hash = getBeanHash();
transaction.unregisterDeleteBean(hash);
}
public boolean isRegisteredForDeleteBean() {
if (transaction == null){
return false;
} else {
Integer hash = getBeanHash();
return transaction.isRegisteredDeleteBean(hash);
}
}
/**
* Return true if this bean has been already been persisted (inserted or updated) in this
* transaction.
*/
public boolean isRegisteredBean() {
return transaction.isRegisteredBean(bean);
}
public BeanManager<T> getBeanManager() {
return beanManager;
}
public void unRegisterBean() {
transaction.unregisterBean(bean);
}
/**
* Return the BeanDescriptor for the associated bean.
*/
public BeanDescriptor<T> getBeanDescriptor() {
return beanDescriptor;
}
/**
* The hash used to register the bean with the transaction.
* <p>
* Takes into account the class type and id value.
* </p>
*/
private Integer getBeanHash() {
if (beanHash == null) {
Object id = beanDescriptor.getId(entityBean);
int hc = 31 * bean.getClass().getName().hashCode();
if (id != null) {
hc += id.hashCode();
}
beanHash = Integer.valueOf(hc);
}
return beanHash;
}
/**
* Return true if a stateless update should also delete any missing details
* beans.
*/
public boolean isDeleteMissingChildren() {
return deleteMissingChildren;
}
public void registerDeleteBean() {
Integer hash = getBeanHash();
transaction.registerDeleteBean(hash);
}
/**
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
*/
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
this.deleteMissingChildren = deleteMissingChildren;
}
public void unregisterDeleteBean() {
Integer hash = getBeanHash();
transaction.unregisterDeleteBean(hash);
}
/**
* Used to skip updates if we know the bean is not dirty. This is the case
* for EntityBeans that have not been modified.
*/
public boolean isDirty() {
return dirty;
}
public boolean isRegisteredForDeleteBean() {
if (transaction == null) {
return false;
} else {
Integer hash = getBeanHash();
return transaction.isRegisteredDeleteBean(hash);
}
}
/**
* Return the concurrency mode used for this persist.
*/
public ConcurrencyMode getConcurrencyMode() {
return concurrencyMode;
}
public BeanManager<T> getBeanManager() {
return beanManager;
}
/**
* Returns a description of the request. This is typically the bean class
* name or the base table for MapBeans.
* <p>
* Used to determine common persist requests for queueing and statement
* batching.
* </p>
*/
public String getFullName() {
return beanDescriptor.getFullName();
}
/**
* Return the BeanDescriptor for the associated bean.
*/
public BeanDescriptor<T> getBeanDescriptor() {
return beanDescriptor;
}
/**
* Return the bean associated with this request.
*/
public T getBean() {
return bean;
}
/**
* Return true if a stateless update should also delete any missing details beans.
*/
public boolean isDeleteMissingChildren() {
return deleteMissingChildren;
}
public EntityBean getEntityBean() {
/**
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
*/
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
this.deleteMissingChildren = deleteMissingChildren;
}
/**
* Prepare the update after potential modifications in a BeanPersistController.
*/
public void postControllerPrepareUpdate() {
if (intercept.isNew() && controller != null) {
// 'stateless update' - set dirty properties modified in controller preUpdate
intercept.setNewBeanForUpdate();
}
}
/**
* Used to skip updates if we know the bean is not dirty. This is the case for EntityBeans that
* have not been modified.
*/
public boolean isDirty() {
return dirty;
}
/**
* Return the concurrency mode used for this persist.
*/
public ConcurrencyMode getConcurrencyMode() {
return concurrencyMode;
}
/**
* Returns a description of the request. This is typically the bean class name or the base table
* for MapBeans.
* <p>
* Used to determine common persist requests for queueing and statement batching.
* </p>
*/
public String getFullName() {
return beanDescriptor.getFullName();
}
/**
* Return the bean associated with this request.
*/
public T getBean() {
return bean;
}
public EntityBean getEntityBean() {
return entityBean;
}
/**
* Return the Id value for the bean.
*/
public Object getBeanId() {
return beanDescriptor.getId(entityBean);
}
* Return the Id value for the bean.
*/
public Object getBeanId() {
return beanDescriptor.getId(entityBean);
}
public BeanDelta createDeltaBean() {
return new BeanDelta(beanDescriptor, getBeanId());
}
public BeanDelta createDeltaBean() {
return new BeanDelta(beanDescriptor, getBeanId());
}
/**
* Return the parent bean for cascading save with unidirectional
* relationship.
*/
public Object getParentBean() {
return parentBean;
}
/**
* Return the parent bean for cascading save with unidirectional relationship.
*/
public Object getParentBean() {
return parentBean;
}
/**
* Return the controller if there is one associated with this type of bean.
* This returns null if there is no controller associated.
*/
public BeanPersistController getBeanController() {
return controller;
}
/**
* Return the controller if there is one associated with this type of bean. This returns null if
* there is no controller associated.
*/
public BeanPersistController getBeanController() {
return controller;
}
/**
* Return the intercept if there is one.
*/
public EntityBeanIntercept getEntityBeanIntercept() {
return intercept;
}
/**
* Return the intercept if there is one.
*/
public EntityBeanIntercept getEntityBeanIntercept() {
return intercept;
}
/**
* Return true if this property is loaded (full bean or included in partial
* bean).
*/
public boolean isLoadedProperty(BeanProperty prop) {
return intercept.isLoadedProperty(prop.getPropertyIndex());
}
/**
* Return true if this property is loaded (full bean or included in partial bean).
*/
public boolean isLoadedProperty(BeanProperty prop) {
return intercept.isLoadedProperty(prop.getPropertyIndex());
}
@Override
public int executeNow() {
switch (type) {
case INSERT:
persistExecute.executeInsertBean(this);
return -1;
@Override
public int executeNow() {
switch (type) {
case INSERT:
persistExecute.executeInsertBean(this);
return -1;
case UPDATE:
persistExecute.executeUpdateBean(this);
return -1;
case UPDATE:
persistExecute.executeUpdateBean(this);
return -1;
case DELETE:
persistExecute.executeDeleteBean(this);
return -1;
case DELETE:
persistExecute.executeDeleteBean(this);
return -1;
default:
throw new RuntimeException("Invalid type " + type);
}
}
default:
throw new RuntimeException("Invalid type " + type);
}
}
@Override
public int executeOrQueue() {
@Override
public int executeOrQueue() {
boolean batch = transaction.isBatchThisRequest();
boolean batch = transaction.isBatchThisRequest();
BatchControl control = transaction.getBatchControl();
if (control != null) {
return control.executeOrQueue(this, batch);
}
if (batch) {
control = persistExecute.createBatchControl(transaction);
return control.executeOrQueue(this, batch);
BatchControl control = transaction.getBatchControl();
if (control != null) {
return control.executeOrQueue(this, batch);
}
if (batch) {
control = persistExecute.createBatchControl(transaction);
return control.executeOrQueue(this, batch);
} else {
return executeNow();
}
}
} else {
return executeNow();
}
}
/**
* Set the generated key back to the bean. Only used for inserts with
* getGeneratedKeys.
*/
public void setGeneratedKey(Object idValue) {
if (idValue != null) {
// remember it for logging summary
this.idValue = beanDescriptor.convertSetId(idValue, entityBean);
}
}
/**
* Set the generated key back to the bean. Only used for inserts with getGeneratedKeys.
*/
public void setGeneratedKey(Object idValue) {
if (idValue != null) {
// remember it for logging summary
this.idValue = beanDescriptor.convertSetId(idValue, entityBean);
}
}
/**
* Set the Id value that was bound. Used for the purposes of logging summary
* information on this request.
*/
public void setBoundId(Object idValue) {
this.idValue = idValue;
}
/**
* Set the Id value that was bound. Used for the purposes of logging summary information on this
* request.
*/
public void setBoundId(Object idValue) {
this.idValue = idValue;
}
/**
* Check for optimistic concurrency exception.
*/
public final void checkRowCount(int rowCount) throws SQLException {
if (rowCount != 1) {
String m = Message.msg("persist.conc2", "" + rowCount);
throw new OptimisticLockException(m, null, bean);
}
}
/**
* Check for optimistic concurrency exception.
*/
public final void checkRowCount(int rowCount) throws SQLException {
if (rowCount != 1) {
String m = Message.msg("persist.conc2", "" + rowCount);
throw new OptimisticLockException(m, null, bean);
}
}
public void postDelete() {
// Delete the bean from the PersistenceContent
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
// Delete from cache early even if transaction fails
beanDescriptor.cacheHandleDelete(idValue, this);
}
/**
* Post processing.
*/
public void postExecute() throws SQLException {
public void postDelete() {
if (controller != null) {
controllerPost();
}
// Delete the bean from the PersistenceContent
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
// Delete from cache early even if transaction fails
beanDescriptor.cacheHandleDelete(idValue, this);
}
if (intercept != null) {
// if bean persisted again then should result in an update
intercept.setLoaded();
}
/**
* Post processing.
*/
public void postExecute() throws SQLException {
addEvent();
if (controller != null) {
controllerPost();
}
if (isLogSummary()) {
logSummary();
}
}
if (intercept != null) {
// if bean persisted again then should result in an update
intercept.setLoaded();
}
private void controllerPost() {
switch (type) {
case INSERT:
controller.postInsert(this);
break;
case UPDATE:
controller.postUpdate(this);
break;
case DELETE:
controller.postDelete(this);
break;
default:
break;
}
}
addEvent();
private void logSummary() {
if (isLogSummary()) {
logSummary();
}
}
String name = beanDescriptor.getName();
switch (type) {
case INSERT:
transaction.logSummary("Inserted [" + name + "] [" + idValue + "]");
break;
case UPDATE:
transaction.logSummary("Updated [" + name + "] [" + idValue + "]");
break;
case DELETE:
transaction.logSummary("Deleted [" + name + "] [" + idValue + "]");
break;
default:
break;
}
}
private void controllerPost() {
switch (type) {
case INSERT:
controller.postInsert(this);
break;
case UPDATE:
controller.postUpdate(this);
break;
case DELETE:
controller.postDelete(this);
break;
default:
break;
}
}
/**
* Add the bean to the TransactionEvent. This will be used by
* TransactionManager to synch Cache, Cluster and text indexes.
*/
private void addEvent() {
private void logSummary() {
TransactionEvent event = transaction.getEvent();
if (event != null) {
event.add(this);
}
}
String name = beanDescriptor.getName();
switch (type) {
case INSERT:
transaction.logSummary("Inserted [" + name + "] [" + idValue + "]");
break;
case UPDATE:
transaction.logSummary("Updated [" + name + "] [" + idValue + "]");
break;
case DELETE:
transaction.logSummary("Deleted [" + name + "] [" + idValue + "]");
break;
default:
break;
}
}
/**
* Determine the concurrency mode depending on fully/partially populated
* bean.
* <p>
* Specifically with version concurrency we want to check that the version
* property was one of the loaded properties.
* </p>
*/
public ConcurrencyMode determineConcurrencyMode() {
// 'partial bean' update/delete...
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
// check the version property was loaded
BeanProperty prop = beanDescriptor.getVersionProperty();
if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) {
// OK to use version property
} else {
concurrencyMode = ConcurrencyMode.NONE;
}
}
return concurrencyMode;
}
/**
* Add the bean to the TransactionEvent. This will be used by TransactionManager to synch Cache,
* Cluster and text indexes.
*/
private void addEvent() {
/**
* Return true if the update DML/SQL must be dynamically generated.
* <p>
* This is the case for updates/deletes of partially populated beans.
* </p>
*/
public boolean isDynamicUpdateSql() {
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
}
TransactionEvent event = transaction.getEvent();
if (event != null) {
event.add(this);
}
}
/**
* Create a GenerateDmlRequest used to generate the DML.
* <p>
* Will used changed properties or loaded properties depending on the
* BeanDescriptor.isUpdateChangesOnly() value.
* </p>
*/
public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) {
return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly());
}
/**
* Determine the concurrency mode depending on fully/partially populated bean.
* <p>
* Specifically with version concurrency we want to check that the version property was one of the
* loaded properties.
* </p>
*/
public ConcurrencyMode determineConcurrencyMode() {
/**
* Test if the property value has changed and if so include it in the
* update.
*/
public boolean isAddToUpdate(BeanProperty prop) {
return intercept.isDirtyProperty(prop.getPropertyIndex());
}
// 'partial bean' update/delete...
if (concurrencyMode.equals(ConcurrencyMode.VERSION)) {
// check the version property was loaded
BeanProperty prop = beanDescriptor.getVersionProperty();
if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) {
// OK to use version property
} else {
concurrencyMode = ConcurrencyMode.NONE;
}
}
return concurrencyMode;
}
/**
* Return true if the update DML/SQL must be dynamically generated.
* <p>
* This is the case for updates/deletes of partially populated beans.
* </p>
*/
public boolean isDynamicUpdateSql() {
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
}
/**
* Create a GenerateDmlRequest used to generate the DML.
* <p>
* Will used changed properties or loaded properties depending on the
* BeanDescriptor.isUpdateChangesOnly() value.
* </p>
*/
public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) {
return new GenerateDmlRequest(emptyStringAsNull, intercept, beanDescriptor.isUpdateChangesOnly());
}
/**
* Test if the property value has changed and if so include it in the update.
*/
public boolean isAddToUpdate(BeanProperty prop) {
return intercept.isDirtyProperty(prop.getPropertyIndex());
}
public List<DerivedRelationshipData> getDerivedRelationships() {
return transaction.getDerivedRelationship(bean);
}
public void postInsert() {
// mark all properties as loaded after an insert to support immediate update
// mark all properties as loaded after an insert to support immediate update
int len = intercept.getPropertyLength();
for (int i = 0; i < len; i++) {
intercept.setLoadedProperty(i);
intercept.setLoadedProperty(i);
}
}
public boolean isReference() {
return beanDescriptor.isReference(intercept);
}
/**
* This many property has been cascade saved. Keep note of this and update the 'many property'
* cache on post commit.
*/
public void addUpdatedManyProperty(BeanPropertyAssocMany<?> updatedAssocMany) {
//if (notifyCache) {
if (updatedManys == null) {
updatedManys = new ArrayList<BeanPropertyAssocMany<?>>(5);
}
updatedManys.add(updatedAssocMany);
//}
// if (notifyCache) {
if (updatedManys == null) {
updatedManys = new ArrayList<BeanPropertyAssocMany<?>>(5);
}
updatedManys.add(updatedAssocMany);
// }
}
/**
@@ -40,15 +40,6 @@ public final class RelationalQueryRequest {
this.trans = (SpiTransaction) t;
}
/**
* Rollback the transaction if it was created for this request.
*/
public void rollbackTransIfRequired() {
if (createdTransaction) {
trans.rollback();
}
}
/**
* Create a transaction if none currently exists.
*/
@@ -58,10 +49,6 @@ public final class RelationalQueryRequest {
if (trans == null || !trans.isActive()) {
// create a local readOnly transaction
trans = ebeanServer.createServerTransaction(false, -1);
// commented out for performance reasons...
// TODO: review performance of trans.setReadOnly(true)
// trans.setReadOnly(true);
createdTransaction = true;
}
}
@@ -72,8 +59,7 @@ public final class RelationalQueryRequest {
*/
public void endTransIfRequired() {
if (createdTransaction) {
// we can rollback as a readOnly transaction.
trans.rollback();
trans.endQueryOnly();
}
}
@@ -45,8 +45,6 @@ public interface SpiOrmQueryRequest<T> {
*/
public void endTransIfRequired();
public void rollbackTransIfRequired();
/**
* Execute the query as findById.
*/
@@ -97,4 +95,9 @@ public interface SpiOrmQueryRequest<T> {
*/
public BeanCollection<T> getFromQueryCache();
/**
* Return the Database platform like clause.
*/
public String getDBLikeClause();
}
@@ -0,0 +1,87 @@
package com.avaje.ebeaninternal.server.ddl;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
/**
* A visitor that creates indexes for columns annotated with ColumnIndex
*
* @author rvbiljouw
* @see com.avaje.ebean.annotation.Index
*/
public class CreateIndexVisitor extends AbstractBeanVisitor {
private final IndexPropertyVisitor iv;
public CreateIndexVisitor(DdlGenContext ctx) {
this.iv = new IndexPropertyVisitor(ctx);
}
@Override
public void visitBegin() {
}
@Override
public boolean visitBean(BeanDescriptor<?> descriptor) {
return descriptor.isInheritanceRoot();
}
@Override
public PropertyVisitor visitProperty(BeanProperty p) {
return iv;
}
@Override
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
visitInheritanceProperties(descriptor, iv);
}
@Override
public void visitEnd() {
}
private static final class IndexPropertyVisitor extends BaseTablePropertyVisitor {
private final DdlGenContext ctx;
public IndexPropertyVisitor(DdlGenContext ctx) {
this.ctx = ctx;
}
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
}
@Override
public void visitOneImported(BeanPropertyAssocOne<?> p) {
}
@Override
public void visitScalar(BeanProperty p) {
String baseTable = p.getBeanDescriptor().getBaseTable();
if (p.isIndexed()) {
String indexName = p.getIndexName();
if (indexName.length() == 0) {
indexName = ctx.getDdlSyntax().getIndexName(baseTable, p.getDbColumn(), ctx.incrementIxCount());
}
ctx.write("create index ")
.write(indexName)
.write(" on ")
.write(baseTable)
.write("(")
.write(p.getDbColumn())
.write(");")
.writeNewLine();
}
}
@Override
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
}
}
}
@@ -2,6 +2,10 @@ package com.avaje.ebeaninternal.server.ddl;
import java.sql.Types;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -12,8 +16,6 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Used as part of CreateTableVisitor to generated the create table DDL script.
@@ -28,6 +30,8 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
private final CreateTableVisitor parent;
private BeanPropertyAssocOne<?> embedded;
public CreateTableColumnVisitor(CreateTableVisitor parent, DdlGenContext ctx) {
this.parent = parent;
this.ctx = ctx;
@@ -66,12 +70,13 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
this.embedded = embedded;
visitScalar(p);
}
private StringBuilder createUniqueConstraintBuffer(String table, String column) {
String uqConstraintName = "uq_" + table + "_" + column;
String uqConstraintName = "uq_"+TableName.parse(table)+"_"+column;
if (uqConstraintName.length() > ddl.getMaxConstraintNameLength()) {
uqConstraintName = uqConstraintName.substring(0, ddl.getMaxConstraintNameLength());
@@ -153,11 +158,12 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
parent.writeColumnName(p.getDbColumn(), p);
String columnDefn = ctx.getColumnDefn(p);
ctx.write(columnDefn);
boolean identity = isIdentity(p);
if (identity) {
writeIdentity();
ctx.write(ddl.getIdentityColumnDefn(columnDefn));
} else {
ctx.write(columnDefn);
}
if (p.isId() && ddl.isInlinePrimaryKeyConstraint()){
@@ -181,18 +187,16 @@ public class CreateTableColumnVisitor extends BaseTablePropertyVisitor {
private String createUniqueConstraint(BeanProperty p) {
StringBuilder expr = createUniqueConstraintBuffer(p.getBeanDescriptor().getBaseTable(), p.getDbColumn());
String baseTable = p.getBeanDescriptor().getBaseTable();
if (baseTable == null) {
// Embedded bean property that has unique constraint on it
baseTable = embedded.getBeanDescriptor().getBaseTable();
}
StringBuilder expr = createUniqueConstraintBuffer(baseTable, p.getDbColumn());
expr.append(p.getDbColumn()).append(")");
return expr.toString();
}
protected void writeIdentity() {
String identity = ddl.getIdentity();
if (identity != null && identity.length() > 0) {
ctx.write(" ").write(identity);
}
}
protected void writeIdentitySuffix() {
String identity = ddl.getIdentitySuffix();
@@ -4,6 +4,10 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.DbDdlSyntax;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -13,8 +17,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.parse.SqlReservedWords;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Used to generated the create table DDL script.
@@ -23,23 +25,22 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
private static final Logger logger = LoggerFactory.getLogger(CreateTableVisitor.class);
final DdlGenContext ctx;
private final DdlGenContext ctx;
final PropertyVisitor pv;
private final PropertyVisitor pv;
final DbDdlSyntax ddl;
private final DbDdlSyntax ddl;
final int columnNameWidth;
private final int columnNameWidth;
// avoid writing columns twice, e.g. when used in associations with insertable=false and updateable=false
private final Set<String> wroteColumns = new HashSet<String>();
private ArrayList<String> checkConstraints = new ArrayList<String>();
private ArrayList<String> checkConstraints = new ArrayList<String>();
private ArrayList<String> uniqueConstraints = new ArrayList<String>();
private ArrayList<String> uniqueConstraints = new ArrayList<String>();
private String table;
private String schema;
private String table;
public Set<String> getWroteColumns() {
return wroteColumns;
@@ -53,235 +54,211 @@ public class CreateTableVisitor extends AbstractBeanVisitor {
this.pv = new CreateTableColumnVisitor(this, ctx);
}
public boolean isDbColumnWritten(String dbColumn) {
// Columns are not case sensitive - user lower case
// as e.g. @JoinColumn(s) may use a different case
return wroteColumns.contains(dbColumn.toLowerCase());
}
public boolean isDbColumnWritten(String dbColumn) {
// Columns are not case sensitive - user lower case
// as e.g. @JoinColumn(s) may use a different case
return wroteColumns.contains(dbColumn.toLowerCase());
}
public void addDbColumnWritten(String dbColumn){
// Column names are case insensitive
wroteColumns.add(dbColumn.toLowerCase());
}
public void addDbColumnWritten(String dbColumn) {
// Column names are case insensitive
wroteColumns.add(dbColumn.toLowerCase());
}
/**
* Write the table name including a check for SQL reserved words.
*/
protected void writeTableName(BeanDescriptor<?> descriptor) {
String tableName = descriptor.getBaseTable();
int dotPos = tableName.lastIndexOf('.');
if (dotPos > -1){
schema = tableName.substring(0, dotPos);
table = tableName.substring(dotPos+1);
} else {
table = tableName;
}
if (SqlReservedWords.isKeyword(table)) {
logger.warn("Table name ["+table+"] is a suspected SQL reserved word for bean "+descriptor.getFullName());
}
ctx.write(tableName);
}
protected String getTable() {
return table;
}
protected String getSchema() {
return schema;
}
/**
* Write the column name including a check for the SQL reserved words.
* @param p
*/
protected void writeColumnName(String columnName, BeanProperty p) {
addDbColumnWritten(columnName);
if (SqlReservedWords.isKeyword(columnName)) {
String propName = p == null ? "(Unknown)" : p.getFullBeanName();
logger.warn("Column name ["+columnName+"] is a suspected SQL reserved word for property "+propName);
}
ctx.write(" ").write(columnName, columnNameWidth).write(" ");
}
/**
* Build a check constraint for the property if required.
* <p>
* Typically check constraint based on Enum mapping values.
* </p>
*/
protected void addCheckConstraint(BeanProperty p, String prefix, String constraintExpression) {
if (p != null && constraintExpression != null){
// build constraint clause
String s = "constraint "+getConstraintName(prefix, p)+" "+constraintExpression;
// add to list as we render all check constraints just prior to primary key
checkConstraints.add(s);
}
}
protected String getConstraintName(String prefix, BeanProperty p) {
return prefix + table + "_" + p.getDbColumn();
}
protected void addUniqueConstraint(String constraintExpression) {
uniqueConstraints.add(constraintExpression);
}
/**
* Write the table name including a check for SQL reserved words.
*/
protected void writeTableName(BeanDescriptor<?> descriptor) {
protected void addCheckConstraint(String constraintExpression) {
checkConstraints.add(constraintExpression);
}
protected void addCheckConstraint(BeanProperty p) {
addCheckConstraint(p,"ck_", p.getDbConstraintExpression());
}
public boolean visitBean(BeanDescriptor<?> descriptor) {
wroteColumns.clear();
if (!descriptor.isInheritanceRoot()){
return false;
}
ctx.write("create table ");
writeTableName(descriptor);
ctx.write(" (").writeNewLine();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && inheritInfo.isRoot()){
String discColumn = inheritInfo.getDiscriminatorColumn();
int discType = inheritInfo.getDiscriminatorType();
int discLength = inheritInfo.getDiscriminatorLength();
DbType dbType = ctx.getDbTypeMap().get(discType);
String discDbType = dbType.renderType(discLength, 0);
writeColumnName(discColumn, null);
ctx.write(discDbType);
ctx.write(" not null,");
ctx.writeNewLine();
}
return true;
}
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
// parse to remove any catalog or schema prefix on the table
table = TableName.parse(descriptor.getBaseTable());
ctx.write(descriptor.getBaseTable());
}
visitInheritanceProperties(descriptor, pv);
if (checkConstraints.size() > 0){
for (String checkConstraint : checkConstraints) {
ctx.write(" ").write(checkConstraint).write(",").writeNewLine();
}
checkConstraints = new ArrayList<String>();
}
if (uniqueConstraints.size() > 0){
for (String constraint : uniqueConstraints) {
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
uniqueConstraints = new ArrayList<String>();
}
CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints();
if (compoundUniqueConstraints != null){
String table = descriptor.getBaseTable();
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
String constraint = createUniqueConstraint(table, i, compoundUniqueConstraints[i]);
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
}
BeanProperty idProp = descriptor.getIdProperty();
/**
* Write the column name including a check for the SQL reserved words.
*/
protected void writeColumnName(String columnName, BeanProperty p) {
if (idProp == null){
// No comma + new line
ctx.removeLast().removeLast();
} else if (ddl.isInlinePrimaryKeyConstraint()) {
// The Primary Key constraint was inlined with the column
// ... No comma + new line
ctx.removeLast().removeLast();
} else {
// Add the primay key constraint
String pkName = ddl.getPrimaryKeyName(table);
ctx.write(" constraint ").write(pkName).write(" primary key (");
VisitorUtil.visit(idProp, new AbstractPropertyVisitor() {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitScalar(BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
});
// remove the last comma, end of PK
ctx.removeLast().write(")");
}
// end of table
ctx.write(")").writeNewLine();
ctx.write(";").writeNewLine().writeNewLine();
ctx.flush();
}
private String createUniqueConstraint(String table, int idx, CompoundUniqueContraint uc) {
String uqConstraintName = "uq_"+table+"_"+(idx+1) ;
StringBuilder sb = new StringBuilder();
sb.append("constraint ")
.append(uqConstraintName)
.append(" unique (");
String[] columns = uc.getColumns();
for (int i = 0; i < columns.length; i++) {
if (i > 0){
sb.append(",");
}
sb.append(columns[i]);
}
sb.append(")");
return sb.toString();
addDbColumnWritten(columnName);
if (SqlReservedWords.isKeyword(columnName)) {
String propName = p == null ? "(Unknown)" : p.getFullBeanName();
logger.warn("Column name [" + columnName + "] is a suspected SQL reserved word for property " + propName);
}
ctx.write(" ").write(columnName, columnNameWidth).write(" ");
}
public void visitBeanDescriptorEnd() {
ctx.write(");").writeNewLine().writeNewLine();
}
/**
* Build a check constraint for the property if required.
* <p>
* Typically check constraint based on Enum mapping values.
* </p>
*/
protected void addCheckConstraint(BeanProperty p, String prefix, String constraintExpression) {
if (p != null && constraintExpression != null) {
public PropertyVisitor visitProperty(BeanProperty p) {
return pv;
}
// build constraint clause
String s = "constraint " + getConstraintName(prefix, p) + " " + constraintExpression;
public void visitBegin() {
}
// add to list as we render all check constraints just prior to primary key
checkConstraints.add(s);
}
}
public void visitEnd() {
ctx.addIntersectionCreateTables();
ctx.flush();
}
protected String getConstraintName(String prefix, BeanProperty p) {
return prefix + table + "_" + p.getDbColumn();
}
protected void addUniqueConstraint(String constraintExpression) {
uniqueConstraints.add(constraintExpression);
}
protected void addCheckConstraint(String constraintExpression) {
checkConstraints.add(constraintExpression);
}
protected void addCheckConstraint(BeanProperty p) {
addCheckConstraint(p, "ck_", p.getDbConstraintExpression());
}
public boolean visitBean(BeanDescriptor<?> descriptor) {
wroteColumns.clear();
if (!descriptor.isInheritanceRoot()) {
return false;
}
ctx.write("create table ");
writeTableName(descriptor);
ctx.write(" (").writeNewLine();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && inheritInfo.isRoot()) {
String discColumn = inheritInfo.getDiscriminatorColumn();
int discType = inheritInfo.getDiscriminatorType();
int discLength = inheritInfo.getDiscriminatorLength();
DbType dbType = ctx.getDbTypeMap().get(discType);
String discDbType = dbType.renderType(discLength, 0);
writeColumnName(discColumn, null);
ctx.write(discDbType);
ctx.write(" not null,");
ctx.writeNewLine();
}
return true;
}
public void visitBeanEnd(BeanDescriptor<?> descriptor) {
visitInheritanceProperties(descriptor, pv);
if (checkConstraints.size() > 0) {
for (String checkConstraint : checkConstraints) {
ctx.write(" ").write(checkConstraint).write(",").writeNewLine();
}
checkConstraints = new ArrayList<String>();
}
if (uniqueConstraints.size() > 0) {
for (String constraint : uniqueConstraints) {
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
uniqueConstraints = new ArrayList<String>();
}
CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints();
if (compoundUniqueConstraints != null) {
String table = descriptor.getBaseTable();
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
String constraint = createUniqueConstraint(table, i, compoundUniqueConstraints[i]);
ctx.write(" ").write(constraint).write(",").writeNewLine();
}
}
BeanProperty idProp = descriptor.getIdProperty();
if (idProp == null) {
// No comma + new line
ctx.removeLast().removeLast();
} else if (ddl.isInlinePrimaryKeyConstraint()) {
// The Primary Key constraint was inlined with the column
// ... No comma + new line
ctx.removeLast().removeLast();
} else {
// Add the primay key constraint
String pkName = ddl.getPrimaryKeyName(table);
ctx.write(" constraint ").write(pkName).write(" primary key (");
VisitorUtil.visit(idProp, new AbstractPropertyVisitor() {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitScalar(BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
@Override
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
ctx.write(p.getDbColumn()).write(", ");
}
});
// remove the last comma, end of PK
ctx.removeLast().write(")");
}
// end of table
ctx.write(")").writeNewLine();
ctx.write(";").writeNewLine().writeNewLine();
ctx.flush();
}
private String createUniqueConstraint(String table, int idx, CompoundUniqueContraint uc) {
StringBuilder sb = new StringBuilder(50);
sb.append("constraint ")
.append("uq_").append(TableName.parse(table)).append("_").append(idx + 1)
.append(" unique (");
String[] columns = uc.getColumns();
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(columns[i]);
}
sb.append(")");
return sb.toString();
}
public void visitBeanDescriptorEnd() {
ctx.write(");").writeNewLine().writeNewLine();
}
public PropertyVisitor visitProperty(BeanProperty p) {
return pv;
}
public void visitBegin() {
}
public void visitEnd() {
ctx.addIntersectionCreateTables();
ctx.flush();
}
}
@@ -98,17 +98,17 @@ public class DdlGenerator implements SpiEbeanPlugin {
}
}
protected void writeDrop(String dropFile) {
protected void writeDrop(String dropFile) {
try {
String c = generateDropDdl();
writeFile(dropFile, c);
try {
String c = generateDropDdl();
writeFile(dropFile, c);
} catch (IOException e) {
String msg = "Error generating Drop DDL";
throw new PersistenceException(msg, e);
}
}
} catch (IOException e) {
String msg = "Error generating Drop DDL";
throw new PersistenceException(msg, e);
}
}
protected void writeCreate(String createFile) {
@@ -149,6 +149,9 @@ public class DdlGenerator implements SpiEbeanPlugin {
AddForeignKeysVisitor fkeys = new AddForeignKeysVisitor(ctx);
VisitorUtil.visit(server, fkeys);
CreateIndexVisitor indexes = new CreateIndexVisitor(ctx);
VisitorUtil.visit(server, indexes);
ctx.flush();
createContent = ctx.getContent();
return createContent;
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.util.Iterator;
import com.avaje.ebean.EbeanServer;
@@ -9,7 +10,7 @@ import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
/**
* Helper functions for performing tasks on Lists Sets or Maps.
@@ -59,9 +60,9 @@ public interface BeanCollectionHelp<T> {
*/
public void refresh(BeanCollection<?> bc, EntityBean parentBean);
/**
* Write the collection out as json.
*/
public void jsonWrite(WriteJsonContext ctx, String name, Object collection, boolean explicitInclude);
/**
* Write the collection out as json.
*/
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException;
}
@@ -1,11 +1,12 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -34,8 +35,6 @@ import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.meta.MetaBeanInfo;
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
import com.avaje.ebean.text.TextException;
import com.avaje.ebean.text.json.JsonWriteBeanVisitor;
import com.avaje.ebeaninternal.api.HashQueryPlan;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
@@ -62,15 +61,13 @@ import com.avaje.ebeaninternal.server.query.CQueryPlan;
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext;
import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext;
import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.TypeManager;
import com.avaje.ebeaninternal.util.SortByClause;
import com.avaje.ebeaninternal.util.SortByClause.Property;
import com.avaje.ebeaninternal.util.SortByClauseParser;
import com.fasterxml.jackson.core.JsonParser;
/**
* Describes Beans including their deployment information.
@@ -195,12 +192,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* Inheritance information. Server side only.
*/
private final InheritInfo inheritInfo;
protected final InheritInfo inheritInfo;
/**
* Derived list of properties that make up the unique id.
*/
private final BeanProperty idProperty;
protected final BeanProperty idProperty;
private final int idPropertyIndex;
/**
@@ -210,11 +207,22 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
private final int versionPropertyIndex;
/**
* Properties that are initialised in the constructor need to be 'unloaded' to support partial object queries.
*/
private final int[] unloadProperties;
/**
* Properties local to this type (not from a super type).
*/
private final BeanProperty[] propertiesLocal;
/**
* Scalar mutable properties (need to dirty check on update).
*/
private final BeanProperty[] propertiesMutable;
private final BeanPropertyAssocOne<?> unidirectional;
/**
@@ -316,7 +324,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
private final boolean cacheSharableBeans;
private final BeanDescriptorCacheHelp<T> cacheHelp;
private final BeanDescriptorJsonHelp<T> jsonHelp;
private final String defaultSelectClause;
private final Set<String> defaultSelectClauseSet;
@@ -386,6 +395,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
this.propertiesBaseCompound = listHelper.getBaseCompound();
this.propertiesEmbedded = listHelper.getEmbedded();
this.propertiesLocal = listHelper.getLocal();
this.propertiesMutable = listHelper.getMutable();
this.unidirectional = listHelper.getUnidirectional();
this.propertiesOne = listHelper.getOnes();
this.propertiesOneExported = listHelper.getOneExported();
@@ -410,7 +420,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly();
this.cacheHelp = new BeanDescriptorCacheHelp<T>(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
this.jsonHelp = new BeanDescriptorJsonHelp<T>(this);
// Check if there are no cascade save associated beans ( subject to change
// in initialiseOther()). Note that if we are in an inheritance hierarchy
@@ -430,14 +440,46 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (Modifier.isAbstract(beanType.getModifiers())) {
this.idPropertyIndex = -1;
this.versionPropertyIndex = -1;
this.unloadProperties = new int[0];
} else {
EntityBeanIntercept ebi = prototypeEntityBean._ebean_getIntercept();
this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName());
this.versionPropertyIndex = (versionProperty == null) ? -1 : ebi.findProperty(versionProperty.getName());
this.unloadProperties = derivePropertiesToUnload(prototypeEntityBean);
}
}
/**
* Derive an array of property positions for properties that are initialised in the constructor.
* These properties need to be unloaded when populating beans for queries.
*/
private int[] derivePropertiesToUnload(EntityBean prototypeEntityBean) {
boolean[] loaded = prototypeEntityBean._ebean_getIntercept().getLoaded();
int[] props = new int[loaded.length];
int pos = 0;
// collect the positions of the properties initialised in the default constructor.
for (int i = 0; i < loaded.length; i++) {
if (loaded[i]) {
props[pos++] = i;
}
}
if (pos == 0) {
// nothing set in the constructor
return new int[0];
}
// populate a smaller/minimal array
int[] unload = new int[pos];
for (int i = 0; i < pos; i++) {
unload[i] = props[i];
}
return unload;
}
/**
* Create an entity bean that is used as a prototype/factory to create new instances.
*/
@@ -559,9 +601,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (isEmbedded()) {
// initialise all the properties
Iterator<BeanProperty> it = propertiesAll();
while (it.hasNext()) {
BeanProperty prop = it.next();
for (BeanProperty prop : propertiesAll()) {
prop.initialise();
}
} else {
@@ -579,9 +619,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (!isEmbedded()) {
// initialise all the non-id properties
Iterator<BeanProperty> it = propertiesAll();
while (it.hasNext()) {
BeanProperty prop = it.next();
for (BeanProperty prop : propertiesAll()) {
if (!prop.isId()) {
prop.initialise();
}
@@ -842,6 +880,20 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
cacheBeanPutData((EntityBean)bean);
}
/**
* Extract the raw cache data from the bean.
*/
public CachedBeanData cacheBeanExtractData(EntityBean bean) {
return cacheHelp.beanExtractData(bean);
}
/**
* Load the raw cache data into the bean.
*/
public void cacheBeanLoadData(EntityBean bean, CachedBeanData data) {
cacheHelp.beanLoadData(bean, data);
}
/**
* Put a bean into the bean cache.
*/
@@ -970,9 +1022,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* Reset the statistics on all the query plans.
*/
public void clearQueryStatistics() {
Iterator<CQueryPlan> it = queryPlanCache.values().iterator();
while (it.hasNext()) {
CQueryPlan queryPlan = (CQueryPlan) it.next();
for (CQueryPlan queryPlan : queryPlanCache.values()) {
queryPlan.resetStatistics();
}
}
@@ -988,13 +1038,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
}
/**
* Return the query plans for this BeanDescriptor.
*/
public Iterator<CQueryPlan> queryPlans() {
return queryPlanCache.values().iterator();
}
public CQueryPlan getQueryPlan(HashQueryPlan key) {
return queryPlanCache.get(key);
}
@@ -1128,7 +1171,17 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
*/
public EntityBean createEntityBean() {
try {
return (EntityBean)prototypeEntityBean._ebean_newInstance();
EntityBean bean = (EntityBean)prototypeEntityBean._ebean_newInstance();
if (unloadProperties.length > 0) {
// 'unload' any properties initialised in the default constructor
EntityBeanIntercept ebi = bean._ebean_getIntercept();
for (int i = 0; i < unloadProperties.length; i++) {
ebi.setPropertyUnloaded(unloadProperties[i]);
}
}
return bean;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
@@ -1155,8 +1208,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoaderByServerName(ebeanServer.getName());
ebi.setBeanLoader(ebeanServer);
// Note: not creating proxies for many's...
ebi.setReference(idPropertyIndex);
@@ -1463,7 +1515,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (propertyDeploy && chain != null) {
BeanFkeyProperty fk = fkeyMap.get(propName);
if (fk != null) {
return fk.create(chain.getExpression());
return fk.create(chain.getExpression(), chain.isContainsMany());
}
}
@@ -1786,10 +1838,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
/**
* Return an Iterator of all BeanProperty. This includes transient properties.
* Return a collection of all BeanProperty. This includes transient properties.
*/
public Iterator<BeanProperty> propertiesAll() {
return propMap.values().iterator();
public Collection<BeanProperty> propertiesAll() {
return propMap.values();
}
/**
@@ -1818,21 +1870,31 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return idProperty;
}
public boolean isInsertMode(EntityBeanIntercept ebi) {
/**
* Return true if this bean should be inserted rather than updated.
*
* @param ebi
* The entity bean intercept
* @param insertMode
* true if the 'root request' was an insert rather than an update
*/
public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) {
if (ebi.isLoaded()) {
// must be an update as the bean is loaded
return false;
}
// determine based on Id property
if (idProperty.isEmbedded()) {
// not using Id generator so just base on isLoaded()
return !ebi.isLoaded();
}
//if (idGenerator == null) {
// return !ebi.isLoaded();
//} else {
return !hasIdProperty(ebi);
//}
if (!hasIdProperty(ebi)) {
// No Id property means it must be an insert
return true;
}
// same as the 'root request'
return insertMode;
}
public boolean isReference(EntityBeanIntercept ebi) {
@@ -1857,7 +1919,27 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return false;
}
/**
* Check for mutable scalar types and mark as dirty if necessary.
*/
public void checkMutableProperties(EntityBeanIntercept ebi) {
for (int i = 0; i < propertiesMutable.length; i++) {
BeanProperty beanProperty = propertiesMutable[i];
if (ebi.isDirtyProperty(beanProperty.getPropertyIndex())) {
// already marked as dirty
} else if (ebi.isLoadedProperty(beanProperty.getPropertyIndex())) {
Object value = beanProperty.getValue(ebi.getOwner());
if (value == null || beanProperty.isDirtyValue(value)) {
// mutable scalar value which is considered dirty so mark
// it as such so that it is included in an update
ebi.markPropertyAsChanged(beanProperty.getPropertyIndex());
}
}
}
}
public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) {
if (!hasVersionProperty(ebi)) {
return ConcurrencyMode.NONE;
} else {
@@ -2031,174 +2113,23 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return propertiesLocal;
}
public void jsonWrite(WriteJsonContext ctx, EntityBean bean) {
if (bean != null) {
ctx.appendObjectBegin();
WriteBeanState prevState = ctx.pushBeanState(bean);
if (inheritInfo != null) {
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
String discValue = localInheritInfo.getDiscriminatorStringValue();
String discColumn = localInheritInfo.getDiscriminatorColumn();
ctx.appendDiscriminator(discColumn, discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
localDescriptor.jsonWriteProperties(ctx, bean);
} else {
jsonWriteProperties(ctx, bean);
}
ctx.pushPreviousState(prevState);
ctx.appendObjectEnd();
}
}
@SuppressWarnings("unchecked")
private void jsonWriteProperties(WriteJsonContext ctx, EntityBean bean) {
JsonWriteBeanVisitor<T> beanVisitor = (JsonWriteBeanVisitor<T>) ctx.getBeanVisitor();
Set<String> props = ctx.getIncludeProperties();
boolean explicitAllProps;
if (props == null) {
explicitAllProps = false;
} else {
explicitAllProps = props.contains("*");
if (explicitAllProps || props.isEmpty()) {
props = null;
}
}
if (idProperty != null) {
Object idValue = idProperty.getValue(bean);
if (idValue != null) {
if (props == null || props.contains(idProperty.getName())) {
idProperty.jsonWrite(ctx, bean);
}
}
}
if (!explicitAllProps && props == null) {
// just render the loaded properties
props = ((EntityBean)bean)._ebean_getIntercept().getLoadedPropertyNames();
}
if (props != null) {
// render only the appropriate properties (when not all properties)
for (String prop : props) {
BeanProperty p = getBeanProperty(prop);
if (p != null && !p.isId()) {
p.jsonWrite(ctx, bean);
}
}
} else {
if (explicitAllProps || !isReference(bean._ebean_getIntercept())) {
// render all the properties and invoke lazy loading if required
for (int j = 0; j < propertiesNonTransient.length; j++) {
propertiesNonTransient[j].jsonWrite(ctx, bean);
}
for (int j = 0; j < propertiesTransient.length; j++) {
propertiesTransient[j].jsonWrite(ctx, bean);
}
}
}
if (beanVisitor != null) {
beanVisitor.visit((T) bean, ctx);
}
}
@SuppressWarnings("unchecked")
public T jsonReadBean(ReadJsonContext ctx, String path) {
ReadBeanState beanState = jsonRead(ctx, path);
if (beanState == null) {
return null;
} else {
return (T) beanState.getBean();
}
}
public ReadBeanState jsonRead(ReadJsonContext ctx, String path) {
if (!ctx.readObjectBegin()) {
// the object is null
return null;
}
if (inheritInfo == null) {
return jsonReadObject(ctx, path);
} else {
// check for the discriminator value to determine the correct sub type
String discColumn = inheritInfo.getRoot().getDiscriminatorColumn();
if (!ctx.readKeyNext()) {
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
throw new TextException(msg);
}
String propName = ctx.getTokenKey();
String discValue;
if (propName.equalsIgnoreCase(discColumn)) {
discValue = ctx.readScalarValue();
if (!ctx.readValueNext()) {
// Expected to read a comma to setup for reading the real properties of the bean
String msg = "Error reading inheritance discriminator [" + discColumn + "]. Expected more json name values?";
throw new TextException(msg);
}
} else {
// Assume that the we are just reading using this bean type
// Push the token key back so that it is re-read as it is one
// of the real properties of the bean itself
ctx.pushTokenKey();
discValue = inheritInfo.getDiscriminatorStringValue();
}
// determine the sub type for this particular json object
InheritInfo localInheritInfo = inheritInfo.readType(discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
return localDescriptor.jsonReadObject(ctx, path);
}
}
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, null);
}
private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) {
EntityBean bean = createEntityBean();
ctx.pushBean(bean, path, this);
do {
if (!ctx.readKeyNext()) {
break;
} else {
// we read a property key ...
String propName = ctx.getTokenKey();
BeanProperty p = getBeanProperty(propName);
if (p != null) {
p.jsonRead(ctx, bean);
ctx.setProperty(propName);
} else {
// unknown property key ...
ctx.readUnmappedJson(propName);
}
if (!ctx.readValueNext()) {
break;
}
}
} while (true);
return ctx.popBeanState();
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, key);
}
public void flushPersistenceContextOnIterate(PersistenceContext persistenceContext) {
persistenceContext.clear(beanType);
for (int i = 0; i < propertiesMany.length; i++) {
persistenceContext.clear(propertiesMany[i].getBeanDescriptor().getBeanType());
}
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWriteProperties(writeJson, bean);
}
public T jsonRead(JsonParser parser, String path) throws IOException {
return jsonHelp.jsonRead(parser, path);
}
protected T jsonReadObject(JsonParser parser, String path) throws IOException {
return jsonHelp.jsonReadObject(parser, path);
}
}
@@ -25,7 +25,6 @@ import com.avaje.ebeaninternal.server.cache.CachedBeanDataUpdate;
import com.avaje.ebeaninternal.server.cache.CachedManyIds;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
@@ -328,14 +327,15 @@ public final class BeanDescriptorCacheHelp<T> {
if (context == null) {
context = new DefaultPersistenceContext();
}
context.put(query.getId(), bean);
DLoadContext loadContext = new DLoadContext(desc.getEbeanServer(), desc, query.isReadOnly(), query);
loadContext.setPersistenceContext(context);
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();
// Not using a loadContext for beans coming out of L2 cache
// so that means no batch lazy loading for these beans
EntityBean entityBean = (EntityBean)bean;
EntityBeanIntercept ebi = entityBean._ebean_getIntercept();
ebi.setPersistenceContext(context);
loadContext.register(null, ebi);
Object id = desc.getId(entityBean);
context.put(id, bean);
}
/**
@@ -360,12 +360,20 @@ public final class BeanDescriptorCacheHelp<T> {
}
}
public CachedBeanData beanExtractData(EntityBean bean) {
return CachedBeanDataFromBean.extract(desc, bean);
}
public void beanLoadData(EntityBean bean, CachedBeanData data) {
CachedBeanDataToBean.load(desc, bean, data);
}
/**
* Put a bean into the bean cache.
*/
public void beanCachePut(EntityBean bean) {
CachedBeanData beanData = CachedBeanDataFromBean.extract(desc, bean);
CachedBeanData beanData = beanExtractData(bean);
Object id = desc.getId(bean);
if (beanLog.isDebugEnabled()) {
@@ -402,15 +410,15 @@ public final class BeanDescriptorCacheHelp<T> {
@SuppressWarnings("unchecked")
private T beanCacheGetInternal(Object id, Boolean readOnly) {
CachedBeanData d = (CachedBeanData) getBeanCache().get(id);
if (d == null) {
CachedBeanData data = (CachedBeanData) getBeanCache().get(id);
if (data == null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - cache miss", cacheName, id);
}
return null;
}
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
Object bean = d.getSharableBean();
Object bean = data.getSharableBean();
if (bean != null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit shared bean", cacheName, id);
@@ -421,18 +429,21 @@ public final class BeanDescriptorCacheHelp<T> {
EntityBean bean = desc.createEntityBean();
desc.convertSetId(id, bean);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
ebi.setBeanLoader(desc.getEbeanServer());
if (Boolean.TRUE.equals(readOnly)) {
bean._ebean_getIntercept().setReadOnly(true);
ebi.setReadOnly(true);
}
CachedBeanDataToBean.load(desc, bean, d);
beanLoadData(bean, data);
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit", cacheName, id);
}
return (T) bean;
}
/**
* Remove a bean from the cache given its Id.
*/
@@ -0,0 +1,131 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson.WriteBean;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
public class BeanDescriptorJsonHelp<T> {
private final BeanDescriptor<T> desc;
private final InheritInfo inheritInfo;
public BeanDescriptorJsonHelp(BeanDescriptor<T> desc) {
this.desc = desc;
this.inheritInfo = desc.inheritInfo;
}
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
writeJson.writeStartObject(key);
if (inheritInfo == null) {
jsonWriteProperties(writeJson, bean);
} else {
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
String discValue = localInheritInfo.getDiscriminatorStringValue();
String discColumn = localInheritInfo.getDiscriminatorColumn();
writeJson.gen().writeStringField(discColumn, discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
localDescriptor.jsonWriteProperties(writeJson, bean);
}
writeJson.writeEndObject();
}
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
WriteBean writeBean = writeJson.createWriteBean(desc, bean);
writeBean.write(writeJson);
}
@SuppressWarnings("unchecked")
public T jsonRead(JsonParser parser, String path) throws IOException {
JsonToken token = parser.nextToken();
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
return null;
}
if (JsonToken.START_OBJECT != token) {
throw new JsonParseException("Unexpected token "+token+" - expecting start_object", parser.getCurrentLocation());
}
if (desc.inheritInfo == null) {
return jsonReadObject(parser, path);
}
// check for the discriminator value to determine the correct sub type
String discColumn = inheritInfo.getRoot().getDiscriminatorColumn();
token = parser.nextToken();
if (token != JsonToken.FIELD_NAME) {
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
throw new JsonParseException(msg, parser.getCurrentLocation());
}
String propName = parser.getCurrentName();
if (!propName.equalsIgnoreCase(discColumn)) {
// just try to assume this is the correct bean type in the inheritance
BeanProperty property = desc.getBeanProperty(propName);
if (property != null) {
EntityBean bean = desc.createEntityBean();
property.jsonRead(parser, bean);
return jsonReadProperties(parser, bean);
}
String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?";
throw new JsonParseException(msg, parser.getCurrentLocation());
}
String discValue = parser.nextTextValue();
// determine the sub type for this particular json object
InheritInfo localInheritInfo = inheritInfo.readType(discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
return (T) localDescriptor.jsonReadObject(parser, path);
}
protected T jsonReadObject(JsonParser parser, String path) throws IOException {
EntityBean bean = desc.createEntityBean();
return jsonReadProperties(parser, bean);
}
@SuppressWarnings("unchecked")
protected T jsonReadProperties(JsonParser parser, EntityBean bean) throws IOException {
do {
JsonToken event = parser.nextToken();
if (JsonToken.FIELD_NAME == event) {
String key = parser.getCurrentName();
BeanProperty p = desc.getBeanProperty(key);
if (p != null) {
p.jsonRead(parser, bean);
} else {
// Object rawValue = EJson.parse(parser);
// unknown property key ...
// ctx.readUnmappedJson(propName);
}
} else if (JsonToken.END_OBJECT == event) {
break;
} else {
throw new RuntimeException("Unexpected token " + event + " - expecting key or end_object at: " + parser.getCurrentLocation());
}
} while (true);
return (T)bean;
}
}
@@ -1,23 +1,27 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Model;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.annotation.ConcurrencyMode;
@@ -60,9 +64,7 @@ import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
import com.avaje.ebeaninternal.server.reflect.BeanReflectProperties;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory;
import com.avaje.ebeaninternal.server.type.TypeManager;
@@ -149,6 +151,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final XmlConfig xmlConfig;
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
/**
* Create for a given database dbConfig.
*/
@@ -177,6 +181,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly();
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory();
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.persistListenerManager = new PersistListenerManager(bootupClasses);
this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses);
@@ -506,9 +511,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityBeanTable() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
}
@@ -522,18 +525,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityDeploymentAssociations() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
readDeployAssociations(info);
}
}
private void readInheritedIdGenerators() {
Iterator<DeployBeanInfo<?>> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo<?> info = it.next();
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && !inheritInfo.isRoot()) {
@@ -609,10 +608,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* @param info the new inheritance info
*/
private void setInheritanceInfo(DeployBeanInfo<?> info) {
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
if (assoc != null){
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -622,7 +621,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
if (assoc != null){
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -751,9 +749,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
String searchName = name.substring(0, p).toLowerCase();
// search for this in the possible matches
Iterator<String> it = matchSet.iterator();
while (it.hasNext()) {
String possibleMappedBy = it.next();
for (String possibleMappedBy : matchSet) {
String possibleLower = possibleMappedBy.toLowerCase();
if (possibleLower.indexOf(searchName) > -1) {
// we have a match..
@@ -873,7 +869,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
if (!tableJoin.hasJoinColumns()) {
// define Join as the inverse of the mappedBy property
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable());
otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable());
}
}
@@ -1010,9 +1006,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
desc.setUpdateChangesOnly(updateChangesOnly);
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
desc.checkInheritanceMapping();
createProperties.createProperties(desc);
@@ -1147,12 +1146,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void setScalarType(DeployBeanDescriptor<?> deployDesc) {
Iterator<DeployBeanProperty> it = deployDesc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop instanceof DeployBeanPropertyAssoc<?>) {
} else {
for (DeployBeanProperty prop : deployDesc.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssoc<?> == false) {
deployUtil.setScalarType(prop);
}
}
@@ -1306,30 +1301,39 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
desc.setBeanReflect(beanReflect);
desc.setProperties(reflectProps.getProperties());
Iterator<DeployBeanProperty> it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
for (DeployBeanProperty prop : desc.propertiesAll()) {
String propName = prop.getName();
Integer pos = reflectProps.getPropertyIndex(propName);
if (pos == null) {
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
}
BeanReflectGetter getter = beanReflect.getGetter(propName, pos.intValue());
BeanReflectSetter setter = beanReflect.getSetter(propName, pos.intValue());
prop.setGetter(getter);
prop.setSetter(setter);
prop.setPropertyIndex(pos.intValue());
if (getter == null) {
String m = "BeanReflectGetter for " + prop.getFullBeanName() + " was not found?";
throw new RuntimeException(m);
if (isPersistentField(prop)) {
throw new IllegalStateException("Property "+propName+" not found in "+reflectProps);
}
} else {
int propertyIndex = pos.intValue();
prop.setPropertyIndex(propertyIndex);
prop.setGetter(beanReflect.getGetter(propName, propertyIndex));
prop.setSetter(beanReflect.getSetter(propName, propertyIndex));
}
}
}
/**
* Return true if this is a persistent field (not transient or static).
*/
private boolean isPersistentField(DeployBeanProperty prop) {
Field field = prop.getField();
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) {
return false;
}
if (field.isAnnotationPresent(Transient.class)) {
return false;
}
return true;
}
/**
* DevNote: It is assumed that Embedded can contain version properties. It is
* also assumed that Embedded beans do NOT themselves contain Embedded beans
@@ -1401,20 +1405,51 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* enhanced or all dynamically subclassed).
*/
private void checkInheritedClasses(Class<?> beanClass) {
Class<?> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
// we got to the top of the inheritance
return;
}
if (Model.class.equals(superclass)) {
// top of the inheritance. Not enhancing Model at this stage
return;
}
if (!EntityBean.class.isAssignableFrom(superclass)) {
if (isMappedSuperWithNoProperties(superclass)) {
// ok to stop and treat just the same as Object.class
return;
}
throw new IllegalStateException("Super type "+superclass+" is not enhanced?");
}
// recursively continue up the inheritance hierarchy
checkInheritedClasses(superclass);
}
/**
* Return true if this is a MappedSuperclass bean with no persistent properties.
* If so it is ok for it not to be enhanced.
*/
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
if (annotation == null) {
return false;
}
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) {
// ignore this field
} else if (field.isAnnotationPresent(Transient.class)) {
// ignore this field
} else {
return false;
}
}
return true;
}
/**
* Comparator to sort the BeanDescriptors by name.
*/

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