Compare commits

...
Author SHA1 Message Date
rbygrave d490966025 [maven-release-plugin] prepare release avaje-ebeanorm-4.7.3 2015-06-24 23:50:02 +12:00
rbygrave 5bc0b01a6c #308 - Deprecate BeanFinder ... preference now to use BeanFindController 2015-06-24 23:42:35 +12:00
rbygrave 2bd14db8f7 #308 - Add BeanFindController 2015-06-24 23:36:34 +12:00
rbygrave 17a0933860 #308 - Add BeanFindController 2015-06-24 23:12:34 +12:00
rbygrave 5bb8384567 No effective change - format only 2015-06-24 22:44:08 +12:00
rbygrave 29a6bbb6e6 #312 - Ability to remove deregister BeanPersistListeners removed 2015-06-24 22:32:12 +12:00
rbygrave 46a1324f58 [maven-release-plugin] prepare for next development iteration 2015-06-22 00:22:45 +12:00
rbygrave 8f7199b586 [maven-release-plugin] prepare release avaje-ebeanorm-4.7.2 2015-06-22 00:22:23 +12:00
rbygrave d2c3142de4 WIP on Finder 2015-06-22 00:20:01 +12:00
rbygrave 721a50219d #307 - Add back support for JsonValueAdapter and JsonReadOptions into 4.x 2015-06-21 01:46:23 +12:00
Rob Bygrave f0b8270e5e Update README.md 2015-06-18 00:11:04 +12:00
rbygrave 92232c2afb [maven-release-plugin] prepare for next development iteration 2015-06-17 23:18:58 +12:00
rbygrave 5e68b1a25e [maven-release-plugin] prepare release avaje-ebeanorm-4.7.1 2015-06-17 23:18:36 +12:00
rbygrave 041324d177 #310 - Child bean with @Id long id = 0 ... when cascade saved from parent ... tries to update instead of insert resulting in OptimisticLockingException 2015-06-17 23:16:02 +12:00
rbygrave 7d02a42276 #309 - ScalarTypeConverters for Java8 time datatypes not working 2015-06-17 21:13:48 +12:00
rbygrave 2a900165f0 #305 - ENH: Add EbeanServerFactory.createWithContextClassLoader() to support OSGi 2015-06-03 20:47:58 +12:00
rbygrave e7c40e9552 No effective change - update javadoc on annotations 2015-06-01 12:26:59 +12:00
rbygrave 0d92807308 No effective change - change test code to use Long as @Version on BasicDomain 2015-06-01 12:14:20 +12:00
rbygrave 16f7de30c6 No effective change - format code 2015-06-01 12:11:53 +12:00
rbygrave 3d250b0eca No effective change - format code, tidy 2015-05-30 21:42:50 +12:00
rbygrave 8403adac90 #298 - JndiDataSourceLookup assumes java: prefix 2015-05-30 20:54:51 +12:00
rbygrave c006aa3f48 No effective change - inline String messages 2015-05-30 20:54:12 +12:00
rbygrave dcb7054248 #303 - Change beginTransaction() to use REQUIRED scope by default 2015-05-30 20:10:56 +12:00
rbygrave 12833bc918 Bump pom version to 4.7.1-SNAPSHOT 2015-05-30 19:43:15 +12:00
rbygrave 71d805bc17 #302 - Postgres specific - Rename @ColumnHstore to @DbHstore (deprecate @ColumnHstore ) 2015-05-30 19:41:54 +12:00
rbygrave ab8704e2c9 Bump version in README 2015-05-29 21:52:43 +12:00
rbygrave 327e2baae7 No effective change - format only 2015-05-29 21:52:00 +12:00
rbygrave 6e89c551d5 [maven-release-plugin] prepare for next development iteration 2015-05-29 21:17:29 +12:00
rbygrave 0a3941227f [maven-release-plugin] prepare release avaje-ebeanorm-4.6.3 2015-05-29 21:17:08 +12:00
rbygrave 0013cb1a76 #301 - MySQL specific issue - java.sql.SQLException: Streaming result set com.mysql.jdbc.RowDataDynamic@3ef4415f is still active. No statements may be issued when any streaming result sets a re open and in use on a given connection. Ensure that you have called .close() on any active streaming result sets before attempting more queries 2015-05-29 21:13:30 +12:00
rbygrave 8fe91f66c0 #300 - Stateless update with deleteMissingChildren and JDBC batched inserts on those children ... can delete newly inserted child 2015-05-29 20:51:23 +12:00
rbygrave 053fdf4abb No effective change - format and tidy 2015-05-29 20:49:55 +12:00
rbygrave 24be907dc5 No effective change - improved logging when PooledConnection.close() throws exception 2015-05-29 20:39:54 +12:00
rbygrave 6aa915b420 Fix test for cross database use 2015-05-29 20:38:38 +12:00
rbygrave 40d87af5b1 Fix test for cross database use 2015-05-29 20:38:07 +12:00
rbygrave 75082fe0c0 Fix test for cross database use 2015-05-29 20:37:12 +12:00
rbygrave 7d43f5a3d7 Fix test for cross database use 2015-05-29 20:36:51 +12:00
rbygrave 99203a35a2 No effective change - reformat and tidy 2015-05-29 00:40:47 +12:00
rbygrave 67f427b83e #299 - ENH: Add support for mapping Jackson JsonNode as JSON content to DB including Postgres JSON and JSON types 2015-05-28 22:54:32 +12:00
rbygrave f5a2cf8ff9 #297 - ENH: Add support for mapping Map<String,Object> as JSON content to DB including Postgres JSON and JSON types. 2015-05-28 00:02:57 +12:00
rbygrave 4e1cda691f No effective change - reformat and tidy 2015-05-19 01:35:27 +12:00
rbygrave 64b01324d1 No effective change - reformat and tidy 2015-05-19 01:25:51 +12:00
rbygrave 3cba238073 No effective change - reformat and tidy 2015-05-18 23:23:11 +12:00
rbygrave b5b4a4639f No effective change - reformat and tidy 2015-05-18 22:17:28 +12:00
rbygrave 2d0a179c3b No effective change - reformat and tidy 2015-05-15 00:09:19 +12:00
rbygrave 40f7a0088d No effective change - add tests for ConvertInetAddressesTest and DefaultTypeFactoryTest 2015-05-14 23:22:19 +12:00
rbygrave 6a02a8d69f No effective change - code format and tidy 2015-05-14 22:42:37 +12:00
rbygrave ed18cbcabf No effective change - add test for ConvertInetAddresses 2015-05-14 21:57:38 +12:00
Rob Bygrave 3ce4f2fb35 Merge pull request #294 from andersha/add_distinct_db2
add distinct to db2
2015-05-14 21:35:59 +12:00
Anders H. Abrahamsen 962a27f838 add distinct to db2 2015-05-13 15:52:50 +02:00
rbygrave 2b583cf707 Update README.md 2015-05-13 23:09:49 +12:00
rbygrave ef9ade4f5c [maven-release-plugin] prepare for next development iteration 2015-05-13 22:56:09 +12:00
rbygrave b1b7774a23 [maven-release-plugin] prepare release avaje-ebeanorm-4.6.2 2015-05-13 22:55:48 +12:00
rbygrave c6632bbc25 No effective change - code tidy, remove unused 2015-05-13 22:51:28 +12:00
rbygrave 8c12fc762c No effective change - code tidy, remove unused 2015-05-13 22:30:40 +12:00
rbygrave 8cb0b13ccb No effective change - code tidy, remove unused 2015-05-13 22:01:10 +12:00
rbygrave c2faffb6f4 No effective change - code tidy 2015-05-13 21:58:19 +12:00
rbygrave e17d823c17 No effective change - code tidy 2015-05-13 21:41:23 +12:00
rbygrave 9bf9338ba9 Fix for #293 - Invalid result rows when ManyToMany "fetch joined" with additional child OneToMany or ManyToMany child relationship fetched 2015-05-13 21:01:00 +12:00
rbygrave 0da764ed0c Test for #293 - Invalid result rows when ManyToMany "fetch joined" with additional child OneToMany or ManyToMany child relationship fetched 2015-05-13 21:00:10 +12:00
rbygrave 43003f4a3a No effective change - adjust test to miss cache 2015-05-13 20:54:54 +12:00
rbygrave 7081d3f54a No effective change - remove unused writer Method (left over from dynamic proxy support) 2015-05-13 20:53:51 +12:00
rbygrave cce2c2e28f No effective change - remove unused writer Method (left over from dynamic proxy support) 2015-05-13 20:52:58 +12:00
rbygrave 61ecf16978 No effective change - reformat 2015-05-11 23:59:41 +12:00
rbygrave 9ff4e63c32 #290 - Tries to register abstract class that implements custom ScalarType interface 2015-05-11 23:51:23 +12:00
rbygrave a13fd55f64 [maven-release-plugin] prepare for next development iteration 2015-05-09 08:58:08 +12:00
rbygrave a90d413e01 [maven-release-plugin] prepare release avaje-ebeanorm-4.6.1 2015-05-09 08:55:41 +12:00
rbygrave f2559f80ef #285 - ENH: support disable search classes config via ServerConfig.setDisableClasspathSearch(true) 2015-05-09 01:30:38 +12:00
rbygrave 23da6f3de8 No effective change - change newline char 2015-05-09 01:12:21 +12:00
rbygrave bdf35646a6 No effective change - change newline char 2015-05-09 01:12:09 +12:00
rbygrave f947a76fce No effective change - change newline char 2015-05-09 01:11:56 +12:00
rbygrave 88f144ec33 No effective change - change newline char 2015-05-09 01:11:44 +12:00
rbygrave 21c8dd524a No effective change - change newline char 2015-05-09 01:11:26 +12:00
rbygrave 624da4a34a No effective change - change newline char 2015-05-09 01:11:09 +12:00
rbygrave e3cccfa9a3 No effective change - change newline char 2015-05-09 01:10:56 +12:00
rbygrave 3358ab16a7 No effective change - change newline char 2015-05-09 01:10:42 +12:00
rbygrave 4eaa6adf08 No effective change - change newline char 2015-05-09 01:09:57 +12:00
rbygrave 4143c0de26 No effective change - change newline char 2015-05-09 01:09:32 +12:00
rbygrave 726d3a0c38 No effective change - change newline char 2015-05-09 01:09:16 +12:00
rbygrave 8f12bbb6c4 No effective change - change newline char 2015-05-09 01:08:53 +12:00
rbygrave 89db75e8c5 No effective change - change newline char 2015-05-09 01:08:33 +12:00
rbygrave dfb69e3cde No effective change - change newline char 2015-05-09 01:08:05 +12:00
rbygrave 39520e367d No effective change - change newline char 2015-05-09 01:07:41 +12:00
rbygrave 30d697d490 No effective change - change newline char 2015-05-09 01:06:00 +12:00
rbygrave 241a1236b8 No effective change - change newline char 2015-05-09 01:05:11 +12:00
rbygrave ccada72323 No effective change - change newline char 2015-05-09 01:03:40 +12:00
rbygrave c2ddab2ba0 No effective change - change newline char 2015-05-09 01:02:55 +12:00
rbygrave 27a0e93fda No effective change - change newline char 2015-05-09 01:01:45 +12:00
rbygrave f306672c6c No effective change - change newline char 2015-05-09 00:59:59 +12:00
rbygrave dc86b8b447 No effective change - change newline char and remove extraneous public modifiers on interfaces 2015-05-09 00:55:12 +12:00
rbygrave d63eb7abe1 No effective change - change newline char and remove extraneous public modifiers on interfaces 2015-05-09 00:51:49 +12:00
rbygrave cc200e8607 No effective change - change newline char and remove extraneous public modifiers on interfaces 2015-05-09 00:28:43 +12:00
rbygrave fffc684edd Add test for beginTransaction() with existing active transaction 2015-05-08 23:51:55 +12:00
rbygrave 82471c2573 #288 - Exclude MaxRows FirstRow in findRowCount 2015-05-08 23:49:11 +12:00
rbygrave f7fa778e99 #287 - ENH: Support nested transactions with the Ebean.beginTransaction() API 2015-05-08 21:55:49 +12:00
rbygrave fc3e1083d7 #283 - SPI change: Rename BootupEbeanManager to SpiContainer ... with a view to future exposing cluster specific methods 2015-05-01 22:06:55 +12:00
rbygrave 819edb20e8 #282 - API: Add Model.Find as alternative to Model.Finder ... which uses reflection to determine entity bean type literal 2015-05-01 21:03:38 +12:00
rbygrave e669a86135 Bump pom to 4.6.1-SNAPSHOT 2015-05-01 21:02:45 +12:00
rbygrave 4f67d2be4e Attempt to create test case for #281 - test case not failing 2015-04-30 20:30:12 +12:00
rbygrave 0356067d86 [maven-release-plugin] prepare for next development iteration 2015-04-24 23:50:25 +12:00
rbygrave 74c9c14838 [maven-release-plugin] prepare release avaje-ebeanorm-4.5.6 2015-04-24 23:50:02 +12:00
rbygrave fc53e281e8 #276 - WriteJson not write Transient properties (without using PathProperties) 2015-04-24 23:16:10 +12:00
rbygrave 0c5e092d16 Fix for #277 - please check fetchPaths not null - NPE on OrmQueryDetail.convertManyFetchJoinsToQueryJoins(OrmQueryDetail.java:331) 2015-04-24 22:18:22 +12:00
rbygrave 1260edc8ed Tweak to PrimaryServer to handle empty string defaultServerName values 2015-04-12 18:13:26 +12:00
Rob Bygrave de372bfb89 Merge pull request #270 from Ryszard-Trojnacki/master
Ability to run maven tests with diffrent databases.
2015-04-12 17:46:22 +12:00
rbygrave 2ed5e026e0 #275 - ENH: Add support for @SequenceGenerator initialValue and allocationSize 2015-04-12 12:10:57 +12:00
rbygrave 6c37dc494b Fix for #265 - java.lang.NoClassDefFoundError: Could not initialize class com.avaje.ebean.Ebean 2015-04-11 19:40:39 +12:00
rbygrave 6807499d01 Tweak test for #261 2015-04-11 19:30:05 +12:00
rbygrave cb6faa022b Fix for #260 - Trying to insert() or save() an entity with no @Id results in NPE 2015-04-11 19:16:36 +12:00
rbygrave 3b30388b35 Fix for #258 - Missing Inserts for Large Batches - Ebean 3.2.2 2015-04-11 18:55:08 +12:00
rbygrave db57404e18 #274 - Predicate using a foreign key column sometimes adds an extra join 2015-04-11 18:53:17 +12:00
rbygrave bede89fe13 Test for #269 - ENH: Add support for wildcard / startsWith jar searching 2015-04-11 11:14:48 +12:00
Ryszard-Trojnacki c9fb28b288 Added parameter datasource.default to jUnit (surefire plugin) for
changing default database in maven tests.
2015-04-02 08:29:53 +02:00
Ryszard-Trojnacki a192b0cdbf Ability to override default datasource with java parameter. 2015-04-02 08:23:33 +02:00
Rob Bygrave 0a15cdd7b2 Merge pull request #262 from ForNeVeR/issue-259
Fix issue #259
2015-04-02 11:57:57 +13:00
Rob Bygrave 14c5a1328b Merge pull request #264 from mrock42/master
Add link to Ebean ORM project website
2015-04-02 11:55:54 +13:00
Micah Schehl cac6333c5e Add Link to Ebean ORM project website 2015-03-30 00:28:47 -04:00
Micah Schehl f7d1291a41 Add Link to Ebean ORM Website 2015-03-30 00:25:20 -04:00
Friedrich von Never 151e44ff67 Add test for issue #259. 2015-03-26 23:27:21 +06:00
Friedrich von Never 4f24944ec4 Do not use getPreWhere in case of unparsed SQL.
Fixes #259.
2015-03-26 22:28:29 +06:00
rbygrave c3ccb1afb5 No effective change - reformat code whitespace etc 2015-03-22 22:05:15 +13:00
Rob Bygrave ca8f529629 Merge pull request #257 from Ryszard-Trojnacki/master
Added ability to set alias for root table of sql query and sql command exists and not exists.
2015-03-22 21:48:31 +13:00
Ryszard-Trojnacki 2e03452e2e Added SQL command "EXISTS" and "NOT EXISTS" to Ebean (ExpressionFactory,
ExpressionList).
2015-03-19 10:43:12 +01:00
Ryszard-Trojnacki f78f4ea64a Added ability to set alias for root table of sql query. 2015-03-18 10:28:54 +01:00
rbygrave 81aeca5475 [maven-release-plugin] prepare for next development iteration 2015-02-09 22:08:23 +13:00
rbygrave 36b5b3b817 [maven-release-plugin] prepare release avaje-ebeanorm-4.5.5 2015-02-09 22:08:01 +13:00
Rob Bygrave d83a70a155 Merge pull request #245 from megazord/master
Adds support to multiple methods as event listeners
2015-02-08 07:58:01 +13:00
Megazord c140574eba Adds support to multiple methods as event listeners
Fixes #244
2015-02-07 01:26:03 -02:00
rbygrave 4ab2f0df3c #238 - update to test, still not reproducing yet 2015-02-05 00:09:43 +13:00
rbygrave a447361ea0 #238 - Potential fix for stackOverflow error / recursion issue 2015-02-05 00:06:03 +13:00
rbygrave 8fc3f2da15 #238 - unsuccessful attempt to reproduce issue 238 2015-02-04 23:13:53 +13:00
rbygrave a838bc38c5 No effective change - format code 2015-02-04 21:09:12 +13:00
Rob Bygrave 84e853bcfa Merge pull request #241 from tbee/EventListenerIssue
Exceptions in events are swallowed
2015-02-04 21:06:41 +13:00
Rob Bygrave e2a508aa5e Merge pull request #242 from tbee/ClasspathreaderConfig
ebean.classpathreader is not initialized from ebean.properties
2015-02-04 21:05:42 +13:00
Tom Eugelink e9c66abfe6 ebean.classpathreader is not initialized from ebean.properties 2015-02-03 15:52:23 +01:00
Tom Eugelink 050aad717d Exceptions in events are swallowed 2015-02-03 15:47:58 +01:00
rbygrave 30e02de6f3 Fix for #236 - OptimisticLockException when saving a model the second time with joda DateTime and mysql 5.6 2015-02-03 00:57:58 +13:00
rbygrave b8a7017ffd update README with version 4.5.4 2015-02-03 00:22:57 +13:00
rbygrave da61136465 [maven-release-plugin] prepare for next development iteration 2015-02-03 00:12:19 +13:00
rbygrave 90ec7bafb3 [maven-release-plugin] prepare release avaje-ebeanorm-4.5.4 2015-02-03 00:11:56 +13:00
rbygrave c638ad8d56 Fix for #235: Stored procedures not commited 2015-02-03 00:01:18 +13:00
rbygrave 6a582c0eef No effective change - code format 2015-02-03 00:00:49 +13:00
rbygrave 0551b3066f Fix for #237: find method is returning old entity rather than updated entity in some situations 2015-02-02 23:12:37 +13:00
rbygrave af36c9ec7d No effective change - update TestQueryFindPagedList with better test case error message 2015-02-02 22:03:42 +13:00
rbygrave bd99badb28 No effective change - code cleanup of BindableProperty to remove unused parameter (property name) 2015-02-02 21:43:08 +13:00
rbygrave f7e07633a1 No effective change - format code 2015-02-02 21:30:31 +13:00
rbygrave ace45a038e Fix for #240 - Add support for using Joda LocalDateTime and DateTime with @CreatedTimestamp, @UpdatedTimestamp 2015-02-02 20:39:06 +13:00
rbygrave 36dd2bbee7 Add test for #234 - Not-trimmed values in configuration properties 2015-01-29 22:29:02 +13:00
rbygrave 71e7baff01 #232 - no effective change, format of TransactionManager only 2015-01-17 13:32:13 +13:00
rbygrave ef093b2c6c Fix for #232 - New ebean leaves connections with active transactions 2015-01-17 13:28:34 +13:00
rbygrave 14de498955 bump version in README to 4.5.3 2015-01-13 00:59:42 +13:00
rbygrave bcda8576bc [maven-release-plugin] prepare for next development iteration 2015-01-13 00:48:20 +13:00
rbygrave 0d35c8cb1b [maven-release-plugin] prepare release avaje-ebeanorm-4.5.3 2015-01-13 00:48:00 +13:00
rbygrave 6362634924 Fix for #230 - Delete with OneToOne fails with foreign key constraint violation 2015-01-13 00:43:27 +13:00
rbygrave 676a9c8d6c Fix for #229 - EbeanCacheWarmer timer not canceled 2015-01-12 20:59:32 +13:00
rbygrave b1b9f68b7e Fix for #228 - LazyLoad bug with QueryIterator. Getting EntityNotFoundException("Bean has been deleted - lazy loading failed") 2015-01-12 20:41:37 +13:00
rbygrave 961b219e3e Fix for #227 - Many2Many cache bug, incorrect query plan hash calculation 2015-01-11 20:45:07 +13:00
rbygrave a998c0be55 Fix for #226 - Json - support parsing json with unknown bean properties 2014-12-19 00:01:02 +13:00
rbygrave e0159ccb4f Modify JsonContext added support for toBean() toList() and toObject() using JsonParser 2014-12-18 22:07:47 +13:00
rbygrave 423066ada7 Update README with version 4.5.2 2014-12-18 01:14:05 +13:00
rbygrave c9d968b4b0 [maven-release-plugin] prepare for next development iteration 2014-12-18 00:46:55 +13:00
rbygrave 34be7cc1f1 [maven-release-plugin] prepare release avaje-ebeanorm-4.5.2 2014-12-18 00:46:32 +13:00
rbygrave 54f21836b0 Javadoc update for RawSql 2014-12-18 00:44:58 +13:00
rbygrave 4adf5a9eba Fix for #224 - ENH: Add tableAliasMapping() to RawSqlBuilder ... for better mapping of complex objects with raw sql 2014-12-17 23:58:27 +13:00
rbygrave b6a0653a3c Fix for #223 - RawSql that includes a OneToMany not working 2014-12-17 23:05:53 +13:00
rbygrave c0810a375e No effective change - code formatting 2014-12-17 23:04:59 +13:00
rbygrave 90fe19eb8d cleanup of ebean.properties 2014-12-17 21:04:21 +13:00
rbygrave 43ecfe7c19 Fix for #185 - RawSql parse not working with "order siblings by NAME" 2014-12-17 21:03:53 +13:00
rbygrave 15be5dd4c7 Small tweak to example ebean.properties 2014-12-17 00:54:34 +13:00
rbygrave d54cffacea #222 - SQL Server - OnetoMany with compound key using IN is not legal in mssql 2014-12-17 00:53:42 +13:00
rbygrave 965de6e84d #101 - SQL SERVER - drop.sql does not disable referential integrity 2014-12-17 00:52:31 +13:00
rbygrave 56fd6b90be Rename db columns that clash with sql server keywords etc 2014-12-17 00:49:23 +13:00
rbygrave dd85eba3f0 Rename db columns that clash with sql server keywords etc 2014-12-17 00:32:43 +13:00
rbygrave d1a40d5676 Enable disallowBatchOnCascade per DatabasePlatform (added for SQL Server) 2014-12-17 00:31:29 +13:00
rbygrave b0462bcda6 Enable disallowBatchOnCascade per DatabasePlatform (added for SQL Server) 2014-12-17 00:28:28 +13:00
rbygrave 59428bb214 Rename db columns that clash with sql server keywords etc 2014-12-17 00:20:48 +13:00
rbygrave 73cf3e8511 [maven-release-plugin] prepare for next development iteration 2014-12-13 01:14:36 +13:00
rbygrave beff4465b0 [maven-release-plugin] prepare release avaje-ebeanorm-4.5.1 2014-12-13 01:14:14 +13:00
rbygrave 563cdd3b3b Fix for #221 - ebean 4.4.1 does no longer respects ebean.namingConvention.useForeignKeyPrefix 2014-12-13 01:12:28 +13:00
rbygrave 1bd7b99235 Added @Transactional support for PersistBatch #220 2014-12-12 23:32:41 +13:00
rbygrave bb9d9f7e17 Bump pom to 4.5.1-SNAPSHOT 2014-12-10 03:30:56 +13:00
rbygrave 6bb38f2674 #220 - Refactor Persist batch, add effectively add PersistBatch.INSERT (to ALL and NONE) and allow batching per request (save(), insert(), update(), delete()) 2014-12-10 03:29:44 +13:00
rbygrave bb6571f426 javadoc update, eol characters 2014-12-04 01:47:11 +13:00
rbygrave 1ee679c5ae javadoc update 2014-12-04 01:20:35 +13:00
rbygrave e5264ca69e Update version in README to 4.4.1 2014-12-03 22:35:09 +13:00
rbygrave 3a86be5f3d #219 - Update PagedList javadoc - enable foreground query for getTotalRowCount() when loadRowCount() has not been used 2014-12-03 22:33:35 +13:00
rbygrave 9f71b0df22 [maven-release-plugin] prepare for next development iteration 2014-12-01 23:39:03 +13:00
rbygrave f271ea8a0d [maven-release-plugin] prepare release avaje-ebeanorm-4.4.1 2014-12-01 23:38:42 +13:00
rbygrave 1ced574cd8 bump pom version to 4.4.1-SNAPSHOT 2014-12-01 23:36:41 +13:00
rbygrave 9dd412cd19 #218 - Internal change - modify SequenceIdGenerator to use Long instead of Integer 2014-12-01 23:35:25 +13:00
rbygrave 8237072f5b #217 - ENH: Restore findFutureList() removing its previous deprecated status. Change implementation to use its own PersistenceContext (aka PersistenceContextScope.QUERY) 2014-12-01 23:09:25 +13:00
rbygrave dd30b43d59 #216 - ENH: Add Model.Finder deleteById(id) method 2014-12-01 23:02:58 +13:00
rbygrave 09f8967459 #215 - Remove findPagingList() ... it has been deprecated for a while, please migrate to findPagedList() 2014-12-01 21:52:40 +13:00
rbygrave e3bf3629ee Remove extraneous ServerConfig.enhanceLogLevel which is no longer used 2014-12-01 21:27:27 +13:00
rbygrave b6edd33f9a Fix for #214 - Change default lazyLoadBatchSize from 1 to 10 (in ServerConfig) 2014-12-01 21:16:30 +13:00
rbygrave 3a11a23e01 Fix for #213 - Make PathProperties easier to use - deprecate JsonWriteOptions 2014-12-01 21:10:51 +13:00
rbygrave 370267ab6f Refactor properties bootup, remove GlobalProperties and allow ServerConfig.loadFromProperties() 2014-11-30 17:08:02 +13:00
rbygrave fe65a0ccb9 Change Test - make test more explicit wrt batch lazy loading 2014-11-30 12:55:46 +13:00
rbygrave 2837324a03 Change Test - make test more explicit wrt batch lazy loading 2014-11-30 12:55:16 +13:00
rbygrave a6bd614f87 bump version in readme 2014-11-27 01:20:57 +13:00
rbygrave dfaf4b2f31 [maven-release-plugin] prepare for next development iteration 2014-11-27 01:01:41 +13:00
rbygrave 0f26160e1b [maven-release-plugin] prepare release avaje-ebeanorm-4.3.1 2014-11-27 01:00:53 +13:00
rbygrave cb66aaf66c Change pom to attach javadocs as jar for deploy to central 2014-11-27 00:58:47 +13:00
rbygrave 78b1c05de1 [maven-release-plugin] prepare for next development iteration 2014-11-26 23:54:47 +13:00
rbygrave c70a4f80eb [maven-release-plugin] prepare release avaje-ebeanorm-4.3.0 2014-11-26 23:54:21 +13:00
rbygrave cc788e281f No effective change - javadoc update 2014-11-26 23:53:06 +13:00
rbygrave d55a7b26f8 No effective change - javadoc warnings 2014-11-26 23:08:01 +13:00
rbygrave 2195d7ecbc #211 - ENH: Add support for File as a bean type ... for streaming into/from db 2014-11-26 22:45:33 +13:00
rbygrave 983391af94 #210 - ENH: Add Query.setLazyLoadBatch(int size) method ... for per query control 2014-11-26 20:24:14 +13:00
rbygrave 478053263f #210 - ENH: Add Query.setLazyLoadBatch(int size) method ... for per query control 2014-11-26 20:13:30 +13:00
rbygrave 1cd31e13b9 bump pom to 4.3.0-SNAPSHOT 2014-11-25 23:55:24 +13:00
rbygrave 374d7faae9 bump pom to 4.3.0-SNAPSHOT 2014-11-25 23:54:46 +13:00
rbygrave 6a6cff2236 Fix for #209 - refresh() ... does not refresh OneToMany or ManyToMany properties 2014-11-25 23:40:21 +13:00
rbygrave d5d6ac675b Add support for java.time types for use with @CreatedTimestamp and @UpdatedTimestamp 2014-11-24 20:41:46 +13:00
rbygrave 8ea1f51c7f Remove deprecated Expression.join() methods 2014-11-23 00:53:48 +13:00
rbygrave 6ecb77dcc2 Fix for #206 - Reuse SqlUpdate when binding a list that can vary in size 2014-11-22 23:40:14 +13:00
rbygrave 216806074c Remove PersistenceContextScope.NONE for now 2014-11-22 12:46:40 +13:00
rbygrave 65cb2ae94d #203: ENH: Add Transaction hook ... for preCommit(), postCommit(), postRollback() 2014-11-22 12:14:21 +13:00
rbygrave 6677a6199c Fix for #207: Add support for PeristenceContextScope QUERY and NONE (in addition to the existing TRANSACTION scope) 2014-11-20 00:53:32 +13:00
rbygrave 55eaa0b484 Fix for #204: Revert change from #163 - Add back support for DataSource with autocommit=true 2014-11-19 20:10:50 +13:00
rbygrave 353f662d70 Fix for #205: For JsonContext API wrap checked IOException in runtime JsonIOException 2014-11-19 20:00:31 +13:00
rbygrave c95d76d6f6 Fix for #201: Change BeanPersistListener to use isRegisterFor() method rather than generics type (just like BeanPersistController) 2014-11-19 19:43:34 +13:00
Rob Bygrave e04d6979d1 Merge pull request #202 from ebean-orm/java8
Add support for java.time types, tidy up ScalarTypes
2014-11-19 00:13:37 +13:00
rbygrave b8078083b1 Add MonthDay support, ScalarTypeMonthDay 2014-11-18 23:57:53 +13:00
rbygrave 07cdb69a5b Add Period support via CompoundTypePeriod, rename server/reflect to server/properties 2014-11-18 23:09:26 +13:00
rbygrave 98f6e1e6af Update ScalarTypeMonth and ScalarTypeDayOfWeek 2014-11-18 20:06:13 +13:00
rbygrave fd4d757f87 WIP Java8 types - ScalarTypeDuration etc 2014-11-18 01:20:26 +13:00
rbygrave b71b0fc8e2 WIP Java8 types - ScalarTypeDuration etc 2014-11-17 01:09:43 +13:00
rbygrave 4303bcfd13 No effective change - tidy up on DeployCreateProperties 2014-11-17 00:23:30 +13:00
rbygrave 7f30dc7b1e WIP Java8 types - ScalarTypeDuration etc 2014-11-17 00:05:20 +13:00
rbygrave 4a882b537c #197 - Add Query findEach() findEachWhile() ... as better method names for findVisit(). More consistent with forEach() methods 2014-11-14 21:26:32 +13:00
rbygrave 641385aab5 ScalarTypeLocalDate update - some test ordering issues 2014-11-14 01:01:32 +13:00
rbygrave 33ab7eb7a5 Add ScalarTypeLocalDate 2014-11-13 23:13:51 +13:00
Rob Bygrave f6674f8e3b [maven-release-plugin] prepare for next development iteration 2014-11-13 01:28:18 +13:00
Rob Bygrave 4e8b3f22de [maven-release-plugin] prepare release avaje-ebeanorm-4.2.0 2014-11-13 01:27:58 +13:00
Rob Bygrave 55e43ddc36 Add whitespace for MANIFEST.MF 2014-11-13 01:26:47 +13:00
Rob Bygrave 97d75c06a1 Revert MANIFEST.MF - invalid header 2014-11-13 01:15:47 +13:00
Rob Bygrave adcfa939e4 revert MANIFEST.MF - invalid jackson part 2014-11-13 01:11:07 +13:00
Rob Bygrave 8a4d78a3f5 Fix JSON for ScalarTypePostgresHstore with field name and nulls 2014-11-13 00:49:20 +13:00
Rob Bygrave 04e77d9949 Bump pom to 4.2.0-SNAPSHOT 2014-11-12 23:30:31 +13:00
Rob Bygrave 76e7b8d199 Add to Model the apply(PathProperties) method 2014-11-12 23:06:26 +13:00
Rob Bygrave 00f85a42f8 JSON Refactor - move EJson into text.json package 2014-11-12 22:56:42 +13:00
Rob Bygrave e649a70509 make SimpleTextParser class not public 2014-11-12 22:35:48 +13:00
Rob Bygrave 2ec50d2c71 Add query.apply(PathProperties) 2014-11-12 22:24:37 +13:00
Rob Bygrave eaee3a9c02 JSON refactor - tidy up 2014-11-12 22:10:28 +13:00
Rob Bygrave 221272328c WIP JSON refactor - tidy up ScalarTypes 2014-11-12 00:17:24 +13:00
Rob Bygrave 1677917465 Move from javax.json to jackson-core for parser/generator 2014-11-10 20:31:11 +13:00
Rob Bygrave bfefdbac41 Merge from JSON refactoring branch 2014-11-06 23:32:27 +13:00
Rob Bygrave 58755a51a9 JSON Refactor - compound type support 2014-11-06 23:18:20 +13:00
Rob Bygrave b313809894 WIP json refactor - initial tidy 2014-11-05 23:15:09 +13:00
rbygrave 0f3b1eb812 Tidy up test - TestInfoOneToOne, oneToOne cascade update 2014-10-29 23:45:24 +13:00
rbygrave 306cc4adf4 Add test for oneToOne cascade update 2014-10-29 23:43:11 +13:00
rbygrave ed88978173 No effective change - javadoc tweak on params 2014-10-29 23:41:40 +13:00
rbygrave ae266968b4 [maven-release-plugin] prepare for next development iteration 2014-10-29 00:11:43 +13:00
rbygrave aa86454cc4 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.9 2014-10-29 00:11:20 +13:00
rbygrave c99bec4940 Add First/Second/SuperSecond test 2014-10-29 00:07:25 +13:00
rbygrave 7426274ef6 bump agent to 4.1.9 2014-10-28 23:46:20 +13:00
Rob Bygrave 130b7e3327 Add another self referencing test - SelfParent 2014-10-20 23:06:50 +13:00
Rob Bygrave de201715fc #196 - Add support for using java.net.InetAddress in entity beans 2014-10-20 22:36:18 +13:00
Rob Bygrave 241f6f3d6d #196 - Add support for using java.net.InetAddress in entity beans 2014-10-20 22:31:22 +13:00
Rob Bygrave 1210aca027 Add test for SqlUpdate in various transaction scopes 2014-10-20 20:18:17 +13:00
Rob Bygrave e653c34211 Fix for #188 - ClassPathSearch not searching in running Jar 2014-10-16 23:15:16 +13:00
Rob Bygrave 131750cf85 No effective change - format 2014-10-16 22:58:13 +13:00
Rob Bygrave 17b16fd5d9 Merge pull request #194 from nedge/master
FIX for ManyToOne targetType & OSGI MANIFEST
2014-10-15 19:55:17 +13:00
Eddie Mc Greal 1065a84709 FIX for ManyToOne targetType & OSGI MANIFEST 2014-10-14 08:10:31 +02:00
Rob Bygrave 13791a2e58 Update version in readme 2014-10-08 22:13:48 +13:00
Rob Bygrave 277703d750 [maven-release-plugin] prepare for next development iteration 2014-10-08 21:44:25 +13:00
Rob Bygrave 637cbc6489 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.8 2014-10-08 21:44:15 +13:00
Rob Bygrave 5d09ca16d6 Fix for #192 - Backslash literal in LIKE expression - Postgres (and H2, MySql) behaviour 2014-10-08 21:42:39 +13:00
Rob Bygrave 6ed1f25e22 [maven-release-plugin] prepare for next development iteration 2014-09-10 00:02:41 +12:00
Rob Bygrave b8ae443074 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.7 2014-09-10 00:02:31 +12:00
Rob Bygrave ab9762bbd9 Fix for #190 - javax.persistence.PersistenceException: Server [null] was not found? at EntityBeanIntercept.loadBean(EntityBeanIntercept.java:693) 2014-09-10 00:00:49 +12:00
Rob Bygrave 2c90b239cd [maven-release-plugin] prepare for next development iteration 2014-08-22 00:45:30 +12:00
Rob Bygrave 13806767a3 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.6 2014-08-22 00:45:20 +12:00
Rob Bygrave 40e43b9d5f update agent and maven plugin versions to 4.1.6 2014-08-22 00:44:08 +12:00
Rob Bygrave 29f1e63146 Fix for #186 - Fix for Lazy load when fetched optional ManyToOne bean is null in database 2014-08-22 00:41:12 +12:00
rbygrave 5cf008b521 Fix for #182 Modify pom removing provided scope from slf4j-api dependency 2014-08-12 23:30:41 +12:00
rbygrave 3e788d4598 [maven-release-plugin] prepare for next development iteration 2014-08-01 21:43:53 +12:00
rbygrave 8a29454893 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.5 2014-08-01 21:43:31 +12:00
rbygrave 62c083a6ea Add LongMaxUpdater, change CQueryPlanStats to use LongMaxUpdater 2014-07-29 23:48:38 +12:00
rbygrave 7f8e4be396 Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2014-07-29 23:44:38 +12:00
rbygrave 2ad39974e8 No effective change - code format and javadoc tidy 2014-07-29 23:44:03 +12:00
Rob Bygrave 664e95d87b Merge pull request #180 from rvbiljouw/master
Index annotation for generating single-column indexes in DDL
2014-07-29 22:45:39 +12:00
Rick van Biljouw 5cf668f65b Disallow Index annotation on fields that have a relationship annotation (OneToMany, ManyToOne or OneToOne) 2014-07-28 19:44:25 +01:00
Rick van Biljouw 31f56621ff Added @Index annotation to generate single-column indexes 2014-07-28 18:43:50 +01:00
rbygrave 11a6cd04ea Update README with latest version 2014-07-26 00:12:41 +12:00
rbygrave fdb573bfe2 Fix for #177 - EbeanServer.refresh() not loading/refreshing @Lob property unless it is annotated with @Basic(fetch=FetchType.EAGER) 2014-07-26 00:09:26 +12:00
rbygrave ae0e1fe691 No effective change - remove some incorrect javadoc 2014-07-25 23:21:14 +12:00
rbygrave 0511e01273 [maven-release-plugin] prepare for next development iteration 2014-07-24 19:45:56 +12:00
rbygrave f3bd38536c [maven-release-plugin] prepare release avaje-ebeanorm-4.1.4 2014-07-24 19:45:30 +12:00
rbygrave 139a558165 Add test TestPropertyChangeListener 2014-07-21 22:59:31 +12:00
rbygrave 53b40f7ce0 Update .gitignore for h2 db directory 2014-07-21 22:32:05 +12:00
rbygrave e50656871d Fix for #175 - Change to use System.getProperty("ebean.serverfactory")
rather than GlobalProperties
2014-07-21 22:31:01 +12:00
rbygrave f7ece0f13c Fix for #174 - Incorrect SQL generated with syntax error at the "order by" clause 2014-07-21 21:49:31 +12:00
rbygrave 562c6fafc9 Update maven-enhancer to 4.1.5, improve test - TestNotEnhancedMappedSuper 2014-07-19 19:41:57 +12:00
rbygrave 63e9f603ec Code cleanup - move old iterator/while loops to for loops 2014-07-19 19:10:13 +12:00
rbygrave 0c4fc4eea3 Fix for #173 - Ability to use a MappedSuperClass without being enhanced as long as it doesn't have persistent fields 2014-07-19 17:49:03 +12:00
rbygrave 2dca1bdf5b Fix for #172 - Regression: @Where predicates not applied in eager fetch (but are on lazy load) 2014-07-18 23:35:14 +12:00
rbygrave 6403a83e0e No effective change - format only 2014-07-18 22:52:49 +12:00
Rob Bygrave b57b91ffef [maven-release-plugin] prepare for next development iteration 2014-07-16 23:18:02 +12:00
Rob Bygrave 4ba5702211 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.3 2014-07-16 23:17:06 +12:00
Rob Bygrave 2e7aeae004 Bump version in README 2014-07-16 23:14:52 +12:00
rbygrave 3f2676ab49 Fix for #168 - ENH: DataSource - add support for configuring autocommit=true 2014-07-16 21:58:32 +12:00
rbygrave 3f29acb25c Fix for #169 - Caused by: java.lang.ClassCastException: org.example.Order cannot be cast to org.example.Customer 2014-07-15 23:09:08 +12:00
rbygrave 6d1a41f979 Fix for #167 - Ebean try to do update child nodes instead of inserting them on cascading save resulting in OptimisticLockException 2014-07-15 22:05:56 +12:00
Rob Bygrave aee46955a7 Add test which saves parent and child using same pk value 2014-07-10 23:27:18 +12:00
rbygrave 38a93b9814 Update README with new version 2014-07-10 22:41:38 +12:00
rbygrave 5f2ad0dc62 [maven-release-plugin] prepare for next development iteration 2014-07-10 21:41:01 +12:00
rbygrave ece7f481bc [maven-release-plugin] prepare release avaje-ebeanorm-4.1.2 2014-07-10 21:40:39 +12:00
rbygrave fa4d0380e5 Add extra test - TestQueryRowCountWithMany 2014-07-10 21:29:44 +12:00
rbygrave 713ba4889d Clean up of test for #162 2014-07-10 21:23:45 +12:00
rbygrave 0543745e18 Test case for #162 - no issue reproduced, no assert failing 2014-07-10 19:48:06 +12:00
rbygrave 4e2bc6cce6 Fix for #166 - Incorrect join with @OneToOne optional on one side and
NOT optional on other side
2014-07-10 03:05:52 +12:00
rbygrave f2d422ff5a Test case for #165 - Incorrect join used when query language with where
expression has a many property and ends with a foreign key column
2014-07-10 01:20:51 +12:00
rbygrave 176197513d No effective change - format only 2014-07-09 23:51:19 +12:00
rbygrave 1e3035c558 Fix for #164 - Incorrect join used when where expression has a many
property and ends with a foreign key column
2014-07-09 23:31:15 +12:00
rbygrave 17f7143447 Fix for #163 - Remove detection of autocommit=true from JdbcTransaction 2014-07-09 22:24:28 +12:00
rbygrave 394dfc96d1 Update Test - cleanup and add clarification test to TestManyWhereJoin 2014-07-09 22:23:31 +12:00
Rob Bygrave 452ce78c3d Fix for #161 - DDL Generation - @OneToOne (foreign key column / JoinColumn) should default to having a unique constraint on it 2014-07-08 23:23:46 +12:00
Rob Bygrave 4cb13760c2 Fix for #160 - DDL generation throwing NPE at com.avaje.ebeaninternal.server.ddl.CreateTableVisitor.isDbColumnWritten(CreateTableVisitor.java:60) 2014-07-08 23:04:27 +12:00
Rob Bygrave 5cc2eff0d4 Fix for #158 - Add support for using setDistinct (by excluding id property from generated sql) 2014-07-07 23:29:48 +12:00
Rob Bygrave af9a01be77 No effective change - update test 2014-07-05 23:08:09 +12:00
Rob Bygrave cceb7c035c Fix for #159 - Refactor - cleanup clode when closing query only transaction + change default option 2014-07-05 22:10:53 +12:00
Rob Bygrave 1ddc103582 Fix for #157 - Regression for @OrderBy on a @ManyToMany property due to change from issue 154 2014-07-02 23:56:42 +12:00
Rob Bygrave b973cc3b86 No effective change - imports of Assert in tests + formatting 2014-07-02 23:29:54 +12:00
Rob Bygrave 10f0ea9bae No effective change - code format and tidy up 2014-07-02 23:10:53 +12:00
Rob Bygrave c49dd54500 Improve exception throw in BeanReflectProperties when _ebean_props not found 2014-07-02 21:23:28 +12:00
Rob Bygrave bf0944bb2a [maven-release-plugin] prepare for next development iteration 2014-06-30 22:06:04 +12:00
Rob Bygrave 59ba6244c1 [maven-release-plugin] prepare release avaje-ebeanorm-4.1.1 2014-06-30 22:05:43 +12:00
Rob Bygrave f88524b34c Fix for #156 - NPE in DDL Generation when Embedded bean as column with unique constraint 2014-06-30 22:03:31 +12:00
Rob Bygrave 0ceda6406e WIP on JSON refactor 2014-06-28 03:09:10 +12:00
Rob Bygrave 6838b4df4f Fix for #155 Add markAsDirty() method 2014-06-27 23:08:36 +12:00
Rob Bygrave 5db7ae504e [maven-release-plugin] prepare for next development iteration 2014-06-26 19:39:13 +12:00
Rob Bygrave 723ae6bffc [maven-release-plugin] prepare release avaje-ebeanorm-4.1.0 2014-06-26 19:38:48 +12:00
Rob Bygrave 33b173dd55 Bump pom to 4.1.0-SNAPSHOT 2014-06-26 19:37:15 +12:00
Rob Bygrave 7966d74eb3 Just tweaking some very common string appending with buffer size prediction 2014-06-26 01:15:48 +12:00
Rob Bygrave 28cb2e210d Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2014-06-26 00:37:40 +12:00
Rob Bygrave 1109d412e7 Fix for #154 - @OrderBy on a @OneToMany property is not used when lazy loading 2014-06-26 00:37:31 +12:00
Rob Bygrave 3e33ece65a No effective change - add protected modified 2014-06-26 00:12:55 +12:00
Rob Bygrave 1dd200fba2 Merge pull request #153 from waffle-iron/master
waffle.io Badge
2014-06-25 09:16:10 +12:00
Making GitHub Delicious. f0ea977f91 add waffle.io badge 2014-06-24 15:13:51 -06:00
Rob Bygrave e830d8efc7 Additional test in TestOrderByWithMany 2014-06-24 00:17:11 +12:00
Rob Bygrave 0b2ba2a82e Fix for #151 - IllegalArgumentException using enum in filter - toBeanType() throws 2014-06-24 00:04:32 +12:00
Rob Bygrave df526b8f3c Fix for #149 - OneToMany with nested ManyToMany deletion bug 2014-06-23 23:34:22 +12:00
Rob Bygrave 8fe1f32e78 No effective change - code reformat on PersistRequestBean 2014-06-21 00:03:28 +12:00
Rob Bygrave c5ea93fb8e Fix for #150 - Stateless update - property changed in BeanPersistAdapter
preUpdate not included in update
2014-06-20 23:49:29 +12:00
Rob Bygrave dd83bc4d26 Test BeanPersistAdapter with stateless update 2014-06-19 02:03:59 +12:00
Rob Bygrave 9b4b7a1a01 Opps, part of #108 2014-06-19 02:03:14 +12:00
Rob Bygrave 548fa745d6 Fix for #108 - ENH (443) : Postgres HStore support 2014-06-19 01:35:07 +12:00
Rob Bygrave 7d83e3f49e Fix for #97 - ENH: (406) Add support for Postgres Serial type 2014-06-13 23:48:45 +12:00
Rob Bygrave 974284bf8a Addition to fix for #139 - DDL constraint generation when table with schema 2014-06-13 02:38:14 +12:00
Rob Bygrave b446f00cd8 Addition to fix for #139 - DDL constraint generation when table with schema 2014-06-12 23:48:40 +12:00
Rob Bygrave a4095b4ae5 Fix for #142 - ENH: Add support for using RawSql with user supplied ResultSet 2014-06-12 21:11:14 +12:00
Rob Bygrave af6dbd0dbe Fix for #140 - Add some support for RawSql findRowCount, remove BeanCollection.hasMoreRows() 2014-06-10 23:52:05 +12:00
Rob Bygrave db17ddd069 No effective change - tidy up test for findPagedList 2014-06-10 23:10:27 +12:00
Rob Bygrave b6b85225e7 Fix for #139 - DDL constraint generation when table with schema 2014-06-10 21:13:16 +12:00
Rob Bygrave b57abfa732 Update README.md 2014-05-30 03:28:25 +12:00
Rob Bygrave 4a4927b1f3 Update README.md 2014-05-30 03:27:38 +12:00
Rob Bygrave 6ac34bec97 Update README.md 2014-05-30 03:26:59 +12:00
Rob Bygrave 82725e6a98 [maven-release-plugin] prepare for next development iteration 2014-05-30 03:07:28 +12:00
Rob Bygrave a744c8b375 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.4 2014-05-30 03:07:04 +12:00
Rob Bygrave 5e2a34de39 removing maven-javadoc-plugin section from pom as it breaks oss sonatype
deployment
2014-05-30 03:05:16 +12:00
Rob Bygrave 8a9ab39a01 Fix for #138 Issue with lazy loading after a findIterate query 2014-05-30 02:50:51 +12:00
Rob Bygrave 515256abfd pom - javadoc + remove unused enhancement section 2014-05-30 01:36:20 +12:00
Rob Bygrave 84e5a6145e Fix for #137 - transaction.setBatchFlushOnQuery(false) not working for
some queries
2014-05-30 01:35:19 +12:00
Rob Bygrave 1f5618d0e3 Fix for #136 - Minor - not log extra SQL into log when using jdbc batch
mode
2014-05-30 01:32:44 +12:00
Rob Bygrave b1de821817 Fix for #135 Make Model abstract, change db() and db(server) to be
static methods.
2014-05-30 01:30:57 +12:00
Rob Bygrave f4dceb97b0 [maven-release-plugin] prepare for next development iteration 2014-05-27 22:28:35 +12:00
Rob Bygrave ad79f175da [maven-release-plugin] prepare release avaje-ebeanorm-4.0.3 2014-05-27 22:28:09 +12:00
Rob Bygrave a4b145bc2d Updated Model - javadoc and cleanup 2014-05-27 22:19:41 +12:00
Rob Bygrave 5491b27528 Fix for #131 - Memory issue (Hard refs) when using findIterate 2014-05-27 20:35:53 +12:00
Rob Bygrave 9211c7e6ab Update Model - insert,update,delete to other server, db(), db(server)
and javadoc tweaks
2014-05-26 01:18:17 +12:00
Rob Bygrave 5bcb6cd116 Cleanup of Model and Finder 2014-05-25 02:33:08 +12:00
Rob Bygrave 534f2c8b07 Initial cleanup of Model and Finder 2014-05-25 02:22:36 +12:00
Rob Bygrave 7ad390934e Add protected method on Ebean to specifically support mocking out the
default server.
2014-05-25 01:59:34 +12:00
Rob Bygrave 2a651c38e3 Initial add of Model and Finder 2014-05-25 00:27:32 +12:00
James RoperandRob Bygrave ddd30f9ea0 Backport to JDK 6
Replaced all uses of ju.Objects.hashCode and ju.Objects.equals with a
copy of their implementations inlined into the code.

Also changed source/target for compiler plugin.

To test, I compiled my own avaje launchagent against 6, and changed to
use that in the pom, compiled/tested the whole project using JDK 7,
then ran mvn surefire:test using JDK 6 - running surefire:test ensures
that mvn doesn't try to recompile everything against 6, since that's not
possible because of some of the delegate classes having delegate methods
to JDK 7 jdbc classes.
2014-05-24 22:51:05 +12:00
Rob Bygrave 01d41b7eea Update README.md 2014-05-19 00:26:21 +12:00
Rob Bygrave 4cdea33073 [maven-release-plugin] prepare for next development iteration 2014-05-18 23:19:06 +12:00
Rob Bygrave 33d7e10ad8 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.2 2014-05-18 23:18:42 +12:00
Rob Bygrave 2201f9220e Change version in pom for 4.0.2 release 2014-05-18 23:17:17 +12:00
Rob Bygrave b8743a5ef2 Fix for #85 - Invalid query when ORDER BY with DISTINCT 2014-05-18 23:00:28 +12:00
Rob Bygrave b587aa8172 No effective change - touch up test assert 2014-05-18 22:04:37 +12:00
Rob Bygrave c37fa83675 No effective change - update test with better assert 2014-05-18 21:51:14 +12:00
Rob Bygrave 08602afcff Fix for #37 - disjunction expression should not produce inner join - left outer join instead 2014-05-18 21:17:19 +12:00
Rob Bygrave 5b98d82f58 No effective change - reformat 2014-05-17 23:53:23 +12:00
Rob Bygrave 22cd5b0e82 Fix for #128 storing UUID as binary 2014-05-17 21:52:51 +12:00
Rob Bygrave 93855478dd Fix for #128 storing UUID as binary 2014-05-17 21:52:18 +12:00
Rob Bygrave 59b86d5b19 Fix for #128 - UUID stored as binary(16) rather than varchar(40) 2014-05-17 21:51:18 +12:00
Rob Bygrave 27afb9ee2e Merge pull request #41 from corux/master
Unit test for BUG 402 / #37
2014-05-17 01:31:21 +12:00
Rob Bygrave a49cd42a0c Fix for #119 - Add support for @PrePersist, @PostPersist, @PostLoad 2014-05-17 01:24:32 +12:00
Rob Bygrave 8c8442ec58 Fix for #112 - ENH (411) : RawSql to support discriminator column mapping. 2014-05-17 00:11:44 +12:00
Rob Bygrave 41f3173adf Add PagedList was part of #96 2014-05-16 01:41:56 +12:00
Rob Bygrave 1a4192b40f Add another embedded bean l2 cache test 2014-05-15 23:57:20 +12:00
Rob Bygrave fffda7c789 Test and additional fix for #56 - Using findIterate with MySQL streams
the entire result set at once
2014-05-15 22:08:23 +12:00
Rob Bygrave b7f6596bcb Test case for #109 - Ebean magically adding +1 hour for a datetime that is inside the hour of a daylight saving change. This issue does not reproduce. 2014-05-15 00:18:03 +12:00
Rob Bygrave 1ea49dd206 Test for #110 - BeanSet of sub type with inheritance has null listen mode 2014-05-15 00:07:25 +12:00
Rob Bygrave 32e4f114b4 No effective change - cleanup of imports on test code 2014-05-14 23:47:46 +12:00
Rob Bygrave fcbedda597 Fix for #102 - When using @Transient along with @Formula I'm not seeing any values in the field. 2014-05-14 23:35:15 +12:00
Rob Bygrave 2055349d76 Fix for #103 DDL creation doesn't handle nullable attribute of @JoinColumn 2014-05-14 23:21:31 +12:00
Rob Bygrave 44ee64cde5 Fix for #57 - Embedded Entities with Autofetch Exception, plus some additional tests 2014-05-14 23:13:14 +12:00
Rob Bygrave 9f916998c9 Test and Fix for #127 Exception using findIds() with inheritance
SINGLE_TABLE
2014-05-14 21:10:52 +12:00
Rob Bygrave 2ddef37632 Merge pull request #58 from mbell697/autofetch-embedded
Failing test case for issue #57
2014-05-01 00:21:59 +12:00
Rob Bygrave 538ebdc899 Fix for #122 Allow the global row limit for queries to be configurable ( CQuery.GLOBAL_ROW_LIMIT) 2014-05-01 00:12:48 +12:00
Rob Bygrave d6e0d661fb no effective change - javadoc update 2014-05-01 00:11:59 +12:00
Rob Bygrave 289deb397a Embedded beans support in l2 cache - initial work 2014-04-30 01:49:20 +12:00
Rob Bygrave 68b4aab837 Add test for old (427) : Binding enum on find throws No ScalarType registered 2014-04-30 00:08:23 +12:00
Rob Bygrave 563293f7a9 Test case and fix for #113 (395) : On save @OneToOne does not cascade parent IDs to child node 2014-04-30 00:01:54 +12:00
Rob Bygrave 5049694fd3 Add test for old 408 bug - does not reproduce, not sure when fixed at this point 2014-04-29 23:19:03 +12:00
Rob Bygrave a9c8d1960c Add test - self referencing relationships: ResourceFile 2014-04-29 22:25:14 +12:00
Rob Bygrave 066b409a65 No effective change -Add test: Truck, Car, Wheel plus test code cleanup 2014-04-29 22:00:07 +12:00
Rob Bygrave 54ad2e0ad0 remove the Object[] transient from the test 2014-04-29 21:21:44 +12:00
Rob Bygrave 77f7bc2a7e Fix for #111 - Ebean doesn't honor @Transient for non-primitive array properties 2014-04-29 21:20:42 +12:00
Rob Bygrave f773d74597 Test for #105 - did not reproduce - Ebean is trying to delete new
objects added to the list of an entity and then removed without being
persisted before
2014-04-29 20:42:51 +12:00
Rob Bygrave 90b23c4985 Bump version of agent and mavenenhancer 2014-04-29 20:36:00 +12:00
Rob Bygrave 60b4ad410b [maven-release-plugin] prepare for next development iteration 2014-04-27 01:59:10 +12:00
Rob Bygrave 56215aafa3 [maven-release-plugin] prepare release avaje-ebeanorm-4.0.1-RC1 2014-04-27 01:58:45 +12:00
Rob Bygrave 5ba66e4d78 #96 - marking findPagingList() as deprecated 2014-04-27 01:52:44 +12:00
Rob Bygrave 0eb7139426 Fix for #95 - Deprecating - Query findFutureList() and SqlQuery findFutureList() 2014-04-27 01:44:26 +12:00
Rob Bygrave d16d92db54 Test case and Fix for #67 - Bug - @AttributeOverrides annotation for attribute definition in entity does not work for embedded primary keys 2014-04-27 00:57:29 +12:00
Rob Bygrave dde59db411 Test case for #75 - Persistence Context Cache not cleared when updating an entity in a running transaction. Actually the test case does not fail. 2014-04-27 00:05:00 +12:00
Rob Bygrave 1a13fc9742 Fixes for #94 - Additional API - added update(Collection beans); and insert(Collection beans); and #93 - ebean.properties defaultDeleteMissingChildren moved to updatesDeleteMissingChildren 2014-04-26 23:43:08 +12:00
Rob Bygrave e1ec0bcdfe Test and Fix for #39 - TestOnCascadeDeleteChildrenWithCompositeKeys from hei1233212000, with fix included 2014-04-25 22:34:03 +12:00
Rob Bygrave 27803afe64 Test and Fix for #39 - TestOnCascadeDeleteChildrenWithCompositeKeys from hei1233212000, with fix included 2014-04-25 22:32:33 +12:00
Rob Bygrave c011dab3e2 Change to use released 4.1.0 version of agent and maven enhancer 2014-04-25 19:58:41 +12:00
Rob Bygrave fb7abb44e0 Add a test for enhancement equals() and hashCode() 2014-04-25 19:23:56 +12:00
Rob Bygrave 14034ffaa0 Change to use enhancement for creating new entity bean instances 2014-04-25 15:54:07 +12:00
Rob Bygrave 75bcbc06e1 #53 - Add test for protected constructor 2014-04-25 15:19:50 +12:00
Rob Bygrave bc75736780 merge of develop-v4 2014-04-25 14:36:06 +12:00
Rob Bygrave ed0fe7fd57 Caching changes and refactoring, includes Fix for Wobu - TestCacheCollectionIds test case 2014-04-25 03:06:22 +12:00
wbuchnerandRob Bygrave 696dabc658 Add Wobu TestCacheDelete test case for cached beans 2014-04-24 02:42:28 +12:00
Wolfgang BuchnerandRob Bygrave 89f7d2bace Test and fix for TestStatelessUpdate.testStatelessRecursiveUpdateWithVersionField() 2014-04-24 02:34:01 +12:00
Rob Bygrave 4c4e084e43 WIP update/refactoring on caching 2014-04-24 01:57:52 +12:00
Wolfgang BuchnerandRob Bygrave 2dccb818a2 unit test for: update ManyToMany collection cache on updating cached beans 2014-04-23 21:19:06 +12:00
Rob Bygrave a7df7da984 Fix #23 - Covers: unit tests for: stateless updates which fails when executing without... 2014-04-23 21:16:30 +12:00
Wolfgang BuchnerandRob Bygrave b012720a5e unit test for: ensuring many relations won't be deleted on a partial stateless update when using BeanPersistAdapter 2014-04-23 03:29:10 +12:00
Rob Bygrave 1451ec0084 Fix for TestStatelessUpdate.testWithoutChangesAndVersionColumnAndIgnoreNullValues 2014-04-23 03:27:39 +12:00
Wolfgang BuchnerandRob Bygrave 0d2101ca7c unit tests for: stateless updates which fails when executing without changes 2014-04-23 03:05:16 +12:00
Wolfgang BuchnerandRob Bygrave a22496a57d added intelliJ files to gitignore 2014-04-23 03:00:00 +12:00
Rob Bygrave 654b10154e Fix for #83 - Bug: Stateless update cascading to OneToOne or ManyToOne incorrectly tries to INSERT rather than UPDATE 2014-04-23 02:31:08 +12:00
Rob Bygrave f4357c2767 Add test for reference on bean with constructor setting properties 2014-04-23 02:01:27 +12:00
Rob Bygrave 0d1c9e2883 Fix for #91 Stateless Update API Change, #90 Removed BeanState.setReference(), #89 Remove support for multiple @Id properties 2014-04-23 01:51:27 +12:00
Rob Bygrave 5e2d592c2a v4 refactor for diff() support 2014-04-21 12:34:51 +12:00
Rob Bygrave 2344f57f84 #88 - v4 - Breaking API Change - BeanPersistRequest.getOldValues() replaced with ... Map<String,ValuePair> getUpdatedValues() 2014-04-20 22:52:02 +12:00
Rob Bygrave fc628d336d Fix for #87 - v4 - Remove Query setBackgroundFetchAfter() feature 2014-04-20 19:30:40 +12:00
Rob Bygrave 18d9c53ce3 Fix for #86 - v4 - Remove deprecated QueryListener. You must convert to findIterate() or findVisit() 2014-04-20 17:18:13 +12:00
Rob Bygrave 811d298355 WIP - rename EntityBeanIntercept lazy loading property methods 2014-04-20 17:01:36 +12:00
Rob Bygrave f7a37537db WIP update api 2014-04-20 16:57:14 +12:00
Rob Bygrave 0c9d95619b [maven-release-plugin] prepare for next development iteration 2014-04-03 23:19:03 +13:00
Rob Bygrave 3b4836cad9 [maven-release-plugin] prepare release avaje-ebeanorm-3.3.2 2014-04-03 23:18:50 +13:00
Rob Bygrave 6048d97aab Update version in README 2014-04-03 23:17:24 +13:00
Rob Bygrave 16d32b408e Merge branch 'master' of https://github.com/ebean-orm/avaje-ebeanorm 2014-04-03 23:15:01 +13:00
Rob Bygrave 0435ab0991 Fix for #84 Fix for classpath searching with maven surefire / jarfile with manifest 2014-04-03 23:14:55 +13:00
rob 95617d1bea Update README with pom version 2014-04-03 09:12:28 +13:00
Rob Bygrave 96e4b4640f Merge in v4 code changes 2014-04-03 03:47:55 +13:00
Rob Bygrave a3c8ed88ca Update pom to 4.0.1-RC1-SNAPSHOT 2014-04-03 02:19:16 +13:00
Rob Bygrave 38d0e7642e [maven-release-plugin] prepare for next development iteration 2014-04-02 21:59:57 +13:00
Rob Bygrave e4306ce1ab [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1 2014-04-02 21:59:46 +13:00
Rob Bygrave 444b656780 Update pom to 3.3.1 2014-04-02 21:58:13 +13:00
Rob Bygrave fcbba23441 Test case for #83 - Bug: Stateless update cascading to OneToOne or
ManyToOne incorrectly tries to INSERT rather than UPDATE
2014-04-02 21:44:41 +13:00
Rob Bygrave 18961047a1 Fix for #80 - Exposing discriminator value in entity bean throws
exception when updating or deleting
2014-03-31 22:49:44 +13:00
Rob Bygrave 34347fc3e1 Fix for #77 - NPE when lazy loading on a OneToMany that is not a leaf 2014-03-31 22:06:10 +13:00
Rob Bygrave 189e0a2ea9 Part of fix for #79 - moving deactivate() and notifyCommit() into finally block 2014-03-21 21:54:47 +13:00
Rob Bygrave 182e027521 Further changes for #79 - returning/cleanup of bad connection 2014-03-21 02:42:41 +13:00
Rob Bygrave f85fa2c21d Fix for #79 - Rollback in case of connection closed - deactivate() not called when connection.rollback() fails 2014-03-20 20:14:14 +13:00
Rob Bygrave 5a5f479568 Merge pull request #81 from rvowles/master
assume that if includes are specified then turn default includes off
2014-03-16 15:02:28 +13:00
Rob Bygrave 9ca2486c9c Change defaultJarMatch back to default to false (as per previous release) 2014-03-16 14:31:21 +13:00
Richard Vowles 656b239925 assume that if includes are specified then turn default includes off 2014-03-16 13:11:51 +13:00
Rob Bygrave 5d46904341 Fix for introduced issue with sub-query expression 2014-03-16 00:05:43 +13:00
Rob Bygrave f0dd069ed2 Fix for #70 - @PrivateOwned makes Ebean try to remove non persistent objects from DB and throws OptimisticLockException 2014-03-15 13:48:56 +13:00
Rob Bygrave 5bd454bdc0 Merge pull request #76 from icaromuniz/master
Adding test case TestPrivateOwnedIgnoreTransientOrphan related to issue #70
2014-03-15 13:14:17 +13:00
Rob Bygrave eeebaf9a08 Fix for #78 - Wrong collection used in excludeJar 2014-03-15 12:53:58 +13:00
Rob Bygrave 636a148509 Fix for #79 - Rollback in case of connection closed 2014-03-15 12:45:44 +13:00
Icaro Muniz 170cd6051e Adding test case TestPrivateOwnedIgnoreTransientOrphan 2014-02-26 09:23:35 -03:00
Rob Bygrave 522fc2384c [maven-release-plugin] prepare for next development iteration 2014-01-31 02:12:20 +13:00
Rob Bygrave 6f214590aa [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1-RC2 2014-01-31 02:12:07 +13:00
Rob Bygrave 0014ea3501 Remove unused Lucene references including Query.UserIndex and associated methods 2014-01-31 01:26:12 +13:00
Rob Bygrave db3140512f set specific packages on dynamically loaded agent for tests 2014-01-31 00:59:48 +13:00
Rob Bygrave 8bfa331c61 Cleanup of expressions after FilterExprPath now only needed on FilterExpressionList 2014-01-31 00:48:59 +13:00
Rob Bygrave 456a1d8608 Remove AutoFetch query statistics meta entity beans, Remove Xml bean parsing (for json etc) 2014-01-31 00:22:38 +13:00
Rob Bygrave 695d3374a8 Default classPath scan include all jars (hopefully better default
behaviour)
2014-01-31 00:17:11 +13:00
Rob Bygrave 5488d054bf [maven-release-plugin] prepare for next development iteration 2014-01-30 23:25:57 +13:00
Rob Bygrave 4d72273dc3 [maven-release-plugin] prepare release avaje-ebeanorm-3.3.1-RC1 2014-01-30 23:25:44 +13:00
Rob Bygrave cb9a5d6709 Change pom version 3.3.1-RC1 2014-01-30 23:23:53 +13:00
Rob Bygrave 1b8aa682c6 Change classPath scan to support searching manifest (maven surefire classpath scanning) 2014-01-30 23:21:50 +13:00
Rob Bygrave ef39bfcf14 Merge branch 'many-lazy-load' 2014-01-30 22:32:50 +13:00
Rob Bygrave 749f8d0d45 Change *ToMany lazy loading to not use the parent entity/table 2014-01-30 22:27:52 +13:00
Rob Bygrave 2fd221bbb5 Removed old XML code that never got into main code 2014-01-23 00:55:09 +13:00
Rob Bygrave ee96c4afa6 Fix for Issue 72 - Bug - models.Role cannot be cast to java.util.Map [error] at com.avaje.ebeaninternal.server.persist.DefaultPersister.saveAssocManyDetails(DefaultPersister.java:877) 2014-01-23 00:50:09 +13:00
Rob Bygrave 87a948223c Merge branch 'dbpool-logging' 2014-01-22 01:09:19 +13:00
Rob Bygrave acca803a80 Add MetaObjectGraphNodeStats and MetaQueryPlanOriginCount etc 2014-01-22 01:05:22 +13:00
Rob Bygrave 470b27efe1 Mark QueryListener deprecated in favor of findIterate() and findVisit() 2014-01-21 19:41:46 +13:00
Rob Bygrave f287205421 Refactor of query plan hash - add HashQueryPlan, HashQuery etc 2014-01-21 00:48:38 +13:00
Rob Bygrave 6bd8c2bd05 Add MetaInfoManager API, remove MetaQueryStatistic entity bean, refactor query execution stats collection 2014-01-19 22:49:18 +13:00
Rob Bygrave 05dcdf162b Refactor internals of batch lazy loading and query fetches 2014-01-18 23:35:42 +13:00
Rob Bygrave e97e77e2e0 Add reading/setting of namingconvention.schema from ebean.properties 2014-01-17 00:31:03 +13:00
Rob Bygrave e685e2f4ac Updated maven version in README 2014-01-16 23:16:00 +13:00
Rob Bygrave c6019bf3c5 Fix for issue 46 - Persistence Context Cache not cleared when deleting a collection of entities in a running transaction 2014-01-16 23:09:57 +13:00
Rob Bygrave 1a9a4c957c Fix for issue 49 - Query parsing error for SELECT something * 1.5 AS something_else 2014-01-16 21:48:27 +13:00
Rob Bygrave bd57a3b9c8 Merge pull request #65 from yuba/master
make Transaction interface extends Closeable
2014-01-16 00:18:33 -08:00
Rob Bygrave 21a71c3a4f Fix for Issue 62 - Query.setId should not return all results for null input Object 2014-01-16 21:10:13 +13:00
Rob Bygrave f66439a62e Fix for Issue 40 - Expr does not have a betweenProperties(...) method. 2014-01-16 21:02:19 +13:00
Rob Bygrave 73452a5f68 Fix for issue 68 - findRowCount sql error when using a @Formula property in the where clause 2014-01-16 20:36:23 +13:00
Rob Bygrave 5f4789f9ba Support heart beat timeout, tidy up free buffer 2014-01-16 00:28:00 +13:00
yuba eaac874ac0 make Transaction interface extends Closeable
To avoid method name conflict, JtaTransaction#close was renamed to
closeConnection()
2013-12-27 14:00:00 +09:00
Rob Bygrave 4900a460d1 Fix for DataSourcePool trim idle connections not firing frequently
enough
2013-12-14 21:24:26 +13:00
Rob Bygrave d16f33d26b Modified DataSource pool - cleanup of reset and statistics collection 2013-12-12 20:57:50 +13:00
Rob Bygrave 664f08fd6f [maven-release-plugin] prepare for next development iteration 2013-11-25 21:06:09 +13:00
Rob Bygrave 39416e933e [maven-release-plugin] prepare release avaje-ebeanorm-3.2.5 2013-11-25 21:06:00 +13:00
Rob Bygrave 74b118e8f0 Merge pull request #59 from andersha/db2
support db2 and SqlLimiter for db2
2013-11-21 03:19:10 -08:00
AndersHA 43853b0357 cleanup db2 platform 2013-11-20 13:54:01 +01:00
AndersHA f62bc9a35f support db2 and write sqllimiter for db2 2013-11-20 11:07:23 +01:00
Mark Bell d8391ba90c Test embedded fetch also as this throws the same exception 2013-11-11 13:23:53 -05:00
Mark Bell c4d41ecb00 Failing test case for auto fetch + embedded exception 2013-11-11 13:13:46 -05:00
Rob Bygrave 9d8fe97de0 Adjusted Fix for Issue 56 - Using findIterate with MySQL streams the
entire result set at once.  This makes the fix specific to the
DatabasePlatform and findIterate() or findVisit().
2013-11-07 23:58:15 +13:00
Rob Bygrave b896e55af1 Fix for Issue 56 - Using findIterate with MySQL streams the entire
result set at once
2013-11-07 23:07:39 +13:00
Rob Bygrave bd75f2cf77 Merge pull request #55 from rvowles/master
Add support for bang paths
2013-11-07 01:15:15 -08:00
Rob Bygrave 4f33fe087e Merge pull request #47 from romming/BUG420
Thanks!!
2013-11-07 00:08:54 -08:00
Rob Bygrave ccf94bf1a4 Merge pull request #51 from mollstam/limitbug
Invalid SQL when firstRow set but not maxRows
2013-11-06 23:16:08 -08:00
Richard Vowles 0acabf820e Add support for bang paths 2013-11-02 20:07:35 +13:00
Tobias 1330fe8fb6 Generate valid SQL when firstRow is set but not maxRows 2013-10-07 16:29:23 +02:00
Rob Bygrave 09ea5217dc [maven-release-plugin] prepare for next development iteration 2013-09-11 16:26:44 +12:00
Rob Bygrave 93304949e8 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.4 2013-09-11 16:26:36 +12:00
Rob Bygrave 1a292275d8 Json Timestamp parsing - Add support for long values 2013-09-11 14:20:36 +12:00
Christian Romming 236fb5541b Fixed whitespace in test. 2013-08-28 23:15:20 +02:00
Christian Romming 1cafea20c6 Test and fix for BUG 420. 2013-08-28 23:06:37 +02:00
Rob Bygrave f71eac5f1e [maven-release-plugin] prepare for next development iteration 2013-08-02 12:18:45 +12:00
Rob Bygrave 3f9c3c1db5 [maven-release-plugin] prepare release avaje-ebeanorm-3.2.3 2013-08-02 12:18:36 +12:00
Rob Bygrave b4df899314 Move agent dependency to 3.2.2 (Fix for Issue 44 @Transactional
handling)
2013-08-02 12:16:41 +12:00
Rob Bygrave 9c7f62e8b1 Allow more flexibility with JSON marshalling of non-entity beans 2013-08-02 12:16:00 +12:00
Rob Bygrave bf8574833a Remove Thread UncaughtExceptionHandler as part of fix for pull/44
@Transactional not rolling back for uncaught RuntimeException - This
change is not required for 44 but is nice cleanup as
UncaughtExceptionHandler is no used.
2013-08-02 12:14:17 +12:00
Rob Bygrave 6f1bb5a9d7 Merge pull request #44 from hei1233212000/master
added a test case to show the issue that the user record does NOT rolback when there is an exception thrown
2013-08-01 16:45:15 -07:00
harry.chan 2f7b4e3a24 added a test case to show the issue that the user record does NOT
rolback when there is an exception thrown
2013-08-01 11:42:38 +08:00
Rob Bygrave 44d95dc06c Fix for Issue 43 - Support for JSON unmarshalling of @XmlRootElement
beans.
2013-07-29 23:24:30 +12:00
Robin Bygrave 6f579ba890 [maven-release-plugin] prepare for next development iteration 2013-07-12 21:10:49 +12:00
Tobias Wallura 39f5284a76 unit test for issue #37 2013-07-10 10:24:03 +02:00
Robin Bygrave 4ec62a5772 initial rework for new enhancement 2013-06-19 20:36:24 +12:00
Robin Bygrave eb1eafe98c WIP - Initial enhancement API change 2013-05-01 14:46:55 +12:00
1355 changed files with 124107 additions and 99435 deletions
+8
View File
@@ -6,4 +6,12 @@
target/
logs/
log/
/db
/mydb.db
# Intellij project files
*.iml
*.ipr
*.iws
.idea/
+14 -6
View File
@@ -1,14 +1,22 @@
avaje-ebeanorm-server
=====================
[![Stories in Ready](https://badge.waffle.io/ebean-orm/avaje-ebeanorm.png?label=ready&title=Ready)](https://waffle.io/ebean-orm/avaje-ebeanorm)
Ebean ORM
==============
Project Website
----------------
[http://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
The internal server implementation of EbeanORM API
Maven Dependency
----------------
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.2.1</version>
<version>4.7.1</version>
</dependency>
[Maven central - avaje-ebeanorm](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebeanorm%22%20AND%20a%3A%22avaje-ebeanorm%22 "maven central ebeanorm")
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cavaje-ebeanorm "maven central ebeanorm")
+124 -30
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>3.2.2</version>
<version>4.7.3</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -48,7 +48,28 @@
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<!-- provided scope for JsonNode support -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
<scope>provided</scope>
</dependency>
<!-- Jackson core used internally by Ebean -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
@@ -56,6 +77,7 @@
<scope>provided</scope>
</dependency>
<!-- provided scope to read validation annotations Size etc -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
@@ -77,13 +99,21 @@
<scope>provided</scope>
</dependency>
<!-- Provided scope for Postgres JSON/JSONB support -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-agentloader</artifactId>
@@ -94,14 +124,14 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>3.2.1</version>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.153</version>
<version>1.4.182</version>
<scope>test</scope>
</dependency>
@@ -112,13 +142,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.4-701.jdbc4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
@@ -129,14 +152,28 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.15</version>
<version>5.1.27</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-integration</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
@@ -147,29 +184,34 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Enhance the meta beans -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>3.2.1</version>
<version>4.5.1</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>
@@ -197,11 +239,63 @@
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
<systemProperties>
<property>
<!-- transfer datasource.default parameter -->
<name>datasource.default</name>
<value>${datasource.default}</value>
</property>
</systemProperties>
</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>
<source>1.8</source>
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
<docletArtifact>
<groupId>org.avaje</groupId>
<artifactId>pygments-doclet</artifactId>
<version>1.0.0</version>
</docletArtifact>
<additionalparam>
-Xdoclint:none
-include-basedir ${project.basedir}
-attributes "idseparator=-; project_name=${project.name}; \
project_version=${project.version}; \
project_desc=${project.description}"
</additionalparam>
<linksource>true</linksource>
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -8,28 +8,28 @@ public interface AdminAutofetch {
/**
* Return true if profiling is enabled.
*/
public boolean isProfiling();
boolean isProfiling();
/**
* Set to true to enable profiling.
*/
public void setProfiling(boolean enable);
void setProfiling(boolean enable);
/**
* Return true if autoFetch automatic query tuning is enabled.
*/
public boolean isQueryTuning();
boolean isQueryTuning();
/**
* Set to true to enable autoFetch automatic query tuning.
*/
public void setQueryTuning(boolean enable);
void setQueryTuning(boolean enable);
/**
* Returns the rate which profiling is collected. This is an int between 0 and
* 100.
*/
public double getProfilingRate();
double getProfilingRate();
/**
* Set the rate at which profiling is collected after the base.
@@ -37,13 +37,13 @@ public interface AdminAutofetch {
* @param rate
* a int between 0 and 100.
*/
public void setProfilingRate(double rate);
void setProfilingRate(double rate);
/**
* Return the number of queries profiled after which profiling is collected at
* a percentage rate.
*/
public int getProfilingBase();
int getProfilingBase();
/**
* Set a base number of queries to profile per query point.
@@ -52,7 +52,7 @@ public interface AdminAutofetch {
* the Profiling Percentage rate.
* </p>
*/
public void setProfilingBase(int profilingBase);
void setProfilingBase(int profilingBase);
/**
* Return the minimum number of queries profiled before autoFetch will start
@@ -62,7 +62,7 @@ public interface AdminAutofetch {
* profiling information is collected.
* </p>
*/
public int getProfilingMin();
int getProfilingMin();
/**
* Set the minimum number of queries profiled per query point before autoFetch
@@ -72,13 +72,13 @@ public interface AdminAutofetch {
* autoFetch starts tuning the query.
* </p>
*/
public void setProfilingMin(int autoFetchMinThreshold);
void setProfilingMin(int autoFetchMinThreshold);
/**
* Fire a garbage collection (hint to the JVM). Assuming garbage collection
* fires this will gather the usage profiling information.
*/
public String collectUsageViaGC();
String collectUsageViaGC();
/**
* This will take the current profiling information and update the "tuned
@@ -89,7 +89,7 @@ public interface AdminAutofetch {
*
* @return a summary of the updates that occurred
*/
public String updateTunedQueryInfo();
String updateTunedQueryInfo();
/**
* Clear all the tuned query info.
@@ -99,7 +99,7 @@ public interface AdminAutofetch {
*
* @return the amount of tuned query information cleared.
*/
public int clearTunedQueryInfo();
int clearTunedQueryInfo();
/**
* Clear all the profiling information.
@@ -112,26 +112,26 @@ public interface AdminAutofetch {
*
* @return the amount of profiled information cleared.
*/
public int clearProfilingInfo();
int clearProfilingInfo();
/**
* Clear the query execution statistics.
*/
public void clearQueryStatistics();
void clearQueryStatistics();
/**
* Return the number of queries tuned by AutoFetch.
*/
public int getTotalTunedQueryCount();
int getTotalTunedQueryCount();
/**
* Return the size of the TuneQuery map.
*/
public int getTotalTunedQuerySize();
int getTotalTunedQuerySize();
/**
* Return the size of the profile map.
*/
public int getTotalProfileSize();
int getTotalProfileSize();
}
@@ -1,40 +1,40 @@
package com.avaje.ebean;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Background thread pool service for executing of tasks asynchronously.
* <p>
* This service is used internally by Ebean for executing background tasks such
* as the {@link Query#findFutureList()} and also for executing background tasks
* periodically.
* </p>
* <p>
* This service has been made available so you can use it for your application
* code if you want. It can be useful for some server caching implementations
* (background population and trimming of the cache etc).
* </p>
*
* @author rbygrave
*/
public interface BackgroundExecutor {
/**
* Execute a task in the background.
*/
public void execute(Runnable r);
/**
* Execute a task periodically with a fixed delay between each execution.
* <p>
* For example, execute a runnable every minute.
* </p>
* <p>
* The delay is the time between executions no matter how long the task took.
* That is, this method has the same behaviour characteristics as
* {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}
* </p>
*/
public void executePeriodically(Runnable r, long delay, TimeUnit unit);
}
package com.avaje.ebean;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Background thread pool service for executing of tasks asynchronously.
* <p>
* This service is used internally by Ebean for executing background tasks such
* as the {@link Query#findFutureList()} and also for executing background tasks
* periodically.
* </p>
* <p>
* This service has been made available so you can use it for your application
* code if you want. It can be useful for some server caching implementations
* (background population and trimming of the cache etc).
* </p>
*
* @author rbygrave
*/
public interface BackgroundExecutor {
/**
* Execute a task in the background.
*/
void execute(Runnable r);
/**
* Execute a task periodically with a fixed delay between each execution.
* <p>
* For example, execute a runnable every minute.
* </p>
* <p>
* The delay is the time between executions no matter how long the task took.
* That is, this method has the same behaviour characteristics as
* {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}
* </p>
*/
void executePeriodically(Runnable r, long delay, TimeUnit unit);
}
+85 -93
View File
@@ -1,94 +1,86 @@
package com.avaje.ebean;
import java.beans.PropertyChangeListener;
import java.util.Set;
/**
* Provides access to the internal state of an entity bean.
*/
public interface BeanState {
/**
* Return true if this is a lazy loading reference bean.
* <p>
* If so the this bean only holds the Id property and will invoke lazy loading
* if any other property is get or set.
* </p>
*/
public boolean isReference();
/**
* Return true if the bean is new (and not yet saved).
*/
public boolean isNew();
/**
* Return true if the bean is new or dirty (and probably needs to be saved).
*/
public boolean isNewOrDirty();
/**
* Return true if the bean has been changed but not yet saved.
*/
public boolean isDirty();
/**
* For partially populated beans returns the properties that are loaded on the
* bean.
* <p>
* Accessing another property will cause lazy loading to occur.
* </p>
*/
public Set<String> getLoadedProps();
/**
* Return the set of changed properties.
*/
public Set<String> getChangedProps();
/**
* Return true if the bean is readOnly.
* <p>
* If a setter is called on a readOnly bean it will throw an exception.
* </p>
*/
public boolean isReadOnly();
/**
* Set the readOnly status for the bean.
*/
public void setReadOnly(boolean readOnly);
/**
* Add a propertyChangeListener.
*/
public void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a propertyChangeListener.
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Advanced - Used to programmatically build a reference object.
* <p>
* You can create a new EntityBean (
* {@link EbeanServer#createEntityBean(Class)}, set its Id property and then
* call this setReference() method.
* </p>
*/
public void setReference();
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
* {@link EbeanServer#createEntityBean(Class)}, then populate its properties
* and then call this method specifying which properties where loaded or null
* for a fully loaded entity bean.
*
* @param loadedProperties
* the properties that where loaded or null for a fully loaded entity
* bean.
*/
public void setLoaded(Set<String> loadedProperties);
package com.avaje.ebean;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.Set;
/**
* Provides access to the internal state of an entity bean.
*/
public interface BeanState {
/**
* Return true if this is a lazy loading reference bean.
* <p>
* If so the this bean only holds the Id property and will invoke lazy loading
* if any other property is get or set.
* </p>
*/
boolean isReference();
/**
* Return true if the bean is new (and not yet saved).
*/
boolean isNew();
/**
* Return true if the bean is new or dirty (and probably needs to be saved).
*/
boolean isNewOrDirty();
/**
* Return true if the bean has been changed but not yet saved.
*/
boolean isDirty();
/**
* For partially populated beans returns the properties that are loaded on the
* bean.
* <p>
* Accessing another property will cause lazy loading to occur.
* </p>
*/
Set<String> getLoadedProps();
/**
* Return the set of changed properties.
*/
Set<String> getChangedProps();
/**
* Return a map of the updated properties and their new and old values.
*/
Map<String,ValuePair> getDirtyValues();
/**
* Return true if the bean is readOnly.
* <p>
* If a setter is called on a readOnly bean it will throw an exception.
* </p>
*/
boolean isReadOnly();
/**
* Set the readOnly status for the bean.
*/
void setReadOnly(boolean readOnly);
/**
* Add a propertyChangeListener.
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a propertyChangeListener.
*/
void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
* {@link EbeanServer#createEntityBean(Class)}, then populate its properties
* and then call this method specifying which properties where loaded or null
* for a fully loaded entity bean.
*/
void setLoaded();
}
+11 -12
View File
@@ -85,17 +85,17 @@ public interface CallableSql {
/**
* Set the label that is put in the transaction log.
*/
public CallableSql setLabel(String label);
CallableSql setLabel(String label);
/**
* Return the statement execution timeout.
*/
public int getTimeout();
int getTimeout();
/**
* Return the callable sql.
*/
public String getSql();
String getSql();
/**
* Set the statement execution timeout. Zero implies unlimited time.
@@ -103,12 +103,12 @@ public interface CallableSql {
* This is set to the underlying CallableStatement.
* </p>
*/
public CallableSql setTimeout(int secs);
CallableSql setTimeout(int secs);
/**
* Set the callable sql.
*/
public CallableSql setSql(String sql);
CallableSql setSql(String sql);
/**
* Bind a parameter that is bound as a IN parameter.
@@ -125,7 +125,7 @@ public interface CallableSql {
* @param value
* the value of the parameter.
*/
public CallableSql bind(int position, Object value);
CallableSql bind(int position, Object value);
/**
* Bind a positioned parameter (same as bind method).
@@ -135,7 +135,7 @@ public interface CallableSql {
* @param value
* the value of the parameter.
*/
public CallableSql setParameter(int position, Object value);
CallableSql setParameter(int position, Object value);
/**
* Register an OUT parameter.
@@ -153,7 +153,7 @@ public interface CallableSql {
* @param type
* the jdbc type of the OUT parameter that will be read.
*/
public CallableSql registerOut(int position, int type);
CallableSql registerOut(int position, int type);
/**
* Return an OUT parameter value.
@@ -165,7 +165,7 @@ public interface CallableSql {
* in batch mode you effectively can't use this method.
* </p>
*/
public Object getObject(int position);
Object getObject(int position);
/**
*
@@ -173,7 +173,7 @@ public interface CallableSql {
* stored procedure calls. This would be the case when ResultSets are returned
* etc.
*/
public boolean executeOverride(CallableStatement cstmt) throws SQLException;
boolean executeOverride(CallableStatement cstmt) throws SQLException;
/**
* Add table modification information to the TransactionEvent.
@@ -188,7 +188,6 @@ public interface CallableSql {
* delete.
* </p>
*/
public CallableSql addModification(String tableName, boolean inserts, boolean updates,
boolean deletes);
CallableSql addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
}
@@ -1,7 +1,6 @@
package com.avaje.ebean;
import java.util.ArrayList;
import java.util.Arrays;
import javax.persistence.PersistenceException;
@@ -57,6 +56,9 @@ final class DRawSqlColumnsParser {
split = tmp.toArray(new String[tmp.size()]);
}
if (split.length == 0) {
throw new PersistenceException("Huh? Not expecting length=0 when parsing column " + colInfo);
}
if (split.length == 1) {
// default to column the same name as the property
return new ColumnMapping.Column(indexPos++, split[0], null);
@@ -64,17 +66,18 @@ final class DRawSqlColumnsParser {
if (split.length == 2) {
return new ColumnMapping.Column(indexPos++, split[0], split[1]);
}
if (split.length == 3) {
if (!split[1].equalsIgnoreCase("as")) {
String msg = "Expecting AS keyword parsing column " + colInfo;
throw new PersistenceException(msg);
}
return new ColumnMapping.Column(indexPos++, split[0], split[2]);
// Ok, we now expect/require the AS keyword and it should be the
// second to last word in the colInfo content
if (!split[split.length - 2].equalsIgnoreCase("as")) {
throw new PersistenceException("Expecting AS keyword as second to last word when parsing column " + colInfo);
}
String msg = "Expecting Max 3 words parsing column " + colInfo + ". Got "
+ Arrays.toString(split);
throw new PersistenceException(msg);
// build back the 'column formula' that precedes the AS keyword
StringBuilder sb = new StringBuilder();
sb.append(split[0]);
for (int i = 1; i < split.length-2; i++) {
sb.append(" ").append(split[i]);
}
return new ColumnMapping.Column(indexPos++, sb.toString(), split[split.length - 1]);
}
private int nextComma() {
@@ -16,8 +16,6 @@ class DRawSqlParser {
public static final String $_WHERE = "${where}";
private static final String ORDER_BY = "order by";
private final SimpleTextParser textParser;
private String sql;
@@ -35,6 +33,7 @@ class DRawSqlParser {
private int groupByPos = -1;
private int havingPos = -1;
private int orderByPos = -1;
private int orderByStmtPos = -1;
private boolean whereExprAnd;
private int whereExprPos = -1;
@@ -66,12 +65,12 @@ class DRawSqlParser {
String preFrom = removeWhitespace(findPreFromSql());
String preWhere = removeWhitespace(findPreWhereSql());
String preHaving = removeWhitespace(findPreHavingSql());
String orderByPrefix = findOrderByPrefixSql();
String orderBySql = findOrderBySql();
preFrom = trimSelectKeyword(preFrom);
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd,
orderBySql, (distinctPos > -1));
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
}
/**
@@ -142,12 +141,12 @@ class DRawSqlParser {
return preWhereExprSql;
}
private String findOrderByPrefixSql() {
return (orderByPos < 1) ? null : sql.substring(orderByPos, orderByStmtPos);
}
private String findOrderBySql() {
if (orderByPos > -1) {
int pos = orderByPos + ORDER_BY.length();
return sql.substring(pos).trim();
}
return null;
return (orderByStmtPos < 1) ? null : sql.substring(orderByStmtPos).trim();
}
private String findPreHavingSql() {
@@ -226,6 +225,11 @@ class DRawSqlParser {
}
orderByPos = textParser.findWordLower("order", startOrderBy);
if (orderByPos > 1) {
// there might be keywords like siblings in between the order
// and by so search for the by keyword explicitly
orderByStmtPos = 2 + textParser.findWordLower("by", orderByPos);
}
}
private int findWhereExprPosition() {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,98 +1,148 @@
package com.avaje.ebean;
import javax.persistence.PersistenceException;
import com.avaje.ebean.common.BootupEbeanManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates EbeanServer instances.
* <p>
* This uses either a ServerConfig or properties in the ebean.properties file to
* configure and create a EbeanServer instance.
* </p>
* <p>
* The EbeanServer instance can either be registered with the Ebean singleton or
* not. The Ebean singleton effectively holds a map of EbeanServers by a name.
* If the EbeanServer is registered with the Ebean singleton you can retrieve it
* later via {@link Ebean#getServer(String)}.
* </p>
* <p>
* One EbeanServer can be nominated as the 'default/primary' EbeanServer. Many
* 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();
/**
* Create using ebean.properties to configure the server.
*/
public static EbeanServer create(String name) {
EbeanServer server = serverFactory.createServer(name);
return server;
}
/**
* Create using the ServerConfig object to configure the server.
*/
public static EbeanServer create(ServerConfig config) {
if (config.getName() == null) {
throw new PersistenceException("The name is null (it is required)");
}
EbeanServer server = serverFactory.createServer(config);
if (config.isDefaultServer()) {
GlobalProperties.setSkipPrimaryServer(true);
}
if (config.isRegister()) {
Ebean.register(server, config.isDefaultServer());
}
return server;
}
private static BootupEbeanManager createServerFactory() {
// String d___ =
// com.avaje.ebean.server.core.DefaultServerFactory.class.getName();
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
String implClassName = GlobalProperties.get("ebean.serverfactory", dflt);
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);
}
}
}
package com.avaje.ebean;
import com.avaje.ebean.common.SpiContainer;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import javax.persistence.PersistenceException;
import java.lang.reflect.Constructor;
import java.util.Properties;
/**
* Creates EbeanServer instances.
* <p>
* This uses either a ServerConfig or properties in the ebean.properties file to
* configure and create a EbeanServer instance.
* </p>
* <p>
* The EbeanServer instance can either be registered with the Ebean singleton or
* not. The Ebean singleton effectively holds a map of EbeanServers by a name.
* If the EbeanServer is registered with the Ebean singleton you can retrieve it
* later via {@link Ebean#getServer(String)}.
* </p>
* <p>
* One EbeanServer can be nominated as the 'default/primary' EbeanServer. Many
* methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
* convenient way of using the 'default/primary' EbeanServer.
* </p>
*/
public class EbeanServerFactory {
private static final String DEFAULT_CONTAINER = "com.avaje.ebeaninternal.server.core.DefaultContainer";
private static SpiContainer container;
/**
* Initialise the container with clustering configuration.
*
* Call this prior to creating any EbeanServer instances or alternatively set the
* ContainerConfig on the ServerConfig when creating the first EbeanServer instance.
*/
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
getContainer(containerConfig);
}
/**
* Create using ebean.properties to configure the server.
*/
public static synchronized EbeanServer create(String name) {
// construct based on loading properties files
// and if invoked by Ebean then it handles registration
SpiContainer serverFactory = getContainer(null);
return serverFactory.createServer(name);
}
/**
* Create using the ServerConfig object to configure the server.
*/
public static synchronized EbeanServer create(ServerConfig config) {
if (config.getName() == null) {
throw new PersistenceException("The name is null (it is required)");
}
EbeanServer server = createInternal(config);
if (config.isDefaultServer()) {
PrimaryServer.setSkip(true);
}
if (config.isRegister()) {
Ebean.register(server, config.isDefaultServer());
}
return server;
}
/**
* Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
*/
public static synchronized EbeanServer createWithContextClassLoader(ServerConfig config, ClassLoader classLoader) {
ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
try {
return EbeanServerFactory.create(config);
} finally {
// set the currentContextLoader back
Thread.currentThread().setContextClassLoader(currentContextLoader);
}
}
/**
* Shutdown gracefully all EbeanServers cleaning up any resources as required.
* <p>
* This is typically invoked via JVM shutdown hook and not explicitly called.
* </p>
*/
public static synchronized void shutdown() {
ShutdownManager.shutdown();
}
private static EbeanServer createInternal(ServerConfig config) {
return getContainer(config.getContainerConfig()).createServer(config);
}
/**
* Get the EbeanContainer initialising it if necessary.
*
* @param containerConfig the configuration controlling clustering communication
*/
private static SpiContainer getContainer(ContainerConfig containerConfig) {
// thread safe in that all calling methods are synchronized
if (container != null) {
return container;
}
if (containerConfig == null) {
// effectively load configuration from ebean.properties
Properties properties = PrimaryServer.getProperties();
containerConfig = new ContainerConfig();
containerConfig.loadFromProperties(properties);
}
container = createContainer(containerConfig);
return container;
}
/**
* Create the container instance using the configuration.
*/
private static SpiContainer createContainer(ContainerConfig containerConfig) {
String implClassName = System.getProperty("ebean.container", DEFAULT_CONTAINER);
try {
Class<?> cls = Class.forName(implClassName);
Constructor<?> constructor = cls.getConstructor(ContainerConfig.class);
return (SpiContainer) constructor.newInstance(containerConfig);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
@@ -1,93 +1,93 @@
package com.avaje.ebean;
/**
* Query by Example expression.
* <p>
* Pass in an example entity and for each non-null scalar properties an
* expression is added.
* </p>
* <p>
* By Default this case sensitive, will ignore numeric zero values and will use
* a Like for string values (you must put in your own wildcards).
* </p>
* <p>
* To get control over the options you can create an ExampleExpression and set
* those options such as case insensitive etc.
* </p>
*
* <pre class="code">
* // create an example bean and set the properties
* // with the query parameters you want
* Customer example = new Customer();
* example.setName(&quot;Rob%&quot;);
* example.setNotes(&quot;%something%&quot;);
*
* List&lt;Customer&gt; list =
* Ebean.find(Customer.class)
* .where()
* // pass the bean into the where() clause
* .exampleLike(example)
* // you can add other expressions to the same query
* .gt(&quot;id&quot;, 2)
* .findList();
*
* </pre>
*
* Similarly you can create an ExampleExpression
*
* <pre>
* Customer example = new Customer();
* example.setName(&quot;Rob%&quot;);
* example.setNotes(&quot;%something%&quot;);
*
* // create a ExampleExpression with more control
* ExampleExpression qbe = new ExampleExpression(example, true, LikeType.EQUAL_TO)
* .includeZeros();
*
* List&lt;Customer&gt; list =
* Ebean.find(Customer.class)
* .where()
* .add(qbe)
* .findList();
* </pre>
*
* @author Rob Bygrave
*/
public interface ExampleExpression extends Expression {
/**
* By calling this method zero value properties are going to be included in
* the expression.
* <p>
* By default numeric zero values are excluded as they can result from
* primitive int and long types.
* </p>
*/
public ExampleExpression includeZeros();
/**
* Set case insensitive to true.
*/
public ExampleExpression caseInsensitive();
/**
* Use startsWith expression for string properties.
*/
public ExampleExpression useStartsWith();
/**
* Use contains expression for string properties.
*/
public ExampleExpression useContains();
/**
* Use endsWith expression for string properties.
*/
public ExampleExpression useEndsWith();
/**
* Use equal to expression for string properties.
*/
public ExampleExpression useEqualTo();
package com.avaje.ebean;
/**
* Query by Example expression.
* <p>
* Pass in an example entity and for each non-null scalar properties an
* expression is added.
* </p>
* <p>
* By Default this case sensitive, will ignore numeric zero values and will use
* a Like for string values (you must put in your own wildcards).
* </p>
* <p>
* To get control over the options you can create an ExampleExpression and set
* those options such as case insensitive etc.
* </p>
*
* <pre class="code">
* // create an example bean and set the properties
* // with the query parameters you want
* Customer example = new Customer();
* example.setName(&quot;Rob%&quot;);
* example.setNotes(&quot;%something%&quot;);
*
* List&lt;Customer&gt; list =
* Ebean.find(Customer.class)
* .where()
* // pass the bean into the where() clause
* .exampleLike(example)
* // you can add other expressions to the same query
* .gt(&quot;id&quot;, 2)
* .findList();
*
* </pre>
*
* Similarly you can create an ExampleExpression
*
* <pre>
* Customer example = new Customer();
* example.setName(&quot;Rob%&quot;);
* example.setNotes(&quot;%something%&quot;);
*
* // create a ExampleExpression with more control
* ExampleExpression qbe = new ExampleExpression(example, true, LikeType.EQUAL_TO)
* .includeZeros();
*
* List&lt;Customer&gt; list =
* Ebean.find(Customer.class)
* .where()
* .add(qbe)
* .findList();
* </pre>
*
* @author Rob Bygrave
*/
public interface ExampleExpression extends Expression {
/**
* By calling this method zero value properties are going to be included in
* the expression.
* <p>
* By default numeric zero values are excluded as they can result from
* primitive int and long types.
* </p>
*/
ExampleExpression includeZeros();
/**
* Set case insensitive to true.
*/
ExampleExpression caseInsensitive();
/**
* Use startsWith expression for string properties.
*/
ExampleExpression useStartsWith();
/**
* Use contains expression for string properties.
*/
ExampleExpression useContains();
/**
* Use endsWith expression for string properties.
*/
ExampleExpression useEndsWith();
/**
* Use equal to expression for string properties.
*/
ExampleExpression useEqualTo();
}
+322 -314
View File
@@ -1,314 +1,322 @@
package com.avaje.ebean;
import java.util.Collection;
import java.util.Map;
/**
* Expression factory for creating standard expressions for WHERE and HAVING
* clauses.
* <p>
* Generally you will only need to use this object for creating OR, JUNCTION or
* CONJUNCTION expressions. To create simple expressions you will most likely
* just use the methods on the ExpressionList object that is returned via
* {@link Query#where()}.
* </p>
* <p>
* This provides a convenient way to create expressions for the 'Default'
* server. It is actually a short cut for using the ExpressionFactory of the
* 'default' EbeanServer.
* <p>
* See also {@link Ebean#getExpressionFactory()}
* </p>
* <p>
* Creates standard common expressions for using in a Query Where or Having
* clause.
* </p>
*
* <pre class="code">
* // Example: Using an Expr.or() method
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
* query.where(
* Expr.or(Expr.eq(&quot;status&quot;, Order.NEW),
* Expr.gt(&quot;orderDate&quot;, lastWeek));
*
* List&lt;Order&gt; list = query.findList();
* ...
* </pre>
*
* @see Query#where()
* @author Rob Bygrave
*/
public class Expr {
private Expr() {
}
/**
* Equal To - property equal to the given value.
*/
public static Expression eq(String propertyName, Object value) {
return Ebean.getExpressionFactory().eq(propertyName, value);
}
/**
* Not Equal To - property not equal to the given value.
*/
public static Expression ne(String propertyName, Object value) {
return Ebean.getExpressionFactory().ne(propertyName, value);
}
/**
* Case Insensitive Equal To - property equal to the given value (typically
* using a lower() function to make it case insensitive).
*/
public static Expression ieq(String propertyName, String value) {
return Ebean.getExpressionFactory().ieq(propertyName, value);
}
/**
* Between - property between the two given values.
*/
public static Expression between(String propertyName, Object value1, Object value2) {
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
}
/**
* Greater Than - property greater than the given value.
*/
public static Expression gt(String propertyName, Object value) {
return Ebean.getExpressionFactory().gt(propertyName, value);
}
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
*/
public static Expression ge(String propertyName, Object value) {
return Ebean.getExpressionFactory().ge(propertyName, value);
}
/**
* Less Than - property less than the given value.
*/
public static Expression lt(String propertyName, Object value) {
return Ebean.getExpressionFactory().lt(propertyName, value);
}
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
public static Expression le(String propertyName, Object value) {
return Ebean.getExpressionFactory().le(propertyName, value);
}
/**
* Is Null - property is null.
*/
public static Expression isNull(String propertyName) {
return Ebean.getExpressionFactory().isNull(propertyName);
}
/**
* Is Not Null - property is not null.
*/
public static Expression isNotNull(String propertyName) {
return Ebean.getExpressionFactory().isNotNull(propertyName);
}
/**
* Case insensitive {@link #exampleLike(Object)}
*/
public static ExampleExpression iexampleLike(Object example) {
return Ebean.getExpressionFactory().iexampleLike(example);
}
/**
* Create the query by Example expression which is case sensitive and using
* LikeType.RAW (you need to add you own wildcards % and _).
*/
public static ExampleExpression exampleLike(Object example) {
return Ebean.getExpressionFactory().exampleLike(example);
}
/**
* Create the query by Example expression specifying more options.
*/
public static ExampleExpression exampleLike(Object example, boolean caseInsensitive,
LikeType likeType) {
return Ebean.getExpressionFactory().exampleLike(example, caseInsensitive, likeType);
}
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
*/
public static Expression like(String propertyName, String value) {
return Ebean.getExpressionFactory().like(propertyName, value);
}
/**
* Case insensitive Like - property like value where the value contains the
* SQL wild card characters % (percentage) and _ (underscore). Typically uses
* a lower() function to make the expression case insensitive.
*/
public static Expression ilike(String propertyName, String value) {
return Ebean.getExpressionFactory().ilike(propertyName, value);
}
/**
* Starts With - property like value%.
*/
public static Expression startsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().startsWith(propertyName, value);
}
/**
* Case insensitive Starts With - property like value%. Typically uses a
* lower() function to make the expression case insensitive.
*/
public static Expression istartsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().istartsWith(propertyName, value);
}
/**
* Ends With - property like %value.
*/
public static Expression endsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().endsWith(propertyName, value);
}
/**
* Case insensitive Ends With - property like %value. Typically uses a lower()
* function to make the expression case insensitive.
*/
public static Expression iendsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().iendsWith(propertyName, value);
}
/**
* Contains - property like %value%.
*/
public static Expression contains(String propertyName, String value) {
return Ebean.getExpressionFactory().contains(propertyName, value);
}
/**
* Case insensitive Contains - property like %value%. Typically uses a lower()
* function to make the expression case insensitive.
*/
public static Expression icontains(String propertyName, String value) {
return Ebean.getExpressionFactory().icontains(propertyName, value);
}
/**
* In - property has a value in the array of values.
*/
public static Expression in(String propertyName, Object[] values) {
return Ebean.getExpressionFactory().in(propertyName, values);
}
/**
* In - using a subQuery.
*/
public static Expression in(String propertyName, Query<?> subQuery) {
return Ebean.getExpressionFactory().in(propertyName, subQuery);
}
/**
* In - property has a value in the collection of values.
*/
public static Expression in(String propertyName, Collection<?> values) {
return Ebean.getExpressionFactory().in(propertyName, values);
}
/**
* Id Equal to - ID property is equal to the value.
*/
public static Expression idEq(Object value) {
return Ebean.getExpressionFactory().idEq(value);
}
/**
* All Equal - Map containing property names and their values.
* <p>
* Expression where all the property names in the map are equal to the
* corresponding value.
* </p>
*
* @param propertyMap
* a map keyed by property names.
*/
public static Expression allEq(Map<String, Object> propertyMap) {
return Ebean.getExpressionFactory().allEq(propertyMap);
}
/**
* Add raw expression with a single parameter.
* <p>
* The raw expression should contain a single ? at the location of the
* parameter.
* </p>
*/
public static Expression raw(String raw, Object value) {
return Ebean.getExpressionFactory().raw(raw, value);
}
/**
* Add raw expression with an array of parameters.
* <p>
* The raw expression should contain the same number of ? as there are
* parameters.
* </p>
*/
public static Expression raw(String raw, Object[] values) {
return Ebean.getExpressionFactory().raw(raw, values);
}
/**
* Add raw expression with no parameters.
*/
public static Expression raw(String raw) {
return Ebean.getExpressionFactory().raw(raw);
}
/**
* And - join two expressions with a logical and.
*/
public static Expression and(Expression expOne, Expression expTwo) {
return Ebean.getExpressionFactory().and(expOne, expTwo);
}
/**
* Or - join two expressions with a logical or.
*/
public static Expression or(Expression expOne, Expression expTwo) {
return Ebean.getExpressionFactory().or(expOne, expTwo);
}
/**
* Negate the expression (prefix it with NOT).
*/
public static Expression not(Expression exp) {
return Ebean.getExpressionFactory().not(exp);
}
/**
* Return a list of expressions that will be joined by AND's.
*/
public static <T> Junction<T> conjunction(Query<T> query) {
return Ebean.getExpressionFactory().conjunction(query);
}
/**
* Return a list of expressions that will be joined by OR's.
*/
public static <T> Junction<T> disjunction(Query<T> query) {
return Ebean.getExpressionFactory().disjunction(query);
}
}
package com.avaje.ebean;
import java.util.Collection;
import java.util.Map;
/**
* Expression factory for creating standard expressions for WHERE and HAVING
* clauses.
* <p>
* Generally you will only need to use this object for creating OR, JUNCTION or
* CONJUNCTION expressions. To create simple expressions you will most likely
* just use the methods on the ExpressionList object that is returned via
* {@link Query#where()}.
* </p>
* <p>
* This provides a convenient way to create expressions for the 'Default'
* server. It is actually a short cut for using the ExpressionFactory of the
* 'default' EbeanServer.
* <p>
* See also {@link Ebean#getExpressionFactory()}
* </p>
* <p>
* Creates standard common expressions for using in a Query Where or Having
* clause.
* </p>
*
* <pre class="code">
* // Example: Using an Expr.or() method
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
* query.where(
* Expr.or(Expr.eq(&quot;status&quot;, Order.NEW),
* Expr.gt(&quot;orderDate&quot;, lastWeek));
*
* List&lt;Order&gt; list = query.findList();
* ...
* </pre>
*
* @see Query#where()
* @author Rob Bygrave
*/
public class Expr {
private Expr() {
}
/**
* Equal To - property equal to the given value.
*/
public static Expression eq(String propertyName, Object value) {
return Ebean.getExpressionFactory().eq(propertyName, value);
}
/**
* Not Equal To - property not equal to the given value.
*/
public static Expression ne(String propertyName, Object value) {
return Ebean.getExpressionFactory().ne(propertyName, value);
}
/**
* Case Insensitive Equal To - property equal to the given value (typically
* using a lower() function to make it case insensitive).
*/
public static Expression ieq(String propertyName, String value) {
return Ebean.getExpressionFactory().ieq(propertyName, value);
}
/**
* Between - property between the two given values.
*/
public static Expression between(String propertyName, Object value1, Object value2) {
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
}
/**
* Between - value between two given properties.
*/
public static Expression between(String lowProperty, String highProperty, Object value) {
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
}
/**
* Greater Than - property greater than the given value.
*/
public static Expression gt(String propertyName, Object value) {
return Ebean.getExpressionFactory().gt(propertyName, value);
}
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
*/
public static Expression ge(String propertyName, Object value) {
return Ebean.getExpressionFactory().ge(propertyName, value);
}
/**
* Less Than - property less than the given value.
*/
public static Expression lt(String propertyName, Object value) {
return Ebean.getExpressionFactory().lt(propertyName, value);
}
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
public static Expression le(String propertyName, Object value) {
return Ebean.getExpressionFactory().le(propertyName, value);
}
/**
* Is Null - property is null.
*/
public static Expression isNull(String propertyName) {
return Ebean.getExpressionFactory().isNull(propertyName);
}
/**
* Is Not Null - property is not null.
*/
public static Expression isNotNull(String propertyName) {
return Ebean.getExpressionFactory().isNotNull(propertyName);
}
/**
* Case insensitive {@link #exampleLike(Object)}
*/
public static ExampleExpression iexampleLike(Object example) {
return Ebean.getExpressionFactory().iexampleLike(example);
}
/**
* Create the query by Example expression which is case sensitive and using
* LikeType.RAW (you need to add you own wildcards % and _).
*/
public static ExampleExpression exampleLike(Object example) {
return Ebean.getExpressionFactory().exampleLike(example);
}
/**
* Create the query by Example expression specifying more options.
*/
public static ExampleExpression exampleLike(Object example, boolean caseInsensitive,
LikeType likeType) {
return Ebean.getExpressionFactory().exampleLike(example, caseInsensitive, likeType);
}
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
*/
public static Expression like(String propertyName, String value) {
return Ebean.getExpressionFactory().like(propertyName, value);
}
/**
* Case insensitive Like - property like value where the value contains the
* SQL wild card characters % (percentage) and _ (underscore). Typically uses
* a lower() function to make the expression case insensitive.
*/
public static Expression ilike(String propertyName, String value) {
return Ebean.getExpressionFactory().ilike(propertyName, value);
}
/**
* Starts With - property like value%.
*/
public static Expression startsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().startsWith(propertyName, value);
}
/**
* Case insensitive Starts With - property like value%. Typically uses a
* lower() function to make the expression case insensitive.
*/
public static Expression istartsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().istartsWith(propertyName, value);
}
/**
* Ends With - property like %value.
*/
public static Expression endsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().endsWith(propertyName, value);
}
/**
* Case insensitive Ends With - property like %value. Typically uses a lower()
* function to make the expression case insensitive.
*/
public static Expression iendsWith(String propertyName, String value) {
return Ebean.getExpressionFactory().iendsWith(propertyName, value);
}
/**
* Contains - property like %value%.
*/
public static Expression contains(String propertyName, String value) {
return Ebean.getExpressionFactory().contains(propertyName, value);
}
/**
* Case insensitive Contains - property like %value%. Typically uses a lower()
* function to make the expression case insensitive.
*/
public static Expression icontains(String propertyName, String value) {
return Ebean.getExpressionFactory().icontains(propertyName, value);
}
/**
* In - property has a value in the array of values.
*/
public static Expression in(String propertyName, Object[] values) {
return Ebean.getExpressionFactory().in(propertyName, values);
}
/**
* In - using a subQuery.
*/
public static Expression in(String propertyName, Query<?> subQuery) {
return Ebean.getExpressionFactory().in(propertyName, subQuery);
}
/**
* In - property has a value in the collection of values.
*/
public static Expression in(String propertyName, Collection<?> values) {
return Ebean.getExpressionFactory().in(propertyName, values);
}
/**
* Id Equal to - ID property is equal to the value.
*/
public static Expression idEq(Object value) {
return Ebean.getExpressionFactory().idEq(value);
}
/**
* All Equal - Map containing property names and their values.
* <p>
* Expression where all the property names in the map are equal to the
* corresponding value.
* </p>
*
* @param propertyMap
* a map keyed by property names.
*/
public static Expression allEq(Map<String, Object> propertyMap) {
return Ebean.getExpressionFactory().allEq(propertyMap);
}
/**
* Add raw expression with a single parameter.
* <p>
* The raw expression should contain a single ? at the location of the
* parameter.
* </p>
*/
public static Expression raw(String raw, Object value) {
return Ebean.getExpressionFactory().raw(raw, value);
}
/**
* Add raw expression with an array of parameters.
* <p>
* The raw expression should contain the same number of ? as there are
* parameters.
* </p>
*/
public static Expression raw(String raw, Object[] values) {
return Ebean.getExpressionFactory().raw(raw, values);
}
/**
* Add raw expression with no parameters.
*/
public static Expression raw(String raw) {
return Ebean.getExpressionFactory().raw(raw);
}
/**
* And - join two expressions with a logical and.
*/
public static Expression and(Expression expOne, Expression expTwo) {
return Ebean.getExpressionFactory().and(expOne, expTwo);
}
/**
* Or - join two expressions with a logical or.
*/
public static Expression or(Expression expOne, Expression expTwo) {
return Ebean.getExpressionFactory().or(expOne, expTwo);
}
/**
* Negate the expression (prefix it with NOT).
*/
public static Expression not(Expression exp) {
return Ebean.getExpressionFactory().not(exp);
}
/**
* Return a list of expressions that will be joined by AND's.
*/
public static <T> Junction<T> conjunction(Query<T> query) {
return Ebean.getExpressionFactory().conjunction(query);
}
/**
* Return a list of expressions that will be joined by OR's.
*/
public static <T> Junction<T> disjunction(Query<T> query) {
return Ebean.getExpressionFactory().disjunction(query);
}
}
+10 -10
View File
@@ -1,10 +1,10 @@
package com.avaje.ebean;
import java.io.Serializable;
/**
* An expression that is part of a WHERE or HAVING clause.
*/
public interface Expression extends Serializable {
}
package com.avaje.ebean;
import java.io.Serializable;
/**
* An expression that is part of a WHERE or HAVING clause.
*/
public interface Expression extends Serializable {
}
@@ -1,257 +1,262 @@
package com.avaje.ebean;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Expression factory for creating standard expressions.
* <p>
* Creates standard common expressions for using in a Query Where or Having
* clause.
* </p>
* <p>
* You will often not use this class directly but instead just add expressions
* via the methods on ExpressionList such as
* {@link ExpressionList#gt(String, Object)}.
* </p>
* <p>
* The ExpressionList is returned from {@link Query#where()}.
* </p>
*
* <pre class="code">
* // Example: fetch orders where status equals new or orderDate > lastWeek.
*
* Expression newOrLastWeek =
* Expr.or(Expr.eq(&quot;status&quot;, Order.Status.NEW),
* Expr.gt(&quot;orderDate&quot;, lastWeek));
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
* query.where().add(newOrLastWeek);
* List&lt;Order&gt; list = query.findList();
* ...
* </pre>
*
* @see Query#where()
*/
public interface ExpressionFactory {
/**
* Return the language for this expression factory.
*/
public String getLang();
/**
* Equal To - property equal to the given value.
*/
public Expression eq(String propertyName, Object value);
/**
* Not Equal To - property not equal to the given value.
*/
public Expression ne(String propertyName, Object value);
/**
* Case Insensitive Equal To - property equal to the given value (typically
* using a lower() function to make it case insensitive).
*/
public Expression ieq(String propertyName, String value);
/**
* Between - property between the two given values.
*/
public Expression between(String propertyName, Object value1, Object value2);
/**
* Between - value between two given properties.
*/
public Expression betweenProperties(String lowProperty, String highProperty, Object value);
/**
* Greater Than - property greater than the given value.
*/
public Expression gt(String propertyName, Object value);
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
*/
public Expression ge(String propertyName, Object value);
/**
* Less Than - property less than the given value.
*/
public Expression lt(String propertyName, Object value);
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
public Expression le(String propertyName, Object value);
/**
* Is Null - property is null.
*/
public Expression isNull(String propertyName);
/**
* Is Not Null - property is not null.
*/
public Expression isNotNull(String propertyName);
/**
* Case insensitive {@link #exampleLike(Object)}
*/
public ExampleExpression iexampleLike(Object example);
/**
* Create the query by Example expression which is case sensitive and using
* LikeType.RAW (you need to add you own wildcards % and _).
*/
public ExampleExpression exampleLike(Object example);
/**
* Create the query by Example expression specifying more options.
*/
public ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType);
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
*/
public Expression like(String propertyName, String value);
/**
* Case insensitive Like - property like value where the value contains the
* SQL wild card characters % (percentage) and _ (underscore). Typically uses
* a lower() function to make the expression case insensitive.
*/
public Expression ilike(String propertyName, String value);
/**
* Starts With - property like value%.
*/
public Expression startsWith(String propertyName, String value);
/**
* Case insensitive Starts With - property like value%. Typically uses a
* lower() function to make the expression case insensitive.
*/
public Expression istartsWith(String propertyName, String value);
/**
* Ends With - property like %value.
*/
public Expression endsWith(String propertyName, String value);
/**
* Case insensitive Ends With - property like %value. Typically uses a lower()
* function to make the expression case insensitive.
*/
public Expression iendsWith(String propertyName, String value);
/**
* Contains - property like %value%.
*/
public Expression contains(String propertyName, String value);
/**
* Case insensitive Contains - property like %value%. Typically uses a lower()
* function to make the expression case insensitive.
*/
public Expression icontains(String propertyName, String value);
/**
* In - property has a value in the array of values.
*/
public Expression in(String propertyName, Object[] values);
/**
* In - using a subQuery.
*/
public Expression in(String propertyName, Query<?> subQuery);
/**
* In - property has a value in the collection of values.
*/
public Expression in(String propertyName, Collection<?> values);
/**
* Id Equal to - ID property is equal to the value.
*/
public Expression idEq(Object value);
/**
* Id IN a list of Id values.
*/
public Expression idIn(List<?> idList);
/**
* All Equal - Map containing property names and their values.
* <p>
* Expression where all the property names in the map are equal to the
* corresponding value.
* </p>
*
* @param propertyMap
* a map keyed by property names.
*/
public Expression allEq(Map<String, Object> propertyMap);
/**
* Add raw expression with a single parameter.
* <p>
* The raw expression should contain a single ? at the location of the
* parameter.
* </p>
*/
public Expression raw(String raw, Object value);
/**
* Add raw expression with an array of parameters.
* <p>
* The raw expression should contain the same number of ? as there are
* parameters.
* </p>
*/
public Expression raw(String raw, Object[] values);
/**
* Add raw expression with no parameters.
*/
public Expression raw(String raw);
/**
* And - join two expressions with a logical and.
*/
public Expression and(Expression expOne, Expression expTwo);
/**
* Or - join two expressions with a logical or.
*/
public Expression or(Expression expOne, Expression expTwo);
/**
* Negate the expression (prefix it with NOT).
*/
public Expression not(Expression exp);
/**
* Return a list of expressions that will be joined by AND's.
*/
public <T> Junction<T> conjunction(Query<T> query);
/**
* Return a list of expressions that will be joined by OR's.
*/
public <T> Junction<T> disjunction(Query<T> query);
/**
* Return a list of expressions that will be joined by AND's.
*/
public <T> Junction<T> conjunction(Query<T> query, ExpressionList<T> parent);
/**
* Return a list of expressions that will be joined by OR's.
*/
public <T> Junction<T> disjunction(Query<T> query, ExpressionList<T> parent);
}
package com.avaje.ebean;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Expression factory for creating standard expressions.
* <p>
* Creates standard common expressions for using in a Query Where or Having
* clause.
* </p>
* <p>
* You will often not use this class directly but instead just add expressions
* via the methods on ExpressionList such as
* {@link ExpressionList#gt(String, Object)}.
* </p>
* <p>
* The ExpressionList is returned from {@link Query#where()}.
* </p>
*
* <pre class="code">
* // Example: fetch orders where status equals new or orderDate > lastWeek.
*
* Expression newOrLastWeek =
* Expr.or(Expr.eq(&quot;status&quot;, Order.Status.NEW),
* Expr.gt(&quot;orderDate&quot;, lastWeek));
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
* query.where().add(newOrLastWeek);
* List&lt;Order&gt; list = query.findList();
* ...
* </pre>
*
* @see Query#where()
*/
public interface ExpressionFactory {
/**
* Equal To - property equal to the given value.
*/
Expression eq(String propertyName, Object value);
/**
* Not Equal To - property not equal to the given value.
*/
Expression ne(String propertyName, Object value);
/**
* Case Insensitive Equal To - property equal to the given value (typically
* using a lower() function to make it case insensitive).
*/
Expression ieq(String propertyName, String value);
/**
* Between - property between the two given values.
*/
Expression between(String propertyName, Object value1, Object value2);
/**
* Between - value between two given properties.
*/
Expression betweenProperties(String lowProperty, String highProperty, Object value);
/**
* Greater Than - property greater than the given value.
*/
Expression gt(String propertyName, Object value);
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
*/
Expression ge(String propertyName, Object value);
/**
* Less Than - property less than the given value.
*/
Expression lt(String propertyName, Object value);
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
Expression le(String propertyName, Object value);
/**
* Is Null - property is null.
*/
Expression isNull(String propertyName);
/**
* Is Not Null - property is not null.
*/
Expression isNotNull(String propertyName);
/**
* Case insensitive {@link #exampleLike(Object)}
*/
ExampleExpression iexampleLike(Object example);
/**
* Create the query by Example expression which is case sensitive and using
* LikeType.RAW (you need to add you own wildcards % and _).
*/
ExampleExpression exampleLike(Object example);
/**
* Create the query by Example expression specifying more options.
*/
ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType);
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
*/
Expression like(String propertyName, String value);
/**
* Case insensitive Like - property like value where the value contains the
* SQL wild card characters % (percentage) and _ (underscore). Typically uses
* a lower() function to make the expression case insensitive.
*/
Expression ilike(String propertyName, String value);
/**
* Starts With - property like value%.
*/
Expression startsWith(String propertyName, String value);
/**
* Case insensitive Starts With - property like value%. Typically uses a
* lower() function to make the expression case insensitive.
*/
Expression istartsWith(String propertyName, String value);
/**
* Ends With - property like %value.
*/
Expression endsWith(String propertyName, String value);
/**
* Case insensitive Ends With - property like %value. Typically uses a lower()
* function to make the expression case insensitive.
*/
Expression iendsWith(String propertyName, String value);
/**
* Contains - property like %value%.
*/
Expression contains(String propertyName, String value);
/**
* Case insensitive Contains - property like %value%. Typically uses a lower()
* function to make the expression case insensitive.
*/
Expression icontains(String propertyName, String value);
/**
* In - property has a value in the array of values.
*/
Expression in(String propertyName, Object[] values);
/**
* In - using a subQuery.
*/
Expression in(String propertyName, Query<?> subQuery);
/**
* In - property has a value in the collection of values.
*/
Expression in(String propertyName, Collection<?> values);
/**
* Exists expression
*/
Expression exists(Query<?> subQuery);
/**
* Not exists expression
*/
Expression notExists(Query<?> subQuery);
/**
* Id Equal to - ID property is equal to the value.
*/
Expression idEq(Object value);
/**
* Id IN a list of Id values.
*/
Expression idIn(List<?> idList);
/**
* All Equal - Map containing property names and their values.
* <p>
* Expression where all the property names in the map are equal to the
* corresponding value.
* </p>
*
* @param propertyMap
* a map keyed by property names.
*/
Expression allEq(Map<String, Object> propertyMap);
/**
* Add raw expression with a single parameter.
* <p>
* The raw expression should contain a single ? at the location of the
* parameter.
* </p>
*/
Expression raw(String raw, Object value);
/**
* Add raw expression with an array of parameters.
* <p>
* The raw expression should contain the same number of ? as there are
* parameters.
* </p>
*/
Expression raw(String raw, Object[] values);
/**
* Add raw expression with no parameters.
*/
Expression raw(String raw);
/**
* And - join two expressions with a logical and.
*/
Expression and(Expression expOne, Expression expTwo);
/**
* Or - join two expressions with a logical or.
*/
Expression or(Expression expOne, Expression expTwo);
/**
* Negate the expression (prefix it with NOT).
*/
Expression not(Expression exp);
/**
* Return a list of expressions that will be joined by AND's.
*/
<T> Junction<T> conjunction(Query<T> query);
/**
* Return a list of expressions that will be joined by OR's.
*/
<T> Junction<T> disjunction(Query<T> query);
/**
* Return a list of expressions that will be joined by AND's.
*/
<T> Junction<T> conjunction(Query<T> query, ExpressionList<T> parent);
/**
* Return a list of expressions that will be joined by OR's.
*/
<T> Junction<T> disjunction(Query<T> query, ExpressionList<T> parent);
}
File diff suppressed because it is too large Load Diff
+252 -247
View File
@@ -1,247 +1,252 @@
package com.avaje.ebean;
import java.io.Serializable;
/**
* Defines the configuration options for a "query fetch" or a
* "lazy loading fetch". This gives you the ability to use multiple smaller
* queries to populate an object graph as opposed to a single large query.
* <p>
* The primary goal is to provide efficient ways of loading complex object
* graphs avoiding SQL Cartesian product and issues around populating object
* graphs that have multiple *ToMany relationships.
* </p>
* <p>
* It also provides the ability to control the lazy loading queries (batch size,
* selected properties and fetches) to avoid N+1 queries etc.
* <p>
* There can also be cases loading across a single OneToMany where 2 SQL queries
* using Ebean FetchConfig.query() can be more efficient than one SQL query.
* When the "One" side is wide (lots of columns) and the cardinality difference
* is high (a lot of "Many" beans per "One" bean) then this can be more
* efficient loaded as 2 SQL queries.
* </p>
*
* <pre class="code">
* // Normal fetch join results in a single SQL query
* List&lt;Order&gt; list = Ebean.find(Order.class).join(&quot;details&quot;).findList();
*
* // Find Orders join details using a single SQL query
* </pre>
* <p>
* Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL
* queries
* </p>
*
* <pre class="code">
* // This will use 2 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .findList();
*
* // query 1) find order
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
* </pre>
* <p>
* Example: Using 2 "query joins"
* </p>
*
* <pre class="code">
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new JoinConfig().query())
* .fetch(&quot;customer&quot;, new JoinConfig().query(5))
* .findList();
*
* // query 1) find order
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
* // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
* </pre>
* <p>
* Example: Using "query joins" and partial objects
* </p>
*
* <pre class="code">
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .select(&quot;status, shipDate&quot;)
* .fetch(&quot;details&quot;, &quot;quantity, price&quot;, new FetchConfig().query())
* .fetch(&quot;details.product&quot;, &quot;sku, name&quot;)
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().query(10))
* .fetch(&quot;customer.contacts&quot;)
* .fetch(&quot;customer.shippingAddress&quot;)
* .findList();
*
* // query 1) find order (status, shipDate)
* // query 2) find orderDetail (quantity, price) fetch product (sku, name) where
* // order.id in (?,? ...)
* // query 3) find customer (name) fetch contacts (*) fetch shippingAddress (*)
* // where id in (?,?,?,?,?)
*
* // Note: the fetch of &quot;details.product&quot; is automatically included into the
* // fetch of &quot;details&quot;
* //
* // Note: the fetch of &quot;customer.contacts&quot; and &quot;customer.shippingAddress&quot;
* // are automatically included in the fetch of &quot;customer&quot;
* </pre>
* <p>
* You can use query() and lazy together on a single join. The query is executed
* immediately and the lazy defines the batch size to use for further lazy
* loading (if lazy loading is invoked).
* </p>
*
* <pre class="code">
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;customer&quot;, new FetchConfig().query(3).lazy(10))
* .findList();
*
* // query 1) find order
* // query 2) find customer where id in (?,?,?) // first 3 customers
* // .. then if lazy loading of customers is invoked
* // .. use a batch size of 10 to load the customers
*
* </pre>
*
* <p>
* Example of controlling the lazy loading query:
* </p>
* <p>
* This gives us the ability to optimise the lazy loading query for a given use
* case.
* </p>
*
* <pre class="code">
* List&lt;Order&gt; list = Ebean.find(Order.class)
* .fetch(&quot;customer&quot;,&quot;name&quot;, new FetchConfig().lazy(5))
* .fetch(&quot;customer.contacts&quot;,&quotcontactName, phone, email&quot)
* .fetch(&quot;customer.shippingAddress&quot;)
* .where().eq(&quot;status&quot;,Order.Status.NEW)
* .findList();
*
* // query 1) find order where status = Order.Status.NEW
* //
* // .. if lazy loading of customers is invoked
* // .. use a batch size of 5 to load the customers
*
* find customer (name)
* fetch contact (contactName, phone, email)
* fetch shippingAddress (*)
* where id in (?,?,?,?,?)
*
* </pre>
*
* @author mario
* @author rbygrave
*/
public class FetchConfig implements Serializable {
private static final long serialVersionUID = 1L;
private int lazyBatchSize = -1;
private int queryBatchSize = -1;
private boolean queryAll;
/**
* Construct the fetch configuration object.
*/
public FetchConfig() {
}
/**
* Specify that this path should be lazy loaded using the default batch load
* size.
*/
public FetchConfig lazy() {
this.lazyBatchSize = 0;
return this;
}
/**
* Specify that this path should be lazy loaded with a specified batch size.
*
* @param lazyBatchSize
* the batch size for lazy loading
*/
public FetchConfig lazy(int lazyBatchSize) {
this.lazyBatchSize = lazyBatchSize;
return this;
}
/**
* Specify that this path should be loaded as a separate query (rather than as
* part of the main query).
* <p>
* This will use the default batch size for separate query which is 100.
* </p>
*/
public FetchConfig query() {
this.queryBatchSize = 0;
this.queryAll = true;
return this;
}
/**
* Specify that this path should be loaded as a separate query (rather than as
* part of the main query).
* <p>
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
* </p>
* <p>
* This will load all beans on this path eagerly.
* </p>
*
* @param queryBatchSize
* the batch size used to load beans on this path
*/
public FetchConfig query(int queryBatchSize) {
this.queryBatchSize = queryBatchSize;
this.queryAll = true;
return this;
}
/**
* Similar to {@link #query(int)} but only fetches the first batch.
* <p>
* If there are more parent beans than the batch size then they will not be
* loaded eagerly but instead use lazy loading.
* </p>
*
* @param queryBatchSize
* the number of parent beans this path is populated for
*/
public FetchConfig queryFirst(int queryBatchSize) {
this.queryBatchSize = queryBatchSize;
this.queryAll = false;
return this;
}
/**
* Return the batch size for lazy loading.
*/
public int getLazyBatchSize() {
return lazyBatchSize;
}
/**
* Return the batch size for separate query load.
*/
public int getQueryBatchSize() {
return queryBatchSize;
}
/**
* Return true if the query fetch should fetch 'all' rather than just the
* 'first' batch.
*/
public boolean isQueryAll() {
return queryAll;
}
}
package com.avaje.ebean;
import java.io.Serializable;
/**
* Defines the configuration options for a "query fetch" or a
* "lazy loading fetch". This gives you the ability to use multiple smaller
* queries to populate an object graph as opposed to a single large query.
* <p>
* The primary goal is to provide efficient ways of loading complex object
* graphs avoiding SQL Cartesian product and issues around populating object
* graphs that have multiple *ToMany relationships.
* </p>
* <p>
* It also provides the ability to control the lazy loading queries (batch size,
* selected properties and fetches) to avoid N+1 queries etc.
* <p>
* There can also be cases loading across a single OneToMany where 2 SQL queries
* using Ebean FetchConfig.query() can be more efficient than one SQL query.
* When the "One" side is wide (lots of columns) and the cardinality difference
* is high (a lot of "Many" beans per "One" bean) then this can be more
* efficient loaded as 2 SQL queries.
* </p>
*
* <pre class="code">
* // Normal fetch join results in a single SQL query
* List&lt;Order&gt; list = Ebean.find(Order.class).fetch(&quot;details&quot;).findList();
*
* // Find Orders join details using a single SQL query
* </pre>
* <p>
* Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL
* queries
* </p>
*
* <pre class="code">
* // This will use 2 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .findList();
*
* // query 1) find order
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
* </pre>
* <p>
* Example: Using 2 "query joins"
* </p>
*
* <pre class="code">
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .fetch(&quot;customer&quot;, new FetchConfig().queryFirst(5))
* .findList();
*
* // query 1) find order
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
* // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
* </pre>
* <p>
* Example: Using "query joins" and partial objects
* </p>
*
* <pre class="code">
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .select(&quot;status, shipDate&quot;)
* .fetch(&quot;details&quot;, &quot;quantity, price&quot;, new FetchConfig().query())
* .fetch(&quot;details.product&quot;, &quot;sku, name&quot;)
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().queryFirst(5))
* .fetch(&quot;customer.contacts&quot;)
* .fetch(&quot;customer.shippingAddress&quot;)
* .findList();
*
* // query 1) find order (status, shipDate)
* // query 2) find orderDetail (quantity, price) fetch product (sku, name) where
* // order.id in (?,? ...)
* // query 3) find customer (name) fetch contacts (*) fetch shippingAddress (*)
* // where id in (?,?,?,?,?)
*
* // Note: the fetch of &quot;details.product&quot; is automatically included into the
* // fetch of &quot;details&quot;
* //
* // Note: the fetch of &quot;customer.contacts&quot; and &quot;customer.shippingAddress&quot;
* // are automatically included in the fetch of &quot;customer&quot;
* </pre>
* <p>
* You can use query() and lazy together on a single join. The query is executed
* immediately and the lazy defines the batch size to use for further lazy
* loading (if lazy loading is invoked).
* </p>
*
* <pre class="code">
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;customer&quot;, new FetchConfig().query(10).lazy(5))
* .findList();
*
* // query 1) find order
* // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
* // .. then if lazy loading of customers is invoked
* // .. use a batch size of 5 to load the customers
*
* </pre>
*
* <p>
* Example of controlling the lazy loading query:
* </p>
* <p>
* This gives us the ability to optimise the lazy loading query for a given use
* case.
* </p>
*
* <pre class="code">
* List&lt;Order&gt; list = Ebean.find(Order.class)
* .fetch(&quot;customer&quot;,&quot;name&quot;, new FetchConfig().lazy(5))
* .fetch(&quot;customer.contacts&quot;,&quotcontactName, phone, email&quot)
* .fetch(&quot;customer.shippingAddress&quot;)
* .where().eq(&quot;status&quot;,Order.Status.NEW)
* .findList();
*
* // query 1) find order where status = Order.Status.NEW
* //
* // .. if lazy loading of customers is invoked
* // .. use a batch size of 5 to load the customers
*
* find customer (name)
* fetch customer.contacts (contactName, phone, email)
* fetch customer.shippingAddress (*)
* where id in (?,?,?,?,?)
*
* </pre>
*
* @author mario
* @author rbygrave
*/
public class FetchConfig implements Serializable {
private static final long serialVersionUID = 1L;
private int lazyBatchSize = -1;
private int queryBatchSize = -1;
private boolean queryAll;
/**
* Construct the fetch configuration object.
*/
public FetchConfig() {
}
/**
* Specify that this path should be lazy loaded using the default batch load
* size.
*/
public FetchConfig lazy() {
this.lazyBatchSize = 0;
this.queryAll = false;
return this;
}
/**
* Specify that this path should be lazy loaded with a specified batch size.
*
* @param lazyBatchSize
* the batch size for lazy loading
*/
public FetchConfig lazy(int lazyBatchSize) {
this.lazyBatchSize = lazyBatchSize;
this.queryAll = false;
return this;
}
/**
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
* This will use the default batch size for separate query which is 100.
* </p>
*/
public FetchConfig query() {
this.queryBatchSize = 0;
this.queryAll = true;
return this;
}
/**
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
* <p>
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
* </p>
* <p>
* This will load all beans on this path eagerly unless a {@link #lazy(int)}
* is also used.
* </p>
*
* @param queryBatchSize
* the batch size used to load beans on this path
*/
public FetchConfig query(int queryBatchSize) {
this.queryBatchSize = queryBatchSize;
// queryAll true as long as a lazy batch size has not already been set
this.queryAll = (lazyBatchSize == -1);
return this;
}
/**
* Eagerly fetch the first batch of beans on this path.
* This is similar to {@link #query(int)} but only fetches the first batch.
* <p>
* If there are more parent beans than the batch size then they will not be
* loaded eagerly but instead use lazy loading.
* </p>
*
* @param queryBatchSize
* the number of parent beans this path is populated for
*/
public FetchConfig queryFirst(int queryBatchSize) {
this.queryBatchSize = queryBatchSize;
this.queryAll = false;
return this;
}
/**
* Return the batch size for lazy loading.
*/
public int getLazyBatchSize() {
return lazyBatchSize;
}
/**
* Return the batch size for separate query load.
*/
public int getQueryBatchSize() {
return queryBatchSize;
}
/**
* Return true if the query fetch should fetch 'all' rather than just the
* 'first' batch.
*/
public boolean isQueryAll() {
return queryAll;
}
}
+20 -20
View File
@@ -90,98 +90,98 @@ public interface Filter<T> {
* Refer to {@link Ebean#sort(List, String)} for more detail.
* </p>
*/
public Filter<T> sort(String sortByClause);
Filter<T> sort(String sortByClause);
/**
* Specify the maximum number of rows/elements to return.
*/
public Filter<T> maxRows(int maxRows);
Filter<T> maxRows(int maxRows);
/**
* Equal To - property equal to the given value.
*/
public Filter<T> eq(String prop, Object value);
Filter<T> eq(String prop, Object value);
/**
* Not Equal To - property not equal to the given value.
*/
public Filter<T> ne(String propertyName, Object value);
Filter<T> ne(String propertyName, Object value);
/**
* Case Insensitive Equal To.
*/
public Filter<T> ieq(String propertyName, String value);
Filter<T> ieq(String propertyName, String value);
/**
* Between - property between the two given values.
*/
public Filter<T> between(String propertyName, Object value1, Object value2);
Filter<T> between(String propertyName, Object value1, Object value2);
/**
* Greater Than - property greater than the given value.
*/
public Filter<T> gt(String propertyName, Object value);
Filter<T> gt(String propertyName, Object value);
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
*/
public Filter<T> ge(String propertyName, Object value);
Filter<T> ge(String propertyName, Object value);
/**
* Less Than - property less than the given value.
*/
public Filter<T> lt(String propertyName, Object value);
Filter<T> lt(String propertyName, Object value);
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
public Filter<T> le(String propertyName, Object value);
Filter<T> le(String propertyName, Object value);
/**
* Is Null - property is null.
*/
public Filter<T> isNull(String propertyName);
Filter<T> isNull(String propertyName);
/**
* Is Not Null - property is not null.
*/
public Filter<T> isNotNull(String propertyName);
Filter<T> isNotNull(String propertyName);
/**
* Starts With.
*/
public Filter<T> startsWith(String propertyName, String value);
Filter<T> startsWith(String propertyName, String value);
/**
* Case insensitive Starts With.
*/
public Filter<T> istartsWith(String propertyName, String value);
Filter<T> istartsWith(String propertyName, String value);
/**
* Ends With.
*/
public Filter<T> endsWith(String propertyName, String value);
Filter<T> endsWith(String propertyName, String value);
/**
* Case insensitive Ends With.
*/
public Filter<T> iendsWith(String propertyName, String value);
Filter<T> iendsWith(String propertyName, String value);
/**
* Contains - property contains the string "value".
*/
public Filter<T> contains(String propertyName, String value);
Filter<T> contains(String propertyName, String value);
/**
* Case insensitive Contains.
*/
public Filter<T> icontains(String propertyName, String value);
Filter<T> icontains(String propertyName, String value);
/**
* In - property has a value contained in the set of values.
*/
public Filter<T> in(String propertyName, Set<?> values);
Filter<T> in(String propertyName, Set<?> values);
/**
* Apply the filter to the list returning a new list of the matching elements
@@ -192,6 +192,6 @@ public interface Filter<T> {
*
* @return Returns a new list with the sorting and filters applied.
*/
public List<T> filter(List<T> sourceList);
List<T> filter(List<T> sourceList);
}
+34 -34
View File
@@ -1,34 +1,34 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* FutureIds represents the result of a background query execution for the Id's.
* <p>
* It extends the java.util.concurrent.Future with the ability to get the Id's
* while the query is still executing in the background.
* </p>
*
* @author rbygrave
*/
public interface FutureIds<T> extends Future<List<Object>> {
/**
* Returns the original query used to fetch the Id's.
*/
public Query<T> getQuery();
/**
* Return the list of Id's which could be partially populated.
* <p>
* That is the query getting the id's could still be running and adding id's
* to this list.
* </p>
* <p>
* To get the list of Id's ensuring the query has finished use the
* {@link Future#get()} method instead of this one.
* </p>
*/
public List<Object> getPartialIds();
}
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* FutureIds represents the result of a background query execution for the Id's.
* <p>
* It extends the java.util.concurrent.Future with the ability to get the Id's
* while the query is still executing in the background.
* </p>
*
* @author rbygrave
*/
public interface FutureIds<T> extends Future<List<Object>> {
/**
* Returns the original query used to fetch the Id's.
*/
Query<T> getQuery();
/**
* Return the list of Id's which could be partially populated.
* <p>
* That is the query getting the id's could still be running and adding id's
* to this list.
* </p>
* <p>
* To get the list of Id's ensuring the query has finished use the
* {@link Future#get()} method instead of this one.
* </p>
*/
List<Object> getPartialIds();
}
+76 -53
View File
@@ -1,53 +1,76 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* FutureList represents the result of a background query execution that will
* return a list of entities.
* <p>
* It extends the java.util.concurrent.Future with the ability to cancel the
* query, check if it is finished and get the resulting list waiting for the
* query to finish (ie. the standard features of java.util.concurrent.Future).
* </p>
* <p>
* A simple example:
* </p>
*
* <pre class="code">
* // create a query to find all orders
* Query&lt;Order&gt; query = Ebean.find(Order.class);
*
* // execute the query in a background thread
* // immediately returning the futureList
* FutureList&lt;Order&gt; futureList = query.findFutureList();
*
* // do something else ...
*
* if (!futureList.isDone()){
* // we can cancel the query execution. This will cancel
* // the underlying query if that is supported by the JDBC
* // driver and database
* futureList.cancel(true);
* }
*
*
* if (!futureList.isCancelled()){
* // wait for the query to finish and return the list
* List&lt;Order&gt; list = futureList.get();
* ...
* }
*
* </pre>
*
* @author rbygrave
*/
public interface FutureList<T> extends Future<List<T>> {
/**
* Return the query that is being executed by a background thread.
*/
public Query<T> getQuery();
}
package com.avaje.ebean;
import javax.persistence.PersistenceException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* FutureList represents the result of a background query execution that will
* return a list of entities.
* <p>
* It extends the java.util.concurrent.Future with the ability to cancel the
* query, check if it is finished and get the resulting list waiting for the
* query to finish (ie. the standard features of java.util.concurrent.Future).
* </p>
* <p>
* A simple example:
* </p>
*
* <pre>{@code
* // create a query to find all orders
* Query<Order> query = Ebean.find(Order.class);
*
* // execute the query in a background thread
* // immediately returning the futureList
* FutureList<Order> futureList = query.findFutureList();
*
* // do something else ...
*
* if (!futureList.isDone()){
* // we can cancel the query execution. This will cancel
* // the underlying query if that is supported by the JDBC
* // driver and database
* futureList.cancel(true);
* }
*
*
* if (!futureList.isCancelled()){
* // wait for the query to finish and return the list
* List<Order> list = futureList.get();
* ...
* }
*
* }</pre>
*/
public interface FutureList<T> extends Future<List<T>> {
/**
* Return the query that is being executed by a background thread.
*/
Query<T> getQuery();
/**
* Same as {@link #get()} but wraps InterruptedException and ExecutionException in the
* unchecked PersistenceException.
*
* @return The query list result
*
* @throws PersistenceException when a InterruptedException or ExecutionException occurs.
*/
List<T> getUnchecked();
/**
* Same as {@link #get(long, java.util.concurrent.TimeUnit)} but wraps InterruptedException
* and ExecutionException in the unchecked PersistenceException.
*
* @return The query list result
*
* @throws TimeoutException if the wait timed out
* @throws PersistenceException if a InterruptedException or ExecutionException occurs.
*/
List<T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException;
}
@@ -1,15 +1,15 @@
package com.avaje.ebean;
import java.util.concurrent.Future;
/**
* Represents the result of a background query execution for the total row count
* for a query.
* <p>
* It extends the java.util.concurrent.Future.
* </p>
*
* @author rbygrave
*/
public interface FutureRowCount<T> extends Future<Integer> {
}
package com.avaje.ebean;
import java.util.concurrent.Future;
/**
* Represents the result of a background query execution for the total row count
* for a query.
* <p>
* It extends the java.util.concurrent.Future.
* </p>
*
* @author rbygrave
*/
public interface FutureRowCount<T> extends Future<Integer> {
}
+883
View File
@@ -0,0 +1,883 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.util.ClassUtil;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
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>
* You can use Dependency Injection like Guice or Spring to construct and wire a EbeanServer instance
* and have that same instance used with this Model and Finder. The way that works is that when the
* DI container creates the EbeanServer instance it can be registered with the Ebean singleton. In this
* way the EbeanServer instance can be injected as per normal Guice / Spring dependency injection and
* that same instance also used to support the Model and Finder active record style.
*
* <p>
* If you choose to use the Model mapped superclass you will probably also chose to additionally add
* a {@link Find} as a public static field to complete the active record pattern and provide a
* relatively nice clean way to write queries.
*
* <h3>Typical common @MappedSuperclass</h3>
* <pre>{@code
*
* // Typically there is a common base model that has some
* // common properties like the ones below
*
* @MappedSuperclass
* public class BaseModel extends Model {
*
* @Id Long id;
*
* @Version Long version;
*
* @CreatedTimestamp Timestamp whenCreated;
*
* @UpdatedTimestamp Timestamp whenUpdated;
*
* ...
*
* }</pre>
*
* <h3>Extend the Model</h3>
* <pre>{@code
*
* // Extend the mappedSuperclass
*
* @Entity @Table(name="oto_account")
* public class Customer extends BaseModel {
*
* // Add a static Find
* // ... with Long being the type of our @Id property.
* // ... Note the {} at the end as Find is an abstract class.
*
* public static final Find<Long,Account> find = new Find<Long,Account>(){};
*
* String name;
* ...
* }
*
* }</pre>
*
* <h3>Modal: save()</h3>
* <pre>{@code
*
* // Active record style ... save(), delete() etc
* Customer customer = new Customer();
* customer.setName("AC234");
*
* // save() method inherited from Model
* customer.save();
*
* }</pre>
*
* <h3>Find byId</h3>
* <pre>{@code
*
* // find byId
* Customer customer = Customer.find.byId(42);
*
* }</pre>
*
* <h3>Find where</h3>
* <pre>{@code
*
* // find where ...
* List<Customer> customers =
* Customer.find
* .where().gt("startDate", lastMonth)
* .findList();
*
* }</pre>
*/
@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>{@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("Franko");
* 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>{@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>
*
* @see EbeanServer#markAsDirty(Object)
*/
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.
*
* @see EbeanServer#save(Object)
*/
public void save() {
db().save(this);
}
/**
* Update this entity.
*
* @see EbeanServer#update(Object)
*/
public void update() {
db().update(this);
}
/**
* Insert this entity.
*
* @see EbeanServer#insert(Object)
*/
public void insert() {
db().insert(this);
}
/**
* Delete this entity.
*
* @see EbeanServer#delete(Object)
*/
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.
*
* @see EbeanServer#refresh(Object)
*/
public void refresh() {
db().refresh(this);
}
/**
* A concrete implementation of Find.
* <p>
* It should be preferred to use {@link Find} instead of Finder as that can use reflection to determine the class
* literal type of the entity bean.
* </p>
* @param <I> type of the Id property
* @param <T> type of the entity bean
*/
public static class Finder<I, T> extends Find<I, T> {
/**
* Create with the type of the entity bean.
*
* <pre>{@code
*
* @Entity
* public class Customer extends BaseModel {
*
* public static final Finder<Long,Customer> find = new Finder<Long,Customer>(Customer.class);
* ...
*
* }</pre>
*
* <p/>
* The preferred approach is to instead use <code>Find</code> as below. This approach is more DRY in that it does
* not require the class literal Customer.class to be passed into the constructor.
*
* <pre>{@code
*
* @Entity
* public class Customer extends BaseModel {
*
* public static final Find<Long,Customer> find = new Find<Long,Customer>(){};
* ...
*
* }</pre>
*/
public Finder(Class<T> type) {
super(null, type);
}
/**
* Create with the type of the entity bean and specific server name.
*/
public Finder(String serverName, Class<T> type) {
super(serverName, type);
}
/**
* Please migrate to use {@link Find} or constructor <code>Finder(Class)</code> that
* does not have the idType parameter.
* <p/>
* Create with the type of the ID property and entity bean and specific server name.
*
* @deprecated
*/
public Finder(Class<I> idType, Class<T> type) {
super(null, type);
}
/**
* Please migrate to use the constructor <code>Finder(String, Class)</code> that
* does not have the idType parameter.
* <p/>
* Create with the type of the ID property and entity bean and specific server name.
*
* @deprecated
*/
public Finder(String serverName, Class<I> idType, Class<T> type) {
super(serverName, type);
}
}
/**
* Helper object for performing queries.
*
* <p>
* Typically a Find instance is defined as a public static field on an entity bean class to provide a
* nice way to write queries.
*
* <h3>Example use:</h3>
*
* <pre>{@code
*
* @Entity
* public class Customer extends BaseModel {
*
* public static final Find<Long,Customer> find = new Find<Long,Customer>(){};
*
* ...
*
* }</pre>
* <p/>
* This enables you to write code like:
* <pre>{@code
*
* Customer customer = Customer.find.byId(42L);
*
* List<Customer> customers =
* Customer.find
* .select("name, dateOfBirth")
* .findList();
*
* }</pre>
*
* <h3>Kotlin</h3>
* In Kotlin you would typically create Find as a companion object.
* <pre>{@code
*
* // kotlin
* companion object : Model.Find<Long, Product>() {}
*
* }</pre>
* @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 entity bean type
*/
public static abstract class Find<I, T> {
/**
* The entity bean type.
*/
private final Class<T> type;
/**
* The name of the EbeanServer, null for the default server.
*/
private final String serverName;
/**
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>.
* <p/>
* Typically you create Find as a public static field on each entity bean as the example below.
*
* <p/>
* Note that Find is an abstract class and hence <code>{}</code> is required. This is done so
* that the type (class literal) of the entity bean can be derived from the generics parameter.
*
* <pre>{@code
*
* @Entity
* public class Customer extends BaseModel {
*
* // Note the trailing {} as Find is an abstract class.
* // We do this so that we can derive the type literal Customer.class
* // via reflection
* public static final Find<Long,Customer> find = new Find<Long,Customer>(){};
* ...
*
* }</pre>
* <p/>
* This enables you to write code like:
* <pre>{@code
*
* Customer customer = Customer.find.byId(42L);
*
* List<Customer> customers =
* Customer.find
* .select("name, email, dateOfBirth")
* .findList();
*
* }</pre>
*
* <h3>Kotlin</h3>
* In Kotlin you would typically create it as a companion object.
*
* <pre>{@code
*
* // kotlin
* companion object : Model.Find<Long, Product>() {}
*
* }</pre>
*/
public Find() {
this.serverName = null;
this.type = (Class<T>)ClassUtil.getSecondArgumentType(getClass());
}
/**
* Construct passing the class literal type of the entity type.
*/
protected Find(String serverName, Class<T> type) {
this.type = type;
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, type);
}
/**
* Delete a bean by Id.
* <p>
* Equivalent to {@link EbeanServer#delete(Class, Object)}
*/
public void deleteById(I id) {
db().delete(type, id);
}
/**
* Retrieves all entities of the given type.
*
* <p>
* This is the same as (synonym for) {@link #findList()}
*/
public List<T> all() {
return findList();
}
/**
* Retrieves an entity by ID.
*
* <p>
* Equivalent to {@link EbeanServer#find(Class, Object)}
*/
public T byId(I id) {
return db().find(type, id);
}
/**
* Creates an entity reference for this ID.
*
* <p>
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
*/
public T ref(I id) {
return db().getReference(type, id);
}
/**
* Creates a filter for sorting and filtering lists of entities locally without going back to
* the database.
* <p>
* Equivalent to {@link EbeanServer#filter(Class)}
*/
public Filter<T> filter() {
return db().filter(type);
}
/**
* Creates a query.
* <p>
* Equivalent to {@link EbeanServer#find(Class)}
*/
public Query<T> query() {
return db().find(type);
}
/**
* Creates a query applying the path properties to set the select and fetch clauses.
* <p>
* Equivalent to {@link Query#apply(com.avaje.ebean.text.PathProperties)}
*/
public Query<T> apply(PathProperties pathProperties) {
return db().find(type).apply(pathProperties);
}
/**
* Returns the next identity value.
*
* @see EbeanServer#nextId(Class)
*/
@SuppressWarnings("unchecked")
public I nextId() {
return (I) db().nextId(type);
}
/**
* Executes a query and returns the results as a list of IDs.
* <p>
* Equivalent to {@link Query#findIds()}
*/
public List<Object> findIds() {
return query().findIds();
}
/**
* Execute the query consuming each bean one at a time.
* <p>
* This is generally used to process large queries where unlike findList
* you do not want to hold all the results in memory at once but instead
* process them one at a time (requiring far less memory).
* </p>
* Equivalent to {@link Query#findEach(QueryEachConsumer)}
*/
public void findEach(QueryEachConsumer<T> consumer) {
query().findEach(consumer);
}
/**
* Execute the query consuming each bean one at a time.
* <p>
* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)}
* <p>
* This is similar to #findEach except that you return boolean
* true to continue processing beans and return false to stop
* processing early.
* </p>
* <p>
* This is generally used to process large queries where unlike findList
* you do not want to hold all the results in memory at once but instead
* process them one at a time (requiring far less memory).
* </p>
* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)}
*/
public void findEachWhile(QueryEachWhileConsumer<T> consumer) {
query().findEachWhile(consumer);
}
/**
* Retrieves all entities of the given type.
* <p>
* The same as {@link #all()}
* <p>
* Equivalent to {@link Query#findList()}
*/
public List<T> findList() {
return query().findList();
}
/**
* Returns all the entities of the given type as a set.
* <p>
* Equivalent to {@link Query#findSet()}
*/
public Set<T> findSet() {
return query().findSet();
}
/**
* Retrieves all entities of the given type as a map of objects.
* <p>
* Equivalent to {@link Query#findMap()}
*/
public Map<?, T> findMap() {
return query().findMap();
}
/**
* Executes the query and returns the results as a map of the objects specifying the map key
* property.
* <p>
* Equivalent to {@link Query#findMap(String, Class)}
*/
public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) {
return query().findMap(keyProperty, keyType);
}
/**
* Return a PagedList of all entities of the given type (use where() to specify predicates as
* needed).
* <p>
* Equivalent to {@link Query#findPagedList(int, int)}
*/
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
return query().findPagedList(pageIndex, pageSize);
}
/**
* Executes a find row count query in a background thread.
* <p>
* Equivalent to {@link Query#findFutureRowCount()}
*/
public FutureRowCount<T> findFutureRowCount() {
return query().findFutureRowCount();
}
/**
* Returns the total number of entities for this type. *
* <p>
* Equivalent to {@link Query#findRowCount()}
*/
public int findRowCount() {
return query().findRowCount();
}
/**
* Returns the <code>ExpressionFactory</code> used by this query.
*/
public ExpressionFactory getExpressionFactory() {
return query().getExpressionFactory();
}
/**
* Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean,
* to load a partial object.
* <p>
* Equivalent to {@link Query#select(String)}
*/
public Query<T> select(String fetchProperties) {
return query().select(fetchProperties);
}
/**
* Specifies a path to load including all its properties.
* <p>
* Equivalent to {@link Query#fetch(String)}
*/
public Query<T> fetch(String path) {
return query().fetch(path);
}
/**
* Additionally specifies a <code>FetchConfig</code> to specify a 'query join' and/or define the
* lazy loading query.
* <p>
* Equivalent to {@link Query#fetch(String, FetchConfig)}
*/
public Query<T> fetch(String path, FetchConfig joinConfig) {
return query().fetch(path, joinConfig);
}
/**
* Specifies a path to fetch with a specific list properties to include, to load a partial
* object.
* <p>
* Equivalent to {@link Query#fetch(String, String)}
*/
public Query<T> fetch(String path, String fetchProperties) {
return query().fetch(path, fetchProperties);
}
/**
* Additionally specifies a <code>FetchConfig</code> to use a separate query or lazy loading to
* load this path.
* <p>
* Equivalent to {@link Query#fetch(String, String, FetchConfig)}
*/
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) {
return query().fetch(assocProperty, fetchProperties, fetchConfig);
}
/**
* Adds expressions to the <code>where</code> clause with the ability to chain on the
* <code>ExpressionList</code>.
* <p>
* Equivalent to {@link Query#where()}
*/
public ExpressionList<T> where() {
return query().where();
}
/**
* Returns the <code>order by</code> clause so that you can append an ascending or descending
* property to the <code>order by</code> clause.
* <p>
* This is exactly the same as {@link #orderBy}.
* <p>
* Equivalent to {@link Query#order()}
*/
public OrderBy<T> order() {
return query().order();
}
/**
* Sets the <code>order by</code> clause, replacing the existing <code>order by</code> clause if
* there is one.
* <p>
* This is exactly the same as {@link #orderBy(String)}.
*/
public Query<T> order(String orderByClause) {
return query().order(orderByClause);
}
/**
* Returns the <code>order by</code> clause so that you can append an ascending or descending
* property to the <code>order by</code> clause.
* <p>
* This is exactly the same as {@link #order}.
* <p>
* Equivalent to {@link Query#orderBy()}
*/
public OrderBy<T> orderBy() {
return query().orderBy();
}
/**
* Set the <code>order by</code> clause replacing the existing <code>order by</code> clause if
* there is one.
* <p>
* This is exactly the same as {@link #order(String)}.
*/
public Query<T> orderBy(String orderByClause) {
return query().orderBy(orderByClause);
}
/**
* Sets the first row to return for this query.
* <p>
* Equivalent to {@link Query#setFirstRow(int)}
*/
public Query<T> setFirstRow(int firstRow) {
return query().setFirstRow(firstRow);
}
/**
* Sets the maximum number of rows to return in the query.
* <p>
* Equivalent to {@link Query#setMaxRows(int)}
*/
public Query<T> setMaxRows(int maxRows) {
return query().setMaxRows(maxRows);
}
/**
* Sets the ID value to query.
*
* <p>
* Use this to perform a find byId query but with additional control over the query such as
* using select and fetch to control what parts of the object graph are returned.
* <p>
* Equivalent to {@link Query#setId(Object)}
*/
public Query<T> setId(Object id) {
return query().setId(id);
}
/**
* Create and return a new query using the OQL.
* <p>
* Equivalent to {@link EbeanServer#createQuery(Class, String)}
*/
public Query<T> setQuery(String oql) {
return db().createQuery(type, oql);
}
/**
* Create and return a new query based on the <code>RawSql</code>.
* <p>
* Equivalent to {@link Query#setRawSql(RawSql)}
*/
public Query<T> setRawSql(RawSql rawSql) {
return query().setRawSql(rawSql);
}
/**
* Create a query with explicit 'Autofetch' use.
*/
public Query<T> setAutofetch(boolean autofetch) {
return query().setAutofetch(autofetch);
}
/**
* Create a query with the select with "for update" specified.
*
* <p>
* This will typically create row level database locks on the selected rows.
*/
public Query<T> setForUpdate(boolean forUpdate) {
return query().setForUpdate(forUpdate);
}
/**
* Create a query specifying whether the returned beans will be read-only.
*/
public Query<T> setReadOnly(boolean readOnly) {
return query().setReadOnly(readOnly);
}
/**
* Create a query specifying if the beans should be loaded into the L2 cache.
*/
public Query<T> setLoadBeanCache(boolean loadBeanCache) {
return query().setLoadBeanCache(loadBeanCache);
}
/**
* Create a query specifying if the L2 bean cache should be used.
*/
public Query<T> setUseCache(boolean useBeanCache) {
return query().setUseCache(useBeanCache);
}
/**
* Create a query specifying if the L2 query cache should be used.
*/
public Query<T> setUseQueryCache(boolean useQueryCache) {
return query().setUseQueryCache(useQueryCache);
}
}
}
+351 -326
View File
@@ -1,326 +1,351 @@
package com.avaje.ebean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Represents an Order By for a Query.
* <p>
* Is a ordered list of OrderBy.Property objects each specifying a property and
* whether it is ascending or descending order.
* </p>
* <p>
* Typically you will not construct an OrderBy yourself but use one that exists
* on the Query object.
* </p>
*
* @author rbygrave
*/
public final class OrderBy<T> implements Serializable {
private static final long serialVersionUID = 9157089257745730539L;
private transient Query<T> query;
private List<Property> list;
/**
* Create an empty OrderBy with no associated query.
*/
public OrderBy() {
this.list = new ArrayList<Property>(2);
}
/**
* Create an orderBy parsing the order by clause.
* <p>
* The order by clause follows SQL order by clause with comma's between each
* property and optionally "asc" or "desc" to represent ascending or
* descending order respectively.
* </p>
*/
public OrderBy(String orderByClause) {
this(null, orderByClause);
}
/**
* Construct with a given query and order by clause.
*/
public OrderBy(Query<T> query, String orderByClause) {
this.query = query;
this.list = new ArrayList<Property>(2);
parse(orderByClause);
}
/**
* Reverse the ascending/descending order on all the properties.
*/
public void reverse() {
for (int i = 0; i < list.size(); i++) {
list.get(i).reverse();
}
}
/**
* Add a property with ascending order to this OrderBy.
*/
public Query<T> asc(String propertyName) {
list.add(new Property(propertyName, true));
return query;
}
/**
* Add a property with descending order to this OrderBy.
*/
public Query<T> desc(String propertyName) {
list.add(new Property(propertyName, false));
return query;
}
/**
* Return the properties for this OrderBy.
*/
public List<Property> getProperties() {
// not returning an Immutable list at this point
return list;
}
/**
* Return true if this OrderBy does not have any properties.
*/
public boolean isEmpty() {
return list.isEmpty();
}
/**
* Return the associated query if there is one.
*/
public Query<T> getQuery() {
return query;
}
/**
* Associate this OrderBy with a query.
*/
public void setQuery(Query<T> query) {
this.query = query;
}
/**
* Return a copy of the OrderBy.
*/
public OrderBy<T> copy() {
OrderBy<T> copy = new OrderBy<T>();
for (int i = 0; i < list.size(); i++) {
copy.add(list.get(i).copy());
}
return copy;
}
/**
* Add a property to the order by.
*/
public void add(Property p) {
list.add(p);
}
public String toString() {
return list.toString();
}
/**
* Returns the OrderBy in string format.
*/
public String toStringFormat() {
if (list.isEmpty()) {
return null;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
Property property = list.get(i);
if (i > 0) {
sb.append(", ");
}
sb.append(property.toStringFormat());
}
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof OrderBy<?>) {
if (obj == this) {
return true;
}
OrderBy<?> other = (OrderBy<?>) obj;
return hashCode() == other.hashCode();
}
return false;
}
@Override
public int hashCode() {
return hash();
}
/**
* Return a hash value for this OrderBy. This can be to determine logical
* equality for OrderBy clauses.
*/
public int hash() {
int hc = OrderBy.class.getName().hashCode();
for (int i = 0; i < list.size(); i++) {
hc = hc * 31 + list.get(i).hash();
}
return hc;
}
/**
* A property and its ascending descending order.
*/
public static final class Property implements Serializable {
private static final long serialVersionUID = 1546009780322478077L;
private String property;
private boolean ascending;
public Property(String property, boolean ascending) {
this.property = property;
this.ascending = ascending;
}
protected int hash() {
int hc = property.hashCode();
hc = hc * 31 + (ascending ? 0 : 1);
return hc;
}
public String toString() {
return toStringFormat();
}
public String toStringFormat() {
if (ascending) {
return property;
} else {
return property + " desc";
}
}
/**
* Reverse the ascending/descending order for this property.
*/
public void reverse() {
this.ascending = !ascending;
}
/**
* Trim off the pathPrefix.
*/
public void trim(String pathPrefix) {
property = property.substring(pathPrefix.length() + 1);
}
/**
* Return a copy of this property.
*/
public Property copy() {
return new Property(property, ascending);
}
/**
* Return the property name.
*/
public String getProperty() {
return property;
}
/**
* Set the property name.
*/
public void setProperty(String property) {
this.property = property;
}
/**
* Return true if the order is ascending.
*/
public boolean isAscending() {
return ascending;
}
/**
* Set to true if the order is ascending.
*/
public void setAscending(boolean ascending) {
this.ascending = ascending;
}
}
private void parse(String orderByClause) {
if (orderByClause == null) {
return;
}
String[] chunks = orderByClause.split(",");
for (int i = 0; i < chunks.length; i++) {
String[] pairs = chunks[i].split(" ");
Property p = parseProperty(pairs);
if (p != null) {
list.add(p);
}
}
}
private Property parseProperty(String[] pairs) {
if (pairs.length == 0) {
return null;
}
ArrayList<String> wordList = new ArrayList<String>(pairs.length);
for (int i = 0; i < pairs.length; i++) {
if (!isEmptyString(pairs[i])) {
wordList.add(pairs[i]);
}
}
if (wordList.isEmpty()) {
return null;
}
if (wordList.size() == 1) {
return new Property(wordList.get(0), true);
}
if (wordList.size() == 2) {
boolean asc = isAscending(wordList.get(1));
return new Property(wordList.get(0), asc);
}
String m = "Expecting a max of 2 words in [" + Arrays.toString(pairs)
+ "] but got " + wordList.size();
throw new RuntimeException(m);
}
private boolean isAscending(String s) {
s = s.toLowerCase();
if (s.startsWith("asc")) {
return true;
}
if (s.startsWith("desc")) {
return false;
}
String m = "Expecting [" + s + "] to be asc or desc?";
throw new RuntimeException(m);
}
private boolean isEmptyString(String s) {
return s == null || s.length() == 0;
}
}
package com.avaje.ebean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Represents an Order By for a Query.
* <p>
* Is a ordered list of OrderBy.Property objects each specifying a property and
* whether it is ascending or descending order.
* </p>
* <p>
* Typically you will not construct an OrderBy yourself but use one that exists
* on the Query object.
* </p>
*/
public final class OrderBy<T> implements Serializable {
private static final long serialVersionUID = 9157089257745730539L;
private transient Query<T> query;
private final List<Property> list;
/**
* Create an empty OrderBy with no associated query.
*/
public OrderBy() {
this.list = new ArrayList<Property>(2);
}
private OrderBy(List<Property> list) {
this.list = list;
}
/**
* Create an orderBy parsing the order by clause.
* <p>
* The order by clause follows SQL order by clause with comma's between each
* property and optionally "asc" or "desc" to represent ascending or
* descending order respectively.
* </p>
*/
public OrderBy(String orderByClause) {
this(null, orderByClause);
}
/**
* Construct with a given query and order by clause.
*/
public OrderBy(Query<T> query, String orderByClause) {
this.query = query;
this.list = new ArrayList<Property>(2);
parse(orderByClause);
}
/**
* Reverse the ascending/descending order on all the properties.
*/
public void reverse() {
for (int i = 0; i < list.size(); i++) {
list.get(i).reverse();
}
}
/**
* Add a property with ascending order to this OrderBy.
*/
public Query<T> asc(String propertyName) {
list.add(new Property(propertyName, true));
return query;
}
/**
* Add a property with descending order to this OrderBy.
*/
public Query<T> desc(String propertyName) {
list.add(new Property(propertyName, false));
return query;
}
/**
* Return a copy of this OrderBy with the path trimmed.
*/
public OrderBy<T> copyWithTrim(String path) {
List<Property> newList = new ArrayList<Property>(list.size());
for (int i = 0; i < list.size(); i++) {
newList.add(list.get(i).copyWithTrim(path));
}
return new OrderBy<T>(newList);
}
/**
* Return the properties for this OrderBy.
*/
public List<Property> getProperties() {
// not returning an Immutable list at this point
return list;
}
/**
* Return true if this OrderBy does not have any properties.
*/
public boolean isEmpty() {
return list.isEmpty();
}
/**
* Return the associated query if there is one.
*/
public Query<T> getQuery() {
return query;
}
/**
* Associate this OrderBy with a query.
*/
public void setQuery(Query<T> query) {
this.query = query;
}
/**
* Return a copy of the OrderBy.
*/
public OrderBy<T> copy() {
OrderBy<T> copy = new OrderBy<T>();
for (int i = 0; i < list.size(); i++) {
copy.add(list.get(i).copy());
}
return copy;
}
/**
* Add a property to the order by.
*/
public void add(Property p) {
list.add(p);
}
public String toString() {
return list.toString();
}
/**
* Returns the OrderBy in string format.
*/
public String toStringFormat() {
if (list.isEmpty()) {
return null;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
Property property = list.get(i);
if (i > 0) {
sb.append(", ");
}
sb.append(property.toStringFormat());
}
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof OrderBy<?>)) {
return false;
}
OrderBy<?> e = (OrderBy<?>) obj;
return e.list.equals(list);
}
/**
* Return a hash value for this OrderBy. This can be to determine logical
* equality for OrderBy clauses.
*/
public int hashCode() {
return list.hashCode();
}
/**
* A property and its ascending descending order.
*/
public static final class Property implements Serializable {
private static final long serialVersionUID = 1546009780322478077L;
private String property;
private boolean ascending;
public Property(String property, boolean ascending) {
this.property = property;
this.ascending = ascending;
}
/**
* Return a copy of this Property with the path trimmed.
*/
public Property copyWithTrim(String path) {
return new Property(property.substring(path.length() + 1), ascending);
}
public int hashCode() {
int hc = property.hashCode();
hc = hc * 31 + (ascending ? 0 : 1);
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Property)) {
return false;
}
Property e = (Property) obj;
return e.ascending == ascending
&& e.property.equals(property);
}
public String toString() {
return toStringFormat();
}
public String toStringFormat() {
if (ascending) {
return property;
} else {
return property + " desc";
}
}
/**
* Reverse the ascending/descending order for this property.
*/
public void reverse() {
this.ascending = !ascending;
}
/**
* Trim off the pathPrefix.
*/
public void trim(String pathPrefix) {
property = property.substring(pathPrefix.length() + 1);
}
/**
* Return a copy of this property.
*/
public Property copy() {
return new Property(property, ascending);
}
/**
* Return the property name.
*/
public String getProperty() {
return property;
}
/**
* Set the property name.
*/
public void setProperty(String property) {
this.property = property;
}
/**
* Return true if the order is ascending.
*/
public boolean isAscending() {
return ascending;
}
/**
* Set to true if the order is ascending.
*/
public void setAscending(boolean ascending) {
this.ascending = ascending;
}
}
private void parse(String orderByClause) {
if (orderByClause == null) {
return;
}
String[] chunks = orderByClause.split(",");
for (int i = 0; i < chunks.length; i++) {
String[] pairs = chunks[i].split(" ");
Property p = parseProperty(pairs);
if (p != null) {
list.add(p);
}
}
}
private Property parseProperty(String[] pairs) {
if (pairs.length == 0) {
return null;
}
ArrayList<String> wordList = new ArrayList<String>(pairs.length);
for (int i = 0; i < pairs.length; i++) {
if (!isEmptyString(pairs[i])) {
wordList.add(pairs[i]);
}
}
if (wordList.isEmpty()) {
return null;
}
if (wordList.size() == 1) {
return new Property(wordList.get(0), true);
}
if (wordList.size() == 2) {
boolean asc = isAscending(wordList.get(1));
return new Property(wordList.get(0), asc);
}
String m = "Expecting a max of 2 words in [" + Arrays.toString(pairs)
+ "] but got " + wordList.size();
throw new RuntimeException(m);
}
private boolean isAscending(String s) {
s = s.toLowerCase();
if (s.startsWith("asc")) {
return true;
}
if (s.startsWith("desc")) {
return false;
}
String m = "Expecting [" + s + "] to be asc or desc?";
throw new RuntimeException(m);
}
private boolean isEmptyString(String s) {
return s == null || s.length() == 0;
}
}
-74
View File
@@ -1,74 +0,0 @@
package com.avaje.ebean;
import java.util.List;
/**
* Represents a Page of results that is part of a PagingList.
* <p>
* Typically a Page represents the data that is shown to the user at a single
* time - and the user 'pages' through a large list.
* </p>
*
* @author rbygrave
*
* @param <T>
* the entity bean type
*
* @see Query#findPagingList(int)
* @see PagingList
*/
public interface Page<T> {
/**
* 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.
*/
public int getTotalPageCount();
/**
* Return the index position of this page.
*/
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();
/**
* Return the next page.
*/
public Page<T> next();
/**
* Return the previous page.
*/
public Page<T> prev();
/**
* 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);
}
@@ -0,0 +1,192 @@
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>
*
* <h4>Example: typical use including total row count</h4>
* <pre>{@code
*
* // We want to find the first 100 new orders
* // ... 0 means first page
* // ... page size is 100
*
* PagedList<Order> pagedList
* = ebeanServer.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findPagedList(0, 100);
*
* // Optional: initiate the loading of the total
* // row count in a background thread
* pagedList.loadRowCount();
*
* // fetch and return the list in the foreground thread
* List<Order> orders = pagedList.getList();
*
* // get the total row count (from the future)
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*
* <h4>Example: No total row count required</h4>
* <pre>{@code
*
* // If you are not getting the 'first page' often
* // you do not bother getting the total row count again
* // so instead just get the page list of data
*
* // fetch and return the list in the foreground thread
* List<Order> orders = pagedList.getList();
*
* }</pre>
*
* @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.
* <pre>{@code
*
* // initiate the loading of the total row count
* // in a background thread
* pagedList.loadRowCount();
*
* // fetch and return the list in the foreground thread
* List<Order> orders = pagedList.getList();
*
* // get the total row count (from the future)
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*
* <p>
* Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount()
* means that exceptions ExecutionException, InterruptedException, TimeoutException are instead
* wrapped in the unchecked PersistenceException (which might be preferrable).
* </p>
*/
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 the row count query.
* <p>
* The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
* Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
* </p>
* <pre>{@code
*
* // initiate the row count query in the background thread
* Future<Integer> rowCount = pagedList.getFutureRowCount();
*
* // fetch and return the list in the foreground thread
* List<Order> orders = pagedList.getList();
*
* // now get the total count with a timeout
* Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
*
* // or ge the total count without a timeout
* Integer totalRowCountViaFuture = rowCount.get();
*
* // which is actually the same as ...
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*/
Future<Integer> getFutureRowCount();
/**
* Return the list of entities for this page.
*/
List<T> getList();
/**
* Return the total row count for all pages.
* <p>
* If loadRowCount() has already been called then the row count query is already executing in a background thread
* and this gets the associated Future and gets the value waiting for the future to finish.
* </p>
* <p>
* If loadRowCount() has not been called then this executes the find row count query and returns the result and this
* will just occur in the current thread and not use a background thread.
* </p>
* <pre>{@code
*
* // Optional: initiate the loading of the total
* // row count in a background thread
* pagedList.loadRowCount();
*
* // fetch and return the list in the foreground thread
* List<Order> orders = pagedList.getList();
*
* // get the total row count (which was being executed
* // in a background thread if loadRowCount() was used)
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*/
int getTotalRowCount();
/**
* Return the total number of pages based on the page size and total row count.
* <p>
* This method requires that the total row count has been fetched and will invoke
* the total row count query if it has not already been invoked.
* </p>
*/
int getTotalPageCount();
/**
* Return the index position of this page. Zero based.
*/
int getPageIndex();
/**
* Return true if there is a next page.
* <p>
* This method requires that the total row count has been fetched and will invoke
* the total row count query if it has not already been invoked.
* </p>
*/
boolean hasNext();
/**
* Return true if there is a previous page.
*/
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.
* <p>
* This method requires that the total row count has been fetched and will invoke
* the total row count query if it has not already been invoked.
* </p>
*
* @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.
*/
String getDisplayXtoYofZ(String to, String of);
}
@@ -1,127 +0,0 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* Used to page through a query result rather than fetching all the results in a
* single query.
* <p>
* Has the ability to use background threads to 'fetch ahead' the next page and
* get the total row count.
* </p>
* <p>
* If you are building a stateless web application and not keeping the
* PagingList over multiple requests then there is not much to be gained in
* using PagingList. Instead you can just use {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)}.
* </p>
*
* <p>
* If you are using PagingList is a stateful web application where the
* PagingList is held over multiple requests then PagingList provides the extra
* benefits of
* <ul>
* <li>Fetch ahead - automatically fetching the next page via background query
* execution</li>
* <li>Automatic propagation of the persistence context</li>
* </ul>
* </p>
* <p>
* So with PagingList when you use Page 2 it can automatically fetch Page 3 data
* in the background (using a findFutureList() query). It also automatically
* propagates the persistence context so that all the queries executed by the
* PagingList all use the same persistence context.
* </p>
*
* <pre>
* PagingList&lt;TOne&gt; pagingList =
* Ebean.find(TOne.class)
* .where().gt(&quot;name&quot;, &quot;2&quot;)
* .findPagingList(10);
*
* // get the row count in the background...
* // ... otherwise it is fetched on demand
* // ... when getRowCount() or getPageCount()
* // ... is called
* pagingList.getFutureRowCount();
*
* // get the first page
* Page&lt;TOne&gt; page = pagingList.getPage(0);
*
* // get the beans from the page as a list
* List&lt;TOne&gt; list = page.getList();
* </pre>
*
* @author rbygrave
*
* @param <T>
* the entity bean type
*/
public interface PagingList<T> {
/**
* Refresh will clear all the pages and row count forcing them to be
* re-fetched when next required.
*/
public void refresh();
// public void fetchAll();
// public String? getOrderBy();
// public void setOrderBy(String?);
/**
* By default fetchAhead is true so use this to turn off fetchAhead.
* <p>
* Set this to false if you don't want to fetch ahead using background
* fetching.
* <p>
* If set to true (or left as to default) then the next page is fetched in the
* background as soon as the list is accessed.
* </p>
*/
public PagingList<T> setFetchAhead(boolean fetchAhead);
/**
* Return the Future for getting the total row count.
*/
public Future<Integer> getFutureRowCount();
/**
* Return the data for all the pages in the form of a single List.
* <p>
* Iterating through this list will automatically fire the paging queries as
* required.
* </p>
*/
public List<T> getAsList();
/**
* Return the page size. This is the number of rows per page.
*/
public int getPageSize();
/**
* Return the total row count.
* <p>
* This gets the result from getFutureRowCount and will wait until that query
* has completed.
* </p>
*/
public int getTotalRowCount();
/**
* Return the total page count.
* <p>
* This is based on the total row count. This will wait until the row count
* has returned if it has not already.
* </p>
*/
public int getTotalPageCount();
/**
* Return the page for a given page position (starting at 0).
*/
public Page<T> getPage(int i);
}
@@ -0,0 +1,35 @@
package com.avaje.ebean;
/**
* Defines the scope for PersistenceContext.
* <p/>
* Ebean has traditionally used Transaction scope for the PersistenceContext. This is used to change the scope to
* use (by default) and explicitly set the scope to use for an individual query.
*
* @see com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)
* @see com.avaje.ebean.Query#setPersistenceContextScope(PersistenceContextScope)
*/
public enum PersistenceContextScope {
/**
* PersistenceContext is scoped to the transaction.
* <p/>
* If a transaction spans 2 or more queries that fetch the same bean in terms of same type
* and same Id value then they share the same bean instance.
* <p/>
* You may want to change to use QUERY scope when you want a query executing in a transaction to effectively
* ignore beans that have already been loaded (by other queries in the same transaction) and instead get a
* 'fresh copy' of the bean.
*/
TRANSACTION,
/**
* PersistenceContext is scoped to the query.
* <p/>
* This means that for this query running in an existing transaction then it will effectively ignore any beans
* that have already been queried/loaded by prior queries in the same transaction.
* <p/>
* You may use QUERY scope on a query that is executed in a transaction and you want to get a 'fresh copy' of the bean.
*/
QUERY
}
@@ -0,0 +1,80 @@
package com.avaje.ebean;
import com.avaje.ebean.config.PropertyMap;
import java.util.Properties;
/**
* Provides singleton state for the default server.
* <p/>
* Intended for internal use as part of bootup, construction, registration of the default server.
*/
class PrimaryServer {
private static Properties globalProperties;
private static String defaultServerName;
private static boolean skip;
/**
* Set whether to skip automatically creating the primary server.
*/
static synchronized void setSkip(boolean skip) {
PrimaryServer.skip = skip;
}
/**
* Return true to skip automatically creating the primary server.
*/
static synchronized boolean isSkip() {
return skip;
}
/**
* Return the default server name.
*/
static synchronized String getPrimaryServerName() {
getProperties();
return defaultServerName;
}
/**
* Return the default configuration Properties.
*/
static synchronized Properties getProperties() {
if (globalProperties == null) {
globalProperties = PropertyMap.defaultProperties();
}
if (defaultServerName == null) {
defaultServerName = determineDefaultServerName();
}
return globalProperties;
}
/**
* Determine and return the default server name checking system environment variables and then global properties.
*/
private static String determineDefaultServerName() {
String defaultServerName = System.getProperty("datasource.default");
if (isEmpty(defaultServerName)) {
defaultServerName = System.getProperty("ebean.default.datasource");
}
if (isEmpty(defaultServerName)) {
defaultServerName = globalProperties.getProperty("datasource.default");
}
if (isEmpty(defaultServerName)) {
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
}
return defaultServerName;
}
/**
* Return true if the string is null or empty.
*/
private static boolean isEmpty(String value) {
return value == null || value.trim().length() == 0;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
*
* <pre>{@code
*
* Query<Customer> query = server.find(Customer.class)
* .where().eq("status", Status.NEW)
* .order().asc("id");
*
* query.findEach((Customer customer) -> {
*
* // do something with customer
* System.out.println("-- visit " + customer);
* });
*
* }</pre>
*
* @param <T>
* the type of entity bean being queried.
*/
public interface QueryEachConsumer<T> {
/**
* Process the bean.
*
* @param bean
* the entity bean to process
*/
void accept(T bean);
}
@@ -0,0 +1,45 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
* <p/>
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findEachWhile((Customer customer) -> {
*
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
*
* // return true to continue processing or false to stop
* return (customer.getId() < 40);
* });
* </pre>
*
* @param <T> the type of entity bean being queried.
*/
public interface QueryEachWhileConsumer<T> {
/**
* Process the bean and return true if you want to continue processing more
* beans. Return false if you want to stop processing further.
*
* @param bean the entity bean to process
* @return true to continue processing more beans or false to stop.
*/
boolean accept(T bean);
}
@@ -1,60 +1,60 @@
package com.avaje.ebean;
import java.util.Iterator;
/**
* Used to provide iteration over query results.
* <p>
* This can be used when you want to process a very large number of results and
* means that you don't have to hold all the results in memory at once (unlike
* findList(), findSet() etc where all the beans are held in the List or Set
* etc).
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* QueryIterator&lt;Customer&gt; it = query.findIterate();
* try {
* while (it.hasNext()) {
* Customer customer = it.next();
* // do something with customer...
* }
* } finally {
* // close the associated resources
* it.close();
* }
* </pre>
*
* @author rbygrave
*
* @param <T>
* the type of entity bean in the iteration
*/
public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
/**
* Returns <tt>true</tt> if the iteration has more elements.
*/
public boolean hasNext();
/**
* Returns the next element in the iteration.
*/
public T next();
/**
* Remove is not allowed.
*/
public void remove();
/**
* Close the underlying resources held by this iterator.
*/
public void close();
}
package com.avaje.ebean;
import java.util.Iterator;
/**
* Used to provide iteration over query results.
* <p>
* This can be used when you want to process a very large number of results and
* means that you don't have to hold all the results in memory at once (unlike
* findList(), findSet() etc where all the beans are held in the List or Set
* etc).
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* QueryIterator&lt;Customer&gt; it = query.findIterate();
* try {
* while (it.hasNext()) {
* Customer customer = it.next();
* // do something with customer...
* }
* } finally {
* // close the associated resources
* it.close();
* }
* </pre>
*
* @author rbygrave
*
* @param <T>
* the type of entity bean in the iteration
*/
public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
/**
* Returns <tt>true</tt> if the iteration has more elements.
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
*/
T next();
/**
* Remove is not allowed.
*/
void remove();
/**
* Close the underlying resources held by this iterator.
*/
void close();
}
@@ -1,38 +0,0 @@
package com.avaje.ebean;
/**
* Provides a mechanism for processing a query one bean at a time.
* <p>
* This is useful when the query will return a large number of results and you
* want to process the beans one at a time rather than whole all of the beans in
* memory at once.
* </p>
*
* <pre class="code">
* QueryListener&lt;Order&gt; listener = ...;
*
* Query&lt;Order&gt; query = Ebean.createQuery(Order.class);
*
* // set the listener that will process each order one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list will be empty ... so don't bother assigning it
* query.findList();
* </pre>
*
* @param <T>
* the type of entity bean
*/
public interface QueryListener<T> {
/**
* Process the bean that has just been read.
* <p>
* This bean will not be added to the List Set or Map and nor will it be put
* into the PersistenceContext. This is what makes this a good way to process
* a large result set (which could normally use a lot of memory).
* </p>
*/
public void process(T bean);
}
@@ -1,49 +1,49 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit(new QueryResultVisitor&lt;Customer&gt;() {
*
* public boolean accept(Customer customer) {
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* return true;
* }
* });
* </pre>
*
* @author rbygrave
*
* @param <T>
* the type of entity bean being queried.
*/
public interface QueryResultVisitor<T> {
/**
* Process the bean and return true if you want to continue processing more
* beans. Return false if you want to stop processing further.
*
* @param bean
* the entity bean to process
* @return true to continue processing or false to stop.
*/
public boolean accept(T bean);
}
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit(new QueryResultVisitor&lt;Customer&gt;() {
*
* public boolean accept(Customer customer) {
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* return true;
* }
* });
* </pre>
*
* @author rbygrave
*
* @param <T>
* the type of entity bean being queried.
*/
public interface QueryResultVisitor<T> {
/**
* Process the bean and return true if you want to continue processing more
* beans. Return false if you want to stop processing further.
*
* @param bean
* the entity bean to process
* @return true to continue processing or false to stop.
*/
boolean accept(T bean);
}
File diff suppressed because it is too large Load Diff
+132 -104
View File
@@ -1,104 +1,132 @@
package com.avaje.ebean;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.Sql;
/**
* Builds RawSql instances from a SQL string and column mappings.
* <p>
* Note that RawSql can also be defined in ebean-orm.xml files and be used as a
* named query.
* </p>
*
* @author rbygrave
*
* @see RawSql
*/
public class RawSqlBuilder {
/**
* Special property name assigned to a DB column that should be ignored.
*/
public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
private final Sql sql;
private final ColumnMapping columnMapping;
/**
* 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
* this query.
*/
public static RawSqlBuilder unparsed(String sql) {
Sql s = new Sql(sql);
return new RawSqlBuilder(s, new ColumnMapping());
}
/**
* Return a RawSqlBuilder parsing the sql.
* <p>
* The sql statement will be parsed so that Ebean can determine where it can
* insert additional WHERE or HAVING expressions.
* </p>
* <p>
* Additionally the selected columns are parsed to determine the column
* ordering. This also means additional checks can be made with the column
* mapping - specifically we can check that all columns are mapped and that
* correct column names are entered into the mapping.
* </p>
*/
public static RawSqlBuilder parse(String sql) {
Sql sql2 = DRawSqlParser.parse(sql);
String select = sql2.getPreFrom();
ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
return new RawSqlBuilder(sql2, mapping);
}
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
this.sql = sql;
this.columnMapping = columnMapping;
}
/**
* Set the mapping of a DB Column to a bean property.
* <p>
* For Unparsed SQL the columnMapping MUST be defined in the same order that
* the columns appear in the SQL statement.
* </p>
*
* @param dbColumn
* the DB column that we are mapping to a bean property
* @param propertyName
* the bean property that we are mapping the DB column to.
*/
public RawSqlBuilder columnMapping(String dbColumn, String propertyName) {
columnMapping.columnMapping(dbColumn, propertyName);
return this;
}
/**
* Ignore this DB column. It is not mapped to any bean property.
*/
public RawSqlBuilder columnMappingIgnore(String dbColumn) {
return columnMapping(dbColumn, IGNORE_COLUMN);
}
/**
* Create the immutable RawSql object. Do this after all the column mapping
* has been defined.
*/
public RawSql create() {
return new RawSql(sql, columnMapping.createImmutableCopy());
}
/**
* Return the internal parsed Sql object (for testing).
*/
protected Sql getSql() {
return sql;
}
}
package com.avaje.ebean;
import java.sql.ResultSet;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.Sql;
/**
* Builds RawSql instances from a SQL string and column mappings.
* <p>
* Note that RawSql can also be defined in ebean-orm.xml files and be used as a
* named query.
* </p>
*
* @see RawSql
*/
public class RawSqlBuilder {
/**
* Special property name assigned to a DB column that should be ignored.
*/
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
* this query.
*/
public static RawSqlBuilder unparsed(String sql) {
Sql s = new Sql(sql);
return new RawSqlBuilder(s, new ColumnMapping());
}
/**
* Return a RawSqlBuilder parsing the sql.
* <p>
* The sql statement will be parsed so that Ebean can determine where it can
* insert additional WHERE or HAVING expressions.
* </p>
* <p>
* Additionally the selected columns are parsed to determine the column
* ordering. This also means additional checks can be made with the column
* mapping - specifically we can check that all columns are mapped and that
* correct column names are entered into the mapping.
* </p>
*/
public static RawSqlBuilder parse(String sql) {
Sql sql2 = DRawSqlParser.parse(sql);
String select = sql2.getPreFrom();
ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
return new RawSqlBuilder(sql2, mapping);
}
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
this.sql = sql;
this.columnMapping = columnMapping;
this.resultSet = null;
}
/**
* Set the mapping of a DB Column to a bean property.
* <p>
* For Unparsed SQL the columnMapping MUST be defined in the same order that
* the columns appear in the SQL statement.
* </p>
*
* @param dbColumn
* the DB column that we are mapping to a bean property
* @param propertyName
* the bean property that we are mapping the DB column to.
*/
public RawSqlBuilder columnMapping(String dbColumn, String propertyName) {
columnMapping.columnMapping(dbColumn, propertyName);
return this;
}
/**
* Ignore this DB column. It is not mapped to any bean property.
*/
public RawSqlBuilder columnMappingIgnore(String dbColumn) {
return columnMapping(dbColumn, IGNORE_COLUMN);
}
/**
* Modify any column mappings with the given table alias to have the path prefix.
* <p>
* For example modify all mappings with table alias "c" to have the path prefix "customer".
* </p>
*/
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
columnMapping.tableAliasMapping(tableAlias, path);
return this;
}
/**
* Create the immutable RawSql object. Do this after all the column mapping
* has been defined.
*/
public RawSql create() {
return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
}
/**
* Return the internal parsed Sql object (for testing).
*/
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) {
@@ -1,47 +1,47 @@
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* The SqlFutureList represents the result of a background SQL query execution.
*
* <p>
* It extends the java.util.concurrent.Future.
* </p>
*
* <pre class="code">
* // create a query
* String sql = ... ;
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
*
* // execute the query in a background thread
* SqlFutureList sqlFuture = sqlQuery.findFutureList();
*
* // do something else ... we will sleep
* Thread.sleep(3000);
* System.out.println("end of sleep");
*
* if (!futureList.isDone()){
* // we can cancel the query execution
* futureList.cancel(true);
* }
*
* System.out.println("and... done:"+futureList.isDone());
*
* if (!futureList.isCancelled()){
* // wait for the query to finish and return the list
* List&lt;SqlRow&gt; list = futureList.get();
* System.out.println("list:"+list);
* }
*
* </pre>
*
* @author rob
*
*/
public interface SqlFutureList extends Future<List<SqlRow>> {
public SqlQuery getQuery();
}
package com.avaje.ebean;
import java.util.List;
import java.util.concurrent.Future;
/**
* The SqlFutureList represents the result of a background SQL query execution.
*
* <p>
* It extends the java.util.concurrent.Future.
* </p>
*
* <pre class="code">
* // create a query
* String sql = ... ;
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
*
* // execute the query in a background thread
* SqlFutureList sqlFuture = sqlQuery.findFutureList();
*
* // do something else ... we will sleep
* Thread.sleep(3000);
* System.out.println("end of sleep");
*
* if (!futureList.isDone()){
* // we can cancel the query execution
* futureList.cancel(true);
* }
*
* System.out.println("and... done:"+futureList.isDone());
*
* if (!futureList.isCancelled()){
* // wait for the query to finish and return the list
* List&lt;SqlRow&gt; list = futureList.get();
* System.out.println("list:"+list);
* }
*
* </pre>
*
* @author rob
*
*/
public interface SqlFutureList extends Future<List<SqlRow>> {
SqlQuery getQuery();
}
+151 -150
View File
@@ -1,150 +1,151 @@
package com.avaje.ebean;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Query object for performing native SQL queries that return SqlRow's.
* <p>
* Firstly note that you can use your own sql queries with <em>entity beans</em>
* by using the SqlSelect annotation. This should be your first approach when
* wanting to use your own SQL queries.
* </p>
* <p>
* If ORM Mapping is too tight and constraining for your problem then SqlQuery
* could be a good approach.
* </p>
* <p>
* The returned SqlRow objects are similar to a LinkedHashMap with some type
* conversion support added.
* </p>
*
* <pre class="code">
* // its typically a good idea to use a named query
* // and put the sql in the orm.xml instead of in your code
*
* String sql = &quot;select id, name from customer where name like :name and status_code = :status&quot;;
*
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
* sqlQuery.setParameter(&quot;name&quot;, &quot;Acme%&quot;);
* sqlQuery.setParameter(&quot;status&quot;, &quot;ACTIVE&quot;);
*
* // execute the query returning a List of MapBean objects
* List&lt;SqlRow&gt; list = sqlQuery.findList();
* </pre>
*
*/
public interface SqlQuery extends Serializable {
/**
* Cancel the query if support by the underlying database and driver.
* <p>
* This must be called from a different thread to the one executing the query.
* </p>
*/
public void cancel();
/**
* Execute the query returning a list.
*/
public List<SqlRow> findList();
/**
* Execute the query returning a set.
*/
public Set<SqlRow> findSet();
/**
* Execute the query returning a map.
*/
public Map<?, SqlRow> findMap();
/**
* Execute the query returning a single row or null.
* <p>
* If this query finds 2 or more rows then it will throw a
* PersistenceException.
* </p>
*/
public SqlRow findUnique();
/**
* Execute find list SQL 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).
* </p>
*
* @return a Future object for the list result of the query
*/
public SqlFutureList findFutureList();
/**
* The same as bind for named parameters.
*/
public SqlQuery setParameter(String name, Object value);
/**
* The same as bind for positioned parameters.
*/
public SqlQuery setParameter(int position, Object value);
/**
* Set a listener to process the query on a row by row basis.
* <p>
* It this case the rows are not loaded into the persistence context and
* instead can be processed by the query listener.
* </p>
* <p>
* Use this when you want to process a large query and do not want to hold the
* entire query result in memory.
* </p>
*/
public SqlQuery setListener(SqlQueryListener queryListener);
/**
* Set the index of the first row of the results to return.
*/
public SqlQuery setFirstRow(int firstRow);
/**
* Set the maximum number of query results to return.
*/
public SqlQuery setMaxRows(int maxRows);
/**
* Set the index after which fetching continues in a background thread.
*/
public SqlQuery setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the column to use to determine the keys for a Map.
*/
public SqlQuery setMapKey(String mapKey);
/**
* Set a timeout on this query.
* <p>
* This will typically result in a call to setQueryTimeout() on a
* preparedStatement. If the timeout occurs an exception will be thrown - this
* will be a SQLException wrapped up in a PersistenceException.
* </p>
*
* @param secs
* the query timeout limit in seconds. Zero means there is no limit.
*/
public SqlQuery setTimeout(int secs);
/**
* A hint which for JDBC translates to the Statement.fetchSize().
* <p>
* Gives the JDBC driver a hint as to the number of rows that should be
* fetched from the database when more rows are needed for ResultSet.
* </p>
*/
public SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint);
}
package com.avaje.ebean;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Query object for performing native SQL queries that return SqlRow's.
* <p>
* Firstly note that you can use your own sql queries with <em>entity beans</em>
* by using the SqlSelect annotation. This should be your first approach when
* wanting to use your own SQL queries.
* </p>
* <p>
* If ORM Mapping is too tight and constraining for your problem then SqlQuery
* could be a good approach.
* </p>
* <p>
* The returned SqlRow objects are similar to a LinkedHashMap with some type
* conversion support added.
* </p>
*
* <pre class="code">
* // its typically a good idea to use a named query
* // and put the sql in the orm.xml instead of in your code
*
* String sql = &quot;select id, name from customer where name like :name and status_code = :status&quot;;
*
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
* sqlQuery.setParameter(&quot;name&quot;, &quot;Acme%&quot;);
* sqlQuery.setParameter(&quot;status&quot;, &quot;ACTIVE&quot;);
*
* // execute the query returning a List of MapBean objects
* List&lt;SqlRow&gt; list = sqlQuery.findList();
* </pre>
*
*/
public interface SqlQuery extends Serializable {
/**
* Cancel the query if support by the underlying database and driver.
* <p>
* This must be called from a different thread to the one executing the query.
* </p>
*/
void cancel();
/**
* Execute the query returning a list.
*/
List<SqlRow> findList();
/**
* Execute the query returning a set.
*/
Set<SqlRow> findSet();
/**
* Execute the query returning a map.
*/
Map<?, SqlRow> findMap();
/**
* Execute the query returning a single row or null.
* <p>
* If this query finds 2 or more rows then it will throw a
* PersistenceException.
* </p>
*/
SqlRow findUnique();
/**
* Execute find list SQL 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).
* </p>
*
* @return a Future object for the list result of the query
* @deprecated
*/
SqlFutureList findFutureList();
/**
* The same as bind for named parameters.
*/
SqlQuery setParameter(String name, Object value);
/**
* The same as bind for positioned parameters.
*/
SqlQuery setParameter(int position, Object value);
/**
* Set a listener to process the query on a row by row basis.
* <p>
* It this case the rows are not loaded into the persistence context and
* instead can be processed by the query listener.
* </p>
* <p>
* Use this when you want to process a large query and do not want to hold the
* entire query result in memory.
* </p>
*/
SqlQuery setListener(SqlQueryListener queryListener);
/**
* Set the index of the first row of the results to return.
*/
SqlQuery setFirstRow(int firstRow);
/**
* Set the maximum number of query results to return.
*/
SqlQuery setMaxRows(int maxRows);
/**
* Set the index after which fetching continues in a background thread.
*/
SqlQuery setBackgroundFetchAfter(int backgroundFetchAfter);
/**
* Set the column to use to determine the keys for a Map.
*/
SqlQuery setMapKey(String mapKey);
/**
* Set a timeout on this query.
* <p>
* This will typically result in a call to setQueryTimeout() on a
* preparedStatement. If the timeout occurs an exception will be thrown - this
* will be a SQLException wrapped up in a PersistenceException.
* </p>
*
* @param secs
* the query timeout limit in seconds. Zero means there is no limit.
*/
SqlQuery setTimeout(int secs);
/**
* A hint which for JDBC translates to the Statement.fetchSize().
* <p>
* Gives the JDBC driver a hint as to the number of rows that should be
* fetched from the database when more rows are needed for ResultSet.
* </p>
*/
SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint);
}
@@ -1,33 +1,33 @@
package com.avaje.ebean;
/**
* Provides a mechanism for processing a SqlQuery one SqlRow at a time.
* <p>
* This is useful when the query will return a large number of results and you
* want to process the beans one at a time rather than have all of the beans in
* memory at once.
* </p>
*
* <pre class="code">
* SqlQueryListener listener = ...;
*
* SqlQuery query = Ebean.createSqlQuery(&quot;my.large.query&quot;);
*
* // set the listener that will process each row one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list will be empty ... so don't bother assigning it...
* query.findList();
* </pre>
*/
public interface SqlQueryListener {
/**
* Process the bean that has just been read.
* <p>
* Note this bean will not be added to the List Set or Map.
* </p>
*/
public void process(SqlRow bean);
}
package com.avaje.ebean;
/**
* Provides a mechanism for processing a SqlQuery one SqlRow at a time.
* <p>
* This is useful when the query will return a large number of results and you
* want to process the beans one at a time rather than have all of the beans in
* memory at once.
* </p>
*
* <pre class="code">
* SqlQueryListener listener = ...;
*
* SqlQuery query = Ebean.createSqlQuery(&quot;my.large.query&quot;);
*
* // set the listener that will process each row one at a time
* query.setListener(listener);
*
* // execute the query. Note that the returned
* // list will be empty ... so don't bother assigning it...
* query.findList();
* </pre>
*/
public interface SqlQueryListener {
/**
* Process the bean that has just been read.
* <p>
* Note this bean will not be added to the List Set or Map.
* </p>
*/
void process(SqlRow bean);
}
+167 -167
View File
@@ -1,168 +1,168 @@
package com.avaje.ebean;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Used to return raw SQL query results.
* <p>
* Refer to {@link SqlQuery} for examples.
* </p>
* <p>
* There are convenience methods such as getInteger(), getBigDecimal() etc. The
* reason for these methods is that the values put into this map often come
* straight from the JDBC resultSet. Depending on the JDBC driver it may put a
* different type into a given property. For example an Integer, BigDecimal,
* Double could all be put into a property depending on the JDBC driver used.
* These convenience methods automatically convert the value as required
* returning the type you expect.
* </p>
*/
public interface SqlRow extends Serializable, Map<String, Object> {
/**
* Return the property names (String).
* <p>
* Internally this uses LinkedHashMap and so the order of the property names
* should be predictable and ordered by the use of LinkedHashMap.
* </p>
*/
public Iterator<String> keys();
/**
* Remove a property from the map. Returns the value of the removed property.
*/
public Object remove(Object name);
/**
* Return a property value by its name.
*/
public Object get(Object name);
/**
* Set a value to a property.
*/
public Object put(String name, Object value);
/**
* Exactly the same as the put method.
* <p>
* I added this method because it seems more bean like to have get and set
* methods.
* </p>
*/
public Object set(String name, Object value);
/**
* Return a property as a Boolean.
*/
public Boolean getBoolean(String name);
/**
* Return a property as a UUID.
*/
public UUID getUUID(String name);
/**
* Return a property as an Integer.
*/
public Integer getInteger(String name);
/**
* Return a property value as a BigDecimal.
*/
public BigDecimal getBigDecimal(String name);
/**
* Return a property value as a Long.
*/
public Long getLong(String name);
/**
* Return the property value as a Double.
*/
public Double getDouble(String name);
/**
* Return the property value as a Float.
*/
public Float getFloat(String name);
/**
* Return a property as a String.
*/
public String getString(String name);
/**
* Return the property as a java.util.Date.
*/
public java.util.Date getUtilDate(String name);
/**
* Return the property as a sql date.
*/
public Date getDate(String name);
/**
* Return the property as a sql timestamp.
*/
public Timestamp getTimestamp(String name);
/**
* String description of the underlying map.
*/
public String toString();
/**
* Clear the map.
*/
public void clear();
/**
* Returns true if the map contains the property.
*/
public boolean containsKey(Object key);
/**
* Returns true if the map contains the value.
*/
public boolean containsValue(Object value);
/**
* Returns the entrySet of the map.
*/
public Set<Map.Entry<String, Object>> entrySet();
/**
* Returns true if the map is empty.
*/
public boolean isEmpty();
/**
* Returns the key set of the map.
*/
public Set<String> keySet();
/**
* Put all the values from t into this map.
*/
public void putAll(Map<? extends String, ? extends Object> t);
/**
* Return the size of the map.
*/
public int size();
/**
* Return the values from this map.
*/
public Collection<Object> values();
package com.avaje.ebean;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Used to return raw SQL query results.
* <p>
* Refer to {@link SqlQuery} for examples.
* </p>
* <p>
* There are convenience methods such as getInteger(), getBigDecimal() etc. The
* reason for these methods is that the values put into this map often come
* straight from the JDBC resultSet. Depending on the JDBC driver it may put a
* different type into a given property. For example an Integer, BigDecimal,
* Double could all be put into a property depending on the JDBC driver used.
* These convenience methods automatically convert the value as required
* returning the type you expect.
* </p>
*/
public interface SqlRow extends Serializable, Map<String, Object> {
/**
* Return the property names (String).
* <p>
* Internally this uses LinkedHashMap and so the order of the property names
* should be predictable and ordered by the use of LinkedHashMap.
* </p>
*/
Iterator<String> keys();
/**
* Remove a property from the map. Returns the value of the removed property.
*/
Object remove(Object name);
/**
* Return a property value by its name.
*/
Object get(Object name);
/**
* Set a value to a property.
*/
Object put(String name, Object value);
/**
* Exactly the same as the put method.
* <p>
* I added this method because it seems more bean like to have get and set
* methods.
* </p>
*/
Object set(String name, Object value);
/**
* Return a property as a Boolean.
*/
Boolean getBoolean(String name);
/**
* Return a property as a UUID.
*/
UUID getUUID(String name);
/**
* Return a property as an Integer.
*/
Integer getInteger(String name);
/**
* Return a property value as a BigDecimal.
*/
BigDecimal getBigDecimal(String name);
/**
* Return a property value as a Long.
*/
Long getLong(String name);
/**
* Return the property value as a Double.
*/
Double getDouble(String name);
/**
* Return the property value as a Float.
*/
Float getFloat(String name);
/**
* Return a property as a String.
*/
String getString(String name);
/**
* Return the property as a java.util.Date.
*/
java.util.Date getUtilDate(String name);
/**
* Return the property as a sql date.
*/
Date getDate(String name);
/**
* Return the property as a sql timestamp.
*/
Timestamp getTimestamp(String name);
/**
* String description of the underlying map.
*/
String toString();
/**
* Clear the map.
*/
void clear();
/**
* Returns true if the map contains the property.
*/
boolean containsKey(Object key);
/**
* Returns true if the map contains the value.
*/
boolean containsValue(Object value);
/**
* Returns the entrySet of the map.
*/
Set<Map.Entry<String, Object>> entrySet();
/**
* Returns true if the map is empty.
*/
boolean isEmpty();
/**
* Returns the key set of the map.
*/
Set<String> keySet();
/**
* Put all the values from t into this map.
*/
void putAll(Map<? extends String, ? extends Object> t);
/**
* Return the size of the map.
*/
int size();
/**
* Return the values from this map.
*/
Collection<Object> values();
}
+145 -140
View File
@@ -1,141 +1,146 @@
package com.avaje.ebean;
/**
* A SqlUpdate for executing insert update or delete statements.
* <p>
* Provides a simple way to execute raw SQL insert update or delete statements
* without having to resort to JDBC.
* </p>
* <p>
* Supports the use of positioned or named parameters and can automatically
* notify Ebean of the table modified so that Ebean can maintain its cache.
* </p>
* <p>
* Note that {@link #setAutoTableMod(boolean)} and
* Ebean#externalModification(String, boolean, boolean, boolean)} can be to
* notify Ebean of external changes and enable Ebean to maintain it's "L2"
* server cache.
* </p>
*
* <pre class="code">
* // example that uses 'named' parameters
* String s = &quot;UPDATE f_topic set post_count = :count where id = :id&quot;
* SqlUpdate update = Ebean.createSqlUpdate(s);
* update.setParameter(&quot;id&quot;, 1);
* update.setParameter(&quot;count&quot;, 50);
*
* int modifiedCount = Ebean.execute(update);
*
* String msg = &quot;There where &quot; + modifiedCount + &quot;rows updated&quot;
* </pre>
*
* @see Update
* @see SqlQuery
* @see CallableSql
*/
public interface SqlUpdate {
/**
* Execute the update returning the number of rows modified.
* <p>
* After you have executed the SqlUpdate you can bind new variables using
* {@link #setParameter(String, Object)} etc and then execute the SqlUpdate
* again.
* </p>
* <p>
* For JDBC batch processing refer to
* {@link Transaction#setBatchMode(boolean)} and
* {@link Transaction#setBatchSize(int)}.
* </p>
*
* @see com.avaje.ebean.Ebean#execute(SqlUpdate)
*/
public int execute();
/**
* Return true if eBean should automatically deduce the table modification
* information and process it.
* <p>
* If this is true then cache invalidation and text index management are aware
* of the modification.
* </p>
*/
public boolean isAutoTableMod();
/**
* Set this to false if you don't want eBean to automatically deduce the table
* modification information and process it.
* <p>
* Set this to false if you don't want any cache invalidation or text index
* management to occur. You may do this when say you update only one column
* and you know that it is not important for cached objects or text indexes.
* </p>
*/
public SqlUpdate setAutoTableMod(boolean isAutoTableMod);
/**
* Return the label that can be seen in the transaction logs.
*/
public String getLabel();
/**
* Set a descriptive text that can be put into the transaction log.
* <p>
* Useful when identifying the statement in the transaction log.
* </p>
*/
public SqlUpdate setLabel(String label);
/**
* Return the sql statement.
*/
public String getSql();
/**
* Return the timeout used to execute this statement.
*/
public int getTimeout();
/**
* Set the timeout in seconds. Zero implies no limit.
* <p>
* This will set the query timeout on the underlying PreparedStatement. If the
* timeout expires a SQLException will be throw and wrapped in a
* PersistenceException.
* </p>
*/
public SqlUpdate setTimeout(int secs);
/**
* Set a parameter via its index position.
*/
public SqlUpdate setParameter(int position, Object value);
/**
* Set a null parameter via its index position. Exactly the same as
* {@link #setNull(int, int)}.
*/
public SqlUpdate setNull(int position, int jdbcType);
/**
* Set a null valued parameter using its index position.
*/
public SqlUpdate setNullParameter(int position, int jdbcType);
/**
* Set a named parameter value.
*/
public SqlUpdate setParameter(String name, Object param);
/**
* Set a named parameter that has a null value. Exactly the same as
* {@link #setNullParameter(String, int)}.
*/
public SqlUpdate setNull(String name, int jdbcType);
/**
* Set a named parameter that has a null value.
*/
public SqlUpdate setNullParameter(String name, int jdbcType);
package com.avaje.ebean;
/**
* A SqlUpdate for executing insert update or delete statements.
* <p>
* Provides a simple way to execute raw SQL insert update or delete statements
* without having to resort to JDBC.
* </p>
* <p>
* Supports the use of positioned or named parameters and can automatically
* notify Ebean of the table modified so that Ebean can maintain its cache.
* </p>
* <p>
* Note that {@link #setAutoTableMod(boolean)} and
* Ebean#externalModification(String, boolean, boolean, boolean)} can be to
* notify Ebean of external changes and enable Ebean to maintain it's "L2"
* server cache.
* </p>
*
* <pre class="code">
* // example that uses 'named' parameters
* String s = &quot;UPDATE f_topic set post_count = :count where id = :id&quot;
* SqlUpdate update = Ebean.createSqlUpdate(s);
* update.setParameter(&quot;id&quot;, 1);
* update.setParameter(&quot;count&quot;, 50);
*
* int modifiedCount = Ebean.execute(update);
*
* String msg = &quot;There where &quot; + modifiedCount + &quot;rows updated&quot;
* </pre>
*
* @see Update
* @see SqlQuery
* @see CallableSql
*/
public interface SqlUpdate {
/**
* Execute the update returning the number of rows modified.
* <p>
* After you have executed the SqlUpdate you can bind new variables using
* {@link #setParameter(String, Object)} etc and then execute the SqlUpdate
* again.
* </p>
* <p>
* For JDBC batch processing refer to
* {@link Transaction#setBatchMode(boolean)} and
* {@link Transaction#setBatchSize(int)}.
* </p>
*
* @see com.avaje.ebean.Ebean#execute(SqlUpdate)
*/
int execute();
/**
* Return true if eBean should automatically deduce the table modification
* information and process it.
* <p>
* If this is true then cache invalidation and text index management are aware
* of the modification.
* </p>
*/
boolean isAutoTableMod();
/**
* Set this to false if you don't want eBean to automatically deduce the table
* modification information and process it.
* <p>
* Set this to false if you don't want any cache invalidation or text index
* management to occur. You may do this when say you update only one column
* and you know that it is not important for cached objects or text indexes.
* </p>
*/
SqlUpdate setAutoTableMod(boolean isAutoTableMod);
/**
* Return the label that can be seen in the transaction logs.
*/
String getLabel();
/**
* Set a descriptive text that can be put into the transaction log.
* <p>
* Useful when identifying the statement in the transaction log.
* </p>
*/
SqlUpdate setLabel(String label);
/**
* Return the sql statement.
*/
String getSql();
/**
* Return the generated sql that has named parameters converted to positioned parameters.
*/
String getGeneratedSql();
/**
* Return the timeout used to execute this statement.
*/
int getTimeout();
/**
* Set the timeout in seconds. Zero implies no limit.
* <p>
* This will set the query timeout on the underlying PreparedStatement. If the
* timeout expires a SQLException will be throw and wrapped in a
* PersistenceException.
* </p>
*/
SqlUpdate setTimeout(int secs);
/**
* Set a parameter via its index position.
*/
SqlUpdate setParameter(int position, Object value);
/**
* Set a null parameter via its index position. Exactly the same as
* {@link #setNull(int, int)}.
*/
SqlUpdate setNull(int position, int jdbcType);
/**
* Set a null valued parameter using its index position.
*/
SqlUpdate setNullParameter(int position, int jdbcType);
/**
* Set a named parameter value.
*/
SqlUpdate setParameter(String name, Object param);
/**
* Set a named parameter that has a null value. Exactly the same as
* {@link #setNullParameter(String, int)}.
*/
SqlUpdate setNull(String name, int jdbcType);
/**
* Set a named parameter that has a null value.
*/
SqlUpdate setNullParameter(String name, int jdbcType);
}
+329 -291
View File
@@ -1,291 +1,329 @@
package com.avaje.ebean;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import java.sql.Connection;
/**
* The Transaction object. Typically representing a JDBC or JTA transaction.
*/
public interface Transaction {
/**
* Read Committed transaction isolation. Same as
* java.sql.Connection.TRANSACTION_READ_COMMITTED.
*/
public static final int READ_COMMITTED = java.sql.Connection.TRANSACTION_READ_COMMITTED;
/**
* Read Uncommitted transaction isolation. Same as
* java.sql.Connection.TRANSACTION_READ_UNCOMMITTED.
*/
public static final int READ_UNCOMMITTED = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* Repeatable read transaction isolation. Same as
* java.sql.Connection.TRANSACTION_REPEATABLE_READ.
*/
public static final int REPEATABLE_READ = java.sql.Connection.TRANSACTION_REPEATABLE_READ;
/**
* Serializable transaction isolation. Same as
* java.sql.Connection.TRANSACTION_SERIALIZABLE.
*/
public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
// /**
// * You can call this after the transaction commit to wait for any changes to
// * Lucene indexes to be made and committed. Note that generally this could
// * be some time so it is not expected that you call this method generally.
// */
// public void waitForIndexUpdates();
/**
* Return true if this transaction is read only.
*/
public boolean isReadOnly();
/**
* Set whether this transaction should be readOnly.
*/
public void setReadOnly(boolean readOnly);
/**
* Commit the transaction.
*/
public void commit() throws RollbackException;
/**
* Rollback the transaction.
*/
public void rollback() throws PersistenceException;
/**
* Rollback the transaction specifying a throwable that caused the rollback to
* occur.
* <p>
* If you are using transaction logging this will log the throwable in the
* transaction logs.
* </p>
*/
public void rollback(Throwable e) throws PersistenceException;
/**
* If the transaction is active then perform rollback. Otherwise do nothing.
*/
public void end() throws PersistenceException;
/**
* Return true if the transaction is active.
*/
public boolean isActive();
/**
* Explicitly turn off or on the cascading nature of save() and delete(). This
* gives the developer exact control over what beans are saved and deleted
* rather than Ebean cascading detecting 'dirty/modified' beans etc.
* <p>
* This is useful if you can getting back entity beans from a layer of code
* (potentially remote) and you prefer to have exact control.
* </p>
* <p>
* This may also be useful if you are using jdbc batching with jdbc drivers
* that do not support getGeneratedKeys.
* </p>
*/
public void setPersistCascade(boolean persistCascade);
/**
* Turn on or off statement batching. Statement batching can be transparent
* for drivers and databases that support getGeneratedKeys. Otherwise you may
* wish to specifically control when batching is used via this method.
* <p>
* Refer to <code>java.sql.PreparedStatement.addBatch();</code>
* <p>
* Note that you may also wish to use the setPersistCascade method to stop
* save and delete cascade behaviour. You may do this to have full control
* over the order of execution rather than the normal cascading fashion.
* </p>
* <p>
* Note that the <em>execution order</em> in batch mode may be different from
* non batch mode execution order. Also note that <em>insert behaviour</em>
* may be different depending on the JDBC driver and its support for
* getGeneratedKeys. That is, for JDBC drivers that do not support
* getGeneratedKeys you may not get back the generated IDs (used for inserting
* associated detail beans etc).
* </p>
* <p>
* Calls to save(), delete(), insert() and execute() all support batch
* processing. This includes normal beans, MapBean, CallableSql and UpdateSql.
* </p>
* <p>
* The flushing of the batched statements is automatic but you can call
* batchFlush when you like. Note that flushing occurs when a query is
* executed or when you mix UpdateSql and CallableSql with save and delete of
* beans.
* </p>
* <p>
* Example: batch processing executing every 3 rows
* </p>
*
* <pre class="code">
* String data = &quot;This is a simple test of the batch processing&quot;
* + &quot; mode and the transaction execute batch method&quot;;
*
* String[] da = data.split(&quot; &quot;);
*
* String sql = &quot;{call sp_t3(?,?)}&quot;;
*
* CallableSql cs = new CallableSql(sql);
* cs.registerOut(2, Types.INTEGER);
*
* // (optional) inform eBean this stored procedure
* // inserts into a table called sp_test
* cs.addModification(&quot;sp_test&quot;, true, false, false);
*
* Transaction t = Ebean.beginTransaction();
* t.setBatchMode(true);
* t.setBatchSize(3);
* try {
* for (int i = 0; i &lt; da.length;) {
*
* cs.setParameter(1, da[i]);
* Ebean.execute(cs);
* }
*
* // NB: commit implicitly flushes
* Ebean.commitTransaction();
*
* } finally {
* Ebean.endTransaction();
* }
* </pre>
*
*/
public void setBatchMode(boolean useBatch);
/**
* Specify the number of statements before a batch is flushed automatically.
*/
public void setBatchSize(int batchSize);
/**
* Specify if you want batched inserts to use getGeneratedKeys.
* <p>
* By default batched inserts will try to use getGeneratedKeys if it is
* supported by the underlying jdbc driver and database.
* </p>
* <p>
* You may want to turn getGeneratedKeys off when you are inserting a large
* number of objects and you don't care about getting back the ids.
* </p>
*/
public void setBatchGetGeneratedKeys(boolean getGeneratedKeys);
/**
* By default when mixing UpdateSql (or CallableSql) with Beans the batch is
* automatically flushed when you change (between persisting beans and
* executing UpdateSql or CallableSql).
* <p>
* If you want to execute both WITHOUT having the batch automatically flush
* you need to call this with batchFlushOnMixed = false.
* </p>
* <p>
* Note that UpdateSql and CallableSql are ALWAYS executed first (before the
* beans are executed). This is because the UpdateSql and CallableSql have
* already been bound to their PreparedStatements. The beans on the other hand
* have a 2 step process (delayed binding).
* </p>
*/
public void setBatchFlushOnMixed(boolean batchFlushOnMixed);
/**
* By default executing a query will automatically flush any batched
* statements (persisted beans, executed UpdateSql etc).
* <p>
* Calling this method with batchFlushOnQuery = false means that you can
* execute a query and the batch will not be automatically flushed.
* </p>
*/
public void setBatchFlushOnQuery(boolean batchFlushOnQuery);
/**
* Return true if the batch (of persisted beans or executed UpdateSql etc)
* should be flushed prior to executing a query.
* <p>
* The default is for this to be true.
* </p>
*/
public boolean isBatchFlushOnQuery();
/**
* The batch will be flushing automatically but you can use this to explicitly
* flush the batch if you like.
* <p>
* Flushing occurs automatically when:
* </p>
* <ul>
* <li>the batch size is reached</li>
* <li>A query is executed on the same transaction</li>
* <li>UpdateSql or CallableSql are mixed with bean save and delete</li>
* </ul>
*/
public void flushBatch() throws PersistenceException, OptimisticLockException;
/**
* Deprecated in favour of {@link #flushBatch()}.
* <p>
* Exactly the same as flushBatch. Deprecated as a name change.
* </p>
*
* @deprecated Please use flushBatch
*/
public void batchFlush() throws PersistenceException, OptimisticLockException;
/**
* Return the underlying Connection object.
* <p>
* Useful where a Developer wishes to use the JDBC API directly. Note that the
* commit() rollback() and end() methods on the Transaction should still be
* used. Calling these methods on the Connection would be a big no no unless
* you know what you are doing.
* </p>
* <p>
* Examples of when a developer may wish to use the connection directly are:
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
* </p>
*/
public Connection getConnection();
/**
* Add table modification information to the TransactionEvent.
* <p>
* Use this in conjunction with getConnection() and raw JDBC.
* </p>
* <p>
* This effectively informs Ebean of the data that has been changed by the
* transaction and this information is normally automatically handled by Ebean
* when you save entity beans or use UpdateSql etc.
* </p>
* <p>
* If you use raw JDBC then you can use this method to inform Ebean for the
* tables that have been modified. Ebean uses this information to keep its
* caches in synch and maintain text indexes.
* </p>
*/
public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
/**
* Add an arbitrary user object to the transaction. The objects added have no
* impact on any internals of ebena and are solely meant as a convenient
* method push user information to e.g. the
* {@link com.avaje.ebean.event.TransactionEventListener}.
*/
public void putUserObject(String name, Object value);
/**
* Get an object added with {@link #putUserObject(String, Object)}.
*/
public Object getUserObject(String name);
}
package com.avaje.ebean;
import com.avaje.ebean.config.PersistBatch;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import java.io.Closeable;
import java.sql.Connection;
/**
* The Transaction object. Typically representing a JDBC or JTA transaction.
*/
public interface Transaction extends Closeable {
/**
* Read Committed transaction isolation. Same as
* java.sql.Connection.TRANSACTION_READ_COMMITTED.
*/
int READ_COMMITTED = java.sql.Connection.TRANSACTION_READ_COMMITTED;
/**
* Read Uncommitted transaction isolation. Same as
* java.sql.Connection.TRANSACTION_READ_UNCOMMITTED.
*/
int READ_UNCOMMITTED = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* Repeatable read transaction isolation. Same as
* java.sql.Connection.TRANSACTION_REPEATABLE_READ.
*/
int REPEATABLE_READ = java.sql.Connection.TRANSACTION_REPEATABLE_READ;
/**
* Serializable transaction isolation. Same as
* java.sql.Connection.TRANSACTION_SERIALIZABLE.
*/
int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
/**
* Register a TransactionCallback with this transaction.
*/
void register(TransactionCallback callback);
/**
* Return true if this transaction is read only.
*/
boolean isReadOnly();
/**
* Set whether this transaction should be readOnly.
*/
void setReadOnly(boolean readOnly);
/**
* Commit the transaction.
*/
void commit() throws RollbackException;
/**
* Rollback the transaction.
*/
void rollback() throws PersistenceException;
/**
* Rollback the transaction specifying a throwable that caused the rollback to
* occur.
* <p>
* If you are using transaction logging this will log the throwable in the
* transaction logs.
* </p>
*/
void rollback(Throwable e) throws PersistenceException;
/**
* If the transaction is active then perform rollback. Otherwise do nothing.
*/
void end() throws PersistenceException;
/**
* Return true if the transaction is active.
*/
boolean isActive();
/**
* Explicitly turn off or on the cascading nature of save() and delete(). This
* gives the developer exact control over what beans are saved and deleted
* rather than Ebean cascading detecting 'dirty/modified' beans etc.
* <p>
* This is useful if you can getting back entity beans from a layer of code
* (potentially remote) and you prefer to have exact control.
* </p>
* <p>
* This may also be useful if you are using jdbc batching with jdbc drivers
* that do not support getGeneratedKeys.
* </p>
*/
void setPersistCascade(boolean persistCascade);
/**
* Turn on or off statement batching. Statement batching can be transparent
* for drivers and databases that support getGeneratedKeys. Otherwise you may
* wish to specifically control when batching is used via this method.
* <p>
* Refer to <code>java.sql.PreparedStatement.addBatch();</code>
* <p>
* Note that you may also wish to use the setPersistCascade method to stop
* save and delete cascade behaviour. You may do this to have full control
* over the order of execution rather than the normal cascading fashion.
* </p>
* <p>
* Note that the <em>execution order</em> in batch mode may be different from
* non batch mode execution order. Also note that <em>insert behaviour</em>
* may be different depending on the JDBC driver and its support for
* getGeneratedKeys. That is, for JDBC drivers that do not support
* getGeneratedKeys you may not get back the generated IDs (used for inserting
* associated detail beans etc).
* </p>
* <p>
* Calls to save(), delete(), insert() and execute() all support batch
* processing. This includes normal beans, MapBean, CallableSql and UpdateSql.
* </p>
* <p>
* The flushing of the batched statements is automatic but you can call
* batchFlush when you like. Note that flushing occurs when a query is
* executed or when you mix UpdateSql and CallableSql with save and delete of
* beans.
* </p>
* <p>
* Example: batch processing executing every 3 rows
* </p>
*
* <pre>{@code
*
* String data = "This is a simple test of the batch processing"
* + " mode and the transaction execute batch method";
*
* String[] da = data.split(" ");
*
* String sql = "{call sp_t3(?,?)}";
*
* CallableSql cs = new CallableSql(sql);
* cs.registerOut(2, Types.INTEGER);
*
* // (optional) inform eBean this stored procedure
* // inserts into a table called sp_test
* cs.addModification("sp_test", true, false, false);
*
* Transaction txn = ebeanServer.beginTransaction();
* txn.setBatchMode(true);
* txn.setBatchSize(3);
* try {
* for (int i = 0; i < da.length;) {
* cs.setParameter(1, da[i]);
* ebeanServer.execute(cs);
* }
*
* // NB: commit implicitly flushes
* txn.commit();
*
* } finally {
* txn.end();
* }
*
* }</pre>
*
*/
void setBatchMode(boolean useBatch);
/**
* The JDBC batch mode to use for this transaction.
* <p>
* If this is NONE then JDBC batch can still be used for each request - save(), insert(), update() or delete()
* and this would be useful if the request cascades to detail beans.
* </p>
*
* @param persistBatchMode the batch mode to use for this transaction
*
* @see com.avaje.ebean.config.ServerConfig#setPersistBatch(com.avaje.ebean.config.PersistBatch)
*/
void setBatch(PersistBatch persistBatchMode);
/**
* Return the batch mode at the transaction level.
*/
PersistBatch getBatch();
/**
* Set the JDBC batch mode to use for a save() or delete() request.
* <p>
* This only takes effect when batch mode on the transaction has not already meant that
* JDBC batch mode is being used.
* </p>
* <p>
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
* </p>
*
* @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete()
*
* @see com.avaje.ebean.config.ServerConfig#setPersistBatchOnCascade(com.avaje.ebean.config.PersistBatch)
*/
void setBatchOnCascade(PersistBatch batchOnCascadeMode);
/**
* Return the batch mode at the request level (for each save(), insert(), update() or delete()).
*/
PersistBatch getBatchOnCascade();
/**
* Specify the number of statements before a batch is flushed automatically.
*/
void setBatchSize(int batchSize);
/**
* Return the current batch size.
*/
int getBatchSize();
/**
* Specify if you want batched inserts to use getGeneratedKeys.
* <p>
* By default batched inserts will try to use getGeneratedKeys if it is
* supported by the underlying jdbc driver and database.
* </p>
* <p>
* You may want to turn getGeneratedKeys off when you are inserting a large
* number of objects and you don't care about getting back the ids.
* </p>
*/
void setBatchGetGeneratedKeys(boolean getGeneratedKeys);
/**
* By default when mixing UpdateSql (or CallableSql) with Beans the batch is
* automatically flushed when you change (between persisting beans and
* executing UpdateSql or CallableSql).
* <p>
* If you want to execute both WITHOUT having the batch automatically flush
* you need to call this with batchFlushOnMixed = false.
* </p>
* <p>
* Note that UpdateSql and CallableSql are ALWAYS executed first (before the
* beans are executed). This is because the UpdateSql and CallableSql have
* already been bound to their PreparedStatements. The beans on the other hand
* have a 2 step process (delayed binding).
* </p>
*/
void setBatchFlushOnMixed(boolean batchFlushOnMixed);
/**
* By default executing a query will automatically flush any batched
* statements (persisted beans, executed UpdateSql etc).
* <p>
* Calling this method with batchFlushOnQuery = false means that you can
* execute a query and the batch will not be automatically flushed.
* </p>
*/
void setBatchFlushOnQuery(boolean batchFlushOnQuery);
/**
* Return true if the batch (of persisted beans or executed UpdateSql etc)
* should be flushed prior to executing a query.
* <p>
* The default is for this to be true.
* </p>
*/
boolean isBatchFlushOnQuery();
/**
* The batch will be flushing automatically but you can use this to explicitly
* flush the batch if you like.
* <p>
* Flushing occurs automatically when:
* </p>
* <ul>
* <li>the batch size is reached</li>
* <li>A query is executed on the same transaction</li>
* <li>UpdateSql or CallableSql are mixed with bean save and delete</li>
* <li>Transaction commit occurs</li>
* </ul>
*/
void flushBatch() throws PersistenceException, OptimisticLockException;
/**
* Return the underlying Connection object.
* <p>
* Useful where a Developer wishes to use the JDBC API directly. Note that the
* commit() rollback() and end() methods on the Transaction should still be
* used. Calling these methods on the Connection would be a big no no unless
* you know what you are doing.
* </p>
* <p>
* Examples of when a developer may wish to use the connection directly are:
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
* </p>
*/
Connection getConnection();
/**
* Add table modification information to the TransactionEvent.
* <p>
* Use this in conjunction with getConnection() and raw JDBC.
* </p>
* <p>
* This effectively informs Ebean of the data that has been changed by the
* transaction and this information is normally automatically handled by Ebean
* when you save entity beans or use UpdateSql etc.
* </p>
* <p>
* If you use raw JDBC then you can use this method to inform Ebean for the
* tables that have been modified. Ebean uses this information to keep its
* caches in synch and maintain text indexes.
* </p>
*/
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
/**
* Add an arbitrary user object to the transaction. The objects added have no
* impact on any internals of ebena and are solely meant as a convenient
* method push user information to e.g. the
* {@link com.avaje.ebean.event.TransactionEventListener}.
*/
void putUserObject(String name, Object value);
/**
* Get an object added with {@link #putUserObject(String, Object)}.
*/
Object getUserObject(String name);
}
@@ -0,0 +1,34 @@
package com.avaje.ebean;
/**
* Provides a callback that can be registered with a Transaction.
* <p/>
* The callback methods are called just prior to and after the transaction performs a commit or rollback.
* <p/>
* A typical use of TransactionCallback would be to clean up non-transactional resources like files. For example,
* when processing files on postCommit/postRollback clean up the associated files. As another example when
* on postCommit of a delete remove associated resources from the file system or remote service.
*/
public interface TransactionCallback {
/**
* Perform processing just prior to the transaction commit.
*/
void preCommit();
/**
* Perform processing just after the transaction commit.
*/
void postCommit();
/**
* Perform processing just prior to the transaction rollback.
*/
void preRollback();
/**
* Perform processing just after the transaction rollback.
*/
void postRollback();
}
@@ -0,0 +1,42 @@
package com.avaje.ebean;
/**
* Adapter that can be extended for easier implementation of TransactionCallback.
* <p/>
* Provides 'no operation' implementation for each of the TransactionCallback methods. It is expected that this
* class is extended and override the methods you need to.
*/
public abstract class TransactionCallbackAdapter implements TransactionCallback {
/**
* Perform processing just prior to the transaction commit.
*/
@Override
public void preCommit() {
// do nothing - override as necessary
}
/**
* Perform processing just after the transaction commit.
*/
@Override
public void postCommit() {
// do nothing - override as necessary
}
/**
* Perform processing just prior to the transaction rollback.
*/
@Override
public void preRollback() {
// do nothing - override as necessary
}
/**
* Perform processing just after the transaction rollback.
*/
@Override
public void postRollback() {
// do nothing - override as necessary
}
}
+45 -45
View File
@@ -1,45 +1,45 @@
package com.avaje.ebean;
/**
* Execute a TxCallable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#execute(TxCallable)} method.
* </p>
* <p>
* Note that this is basically the same as TxRunnable except that it returns an
* Object (and you specify the return type via generics).
* </p>
* <p>
* See also {@link TxRunnable}.
* </p>
*
* <pre class="code">
* Ebean.execute(new TxCallable&lt;String&gt;() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName(&quot;u1 mod&quot;);
* u2.setName(&quot;u2 mod&quot;);
*
* Ebean.save(u1);
* Ebean.save(u2);
*
* return u1.getEmail();
* }
* });
* </pre>
*
* @see TxRunnable
*/
public interface TxCallable<T> {
/**
* Execute the method within a transaction scope returning the result.
* <p>
* If you do not want to return a result you should look to use TxRunnable
* instead.
* </p>
*/
public T call();
}
package com.avaje.ebean;
/**
* Execute a TxCallable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#execute(TxCallable)} method.
* </p>
* <p>
* Note that this is basically the same as TxRunnable except that it returns an
* Object (and you specify the return type via generics).
* </p>
* <p>
* See also {@link TxRunnable}.
* </p>
*
* <pre class="code">
* Ebean.execute(new TxCallable&lt;String&gt;() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName(&quot;u1 mod&quot;);
* u2.setName(&quot;u2 mod&quot;);
*
* Ebean.save(u1);
* Ebean.save(u2);
*
* return u1.getEmail();
* }
* });
* </pre>
*
* @see TxRunnable
*/
public interface TxCallable<T> {
/**
* Execute the method within a transaction scope returning the result.
* <p>
* If you do not want to return a result you should look to use TxRunnable
* instead.
* </p>
*/
T call();
}
+100 -100
View File
@@ -1,100 +1,100 @@
package com.avaje.ebean;
import java.sql.Connection;
/**
* The Transaction Isolation levels.
* <p>
* These match those of java.sql.Connection with the addition of DEFAULT which
* implies the configured default of the DataSource.
* </p>
* <p>
* This can be used with TxScope to define transactional scopes to execute
* method within.
* </p>
*
* @see TxScope
*/
public enum TxIsolation {
/**
* Read Committed Isolation level. This is typically the default for most
* configurations.
*/
READ_COMMITED(Connection.TRANSACTION_READ_COMMITTED),
/**
* Read uncommitted Isolation level.
*/
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
/**
* Repeatable Read Isolation level.
*/
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
/**
* Serializable Isolation level.
*/
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE),
/**
* No Isolation level.
*/
NONE(Connection.TRANSACTION_NONE),
/**
* The default isolation level. This typically means the default that the
* DataSource is using or configured to use.
*/
DEFAULT(-1);
final int level;
private TxIsolation(int level) {
this.level = level;
}
/**
* Return the level as per java.sql.Connection.
* <p>
* Note that -1 denotes the default isolation level.
* </p>
*/
public int getLevel() {
return level;
}
/**
* Return the TxIsolation given the java.sql.Connection isolation level.
* <p>
* Note that -1 denotes the default isolation level.
* </p>
*/
public static TxIsolation fromLevel(int connectionIsolationLevel) {
switch (connectionIsolationLevel) {
case Connection.TRANSACTION_READ_UNCOMMITTED:
return TxIsolation.READ_UNCOMMITTED;
case Connection.TRANSACTION_READ_COMMITTED:
return TxIsolation.READ_COMMITED;
case Connection.TRANSACTION_REPEATABLE_READ:
return TxIsolation.REPEATABLE_READ;
case Connection.TRANSACTION_SERIALIZABLE:
return TxIsolation.SERIALIZABLE;
case Connection.TRANSACTION_NONE:
return TxIsolation.NONE;
case -1:
return TxIsolation.DEFAULT;
default:
throw new RuntimeException("Unknown isolation level " + connectionIsolationLevel);
}
}
}
package com.avaje.ebean;
import java.sql.Connection;
/**
* The Transaction Isolation levels.
* <p>
* These match those of java.sql.Connection with the addition of DEFAULT which
* implies the configured default of the DataSource.
* </p>
* <p>
* This can be used with TxScope to define transactional scopes to execute
* method within.
* </p>
*
* @see TxScope
*/
public enum TxIsolation {
/**
* Read Committed Isolation level. This is typically the default for most
* configurations.
*/
READ_COMMITED(Connection.TRANSACTION_READ_COMMITTED),
/**
* Read uncommitted Isolation level.
*/
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
/**
* Repeatable Read Isolation level.
*/
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
/**
* Serializable Isolation level.
*/
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE),
/**
* No Isolation level.
*/
NONE(Connection.TRANSACTION_NONE),
/**
* The default isolation level. This typically means the default that the
* DataSource is using or configured to use.
*/
DEFAULT(-1);
final int level;
TxIsolation(int level) {
this.level = level;
}
/**
* Return the level as per java.sql.Connection.
* <p>
* Note that -1 denotes the default isolation level.
* </p>
*/
public int getLevel() {
return level;
}
/**
* Return the TxIsolation given the java.sql.Connection isolation level.
* <p>
* Note that -1 denotes the default isolation level.
* </p>
*/
public static TxIsolation fromLevel(int connectionIsolationLevel) {
switch (connectionIsolationLevel) {
case Connection.TRANSACTION_READ_UNCOMMITTED:
return TxIsolation.READ_UNCOMMITTED;
case Connection.TRANSACTION_READ_COMMITTED:
return TxIsolation.READ_COMMITED;
case Connection.TRANSACTION_REPEATABLE_READ:
return TxIsolation.REPEATABLE_READ;
case Connection.TRANSACTION_SERIALIZABLE:
return TxIsolation.SERIALIZABLE;
case Connection.TRANSACTION_NONE:
return TxIsolation.NONE;
case -1:
return TxIsolation.DEFAULT;
default:
throw new RuntimeException("Unknown isolation level " + connectionIsolationLevel);
}
}
}
+39 -39
View File
@@ -1,39 +1,39 @@
package com.avaje.ebean;
/**
* Execute a TxRunnable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#execute(TxRunnable)} method.
* </p>
* <p>
* See also {@link TxCallable}.
* </p>
*
* <pre class="code">
*
* // this run method runs in a transaction scope
* // which by default is TxScope.REQUIRED
*
* Ebean.execute(new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName(&quot;u1 mod&quot;);
* u2.setName(&quot;u2 mod&quot;);
*
* Ebean.save(u1);
* Ebean.save(u2);
* }
* });
* </pre>
*
* @see TxCallable
*/
public interface TxRunnable {
/**
* Run the method in a transaction sope.
*/
public void run();
}
package com.avaje.ebean;
/**
* Execute a TxRunnable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#execute(TxRunnable)} method.
* </p>
* <p>
* See also {@link TxCallable}.
* </p>
*
* <pre class="code">
*
* // this run method runs in a transaction scope
* // which by default is TxScope.REQUIRED
*
* Ebean.execute(new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName(&quot;u1 mod&quot;);
* u2.setName(&quot;u2 mod&quot;);
*
* Ebean.save(u1);
* Ebean.save(u2);
* }
* });
* </pre>
*
* @see TxCallable
*/
public interface TxRunnable {
/**
* Run the method in a transaction sope.
*/
void run();
}
@@ -1,5 +1,7 @@
package com.avaje.ebean;
import com.avaje.ebean.config.PersistBatch;
import java.util.ArrayList;
/**
@@ -28,12 +30,39 @@ public final class TxScope {
TxIsolation isolation;
PersistBatch batch;
PersistBatch batchOnCascade;
int batchSize;
boolean readOnly;
ArrayList<Class<? extends Throwable>> rollbackFor;
ArrayList<Class<? extends Throwable>> noRollbackFor;
/**
* Return true if PersistBatch has been set.
*/
public boolean isBatchSet() {
return batch != null && batch != PersistBatch.INHERIT;
}
/**
* Return true if batch on cascade has been set.
*/
public boolean isBatchOnCascadeSet() {
return batchOnCascade != null && batchOnCascade != PersistBatch.INHERIT;
}
/**
* Return true if batch size has been set.
*/
public boolean isBatchSizeSet() {
return batchSize > 0;
}
/**
* Helper method to create a TxScope with REQUIRES.
*/
@@ -114,6 +143,51 @@ public final class TxScope {
return this;
}
/**
* Return the batch mode.
*/
public PersistBatch getBatch() {
return batch;
}
/**
* Set the batch mode to use.
*/
public TxScope setBatch(PersistBatch batch) {
this.batch = batch;
return this;
}
/**
* Return the batch on cascade mode.
*/
public PersistBatch getBatchOnCascade() {
return batchOnCascade;
}
/**
* Set the batch on cascade mode.
*/
public TxScope setBatchOnCascade(PersistBatch batchOnCascade) {
this.batchOnCascade = batchOnCascade;
return this;
}
/**
* Return the batch size. 0 means use the default value.
*/
public int getBatchSize() {
return batchSize;
}
/**
* Set the batch size to use.
*/
public TxScope setBatchSize(int batchSize) {
this.batchSize = batchSize;
return this;
}
/**
* Return if the transaction should be treated as read only.
*/
+13 -13
View File
@@ -54,7 +54,7 @@ public interface Update<T> {
/**
* Return the name if it is a named update.
*/
public String getName();
String getName();
/**
* Set this to false if you do not want the cache to invalidate related
@@ -64,7 +64,7 @@ public interface Update<T> {
* parts of the "L2" server cache.
* </p>
*/
public Update<T> setNotifyCache(boolean notifyCache);
Update<T> setNotifyCache(boolean notifyCache);
/**
* Set a timeout for statement execution.
@@ -77,12 +77,12 @@ public interface Update<T> {
* @param secs
* the timeout in seconds. Zero implies unlimited.
*/
public Update<T> setTimeout(int secs);
Update<T> setTimeout(int secs);
/**
* Execute the statement returning the number of rows modified.
*/
public int execute();
int execute();
/**
* Set an ordered bind parameter.
@@ -98,7 +98,7 @@ public interface Update<T> {
* @param value
* the parameter value to bind.
*/
public Update<T> set(int position, Object value);
Update<T> set(int position, Object value);
/**
* Set and ordered bind parameter (same as bind).
@@ -108,7 +108,7 @@ public interface Update<T> {
* @param value
* the parameter value to bind.
*/
public Update<T> setParameter(int position, Object value);
Update<T> setParameter(int position, Object value);
/**
* Set an ordered parameter that is null. The JDBC type of the null must be
@@ -117,12 +117,12 @@ public interface Update<T> {
* position starts at value 1 (not 0) to be consistent with PreparedStatement.
* </p>
*/
public Update<T> setNull(int position, int jdbcType);
Update<T> setNull(int position, int jdbcType);
/**
* Set an ordered parameter that is null (same as bind).
*/
public Update<T> setNullParameter(int position, int jdbcType);
Update<T> setNullParameter(int position, int jdbcType);
/**
* Set a named parameter. Named parameters have a colon to prefix the name.
@@ -135,12 +135,12 @@ public interface Update<T> {
* @param value
* the parameter value.
*/
public Update<T> set(String name, Object value);
Update<T> set(String name, Object value);
/**
* Bind a named parameter (same as bind).
*/
public Update<T> setParameter(String name, Object param);
Update<T> setParameter(String name, Object param);
/**
* Set a named parameter that is null. The JDBC type of the null must be
@@ -154,16 +154,16 @@ public interface Update<T> {
* @param jdbcType
* the type of the property being bound.
*/
public Update<T> setNull(String name, int jdbcType);
Update<T> setNull(String name, int jdbcType);
/**
* Bind a named parameter that is null (same as bind).
*/
public Update<T> setNullParameter(String name, int jdbcType);
Update<T> setNullParameter(String name, int jdbcType);
/**
* Return the sql that is actually executed.
*/
public String getGeneratedSql();
String getGeneratedSql();
}
+50 -34
View File
@@ -1,34 +1,50 @@
package com.avaje.ebean;
/**
* Holds two values as the result of a difference comparison.
*/
public class ValuePair {
final Object value1;
final Object value2;
public ValuePair(Object value1, Object value2) {
this.value1 = value1;
this.value2 = value2;
}
/**
* Return the first value.
*/
public Object getValue1() {
return value1;
}
/**
* Return the second value.
*/
public Object getValue2() {
return value2;
}
public String toString() {
return value1 + "," + value2;
}
}
package com.avaje.ebean;
/**
* Holds two values as the result of a difference comparison.
*/
public class ValuePair {
private final Object newValue;
private final Object oldValue;
public ValuePair(Object newValue, Object oldValue) {
this.newValue = newValue;
this.oldValue = oldValue;
}
/**
* Return the new value.
*/
public Object getNewValue() {
return newValue;
}
/**
* Return the old value.
*/
public Object getOldValue() {
return oldValue;
}
/**
* Return the new value.
*/
@Deprecated
public Object getValue1() {
return newValue;
}
/**
* Return the old value.
*/
@Deprecated
public Object getValue2() {
return oldValue;
}
public String toString() {
return newValue + "," + oldValue;
}
}
@@ -1,64 +1,58 @@
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;
import com.avaje.ebean.Query;
import com.avaje.ebean.Query.UseIndex;
/**
* Specify the default cache use specific entity type.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheStrategy {
/**
* When set to true the bean cache will be used unless explicitly stated not
* to in a query via {@link Query#setUseCache(boolean)}.
*/
boolean useBeanCache() default true;
/**
* A single property that is a natural unique identifier for the bean.
* <p>
* When a findUnique query is used with this property as the sole expression
* then there will be a lookup into the L2 natural key cache.
* </p>
*/
String naturalKey() default "";
/**
* When set to true the beans returned from a query will default to be
* readOnly.
* <p>
* If the bean is readOnly and has no relationships then it may be sharable.
* </p>
* <p>
* If you try to modify a readOnly bean it will throw an
* IllegalStateException.
* </p>
*/
boolean readOnly() default false;
/**
* Specify a query that can be used to warm the cache.
* <p>
* All the beans fetched by this query will be loaded into the bean cache and
* the query itself will be loaded into the query cache.
* </p>
* <p>
* The warming query will typically be executed at startup time after a short
* delay (defaults to a 30 seconds delay).
* </p>
*/
String warmingQuery() default "";
/**
* Default setting for using a text index if it has been defined on this bean
* type.
*/
UseIndex useIndex() default UseIndex.DEFAULT;
};
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;
import com.avaje.ebean.Query;
/**
* Specify the default cache use specific entity type.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheStrategy {
/**
* When set to true the bean cache will be used unless explicitly stated not
* to in a query via {@link Query#setUseCache(boolean)}.
*/
boolean useBeanCache() default true;
/**
* A single property that is a natural unique identifier for the bean.
* <p>
* When a findUnique query is used with this property as the sole expression
* then there will be a lookup into the L2 natural key cache.
* </p>
*/
String naturalKey() default "";
/**
* When set to true the beans returned from a query will default to be
* readOnly.
* <p>
* If the bean is readOnly and has no relationships then it may be sharable.
* </p>
* <p>
* If you try to modify a readOnly bean it will throw an
* IllegalStateException.
* </p>
*/
boolean readOnly() default false;
/**
* Specify a query that can be used to warm the cache.
* <p>
* All the beans fetched by this query will be loaded into the bean cache and
* the query itself will be loaded into the query cache.
* </p>
* <p>
* The warming query will typically be executed at startup time after a short
* delay (defaults to a 30 seconds delay).
* </p>
*/
String warmingQuery() default "";
}
@@ -1,47 +1,47 @@
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;
/**
* Specify cache tuning for a specific entity type.
* <p>
* If this is not specified then the system default settings are used.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheTuning {
/**
* The maximum size for the cache.
* <p>
* This defaults to 0 which means unlimited.
* </p>
*/
int maxSize() default 0;
/**
* The maximum time (in seconds) that a cache entry is allowed to stay in the
* cache when it has not been accessed.
* <p>
* This defaults to 0 which means unlimited.
* </p>
*/
int maxIdleSecs() default 0;
/**
* The maximum time (in seconds) a cache entry is allowed to stay in the
* cache.
* <p>
* This is not generally required as the cache entries are automatically
* evicted when related data changes are committed.
* </p>
* <p>
* This defaults to 0 which means unlimited.
* </p>
*/
int maxSecsToLive() default 0;
};
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specify cache tuning for a specific entity type.
* <p>
* If this is not specified then the system default settings are used.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheTuning {
/**
* The maximum size for the cache.
* <p>
* This defaults to 0 which means unlimited.
* </p>
*/
int maxSize() default 0;
/**
* The maximum time (in seconds) that a cache entry is allowed to stay in the
* cache when it has not been accessed.
* <p>
* This defaults to 0 which means unlimited.
* </p>
*/
int maxIdleSecs() default 0;
/**
* The maximum time (in seconds) a cache entry is allowed to stay in the
* cache.
* <p>
* This is not generally required as the cache entries are automatically
* evicted when related data changes are committed.
* </p>
* <p>
* This defaults to 0 which means unlimited.
* </p>
*/
int maxSecsToLive() default 0;
}
@@ -0,0 +1,17 @@
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;
/**
* Deprecated - please use @DbHstore instead.
* @see DbHstore
*/
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnHstore {
}
@@ -13,10 +13,5 @@ public enum ConcurrencyMode {
/**
* Use a version column.
*/
VERSION,
/**
* Use all the columns (except Lobs).
*/
ALL
VERSION
}
@@ -1,31 +1,34 @@
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;
/**
* For a timestamp property that is set to the datetime when the entity is
* created/inserted.
* <p>
* An alternative to using this annotation would be to use insertable=false,
* updateable=false with @Column and have the DB insert the current time
* (default value on the DB column is SYSTIME etc).
* </p>
* <p>
* The downside to this approach is that the inserted entity does not have the
* timestamp value after the insert has occurred. You need to fetch the entity
* back to get the inserted timestamp if you want to used it.
* </p>
*
* <pre class="code">
* &#064;Column(insertable = false, updateable = false)
* Timestamp cretimestamp;
* </pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatedTimestamp {
};
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;
/**
* For a timestamp property that is set to the datetime when the entity is
* created/inserted.
* <p>
* An alternative to using this annotation would be to use insertable=false,
* updateable=false with @Column and have the DB insert the current time
* (default value on the DB column is SYSTIME etc).
* </p>
* <p>
* The downside to this approach is that the inserted entity does not have the
* timestamp value after the insert has occurred. You need to fetch the entity
* back to get the inserted timestamp if you want to used it.
* </p>
*
* <h3>Example:</h3>
* <pre>{@code
*
* @CreatedTimestamp
* Timestamp whenCreated;
*
* }</pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatedTimestamp {
}
@@ -0,0 +1,26 @@
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;
/**
* Used for mapping a Map<String,String> type property to Postgres HSTORE data type.
* <p>
* The Map property should have keys and values of type String.
* </p>
*
* <h3>Example:</h3>
* <pre>{@code
*
* @DbHstore
* Map<String, String> tags;
*
* }</pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DbHstore {
}
@@ -0,0 +1,44 @@
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;
/**
* Specify a property holding JSON content.
* <p>
* By default the content will be stored in a DB Clob except on Postgres where DB JSON type is used.
* </p>
* <h3>Example:</h3>
* <pre>{@code
*
* // Store as JSON on Postgres or Clob on other databases
* @DbJson
* Map<String,Object> content;
*
* }</pre>
*
* <h3>Example with JSONB storage</h3>
* <pre>{@code
*
* // Store as JSONB on Postgres or Clob on other databases
* @DbJson(storage = DbJsonType.JSONB)
* Map<String,Object> content;
*
* }</pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DbJson {
/**
* Specify the database type used to store the JSON content.
*/
DbJsonType storage() default DbJsonType.JSON;
/**
* For VARCHAR storage specify the column length (defaults to 3000).
*/
int length() default 0;
}
@@ -0,0 +1,39 @@
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;
/**
* Specify a property holding JSON content.
* <p>
* The content will be stored on Postgres using it's JSONB type and as Clob for other databases.
* </p>
* <p>
* This is equivalent to using <code>@DbJson(storage = DbJsonType.JSONB)</code>
* </p>
*
* <h3>Example:</h3>
* <pre>{@code
*
* // Store as JSONB on Postgres or Clob on other databases
* @DbJsonB
* Map<String,Object> content;
*
* }</pre>
*
* <h3>Equivalent to:</h3>
* <pre>{@code
*
* // Store as JSONB on Postgres or Clob on other databases
* @DbJson(storage = DbJsonType.JSONB)
* Map<String,Object> content;
*
* }</pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DbJsonB {
}
@@ -0,0 +1,32 @@
package com.avaje.ebean.annotation;
/**
* Specify the DB storage type used to store JSON content.
*/
public enum DbJsonType {
/**
* Store as JSON on Postgres and for other databases store as CLOB.
*/
JSON,
/**
* Store as JSONB on Postgres and for other databases store as CLOB.
*/
JSONB,
/**
* Store as database VARCHAR.
*/
VARCHAR,
/**
* Store as database CLOB.
*/
CLOB,
/**
* Store as database BLOB.
*/
BLOB
}
@@ -1,31 +1,31 @@
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;
/**
* Specify property name to db column mapping for Embedded beans.
* <p>
* This is designed to be easier to use than the AttributeOverride annotation in
* standard JPA.
* </p>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EmbeddedColumns {
/**
* A list of property names mapped to DB columns.
* <p>
* For example <code>currency=IN_CURR, amount=IN_AMOUNT</code>
* </p>
* <p>
* Where currency and amount are properties and IN_CURR and IN_AMOUNT are the
* respective DB columns these properties will be mapped to.
* </p>
*/
String columns() default "";
};
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;
/**
* Specify property name to db column mapping for Embedded beans.
* <p>
* This is designed to be easier to use than the AttributeOverride annotation in
* standard JPA.
* </p>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EmbeddedColumns {
/**
* A list of property names mapped to DB columns.
* <p>
* For example <code>currency=IN_CURR, amount=IN_AMOUNT</code>
* </p>
* <p>
* Where currency and amount are properties and IN_CURR and IN_AMOUNT are the
* respective DB columns these properties will be mapped to.
* </p>
*/
String columns() default "";
}
@@ -1,24 +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;
/**
* Specify that the property is stored in encrypted form.
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypted {
/**
* When true try to use DB encryption rather than local java encryption.
*/
boolean dbEncryption() default true;
/**
* Used to specify the DB column length.
*/
int dbLength() default 0;
};
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specify that the property is stored in encrypted form.
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypted {
/**
* When true try to use DB encryption rather than local java encryption.
*/
boolean dbEncryption() default true;
/**
* Used to specify the DB column length.
*/
int dbLength() default 0;
}
@@ -1,90 +1,90 @@
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;
/**
* For mapping the values of an Enum to and from Database values.
* <p>
* Also refer to the {@link EnumValue} approach which probably the preferred now
* (preferred over using this EnumMapping annotation).
* </p>
* <p>
* Both of the approaches defined in the JPA have significant problems!!!
* </p>
* <p>
* Using the ordinal value is VERY RISKY because that depends on the compile
* order of the enum values. Aka if you change the order of the enum values you
* have changed their ordinal values and now your DB values are WRONG - a HUGE
* disaster!!!.
* </p>
* <p>
* Using the String values of enums is fairly restrictive because in a Database
* these values are usually truncated into short codes (e.g. "A" short for
* "ACTIVE") so space used in the database is minimised. Making your enum names
* match the database values would give them very short less meaningful names -
* not a great solution.
* </p>
* <p>
* You can use this annotation to control the mapping of your enums to database
* values.
* </p>
* <p>
* The design of this using nameValuePairs is not optimal for safety or
* refactoring so if you have a better solution I'm all ears. The other
* solutions would probably involve modifying each enumeration with a method
* which may be ok.
* </p>
* <p>
* An example mapping the UserState enum.
* </p>
*
* <pre class="code">
* ...
* &#064;EnumMapping(nameValuePairs=&quot;NEW=N, ACTIVE=A, INACTIVE=I&quot;)
* public enum UserState {
* NEW,
* ACTIVE,
* INACTIVE;
* }
* </pre>
*
* @see EnumValue
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumMapping {
/**
* A comma delimited list of name=value pairs.
* <p>
* e.g. "ACTIVE=A, INACTIVE=I, NEW=N".
* </p>
* <p>
* Where ACTIVE, INACTIVE and NEW are the enumeration values and "A", "I" and
* "N" are the database values.
* </p>
* <p>
* This is not really an optimal approach so if you have a better one I'm all
* ears - thanks.
* </p>
*/
String nameValuePairs();
/**
* Defaults to mapping values to database VARCHAR type. If this is set to true
* then the values will be converted to INTEGER and mapped to the database
* integer type.
* <p>
* e.g. "ACTIVE=1, INACTIVE=0, NEW=2".
* </p>
*/
boolean integerType() default false;
/**
* The length of DB column if mapping to string values.
*/
int length() default 0;
};
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* For mapping the values of an Enum to and from Database values.
* <p>
* Also refer to the {@link EnumValue} approach which probably the preferred now
* (preferred over using this EnumMapping annotation).
* </p>
* <p>
* Both of the approaches defined in the JPA have significant problems!!!
* </p>
* <p>
* Using the ordinal value is VERY RISKY because that depends on the compile
* order of the enum values. Aka if you change the order of the enum values you
* have changed their ordinal values and now your DB values are WRONG - a HUGE
* disaster!!!.
* </p>
* <p>
* Using the String values of enums is fairly restrictive because in a Database
* these values are usually truncated into short codes (e.g. "A" short for
* "ACTIVE") so space used in the database is minimised. Making your enum names
* match the database values would give them very short less meaningful names -
* not a great solution.
* </p>
* <p>
* You can use this annotation to control the mapping of your enums to database
* values.
* </p>
* <p>
* The design of this using nameValuePairs is not optimal for safety or
* refactoring so if you have a better solution I'm all ears. The other
* solutions would probably involve modifying each enumeration with a method
* which may be ok.
* </p>
* <p>
* An example mapping the UserState enum.
* </p>
*
* <pre class="code">
* ...
* &#064;EnumMapping(nameValuePairs=&quot;NEW=N, ACTIVE=A, INACTIVE=I&quot;)
* public enum UserState {
* NEW,
* ACTIVE,
* INACTIVE;
* }
* </pre>
*
* @see EnumValue
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumMapping {
/**
* A comma delimited list of name=value pairs.
* <p>
* e.g. "ACTIVE=A, INACTIVE=I, NEW=N".
* </p>
* <p>
* Where ACTIVE, INACTIVE and NEW are the enumeration values and "A", "I" and
* "N" are the database values.
* </p>
* <p>
* This is not really an optimal approach so if you have a better one I'm all
* ears - thanks.
* </p>
*/
String nameValuePairs();
/**
* Defaults to mapping values to database VARCHAR type. If this is set to true
* then the values will be converted to INTEGER and mapped to the database
* integer type.
* <p>
* e.g. "ACTIVE=1, INACTIVE=0, NEW=2".
* </p>
*/
boolean integerType() default false;
/**
* The length of DB column if mapping to string values.
*/
int length() default 0;
}
@@ -1,46 +1,48 @@
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;
/**
* Enables you to specify a value to use to persist for an enum value.
*
* <pre class="code">
* public enum Status {
* &#064;EnumValue(&quot;N&quot;)
* NEW,
*
* &#064;EnumValue(&quot;A&quot;)
* ACTIVE,
*
* &#064;EnumValue(&quot;I&quot;)
* INACTIVE,
* }
*
* </pre>
* <p>
* This is an alternative to using the JPA standard approach or Ebean's
* {@link EnumMapping} annotation.
* </p>
* <p>
* Note that if all the EnumValue values are parsable as Integers then Ebean
* will persist and fetch them as integers - otherwise they will be persisted
* and fetched as strings.
* </p>
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumValue {
/**
* Specify the value to persist for a specific enum value.
* <p>
* If all the values are parsable as Integers then Ebean will persist and
* fetch them as integers rather than strings.
* </p>
*/
String value();
};
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;
/**
* Enables you to specify a value to use to persist for an enum value.
*
* <pre>{@code
*
* public enum Status {
*
* @EnumValue("N")
* NEW,
*
* @EnumValue("A")
* ACTIVE,
*
* @EnumValue("I")
* INACTIVE,
* }
*
* }</pre>
* <p>
* This is an alternative to using the JPA standard approach or Ebean's
* {@link EnumMapping} annotation.
* </p>
* <p>
* Note that if all the EnumValue values are parsable as Integers then Ebean
* will persist and fetch them as integers - otherwise they will be persisted
* and fetched as strings.
* </p>
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumValue {
/**
* Specify the value to persist for a specific enum value.
* <p>
* If all the values are parsable as Integers then Ebean will persist and
* fetch them as integers rather than strings.
* </p>
*/
String value();
}
@@ -12,17 +12,17 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
public @interface Expose {
/**
* If {@code true}, the field marked with this annotation is written out in the JSON while
* serializing. If {@code false}, the field marked with this annotation is skipped from the
* serialized output. Defaults to {@code true}.
*/
public boolean serialize() default true;
/**
* If {@code true}, the field marked with this annotation is written out in the JSON while
* serializing. If {@code false}, the field marked with this annotation is skipped from the
* serialized output. Defaults to {@code true}.
*/
boolean serialize() default true;
/**
* If {@code true}, the field marked with this annotation is deserialized from the JSON.
* If {@code false}, the field marked with this annotation is skipped during deserialization.
* Defaults to {@code true}.
*/
public boolean deserialize() default true;
/**
* If {@code true}, the field marked with this annotation is deserialized from the JSON.
* If {@code false}, the field marked with this annotation is skipped during deserialization.
* Defaults to {@code true}.
*/
boolean deserialize() default true;
}
@@ -1,103 +1,106 @@
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;
import com.avaje.ebean.Query;
/**
* Assign to a property to be based on a SQL formula.
* <p>
* This is typically a SQL Literal value, SQL case statement, SQL function or
* similar.
* </p>
* <p>
* Any property based on a formula becomes a read only property.
* </p>
* <p>
* You may also put use the Transient annotation with the Formula annotation.
* The effect of the Transient annotation in this case is that the formula will
* <b>NOT</b> be included in queries by default - you have to explicitly include
* it via {@link Query#select(String)} or {@link Query#join(String, String)}.
* You may want to do this if the Formula is relatively expensive and only want
* it included in the query when you explicitly state it.
* </p>
*
* <pre class="code">
* // On the Order &quot;master&quot; bean
* // ... a formula using the Order details
* // ... sum(order_qty*unit_price)
* &#064;Transient
* &#064;Formula(select = &quot;_b${ta}.total_amount&quot;, join = &quot;join (select order_id, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) as _b${ta} on _b${ta}.order_id = ${ta}.id&quot;)
* Double totalAmount;
*
* </pre>
* <p>
* As the totalAmount formula is also Transient it is not included by default in
* queries - it needs to be explicitly included.
* </p>
*
* <pre class="code">
* // find by Id
* Order o1 = Ebean.find(Order.class)
* .select(&quot;id, totalAmount&quot;)
* .setId(1).findUnique();
*
* // find list ... using totalAmount in the where clause
* List&lt;Order&gt; list = Ebean.find(Order.class)
* .select(&quot;id, totalAmount&quot;)
* .where()
* .eq(&quot;status&quot;, Order.Status.NEW)
* .gt(&quot;totalAmount&quot;, 10)
* .findList();
*
* // as a join from customer
* List&lt;Customer&gt; l0 = Ebean.find(Customer.class)
* .select(&quot;id, name&quot;)
* .join(&quot;orders&quot;, &quot;status, totalAmount&quot;)
* .where()
* .gt(&quot;id&quot;, 0)
* .gt(&quot;orders.totalAmount&quot;, 10)
* .findList();
*
* </pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Formula {
/**
* The SQL to be used in the SELECT part of the SQL to populate a property.
*/
String select();
/**
* OPTIONAL - the SQL to be used in the JOIN part of the SQL to support the
* formula.
* <p>
* This is commonly used to join a 'dynamic view' to support aggregation such
* as count, sum etc.
* </p>
* <p>
* The join string should start with either "left outer join" or "join".
* </p>
*
* <p>
* You will almost certainly use the "${ta}" as a place holder for the table
* alias of the table you are joining back to (the "base table" of the entity
* bean).
* </p>
* <p>
* The example below is used to support a total count of topics created by a
* user.
* </p>
*
* <pre class="code">
* join (select user_id, count(*) as topic_count from f_topic group by user_id) as _tc on _tc.user_id = ${ta}.id
* </pre>
*/
String join() default "";
};
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;
import com.avaje.ebean.Query;
/**
* Assign to a property to be based on a SQL formula.
* <p>
* This is typically a SQL Literal value, SQL case statement, SQL function or
* similar.
* </p>
* <p>
* Any property based on a formula becomes a read only property.
* </p>
* <p>
* You may also put use the Transient annotation with the Formula annotation.
* The effect of the Transient annotation in this case is that the formula will
* <b>NOT</b> be included in queries by default - you have to explicitly include
* it via {@link Query#select(String)} or {@link Query#fetch(String, String, com.avaje.ebean.FetchConfig)}.
* You may want to do this if the Formula is relatively expensive and only want
* it included in the query when you explicitly state it.
* </p>
*
* <pre class="code">
* // On the Order &quot;master&quot; bean
* // ... a formula using the Order details
* // ... sum(order_qty*unit_price)
* &#064;Transient
* &#064;Formula(select = &quot;_b${ta}.total_amount&quot;, join = &quot;join (select order_id, sum(order_qty*unit_price) as total_amount from o_order_detail group by order_id) as _b${ta} on _b${ta}.order_id = ${ta}.id&quot;)
* Double totalAmount;
*
* </pre>
* <p>
* As the totalAmount formula is also Transient it is not included by default in
* queries - it needs to be explicitly included.
* </p>
*
* <pre>{@code
*
* // find by Id
* Order o1 = Ebean.find(Order.class)
* .select("id, totalAmount")
* .setId(1).findUnique();
*
* // find list ... using totalAmount in the where clause
* List<Order> list = Ebean.find(Order.class)
* .select("id, totalAmount")
* .where()
* .eq("status", Order.Status.NEW)
* .gt("totalAmount", 10)
* .findList();
*
* // as a join from customer
* List<Customer> l0 = Ebean.find(Customer.class)
* .select("id, name")
* .fetch("orders", "status, totalAmount")
* .where()
* .gt("id", 0)
* .gt("orders.totalAmount", 10)
* .findList();
*
* }</pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Formula {
/**
* The SQL to be used in the SELECT part of the SQL to populate a property.
*/
String select();
/**
* OPTIONAL - the SQL to be used in the JOIN part of the SQL to support the
* formula.
* <p>
* This is commonly used to join a 'dynamic view' to support aggregation such
* as count, sum etc.
* </p>
* <p>
* The join string should start with either "left outer join" or "join".
* </p>
*
* <p>
* You will almost certainly use the "${ta}" as a place holder for the table
* alias of the table you are joining back to (the "base table" of the entity
* bean).
* </p>
* <p>
* The example below is used to support a total count of topics created by a
* user.
* </p>
*
* <pre>{@code
*
* join (select user_id, count(*) as topic_count from f_topic group by user_id) as _tc on _tc.user_id = ${ta}.id
*
* }</pre>
*/
String join() default "";
}
@@ -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 "";
}
@@ -1,37 +1,37 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An Update statement for a particular entity bean type.
* <p>
* The update can either be a sql insert,update or delete statement with tables
* and columns etc or the equivalent statement but with table names and columns
* expressed as bean types and bean properties.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedUpdate {
/**
* The name of the update.
*/
String name();
/**
* The insert, update or delete statement.
*/
String update();
/**
* Set this to false if you do not want the cache to be notified. If true the
* cache will invalidate appropriate objects from the cache (after a
* successful transaction commit).
*/
boolean notifyCache() default true;
};
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An Update statement for a particular entity bean type.
* <p>
* The update can either be a sql insert,update or delete statement with tables
* and columns etc or the equivalent statement but with table names and columns
* expressed as bean types and bean properties.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedUpdate {
/**
* The name of the update.
*/
String name();
/**
* The insert, update or delete statement.
*/
String update();
/**
* Set this to false if you do not want the cache to be notified. If true the
* cache will invalidate appropriate objects from the cache (after a
* successful transaction commit).
*/
boolean notifyCache() default true;
}
@@ -1,20 +1,20 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Holds an array of named Update statements for a particular entity bean type.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedUpdates {
/**
* An array of named updates.
*/
NamedUpdate[] value();
};
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Holds an array of named Update statements for a particular entity bean type.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedUpdates {
/**
* An array of named updates.
*/
NamedUpdate[] value();
}
@@ -1,34 +1,34 @@
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;
/**
* Specify that the elements of a OneToMany are private owned.
* <p>
* This means that if they are removed from the List/Set/Map they will be
* deleted when their parent object is saved.
* </p>
* <p>
* This could also be described as deleting orphans - in that beans removed from
* the List/Set/Map will be deleted automatically when the parent bean is saved.
* They are considered 'orphans' when they have been removed from the collection
* in that they are no longer associated/linked to their parent bean.
* </p>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivateOwned {
/**
* Set this to false if you don't want cascade REMOVE on this relationship.
* <p>
* That is, by default PrivateOwned implicitly adds a cascade REMOVE to the
* relationship and if you don't want that you need to set this to false.
* </p>
*/
boolean cascadeRemove() default true;
};
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;
/**
* Specify that the elements of a OneToMany are private owned.
* <p>
* This means that if they are removed from the List/Set/Map they will be
* deleted when their parent object is saved.
* </p>
* <p>
* This could also be described as deleting orphans - in that beans removed from
* the List/Set/Map will be deleted automatically when the parent bean is saved.
* They are considered 'orphans' when they have been removed from the collection
* in that they are no longer associated/linked to their parent bean.
* </p>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivateOwned {
/**
* Set this to false if you don't want cascade REMOVE on this relationship.
* <p>
* That is, by default PrivateOwned implicitly adds a cascade REMOVE to the
* relationship and if you don't want that you need to set this to false.
* </p>
*/
boolean cascadeRemove() default true;
}
@@ -1,24 +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;
/**
* Specify explicit sql for multiple select statements. Need to use this if you
* have more than one SqlSelect for a given bean.
* <p>
* FUTURE: Support explicit sql for SqlInsert, SqlUpdate and SqlDelete.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Sql {
/**
* The sql select statements.
*/
SqlSelect[] select() default { @SqlSelect };
};
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;
/**
* Specify explicit sql for multiple select statements. Need to use this if you
* have more than one SqlSelect for a given bean.
* <p>
* FUTURE: Support explicit sql for SqlInsert, SqlUpdate and SqlDelete.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Sql {
/**
* The sql select statements.
*/
SqlSelect[] select() default { @SqlSelect };
}
@@ -1,298 +1,298 @@
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;
import com.avaje.ebean.Query;
/**
* Specify an explicit sql select statement to use for querying an entity bean.
* <p>
* The reason for using explicit sql is that you want better control over the
* exact sql or sql that Ebean does not generate for you (such as group by,
* union, intersection, window functions, recursive queries).
* </p>
* <p>
* An example of two sql select queries deployed on the ReportTopic entity bean.
* The first one has no name specified so it becomes the default query. The
* second query extends the first adding a where clause with a named parameter.
* </p>
*
* <pre class="code">
* ...
* &#064;Entity
* &#064;Sql(select = {
* &#064;SqlSelect(query =
* &quot;select t.id, t.title, count(p.id) as score &quot;+
* &quot;from f_topic t &quot;+
* &quot;join f_topic_post p on p.topic_id = t.id &quot;+
* &quot;group by t.id, t.title&quot;),
* &#064;SqlSelect(
* name = &quot;with.title&quot;,
* extend = &quot;default&quot;,
* debug = true,
* where = &quot;title like :likeTitle&quot;)
* })
* public class ReportTopic
* &#064;Id Integer id;
* String title;
* Double score;
* ...
* </pre>
*
* <p>
* An example using the first "default" query.
* </p>
*
* <pre class="code">
*
* List&lt;ReportTopic&gt; list =
* Ebean.find(ReportTopic.class)
* .having().gt(&quot;score&quot;, 0)
* .findList();
*
* </pre>
*
* <p>
* The resulting sql, note the having clause has been added.
* </p>
*
* <pre class="code">
* select t.id, t.title, count(p.id) as score
* from f_topic t join f_topic_post p on p.topic_id = t.id
* group by t.id, t.title
* having count(p.id) &gt; ?
* </pre>
*
* <p>
* An example using the second query. Note the named parameter "likeTitle" must
* be set.
* </p>
*
* <pre class="code">
* List&lt;ReportTopic&gt; list =
* Ebean.find(ReportTopic.class, &quot;with.title&quot;)
* .set(&quot;likeTitle&quot;, &quot;a%&quot;)
* .findList();
* </pre>
*
* <p>
* Ebean tries to parse the sql in the query to determine 4 things
* <li>Location for inserting WHERE expressions (if required)</li>
* <li>Location for inserting HAVING expressions (if required)</li>
* <li>Mapping of columns to bean properties</li>
* <li>The order by clause</li>
* </p>
* <p>
* If Ebean is unable to parse out this information (perhaps because the sql
* contains multiple select from keywords etc) then you need to manually specify
* it.
* </p>
* <p>
* Insert ${where} or ${andWhere} into the location where Ebean can insert any
* expressions added to the where clause. Use ${andWhere} if the sql already has
* the WHERE keyword and Ebean will instead start with a AND keyword.
* </p>
* <p>
* Insert ${having} or ${andHaving} into the location where Ebean can insert any
* expressions added to the having clause. Use ${andHaving} if the sql already
* has a HAVING keyword and Ebean will instead start with a AND keyword.
* </p>
* <p>
* Use the columnMapping property if Ebean is unable to determine the columns
* and map them to bean properties.
* </p>
* <p>
* Example with ${andWhere} & ${having}.
* </p>
*
* <pre class="code">
* &#064;SqlSelect(
* name = &quot;explicit.where&quot;,
* query =
* &quot;select t.id, t.title, count(p.id) as score &quot;+
* &quot;from f_topic t, f_topic_post p &quot;+
* &quot;where p.topic_id = t.id ${andWhere} &quot;+
* &quot;group by t.id, t.title ${having}&quot;),
* </pre>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Deprecated
public @interface SqlSelect {
/**
* The name of the query. If left blank this is assumed to be the default
* query for this bean type.
* <p>
* This will default to "default" and in that case becomes the default query
* used for the bean.
* </p>
*/
String name() default "default";
/**
* The tableAlias used when adding where expressions to the query.
*/
String tableAlias() default "";
/**
* The sql select statement.
* <p>
* If this query <em>extends</em> another then this string is appended to the
* parent query string. Often when using <em>extend</em> you will leave the
* query part blank and just specify a where and/or having clauses.
* </p>
* <p>
* This sql <em>CAN NOT</em> contain named parameters. You have to put these
* in the separate where and/or having sections.
* </p>
* <p>
* Ebean automatically tries to determine the location in the sql string for
* putting in additional where or having clauses. If Ebean is unable to
* successfully determine this then you have to explicitly specify these
* locations by including
* <em>${where} or ${andWhere} and ${having} or ${andHaving}</em> in the sql.
* </p>
* <p>
* <b>${where}</b> location of where clause (and will add WHERE ... ) <br/>
* Use this when there is no where clause in the sql. If expressions are added
* to the where clause Ebean will put them in at this location starting with
* the WHERE keyword.
* <p>
* <p>
* <b>${andWhere}</b> <br/>
* Use this instead of ${where} if there IS an existing where clause in the
* sql. Ebean will add the expressions starting with the AND keyword.
* <p>
* <b>${having}</b> location of having clause (and will add HAVING... ) <br/>
* </p>
* <p>
* <b>${andHaving}</b> <br/>
* Use this instead of ${having} when there IS an existing HAVING clause.
* Ebean will add the expressions starting with the AND keyword.
* </p>
* <p>
* You can include one of ${where} OR ${andWhere} but not both.
* </p>
* <p>
* You can include one of ${having} OR ${andHaving} but not both.
* </p>
*/
String query() default "";
/**
* Specify the name of a sql-select query that this one 'extends'.
* <p>
* When a query is extended the sql query contents are appended together. The
* where and having clauses are NOT appended but overridden.
* </p>
*/
String extend() default "";
/**
* Specify a where clause typically containing named parameters.
* <p>
* If a where clause is specified with named parameters then they will need to
* be set on the query via {@link Query#setParameter(String, Object)}.
* </p>
* <p>
* In the example below the query specifies a where clause that includes a
* named parameter "likeTitle".
* </p>
*
* <pre class="code">
* ...
* &#064;Entity
* &#064;Sql(select = {
* ...
* &#064;SqlSelect(
* name = &quot;with.title&quot;,
* extend = &quot;default&quot;,
* debug = true,
* where = &quot;title like :likeTitle&quot;)
* })
* public class ReportTopic
* ...
* </pre>
*
* <p>
* Example use of the above named query.
* </p>
*
* <pre class="code">
*
* Query&lt;ReportTopic&gt; query0 = Ebean.createQuery(ReportTopic.class, &quot;with.title&quot;);
*
* query0.set(&quot;likeTitle&quot;, &quot;Bana%&quot;);
*
* List&lt;ReportTopic&gt; list0 = query0.findList();
* </pre>
*
*/
String where() default "";
/**
* Specify a having clause typically containing named parameters.
* <p>
* If a having clause is specified with named parameters then they will need
* to be set on the query via {@link Query#setParameter(String, Object)}.
* </p>
*/
String having() default "";
/**
* (Optional) Explicitly specify column to property mapping.
* <p>
* This is required when Ebean is unable to parse the sql. This could occur if
* the sql contains multiple select keywords etc.
* </p>
* <p>
* Specify the columns and property names they map to in the format.
* </p>
*
* <pre class="code">
* column1 propertyName1, column2 propertyName2, ...
* </pre>
*
* <p>
* Optionally put a AS keyword between the column and property.
* </p>
*
* <pre class="code">
* // the AS keyword is optional
* column1 AS propertyName1, column2 propertyName2, ...
* </pre>
*
* <p>
* <b>column</b> should contain the table alias if there is one
* </p>
* <p>
* <b>propertyName</b> should match the property name.
* </p>
*
* <p>
* Example mapping 5 columns to properties.
* </p>
*
* <pre class="code">
* columnMapping=&quot;t.id, t.bug_body description, t.bug_title as title, count(p.id) as scoreValue&quot;,
* </pre>
*
* <p>
* Without this set Ebean will parse the sql looking for the select clause and
* try to map the columns to property names. It is expected that Ebean will
* not be able to successfully parse some sql and for those cases you should
* specify the column to property mapping explicitly.
* </p>
*
*/
String columnMapping() default "";
/**
* Set this to true to have debug output when Ebean parses the sql-select.
*/
boolean debug() default false;
};
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;
import com.avaje.ebean.Query;
/**
* Specify an explicit sql select statement to use for querying an entity bean.
* <p>
* The reason for using explicit sql is that you want better control over the
* exact sql or sql that Ebean does not generate for you (such as group by,
* union, intersection, window functions, recursive queries).
* </p>
* <p>
* An example of two sql select queries deployed on the ReportTopic entity bean.
* The first one has no name specified so it becomes the default query. The
* second query extends the first adding a where clause with a named parameter.
* </p>
*
* <pre class="code">
* ...
* &#064;Entity
* &#064;Sql(select = {
* &#064;SqlSelect(query =
* &quot;select t.id, t.title, count(p.id) as score &quot;+
* &quot;from f_topic t &quot;+
* &quot;join f_topic_post p on p.topic_id = t.id &quot;+
* &quot;group by t.id, t.title&quot;),
* &#064;SqlSelect(
* name = &quot;with.title&quot;,
* extend = &quot;default&quot;,
* debug = true,
* where = &quot;title like :likeTitle&quot;)
* })
* public class ReportTopic
* &#064;Id Integer id;
* String title;
* Double score;
* ...
* </pre>
*
* <p>
* An example using the first "default" query.
* </p>
*
* <pre class="code">
*
* List&lt;ReportTopic&gt; list =
* Ebean.find(ReportTopic.class)
* .having().gt(&quot;score&quot;, 0)
* .findList();
*
* </pre>
*
* <p>
* The resulting sql, note the having clause has been added.
* </p>
*
* <pre class="code">
* select t.id, t.title, count(p.id) as score
* from f_topic t join f_topic_post p on p.topic_id = t.id
* group by t.id, t.title
* having count(p.id) &gt; ?
* </pre>
*
* <p>
* An example using the second query. Note the named parameter "likeTitle" must
* be set.
* </p>
*
* <pre class="code">
* List&lt;ReportTopic&gt; list =
* Ebean.find(ReportTopic.class, &quot;with.title&quot;)
* .set(&quot;likeTitle&quot;, &quot;a%&quot;)
* .findList();
* </pre>
*
* <p>
* Ebean tries to parse the sql in the query to determine 4 things
* <li>Location for inserting WHERE expressions (if required)</li>
* <li>Location for inserting HAVING expressions (if required)</li>
* <li>Mapping of columns to bean properties</li>
* <li>The order by clause</li>
* </p>
* <p>
* If Ebean is unable to parse out this information (perhaps because the sql
* contains multiple select from keywords etc) then you need to manually specify
* it.
* </p>
* <p>
* Insert ${where} or ${andWhere} into the location where Ebean can insert any
* expressions added to the where clause. Use ${andWhere} if the sql already has
* the WHERE keyword and Ebean will instead start with a AND keyword.
* </p>
* <p>
* Insert ${having} or ${andHaving} into the location where Ebean can insert any
* expressions added to the having clause. Use ${andHaving} if the sql already
* has a HAVING keyword and Ebean will instead start with a AND keyword.
* </p>
* <p>
* Use the columnMapping property if Ebean is unable to determine the columns
* and map them to bean properties.
* </p>
* <p>
* Example with ${andWhere} & ${having}.
* </p>
*
* <pre class="code">
* &#064;SqlSelect(
* name = &quot;explicit.where&quot;,
* query =
* &quot;select t.id, t.title, count(p.id) as score &quot;+
* &quot;from f_topic t, f_topic_post p &quot;+
* &quot;where p.topic_id = t.id ${andWhere} &quot;+
* &quot;group by t.id, t.title ${having}&quot;),
* </pre>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Deprecated
public @interface SqlSelect {
/**
* The name of the query. If left blank this is assumed to be the default
* query for this bean type.
* <p>
* This will default to "default" and in that case becomes the default query
* used for the bean.
* </p>
*/
String name() default "default";
/**
* The tableAlias used when adding where expressions to the query.
*/
String tableAlias() default "";
/**
* The sql select statement.
* <p>
* If this query <em>extends</em> another then this string is appended to the
* parent query string. Often when using <em>extend</em> you will leave the
* query part blank and just specify a where and/or having clauses.
* </p>
* <p>
* This sql <em>CAN NOT</em> contain named parameters. You have to put these
* in the separate where and/or having sections.
* </p>
* <p>
* Ebean automatically tries to determine the location in the sql string for
* putting in additional where or having clauses. If Ebean is unable to
* successfully determine this then you have to explicitly specify these
* locations by including
* <em>${where} or ${andWhere} and ${having} or ${andHaving}</em> in the sql.
* </p>
* <p>
* <b>${where}</b> location of where clause (and will add WHERE ... ) <br/>
* Use this when there is no where clause in the sql. If expressions are added
* to the where clause Ebean will put them in at this location starting with
* the WHERE keyword.
* <p>
* <p>
* <b>${andWhere}</b> <br/>
* Use this instead of ${where} if there IS an existing where clause in the
* sql. Ebean will add the expressions starting with the AND keyword.
* <p>
* <b>${having}</b> location of having clause (and will add HAVING... ) <br/>
* </p>
* <p>
* <b>${andHaving}</b> <br/>
* Use this instead of ${having} when there IS an existing HAVING clause.
* Ebean will add the expressions starting with the AND keyword.
* </p>
* <p>
* You can include one of ${where} OR ${andWhere} but not both.
* </p>
* <p>
* You can include one of ${having} OR ${andHaving} but not both.
* </p>
*/
String query() default "";
/**
* Specify the name of a sql-select query that this one 'extends'.
* <p>
* When a query is extended the sql query contents are appended together. The
* where and having clauses are NOT appended but overridden.
* </p>
*/
String extend() default "";
/**
* Specify a where clause typically containing named parameters.
* <p>
* If a where clause is specified with named parameters then they will need to
* be set on the query via {@link Query#setParameter(String, Object)}.
* </p>
* <p>
* In the example below the query specifies a where clause that includes a
* named parameter "likeTitle".
* </p>
*
* <pre class="code">
* ...
* &#064;Entity
* &#064;Sql(select = {
* ...
* &#064;SqlSelect(
* name = &quot;with.title&quot;,
* extend = &quot;default&quot;,
* debug = true,
* where = &quot;title like :likeTitle&quot;)
* })
* public class ReportTopic
* ...
* </pre>
*
* <p>
* Example use of the above named query.
* </p>
*
* <pre class="code">
*
* Query&lt;ReportTopic&gt; query0 = Ebean.createQuery(ReportTopic.class, &quot;with.title&quot;);
*
* query0.set(&quot;likeTitle&quot;, &quot;Bana%&quot;);
*
* List&lt;ReportTopic&gt; list0 = query0.findList();
* </pre>
*
*/
String where() default "";
/**
* Specify a having clause typically containing named parameters.
* <p>
* If a having clause is specified with named parameters then they will need
* to be set on the query via {@link Query#setParameter(String, Object)}.
* </p>
*/
String having() default "";
/**
* (Optional) Explicitly specify column to property mapping.
* <p>
* This is required when Ebean is unable to parse the sql. This could occur if
* the sql contains multiple select keywords etc.
* </p>
* <p>
* Specify the columns and property names they map to in the format.
* </p>
*
* <pre class="code">
* column1 propertyName1, column2 propertyName2, ...
* </pre>
*
* <p>
* Optionally put a AS keyword between the column and property.
* </p>
*
* <pre class="code">
* // the AS keyword is optional
* column1 AS propertyName1, column2 propertyName2, ...
* </pre>
*
* <p>
* <b>column</b> should contain the table alias if there is one
* </p>
* <p>
* <b>propertyName</b> should match the property name.
* </p>
*
* <p>
* Example mapping 5 columns to properties.
* </p>
*
* <pre class="code">
* columnMapping=&quot;t.id, t.bug_body description, t.bug_title as title, count(p.id) as scoreValue&quot;,
* </pre>
*
* <p>
* Without this set Ebean will parse the sql looking for the select clause and
* try to map the columns to property names. It is expected that Ebean will
* not be able to successfully parse some sql and for those cases you should
* specify the column to property mapping explicitly.
* </p>
*
*/
String columnMapping() default "";
/**
* Set this to true to have debug output when Ebean parses the sql-select.
*/
boolean debug() default false;
}
@@ -1,118 +1,128 @@
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;
import com.avaje.ebean.TxIsolation;
import com.avaje.ebean.TxType;
/**
* Specify transaction scoping for a method.
* <p>
* <b><i> This is only supported if "Enhancement" is used via javaagent, ANT
* task or IDE enhancement plugin etc. </i></b>
* </p>
* <p>
* Note: Currently there are 3 known annotations that perform this role.
* <ul>
* <li>EJB's javax.ejb.TransactionAttribute</li>
* <li>Spring's org.springframework.transaction.annotation.Transactional</li>
* <li>and this one, Ebean's own com.avaje.ebean.annotation.Transactional</li>
* </ul>
* Spring created their one because the EJB annotation does not support features
* such as isolation level and specifying rollbackOn, noRollbackOn exceptions.
* This one exists for Ebean because I agree that the standard one is
* insufficient and don't want to include a dependency on Spring.
* </p>
* <p>
* The default behaviour of EJB (and hence Spring) is to NOT ROLLBACK on checked
* exceptions. I find this very counter-intuitive. Ebean will provide a property
* to set the default behaviour to rollback on any exception and optionally
* change the setting to be consistent with EJB/Spring if people wish to do so.
* </p>
*
* <pre class="code">
*
* // a normal class
* public class MySimpleUserService {
*
* // this method is transactional automatically handling
* // transaction begin, commit and rollback etc
* &#064;Transactional
* public void runInTrans() throws IOException {
*
* // tasks performed within the transaction
* ...
* // find some objects
* Customer cust = Ebean.find(Customer.class, 1);
*
* Order order = ...;
* ...
* // save some objects
* Ebean.save(customer);
* 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)
public @interface Transactional {
/**
* The type of transaction scoping. Defaults to REQUIRED.
*/
TxType type() default TxType.REQUIRED;
/**
* The transaction isolation level this transaction should have.
* <p>
* This will only be used if this scope creates the transaction. If the
* transaction has already started then this will currently be ignored (you
* could argue that it should throw an exception).
* </p>
*/
TxIsolation isolation() default TxIsolation.DEFAULT;
/**
* Set this to true if the transaction should be only contain queries.
*/
boolean readOnly() default false;
/**
* The name of the server that you want the transaction to be created from.
* <p>
* If left blank the 'default' server is used.
* </p>
*/
String serverName() default "";
// int timeout() default 0;
/**
* The throwable's that will explicitly cause a rollback to occur.
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* The throwable's that will explicitly NOT cause a rollback to occur.
*/
Class<? extends Throwable>[] noRollbackFor() default {};
};
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;
import com.avaje.ebean.TxIsolation;
import com.avaje.ebean.TxType;
import com.avaje.ebean.config.PersistBatch;
/**
* Specify transaction scoping for a method.
* <p>
* <b><i> This is only supported if "Enhancement" is used via javaagent, ANT
* task or IDE enhancement plugin etc. </i></b>
* </p>
* <p>
* Note: Currently there are 3 known annotations that perform this role.
* <ul>
* <li>EJB's javax.ejb.TransactionAttribute</li>
* <li>Spring's org.springframework.transaction.annotation.Transactional</li>
* <li>and this one, Ebean's own com.avaje.ebean.annotation.Transactional</li>
* </ul>
* Spring created their one because the EJB annotation does not support features
* such as isolation level and specifying rollbackOn, noRollbackOn exceptions.
* This one exists for Ebean because I agree that the standard one is
* insufficient and don't want to include a dependency on Spring.
* </p>
* <p>
* The default behaviour of EJB (and hence Spring) is to NOT ROLLBACK on checked
* exceptions. I find this very counter-intuitive. Ebean will provide a property
* to set the default behaviour to rollback on any exception and optionally
* change the setting to be consistent with EJB/Spring if people wish to do so.
* </p>
*
* <pre>{@code
*
* // a normal class
* public class MySimpleUserService {
*
* // this method is transactional automatically handling
* // transaction begin, commit and rollback etc
* @Transactional
* public void runInTrans() throws IOException {
*
* // tasks performed within the transaction
* ...
* // find some objects
* Customer cust = ebeanServer.find(Customer.class, 42);
*
* Order order = ...;
* ...
* // save some objects
* ebeanServer.save(customer);
* ebeanServer.save(order);
* }
*
* }</pre>
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
/**
* The type of transaction scoping. Defaults to REQUIRED.
*/
TxType type() default TxType.REQUIRED;
/**
* Persist batch mode for the transaction.
*/
PersistBatch batch() default PersistBatch.INHERIT;
/**
* Persist batch mode for the request if not set on the transaction.
* <p>
* If batch is set to NONE then batchOnCascade can be set to INSERT or ALL
* and then each save(), delete(), insert(), update() request that cascades
* to child beans can use JDBC batch.
* </p>
*/
PersistBatch batchOnCascade() default PersistBatch.INHERIT;
/**
* The batch size to use when using JDBC batch mode.
* <p>
* If unset this defaults to the value set in ServerConfig.
* </p>
*/
int batchSize() default 0;
/**
* The transaction isolation level this transaction should have.
* <p>
* This will only be used if this scope creates the transaction. If the
* transaction has already started then this will currently be ignored (you
* could argue that it should throw an exception).
* </p>
*/
TxIsolation isolation() default TxIsolation.DEFAULT;
/**
* Set this to true if the transaction should be only contain queries.
*/
boolean readOnly() default false;
/**
* The name of the server that you want the transaction to be created from.
* <p>
* If left blank the 'default' server is used.
* </p>
*/
String serverName() default "";
// int timeout() default 0;
/**
* The Throwable's that will explicitly cause a rollback to occur.
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* The Throwable's that will explicitly NOT cause a rollback to occur.
*/
Class<? extends Throwable>[] noRollbackFor() default {};
}
@@ -1,34 +1,34 @@
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;
/**
* Specify the update mode for the specific entity type.
* <p>
* Control whether all 'loaded' properties are included in an Update or whether
* just properties that have changed will be included in the update.
* </p>
* <p>
* Note that the default can be set via ebean.properties.
* </p>
*
* <pre>
* ## Set to update all loaded properties
* ebean.updateChangesOnly=false
* </pre>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface UpdateMode {
/**
* Set to false if you want to include all the 'loaded' properties in the
* update. Otherwise, just the properties that have changed will be included
* in the update.
*/
boolean updateChangesOnly() default true;
};
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;
/**
* Specify the update mode for the specific entity type.
* <p>
* Control whether all 'loaded' properties are included in an Update or whether
* just properties that have changed will be included in the update.
* </p>
* <p>
* Note that the default can be set via ebean.properties.
* </p>
*
* <pre>
* ## Set to update all loaded properties
* ebean.updateChangesOnly=false
* </pre>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface UpdateMode {
/**
* Set to false if you want to include all the 'loaded' properties in the
* update. Otherwise, just the properties that have changed will be included
* in the update.
*/
boolean updateChangesOnly() default true;
}
@@ -1,16 +1,16 @@
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;
/**
* For a timestamp property that is set to the datetime when the entity was last
* updated.
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface UpdatedTimestamp {
};
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;
/**
* For a timestamp property that is set to the datetime when the entity was last
* updated.
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface UpdatedTimestamp {
}
@@ -1,51 +1,54 @@
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;
/**
* Add an Literal to add to the where clause when a many property (List, Set or
* Map) is loaded or refreshed.
*
* <pre class="code">
* // on a OneToMany property...
*
* &#064;OneToMany
* &#064;Where(clause = &quot;deleted='y'&quot;)
* List&lt;Topic&gt; topics;
* </pre>
*
* <p>
* Note that you can include "${ta}" as a place holder for the table alias if
* you need to include the table alias in the clause.
* </p>
*
* <pre class="code">
* // ... including the ${ta} table alias placeholder...
*
* &#064;OneToMany
* &#064;Where(clause = &quot;${ta}.deleted='y'&quot;)
* List&lt;Topic&gt; topics;
* </pre>
*
* <p>
* This will be added to the where clause when lazy loading the OneToMany
* property or when there is a join to that OneToMany property.
* </p>
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Where {
/**
* The clause added to the lazy load query.
* <p>
* Note that you can include "${ta}" as a place holder for the table alias if
* you need to include the table alias in the clause.
* </p>
*/
String clause();
};
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;
/**
* Add an Literal to add to the where clause when a many property (List, Set or
* Map) is loaded or refreshed.
*
* <pre>{@code
*
* // on a OneToMany property...
*
* @OneToMany
* @Where(clause = "deleted='y'")
* List<Topic> topics;
*
* }</pre>
*
* <p>
* Note that you can include "${ta}" as a place holder for the table alias if
* you need to include the table alias in the clause.
* </p>
*
* <pre>{@code
* // ... including the ${ta} table alias placeholder...
*
* @OneToMany
* @Where(clause = "${ta}.deleted='y'")
* List<Topic> topics;
*
* }</pre>
*
* <p>
* This will be added to the where clause when lazy loading the OneToMany
* property or when there is a join to that OneToMany property.
* </p>
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Where {
/**
* The clause added to the lazy load query.
* <p>
* Note that you can include "${ta}" as a place holder for the table alias if
* you need to include the table alias in the clause.
* </p>
*/
String clause();
}
@@ -1,243 +1,201 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.Query;
/**
* Lazy loading capable Maps, Lists and Sets.
* <p>
* This also includes the ability to listen for additions and removals to or
* from the Map Set or List. The purpose of gathering the additions and removals
* is to support persisting ManyToMany objects. The additions and removals
* become inserts and deletes from the intersection table.
* </p>
* <p>
* Technically this is <em>NOT</em> an extension of
* <em>java.util.Collection</em>. The reason being that java.util.Map is not a
* Collection. I realise this makes this name confusing so I apologise for that.
* </p>
*/
public interface BeanCollection<E> extends Serializable {
public enum ModifyListenMode {
/** The common mode */
NONE,
/** Mode used for PrivateOwned */
REMOVALS,
/** Mode used for ManyToMany relationships */
ALL
}
/**
* Return the bean that owns this collection.
*/
public Object getOwnerBean();
/**
* Return the bean property name this collection represents.
*/
public String getPropertyName();
/**
* Return the index position of this collection in the lazy/query loader.
* <p>
* Used for batch loading of collections.
* </p>
*/
public int getLoaderIndex();
/**
* Check after the lazy load that the underlying collection is not null
* (handle case where join to many not outer).
* <p>
* That is, if the collection was not loaded due to filterMany predicates etc
* then make sure the collection is set to empty.
* </p>
*/
public boolean checkEmptyLazyLoad();
/**
* Return the filter (if any) that was used in building this collection.
* <p>
* This is so that the filter can be applied on refresh.
* </p>
*/
public ExpressionList<?> getFilterMany();
/**
* Set the filter that was used in building this collection.
*/
public void setFilterMany(ExpressionList<?> filterMany);
/**
* Set when this collection is being loaded via a background thread.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void setBackgroundFetch(Future<Integer> future);
/**
* Wait for the fetch to complete with a given timeout.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void backgroundFetchWait(long wait, TimeUnit timeUnit);
/**
* Wait for the fetch to complete.
* <p>
* Refer to {@link Query#setBackgroundFetchAfter(int)}
* </p>
*/
public void backgroundFetchWait();
/**
* Set a listener to be notified when the BeanCollection is first touched.
*/
public void setBeanCollectionTouched(BeanCollectionTouched notify);
/**
* Set the loader that will be used to lazy/query load this collection.
*/
public void setLoader(int beanLoaderIndex, BeanCollectionLoader beanLoader);
/**
* Set to true if you want the BeanCollection to be treated as read only. This
* means no elements can be added or removed etc.
*/
public void setReadOnly(boolean readOnly);
/**
* Return true if the collection should be treated as readOnly and no elements
* can be added or removed etc.
*/
public boolean isReadOnly();
/**
* Add the bean to the collection.
* <p>
* This is disallowed for BeanMap.
* </p>
*/
public void internalAdd(Object bean);
/**
* Returns the underlying List Set or Map object.
*/
public Object getActualCollection();
/**
* Return the number of elements in the List Set or Map.
*/
public int size();
/**
* Return true if the List Set or Map is empty.
*/
public boolean isEmpty();
/**
* Returns the underlying details as an iterator.
* <p>
* Note that for maps this returns the entrySet as we need the keys of the
* map.
* </p>
*/
public Collection<E> getActualDetails();
/**
* Set to true if maxRows was hit and there are actually more rows available.
* <p>
* Can be used by client code that is paging through results using
* setFirstRow() setMaxRows(). If this returns true then the client can
* display a 'next' button etc.
* </p>
*/
public boolean hasMoreRows();
/**
* Set to true when maxRows is hit but there are actually more rows available.
* This is set so that client code knows that there is more data available.
*/
public void setHasMoreRows(boolean hasMoreRows);
/**
* Returns true if the fetch has finished. False if the fetch is continuing in
* a background thread.
*/
public boolean isFinishedFetch();
/**
* Set to true when a fetch has finished. Used when a fetch continues in the
* background.
*/
public void setFinishedFetch(boolean finishedFetch);
/**
* return true if there are real rows held. Return false is this is using
* Deferred fetch to lazy load the rows and the rows have not yet been
* fetched.
*/
public boolean isPopulated();
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
public boolean isReference();
/**
* Set modify listening on or off. This is used to keep track of objects that
* have been added to or removed from the list set or map.
* <p>
* This is required only for ManyToMany collections. The additions and
* deletions are used to insert or delete entries from the intersection table.
* Otherwise modifyListening is false.
* </p>
*/
public void setModifyListening(ModifyListenMode modifyListenMode);
/**
* Add an object to the additions list.
* <p>
* This will potentially end up as an insert into a intersection table for a
* ManyToMany.
* </p>
*/
public void modifyAddition(E bean);
/**
* Add an object to the deletions list.
* <p>
* This will potentially end up as an delete from an intersection table for a
* ManyToMany.
* </p>
*/
public void modifyRemoval(Object bean);
/**
* Return the list of objects added to the list set or map. These will used to
* insert rows into the intersection table of a ManyToMany.
*/
public Set<E> getModifyAdditions();
/**
* Return the list of objects removed from the list set or map. These will
* used to delete rows from the intersection table of a ManyToMany.
*/
public Set<E> getModifyRemovals();
/**
* Reset the set of additions and deletions. This is called after the
* additions and removals have been processed.
*/
public void modifyReset();
}
package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import com.avaje.ebean.ExpressionList;
/**
* Lazy loading capable Maps, Lists and Sets.
* <p>
* This also includes the ability to listen for additions and removals to or
* from the Map Set or List. The purpose of gathering the additions and removals
* is to support persisting ManyToMany objects. The additions and removals
* become inserts and deletes from the intersection table.
* </p>
* <p>
* Technically this is <em>NOT</em> an extension of
* <em>java.util.Collection</em>. The reason being that java.util.Map is not a
* Collection. I realise this makes this name confusing so I apologise for that.
* </p>
*/
public interface BeanCollection<E> extends Serializable {
enum ModifyListenMode {
/** The common mode */
NONE,
/** Mode used for PrivateOwned */
REMOVALS,
/** Mode used for ManyToMany relationships */
ALL
}
/**
* Reset the collection back to an empty state ready for reloading.
* <p>
* This is done as part of bean refresh.
*/
void reset(EntityBean ownerBean, String propertyName);
/**
* Return true if the collection is empty and untouched. Used to detect if a
* collection was 'cleared' deliberately or just un-initialised.
*/
boolean isEmptyAndUntouched();
/**
* Return the bean that owns this collection.
*/
EntityBean getOwnerBean();
/**
* Return the bean property name this collection represents.
*/
String getPropertyName();
/**
* Check after the lazy load that the underlying collection is not null
* (handle case where join to many not outer).
* <p>
* That is, if the collection was not loaded due to filterMany predicates etc
* then make sure the collection is set to empty.
* </p>
*/
boolean checkEmptyLazyLoad();
/**
* Return the filter (if any) that was used in building this collection.
* <p>
* This is so that the filter can be applied on refresh.
* </p>
*/
ExpressionList<?> getFilterMany();
/**
* Set the filter that was used in building this collection.
*/
void setFilterMany(ExpressionList<?> filterMany);
/**
* Set a listener to be notified when the BeanCollection is first touched.
*/
void setBeanCollectionTouched(BeanCollectionTouched notify);
/**
* Return true if the collection has been registered with the batch loading context.
*/
boolean isRegisteredWithLoadContext();
/**
* Set the loader that will be used to lazy/query load this collection.
* <p>
* This is effectively the batch loading context this collection is registered with.
* </p>
*/
void setLoader(BeanCollectionLoader beanLoader);
/**
* Set to true if you want the BeanCollection to be treated as read only. This
* means no elements can be added or removed etc.
*/
void setReadOnly(boolean readOnly);
/**
* Return true if the collection should be treated as readOnly and no elements
* can be added or removed etc.
*/
boolean isReadOnly();
/**
* Add the bean to the collection.
* <p>
* This is disallowed for BeanMap.
* </p>
*/
void internalAdd(Object bean);
/**
* Return the number of elements in the List Set or Map.
*/
int size();
/**
* Return true if the List Set or Map is empty.
*/
boolean isEmpty();
/**
* Returns the underlying collection of beans from the Set, Map or List.
*/
Collection<E> getActualDetails();
/**
* Returns the underlying entries so for Maps this is a collection of
* Map.Entry.
* <p>
* For maps this returns the entrySet as we need the keys of the map.
* </p>
*/
Collection<?> getActualEntries();
/**
* 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
* fetched.
*/
boolean isPopulated();
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
boolean isReference();
/**
* Set modify listening on or off. This is used to keep track of objects that
* have been added to or removed from the list set or map.
* <p>
* This is required only for ManyToMany collections. The additions and
* deletions are used to insert or delete entries from the intersection table.
* Otherwise modifyListening is false.
* </p>
*/
void setModifyListening(ModifyListenMode modifyListenMode);
/**
* Add an object to the additions list.
* <p>
* This will potentially end up as an insert into a intersection table for a
* ManyToMany.
* </p>
*/
void modifyAddition(E bean);
/**
* Add an object to the deletions list.
* <p>
* This will potentially end up as an delete from an intersection table for a
* ManyToMany.
* </p>
*/
void modifyRemoval(Object bean);
/**
* Return the list of objects added to the list set or map. These will used to
* insert rows into the intersection table of a ManyToMany.
*/
Set<E> getModifyAdditions();
/**
* Return the list of objects removed from the list set or map. These will
* used to delete rows from the intersection table of a ManyToMany.
*/
Set<E> getModifyRemovals();
/**
* Reset the set of additions and deletions. This is called after the
* additions and removals have been processed.
*/
void modifyReset();
}
@@ -1,16 +1,16 @@
package com.avaje.ebean.bean;
/**
* Interface to define the addition of a bean to the underlying collection.
* <p>
* For maps this takes into account the map key. For List and Set this simply
* adds the bean to the underlying list or set.
* </p>
*/
public interface BeanCollectionAdd {
/**
* Add a loaded bean to the collection.
*/
public void addBean(Object bean);
}
package com.avaje.ebean.bean;
/**
* Interface to define the addition of a bean to the underlying collection.
* <p>
* For maps this takes into account the map key. For List and Set this simply
* adds the bean to the underlying list or set.
* </p>
*/
public interface BeanCollectionAdd {
/**
* Add a loaded bean to the collection.
*/
void addBean(EntityBean bean);
}
@@ -1,21 +1,21 @@
package com.avaje.ebean.bean;
/**
* Loads a entity bean collection.
* <p>
* Typically invokes lazy loading for a single or batch of collections.
* </p>
*/
public interface BeanCollectionLoader {
/**
* Return the name of the associated EbeanServer.
*/
public String getName();
/**
* Invoke the lazy loading for this bean collection.
*/
public void loadMany(BeanCollection<?> collection, boolean onlyIds);
}
package com.avaje.ebean.bean;
/**
* Loads a entity bean collection.
* <p>
* Typically invokes lazy loading for a single or batch of collections.
* </p>
*/
public interface BeanCollectionLoader {
/**
* Return the name of the associated EbeanServer.
*/
String getName();
/**
* Invoke the lazy loading for this bean collection.
*/
void loadMany(BeanCollection<?> collection, boolean onlyIds);
}
@@ -1,20 +1,20 @@
package com.avaje.ebean.bean;
/**
* Used to specify a listener to be notified when a BeanCollection is first
* used.
* <p>
* To use this you can set a BeanCollectionTouched onto a BeanCollection before
* it has been used. When the BeanCollection is first used by the client code
* then the BeanCollectionTouched is notified. It can only be notified once.
* </p>
*
* @author rbygrave
*/
public interface BeanCollectionTouched {
/**
* Notify the listener that the bean collection has been used.
*/
public void notifyTouched(BeanCollection<?> c);
}
package com.avaje.ebean.bean;
/**
* Used to specify a listener to be notified when a BeanCollection is first
* used.
* <p>
* To use this you can set a BeanCollectionTouched onto a BeanCollection before
* it has been used. When the BeanCollection is first used by the client code
* then the BeanCollectionTouched is notified. It can only be notified once.
* </p>
*
* @author rbygrave
*/
public interface BeanCollectionTouched {
/**
* Notify the listener that the bean collection has been used.
*/
void notifyTouched(BeanCollection<?> c);
}
@@ -1,21 +1,21 @@
package com.avaje.ebean.bean;
/**
* Loads a entity bean.
* <p>
* Typically invokes lazy loading for a single or batch of entity beans.
* </p>
*/
public interface BeanLoader {
/**
* Return the name of the associated EbeanServer.
*/
public String getName();
/**
* Invoke the lazy loading for this bean.
*/
public void loadBean(EntityBeanIntercept ebi);
}
package com.avaje.ebean.bean;
/**
* Loads a entity bean.
* <p>
* Typically invokes lazy loading for a single or batch of entity beans.
* </p>
*/
public interface BeanLoader {
/**
* Return the name of the associated EbeanServer.
*/
String getName();
/**
* Invoke the lazy loading for this bean.
*/
void loadBean(EntityBeanIntercept ebi);
}
+118 -98
View File
@@ -1,98 +1,118 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
/**
* Represent the call stack (stack trace elements).
* <p>
* Used with a query to identify a CallStackQuery for AutoFetch automatic query
* tuning.
* </p>
* <p>
* This is used so that a single query called from different methods can be
* tuned for each different call stack.
* </p>
* <p>
* Note the call stack is trimmed to remove the common ebean internal elements.
* </p>
*/
public final class CallStack implements Serializable {
private static final long serialVersionUID = -8590644046907438579L;
private final String zeroHash;
private final String pathHash;
private final StackTraceElement[] callStack;
public CallStack(StackTraceElement[] callStack) {
this.callStack = callStack;
this.zeroHash = enc(callStack[0].hashCode());
int hc = 0;
for (int i = 1; i < callStack.length; i++) {
hc = 31 * hc + callStack[i].hashCode();
}
this.pathHash = enc(hc);
}
/**
* Return the first element of the call stack.
*/
public StackTraceElement getFirstStackTraceElement() {
return callStack[0];
}
/**
* Return the call stack.
*/
public StackTraceElement[] getCallStack() {
return callStack;
}
/**
* Return the hash for the first stack element.
*/
public String getZeroHash() {
return zeroHash;
}
/**
* Return the hash for the stack elements (excluding first stack element).
*/
public String getPathHash() {
return pathHash;
}
public String toString() {
return zeroHash + ":" + pathHash + ":" + callStack[0];
}
public String getOriginKey(int queryHash) {
return zeroHash + "." + enc(queryHash) + "." + pathHash;
}
private static final int radix = 1 << 6;
private static final int mask = radix - 1;
/**
* Convert the integer to unsigned base 64.
*/
public static String enc(int i) {
char[] buf = new char[32];
int charPos = 32;
do {
buf[--charPos] = intToBase64[i & mask];
i >>>= 6;
} while (i != 0);
return new String(buf, charPos, (32 - charPos));
}
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
}
package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Arrays;
/**
* Represent the call stack (stack trace elements).
* <p>
* Used with a query to identify a CallStackQuery for AutoFetch automatic query
* tuning.
* </p>
* <p>
* This is used so that a single query called from different methods can be
* tuned for each different call stack.
* </p>
* <p>
* Note the call stack is trimmed to remove the common ebean internal elements.
* </p>
*/
public final class CallStack implements Serializable {
private static final long serialVersionUID = -8590644046907438579L;
private final String zeroHash;
private final String pathHash;
private final StackTraceElement[] callStack;
public CallStack(StackTraceElement[] callStack) {
this.callStack = callStack;
this.zeroHash = enc(callStack[0].hashCode());
int hc = 0;
for (int i = 1; i < callStack.length; i++) {
hc = 31 * hc + callStack[i].hashCode();
}
this.pathHash = enc(hc);
}
public int hashCode() {
int hc = 0;
for (int i = 0; i < callStack.length; i++) {
hc = 31 * hc + callStack[i].hashCode();
}
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof CallStack)) {
return false;
}
CallStack e = (CallStack) obj;
return Arrays.equals(callStack, e.callStack);
}
/**
* Return the first element of the call stack.
*/
public StackTraceElement getFirstStackTraceElement() {
return callStack[0];
}
/**
* Return the call stack.
*/
public StackTraceElement[] getCallStack() {
return callStack;
}
/**
* Return the hash for the first stack element.
*/
public String getZeroHash() {
return zeroHash;
}
/**
* Return the hash for the stack elements (excluding first stack element).
*/
public String getPathHash() {
return pathHash;
}
public String toString() {
return zeroHash + ":" + pathHash + ":" + callStack[0];
}
public String getOriginKey(int queryHash) {
return zeroHash + "." + enc(queryHash) + "." + pathHash;
}
private static final int radix = 1 << 6;
private static final int mask = radix - 1;
/**
* Convert the integer to unsigned base 64.
*/
public static String enc(int i) {
char[] buf = new char[32];
int charPos = 32;
do {
buf[--charPos] = intToBase64[i & mask];
i >>>= 6;
} while (i != 0);
return new String(buf, charPos, (32 - charPos));
}
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
}
+114 -127
View File
@@ -1,127 +1,114 @@
package com.avaje.ebean.bean;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
/**
* Bean that is aware of EntityBeanIntercept.
* <p>
* This interface and implementation of these methods is added to Entity Beans
* via instrumentation. These methods have a funny _ebean_ prefix to avoid any
* clash with normal methods these beans would have. These methods are not for
* general application consumption.
* </p>
*/
public interface EntityBean extends Serializable {
/**
* Return the enhancement marker value.
* <p>
* This is the class name of the enhanced class and used to check that all
* entity classes are enhanced (specifically not just a super class).
* </p>
*/
public String _ebean_getMarker();
/**
* Create and return a new entity bean instance.
*/
public Object _ebean_newInstance();
/**
* Add a PropertyChangeListener to this bean.
*/
public void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a PropertyChangeListener from this bean.
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Generated method that sets the loaded state on all the embedded beans on
* this entity bean by using EntityBeanIntercept.setEmbeddedLoaded(Object o);
*/
public void _ebean_setEmbeddedLoaded();
/**
* Return true if any embedded beans are new or dirty.
*/
public boolean _ebean_isEmbeddedNewOrDirty();
/**
* Return the intercept for this object.
*/
public EntityBeanIntercept _ebean_getIntercept();
/**
* Similar to _ebean_getIntercept() except it checks to see if the intercept
* field is null and will create it if required.
* <p>
* This is really only required when transientInternalFields=true as an
* enhancement option. In this case the intercept field is transient and will
* be null after a bean has been deserialised.
* </p>
* <p>
* This transientInternalFields=true option was to support some serialization
* frameworks that can't take into account our ebean fields.
* </p>
*/
public EntityBeanIntercept _ebean_intercept();
/**
* Create a copy of this entity bean.
* <p>
* This occurs when a bean is changed. The copy represents the bean as it was
* initially (oldValues) before any changes where made. This is used for
* optimistic concurrency control.
* </p>
*/
public Object _ebean_createCopy();
/**
* Return the fields in their index order.
*/
public String[] _ebean_getFieldNames();
/**
* Set the value of a field of an entity bean of this type.
* <p>
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading and oldValues creation.
* </p>
*
* @param fieldIndex
* the index of the field
* @param entityBean
* the entityBean of this type to modify
* @param value
* the value to set
*/
public void _ebean_setField(int fieldIndex, Object entityBean, Object value);
/**
* Set the field value with interception.
*/
public void _ebean_setFieldIntercept(int fieldIndex, Object entityBean, Object value);
/**
* Return the value of a field from an entity bean of this type.
* <p>
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading.
* </p>
*
* @param fieldIndex
* the index of the field
* @param entityBean
* the entityBean to get the value from
*/
public Object _ebean_getField(int fieldIndex, Object entityBean);
/**
* Return the field value with interception.
*/
public Object _ebean_getFieldIntercept(int fieldIndex, Object entityBean);
}
package com.avaje.ebean.bean;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
/**
* Bean that is aware of EntityBeanIntercept.
* <p>
* This interface and implementation of these methods is added to Entity Beans
* via instrumentation. These methods have a funny _ebean_ prefix to avoid any
* clash with normal methods these beans would have. These methods are not for
* general application consumption.
* </p>
*/
public interface EntityBean extends Serializable {
String[] _ebean_getPropertyNames();
String _ebean_getPropertyName(int pos);
/**
* Return the enhancement marker value.
* <p>
* This is the class name of the enhanced class and used to check that all
* entity classes are enhanced (specifically not just a super class).
* </p>
*/
String _ebean_getMarker();
/**
* Create and return a new entity bean instance.
*/
Object _ebean_newInstance();
/**
* Add a PropertyChangeListener to this bean.
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a PropertyChangeListener from this bean.
*/
void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Generated method that sets the loaded state on all the embedded beans on
* this entity bean by using EntityBeanIntercept.setEmbeddedLoaded(Object o);
*/
void _ebean_setEmbeddedLoaded();
/**
* Return true if any embedded beans are new or dirty.
*/
boolean _ebean_isEmbeddedNewOrDirty();
/**
* Return the intercept for this object.
*/
EntityBeanIntercept _ebean_getIntercept();
/**
* Similar to _ebean_getIntercept() except it checks to see if the intercept
* field is null and will create it if required.
* <p>
* This is really only required when transientInternalFields=true as an
* enhancement option. In this case the intercept field is transient and will
* be null after a bean has been deserialised.
* </p>
* <p>
* This transientInternalFields=true option was to support some serialization
* frameworks that can't take into account our ebean fields.
* </p>
*/
EntityBeanIntercept _ebean_intercept();
/**
* Create a copy of this entity bean.
* <p>
* This occurs when a bean is changed. The copy represents the bean as it was
* initially (oldValues) before any changes where made. This is used for
* optimistic concurrency control.
* </p>
*/
Object _ebean_createCopy();
/**
* Set the value of a field of an entity bean of this type.
* <p>
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading and oldValues creation.
* </p>
*/
void _ebean_setField(int fieldIndex, Object value);
/**
* Set the field value with interception.
*/
void _ebean_setFieldIntercept(int fieldIndex, Object value);
/**
* Return the value of a field from an entity bean of this type.
* <p>
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading.
* </p>
*/
Object _ebean_getField(int fieldIndex);
/**
* Return the field value with interception.
*/
Object _ebean_getFieldIntercept(int fieldIndex);
}
File diff suppressed because it is too large Load Diff
@@ -1,126 +1,126 @@
package com.avaje.ebean.bean;
import java.lang.ref.WeakReference;
import java.util.HashSet;
/**
* Collects profile information for a bean (or reference/proxy bean) at a given
* AutoFetchNode.
* <p>
* The AutoFetchNode identifies the location of the bean in the object graph.
* </p>
* <p>
* It has to use a weak reference so as to ensure that it does not stop the
* associated bean from being garbage collected.
* </p>
*/
public final class NodeUsageCollector {
/**
* The point in the object graph for a specific query and call stack point.
*/
private final ObjectGraphNode node;
/**
* Weak to allow garbage collection.
*/
private final WeakReference<NodeUsageListener> managerRef;
/**
* The properties used at this profile point.
*/
private final HashSet<String> used = new HashSet<String>();
/**
* set to true if the bean is modified (setter called)
*/
private boolean modified;
/**
* The property that cause a reference to lazy load.
*/
private String loadProperty;
public NodeUsageCollector(ObjectGraphNode node, WeakReference<NodeUsageListener> managerRef) {
this.node = node;
// weak to allow garbage collection.
this.managerRef = managerRef;
}
/**
* The bean has been modified by a setter method.
*/
public void setModified() {
modified = true;
}
/**
* Add the name of a property that has been used.
*/
public void addUsed(String property) {
used.add(property);
}
/**
* The property that invoked a lazy load.
*/
public void setLoadProperty(String loadProperty) {
this.loadProperty = loadProperty;
}
/**
* Publish the usage info to the manager.
*/
private void publishUsageInfo() {
NodeUsageListener manager = managerRef.get();
if (manager != null) {
manager.collectNodeUsage(this);
}
}
/**
* publish the collected usage information when garbage collection occurs.
*/
@Override
protected void finalize() throws Throwable {
publishUsageInfo();
super.finalize();
}
/**
* Return the associated node which identifies the location in the object
* graph of the bean/reference.
*/
public ObjectGraphNode getNode() {
return node;
}
/**
* Return true if no properties where used.
*/
public boolean isEmpty() {
return used.isEmpty();
}
/**
* Return the set of used properties.
*/
public HashSet<String> getUsed() {
return used;
}
/**
* Return true if the bean was modified by a setter.
*/
public boolean isModified() {
return modified;
}
public String getLoadProperty() {
return loadProperty;
}
public String toString() {
return node + " read:" + used + " modified:" + modified;
}
}
package com.avaje.ebean.bean;
import java.lang.ref.WeakReference;
import java.util.HashSet;
/**
* Collects profile information for a bean (or reference/proxy bean) at a given
* AutoFetchNode.
* <p>
* The AutoFetchNode identifies the location of the bean in the object graph.
* </p>
* <p>
* It has to use a weak reference so as to ensure that it does not stop the
* associated bean from being garbage collected.
* </p>
*/
public final class NodeUsageCollector {
/**
* The point in the object graph for a specific query and call stack point.
*/
private final ObjectGraphNode node;
/**
* Weak to allow garbage collection.
*/
private final WeakReference<NodeUsageListener> managerRef;
/**
* The properties used at this profile point.
*/
private final HashSet<String> used = new HashSet<String>();
/**
* set to true if the bean is modified (setter called)
*/
private boolean modified;
/**
* The property that cause a reference to lazy load.
*/
private String loadProperty;
public NodeUsageCollector(ObjectGraphNode node, WeakReference<NodeUsageListener> managerRef) {
this.node = node;
// weak to allow garbage collection.
this.managerRef = managerRef;
}
/**
* The bean has been modified by a setter method.
*/
public void setModified() {
modified = true;
}
/**
* Add the name of a property that has been used.
*/
public void addUsed(String property) {
used.add(property);
}
/**
* The property that invoked a lazy load.
*/
public void setLoadProperty(String loadProperty) {
this.loadProperty = loadProperty;
}
/**
* Publish the usage info to the manager.
*/
private void publishUsageInfo() {
NodeUsageListener manager = managerRef.get();
if (manager != null) {
manager.collectNodeUsage(this);
}
}
/**
* publish the collected usage information when garbage collection occurs.
*/
@Override
protected void finalize() throws Throwable {
publishUsageInfo();
super.finalize();
}
/**
* Return the associated node which identifies the location in the object
* graph of the bean/reference.
*/
public ObjectGraphNode getNode() {
return node;
}
/**
* Return true if no properties where used.
*/
public boolean isEmpty() {
return used.isEmpty();
}
/**
* Return the set of used properties.
*/
public HashSet<String> getUsed() {
return used;
}
/**
* Return true if the bean was modified by a setter.
*/
public boolean isModified() {
return modified;
}
public String getLoadProperty() {
return loadProperty;
}
public String toString() {
return node + " read:" + used + " modified:" + modified;
}
}
@@ -1,16 +1,16 @@
package com.avaje.ebean.bean;
/**
* Collects the profile information.
*/
public interface NodeUsageListener {
/**
* Collect node usage "profiling" information.
* <p>
* This is the properties that are used for a given bean in the object graph.
* This information is used by autoFetch to tune queries.
* </p>
*/
public void collectNodeUsage(NodeUsageCollector collector);
}
package com.avaje.ebean.bean;
/**
* Collects the profile information.
*/
public interface NodeUsageListener {
/**
* Collect node usage "profiling" information.
* <p>
* This is the properties that are used for a given bean in the object graph.
* This information is used by autoFetch to tune queries.
* </p>
*/
void collectNodeUsage(NodeUsageCollector collector);
}
@@ -1,69 +1,88 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
/**
* Identifies a unique node of an object graph.
* <p>
* It represents a location relative to the root of an object graph and specific
* to a query and call stack hash.
* </p>
*/
public final class ObjectGraphNode implements Serializable {
private static final long serialVersionUID = 2087081778650228996L;
/**
* Identifies the origin.
*/
private final ObjectGraphOrigin originQueryPoint;
/**
* The path relative to the root.
*/
private final String path;
/**
* Create at a sub level.
*/
public ObjectGraphNode(ObjectGraphNode parent, String path) {
this.originQueryPoint = parent.getOriginQueryPoint();
this.path = parent.getChildPath(path);
}
/**
* Create an the root level.
*/
public ObjectGraphNode(ObjectGraphOrigin originQueryPoint, String path) {
this.originQueryPoint = originQueryPoint;
this.path = path;
}
/**
* Return the origin query point.
*/
public ObjectGraphOrigin getOriginQueryPoint() {
return originQueryPoint;
}
private String getChildPath(String childPath) {
if (path == null) {
return childPath;
} else if (childPath == null) {
return path;
} else {
return path + "." + childPath;
}
}
/**
* Return the path relative to the root.
*/
public String getPath() {
return path;
}
public String toString() {
return "origin:" + originQueryPoint + " " + ":" + path + ":" + path;
}
}
package com.avaje.ebean.bean;
import java.io.Serializable;
/**
* Identifies a unique node of an object graph.
* <p>
* It represents a location relative to the root of an object graph and specific
* to a query and call stack hash.
* </p>
*/
public final class ObjectGraphNode implements Serializable {
private static final long serialVersionUID = 2087081778650228996L;
/**
* Identifies the origin.
*/
private final ObjectGraphOrigin originQueryPoint;
/**
* The path relative to the root.
*/
private final String path;
/**
* Create at a sub level.
*/
public ObjectGraphNode(ObjectGraphNode parent, String path) {
this.originQueryPoint = parent.getOriginQueryPoint();
this.path = parent.getChildPath(path);
}
/**
* Create an the root level.
*/
public ObjectGraphNode(ObjectGraphOrigin originQueryPoint, String path) {
this.originQueryPoint = originQueryPoint;
this.path = path;
}
/**
* Return the origin query point.
*/
public ObjectGraphOrigin getOriginQueryPoint() {
return originQueryPoint;
}
private String getChildPath(String childPath) {
if (path == null) {
return childPath;
} else if (childPath == null) {
return path;
} else {
return path + "." + childPath;
}
}
/**
* Return the path relative to the root.
*/
public String getPath() {
return path;
}
public String toString() {
return "origin:" + originQueryPoint + " path[" + path+"]";
}
public int hashCode() {
int hc = 31 * originQueryPoint.hashCode();
hc = 31 * hc + (path == null ? 0 : path.hashCode());
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ObjectGraphNode)) {
return false;
}
ObjectGraphNode e = (ObjectGraphNode) obj;
return ((e.path == path) || (e.path != null && e.path.equals(path)))
&& e.originQueryPoint.equals(originQueryPoint);
}
}
@@ -1,61 +1,84 @@
package com.avaje.ebean.bean;
import java.io.Serializable;
/**
* Represents a "origin" of an ORM object graph. This combines the call stack
* and query plan hash.
* <p>
* The call stack is included so that the query can have different tuned fetches
* for each unique call stack. For example, a query to fetch a customer could be
* called by three different methods and each can be treated as a separate
* origin point (and autoFetch can tune each one separately).
* </p>
*/
public final class ObjectGraphOrigin implements Serializable {
private static final long serialVersionUID = 410937765287968707L;
private final CallStack callStack;
private final String key;
private final String beanType;
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
this.callStack = callStack;
this.beanType = beanType;
this.key = callStack.getOriginKey(queryHash);
}
/**
* The key includes the queryPlan hash and the callStack hash. This becomes
* the unique identifier for a query point.
*/
public String getKey() {
return key;
}
/**
* The type of bean the query is fetching.
*/
public String getBeanType() {
return beanType;
}
/**
* The call stack involved.
*/
public CallStack getCallStack() {
return callStack;
}
public String getFirstStackElement() {
return callStack.getFirstStackTraceElement().toString();
}
public String toString() {
return key + " " + beanType + " " + callStack.getFirstStackTraceElement();
}
}
package com.avaje.ebean.bean;
import java.io.Serializable;
/**
* Represents a "origin" of an ORM object graph. This combines the call stack
* and query plan hash.
* <p>
* The call stack is included so that the query can have different tuned fetches
* for each unique call stack. For example, a query to fetch a customer could be
* called by three different methods and each can be treated as a separate
* origin point (and autoFetch can tune each one separately).
* </p>
*/
public final class ObjectGraphOrigin implements Serializable {
private static final long serialVersionUID = 410937765287968708L;
private final CallStack callStack;
private final String beanType;
private final int queryHash;
private final String key;
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
this.callStack = callStack;
this.beanType = beanType;
this.queryHash = queryHash;
this.key = callStack.getOriginKey(queryHash);
}
/**
* The key includes the queryPlan hash and the callStack hash. This becomes
* the unique identifier for a query point.
*/
public String getKey() {
return key;
}
/**
* The type of bean the query is fetching.
*/
public String getBeanType() {
return beanType;
}
/**
* The call stack involved.
*/
public CallStack getCallStack() {
return callStack;
}
public String getFirstStackElement() {
return callStack.getFirstStackTraceElement().toString();
}
public String toString() {
return "key["+ key + "] type[" + beanType + "] " + callStack.getFirstStackTraceElement()+" ";
}
public int hashCode() {
int hc = 31 * callStack.hashCode();
hc = 31 * hc + beanType.hashCode();
hc = 31 * hc + queryHash;
return hc;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ObjectGraphOrigin)) {
return false;
}
ObjectGraphOrigin e = (ObjectGraphOrigin) obj;
return e.queryHash == queryHash
&& e.beanType.equals(beanType)
&& e.callStack.equals(callStack);
}
}
@@ -1,52 +1,110 @@
package com.avaje.ebean.bean;
/**
* Holds entity beans by there type and id.
* <p>
* This is used to ensure only one instance for a given entity type and id is
* used to build object graphs from queries and lazy loading.
* </p>
*/
public interface PersistenceContext {
/**
* Put the entity bean into the PersistanceContext.
*/
public void put(Object id, Object bean);
/**
* Put the entity bean into the PersistanceContext if one is not already
* present (for this id).
* <p>
* Returns an existing entity bean (if one is already there) and otherwise
* returns null.
* </p>
*/
public Object putIfAbsent(Object id, Object bean);
/**
* Return an object given its type and unique id.
*/
public Object get(Class<?> beanType, Object uid);
/**
* Clear all the references.
*/
public void clear();
/**
* Clear all the references for a given type of entity bean.
*/
public void clear(Class<?> beanType);
/**
* Clear the reference to a specific entity bean.
*/
public void clear(Class<?> beanType, Object uid);
/**
* Return the number of beans of the given type in the persistence context.
*/
public int size(Class<?> beanType);
}
package com.avaje.ebean.bean;
/**
* Holds entity beans by there type and id.
* <p>
* This is used to ensure only one instance for a given entity type and id is
* used to build object graphs from queries and lazy loading.
* </p>
*/
public interface PersistenceContext {
/**
* Put the entity bean into the PersistanceContext.
*/
void put(Object id, Object bean);
/**
* Put the entity bean into the PersistanceContext if one is not already
* present (for this id).
* <p>
* Returns an existing entity bean (if one is already there) and otherwise
* returns null.
* </p>
*/
Object putIfAbsent(Object id, Object bean);
/**
* Return an object given its type and unique id.
*/
Object get(Class<?> beanType, Object uid);
/**
* Get the bean from the persistence context also checked to see if it had
* been previously deleted (if so then you also can't hit the L2 cache to
* fetch the bean for this particular persistence context).
*/
WithOption getWithOption(Class<?> beanType, Object uid);
/**
* Clear all the references.
*/
void clear();
/**
* Clear all the references for a given type of entity bean.
*/
void clear(Class<?> beanType);
/**
* Clear the reference to a specific entity bean.
*/
void clear(Class<?> beanType, Object uid);
/**
* Clear the reference as a result of an entity being deleted.
*/
void deleted(Class<?> beanType, Object id);
/**
* Return the number of beans of the given type in the persistence context.
*/
int size(Class<?> beanType);
/**
* Wrapper on a bean to also indicate if a bean has been deleted.
* <p>
* If a bean has been deleted then for the same persistence context is should
* not be able to be fetched from persistence context or L2 cache.
* </p>
*/
class WithOption {
/**
* The bean was previously deleted from this persistence context (can't hit
* L2 cache).
*/
public static WithOption DELETED = new WithOption(true);
private final boolean deleted;
private final Object bean;
private WithOption(boolean deleted) {
this.deleted = true;
this.bean = null;
}
/**
* The bean exists in the persistence context (and not been previously deleted).
*/
public WithOption(Object bean) {
this.deleted = false;
this.bean = bean;
}
/**
* Return true if the bean was deleted. This means you can't hit the L2
* cache.
*/
public boolean isDeleted() {
return deleted;
}
/**
* Return the bean (from the persistence context).
*/
public Object getBean() {
return bean;
}
}
}
+89 -89
View File
@@ -1,89 +1,89 @@
package com.avaje.ebean.cache;
import com.avaje.ebean.EbeanServer;
/**
* Represents part of the "L2" server side cache.
* <p>
* This is used to cache beans or query results (bean collections).
* </p>
* <p>
* There are 2 ServerCache's for each bean type. One is used as the 'bean cache'
* which holds beans of a given type. The other is the 'query cache' holding
* query results for a given type.
* </p>
*
* @author rbygrave
*/
public interface ServerCache {
/**
* Just after a cache is created this init method is called. This is so that a
* cache implementation can make use of the BackgroundExecutor service to
* trim/cleanup itself or use the EbeanServer to populate itself.
* <p>
* This method is called after the cache is constructed but before the cache
* is made available for use.
* </p>
*/
public void init(EbeanServer ebeanServer);
/**
* Return the configuration options for this cache.
*/
public ServerCacheOptions getOptions();
/**
* Update the configuration options for this cache.
*/
public void setOptions(ServerCacheOptions options);
/**
* Return the value given the key.
*/
public Object get(Object id);
/**
* Put the value in the cache with a given id.
*/
public Object put(Object id, Object value);
/**
* Put the value in the cache but only if a matching value is not already in
* the cache.
*/
public Object putIfAbsent(Object id, Object value);
/**
* Remove a entry from the cache given its id.
*/
public Object remove(Object id);
/**
* Clear all entries from the cache.
* <p>
* NOTE: Be careful using this method in that most of the time application
* code should clear BOTH the bean and query caches at the same time. This can
* be done via {@link ServerCacheManager#clear(Class)}.
* </p>
*/
public void clear();
/**
* Return the number of entries in the cache.
*/
public int size();
/**
* Return the hit ratio the cache is currently getting.
*/
public int getHitRatio();
/**
* Return statistics for the cache.
*
* @param reset
* if true the statistics are reset.
*/
public ServerCacheStatistics getStatistics(boolean reset);
}
package com.avaje.ebean.cache;
import com.avaje.ebean.EbeanServer;
/**
* Represents part of the "L2" server side cache.
* <p>
* This is used to cache beans or query results (bean collections).
* </p>
* <p>
* There are 2 ServerCache's for each bean type. One is used as the 'bean cache'
* which holds beans of a given type. The other is the 'query cache' holding
* query results for a given type.
* </p>
*
* @author rbygrave
*/
public interface ServerCache {
/**
* Just after a cache is created this init method is called. This is so that a
* cache implementation can make use of the BackgroundExecutor service to
* trim/cleanup itself or use the EbeanServer to populate itself.
* <p>
* This method is called after the cache is constructed but before the cache
* is made available for use.
* </p>
*/
void init(EbeanServer ebeanServer);
/**
* Return the configuration options for this cache.
*/
ServerCacheOptions getOptions();
/**
* Update the configuration options for this cache.
*/
void setOptions(ServerCacheOptions options);
/**
* Return the value given the key.
*/
Object get(Object id);
/**
* Put the value in the cache with a given id.
*/
Object put(Object id, Object value);
/**
* Put the value in the cache but only if a matching value is not already in
* the cache.
*/
Object putIfAbsent(Object id, Object value);
/**
* Remove a entry from the cache given its id.
*/
Object remove(Object id);
/**
* Clear all entries from the cache.
* <p>
* NOTE: Be careful using this method in that most of the time application
* code should clear BOTH the bean and query caches at the same time. This can
* be done via {@link ServerCacheManager#clear(Class)}.
* </p>
*/
void clear();
/**
* Return the number of entries in the cache.
*/
int size();
/**
* Return the hit ratio the cache is currently getting.
*/
int getHitRatio();
/**
* Return statistics for the cache.
*
* @param reset
* if true the statistics are reset.
*/
ServerCacheStatistics getStatistics(boolean reset);
}
+26 -26
View File
@@ -1,26 +1,26 @@
package com.avaje.ebean.cache;
import com.avaje.ebean.EbeanServer;
/**
* Defines method for constructing caches for beans and queries.
*/
public interface ServerCacheFactory {
/**
* Just after the ServerCacheFactory is constructed this method is called
* passing the EbeanServer.
* <p>
* This is so that a cache implementation can utilise the EbeanServer to
* populate itself or use the BackgroundExecutor service to schedule periodic
* cache trimming/cleanup.
* </p>
*/
public void init(EbeanServer ebeanServer);
/**
* Create the cache for the given type with options.
*/
public ServerCache createCache(String cacheKey, ServerCacheOptions cacheOptions);
}
package com.avaje.ebean.cache;
import com.avaje.ebean.EbeanServer;
/**
* Defines method for constructing caches for beans and queries.
*/
public interface ServerCacheFactory {
/**
* Just after the ServerCacheFactory is constructed this method is called
* passing the EbeanServer.
* <p>
* This is so that a cache implementation can utilise the EbeanServer to
* populate itself or use the BackgroundExecutor service to schedule periodic
* cache trimming/cleanup.
* </p>
*/
void init(EbeanServer ebeanServer);
/**
* Create the cache for the given type with options.
*/
ServerCache createCache(String cacheKey, ServerCacheOptions cacheOptions);
}
+55 -55
View File
@@ -1,55 +1,55 @@
package com.avaje.ebean.cache;
import com.avaje.ebean.EbeanServer;
/**
* The cache service for server side caching of beans and query results.
*/
public interface ServerCacheManager {
/**
* This method is called just after the construction of the
* ServerCacheManager.
* <p>
* The EbeanServer is provided so that cache implementations can make use of
* EbeanServer and BackgroundExecutor for automatically populating and
* background trimming of the cache.
* </p>
*/
public void init(EbeanServer server);
public void setCaching(Class<?> beanType, boolean useCache);
/**
* Return true if there is an active bean cache for this type of bean.
*/
public boolean isBeanCaching(Class<?> beanType);
/**
* Return the cache for mapping natural keys to id values.
*/
public ServerCache getNaturalKeyCache(Class<?> beanType);
/**
* Return the cache for beans of a particular type.
*/
public ServerCache getBeanCache(Class<?> beanType);
public ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName);
/**
* Return the cache for query results of a particular type of bean.
*/
public ServerCache getQueryCache(Class<?> beanType);
/**
* This clears both the bean and query cache for a given type.
*/
public void clear(Class<?> beanType);
/**
* Clear all the caches.
*/
public void clearAll();
}
package com.avaje.ebean.cache;
import com.avaje.ebean.EbeanServer;
/**
* The cache service for server side caching of beans and query results.
*/
public interface ServerCacheManager {
/**
* This method is called just after the construction of the
* ServerCacheManager.
* <p>
* The EbeanServer is provided so that cache implementations can make use of
* EbeanServer and BackgroundExecutor for automatically populating and
* background trimming of the cache.
* </p>
*/
void init(EbeanServer server);
void setCaching(Class<?> beanType, boolean useCache);
/**
* Return true if there is an active bean cache for this type of bean.
*/
boolean isBeanCaching(Class<?> beanType);
/**
* Return the cache for mapping natural keys to id values.
*/
ServerCache getNaturalKeyCache(Class<?> beanType);
/**
* Return the cache for beans of a particular type.
*/
ServerCache getBeanCache(Class<?> beanType);
ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName);
/**
* Return the cache for query results of a particular type of bean.
*/
ServerCache getQueryCache(Class<?> beanType);
/**
* This clears both the bean and query cache for a given type.
*/
void clear(Class<?> beanType);
/**
* Clear all the caches.
*/
void clearAll();
}
+110 -110
View File
@@ -1,110 +1,110 @@
package com.avaje.ebean.cache;
import com.avaje.ebean.annotation.CacheTuning;
/**
* Options for controlling a cache.
*/
public class ServerCacheOptions {
private int maxSize;
private int maxIdleSecs;
private int maxSecsToLive;
/**
* Construct with no set options.
*/
public ServerCacheOptions() {
}
/**
* Create from the cacheTuning deployment annotation.
*/
public ServerCacheOptions(CacheTuning cacheTuning) {
this.maxSize = cacheTuning.maxSize();
this.maxIdleSecs = cacheTuning.maxIdleSecs();
this.maxSecsToLive = cacheTuning.maxSecsToLive();
}
/**
* Create merging default options with the deployment specified ones.
*/
public ServerCacheOptions(ServerCacheOptions d) {
this.maxSize = d.getMaxSize();
this.maxIdleSecs = d.getMaxIdleSecs();
this.maxSecsToLive = d.getMaxIdleSecs();
}
/**
* Apply any settings from the default settings that have not already been
* specifically set.
*/
public void applyDefaults(ServerCacheOptions defaults) {
if (maxSize == 0) {
maxSize = defaults.getMaxSize();
}
if (maxIdleSecs == 0) {
maxIdleSecs = defaults.getMaxIdleSecs();
}
if (maxSecsToLive == 0) {
maxSecsToLive = defaults.getMaxSecsToLive();
}
}
/**
* Return a copy of this object.
*/
public ServerCacheOptions copy() {
ServerCacheOptions copy = new ServerCacheOptions();
copy.maxSize = maxSize;
copy.maxIdleSecs = maxIdleSecs;
copy.maxSecsToLive = maxSecsToLive;
return copy;
}
/**
* Return the maximum cache size.
*/
public int getMaxSize() {
return maxSize;
}
/**
* Set the maximum cache size.
*/
public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
/**
* Return the maximum idle time.
*/
public int getMaxIdleSecs() {
return maxIdleSecs;
}
/**
* Set the maximum idle time.
*/
public void setMaxIdleSecs(int maxIdleSecs) {
this.maxIdleSecs = maxIdleSecs;
}
/**
* Return the maximum time to live.
*/
public int getMaxSecsToLive() {
return maxSecsToLive;
}
/**
* Set the maximum time to live.
*/
public void setMaxSecsToLive(int maxSecsToLive) {
this.maxSecsToLive = maxSecsToLive;
}
}
package com.avaje.ebean.cache;
import com.avaje.ebean.annotation.CacheTuning;
/**
* Options for controlling a cache.
*/
public class ServerCacheOptions {
private int maxSize;
private int maxIdleSecs;
private int maxSecsToLive;
/**
* Construct with no set options.
*/
public ServerCacheOptions() {
}
/**
* Create from the cacheTuning deployment annotation.
*/
public ServerCacheOptions(CacheTuning cacheTuning) {
this.maxSize = cacheTuning.maxSize();
this.maxIdleSecs = cacheTuning.maxIdleSecs();
this.maxSecsToLive = cacheTuning.maxSecsToLive();
}
/**
* Create merging default options with the deployment specified ones.
*/
public ServerCacheOptions(ServerCacheOptions d) {
this.maxSize = d.getMaxSize();
this.maxIdleSecs = d.getMaxIdleSecs();
this.maxSecsToLive = d.getMaxIdleSecs();
}
/**
* Apply any settings from the default settings that have not already been
* specifically set.
*/
public void applyDefaults(ServerCacheOptions defaults) {
if (maxSize == 0) {
maxSize = defaults.getMaxSize();
}
if (maxIdleSecs == 0) {
maxIdleSecs = defaults.getMaxIdleSecs();
}
if (maxSecsToLive == 0) {
maxSecsToLive = defaults.getMaxSecsToLive();
}
}
/**
* Return a copy of this object.
*/
public ServerCacheOptions copy() {
ServerCacheOptions copy = new ServerCacheOptions();
copy.maxSize = maxSize;
copy.maxIdleSecs = maxIdleSecs;
copy.maxSecsToLive = maxSecsToLive;
return copy;
}
/**
* Return the maximum cache size.
*/
public int getMaxSize() {
return maxSize;
}
/**
* Set the maximum cache size.
*/
public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
/**
* Return the maximum idle time.
*/
public int getMaxIdleSecs() {
return maxIdleSecs;
}
/**
* Set the maximum idle time.
*/
public void setMaxIdleSecs(int maxIdleSecs) {
this.maxIdleSecs = maxIdleSecs;
}
/**
* Return the maximum time to live.
*/
public int getMaxSecsToLive() {
return maxSecsToLive;
}
/**
* Set the maximum time to live.
*/
public void setMaxSecsToLive(int maxSecsToLive) {
this.maxSecsToLive = maxSecsToLive;
}
}
+124 -124
View File
@@ -1,124 +1,124 @@
package com.avaje.ebean.cache;
/**
* The statistics collected per cache.
* <p>
* These can be monitored to review the effectiveness of a particular cache.
* </p>
*
* @author rbygrave
*
*/
public class ServerCacheStatistics {
protected String cacheName;
protected int maxSize;
protected int size;
protected int hitCount;
protected int missCount;
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(cacheName);
sb.append(" size:").append(size);
sb.append(" hitRatio:").append(getHitRatio());
sb.append(" hitCount:").append(hitCount);
sb.append(" missCount:").append(missCount);
sb.append(" maxSize:").append(maxSize);
return sb.toString();
}
/**
* Return the name of the cache.
*/
public String getCacheName() {
return cacheName;
}
/**
* Set the name of the cache.
*/
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
/**
* Return the hit count. The number of successful gets.
*/
public int getHitCount() {
return hitCount;
}
/**
* Set the hit count.
*/
public void setHitCount(int hitCount) {
this.hitCount = hitCount;
}
/**
* Return the miss count. The number of gets that returned null.
*/
public int getMissCount() {
return missCount;
}
/**
* Set the miss count.
*/
public void setMissCount(int missCount) {
this.missCount = missCount;
}
/**
* Return the size of the cache.
*/
public int getSize() {
return size;
}
/**
* Set the size of the cache.
*/
public void setSize(int size) {
this.size = size;
}
/**
* Return the maximum size of the cache.
* <p>
* Can be used in conjunction with the size to determine if the cache use is
* being potentially limited by its maximum size.
* </p>
*/
public int getMaxSize() {
return maxSize;
}
/**
* Set the maximum size of the cache.
*/
public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
/**
* Returns an int from 0 to 100 (percentage) for the hit ratio.
* <p>
* A hit ratio of 100 means every get request against the cache hits an entry.
* </p>
*/
public int getHitRatio() {
int totalCount = hitCount + missCount;
if (totalCount == 0) {
return 0;
} else {
return hitCount * 100 / totalCount;
}
}
}
package com.avaje.ebean.cache;
/**
* The statistics collected per cache.
* <p>
* These can be monitored to review the effectiveness of a particular cache.
* </p>
*
* @author rbygrave
*
*/
public class ServerCacheStatistics {
protected String cacheName;
protected int maxSize;
protected int size;
protected int hitCount;
protected int missCount;
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(cacheName);
sb.append(" size:").append(size);
sb.append(" hitRatio:").append(getHitRatio());
sb.append(" hitCount:").append(hitCount);
sb.append(" missCount:").append(missCount);
sb.append(" maxSize:").append(maxSize);
return sb.toString();
}
/**
* Return the name of the cache.
*/
public String getCacheName() {
return cacheName;
}
/**
* Set the name of the cache.
*/
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
/**
* Return the hit count. The number of successful gets.
*/
public int getHitCount() {
return hitCount;
}
/**
* Set the hit count.
*/
public void setHitCount(int hitCount) {
this.hitCount = hitCount;
}
/**
* Return the miss count. The number of gets that returned null.
*/
public int getMissCount() {
return missCount;
}
/**
* Set the miss count.
*/
public void setMissCount(int missCount) {
this.missCount = missCount;
}
/**
* Return the size of the cache.
*/
public int getSize() {
return size;
}
/**
* Set the size of the cache.
*/
public void setSize(int size) {
this.size = size;
}
/**
* Return the maximum size of the cache.
* <p>
* Can be used in conjunction with the size to determine if the cache use is
* being potentially limited by its maximum size.
* </p>
*/
public int getMaxSize() {
return maxSize;
}
/**
* Set the maximum size of the cache.
*/
public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
/**
* Returns an int from 0 to 100 (percentage) for the hit ratio.
* <p>
* A hit ratio of 100 means every get request against the cache hits an entry.
* </p>
*/
public int getHitRatio() {
int totalCount = hitCount + missCount;
if (totalCount == 0) {
return 0;
} else {
return hitCount * 100 / totalCount;
}
}
}
@@ -1,290 +1,222 @@
package com.avaje.ebean.common;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.persistence.PersistenceException;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
/**
* Base class for List Set and Map implementations of BeanCollection.
*
* @author rbygrave
*/
public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
private static final long serialVersionUID = 3365725236140187588L;
protected boolean readOnly;
/**
* The EbeanServer this is associated with. (used for lazy fetch).
*/
protected transient BeanCollectionLoader loader;
protected transient ExpressionList<?> filterMany;
protected int loaderIndex;
protected String ebeanServerName;
protected transient BeanCollectionTouched beanCollectionTouched;
protected transient Future<Integer> fetchFuture;
/**
* The owning bean (used for lazy fetch).
*/
protected final Object ownerBean;
/**
* The name of this property in the owning bean (used for lazy fetch).
*/
protected final String propertyName;
/**
* Can be false when a background thread is used to continue the fetch the
* rows. It will set this to true when it is finished. If no background thread
* is used then this should already be true.
*/
protected boolean finishedFetch = true;
/**
* Flag set to true if rows are limited by firstRow maxRows and more rows
* exist. For use by client to enable 'next' for paging.
*/
protected boolean hasMoreRows;
protected ModifyHolder<E> modifyHolder;
protected ModifyListenMode modifyListenMode;
protected boolean modifyAddListening;
protected boolean modifyRemoveListening;
protected boolean modifyListening;
/**
* Constructor not non-lazy loading collection.
*/
public AbstractBeanCollection() {
this.ownerBean = null;
this.propertyName = null;
}
/**
* Used to create deferred fetch proxy.
*/
public AbstractBeanCollection(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
this.loader = loader;
this.ebeanServerName = loader.getName();
this.ownerBean = ownerBean;
this.propertyName = propertyName;
if (ownerBean instanceof EntityBean) {
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
this.readOnly = ebi.isReadOnly();
}
}
public Object getOwnerBean() {
return ownerBean;
}
public String getPropertyName() {
return propertyName;
}
public int getLoaderIndex() {
return loaderIndex;
}
public ExpressionList<?> getFilterMany() {
return filterMany;
}
public void setFilterMany(ExpressionList<?> filterMany) {
this.filterMany = filterMany;
}
protected void lazyLoadCollection(boolean onlyIds) {
if (loader == null) {
loader = (BeanCollectionLoader) Ebean.getServer(ebeanServerName);
}
if (loader == null) {
String msg = "Lazy loading but LazyLoadEbeanServer is null?"
+ " The LazyLoadEbeanServer needs to be set after deserialization"
+ " to support lazy loading.";
throw new PersistenceException(msg);
}
loader.loadMany(this, onlyIds);
checkEmptyLazyLoad();
}
protected void touched() {
if (beanCollectionTouched != null) {
// only call this once
beanCollectionTouched.notifyTouched(this);
beanCollectionTouched = null;
}
}
public void setBeanCollectionTouched(BeanCollectionTouched notify) {
this.beanCollectionTouched = notify;
}
public void setLoader(int beanLoaderIndex, BeanCollectionLoader loader) {
this.loaderIndex = beanLoaderIndex;
this.loader = loader;
this.ebeanServerName = loader.getName();
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
/**
* Set to true if maxRows was hit and there are actually more rows available.
* <p>
* Can be used by client code that is paging through results using
* setFirstRow() setMaxRows(). If this returns true then the client can
* display a 'next' button etc.
* </p>
*/
public boolean hasMoreRows() {
return hasMoreRows;
}
/**
* Set to true when maxRows is hit but there are actually more rows available.
* This is set so that client code knows that there is more data available.
*/
public void setHasMoreRows(boolean hasMoreRows) {
this.hasMoreRows = hasMoreRows;
}
/**
* Returns true if the fetch has finished. False if the fetch is continuing in
* a background thread.
*/
public boolean isFinishedFetch() {
return finishedFetch;
}
/**
* Set to true when a fetch has finished. Used when a fetch continues in the
* background.
*/
public void setFinishedFetch(boolean finishedFetch) {
this.finishedFetch = finishedFetch;
}
public void setBackgroundFetch(Future<Integer> fetchFuture) {
this.fetchFuture = fetchFuture;
}
public void backgroundFetchWait(long wait, TimeUnit timeUnit) {
if (fetchFuture != null) {
try {
fetchFuture.get(wait, timeUnit);
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
public void backgroundFetchWait() {
if (fetchFuture != null) {
try {
fetchFuture.get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
}
protected void checkReadOnly() {
if (readOnly) {
String msg = "This collection is in ReadOnly mode";
throw new IllegalStateException(msg);
}
}
// ---------------------------------------------------------
// Support for modify additions deletions etc - ManyToMany
// ---------------------------------------------------------
/**
* set modifyListening to be on or off.
*/
public void setModifyListening(ModifyListenMode mode) {
this.modifyListenMode = mode;
this.modifyAddListening = ModifyListenMode.ALL.equals(mode);
this.modifyRemoveListening = modifyAddListening || ModifyListenMode.REMOVALS.equals(mode);
this.modifyListening = modifyRemoveListening || modifyAddListening;
if (modifyListening) {
// lose any existing modifications
modifyHolder = null;
}
}
/**
* Return the modify listening mode this collection is using.
*/
public ModifyListenMode getModifyListenMode() {
return modifyListenMode;
}
protected ModifyHolder<E> getModifyHolder() {
if (modifyHolder == null) {
modifyHolder = new ModifyHolder<E>();
}
return modifyHolder;
}
public void modifyAddition(E bean) {
if (modifyAddListening) {
getModifyHolder().modifyAddition(bean);
}
}
public void modifyRemoval(Object bean) {
if (modifyRemoveListening) {
getModifyHolder().modifyRemoval(bean);
}
}
public void modifyReset() {
if (modifyHolder != null) {
modifyHolder.reset();
}
}
public Set<E> getModifyAdditions() {
if (modifyHolder == null) {
return null;
} else {
return modifyHolder.getModifyAdditions();
}
}
public Set<E> getModifyRemovals() {
if (modifyHolder == null) {
return null;
} else {
return modifyHolder.getModifyRemovals();
}
}
}
package com.avaje.ebean.common;
import java.util.Set;
import javax.persistence.PersistenceException;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.EntityBean;
/**
* Base class for List Set and Map implementations of BeanCollection.
*
* @author rbygrave
*/
public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
private static final long serialVersionUID = 3365725236140187588L;
protected boolean readOnly;
/**
* The EbeanServer this is associated with. (used for lazy fetch).
*/
protected transient BeanCollectionLoader loader;
protected transient ExpressionList<?> filterMany;
/**
* Flag set when registered with the batch loading context.
*/
protected boolean registeredWithLoadContext;
protected String ebeanServerName;
protected transient BeanCollectionTouched beanCollectionTouched;
/**
* The owning bean (used for lazy fetch).
*/
protected EntityBean ownerBean;
/**
* The name of this property in the owning bean (used for lazy fetch).
*/
protected String propertyName;
protected ModifyHolder<E> modifyHolder;
protected ModifyListenMode modifyListenMode;
protected boolean modifyAddListening;
protected boolean modifyRemoveListening;
protected boolean modifyListening;
/**
* Flag used to tell if empty collections have been cleared etc or just
* uninitialised.
*/
protected boolean touched;
/**
* Constructor not non-lazy loading collection.
*/
public AbstractBeanCollection() {
}
/**
* Used to create deferred fetch proxy.
*/
public AbstractBeanCollection(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
this.loader = loader;
this.ebeanServerName = loader.getName();
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
}
public EntityBean getOwnerBean() {
return ownerBean;
}
public String getPropertyName() {
return propertyName;
}
public ExpressionList<?> getFilterMany() {
return filterMany;
}
public void setFilterMany(ExpressionList<?> filterMany) {
this.filterMany = filterMany;
}
protected void lazyLoadCollection(boolean onlyIds) {
if (loader == null) {
loader = (BeanCollectionLoader) Ebean.getServer(ebeanServerName);
}
if (loader == null) {
String msg = "Lazy loading but LazyLoadEbeanServer is null?"
+ " The LazyLoadEbeanServer needs to be set after deserialization"
+ " to support lazy loading.";
throw new PersistenceException(msg);
}
loader.loadMany(this, onlyIds);
checkEmptyLazyLoad();
}
/**
* Set touched. If setFlag is false then typically an isEmpty() call and still
* considering that to be untouched.
*/
protected void touched(boolean setFlag) {
if (setFlag) {
touched = true;
}
if (beanCollectionTouched != null) {
// only call this once
beanCollectionTouched.notifyTouched(this);
beanCollectionTouched = null;
}
}
public void setBeanCollectionTouched(BeanCollectionTouched notify) {
this.beanCollectionTouched = notify;
}
public boolean isRegisteredWithLoadContext() {
return registeredWithLoadContext;
}
public void setLoader(BeanCollectionLoader loader) {
this.registeredWithLoadContext = true;
this.loader = loader;
this.ebeanServerName = loader.getName();
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
protected void checkReadOnly() {
if (readOnly) {
String msg = "This collection is in ReadOnly mode";
throw new IllegalStateException(msg);
}
}
// ---------------------------------------------------------
// Support for modify additions deletions etc - ManyToMany
// ---------------------------------------------------------
/**
* set modifyListening to be on or off.
*/
public void setModifyListening(ModifyListenMode mode) {
this.modifyListenMode = mode;
this.modifyAddListening = ModifyListenMode.ALL.equals(mode);
this.modifyRemoveListening = modifyAddListening || ModifyListenMode.REMOVALS.equals(mode);
this.modifyListening = modifyRemoveListening || modifyAddListening;
if (modifyListening) {
// lose any existing modifications
modifyHolder = null;
}
}
/**
* Return the modify listening mode this collection is using.
*/
public ModifyListenMode getModifyListenMode() {
return modifyListenMode;
}
protected ModifyHolder<E> getModifyHolder() {
if (modifyHolder == null) {
modifyHolder = new ModifyHolder<E>();
}
return modifyHolder;
}
public void modifyAddition(E bean) {
if (modifyAddListening) {
getModifyHolder().modifyAddition(bean);
}
}
public void modifyRemoval(Object bean) {
if (modifyRemoveListening) {
getModifyHolder().modifyRemoval(bean);
}
}
public void modifyReset() {
if (modifyHolder != null) {
modifyHolder.reset();
}
}
public Set<E> getModifyAdditions() {
if (modifyHolder == null) {
return null;
} else {
return modifyHolder.getModifyAdditions();
}
}
public Set<E> getModifyRemovals() {
if (modifyHolder == null) {
return null;
} else {
return modifyHolder.getModifyRemovals();
}
}
}
+468 -443
View File
@@ -1,443 +1,468 @@
package com.avaje.ebean.common;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
/**
* List capable of lazy loading.
*/
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>,
BeanCollectionAdd {
/**
* The underlying List implementation.
*/
private List<E> list;
/**
* Specify the underlying List implementation.
*/
public BeanList(List<E> list) {
super();
this.list = list;
}
/**
* Uses an ArrayList as the underlying List implementation.
*/
public BeanList() {
this(new ArrayList<E>());
}
/**
* Used to create deferred fetch proxy.
*/
public BeanList(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
list.add((E) bean);
}
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
list.add((E) bean);
}
public boolean checkEmptyLazyLoad() {
if (list == null) {
list = new ArrayList<E>();
return true;
} else {
return false;
}
}
private void initClear() {
synchronized (this) {
if (list == null) {
if (modifyListening) {
lazyLoadCollection(true);
} else {
list = new ArrayList<E>();
}
}
touched();
}
}
private void init() {
synchronized (this) {
if (list == null) {
lazyLoadCollection(false);
}
touched();
}
}
/**
* Set the actual underlying list.
* <p>
* This is primarily for the deferred fetching function.
* </p>
*/
@SuppressWarnings("unchecked")
public void setActualList(List<?> list) {
this.list = (List<E>) list;
}
/**
* Return the actual underlying list.
*/
public List<E> getActualList() {
return list;
}
public Collection<E> getActualDetails() {
return list;
}
/**
* Returns the underlying list.
*/
public Object getActualCollection() {
return list;
}
/**
* Return true if the underlying list is populated.
*/
public boolean isPopulated() {
return list != null;
}
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
public boolean isReference() {
return list == null;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("BeanList ");
if (isReadOnly()) {
sb.append("readOnly ");
}
if (list == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(list.size()).append("] ");
sb.append("hasMoreRows[").append(hasMoreRows).append("] ");
sb.append("list").append(list).append("");
}
return sb.toString();
}
/**
* Equal if obj is a List and equal in a list sense.
* <p>
* Specifically obj does not need to be a BeanList but any list. This does not
* use the FindMany, fetchedMaxRows or finishedFetch properties in the equals
* test.
* </p>
*/
public boolean equals(Object obj) {
init();
return list.equals(obj);
}
public int hashCode() {
init();
return list.hashCode();
}
// -----------------------------------------------------//
// The additional methods are here
// -----------------------------------------------------//
// -----------------------------------------------------//
// proxy method for List
// -----------------------------------------------------//
public void add(int index, E element) {
checkReadOnly();
init();
if (modifyAddListening) {
modifyAddition(element);
}
list.add(index, element);
}
public boolean add(E o) {
checkReadOnly();
init();
if (modifyAddListening) {
if (list.add(o)) {
modifyAddition(o);
return true;
} else {
return false;
}
}
return list.add(o);
}
public boolean addAll(Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
// all elements in c are added (no contains checking)
getModifyHolder().modifyAdditionAll(c);
}
return list.addAll(c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
// all elements in c are added (no contains checking)
getModifyHolder().modifyAdditionAll(c);
}
return list.addAll(index, c);
}
public void clear() {
checkReadOnly();
// TODO: when clear() and not initialised could be more clever
// and fetch just the Id's
initClear();
if (modifyRemoveListening) {
for (int i = 0; i < list.size(); i++) {
getModifyHolder().modifyRemoval(list.get(i));
}
}
list.clear();
}
public boolean contains(Object o) {
init();
return list.contains(o);
}
public boolean containsAll(Collection<?> c) {
init();
return list.containsAll(c);
}
public E get(int index) {
init();
return list.get(index);
}
public int indexOf(Object o) {
init();
return list.indexOf(o);
}
public boolean isEmpty() {
init();
return list.isEmpty();
}
public Iterator<E> iterator() {
init();
if (isReadOnly()) {
return new ReadOnlyListIterator<E>(list.listIterator());
}
if (modifyListening) {
Iterator<E> it = list.iterator();
return new ModifyIterator<E>(this, it);
}
return list.iterator();
}
public int lastIndexOf(Object o) {
init();
return list.lastIndexOf(o);
}
public ListIterator<E> listIterator() {
init();
if (isReadOnly()) {
return new ReadOnlyListIterator<E>(list.listIterator());
}
if (modifyListening) {
ListIterator<E> it = list.listIterator();
return new ModifyListIterator<E>(this, it);
}
return list.listIterator();
}
public ListIterator<E> listIterator(int index) {
init();
if (isReadOnly()) {
return new ReadOnlyListIterator<E>(list.listIterator(index));
}
if (modifyListening) {
ListIterator<E> it = list.listIterator(index);
return new ModifyListIterator<E>(this, it);
}
return list.listIterator(index);
}
public E remove(int index) {
checkReadOnly();
init();
if (modifyRemoveListening) {
E o = list.remove(index);
modifyRemoval(o);
return o;
}
return list.remove(index);
}
public boolean remove(Object o) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean isRemove = list.remove(o);
if (isRemove) {
modifyRemoval(o);
}
return isRemove;
}
return list.remove(o);
}
public boolean removeAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<?> it = c.iterator();
while (it.hasNext()) {
Object o = (Object) it.next();
if (list.remove(o)) {
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return list.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<E> it = list.iterator();
while (it.hasNext()) {
Object o = (Object) it.next();
if (!c.contains(o)) {
it.remove();
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return list.retainAll(c);
}
public E set(int index, E element) {
checkReadOnly();
init();
if (modifyListening) {
E o = list.set(index, element);
modifyAddition(element);
modifyRemoval(o);
return o;
}
return list.set(index, element);
}
public int size() {
init();
return list.size();
}
public List<E> subList(int fromIndex, int toIndex) {
init();
if (isReadOnly()) {
return Collections.unmodifiableList(list.subList(fromIndex, toIndex));
}
if (modifyListening) {
return new ModifyList<E>(this, list.subList(fromIndex, toIndex));
}
return list.subList(fromIndex, toIndex);
}
public Object[] toArray() {
init();
return list.toArray();
}
public <T> T[] toArray(T[] a) {
init();
return list.toArray(a);
}
private static class ReadOnlyListIterator<E> implements ListIterator<E>, Serializable {
private static final long serialVersionUID = 3097271091406323699L;
private final ListIterator<E> i;
ReadOnlyListIterator(ListIterator<E> i) {
this.i = i;
}
public void add(E o) {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
public void remove() {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
public void set(E o) {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
public boolean hasNext() {
return i.hasNext();
}
public boolean hasPrevious() {
return i.hasPrevious();
}
public E next() {
return i.next();
}
public int nextIndex() {
return i.nextIndex();
}
public E previous() {
return i.previous();
}
public int previousIndex() {
return i.previousIndex();
}
}
}
package com.avaje.ebean.common;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* List capable of lazy loading.
*/
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
private static final long serialVersionUID = 1L;
/**
* The underlying List implementation.
*/
private List<E> list;
/**
* Specify the underlying List implementation.
*/
public BeanList(List<E> list) {
super();
this.list = list;
}
/**
* Uses an ArrayList as the underlying List implementation.
*/
public BeanList() {
this(new ArrayList<E>());
}
/**
* Used to create deferred fetch proxy.
*/
public BeanList(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.list = null;
this.touched = false;
}
@Override
public boolean isEmptyAndUntouched() {
return !touched && (list == null || list.isEmpty());
}
@SuppressWarnings("unchecked")
public void addBean(EntityBean bean) {
list.add((E) bean);
}
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
if (list == null) {
list = new ArrayList<E>();
}
if (bean != null) {
list.add((E) bean);
}
}
public boolean checkEmptyLazyLoad() {
if (list == null) {
list = new ArrayList<E>();
return true;
} else {
return false;
}
}
private void initClear() {
synchronized (this) {
if (list == null) {
if (modifyListening) {
lazyLoadCollection(true);
} else {
list = new ArrayList<E>();
}
}
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (list == null) {
lazyLoadCollection(false);
}
touched(setTouched);
}
}
/**
* Set the actual underlying list.
* <p>
* This is primarily for the deferred fetching function.
* </p>
*/
@SuppressWarnings("unchecked")
public void setActualList(List<?> list) {
this.list = (List<E>) list;
}
/**
* Return the actual underlying list.
*/
public List<E> getActualList() {
return list;
}
public Collection<E> getActualDetails() {
return list;
}
@Override
public Collection<?> getActualEntries() {
return list;
}
/**
* Return true if the underlying list is populated.
*/
public boolean isPopulated() {
return list != null;
}
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
public boolean isReference() {
return list == null;
}
public String toString() {
StringBuilder sb = new StringBuilder(50);
sb.append("BeanList ");
if (isReadOnly()) {
sb.append("readOnly ");
}
if (list == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(list.size()).append("] ");
sb.append("list").append(list).append("");
}
return sb.toString();
}
/**
* Equal if obj is a List and equal in a list sense.
* <p>
* Specifically obj does not need to be a BeanList but any list. This does not
* use the FindMany, fetchedMaxRows or finishedFetch properties in the equals
* test.
* </p>
*/
public boolean equals(Object obj) {
init();
return list.equals(obj);
}
public int hashCode() {
init();
return list.hashCode();
}
// -----------------------------------------------------//
// The additional methods are here
// -----------------------------------------------------//
// -----------------------------------------------------//
// proxy method for List
// -----------------------------------------------------//
public void add(int index, E element) {
checkReadOnly();
init();
if (modifyAddListening) {
modifyAddition(element);
}
list.add(index, element);
}
public boolean add(E o) {
checkReadOnly();
init();
if (modifyAddListening) {
if (list.add(o)) {
modifyAddition(o);
return true;
} else {
return false;
}
}
return list.add(o);
}
public boolean addAll(Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
// all elements in c are added (no contains checking)
getModifyHolder().modifyAdditionAll(c);
}
return list.addAll(c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
// all elements in c are added (no contains checking)
getModifyHolder().modifyAdditionAll(c);
}
return list.addAll(index, c);
}
public void clear() {
checkReadOnly();
// TODO: when clear() and not initialised could be more clever
// and fetch just the Id's
initClear();
if (modifyRemoveListening) {
for (int i = 0; i < list.size(); i++) {
getModifyHolder().modifyRemoval(list.get(i));
}
}
list.clear();
}
public boolean contains(Object o) {
init();
return list.contains(o);
}
public boolean containsAll(Collection<?> c) {
init();
return list.containsAll(c);
}
public E get(int index) {
init();
return list.get(index);
}
public int indexOf(Object o) {
init();
return list.indexOf(o);
}
public boolean isEmpty() {
initAsUntouched();
return list.isEmpty();
}
public Iterator<E> iterator() {
init();
if (isReadOnly()) {
return new ReadOnlyListIterator<E>(list.listIterator());
}
if (modifyListening) {
Iterator<E> it = list.iterator();
return new ModifyIterator<E>(this, it);
}
return list.iterator();
}
public int lastIndexOf(Object o) {
init();
return list.lastIndexOf(o);
}
public ListIterator<E> listIterator() {
init();
if (isReadOnly()) {
return new ReadOnlyListIterator<E>(list.listIterator());
}
if (modifyListening) {
ListIterator<E> it = list.listIterator();
return new ModifyListIterator<E>(this, it);
}
return list.listIterator();
}
public ListIterator<E> listIterator(int index) {
init();
if (isReadOnly()) {
return new ReadOnlyListIterator<E>(list.listIterator(index));
}
if (modifyListening) {
ListIterator<E> it = list.listIterator(index);
return new ModifyListIterator<E>(this, it);
}
return list.listIterator(index);
}
public E remove(int index) {
checkReadOnly();
init();
if (modifyRemoveListening) {
E o = list.remove(index);
modifyRemoval(o);
return o;
}
return list.remove(index);
}
public boolean remove(Object o) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean isRemove = list.remove(o);
if (isRemove) {
modifyRemoval(o);
}
return isRemove;
}
return list.remove(o);
}
public boolean removeAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<?> it = c.iterator();
while (it.hasNext()) {
Object o = it.next();
if (list.remove(o)) {
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return list.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<E> it = list.iterator();
while (it.hasNext()) {
Object o = it.next();
if (!c.contains(o)) {
it.remove();
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return list.retainAll(c);
}
public E set(int index, E element) {
checkReadOnly();
init();
if (modifyListening) {
E o = list.set(index, element);
modifyAddition(element);
modifyRemoval(o);
return o;
}
return list.set(index, element);
}
public int size() {
init();
return list.size();
}
public List<E> subList(int fromIndex, int toIndex) {
init();
if (isReadOnly()) {
return Collections.unmodifiableList(list.subList(fromIndex, toIndex));
}
if (modifyListening) {
return new ModifyList<E>(this, list.subList(fromIndex, toIndex));
}
return list.subList(fromIndex, toIndex);
}
public Object[] toArray() {
init();
return list.toArray();
}
public <T> T[] toArray(T[] a) {
init();
return list.toArray(a);
}
private static class ReadOnlyListIterator<E> implements ListIterator<E>, Serializable {
private static final long serialVersionUID = 3097271091406323699L;
private final ListIterator<E> i;
ReadOnlyListIterator(ListIterator<E> i) {
this.i = i;
}
public void add(E o) {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
public void remove() {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
public void set(E o) {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
public boolean hasNext() {
return i.hasNext();
}
public boolean hasPrevious() {
return i.hasPrevious();
}
public E next() {
return i.next();
}
public int nextIndex() {
return i.nextIndex();
}
public E previous() {
return i.previous();
}
public int previousIndex() {
return i.previousIndex();
}
}
}
+303 -263
View File
@@ -1,263 +1,303 @@
package com.avaje.ebean.common;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionLoader;
/**
* Map capable of lazy loading.
*/
public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Map<K, E> {
/**
* The underlying map implementation.
*/
private Map<K, E> map;
/**
* Create with a given Map.
*/
public BeanMap(Map<K, E> map) {
this.map = map;
}
/**
* Create using a underlying LinkedHashMap.
*/
public BeanMap() {
this(new LinkedHashMap<K, E>());
}
public BeanMap(BeanCollectionLoader ebeanServer, Object ownerBean, String propertyName) {
super(ebeanServer, ownerBean, propertyName);
}
public void internalAdd(Object bean) {
throw new RuntimeException("Not allowed for map");
}
/**
* Return true if the underlying map has been populated. Returns false if it
* has a deferred fetch pending.
*/
public boolean isPopulated() {
return map != null;
}
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
public boolean isReference() {
return map == null;
}
public boolean checkEmptyLazyLoad() {
if (map == null) {
map = new LinkedHashMap<K, E>();
return true;
} else {
return false;
}
}
private void initClear() {
synchronized (this) {
if (map == null) {
if (modifyListening) {
lazyLoadCollection(true);
} else {
map = new LinkedHashMap<K, E>();
}
}
touched();
}
}
private void init() {
synchronized (this) {
if (map == null) {
lazyLoadCollection(false);
}
touched();
}
}
/**
* Set the actual underlying map. Used for performing lazy fetch.
*/
@SuppressWarnings("unchecked")
public void setActualMap(Map<?, ?> map) {
this.map = (Map<K, E>) map;
}
/**
* Return the actual underlying map.
*/
public Map<K, E> getActualMap() {
return map;
}
/**
* Returns the map entrySet iterator.
* <p>
* This is because the key values may need to be set against the details (so
* they don't need to be set twice).
* </p>
*/
public Collection<E> getActualDetails() {
return map.values();
}
/**
* Returns the underlying map.
*/
public Object getActualCollection() {
return map;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("BeanMap ");
if (isReadOnly()) {
sb.append("readOnly ");
}
if (map == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" map").append(map);
}
return sb.toString();
}
/**
* Equal if obj is a Map and equal in a Map sense.
*/
public boolean equals(Object obj) {
init();
return map.equals(obj);
}
public int hashCode() {
init();
return map.hashCode();
}
public void clear() {
checkReadOnly();
initClear();
if (modifyRemoveListening) {
for (K key : map.keySet()) {
E o = map.remove(key);
modifyRemoval(o);
}
}
map.clear();
}
public boolean containsKey(Object key) {
init();
return map.containsKey(key);
}
public boolean containsValue(Object value) {
init();
return map.containsValue(value);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Set<Entry<K, E>> entrySet() {
init();
if (isReadOnly()) {
return Collections.unmodifiableSet(map.entrySet());
}
if (modifyListening) {
Set<Entry<K, E>> s = map.entrySet();
return new ModifySet(this, s);
}
return map.entrySet();
}
public E get(Object key) {
init();
return map.get(key);
}
public boolean isEmpty() {
init();
return map.isEmpty();
}
public Set<K> keySet() {
init();
if (isReadOnly()) {
return Collections.unmodifiableSet(map.keySet());
}
// we don't really care about modifications to the ketSet?
return map.keySet();
}
public E put(K key, E value) {
checkReadOnly();
init();
if (modifyListening) {
Object o = map.put(key, value);
modifyAddition(value);
modifyRemoval(o);
}
return map.put(key, value);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void putAll(Map<? extends K, ? extends E> t) {
checkReadOnly();
init();
if (modifyListening) {
Iterator it = t.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Object o = map.put((K) entry.getKey(), (E) entry.getValue());
modifyAddition((E) entry.getValue());
modifyRemoval(o);
}
}
map.putAll(t);
}
public E remove(Object key) {
checkReadOnly();
init();
if (modifyRemoveListening) {
E o = map.remove(key);
modifyRemoval(o);
return o;
}
return map.remove(key);
}
public int size() {
init();
return map.size();
}
public Collection<E> values() {
init();
if (isReadOnly()) {
return Collections.unmodifiableCollection(map.values());
}
if (modifyListening) {
Collection<E> c = map.values();
return new ModifyCollection<E>(this, c);
}
return map.values();
}
}
package com.avaje.ebean.common;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* Map capable of lazy loading.
*/
public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Map<K, E> {
private static final long serialVersionUID = 1L;
/**
* The underlying map implementation.
*/
private Map<K, E> map;
/**
* Create with a given Map.
*/
public BeanMap(Map<K, E> map) {
this.map = map;
}
/**
* Create using a underlying LinkedHashMap.
*/
public BeanMap() {
this(new LinkedHashMap<K, E>());
}
public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) {
super(ebeanServer, ownerBean, propertyName);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.map = null;
this.touched = false;
}
public boolean isEmptyAndUntouched() {
return !touched && (map == null || map.isEmpty());
}
public void internalPutNull() {
if (map == null) {
map = new LinkedHashMap<K, E>();
}
}
@SuppressWarnings("unchecked")
public void internalPut(Object key, Object bean) {
if (map == null) {
map = new LinkedHashMap<K, E>();
}
if (key != null) {
map.put((K) key, (E) bean);
}
}
public void internalAdd(Object bean) {
throw new RuntimeException("Not allowed for map");
}
/**
* Return true if the underlying map has been populated. Returns false if it
* has a deferred fetch pending.
*/
public boolean isPopulated() {
return map != null;
}
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
public boolean isReference() {
return map == null;
}
public boolean checkEmptyLazyLoad() {
if (map == null) {
map = new LinkedHashMap<K, E>();
return true;
} else {
return false;
}
}
private void initClear() {
synchronized (this) {
if (map == null) {
if (modifyListening) {
lazyLoadCollection(true);
} else {
map = new LinkedHashMap<K, E>();
}
}
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (map == null) {
lazyLoadCollection(false);
}
touched(setTouched);
}
}
/**
* Set the actual underlying map. Used for performing lazy fetch.
*/
@SuppressWarnings("unchecked")
public void setActualMap(Map<?, ?> map) {
this.map = (Map<K, E>) map;
}
/**
* Return the actual underlying map.
*/
public Map<K, E> getActualMap() {
return map;
}
/**
* Returns the collection of beans (map values).
*/
public Collection<E> getActualDetails() {
return map.values();
}
/**
* Returns the map entrySet.
* <p>
* This is because the key values may need to be set against the details (so
* they don't need to be set twice).
* </p>
*/
public Collection<?> getActualEntries() {
return map.entrySet();
}
public String toString() {
StringBuilder sb = new StringBuilder(50);
sb.append("BeanMap ");
if (isReadOnly()) {
sb.append("readOnly ");
}
if (map == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" map").append(map);
}
return sb.toString();
}
/**
* Equal if obj is a Map and equal in a Map sense.
*/
public boolean equals(Object obj) {
init();
return map.equals(obj);
}
public int hashCode() {
init();
return map.hashCode();
}
public void clear() {
checkReadOnly();
initClear();
if (modifyRemoveListening) {
for (K key : map.keySet()) {
E o = map.remove(key);
modifyRemoval(o);
}
}
map.clear();
}
public boolean containsKey(Object key) {
init();
return map.containsKey(key);
}
public boolean containsValue(Object value) {
init();
return map.containsValue(value);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Set<Entry<K, E>> entrySet() {
init();
if (isReadOnly()) {
return Collections.unmodifiableSet(map.entrySet());
}
if (modifyListening) {
Set<Entry<K, E>> s = map.entrySet();
return new ModifySet(this, s);
}
return map.entrySet();
}
public E get(Object key) {
init();
return map.get(key);
}
public boolean isEmpty() {
initAsUntouched();
return map.isEmpty();
}
public Set<K> keySet() {
init();
if (isReadOnly()) {
return Collections.unmodifiableSet(map.keySet());
}
// we don't really care about modifications to the ketSet?
return map.keySet();
}
public E put(K key, E value) {
checkReadOnly();
init();
if (modifyListening) {
Object o = map.put(key, value);
modifyAddition(value);
modifyRemoval(o);
}
return map.put(key, value);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void putAll(Map<? extends K, ? extends E> t) {
checkReadOnly();
init();
if (modifyListening) {
Iterator it = t.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Object o = map.put((K) entry.getKey(), (E) entry.getValue());
modifyAddition((E) entry.getValue());
modifyRemoval(o);
}
}
map.putAll(t);
}
public E remove(Object key) {
checkReadOnly();
init();
if (modifyRemoveListening) {
E o = map.remove(key);
modifyRemoval(o);
return o;
}
return map.remove(key);
}
public int size() {
init();
return map.size();
}
public Collection<E> values() {
init();
if (isReadOnly()) {
return Collections.unmodifiableCollection(map.values());
}
if (modifyListening) {
Collection<E> c = map.values();
return new ModifyCollection<E>(this, c);
}
return map.values();
}
}
+340 -316
View File
@@ -1,316 +1,340 @@
package com.avaje.ebean.common;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
/**
* Set capable of lazy loading.
*/
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>,
BeanCollectionAdd {
/**
* The underlying Set implementation.
*/
private Set<E> set;
/**
* Create with a specific Set implementation.
*/
public BeanSet(Set<E> set) {
this.set = set;
}
/**
* Create using an underlying LinkedHashSet.
*/
public BeanSet() {
this(new LinkedHashSet<E>());
}
public BeanSet(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
set.add((E) bean);
}
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
set.add((E) bean);
}
/**
* Returns true if the underlying set has its data.
*/
public boolean isPopulated() {
return set != null;
}
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
public boolean isReference() {
return set == null;
}
public boolean checkEmptyLazyLoad() {
if (set == null) {
set = new LinkedHashSet<E>();
return true;
} else {
return false;
}
}
private void initClear() {
synchronized (this) {
if (set == null) {
if (modifyListening) {
lazyLoadCollection(true);
} else {
set = new LinkedHashSet<E>();
}
}
touched();
}
}
private void init() {
synchronized (this) {
if (set == null) {
lazyLoadCollection(true);
}
touched();
}
}
/**
* Set the underlying set (used for lazy fetch).
*/
@SuppressWarnings("unchecked")
public void setActualSet(Set<?> set) {
this.set = (Set<E>) set;
}
/**
* Return the actual underlying set.
*/
public Set<E> getActualSet() {
return set;
}
public Collection<E> getActualDetails() {
return set;
}
/**
* Returns the underlying set.
*/
public Object getActualCollection() {
return set;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("BeanSet ");
if (isReadOnly()) {
sb.append("readOnly ");
}
if (set == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(set.size()).append("]");
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
sb.append(" set").append(set);
}
return sb.toString();
}
/**
* Equal if obj is a Set and equal in a Set sense.
*/
public boolean equals(Object obj) {
init();
return set.equals(obj);
}
public int hashCode() {
init();
return set.hashCode();
}
// -----------------------------------------------------//
// proxy method for map
// -----------------------------------------------------//
public boolean add(E o) {
checkReadOnly();
init();
if (modifyAddListening) {
if (set.add(o)) {
modifyAddition(o);
return true;
} else {
return false;
}
}
return set.add(o);
}
public boolean addAll(Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
boolean changed = false;
Iterator<? extends E> it = c.iterator();
while (it.hasNext()) {
E o = it.next();
if (set.add(o)) {
modifyAddition(o);
changed = true;
}
}
return changed;
}
return set.addAll(c);
}
public void clear() {
checkReadOnly();
initClear();
if (modifyRemoveListening) {
Iterator<E> it = set.iterator();
while (it.hasNext()) {
E e = it.next();
modifyRemoval(e);
}
}
set.clear();
}
public boolean contains(Object o) {
init();
return set.contains(o);
}
public boolean containsAll(Collection<?> c) {
init();
return set.containsAll(c);
}
public boolean isEmpty() {
init();
return set.isEmpty();
}
public Iterator<E> iterator() {
init();
if (isReadOnly()) {
return new ReadOnlyIterator<E>(set.iterator());
}
if (modifyListening) {
return new ModifyIterator<E>(this, set.iterator());
}
return set.iterator();
}
public boolean remove(Object o) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (set.remove(o)) {
modifyRemoval(o);
return true;
}
return false;
}
return set.remove(o);
}
public boolean removeAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<?> it = c.iterator();
while (it.hasNext()) {
Object o = (Object) it.next();
if (set.remove(o)) {
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return set.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<?> it = set.iterator();
while (it.hasNext()) {
Object o = it.next();
if (!c.contains(o)) {
it.remove();
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return set.retainAll(c);
}
public int size() {
init();
return set.size();
}
public Object[] toArray() {
init();
return set.toArray();
}
public <T> T[] toArray(T[] a) {
init();
return set.toArray(a);
}
private static class ReadOnlyIterator<E> implements Iterator<E>, Serializable {
private static final long serialVersionUID = 2577697326745352605L;
private final Iterator<E> it;
ReadOnlyIterator(Iterator<E> it) {
this.it = it;
}
public boolean hasNext() {
return it.hasNext();
}
public E next() {
return it.next();
}
public void remove() {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
}
}
package com.avaje.ebean.common;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
/**
* Set capable of lazy loading.
*/
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
private static final long serialVersionUID = 1L;
/**
* The underlying Set implementation.
*/
private Set<E> set;
/**
* Create with a specific Set implementation.
*/
public BeanSet(Set<E> set) {
this.set = set;
}
/**
* Create using an underlying LinkedHashSet.
*/
public BeanSet() {
this(new LinkedHashSet<E>());
}
public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
@Override
public void reset(EntityBean ownerBean, String propertyName) {
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.set = null;
this.touched = false;
}
public boolean isEmptyAndUntouched() {
return !touched && (set == null || set.isEmpty());
}
@SuppressWarnings("unchecked")
public void addBean(EntityBean bean) {
set.add((E) bean);
}
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
if (set == null) {
set = new LinkedHashSet<E>();
}
if (bean != null) {
set.add((E) bean);
}
}
/**
* Returns true if the underlying set has its data.
*/
public boolean isPopulated() {
return set != null;
}
/**
* Return true if this is a reference (lazy loading) bean collection. This is
* the same as !isPopulated();
*/
public boolean isReference() {
return set == null;
}
public boolean checkEmptyLazyLoad() {
if (set == null) {
set = new LinkedHashSet<E>();
return true;
} else {
return false;
}
}
private void initClear() {
synchronized (this) {
if (set == null) {
if (modifyListening) {
lazyLoadCollection(true);
} else {
set = new LinkedHashSet<E>();
}
}
touched(true);
}
}
private void initAsUntouched() {
init(false);
}
private void init() {
init(true);
}
private void init(boolean setTouched) {
synchronized (this) {
if (set == null) {
lazyLoadCollection(true);
}
touched(setTouched);
}
}
/**
* Set the underlying set (used for lazy fetch).
*/
@SuppressWarnings("unchecked")
public void setActualSet(Set<?> set) {
this.set = (Set<E>) set;
}
/**
* Return the actual underlying set.
*/
public Set<E> getActualSet() {
return set;
}
public Collection<E> getActualDetails() {
return set;
}
@Override
public Collection<?> getActualEntries() {
return set;
}
public String toString() {
StringBuilder sb = new StringBuilder(50);
sb.append("BeanSet ");
if (isReadOnly()) {
sb.append("readOnly ");
}
if (set == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(set.size()).append("]");
sb.append(" set").append(set);
}
return sb.toString();
}
/**
* Equal if obj is a Set and equal in a Set sense.
*/
public boolean equals(Object obj) {
init();
return set.equals(obj);
}
public int hashCode() {
init();
return set.hashCode();
}
// -----------------------------------------------------//
// proxy method for map
// -----------------------------------------------------//
public boolean add(E o) {
checkReadOnly();
init();
if (modifyAddListening) {
if (set.add(o)) {
modifyAddition(o);
return true;
} else {
return false;
}
}
return set.add(o);
}
public boolean addAll(Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
boolean changed = false;
Iterator<? extends E> it = c.iterator();
while (it.hasNext()) {
E o = it.next();
if (set.add(o)) {
modifyAddition(o);
changed = true;
}
}
return changed;
}
return set.addAll(c);
}
public void clear() {
checkReadOnly();
initClear();
if (modifyRemoveListening) {
Iterator<E> it = set.iterator();
while (it.hasNext()) {
E e = it.next();
modifyRemoval(e);
}
}
set.clear();
}
public boolean contains(Object o) {
init();
return set.contains(o);
}
public boolean containsAll(Collection<?> c) {
init();
return set.containsAll(c);
}
public boolean isEmpty() {
initAsUntouched();
return set.isEmpty();
}
public Iterator<E> iterator() {
init();
if (isReadOnly()) {
return new ReadOnlyIterator<E>(set.iterator());
}
if (modifyListening) {
return new ModifyIterator<E>(this, set.iterator());
}
return set.iterator();
}
public boolean remove(Object o) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (set.remove(o)) {
modifyRemoval(o);
return true;
}
return false;
}
return set.remove(o);
}
public boolean removeAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<?> it = c.iterator();
while (it.hasNext()) {
Object o = it.next();
if (set.remove(o)) {
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return set.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
checkReadOnly();
init();
if (modifyRemoveListening) {
boolean changed = false;
Iterator<?> it = set.iterator();
while (it.hasNext()) {
Object o = it.next();
if (!c.contains(o)) {
it.remove();
modifyRemoval(o);
changed = true;
}
}
return changed;
}
return set.retainAll(c);
}
public int size() {
init();
return set.size();
}
public Object[] toArray() {
init();
return set.toArray();
}
public <T> T[] toArray(T[] a) {
init();
return set.toArray(a);
}
private static class ReadOnlyIterator<E> implements Iterator<E>, Serializable {
private static final long serialVersionUID = 2577697326745352605L;
private final Iterator<E> it;
ReadOnlyIterator(Iterator<E> it) {
this.it = it;
}
public boolean hasNext() {
return it.hasNext();
}
public E next() {
return it.next();
}
public void remove() {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
}
}
@@ -1,38 +0,0 @@
package com.avaje.ebean.common;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.ServerConfig;
/**
* Creates the EbeanServer implementations. This is used by the Ebean singleton
* to determine the implementation for each server name.
* <p>
* Note that on a remote client it is expected that this factory will return
* EbeanServers that behave as a proxy using http or tcp sockets etc to talk to
* the EbeanServer on the application server.
* </p>
*/
public interface BootupEbeanManager {
/**
* Create the EbeanServer for a given configuration.
*
* @param configuration
* The configuration information for this server.
*/
public EbeanServer createServer(ServerConfig configuration);
/**
* Create an EbeanServer just using the name.
* <p>
* In this case the dataSource parameters etc will be defined on the global
* avaje.properties file.
* </p>
*/
public EbeanServer createServer(String name);
/**
* Shutdown any Ebean wide resources such as clustering.
*/
public void shutdown();
}

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