Compare commits

...
Author SHA1 Message Date
rob bygrave 2b3e485beb [maven-release-plugin] prepare release ebean-11.19.2 2018-08-06 21:42:41 +12:00
rob bygrave d4be0718a8 #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query
Change to add generics for FetchGroup
2018-08-06 21:30:36 +12:00
rob bygrave 6d0556a120 #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query
Change to add generics for FetchGroup
2018-08-02 23:18:44 +12:00
rob bygrave ec7e4e4935 #1461 - @OneToMany with @JoinTable without table name does not default join table name 2018-08-02 22:25:48 +12:00
rob bygrave 471605619a #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query 2018-07-30 23:51:52 +12:00
rob bygrave 02fe956056 #1459 - findNative with join to a @OneToMany ... does not map properly 2018-07-27 12:31:56 +12:00
rob bygrave db13034fec No effective change - update code style in test 2018-07-27 12:31:27 +12:00
rob bygrave c2489beb82 [maven-release-plugin] prepare for next development iteration 2018-07-18 22:25:00 +12:00
rob bygrave 9a424d7d6d [maven-release-plugin] prepare release ebean-11.19.1 2018-07-18 22:24:45 +12:00
rob bygrave 9b74602617 Bump pom to 11.19.1-SNAPSHOT 2018-07-18 22:22:48 +12:00
Roland PramlandRob Bygrave 5c99986476 Migration with procedures (#1451)
DB Migration with helper stored procedures (for MySql and SQL Server)
2018-07-18 22:21:47 +12:00
rob bygrave 7d38933e9d Refactor remove dead code - BindableRequest registerGeneratedVersion()
No effective change
2018-07-18 22:16:15 +12:00
rob bygrave 7668fb11a9 [maven-release-plugin] prepare for next development iteration 2018-07-18 22:05:02 +12:00
rob bygrave eac68166d9 [maven-release-plugin] prepare release ebean-11.18.6 2018-07-18 22:04:48 +12:00
rob bygrave cb0023345f Refactor scalar types for Varchar, LongVarchar and Clob - ScalarTypeStringBase 2018-07-18 21:59:11 +12:00
rob bygrave f0f7feb125 No effective change - code format 2018-07-18 21:49:19 +12:00
Roland PramlandRob Bygrave c8e32253a5 FIX: support for ScalarTypeConverter and @Lob (#1454) 2018-07-18 21:41:46 +12:00
rob bygrave 01541bb208 #1457 - Issue with @ManyToOne with @EmbeddedId where only some columns are insertable or updatable 2018-07-18 21:29:08 +12:00
rob bygrave 2d44d06f36 #1456 - io.ebean.annotation.NotNull not honored by @ManyToOne property 2018-07-18 19:36:41 +12:00
rob bygrave fda930a162 No effective change - fix javadoc title to Ebean 11 2018-07-18 17:01:14 +12:00
Roland PramlandRob Bygrave 5a6cdb9a86 FIX: null handling in ScalarType.toBeanType (#1455) 2018-07-18 16:33:36 +12:00
rob bygrave 5ef7fe8504 [maven-release-plugin] prepare for next development iteration 2018-07-15 16:09:15 +12:00
rob bygrave 11586fb635 [maven-release-plugin] prepare release ebean-11.18.5 2018-07-15 16:09:00 +12:00
rob bygrave d555122067 #1453 - @Formula property included in DDL when on @Inheritance bean and without @Transient 2018-07-15 15:36:29 +12:00
rob bygrave b6d3dc3516 Fix test code only - Tune entity bean needs at least 1 non Id property 2018-07-15 15:01:17 +12:00
rob bygrave bbf54d6196 git push origin masterMerge branch 'FOCONIS-cache-bug-delete' 2018-07-15 14:13:48 +12:00
rob bygrave 74c88ce511 #1452 Fix - Deleting a bean does not invalidate the bean cache (Regression from 11.18.1 - introduced by changes in #1427) 2018-07-15 14:13:29 +12:00
rob bygrave 309bf6a11a Merge branch 'cache-bug-delete' of https://github.com/FOCONIS/ebean into FOCONIS-cache-bug-delete 2018-07-15 13:56:37 +12:00
rob bygrave 02b6ab0f74 No effective change, move null check on orderBy to prior to parse 2018-07-15 13:55:51 +12:00
rob bygrave 0c41993ce7 No effective change, whitespace 2018-07-15 13:55:15 +12:00
rob bygrave d949c48245 git push origin masterMerge branch 'tobias--bugfix/lazy_fetch_with_id_column_name' 2018-07-15 13:52:14 +12:00
rob bygrave 93e603ca25 #1449 Fix for failing lazy fetch when logical property name starts with underscore 2018-07-15 13:49:05 +12:00
rob bygrave 117d22fd67 Merge branch 'bugfix/lazy_fetch_with_id_column_name' of https://github.com/tobias-/ebean into tobias--bugfix/lazy_fetch_with_id_column_name 2018-07-15 13:11:33 +12:00
Roland Praml f13de2c8a6 ADD: Testcase for delete with cache 2018-07-13 19:57:56 +02:00
Mitsch164andRob Bygrave 5b8ef8154a Fix Transaction Flushing when ID Property is Loaded (#1450)
* Fix for unintentianal flush when ID is loaded

* fix duplicateKey Exception

* higher id
2018-07-12 15:10:17 +12:00
Tobias df892f4509 Failing testcase with lazy fetch
For some reason, all entities with an id starting with _ fails when
lazy-fetching due to sort not being properly renamed.
2018-07-11 15:52:56 +02:00
rob bygrave 2755e6d1ec [maven-release-plugin] prepare for next development iteration 2018-07-05 23:40:12 +12:00
rob bygrave 2024193da9 [maven-release-plugin] prepare release ebean-11.18.4 2018-07-05 23:39:56 +12:00
rob bygrave 72788c7428 No effective change - tidy test code 2018-07-05 22:41:28 +12:00
rob bygrave 8789709fb8 No effective change - tidy test code 2018-07-05 22:40:09 +12:00
rob bygrave 588aaf9361 #1447 - Delete query ... should honour cascading delete if defined
See also PR #1445 with the failing test case
2018-07-05 22:37:54 +12:00
Michael BenzandRob Bygrave 68649d0e98 Failing unit test to demonstrate the bug that deleting an entity via … (#1445)
* Failing unit test to demonstrate the bug that deleting an entity via an Ebean query does not respect the cascade setting.
(here a mapped optional OneToOne entity is not deleted using an Ebean query even if the CascadeType.ALL was used)

* moved init and cleanup code to @Before and @After JUnit methods and added another test that shows Ebean.delete() works as expected
2018-07-05 19:52:40 +12:00
rob bygrave f6047d3b73 [maven-release-plugin] prepare for next development iteration 2018-07-04 22:09:27 +12:00
rob bygrave 629e096e28 [maven-release-plugin] prepare release ebean-11.18.3 2018-07-04 22:09:12 +12:00
rob bygrave c981944c8e #1444 - JSON parsing with inheritance - java.lang.UnsupportedOperationException: cannot create entity bean for abstract entity ... 2018-07-04 21:26:17 +12:00
rob bygrave 1ba9f4e9de No effective change - Add simple profile query main 2018-07-04 21:25:36 +12:00
rob bygrave ce02e7ce70 No effective change - tidy tests 2018-07-04 21:25:10 +12:00
rob bygrave 1d9a2ed43a No effective change - tidy DJsonContext 2018-07-02 21:33:59 +12:00
rob bygrave ae81ce5f47 #1443 - Bump ebean-migration to 11.7.1 (which adds JdbcMigration support and "I" run first repeatable migrations) 2018-07-01 20:30:54 +12:00
rob bygrave 9e42f98902 [maven-release-plugin] prepare for next development iteration 2018-06-28 10:27:06 +12:00
rob bygrave ada21729ec [maven-release-plugin] prepare release ebean-11.18.2 2018-06-28 10:26:53 +12:00
rob bygrave 8b7d882b8a #1442 - Add support to set ebean.dbSchema ... (~ default DB schema) with create-all.sql and DB migrations (so it all just works) 2018-06-28 10:23:34 +12:00
Michael BenzandRob Bygrave cf2d9df256 Migration enum drop check constraints (#1435)
* Fix: apply drop checkConstraints properly which were not applied for Enum to String

* Use IF EXISTS to drop constraints

* added TODO and comment about MariaDB supporting CHECK constraints from version 10.2.1 forward compared to MySQL 5.7/8.0 parsing but ignoring CHECK constraints

* updated reference scripts for dropping constraints on enums
2018-06-27 10:38:56 +12:00
rob bygrave 48b05eb358 No effective change - modify logback-test.xml to put the test example logging files into target directory 2018-06-27 10:37:00 +12:00
rob bygrave 0796fc0d88 [maven-release-plugin] prepare for next development iteration 2018-06-27 00:30:37 +12:00
rob bygrave df585644e9 [maven-release-plugin] prepare release ebean-11.18.1 2018-06-27 00:30:23 +12:00
rob bygrave ff3c5b3d82 #1441 - Update ebean-dbmigration.xsd adding missing alterForeignKey element 2018-06-27 00:23:36 +12:00
rob bygrave 1a03e84f0e #1439 - IllegalArgumentException: No rule for io.ebeaninternal.dbmigration.migration.Sql@315df4bb 2018-06-26 22:40:08 +12:00
rob bygrave 49ab17cd34 #1438 - Mapping of @ElementCollection with an Enum that is otherwise not used produces error 2018-06-26 22:11:40 +12:00
rob bygrave 9cd189034c #1437 - or(expr, expr) ... where expr has a ToMany should produce a LEFT JOIN (and instead we only get JOIN) 2018-06-26 21:33:44 +12:00
rob bygrave ed566ca153 #1427 - QueryCache should be cleared, if one of a dependent bean is updated
Refactor on PostCommitProcessing with fixes for dependent table invalidation
2018-06-26 00:25:33 +12:00
rob bygrave 9159cec36c No effective change - remove invalid javadoc on Query due to #1434 2018-06-19 16:35:42 +12:00
rob bygrave b58fc2df04 #1436 - PersistenceException: Query threw SQLException:No ScalarType registered for class java.util.Arrays$ArrayList Query was:select id, name from o_customer where id = any(?)
Add test for SqlUpdate
2018-06-19 16:30:21 +12:00
rob bygrave dd8607b072 #1436 - PersistenceException: Query threw SQLException:No ScalarType registered for class java.util.Arrays$ArrayList Query was:select id, name from o_customer where id = any(?) 2018-06-19 15:30:19 +12:00
rob bygrave a5a8765bd0 Update tests only - non native boolean (MySql) and ignore non-named savepoint nested transaction tests with MySql. 2018-06-19 15:24:35 +12:00
rob bygrave ae55b58438 No effective change - remove unused imports 2018-06-19 01:48:45 +12:00
rob bygrave 671d082ad3 #1427 - Map all deleteByIds into a single CacheChangeBeanRemove and use bulk removeAll api to remove from cache 2018-06-19 01:34:28 +12:00
rob bygrave 43e95d81f6 #1427 - RemoteTransactionEvent process dependent tables 2018-06-19 00:50:14 +12:00
rob bygrave 3a64cf6952 #1434 - Remove deprecated API from EbeanServer - finder methods that take explicit transaction. Migrate to use ebeanServer.extended() 2018-06-18 21:34:51 +12:00
rob bygrave 8d976f83a9 #1417 - Breaking API - Remove PersistBatch.INSERT ... migrate to PersistBatch.ALL 2018-06-18 21:15:10 +12:00
rob bygrave 117737c790 #1433 - Remove io.ebeaninternal.api.SpiContainerBootup ... which was replaced by io.ebean.config.AutoConfigure 2018-06-18 20:02:14 +12:00
rob bygrave 085843339f #1432 - Refactor internals for RemoteTransactionEvent / BeanPersistIds ... to not send inserted ids 2018-06-17 23:34:27 +12:00
rob bygrave bf33b3f96f #1431 - Refactor internals for RemoteTransactionEvent binary serialisation 2018-06-17 23:29:11 +12:00
rob bygrave e1d4159453 #1431 - Refactor internals for RemoteTransactionEvent binary serialisation 2018-06-17 22:25:45 +12:00
rob bygrave 4f1f3cadf9 Merge branch 'feature/query-cache-1427' 2018-06-16 16:02:35 +12:00
rob bygrave 01e1a4642b [maven-release-plugin] prepare for next development iteration 2018-06-16 15:26:11 +12:00
rob bygrave 3a75e1f66c [maven-release-plugin] prepare release ebean-11.17.5 2018-06-16 15:25:56 +12:00
rob bygrave 47ac4c5c5b #1430 - Regression (from 11.10.1) - DataSource user not set ... due to PrimaryServer System.setProperty("ebean_db", defaultServerName); 2018-06-16 15:23:41 +12:00
rob bygrave 9c73309dfd #1427 - QueryCache should be cleared, if one of a dependent bean is updated
Update RemoteTransactionEvent, add (back) common binary message handling (Common to TCP, K8s and upcoming binary pubsub)
2018-06-16 15:15:54 +12:00
rob bygrave bd3c99e67f #1427 - QueryCache should be cleared, if one of a dependent bean is updated
Wrap Clock as ClockService to use in TransactionManager and DefaultServer (and allow for changing the Clock implementation for testing).
2018-06-16 00:22:44 +12:00
rob bygrave 5adbebaf6e Merge branch 'master' into feature/query-cache-1427 2018-06-16 00:09:25 +12:00
rob bygrave 6a9ab49ef0 #1427 - QueryCache should be cleared, if one of a dependent bean is updated
Propagation across cluster.
2018-06-16 00:09:03 +12:00
rob bygrave 4308a98a0a #1428 - ENH: Use java.time.Clock for @WhenModified and @WhenCreated and allow it to be Replaceable for testing
Adjust such that Clock is on DefaultServer and can be set via ExtendedServer interface
2018-06-15 20:49:14 +12:00
tobias-andRob Bygrave f86f1b2647 Replaceable clock (#1428)
ENH: Use java.time.Clock for @WhenModified and @WhenCreated and allow it to be Replaceable for testing
2018-06-15 20:34:11 +12:00
rob bygrave 96cc8d7605 #1427 - QueryCache should be cleared, if one of a dependent bean is updated
Initial work, does not include propagation across cluster.
2018-06-15 17:36:23 +12:00
rob bygrave 59537227b6 #1426 - ENH: Add bitwiseNot expression ... e.g. bitwiseNot("flags", BwFlags.HAS_COLOUR) 2018-06-14 23:28:30 +12:00
rob bygrave 81c6d4601d #1425 - Stateless update with OneToMany with SoftDelete and deleteMissingChildren = true ... does a hard delete 2018-06-14 16:09:45 +12:00
rob bygrave e21839de91 #1424 - Deprecate / Move ... finder methods that take explicit transaction to ExtendedServer API 2018-06-14 11:03:29 +12:00
rob bygrave a61db2ab35 [maven-release-plugin] prepare for next development iteration 2018-06-14 00:18:14 +12:00
rob bygrave bddfa3bc5f [maven-release-plugin] prepare release ebean-11.17.4 2018-06-14 00:18:00 +12:00
Roland PramlandRob Bygrave 9ad460ecd8 FIX: applied history drops were not recognized (#1422) 2018-06-14 00:07:14 +12:00
rob bygrave 50593a7f9d #1423 - Close resources - InputStream of entity bean properties of File type 2018-06-14 00:04:43 +12:00
rob bygrave c088cfa339 #1421 - Nested programatic transaction using auto close / try with resources ... does not end/rollback the parent/outer transaction 2018-06-13 22:22:29 +12:00
rob bygrave e683e25a95 #1420 - ENH: Add a ServiceLoader mechanism to configure ServerConfig - io.ebean.config.ServerConfigProvider 2018-06-13 21:35:26 +12:00
rob bygrave c7052c5bfa #1420 - ENH: Add a ServiceLoader mechanism to configure ServerConfig - io.ebean.config.ServerConfigProvider 2018-06-13 21:30:41 +12:00
rob bygrave 698ee2198d No effective change - refactor DefaultContainer from iterator to forEach 2018-06-12 22:14:33 +12:00
rob bygrave 91c4eca528 #1419 - Refactor - saveAll() and deleteAll() ... when collections are empty skip transaction init check 2018-06-12 22:08:17 +12:00
rob bygrave 81897bd54d #1418 - Add saveAll() and deleteAll() to io.ebean.BeanRepository 2018-06-12 21:40:39 +12:00
rob bygrave 2427630c07 #1400 - Postgres @History table sys_period empty with multiple updates 2018-06-11 22:10:43 +12:00
rob bygrave 3238c0def4 [maven-release-plugin] prepare for next development iteration 2018-06-11 14:27:53 +12:00
rob bygrave 93f75151f0 [maven-release-plugin] prepare release ebean-11.17.3 2018-06-11 14:27:40 +12:00
rob bygrave 32e9b48359 #1414 - Support dynamic aggregate formula without alias e.g. query.select("date, sum(totalKms), sum(hours)") 2018-06-11 14:13:13 +12:00
rob bygrave 258e29d2f0 #1415 - Add asDto(Class<D> dtoClass) ... to ExpressionList to support fluid style 2018-06-11 13:29:13 +12:00
rob bygrave ec2f1f7243 #1414 - Support dynamic aggregate formula without alias e.g. query.select("date, sum(totalKms), sum(hours)") 2018-06-11 13:20:39 +12:00
Rob Bygrave 5775985468 [maven-release-plugin] prepare for next development iteration 2018-06-11 00:12:45 +12:00
Rob Bygrave 4788dd4ec9 [maven-release-plugin] prepare release ebean-11.17.2 2018-06-11 00:12:30 +12:00
Rob Bygrave ca7e5b79b8 #1412 - Fix Resource leaks for properties & yaml files etc - ManifestReader
Not fixing a leak but just changing to use try with resources
2018-06-11 00:04:58 +12:00
Rob Bygrave 90dfbcd7b3 #1412 - Fix Resource leaks for properties & yaml files etc - DefaultContainer.checkDataSource()
The resource was already being closed properly, changing to use try with resources and note that this now throws PersistenceException if we fail to close() on the connection (which I think is an ok change).
2018-06-11 00:02:14 +12:00
Rob Bygrave 6b39bad4da No effective change - use JdbcClose for DatabasePlatformFactory 2018-06-10 23:55:45 +12:00
Rob Bygrave 25dea0b5b0 #1412 - Fix Resource leaks for properties & yaml files etc - DefaultAutoTuneService 2018-06-10 23:48:49 +12:00
Rob Bygrave 0069d2fabc #1412 - Fix Resource leaks for properties & yaml files etc - ExtraDdlXmlReader 2018-06-10 23:46:55 +12:00
Rob Bygrave 98964638ba #1412 - Fix Resource leaks for properties & yaml files etc - MigrationXmlReader 2018-06-10 23:43:41 +12:00
Rob Bygrave 4573a995f1 #1412 - Fix Resource leaks for properties & yaml files etc
No effective change - reformat only
2018-06-10 23:37:28 +12:00
Rob Bygrave 4bc80cc724 #1412 - Fix Resource leaks for properties & yaml files etc - EbeanVersion 2018-06-10 23:36:12 +12:00
Rob Bygrave a2972abe3e #1412 - Fix Resource leaks for properties & yaml files etc - DdlGenerator 2018-06-10 23:33:21 +12:00
Rob Bygrave 54eaf561f4 #1412 - Fix Resource leaks for properties & yaml files etc - Loader 2018-06-10 23:31:16 +12:00
Rob Bygrave 3134dd3165 No effective change - reformat only 2018-06-10 23:23:48 +12:00
Roland PramlandRob Bygrave 8fc6abd480 IOUtils is no longer used, using java 1.7 StandardCharsets makes EncodeUtil also obsolete (#1410) 2018-06-10 22:30:47 +12:00
rob bygrave f031bb72b6 #1408 - Incorrect SQL when @Aggregation formula matches the property - sum(t0.sum(totalKms)) 2018-06-08 17:38:15 +12:00
rob bygrave 42f0b419e8 #1405 - ENH: add findSingleDecimal() and findSingleLong() alias methods for SqlQuery.findSingleAttribute() ... 2018-06-08 02:19:36 +12:00
rob bygrave 7cbae758fc #1407 - ENH: Add SqlQuery RowMapper and RowConsumer ... for raw JDBC ResultSet mapping and consuming 2018-06-08 02:04:38 +12:00
rob bygrave fb32cfa170 #1406 - ENH: Add SqlQuery.findSingleAttributeList() ... for returning lists of scalar values via SqlQuery 2018-06-07 22:39:19 +12:00
rob bygrave ffa8268125 #1405 - ENH: Add SqlQuery.findSingleAttribute() ... for returning single scalar values via SqlQuery 2018-06-07 22:17:01 +12:00
rob bygrave eba1003c19 #1404 - Query.findCount() with query.setDistinct(true) ... doesn't give count distinct SQL query 2018-06-07 00:32:39 +12:00
rob bygrave eca6383dba [maven-release-plugin] prepare for next development iteration 2018-06-01 15:44:03 +12:00
rob bygrave 624d08f46c [maven-release-plugin] prepare release ebean-11.17.1 2018-06-01 15:43:50 +12:00
rob bygrave 14749daf9b Bump to 11.17.1-SNAPSHOT ... as we added a SPI for io.ebeaninternal.api.SpiLogManager (for capturing the io.ebean.SQL log for testing purposes) 2018-06-01 15:42:24 +12:00
rob bygrave 2817875706 #1402 - Refactor SQL Logging internals (io.ebean.SQL) such that it is easier to capture for testing/asserts 2018-06-01 14:15:05 +12:00
rob bygrave 3f8d705439 #1402 - Refactor SQL Logging internals (io.ebean.SQL) such that it is easier to capture for testing/asserts 2018-06-01 14:10:00 +12:00
rob bygrave e9faef2692 #1401 - FOR UPDATE queries inside a transaction are not honored when beans are already cached (in the persistence context) 2018-06-01 11:33:41 +12:00
rob bygrave d459807af7 #1402 - Refactor SQL Logging internals (io.ebean.SQL) such that it is easier to capture for testing/asserts 2018-06-01 09:19:26 +12:00
ooknightandRob Bygrave 10ad9ef096 beautify bindNoLog format (#1399) 2018-05-31 20:23:52 +12:00
rob bygrave 11e3649b85 #1398 - Support EQ / "Equal to" predicate on client side encrypted entity property - @Encrypted(dbEncryption = false) 2018-05-31 16:02:49 +12:00
rob bygrave ddc00fedb6 #1397 - @DbForeignKey(noConstraint = true) is ignored on @ManyToMany ... foreign constraints still added 2018-05-31 10:55:58 +12:00
rob bygrave 0aa9208dcc [maven-release-plugin] prepare for next development iteration 2018-05-30 03:36:38 +12:00
rob bygrave e3b1996dc9 [maven-release-plugin] prepare release ebean-11.16.1 2018-05-30 03:36:24 +12:00
rob bygrave 24ca744e06 Bump version to 11.16.1-SNAPSHOT 2018-05-30 03:35:13 +12:00
rob bygrave 7f9ce5bdd4 #1396 merge() with ManyToMany throwing error Referential integrity constraint violation 2018-05-30 03:32:38 +12:00
rob bygrave 23e5de0819 #1394 #1395 - ENH: Add addBatch() and executeBatch() to SqlUpdate ... alternative to changing transaction batch mode enhancement 2018-05-30 03:32:23 +12:00
rob bygrave c23508819f #1396 merge() with ManyToMany throwing error Referential integrity constraint violation 2018-05-30 03:32:00 +12:00
rob bygrave af030e3da4 #1394 #1395 - ENH: Add addBatch() and executeBatch() to SqlUpdate ... alternative to changing transaction batch mode enhancement
ENH: Add merge() API (using default merge options) to EbeanServer, Ebean and BeanRepository enhancement
2018-05-30 03:31:32 +12:00
rob bygrave d8e67d09a9 #1393 - MergeOptionsBuilder build() return MergeOptions interface type 2018-05-29 15:30:39 +12:00
rob bygrave 66c08e663e #1392 - MergeHandler, change regex Pattern to be static 2018-05-29 15:25:38 +12:00
rob bygrave 9a096e272e #1392 - MergeOptionsBuilder setClientGeneratedIds() ... should not be required if the Id property does not have Identity/Sequence generator assigned 2018-05-29 15:24:32 +12:00
rob bygrave 21e87e12cb Adjust test for #1391 ... such that OrganizationTreeNode has at least one db column as part of insert (for PG) 2018-05-29 13:38:17 +12:00
rob bygrave 7c6d7dd4d0 Merge branch 'FOCONIS-fix-create-reference-bean' 2018-05-29 13:28:32 +12:00
Roland Praml c5329e9db0 reformatting 2018-05-28 17:05:52 +02:00
chrishartlandRoland Praml bccb9f8ddb Fix/create reference with descriptor using inheritance (#13)
* FIXED: Discriminator must be ask for inherited classes

* Fixed: Compile

* Refactoring and Testing
2018-05-28 16:54:53 +02:00
rob bygrave c57af29fca [maven-release-plugin] prepare for next development iteration 2018-05-29 00:44:36 +12:00
rob bygrave 37b6a321ab [maven-release-plugin] prepare release ebean-11.15.12 2018-05-29 00:44:22 +12:00
rob bygrave 896f7d9ea8 No effective change - improve error message when entity not registered to include entity annotation question 2018-05-29 00:38:41 +12:00
rob bygrave 141a0d9358 #1390 - ENH: Support explicit @Id @GeneratedValue ... mapping (where absence of @GeneratedValue means externally supplied id values) 2018-05-29 00:35:31 +12:00
Roland PramlandRob Bygrave 5a7f24da5d FIX: jsonRead of transient maps (#1389)
FIX: jsonRead of transient maps - to skip the read of start_array - Avoid error "Unexpected token start_object - expecting start_array "
2018-05-28 23:06:45 +12:00
rob bygrave 8ea86d6c6d [maven-release-plugin] prepare for next development iteration 2018-05-27 19:19:52 +12:00
rob bygrave fb5eeb60b1 [maven-release-plugin] prepare release ebean-11.15.11 2018-05-27 19:19:37 +12:00
rob bygrave 33ec8abb9d No effective change - update test remove unused exception 2018-05-27 17:38:33 +12:00
rob bygrave bbd7baf1be #1388 - AutoTune - tuned query includes "null" as fetched property 2018-05-27 17:33:26 +12:00
rob bygrave 2e36402a76 #1387 - Revert #1374 and expose io.ebean.event.ServletContextListener (as public API) 2018-05-27 15:54:32 +12:00
rob bygrave fa4af4ef98 Revert "#1374 - Remove io.ebeaninternal.server.core.ServletContextListener - should be unused"
This reverts commit 95737cd3c5.
2018-05-27 15:41:51 +12:00
rob bygrave ed6a9e2dbb #1386 - ENH: For DbMigration generation - add support for setting version, name, generatePendingDrop, applyPrefix, ddl header 2018-05-27 15:37:45 +12:00
rob bygrave 42d83705b7 #1384 - Change MigrationXmlWriter to not write <!DOCTYPE xml> (as it is not needed/expected now) 2018-05-24 00:15:25 +12:00
rob bygrave ae58d9223a No effective change - add test for PrimaryKeyJoinColumn - insertWithoutExtra 2018-05-24 00:07:30 +12:00
rob bygrave a9a89938f9 #1368 - Remove unused resource - default-create-table.sql ... which is in ebean-migration 2018-05-24 00:06:18 +12:00
rob bygrave 4b91f5fe0b #1380 - Make javax.xml an optional dependency (as it is not part of JDK from 9 onwards) 2018-05-21 01:06:26 +12:00
rob bygrave 85d3caf079 #1367 - Modularized project cannot locate ebean.properties file on Java 10 2018-05-21 00:29:04 +12:00
rob bygrave f6d8c8b618 [maven-release-plugin] prepare for next development iteration 2018-05-21 00:13:59 +12:00
rob bygrave d0fa3f0cb1 [maven-release-plugin] prepare release ebean-11.15.10 2018-05-21 00:13:43 +12:00
Roland PramlandRob Bygrave 41a31c1aa0 trigger based history will recreate view properly and handle default … (#1370)
* trigger based history will recreate view properly and handle default value also for history view

* HistoryTriggers are generated at the end

* Reference Script

* MYSQL: lock all tables at once
2018-05-20 17:19:10 +12:00
Roland PramlandRob Bygrave ab301f970c FIX: correct caching when collection with different inherited beans is put to the cache (#1379)
* FIX: correct cahcing when collection with different inherited beans is put to the cache

* removed some sysouts
2018-05-20 16:53:56 +12:00
rob bygrave d1b7b082a2 [maven-release-plugin] prepare for next development iteration 2018-05-17 12:50:26 +12:00
rob bygrave 5fb3a1e019 [maven-release-plugin] prepare release ebean-11.15.9 2018-05-17 12:50:12 +12:00
rob bygrave cb0dee998b #1375 - Bug: RawSql parsing fails when using placeholders 2018-05-17 12:48:08 +12:00
Roland PramlandRob Bygrave 53b606289d FIX: use LinkedHashMap in enum types to get deterministic ordering (#1378) 2018-05-17 12:26:37 +12:00
rob bygrave 574d5e215b [maven-release-plugin] prepare for next development iteration 2018-05-14 22:19:37 +12:00
rob bygrave 508f2129f5 [maven-release-plugin] prepare release ebean-11.15.8 2018-05-14 22:19:21 +12:00
rob bygrave cdfcbeec43 #1377 - Bump dependency - jsnakeyaml 1.19 to 1.21 2018-05-14 22:17:27 +12:00
rob bygrave 0e9782e6b3 #1376 - Bump dependency - jackson-core from 2.9.0 to 2.9.5 2018-05-14 22:15:58 +12:00
rob bygrave 927fa5f7d3 [maven-release-plugin] prepare for next development iteration 2018-05-14 22:10:34 +12:00
rob bygrave 53a80c7dd1 [maven-release-plugin] prepare release ebean-11.15.7 2018-05-14 22:10:18 +12:00
rob bygrave 89789f3347 #1372 - Missing @OneToOne mapping yields unusable error messages.
Restore mapping.
2018-05-14 21:33:11 +12:00
rob bygrave 9dc5ca602c #1372 - Missing @OneToOne mapping yields unusable error messages. 2018-05-14 21:32:48 +12:00
rob bygrave 95737cd3c5 #1374 - Remove io.ebeaninternal.server.core.ServletContextListener - should be unused 2018-05-14 21:23:13 +12:00
Roland PramlandRob Bygrave 0a49fc9b62 FIX: NPE when fetching inherited beans from cache (#1373) 2018-05-14 21:12:42 +12:00
rob bygrave f8c80c7fd2 [maven-release-plugin] prepare for next development iteration 2018-05-10 17:22:18 +12:00
rob bygrave fc837b6472 [maven-release-plugin] prepare release ebean-11.15.6 2018-05-10 17:22:04 +12:00
rob bygrave cfaedcdbc5 #1371 - Regression in 11.15.5 with @Inheritance on abstract class with discriminatorType = INTEGER 2018-05-10 17:20:09 +12:00
rob bygrave 111bac7514 [maven-release-plugin] prepare for next development iteration 2018-05-06 20:50:56 +12:00
rob bygrave 1a2ca63d65 [maven-release-plugin] prepare release ebean-11.15.5 2018-05-06 20:50:40 +12:00
rob bygrave 33031d1c69 #1369 - BeanListener and/or BeanPersistAdapter not fired on delete of children when cascaded 2018-05-06 20:21:10 +12:00
Roland PramlandRob Bygrave 49d9d88fbe FIX: load all props from cache when inheritance is involved (#1366) 2018-04-27 14:44:18 +12:00
Rob Bygrave 756f867ed2 #1365 - Regression: property ebean.namingConvention=io.ebean.config.MatchingNamingConvention ... no longer works 2018-04-18 21:24:37 +12:00
Rob Bygrave 12108ada38 #1363 - Update ebean-migration to 11.5.4 2018-04-18 20:47:59 +12:00
rob bygrave fb2fdc9b53 Merge branch 'master' of github.com:ebean-orm/ebean 2018-04-12 20:44:57 +12:00
rob bygrave d570971fd1 #1363 - Update ebean-migration to 11.5.2 2018-04-12 20:44:39 +12:00
André CamiloandRob Bygrave a8434f22ff Inheritance - Add support for @DiscriminatorValue to be optional (#1361) 2018-04-12 20:38:09 +12:00
rob bygrave b0d3921264 [maven-release-plugin] prepare for next development iteration 2018-04-05 22:23:18 +12:00
rob bygrave 4167dfc4af [maven-release-plugin] prepare release ebean-11.15.4 2018-04-05 22:23:03 +12:00
rob bygrave e49a0c9ffb #1360 - ENH: Add transaction.setNestedUseSavepoint() ... such that nested transactions are actually Savepoints
Additional tests
2018-04-05 21:48:54 +12:00
rob bygrave dd6f223ae1 #1360 - ENH: Add transaction.setNestedUseSavepoint() ... such that nested transactions are actually Savepoints 2018-04-05 21:35:29 +12:00
Roman ParshikovandRob Bygrave 05aff53241 Fix Maven links in README (#1358) 2018-04-03 21:17:46 +12:00
Rob Bygrave 9a3362e842 [maven-release-plugin] prepare for next development iteration 2018-03-29 14:35:40 +13:00
Rob Bygrave 04e0069eb3 [maven-release-plugin] prepare release ebean-11.15.3 2018-03-29 14:35:25 +13:00
Rob Bygrave b8c4e1c3d3 #1357 - Regression in 11.15.2 - for nested Ebean.beginTransaction(TxScope.requiresNew()); ... where closed via ebeanServer.endTransaction(); rather than transaction.end() 2018-03-29 10:22:12 +13:00
Rob Bygrave 6b136248fa [maven-release-plugin] prepare for next development iteration 2018-03-29 01:49:43 +13:00
Rob Bygrave aa004f11af [maven-release-plugin] prepare release ebean-11.15.2 2018-03-29 01:49:28 +13:00
Rob Bygrave acd5578d8a #115 - Mapping - Add support for @ElementCollection enhancement
Add element collection L2 cache support
2018-03-29 01:15:58 +13:00
Rob Bygrave 07946cd505 #115 - Mapping - Add support for @ElementCollection enhancement
Add element collection notification - combine into preUpdate/postUpdate of the bean
2018-03-28 17:31:53 +13:00
Rob Bygrave 57870aaa05 #115 - Mapping - Add support for @ElementCollection enhancement
Add embedded bean support
2018-03-28 13:22:46 +13:00
Rob Bygrave cca837bcfa #1354 - ENH: Add SqlUpdate setNextParameter() ... as alternative for positioned parameters 2018-03-28 00:45:12 +13:00
Rob Bygrave 534eb2f0e2 #1354 - ENH: Add SqlUpdate setNextParameter() ... as alternative for positioned parameters 2018-03-27 23:24:30 +13:00
Rob Bygrave dfa7b765f9 #1354 - ENH: Add SqlUpdate setNextParameter() ... as alternative for positioned parameters 2018-03-27 22:58:19 +13:00
Rob Bygrave 4ef581f87c No effective change - Update agent in pom to 11.11.1 2018-03-27 17:16:35 +13:00
Rob Bygrave eb96c726fd No effective change - Modify some tests to use transaction try with resources 2018-03-27 10:47:24 +13:00
Rob Bygrave abbcd35ee6 #1353 - Use of ebeanServer.commitTransaction() in nested transaction doesn't match transction.commit() and closes parent scope 2018-03-27 09:14:45 +13:00
Rob Bygrave db5a2b194d No effective change - add extra test coverage on DefaultTransactionThreadLocal 2018-03-26 23:31:31 +13:00
Rob Bygrave 74e0a16531 #1351 - ENH: Add named DtoQuery
Merge branch 'feature/namedDtoQuery' of https://github.com/hexagonframework/ebean into hexagonframework-feature/namedDtoQuery
2018-03-26 20:45:39 +13:00
Rob Bygrave 8601f51a6c #1351 - ENH: Add named DtoQuery
Merge branch 'feature/namedDtoQuery' of https://github.com/hexagonframework/ebean into hexagonframework-feature/namedDtoQuery
2018-03-26 20:45:22 +13:00
Rob Bygrave 6534feb5da #1350 - ENH: Add mappingsLocation config to search xml mapping files.
Minor adjustments - read "mappingLocations" only etc.
2018-03-26 16:55:25 +13:00
Yuan XueguiandRob Bygrave 6cf5e3386d ENH: Add mappingsLocation config to search xml mapping files. (#1350)
ENH: Add mappingsLocation config and search xml mapping files feature.
2018-03-26 16:37:25 +13:00
Rob Bygrave 7651256083 #1352 - Bump default datasource - with better defaults for Idle connection cleanup (5 mins) 2018-03-26 15:47:20 +13:00
Rob Bygrave 057d7480b0 #115 - Mapping - Add support for @ElementCollection enhancement
Add simple Map support with Json
2018-03-21 00:15:40 +13:00
Rob Bygrave 85c4385498 #115 - Mapping - Add support for @ElementCollection enhancement
Initial support - add simple Set support and Json support for simple List and Set.
2018-03-20 17:24:35 +13:00
Rob Bygrave 74a9019535 #115 - Mapping - Add support for @ElementCollection enhancement
Initial support - simple List.
2018-03-20 14:22:29 +13:00
Rob Bygrave c44931ea25 #115 - Supporting changes for Element collection support 2018-03-20 09:04:58 +13:00
Rob Bygrave 012322503f #115 - Supporting changes for Element collection support 2018-03-20 09:03:16 +13:00
yuanxuegui 5575273c96 ENH: Add named DtoQuery, such that we can define native SQL queries in ebean mapping xml for DtoQuery. 2018-03-19 18:04:49 +08:00
Rob Bygrave f3251691e2 Test only change - Add test for SavePoint and compare to transaction.setRollbackOnly() 2018-03-19 09:18:00 +13:00
yuanxuegui 9d336cb20b ENH: Add mappingsLocation config to search xml mapping files.
ENH: Add mappingsLocation config to search xml mapping files.

ENH: Add mappingsLocation config to search xml mapping files.

ENH: Add mappingsLocation config to search xml mapping files.

ENH: Add mappingsLocation config to search xml mapping files.

ENH: Add mappingsLocation config to search xml mapping files.

remove unused import.

ENH: Add mappingsLocation config and search xml mapping files feature.
2018-03-17 17:27:51 +08:00
Rob Bygrave 9895e48485 #1349 - Example passing test for - RawSql fetch object with inheritance / 2 2018-03-15 22:47:37 +13:00
Rob Bygrave 4799c56e25 #225 - Mapping - Add / review support for @IdClass 2018-03-15 18:19:17 +13:00
Rob Bygrave 602a35d0b6 #1348 - ENH: Add support for bitwiseAnd / bitwiseAny / bitwiseAll .. expressions for use with integer/long "Flags" properties 2018-03-14 16:23:16 +13:00
Rob Bygrave 4bc1f2ede4 [maven-release-plugin] prepare for next development iteration 2018-03-14 00:03:11 +13:00
Rob Bygrave 674617eb9e [maven-release-plugin] prepare release ebean-11.15.1 2018-03-14 00:02:58 +13:00
Rob Bygrave afead2da41 #1221 - ENH: Add a merge(bean) and merge(bean, paths) 2018-03-13 23:55:47 +13:00
Rob Bygrave 607cf3156c #1347 - Improve cleanup of inactive transactions from threadLocal when no end() used 2018-03-13 00:15:42 +13:00
Rob Bygrave af2b8976fa #1346 - Since ebean version 11.12.1 executing docStore query with findPagedList fails with error (due to order by id) 2018-03-12 18:28:17 +13:00
Rob Bygrave 85bfe3d03e #1345 - Refactor io.ebean.meta.MetaInfoManager API (query execution metrics). Breaking change for people collecting query execution metrics. 2018-03-10 02:51:51 +13:00
Rob Bygrave eda6350d89 #1344 - ENH: Add initial Cockroach DB support ... need to review JSON
Adds in the DB ARRAY support (exactly the same as Postgres)
2018-03-09 17:40:34 +13:00
Rob Bygrave 8ac1b745df #1344 - ENH: Add initial Cockroach DB support ... need to review JSON and DB ARRAY support 2018-03-09 16:54:38 +13:00
Rob Bygrave 6c8cf0c5b7 #1343 - Update for ... #1283 ENH: Support simplified test configuration (with docker setup typically via application-test.yml)
DefaultContainer with config.loadFromProperties(PropertiesLoader.load()); effectively runs eval twice on properties which is unnecessary.

Additionally add ServerConfig.setRunMigration() because I think it makes more sense / easier to find.

Remote PropertiesLoader.setProperty() as it is no longer required for the ebean-test-config plugin.
2018-03-09 10:34:25 +13:00
Rob Bygrave d0b24dc340 #1343 - Update the #1283 ENH: Support simplified test configuration (with docker setup typically via application-test.yml) 2018-03-09 00:44:04 +13:00
Rob Bygrave 2042bdf29a #1342 - Refactor and rename DbTypeConfig to Platform config to support multi-platform db migration 2018-03-08 23:23:50 +13:00
Rob Bygrave 4f327c2751 No effective change - default h2 platform for testing 2018-03-08 20:47:48 +13:00
Rob Bygrave fbc2ba3912 No effective change - Test update DDL review files 2018-03-08 20:47:28 +13:00
Rob Bygrave 93d659050b No effective change - Test on rename of DDL review files 2018-03-08 20:44:15 +13:00
Rob Bygrave 2520837f5e No effective change - Test on rename of DDL review files for oracle 2018-03-08 20:42:36 +13:00
Rob Bygrave 2f349ff054 #1318 No effective change - DDL review for ... ENH: native UUID-support for SqlServer
Shows the use of uniqueidentifier DB column type for UUID with sql server.
2018-03-08 19:37:30 +13:00
Rob Bygrave 0aec597cf2 #1318 No effective change - DDL review for ... ENH: native UUID-support for SqlServer 2018-03-08 19:35:48 +13:00
Rob Bygrave 5faec61cd2 #1318 - ENH: native UUID-support for SqlServer
Merge branch 'native-uuid-sqlserver' of https://github.com/FOCONIS/ebean into FOCONIS-native-uuid-sqlserver

# Conflicts:
#	src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
2018-03-08 19:34:36 +13:00
Rob Bygrave 7252e2d7d9 #1318 - ENH: native UUID-support for SqlServer
Merge branch 'native-uuid-sqlserver' of https://github.com/FOCONIS/ebean into FOCONIS-native-uuid-sqlserver

# Conflicts:
#	src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
2018-03-08 19:34:09 +13:00
Rob Bygrave 0cc87009d0 #1340 - (test only change) Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
Update DbMigrationGenerateTest ... for SqlServer17 platform
2018-03-08 18:37:36 +13:00
Rob Bygrave ef58954af5 Test only changes - related to #1336 - Change DDL generation such that it does create index before alter table add foreign key 2018-03-08 18:31:12 +13:00
Rob Bygrave 824f5e468e #1340 - (test only change) Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
Reset Offline platform in DbMigrationSqlServerTest
2018-03-08 18:18:13 +13:00
Rob Bygrave d84cdab714 #1340 - Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
Only need to run H2 based test TestAutoCommitDataSource with H2
2018-03-08 17:45:19 +13:00
Rob Bygrave 5a3550e603 #1340 - Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
Unrelated - update dbmigration ddl for create index / add fk change in order (db2)
2018-03-08 17:43:25 +13:00
Rob Bygrave f840cfc2ae #1340 - Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
test changes
2018-03-08 17:40:11 +13:00
Rob Bygrave 8e0d2f8749 #1340 - Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
test - dbmigration ddl
2018-03-08 17:39:10 +13:00
Rob Bygrave eef514f922 #1340 - Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
Changes to pom
2018-03-08 17:37:29 +13:00
Rob Bygrave 4fc7898f85 #1340 - Split SQL Server Platform into SqlServer16 and SqlServer17 (where 2017 which uses UTF8 types nvarchar etc and prefers Sequences).
Changes to main
2018-03-08 17:36:53 +13:00
Rob Bygrave 6258a5d263 Logging only change - Drop the logging from WARN to DEBUG for beans without an Id property (which is not usual but valid for Ebean) 2018-03-08 14:36:18 +13:00
Rob Bygrave dbf5f3b57b [maven-release-plugin] prepare for next development iteration 2018-03-08 14:16:45 +13:00
Rob Bygrave 4b594fb533 [maven-release-plugin] prepare release ebean-11.14.3 2018-03-08 14:16:30 +13:00
Rob Bygrave c0efd49c73 #1341 - Cascade persist with JDBC batch and BeanPersistAdapter with preUpdate() that invokes lazy loading on persisted bean ... results in OptimisticLockException 2018-03-08 14:12:32 +13:00
Rob Bygrave 54ba1480eb [maven-release-plugin] prepare for next development iteration 2018-03-07 20:22:38 +13:00
Rob Bygrave 4d8bc0a2ce [maven-release-plugin] prepare release ebean-11.14.2 2018-03-07 20:22:25 +13:00
Rob Bygrave e182190f96 #1339 - Regression: Fix for not throwing OptimisticLockException when loaded bean has @Version property set 2018-03-07 20:19:48 +13:00
Rob Bygrave 8711ebc6f7 Merge branch 'fix-limit-constraint-names' of https://github.com/FOCONIS/ebean into FOCONIS-fix-limit-constraint-names 2018-03-07 17:13:56 +13:00
Rob Bygrave baa7242939 #1338 - Regression since 11.12.1 - resolve expressions in Properties given to serverConfig.loadFromProperties() 2018-03-07 16:43:42 +13:00
Rob Bygrave 47e8d27212 #1337 - Fix YamlLoader ... such that it loads multi-part yml source 2018-03-07 16:21:00 +13:00
Rob Bygrave 70aed3906e [maven-release-plugin] prepare for next development iteration 2018-03-07 02:24:21 +13:00
Rob Bygrave 70c3860a65 [maven-release-plugin] prepare release ebean-11.14.1 2018-03-07 02:24:07 +13:00
Rob Bygrave 1ba21d5c43 Bump to 11.14.1-SNAPSHOT ... Modified DDL generation order of create index vs alter table add FK 2018-03-07 02:22:58 +13:00
Rob Bygrave 2019bc7bf1 #1336 - Change DDL generation such that it does create index before alter table add foreign key 2018-03-07 02:19:10 +13:00
Rob Bygrave 3cb52917e3 #1335 - Add Try catch to YAML loading ... add system property ebeanSkipYaml ... to enable skipping the read of application.yml (well, all yml) 2018-03-07 02:03:54 +13:00
Rob Bygrave 55a257a64c #1334 - ENH: Convert ORM query into DTO query ... with dynamic formula support 2018-03-07 01:52:18 +13:00
Rob Bygrave 83ef204f3c Merge branch 'master' of github.com:ebean-orm/ebean 2018-03-06 21:15:07 +13:00
Rob Bygrave a18cac035b #1333 - ENH: Support "dynamic formula" with findSingleAttributeList() ... e.g. .select("concat(lastName,', ',firstName)")
Support property alias via "AS" ...  concat(lastName,', ',firstName) as lastName
2018-03-06 21:14:26 +13:00
Rob Bygrave 873c14faaf #1333 - ENH: Support "dynamic formula" with findSingleAttributeList() ... e.g. .select("concat(lastName,', ',firstName)")
Support cast of formula like ... select("max(updtime)::Instant")
2018-03-06 20:58:30 +13:00
Rob Bygrave e1af7c29eb #1333 - ENH: Support "dynamic formula" with findSingleAttributeList() ... e.g. .select("concat(lastName,', ',firstName)")
Concat automatically maps to String ScalarType
2018-03-06 17:35:26 +13:00
Rob Bygrave c1c85b67c2 #1333 - ENH: Support "dynamic formula" with findSingleAttributeList() ... e.g. .select("concat(lastName,', ',firstName)") 2018-03-06 17:29:45 +13:00
Rob Bygrave 05b8adbf6e No effective change - change test from extends testcase to normal junit 2018-03-06 16:37:47 +13:00
Rob Bygrave 3ca4efc818 #1331 - ENH: Support Aggregation formula with findSingleAttribute() ... max, min, avg, count and count(distinct ..) 2018-03-06 12:58:43 +13:00
Rob Bygrave ec2c3b13d4 No effective change - add test for deploy parser 2018-03-06 01:08:42 +13:00
Rob Bygrave 5a3e386b7c No effective change - add test for selecting only some @Aggregation properties 2018-03-05 19:59:40 +13:00
Roland PramlandRob Bygrave 7e56a147be FIX: ReadOnlyDatasource must also be configured (#1323) 2018-03-05 19:29:40 +13:00
Rob Bygrave 62cdfb3209 #1330 - Update ANTLR dependency to 4.7.1 2018-03-05 19:22:38 +13:00
Rob Bygrave 740532da4b #1329 - Ebean 11.12.1 broken when using ScalarTypeConverter with enum 2018-03-05 19:19:27 +13:00
Rob Bygrave 130356cb3b [maven-release-plugin] prepare for next development iteration 2018-03-02 23:39:17 +13:00
Rob Bygrave ab84cfae52 [maven-release-plugin] prepare release ebean-11.13.1 2018-03-02 23:39:03 +13:00
Rob Bygrave ad21b7a3fd Merge branch 'master' of github.com:ebean-orm/ebean 2018-03-02 23:37:55 +13:00
Rob Bygrave 31581082d2 Bump to 11.13.1-SNAPSHOT ... added findDto() 2018-03-02 23:37:38 +13:00
Roland PramlandRob Bygrave aa84dfe7d8 Add: Configuration md that describes the ebean options (#1325) 2018-03-02 23:33:10 +13:00
Rob Bygrave aad5b33db2 #1297 - ENH: Add DtoQuery ... such that we can map native SQL queries automatically into "DTO beans" 2018-03-02 23:32:01 +13:00
Roland PramlandRob Bygrave 5801f7620d FIX: add columnDefaultValue to more places (#1327) 2018-03-02 16:39:53 +13:00
Roland PramlandRob Bygrave 514416a1e0 Testcase Fix for SqlServer (#1326)
Test change only - Testcase Fix for SqlServer
2018-03-02 16:38:59 +13:00
Roland PramlandRob Bygrave 8faa95adc1 ENH: new method AnnotationUtil.findMetaAnnotationsRecursive (#1319)
* ENH: new method AnnotationUtil.findMetaAnnotationsRecursive

* clarified in javadoc
2018-03-02 16:28:41 +13:00
Rob Bygrave ebaeafc11b #1303 - NEW: Ebean.checkUniqueness ... add example to javadoc 2018-03-02 16:25:13 +13:00
Roland Praml 20e46cad9b Merge remote-tracking branch 'ebean/master' into native-uuid-sqlserver 2018-03-02 03:18:20 +01:00
Rob Bygrave 98e014f8c8 #1300 - NEW: FetchCountDistinct ... add example to javadoc + support chaining use via ExpressionList 2018-03-02 14:30:38 +13:00
Rob Bygrave 78912d75e0 #1306 - No effective change - javadoc update 2018-03-02 13:45:31 +13:00
Rob Bygrave 09786a7062 #1301 - No effective change - format lines 2018-03-02 13:30:40 +13:00
Rob Bygrave 54530248a5 #1312 - ENH: added SqlRow factory method to RawSqlService
Support the PR added binaryOptimizedUUID
2018-03-02 13:25:55 +13:00
Roland Praml 9953ef35eb ENH: native UUID-support for SqlServer 2018-03-02 01:14:42 +01:00
Roland Praml fdafa776e1 Merge remote-tracking branch 'ebean/master' into fix-limit-constraint-names
# Conflicts:
#	src/test/resources/dbmigration/migrationtest/db2/1.0__initial.sql
#	src/test/resources/dbmigration/migrationtest/db2/1.1.sql
#	src/test/resources/dbmigration/migrationtest/db2/1.3.sql
2018-03-02 01:03:36 +01:00
Roland Praml 392eb2744d ADD: comment 2018-03-02 00:17:23 +01:00
Roland PramlandRob Bygrave b642b3c644 FIX: Splitcolumns must return an empty array if string was empty (#1315)
* Add test suite from rpraml

* FIX: Splitcolumns must return an empty array if string was empty

* Just the reference scripts
2018-03-02 12:16:29 +13:00
Roland PramlandRob Bygrave 0e1c815826 FIX: DataReader was read twice in exception-path (#1314) 2018-03-02 12:15:01 +13:00
Roland PramlandRob Bygrave 83b463040f ENH: Add sanity check to InExpression (#1313) 2018-03-02 12:14:09 +13:00
Roland PramlandRob Bygrave aea7d937eb ENH: added SqlRow factory method to RawSqlService (#1312)
* ENH: added SqlRow factory method to RawSqlService

* compile fixes

* ADD: sqlRow factory also to RawSqlBuilder
2018-03-02 12:13:16 +13:00
Roland PramlandRob Bygrave 78628ffad0 NEW: DB-with-Master option (#1311) 2018-03-02 12:12:34 +13:00
Roland PramlandRob Bygrave 56a742ad70 NEW: UUID version 1 generator (#1309)
* NEW: Uuid Type 1 generator

* Remove parameter (from other PR)

* FIX: Mac detection

* Have to use nodeId in generation
2018-03-02 12:09:21 +13:00
Roland Praml a4415b03ff FIX: limit the constraint names 2018-03-02 00:08:52 +01:00
Roland PramlandRob Bygrave e252f71792 ENH: Optimized binary form for UUID (Type 1) (#1308)
* ENH: Optimized binary form for UUID (Type 1)

* added missing parameter in test
2018-03-02 12:04:56 +13:00
Roland PramlandRob Bygrave 18ca3403ac NEW: flag to control, if javax.validation NotNull annotation should generate a NotNull column or not (#1306) 2018-03-02 12:02:02 +13:00
Roland PramlandRob Bygrave 94a118d635 FIX: do not set username and password for DbMigrationConfig (#1304) 2018-03-02 11:59:01 +13:00
Roland PramlandRob Bygrave efe325034b NEW: Ebean.checkUniqueness - you can check a bean, if save would work (#1303) 2018-03-02 11:57:44 +13:00
Roland PramlandRob Bygrave 0c368ed7b3 FIX: extra-ddl can contribute to "drop-all" script, otherwise the drop all script is not appyable to h2:file (#1301) 2018-03-02 11:48:35 +13:00
Roland PramlandRob Bygrave c1b022c123 NEW: FetchCountDistinct - query can return distinct values with their counts (#1300)
* NEW: FetchCountDistinct - query can return distinct values with their counts

* Have to disable asserts, because of #1298
2018-03-02 11:46:41 +13:00
Roland PramlandRob Bygrave 021d57f4b2 FIX: @Formula on @ManyToOne produces SQLException ... fix appends missing FROM clause for formula joins #1299 2018-03-02 11:43:08 +13:00
Roland PramlandRob Bygrave 11e180aa3f FIX: introduced TransactionalTestCase, so that changes to DB are not persisted (#1298)
* FIX: introduced TransactionalTestCase, so that changes to DB are not persisted

* removed license header
2018-03-02 09:10:21 +13:00
Rob Bygrave 9e2ddc9f8c [maven-release-plugin] prepare for next development iteration 2018-03-01 13:43:27 +13:00
877 changed files with 54114 additions and 16944 deletions
+1
View File
@@ -10,3 +10,4 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
spaces_around_operators = true
max_line_length = 130
+1
View File
@@ -20,3 +20,4 @@ profiling/
*.ipr
*.iws
.idea/
*uuid.state
+298
View File
@@ -0,0 +1,298 @@
Overview of ebean.properties file
=================================
### DbMigration options
You can set individual values for different platforms when generating migrations
`dbmigration.platform.<PLATFORM>.databaseSequenceBatchSize`
For DB's using sequences this is the number of sequence values prefetched.
`dbmigration.platform.<PLATFORM>.dbuuid`
Control, how UUID generation should work - it affects DDL which column type is generated. Possible values:
- BINARY enforces binary UUID
- VARCHAR enforces varchar UUID
- BINARY_OPTIMIZED enforces binary-optimized UUID (makes sense only with Type1 ID)
- AUTO_BINARY use binary when platform does not support native UUID
- AUTO_BINARY_OPTIMIZED use binary-optimized when platform does not support native UUID
- AUTO_VARCHAR (default) use varchar when platform does not support UUID
`dbmigration.platform.<PLATFORM>.uuidStoreAsBinary`
Same as setting dbuuid to BINARY
`dbmigration.platform.<PLATFORM>.geometrySRID`
The Geometry SRID value (default 4326).
`dbmigration.platform.<PLATFORM>.idType`
The ID type (IDENTITY, SEQUENCE, GENERATOR, EXTERNAL)
`dbmigration.platform.<PLATFORM>.mapping`
Adjust the mapping. For example `BOOLEAN=integer(32);BIT=tinyint(3)`
`ebean.migration.applyPrefix`
Set this to "V" to be compatible with FlywayDB.
`ebean.migration.applySuffix`
Defaults to '.sql'
`ebean.migration.dbusername`
DB user used to run the DB migration.
`ebean.migration.dbpassword`
DB password used to run the DB migration.
`ebean.migration.generate`
Set to true if the DB migration should be generated on server start.
`ebean.migration.generatePendingDrop`
The version of a pending drop that should be generated as the next migration.
`ebean.migration.includeGeneratedFileComment`
TODO
`ebean.migration.metaTable`
For running migration the DB table that holds migration execution status. Default 'db_migration'
`ebean.migration.migrationPath`
Resource path for the migration xml and sql.
`ebean.migration.modelPath`
Subdirectory the model xml files go into. Default 'model'
`ebean.migration.modelSuffix`
Suffix. Default '.model.xml'
`ebean.migration.name`
Description text that can be appended to the version to become the ddl script file name
`ebean.migration.patchInsertOn`
migration versions that should be added to history without running.
`ebean.migration.patchResetChecksumOn`
migration versions that should have their checksum reset and not run.
Use this if you get a 'Checksum mismatch' error.
`ebean.migration.placeholders`
A comma and equals delimited placeholders that are substituted in SQL scripts when running migration (used by DB Migration runner only).
`ebean.migration.platform`
The database platform to generate migration DDL for.
`ebean.migration.run`
Flag set to true means to run any outstanding migrations on startup.
`ebean.migration.version`
The migration version name (typically FlywayDb compatible). Example: 1.1.1_2
### Ebean UUID options
`ebean.uuidVersion`
Controls, how the UUIDs are generated. Possible values
- VERSION4 (default) generate random V4 UUIDs,
- VERSION1 generate rfc4122 compliant Type 1 UUIDs (requires a state file)
- VERSION1RND generate fake Type 1 UUIDs
Note, that V1 UUIDs in conjunction with AUTO_BINARY_OPTIMIZED will give you the best index performance, but you MUST understand how this works to avoid collisions.
`ebean.uuidStateFile`
The state file that is Required to generate V1 UUIDs
### DocStoreConfig
`ebean.docstore.active`
True when the Document store integration is active/on.
`ebean.docstore.allowAllCertificates`
Set to true such that the client allows connections to invalid/self signed SSL certificates.
`ebean.docstore.bulkBatchSize`
The default batch size to use for the Bulk API calls.
`ebean.docstore.create`
When true the Document store should create any document indexes that don't already exist.
`ebean.docstore.dropCreate`
When true the Document store should drop and re-create document indexes.
`ebean.docstore.generateMapping`
Set to true means Ebean will generate mapping files on startup.
`ebean.docstore.mappingPath`
Resource path for the Document store mapping files.
`ebean.docstore.mappingSuffix`
Suffix used for mapping files.
`ebean.docstore.password`
Password credential that be used for authentication to document store.
`ebean.docstore.pathToResources`
Location of resources that mapping files are generated into.
`ebean.docstore.persist`
The default mode used by indexes. `DEFAULT|IGNORE|QUEUE|UPDATE`
`ebean.docstore.url`
The URL of the Document store server. For example: http://localhost:9200.
`ebean.docstore.username`
Credential that be used for authentication to document store.
### Ebean Autotune
`ebean.autoTune.mode`
The autoTune mode `DEFAULT_OFF|DEFAULT_ON|DEFAULT_ONIFEMPTY`
`ebean.autoTune.profiling`
TODO
`ebean.autoTune.profilingBase`
TODO
`ebean.autoTune.profilingFile`
TODO
`ebean.autoTune.profilingRate`
TODO
`ebean.autoTune.profilingUpdateFrequency`
TODO
`ebean.autoTune.queryTuning`
TODO
`ebean.autoTune.queryTuningAddVersion`
TODO
`ebean.autoTune.queryTuningFile`
TODO
`ebean.autoTune.skipGarbageCollectionOnShutdown`
TODO
`ebean.autoTune.skipProfileReportingOnShutdown`
TODO
### Ebean options
`ebean.allQuotedIdentifiers`
Quote all identifiers
`ebean.asOfSysPeriod`
Column used to support history and 'As of' queries. This column is a timestamp range or equivalent.
`ebean.asOfViewSuffix`
Suffix appended to the base table to derive the view that contains the union of the base table and the history table in order to support asOf queries.
`ebean.autoCommitMode`
Set to true if the DataSource uses autoCommit. Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
`ebean.autoReadOnlyDataSource`
When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig
`ebean.autostart`
Should the server start all
`ebean.backgroundExecutorSchedulePoolSize`
TODO
`ebean.backgroundExecutorShutdownSecs`
TODO
and some more todos:
`ebean.batch.mode`
`ebean.batch.size`
`ebean.changeLogAsync`
`ebean.changeLogIncludeInserts`
`ebean.classes`
`ebean.collectQueryOrigins`
`ebean.collectQueryStatsByNode`
`ebean.currentUserProvider`
`ebean.dataSourceJndiName`
`ebean.dataTimeZone`
`ebean.databaseBooleanFalse`
`ebean.databaseBooleanTrue`
`ebean.databasePlatform`
`ebean.databasePlatformName`
`ebean.databaseSequenceBatchSize`
ebean.datasource.h2.adminpassword
ebean.datasource.h2.adminusername
`ebean.dbEncrypt`
`ebean.dbOffline`
ebean.ddl.createOnly
ebean.ddl.generate
ebean.ddl.header
ebean.ddl.initSql
ebean.ddl.run
ebean.ddl.seedSql
ebean.defaultDeleteMissingChildren
ebean.defaultOrderById
ebean.disableClasspathSearch
ebean.disableL2Cache
ebean.docStoreOnly
ebean.docstore.active
ebean.docstore.allowAllCertificates
ebean.docstore.bulkBatchSize
ebean.docstore.create
ebean.docstore.dropCreate
ebean.docstore.generateMapping
ebean.docstore.mappingPath
ebean.docstore.mappingSuffix
ebean.docstore.password
ebean.docstore.pathToResources
ebean.docstore.persist
ebean.docstore.url
ebean.docstore.username
ebean.encryptDeployManager
ebean.encryptKeyManager
ebean.encryptor
ebean.explicitTransactionBeginMode
ebean.expressionEqualsWithNullAsNoop
ebean.expressionNativeIlike
ebean.geometrySRID
ebean.historyTableSuffix
ebean.jdbcFetchSizeFindEach
ebean.jdbcFetchSizeFindList
ebean.jodaLocalTimeMode
ebean.jsonDateTime
ebean.jsonInclude
ebean.lazyLoadBatchSize
ebean.localTimeWithNanos
ebean.namingConvention.schema
ebean.namingConvention.sequenceFormat
ebean.namingConvention.useForeignKeyPrefix
ebean.namingconvention
ebean.notifyL2CacheInForeground
ebean.packages
ebean.persistBatch
ebean.persistBatchOnCascade
ebean.persistBatchSize
ebean.persistBatching
ebean.persistenceContextScope
ebean.profiling
ebean.profiling.directory
ebean.profiling.includeProfileIds
ebean.profiling.minimumMicros
ebean.profiling.profilesPerFile
ebean.profiling.verbose
ebean.queryBatchSize
ebean.queryPlanTTLSeconds
ebean.search.packages
ebean.serverCachePlugin
ebean.skipCacheAfterWrite
ebean.slowQueryMillis
ebean.tenant.catalogProvider
ebean.tenant.currentTenantProvider
ebean.tenant.mode
ebean.tenant.partitionColumn
ebean.tenant.schemaProvider
ebean.updateAllPropertiesInBatch
ebean.updateChangesOnly
ebean.updatesDeleteMissingChildren
ebean.useJavaxValidationNotNull
ebean.useJtaTransactionManager
+3 -3
View File
@@ -11,7 +11,7 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
## Current versions
* [![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean) - ebean
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin) - ebean-maven-plugin
* [![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean) - ebean
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
+19 -14
View File
@@ -9,15 +9,20 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.12.1</version>
<version>11.19.2</version>
<packaging>jar</packaging>
<name>ebean</name>
<url>http://ebean-orm.github.io/</url>
<properties>
<jackson-core.version>2.9.5</jackson-core.version>
<snakeyaml.version>1.21</snakeyaml.version>
</properties>
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.12.1</tag>
<tag>ebean-11.19.2</tag>
</scm>
<profiles>
@@ -89,7 +94,7 @@
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.19</version>
<version>${snakeyaml.version}</version>
</dependency>
<!--
@@ -112,13 +117,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>3.8</version>
<version>4.1</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-datasource</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</dependency>
<dependency>
@@ -130,27 +135,27 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.3.1</version>
<version>11.7.1</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.6</version>
<version>4.7.1</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
<version>${jackson-core.version}</version>
</dependency>
<!-- provided scope for JsonNode support -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
<version>${jackson-core.version}</version>
<scope>provided</scope>
</dependency>
@@ -187,7 +192,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212</version>
<version>42.2.2</version>
<scope>provided</scope>
</dependency>
@@ -224,7 +229,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>11.10.1</version>
<version>11.11.1</version>
<scope>test</scope>
</dependency>
@@ -254,7 +259,7 @@
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.1.3.jre8-preview</version>
<version>6.4.0.jre8</version>
<scope>test</scope>
</dependency>
@@ -297,7 +302,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>11.10.1</version>
<version>11.11.1</version>
<executions>
<execution>
<id>test</id>
@@ -355,7 +360,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<doctitle>Ebean 10</doctitle>
<doctitle>Ebean 11</doctitle>
<overview>src/main/java/io/ebean/overview.html</overview>
<source>1.8</source>
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
@@ -2,6 +2,8 @@ package io.ebean;
import io.ebean.bean.EntityBean;
import java.util.Collection;
/**
* Provides finder functionality for use with "Dependency Injection style" use of Ebean.
* <p>
@@ -115,6 +117,13 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
db().save(bean);
}
/**
* Save all the beans in the collection.
*/
public int saveAll(Collection<T> bean) {
return db().saveAll(bean);
}
/**
* Update this entity.
*
@@ -154,6 +163,13 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
return db().delete(bean);
}
/**
* Delete all the beans in the collection.
*/
public int deleteAll(Collection<T> beans) {
return db().deleteAll(beans);
}
/**
* Delete a bean permanently without soft delete.
* <p>
@@ -167,6 +183,30 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
return db().deletePermanent(bean);
}
/**
* Merge this entity using the default merge options.
* <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#merge(Object)
*/
public void merge(T bean) {
db().merge(bean);
}
/**
* Merge this entity using the specified merge options.
* <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#merge(Object, MergeOptions)
*/
public void merge(T bean, MergeOptions options) {
db().merge(bean, options);
}
/**
* Refreshes this entity from the database.
*
@@ -0,0 +1,27 @@
package io.ebean;
/**
* Enumeration to use with {@link Query#setCountDistinct(CountDistinctOrder)}.
* @author Roland Praml, FOCONIS AG
*
*/
public enum CountDistinctOrder {
NO_ORDERING,
/** order by attribute ascending */
ATTR_ASC,
/** order by attribute descending */
ATTR_DESC,
/** order by count ascending and attribute ascending */
COUNT_ASC_ATTR_ASC,
/** order by count ascending and attribute descending */
COUNT_ASC_ATTR_DESC,
/** order by count descending and attribute ascending */
COUNT_DESC_ATTR_ASC,
/** order by count descending and attribute descending */
COUNT_DESC_ATTR_DESC,
}
+33
View File
@@ -0,0 +1,33 @@
package io.ebean;
import java.io.Serializable;
/**
* Holds a distinct value with it's count.
* (Used with {@link Query#findSingleAttributeList()} and {@link Query#setCountDistinct(CountDistinctOrder)}.)
* @author Roland Praml, FOCONIS AG
*/
public class CountedValue<A> implements Serializable {
private static final long serialVersionUID = -2267971668356749695L;
private final A value;
private final long count;
public CountedValue(A value, long count) {
this.value = value;
this.count = count;
}
public long getCount() {
return count;
}
public A getValue() {
return value;
}
@Override
public String toString() {
return count + ": " + value;
}
}
+133
View File
@@ -0,0 +1,133 @@
package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Query for performing native SQL queries that return DTO Bean's.
* <p>
* These beans are just normal classes. They must have public constructors
* and setters.
* <p>
* Constructors with arguments are used if the number of constructor arguments
* matches the number of columns in the resultSet.
* </p>
* <p>
* If the number of columns in the resultSet is greater than the largest constructor
* then the largest constructor is used for the first columns and remaining columns
* are mapped by setter methods.
* </p>
*
* <pre>{@code
*
* // CustomerDto is just a 'bean like' class
* // with public constructor(s) and public setter methods
*
* String sql = "select id, name from customer where name like :name and status_code = :status";
*
* List<CustomerDto> beans =
* Ebean.findDto(CustomrDto.class, sql)
* .setParameter("name", "Acme%")
* .setParameter("status", "ACTIVE")
* .findList();
*
* }</pre>
*/
public interface DtoQuery<T> {
/**
* Execute the query returning a list.
*/
@Nonnull
List<T> findList();
/**
* Execute the query iterating a row at a time.
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEach(Consumer<T> consumer);
/**
* Execute the query iterating a row at a time with the ability to stop consuming part way through.
* <p>
* Returning false after processing a row stops the iteration through the query results.
* </p>
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEachWhile(Predicate<T> consumer);
/**
* Execute the query returning a single bean.
*/
@Nullable
T findOne();
/**
* Execute the query returning an optional bean.
*/
@Nonnull
Optional<T> findOneOrEmpty();
/**
* The same as bind for named parameters.
*/
DtoQuery<T> setParameter(String name, Object value);
/**
* The same as bind for positioned parameters.
*/
DtoQuery<T> setParameter(int position, Object value);
/**
* Set the index of the first row of the results to return.
*/
DtoQuery<T> setFirstRow(int firstRow);
/**
* Set the maximum number of query results to return.
*/
DtoQuery<T> setMaxRows(int maxRows);
/**
* When resultSet columns are not able to be mapped to a bean property then instead of
* throwing effectively skip reading that column.
*/
DtoQuery<T> setRelaxedMode();
/**
* Set a label on the query to make it easier to identify queries related to query execution statistics.
*
* @param label A label that is unique to the DTO bean type.
*/
DtoQuery<T> setLabel(String label);
/**
* 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.
*/
DtoQuery<T> 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>
*/
DtoQuery<T> setBufferFetchSizeHint(int bufferFetchSizeHint);
}
+99 -2
View File
@@ -3,11 +3,13 @@ package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ServerConfig;
import io.ebean.plugin.Property;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
@@ -15,6 +17,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
@@ -113,10 +116,11 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public final class Ebean {
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
static {
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
}
/**
* Manages creation and cache of EbeanServers.
*/
@@ -371,7 +375,7 @@ public final class Ebean {
* Note that this provides an try finally alternative to using {@link #executeCall(TxScope, Callable)} or
* {@link #execute(TxScope, Runnable)}.
* </p>
*
* <p>
* <h3>REQUIRES_NEW example:</h3>
* <pre>{@code
* // Start a new transaction. If there is a current transaction
@@ -634,6 +638,25 @@ public final class Ebean {
serverMgr.getDefaultServer().updateAll(beans);
}
/**
* Merge the bean using the default merge options.
*
* @param bean The bean to merge
*/
public static void merge(Object bean) {
serverMgr.getDefaultServer().merge(bean);
}
/**
* Merge the bean using the given merge options.
*
* @param bean The bean to merge
* @param options The options to control the merge
*/
public static void merge(Object bean, MergeOptions options) {
serverMgr.getDefaultServer().merge(bean, options);
}
/**
* Save all the beans from a Collection.
*/
@@ -641,6 +664,65 @@ public final class Ebean {
return serverMgr.getDefaultServer().saveAll(beans);
}
/**
* This method checks the uniqueness of a bean. I.e. if the save will work. It will return the
* properties that violates an unique / primary key. This may be done in an UI save action to
* validate if the user has entered correct values.
* <p>
* Note: This method queries the DB for uniqueness of all indices, so do not use it in a batch update.
* <p>
* Note: This checks only the root bean!
* <p>
* <pre>{@code
*
* // there is a unique constraint on title
*
* Document doc = new Document();
* doc.setTitle("One flew over the cuckoo's nest");
* doc.setBody("clashes with doc1");
*
* Set<Property> properties = server().checkUniqueness(doc);
*
* if (properties.isEmpty()) {
* // it is unique ... carry on
*
* } else {
* // build a user friendly message
* // to return message back to user
*
* String uniqueProperties = properties.toString();
*
* StringBuilder msg = new StringBuilder();
*
* properties.forEach((it)-> {
* Object propertyValue = it.getVal(doc);
* String propertyName = it.getName();
* msg.append(" property["+propertyName+"] value["+propertyValue+"]");
* });
*
* // uniqueProperties > [title]
* // custom msg > property[title] value[One flew over the cuckoo's nest]
*
* }
*
* }</pre>
*
* @param bean The entity bean to check uniqueness on
* @return a set of Properties if constraint validation was detected or empty list.
*/
@Nonnull
public static Set<Property> checkUniqueness(Object bean) {
return serverMgr.getDefaultServer().checkUniqueness(bean);
}
/**
* Same as {@link #checkUniqueness(Object)}. but with given transaction.
*/
@Nonnull
public static Set<Property> checkUniqueness(Object bean, Transaction transaction) {
return serverMgr.getDefaultServer().checkUniqueness(bean, transaction);
}
/**
* Delete the bean.
* <p>
@@ -1053,6 +1135,21 @@ public final class Ebean {
return serverMgr.getDefaultServer().findNative(beanType, nativeSql);
}
/**
* Create a Query for DTO beans.
* <p>
* DTO beans are just normal bean like classes with public constructor(s) and setters.
* They do not need to be registered with Ebean before use.
* </p>
*
* @param dtoType The type of the DTO bean the rows will be mapped into.
* @param sql The SQL query to execute.
* @param <T> The type of the DTO bean.
*/
public static <T> DtoQuery<T> findDto(Class<T> dtoType, String sql) {
return serverMgr.getDefaultServer().findDto(dtoType, sql);
}
/**
* Create an Update query to perform a bulk update.
* <p>
+138 -427
View File
@@ -4,13 +4,13 @@ import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ServerConfig;
import io.ebean.meta.MetaInfoManager;
import io.ebean.plugin.Property;
import io.ebean.plugin.SpiServer;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
@@ -424,6 +424,32 @@ public interface EbeanServer {
*/
<T> Update<T> createUpdate(Class<T> beanType, String ormUpdate);
/**
* Create a Query for DTO beans.
* <p>
* DTO beans are just normal bean like classes with public constructor(s) and setters.
* They do not need to be registered with Ebean before use.
* </p>
*
* @param dtoType The type of the DTO bean the rows will be mapped into.
* @param sql The SQL query to execute.
* @param <T> The type of the DTO bean.
*/
<T> DtoQuery<T> findDto(Class<T> dtoType, String sql);
/**
* Create a named Query for DTO beans.
* <p>
* DTO beans are just normal bean like classes with public constructor(s) and setters.
* They do not need to be registered with Ebean before use.
* </p>
*
* @param dtoType The type of the DTO bean the rows will be mapped into.
* @param namedQuery The name of the query
* @param <T> The type of the DTO bean.
*/
<T> DtoQuery<T> createNamedDtoQuery(Class<T> dtoType, String namedQuery);
/**
* Create a SqlQuery for executing native sql
* query statements.
@@ -495,30 +521,44 @@ public interface EbeanServer {
* etc.
* </p>
* <p>
* <h3>Using try with resources</h3>
* <pre>{@code
*
* // start a transaction (stored in a ThreadLocal)
* ebeanServer.beginTransaction();
*
* try (Transaction txn = ebeanServer.beginTransaction()) {
*
* Order order = ebeanServer.find(Order.class,10);
* ...
* ebeanServer.save(order);
*
* txn.commit();
* }
*
* }</pre>
* <p>
* <h3>Using try finally block</h3>
* <pre>{@code
*
* // start a transaction (stored in a ThreadLocal)
* Transaction txn = ebeanServer.beginTransaction();
* try {
* Order order = ebeanServer.find(Order.class,10);
*
* ebeanServer.save(order);
*
* ebeanServer.commitTransaction();
* txn.commit();
*
* } finally {
* // rollback if we didn't commit
* // i.e. an exception occurred before commitTransaction().
* ebeanServer.endTransaction();
* txn.end();
* }
*
* }</pre>
* <p>
* <h3>Transaction options:</h3>
* <h3>Transaction options</h3>
* <pre>{@code
*
* Transaction txn = ebeanServer.beginTransaction();
* try {
* try (Transaction txn = ebeanServer.beginTransaction()) {
* // explicitly turn on/off JDBC batch use
* txn.setBatchMode(true);
* txn.setBatchSize(50);
@@ -539,10 +579,6 @@ public interface EbeanServer {
* ...
*
* txn.commit();
*
* } finally {
* // rollback if necessary
* txn.end();
* }
*
* }</pre>
@@ -572,19 +608,16 @@ public interface EbeanServer {
* <pre>{@code
* // Start a new transaction. If there is a current transaction
* // suspend it until this transaction ends
* Transaction txn = server.beginTransaction(TxScope.requiresNew());
* try {
* try (Transaction txn = server.beginTransaction(TxScope.requiresNew())) {
*
* ...
*
* // commit the transaction
* txn.commit();
*
* } finally {
* // end this transaction which:
* // A) will rollback transaction if it has not been committed already
* // At end this transaction will:
* // A) will rollback transaction if it has not been committed
* // B) will restore a previously suspended transaction
* txn.end();
* }
*
* }</pre>
@@ -593,20 +626,13 @@ public interface EbeanServer {
* <pre>{@code
*
* // start a new transaction if there is not a current transaction
* Transaction txn = server.beginTransaction(TxScope.required());
* try {
* try (Transaction txn = server.beginTransaction(TxScope.required())) {
*
* ...
*
* // commit the transaction if it was created or
* // do nothing if there was already a current transaction
* txn.commit();
*
* } finally {
* // end this transaction which will rollback the transaction
* // if it was created for this try finally scope and has not
* // already been committed
* txn.end();
* }
*
* }</pre>
@@ -782,411 +808,18 @@ public interface EbeanServer {
<T> T getReference(Class<T> beanType, Object id);
/**
* Return the number of 'top level' or 'root' entities this query should return.
*
* @see Query#findCount()
* @see Query#findFutureCount()
*/
<T> int findCount(Query<T> query, Transaction transaction);
/**
* Return the Id values of the query as a List.
*
* @see Query#findIds()
*/
@Nonnull
<A, T> List<A> findIds(Query<T> query, Transaction transaction);
/**
* Return a QueryIterator for the query.
* Return the extended API for EbeanServer.
* <p>
* Generally using {@link #findEach(Query, Consumer, Transaction)} or
* {@link #findEachWhile(Query, Predicate, Transaction)} is preferred
* to findIterate(). The reason is that those methods automatically take care of
* closing the queryIterator (and the underlying jdbc statement and resultSet).
* The extended API has the options for executing queries that take an explicit
* transaction as an argument.
* </p>
* <p>
* This is similar to findEach in that not all the result beans need to be held
* in memory at the same time and as such is good for processing large queries.
* </p>
*
* @see Query#findIterate()
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
@Nonnull
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
/**
* Execute the query visiting the each bean one at a time.
* <p>
* Unlike findList() this is suitable for processing a query that will return
* a very large resultSet. The reason is that not all the result beans need to be
* held in memory at the same time and instead processed one at a time.
* </p>
* <p>
* Internally this query using a PersistenceContext scoped to each bean (and the
* beans associated object graph).
* </p>
* <p>
* <pre>{@code
*
* ebeanServer.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findEach((Order order) -> {
*
* // do something with the order bean
* System.out.println(" -- processing order ... " + order);
* });
*
* }</pre>
*
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
<T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction);
/**
* Execute the query visiting the each bean one at a time.
* <p>
* Compared to findEach() this provides the ability to stop processing the query
* results early by returning false for the Predicate.
* </p>
* <p>
* Unlike findList() this is suitable for processing a query that will return
* a very large resultSet. The reason is that not all the result beans need to be
* held in memory at the same time and instead processed one at a time.
* </p>
* <p>
* Internally this query using a PersistenceContext scoped to each bean (and the
* beans associated object graph).
* </p>
* <p>
* <pre>{@code
*
* ebeanServer.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findEachWhile((Order order) -> {
*
* // do something with the order bean
* System.out.println(" -- processing order ... " + order);
*
* boolean carryOnProcessing = ...
* return carryOnProcessing;
* });
*
* }</pre>
*
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
<T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction);
/**
* Return versions of a @History entity bean.
* <p>
* Generally this query is expected to be a find by id or unique predicates query.
* It will execute the query against the history returning the versions of the bean.
* Typically we only need to use the extended API when we do NOT want to use the
* usual ThreadLocal based mechanism to obtain the current transaction but instead
* supply the transaction explicitly.
* </p>
*/
@Nonnull
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
/**
* Execute a query returning a list of beans.
* <p>
* Generally you are able to use {@link Query#findList()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
* <p>
* <pre>{@code
*
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* .where().ilike("name", "rob%")
* .findList();
*
* }</pre>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the list of fetched beans.
* @see Query#findList()
*/
@Nonnull
<T> List<T> findList(Query<T> query, Transaction transaction);
/**
* Execute find row count 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>
*
* @param query the query to execute the row count on
* @param transaction the transaction (can be null).
* @return a Future object for the row count query
* @see Query#findFutureCount()
*/
@Nonnull
<T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
/**
* Execute find Id's 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>
*
* @param query the query to execute the fetch Id's on
* @param transaction the transaction (can be null).
* @return a Future object for the list of Id's
* @see Query#findFutureIds()
*/
@Nonnull
<T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
/**
* Execute find list query in a background thread returning a FutureList object.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a timeout).
* <p>
* This query will execute in it's own PersistenceContext and using its own transaction.
* What that means is that it will not share any bean instances with other queries.
*
* @param query the query to execute in the background
* @param transaction the transaction (can be null).
* @return a Future object for the list result of the query
* @see Query#findFutureList()
*/
@Nonnull
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
/**
* Return a PagedList for this query using firstRow and maxRows.
* <p>
* The benefit of using this over findList() is that it provides functionality to get the
* total row count etc.
* </p>
* <p>
* If maxRows is not set on the query prior to calling findPagedList() then a
* PersistenceException is thrown.
* </p>
* <p>
* <pre>{@code
*
* PagedList<Order> pagedList = Ebean.find(Order.class)
* .setFirstRow(50)
* .setMaxRows(20)
* .findPagedList();
*
* // fetch the total row count in the background
* pagedList.loadRowCount();
*
* List<Order> orders = pagedList.getList();
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*
* @return The PagedList
* @see Query#findPagedList()
*/
@Nonnull
<T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
/**
* Execute the query returning a set of entity beans.
* <p>
* Generally you are able to use {@link Query#findSet()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
* <p>
* <pre>{@code
*
* Set<Customer> customers =
* ebeanServer.find(Customer.class)
* .where().ilike("name", "rob%")
* .findSet();
*
* }</pre>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute
* @param transaction the transaction to use (can be null).
* @return the set of fetched beans.
* @see Query#findSet()
*/
@Nonnull
<T> Set<T> findSet(Query<T> query, Transaction transaction);
/**
* Execute the query returning the entity beans in a Map.
* <p>
* Generally you are able to use {@link Query#findMap()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the map of fetched beans.
* @see Query#findMap()
*/
@Nonnull
<K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
/**
* Execute the query returning a list of values for a single property.
* <p>
* <h3>Example 1:</h3>
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* .select("name")
* .orderBy().asc("name")
* .findSingleAttributeList();
*
* }</pre>
* <h3>Example 2:</h3>
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* .setDistinct(true)
* .select("name")
* .where().eq("status", Customer.Status.NEW)
* .orderBy().asc("name")
* .setMaxRows(100)
* .findSingleAttributeList();
*
* }</pre>
*
* @return the list of values for the selected property
* @see Query#findSingleAttributeList()
*/
@Nonnull
<A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
/**
* Execute the query returning at most one entity bean or null (if no matching
* bean is found).
* <p>
* This will throw a NonUniqueResultException if the query finds more than one result.
* </p>
* <p>
* Generally you are able to use {@link Query#findOne()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the list of fetched beans.
* @throws NonUniqueResultException if more than one result was found
* @see Query#findOne()
*/
@Nullable
<T> T findOne(Query<T> query, Transaction transaction);
/**
* Similar to findOne() but returns an Optional (rather than nullable).
*/
@Nonnull
<T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
/**
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
* <p>
* Note that if the query includes joins then the generated delete statement may not be
* optimal depending on the database platform.
* </p>
*
* @param query the query used for the delete
* @param transaction the transaction to use (can be null)
* @param <T> the type of entity bean to fetch.
* @return the number of beans/rows that were deleted
*/
<T> int delete(Query<T> query, Transaction transaction);
/**
* Execute the update query returning the number of rows updated.
* <p>
* The update query must be created using {@link #update(Class)}.
* </p>
*
* @param query the update query to execute
* @param transaction the optional transaction to use for the update (can be null)
* @param <T> the type of entity bean
* @return The number of rows updated
*/
<T> int update(Query<T> query, Transaction transaction);
/**
* Execute the sql query returning a list of MapBean.
* <p>
* Generally you are able to use {@link SqlQuery#findList()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the list of fetched MapBean.
* @see SqlQuery#findList()
*/
@Nonnull
List<SqlRow> findList(SqlQuery query, Transaction transaction);
/**
* Execute the SqlQuery iterating a row at a time.
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction);
/**
* Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
* <p>
* Returning false after processing a row stops the iteration through the query results.
* </p>
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction);
/**
* Execute the sql query returning a single MapBean or null.
* <p>
* This will throw a PersistenceException if the query found more than one
* result.
* </p>
* <p>
* Generally you are able to use {@link SqlQuery#findOne()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the fetched MapBean or null if none was found.
* @see SqlQuery#findOne()
*/
@Nullable
SqlRow findOne(SqlQuery query, Transaction transaction);
ExtendedServer extended();
/**
* Either Insert or Update the bean depending on its state.
@@ -1461,6 +1094,61 @@ public interface EbeanServer {
*/
int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* This method checks the uniqueness of a bean. I.e. if the save will work. It will return the
* properties that violates an unique / primary key. This may be done in an UI save action to
* validate if the user has entered correct values.
* <p>
* Note: This method queries the DB for uniqueness of all indices, so do not use it in a batch update.
* <p>
* Note: This checks only the root bean!
* <p>
* <pre>{@code
*
* // there is a unique constraint on title
*
* Document doc = new Document();
* doc.setTitle("One flew over the cuckoo's nest");
* doc.setBody("clashes with doc1");
*
* Set<Property> properties = server().checkUniqueness(doc);
*
* if (properties.isEmpty()) {
* // it is unique ... carry on
*
* } else {
* // build a user friendly message
* // to return message back to user
*
* String uniqueProperties = properties.toString();
*
* StringBuilder msg = new StringBuilder();
*
* properties.forEach((it)-> {
* Object propertyValue = it.getVal(doc);
* String propertyName = it.getName();
* msg.append(" property["+propertyName+"] value["+propertyValue+"]");
* });
*
* // uniqueProperties > [title]
* // custom msg > property[title] value[One flew over the cuckoo's nest]
*
* }
*
* }</pre>
*
* @param bean The entity bean to check uniqueness on
* @return a set of Properties if constraint validation was detected or empty list.
*/
@Nonnull
Set<Property> checkUniqueness(Object bean);
/**
* Same as {@link #checkUniqueness(Object)}. but with given transaction.
*/
@Nonnull
Set<Property> checkUniqueness(Object bean, Transaction transaction);
/**
* Marks the entity bean as dirty.
* <p>
@@ -1547,6 +1235,29 @@ public interface EbeanServer {
*/
void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Merge the bean using the default merge options (no paths specified, default delete).
*
* @param bean The bean to merge
*/
void merge(Object bean);
/**
* Merge the bean using the given merge options.
*
* @param bean The bean to merge
* @param options The options to control the merge
*/
void merge(Object bean, MergeOptions options);
/**
* Merge the bean using the given merge options and a transaction.
*
* @param bean The bean to merge
* @param options The options to control the merge
*/
void merge(Object bean, MergeOptions options, Transaction transaction);
/**
* Insert the bean.
* <p>
+13 -12
View File
@@ -1,11 +1,11 @@
package io.ebean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to determine the ebean version. (
* @author Roland Praml, FOCONIS AG
@@ -13,31 +13,32 @@ import org.slf4j.LoggerFactory;
*/
public class EbeanVersion {
private EbeanVersion() {
}
private static final Logger logger = LoggerFactory.getLogger(EbeanVersion.class);
private static String version = "unknown";
static {
try {
Properties prop = new Properties();
InputStream in = Ebean.class.getResourceAsStream("/META-INF/maven/io.ebean/ebean/pom.properties");
if (in != null) {
prop.load(in);
in.close();
version = prop.getProperty("version");
try (InputStream in = Ebean.class.getResourceAsStream("/META-INF/maven/io.ebean/ebean/pom.properties")) {
if (in != null) {
prop.load(in);
in.close();
version = prop.getProperty("version");
}
}
logger.info("ebean version: {}", version);
} catch (IOException e) {
logger.warn("Could not determine ebean version: {}", e.getMessage());
}
}
/**
* Returns the ebean version (read from /META-INF/maven/io.ebean/ebean/pom.properties)
*/
public static String getVersion() {
return version;
}
}
@@ -327,6 +327,30 @@ public interface ExpressionFactory {
*/
Expression allEq(Map<String, Object> propertyMap);
/**
* Add expression for ANY of the given bit flags to be set.
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
Expression bitwiseAny(String propertyName, long flags);
/**
* Add expression for ALL of the given bit flags to be set.
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
Expression bitwiseAll(String propertyName, long flags);
/**
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
Expression bitwiseAnd(String propertyName, long flags, long match);
/**
* Add raw expression with a single parameter.
* <p>
+112 -3
View File
@@ -120,6 +120,14 @@ public interface ExpressionList<T> {
*/
Query<T> asDraft();
/**
* Convert the query to a DTO bean query.
* <p>
* We effectively use the underlying ORM query to build the SQL and then execute
* and map it into DTO beans.
*/
<D> DtoQuery<D> asDto(Class<D> dtoClass);
/**
* Execute using "for update" clause which results in the DB locking the record.
*/
@@ -404,6 +412,11 @@ public interface ExpressionList<T> {
*/
Query<T> select(String properties);
/**
* Apply the fetchGroup which defines what part of the object graph to load.
*/
Query<T> select(FetchGroup<T> fetchGroup);
/**
* Set whether this query uses DISTINCT.
* <p>
@@ -480,6 +493,36 @@ public interface ExpressionList<T> {
*/
Query<T> setUseQueryCache(CacheMode useCache);
/**
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
* <p>
* <pre>{@code
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* Ebean.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
*
* // fetch as single attribute with a COUNT
* .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
* .findSingleAttributeList();
*
* for (CountedValue<Order.Status> entry : orderStatusCount) {
* System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
* }
*
* // produces
*
* count:3 orderStatus:NEW
* count:1 orderStatus:SHIPPED
* count:1 orderStatus:COMPLETE
*
* }</pre>
*/
Query<T> setCountDistinct(CountDistinctOrder orderBy);
/**
* Calls {@link #setUseQueryCache(CacheMode)} with <code>ON</code> or <code>OFF</code>.
*
@@ -515,6 +558,11 @@ public interface ExpressionList<T> {
*/
Query<T> setDisableReadAuditing();
/**
* Set a label on the query (to help identify query execution statistics).
*/
Query<T> setLabel(String label);
/**
* Add expressions to the having clause.
* <p>
@@ -843,7 +891,7 @@ public interface ExpressionList<T> {
/**
* In - using a subQuery.
*
* <p>
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
@@ -853,7 +901,7 @@ public interface ExpressionList<T> {
/**
* In - property has a value in the array of values.
*
* <p>
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
@@ -863,7 +911,7 @@ public interface ExpressionList<T> {
/**
* In - property has a value in the collection of values.
*
* <p>
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
@@ -961,6 +1009,67 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> arrayIsNotEmpty(String propertyName);
/**
* Add expression for ANY of the given bit flags to be set.
* <pre>{@code
*
* where().bitwiseAny("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
ExpressionList<T> bitwiseAny(String propertyName, long flags);
/**
* Add expression for ALL of the given bit flags to be set.
* <p>
* <pre>{@code
*
* where().bitwiseAll("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
ExpressionList<T> bitwiseAll(String propertyName, long flags);
/**
* Add expression for the given bit flags to be NOT set.
* <p>
* <pre>{@code
*
* where().bitwiseNot("flags", BwFlags.HAS_COLOUR)
*
* }</pre>
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
ExpressionList<T> bitwiseNot(String propertyName, long flags);
/**
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
* <p>
* <pre>{@code
*
* // Flags Bulk + Size = Size
* // ... meaning Bulk is not set and Size is set
*
* long selectedFlags = BwFlags.HAS_BULK + BwFlags.HAS_SIZE;
* long mask = BwFlags.HAS_SIZE; // Only Size flag set
*
* where().bitwiseAnd("flags", selectedFlags, mask)
*
* }</pre>
*
* @param propertyName The property that holds the flags value
* @param flags The flags we are looking for
*/
ExpressionList<T> bitwiseAnd(String propertyName, long flags, long match);
/**
* Add raw expression with a single parameter.
* <p>
+452
View File
@@ -0,0 +1,452 @@
package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import java.time.Clock;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* The extended API for EbeanServer.
* <p>
* This provides the finder methods that take an explicit transaction rather than obtaining
* the transaction from the usual mechanism (which is ThreadLocal based).
* </p>
* <p>
* In general we only want to use this ExtendedServer API when we want to avoid / bypass
* the use of the mechanism to get the current transaction and instead explicitly supply
* the transaction to use.
* </p>
* <p>
* Note that in all cases the transaction supplied can be null and in this case the EbeanServer
* will use the normal mechanism to obtain the transaction to use.
* </p>
*/
public interface ExtendedServer {
/**
* Return the NOW time from the Clock.
*/
long clockNow();
/**
* Set the Clock to use for <code>@WhenCreated</code> and <code>@WhenModified</code>.
* <p>
* Note that we only expect to change the Clock for testing purposes.
* </p>
*/
void setClock(Clock clock);
/**
* Return the number of 'top level' or 'root' entities this query should return.
*
* @see Query#findCount()
* @see Query#findFutureCount()
*/
<T> int findCount(Query<T> query, Transaction transaction);
/**
* Return the Id values of the query as a List.
*
* @see Query#findIds()
*/
@Nonnull
<A, T> List<A> findIds(Query<T> query, Transaction transaction);
/**
* Return a QueryIterator for the query.
* <p>
* Generally using {@link #findEach(Query, Consumer, Transaction)} or
* {@link #findEachWhile(Query, Predicate, Transaction)} is preferred
* to findIterate(). The reason is that those methods automatically take care of
* closing the queryIterator (and the underlying jdbc statement and resultSet).
* </p>
* <p>
* This is similar to findEach in that not all the result beans need to be held
* in memory at the same time and as such is good for processing large queries.
* </p>
*
* @see Query#findIterate()
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
@Nonnull
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
/**
* Execute the query visiting the each bean one at a time.
* <p>
* Unlike findList() this is suitable for processing a query that will return
* a very large resultSet. The reason is that not all the result beans need to be
* held in memory at the same time and instead processed one at a time.
* </p>
* <p>
* Internally this query using a PersistenceContext scoped to each bean (and the
* beans associated object graph).
* </p>
* <p>
* <pre>{@code
*
* ebeanServer.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findEach((Order order) -> {
*
* // do something with the order bean
* System.out.println(" -- processing order ... " + order);
* });
*
* }</pre>
*
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
<T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction);
/**
* Execute the query visiting the each bean one at a time.
* <p>
* Compared to findEach() this provides the ability to stop processing the query
* results early by returning false for the Predicate.
* </p>
* <p>
* Unlike findList() this is suitable for processing a query that will return
* a very large resultSet. The reason is that not all the result beans need to be
* held in memory at the same time and instead processed one at a time.
* </p>
* <p>
* Internally this query using a PersistenceContext scoped to each bean (and the
* beans associated object graph).
* </p>
* <p>
* <pre>{@code
*
* ebeanServer.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findEachWhile((Order order) -> {
*
* // do something with the order bean
* System.out.println(" -- processing order ... " + order);
*
* boolean carryOnProcessing = ...
* return carryOnProcessing;
* });
*
* }</pre>
*
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
<T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction);
/**
* Return versions of a @History entity bean.
* <p>
* Generally this query is expected to be a find by id or unique predicates query.
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
/**
* Execute a query returning a list of beans.
* <p>
* Generally you are able to use {@link Query#findList()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
* <p>
* <pre>{@code
*
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* .where().ilike("name", "rob%")
* .findList();
*
* }</pre>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the list of fetched beans.
* @see Query#findList()
*/
@Nonnull
<T> List<T> findList(Query<T> query, Transaction transaction);
/**
* Execute find row count 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>
*
* @param query the query to execute the row count on
* @param transaction the transaction (can be null).
* @return a Future object for the row count query
* @see Query#findFutureCount()
*/
@Nonnull
<T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
/**
* Execute find Id's 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>
*
* @param query the query to execute the fetch Id's on
* @param transaction the transaction (can be null).
* @return a Future object for the list of Id's
* @see Query#findFutureIds()
*/
@Nonnull
<T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
/**
* Execute find list query in a background thread returning a FutureList object.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a timeout).
* <p>
* This query will execute in it's own PersistenceContext and using its own transaction.
* What that means is that it will not share any bean instances with other queries.
*
* @param query the query to execute in the background
* @param transaction the transaction (can be null).
* @return a Future object for the list result of the query
* @see Query#findFutureList()
*/
@Nonnull
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
/**
* Return a PagedList for this query using firstRow and maxRows.
* <p>
* The benefit of using this over findList() is that it provides functionality to get the
* total row count etc.
* </p>
* <p>
* If maxRows is not set on the query prior to calling findPagedList() then a
* PersistenceException is thrown.
* </p>
* <p>
* <pre>{@code
*
* PagedList<Order> pagedList = Ebean.find(Order.class)
* .setFirstRow(50)
* .setMaxRows(20)
* .findPagedList();
*
* // fetch the total row count in the background
* pagedList.loadRowCount();
*
* List<Order> orders = pagedList.getList();
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*
* @return The PagedList
* @see Query#findPagedList()
*/
@Nonnull
<T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
/**
* Execute the query returning a set of entity beans.
* <p>
* Generally you are able to use {@link Query#findSet()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
* <p>
* <pre>{@code
*
* Set<Customer> customers =
* ebeanServer.find(Customer.class)
* .where().ilike("name", "rob%")
* .findSet();
*
* }</pre>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute
* @param transaction the transaction to use (can be null).
* @return the set of fetched beans.
* @see Query#findSet()
*/
@Nonnull
<T> Set<T> findSet(Query<T> query, Transaction transaction);
/**
* Execute the query returning the entity beans in a Map.
* <p>
* Generally you are able to use {@link Query#findMap()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the map of fetched beans.
* @see Query#findMap()
*/
@Nonnull
<K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
/**
* Execute the query returning a list of values for a single property.
* <p>
* <h3>Example 1:</h3>
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* .select("name")
* .orderBy().asc("name")
* .findSingleAttributeList();
*
* }</pre>
* <h3>Example 2:</h3>
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* .setDistinct(true)
* .select("name")
* .where().eq("status", Customer.Status.NEW)
* .orderBy().asc("name")
* .setMaxRows(100)
* .findSingleAttributeList();
*
* }</pre>
*
* @return the list of values for the selected property
* @see Query#findSingleAttributeList()
*/
@Nonnull
<A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
/**
* Execute the query returning at most one entity bean or null (if no matching
* bean is found).
* <p>
* This will throw a NonUniqueResultException if the query finds more than one result.
* </p>
* <p>
* Generally you are able to use {@link Query#findOne()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param <T> the type of entity bean to fetch.
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the list of fetched beans.
* @throws NonUniqueResultException if more than one result was found
* @see Query#findOne()
*/
@Nullable
<T> T findOne(Query<T> query, Transaction transaction);
/**
* Similar to findOne() but returns an Optional (rather than nullable).
*/
@Nonnull
<T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
/**
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
* <p>
* Note that if the query includes joins then the generated delete statement may not be
* optimal depending on the database platform.
* </p>
*
* @param query the query used for the delete
* @param transaction the transaction to use (can be null)
* @param <T> the type of entity bean to fetch.
* @return the number of beans/rows that were deleted
*/
<T> int delete(Query<T> query, Transaction transaction);
/**
* Execute the update query returning the number of rows updated.
* <p>
* The update query must be created using {@link EbeanServer#update(Class)}.
* </p>
*
* @param query the update query to execute
* @param transaction the optional transaction to use for the update (can be null)
* @param <T> the type of entity bean
* @return The number of rows updated
*/
<T> int update(Query<T> query, Transaction transaction);
/**
* Execute the sql query returning a list of MapBean.
* <p>
* Generally you are able to use {@link SqlQuery#findList()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the list of fetched MapBean.
* @see SqlQuery#findList()
*/
@Nonnull
List<SqlRow> findList(SqlQuery query, Transaction transaction);
/**
* Execute the SqlQuery iterating a row at a time.
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction);
/**
* Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
* <p>
* Returning false after processing a row stops the iteration through the query results.
* </p>
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction);
/**
* Execute the sql query returning a single MapBean or null.
* <p>
* This will throw a PersistenceException if the query found more than one
* result.
* </p>
* <p>
* Generally you are able to use {@link SqlQuery#findOne()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
*
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the fetched MapBean or null if none was found.
* @see SqlQuery#findOne()
*/
@Nullable
SqlRow findOne(SqlQuery query, Transaction transaction);
}
+114
View File
@@ -0,0 +1,114 @@
package io.ebean;
import javax.annotation.Nonnull;
/**
* Defines what part of the object graph to load (select and fetch clauses).
* <p>
* Using a FetchGroup effectively sets the select() and fetch() clauses for a query. It is alternative
* to specifying the select() and fetch() clauses on the query allowing for more re-use of "what to load"
* that can be defined separately from the query and combined with other FetchGroups.
* </p>
*
* <h3>Select example</h3>*
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* <h3>Select and fetch example</h3>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* <h3>Combining FetchGroups</h3>
* <p>
* FetchGroups can be combined together to form another FetchGroup.
* </p>
* <pre>{@code
*
* FetchGroup<Address> FG_ADDRESS = FetchGroup.of(Address.class)
* .select("line1, line2, city")
* .fetch("country", "name")
* .build();
*
* FetchGroup<Customer> FG_CUSTOMER = FetchGroup.of(Customer.class)
* .select("name, version")
* .fetch("billingAddress", FG_ADDRESS)
* .build();
*
*
* Customer.query()
* .select(FG_CUSTOMER)
* .findList();
*
* }</pre>
*
* @param <T> The bean type the Fetch group can be applied to
*/
public interface FetchGroup<T> {
/**
* Return the FetchGroup with the given select clause.
* <p>
* We use this for simple FetchGroup that only select() properties and do not have additional fetch() clause.
* </p>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* @param select The select clause of the FetchGroup
*
* @return The FetchGroup with the given select clause
*/
@Nonnull
static <T> FetchGroup<T> of(Class<T> cls, String select) {
return XServiceProvider.fetchGroupOf(cls, select);
}
/**
* Return the FetchGroupBuilder with the given select clause that we can add fetch clauses to.
* <p>
* We chain select() with one or more fetch() clauses to define the object graph to load.
* </p>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* @return The FetchGroupBuilder with the given select clause which we will add fetch clauses to
*/
@Nonnull
static <T> FetchGroupBuilder<T> of(Class<T> cls) {
return XServiceProvider.fetchGroupOf(cls);
}
}
@@ -0,0 +1,93 @@
package io.ebean;
import javax.annotation.Nonnull;
/**
* Builds a FetchGroup by adding fetch clauses.
* <p>
* We add select() and fetch() clauses to define the object graph we want to load.
* </p>
*
* <pre>{@code
*
* FetchGroup fetchGroup = FetchGroup
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .where()
* ...
* .findList();
*
* }</pre>
*/
public interface FetchGroupBuilder<T> {
/**
* Specify specific properties to select (top level properties).
*/
@Nonnull
FetchGroupBuilder<T> select(String select);
/**
* Fetch all the properties at the given path.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path);
/**
* Fetch the path with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path using a query join with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path lazily with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path including specified properties.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path, String properties);
/**
* Fetch the path including all its properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path);
/**
* Fetch the path including specified properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, String properties);
/**
* Fetch the path including all its properties lazily.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path);
/**
* Fetch the path including specified properties lazily.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path, String properties);
/**
* Build and return the FetchGroup.
*/
@Nonnull
FetchGroup<T> build();
}
+29
View File
@@ -0,0 +1,29 @@
package io.ebean;
import java.util.Set;
/**
* Options used to control a merge. Use MergeOptionsBuilder to create an instance.
* <p>
* Instances of MergeOptions are thread safe and safe to share across threads.
*/
public interface MergeOptions {
/**
* Returns true if Id values are supplied by the client.
* <p>
* This would be the case when for example a mobile creates data in it's own local database
* and then sync's. In this case often the id values are UUID.
*/
boolean isClientGeneratedIds();
/**
* Return true if delete permanent should be used and false for 'normal' delete that allows soft deletes.
*/
boolean isDeletePermanent();
/**
* Return the paths included in the merge.
*/
Set<String> paths();
}
@@ -0,0 +1,95 @@
package io.ebean;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Builds a MergeOptions which is immutable and thread safe.
*/
public class MergeOptionsBuilder {
private static final MOptions DEFAULT_OPTIONS = new MOptions();
private Set<String> paths = new LinkedHashSet<>();
private boolean clientGeneratedIds;
private boolean deletePermanent;
/**
* Return the default options.
*/
public static MergeOptions defaultOptions() {
return DEFAULT_OPTIONS;
}
/**
* Add a path that will included in the merge.
*
* @param path The path relative to the root type.
* @return The builder to chain another addPath() or build().
*/
public MergeOptionsBuilder addPath(String path) {
paths.add(path);
return this;
}
/**
* Set to true if Id values are supplied by the client.
* <p>
* This would be the case when for example a mobile creates data in it's own local database
* and then sync's. In this case often the id values are UUID.
*/
public MergeOptionsBuilder setClientGeneratedIds() {
this.clientGeneratedIds = true;
return this;
}
/**
* Set that deletions should use delete permanent (rather than default which allows soft deletes).
*/
public MergeOptionsBuilder setDeletePermanent() {
this.deletePermanent = true;
return this;
}
/**
* Build and return the MergeOptions instance.
*/
public MergeOptions build() {
return new MOptions(paths, clientGeneratedIds, deletePermanent);
}
private static class MOptions implements MergeOptions {
private final boolean clientGeneratedIds;
private final boolean deletePermanent;
private final Set<String> paths;
private MOptions(){
this.clientGeneratedIds = false;
this.paths = new LinkedHashSet<>();
this.deletePermanent = false;
}
private MOptions(Set<String> paths, boolean clientGeneratedIds, boolean deletePermanent) {
this.paths = paths;
this.clientGeneratedIds = clientGeneratedIds;
this.deletePermanent = deletePermanent;
}
public Set<String> paths() {
return paths;
}
@Override
public boolean isClientGeneratedIds() {
return clientGeneratedIds;
}
@Override
public boolean isDeletePermanent() {
return deletePermanent;
}
}
}
@@ -76,7 +76,6 @@ class PrimaryServer {
if (defaultServerName == null) {
defaultServerName = "db";
}
System.setProperty("ebean_db", defaultServerName);
return defaultServerName;
}
+58 -8
View File
@@ -236,6 +236,14 @@ public interface Query<T> {
*/
Query<T> asDraft();
/**
* Convert the query to a DTO bean query.
* <p>
* We effectively use the underlying ORM query to build the SQL and then execute
* and map it into DTO beans.
*/
<D> DtoQuery<D> asDto(Class<D> dtoClass);
/**
* Cancel the query execution if supported by the underlying database and
* driver.
@@ -389,6 +397,11 @@ public interface Query<T> {
*/
Query<T> select(String fetchProperties);
/**
* Apply the fetchGroup which defines what part of the object graph to load.
*/
Query<T> select(FetchGroup<T> fetchGroup);
/**
* Specify a path to fetch eagerly including specific properties.
* <p>
@@ -594,8 +607,6 @@ public interface Query<T> {
* <p>
* This query will execute against the EbeanServer that was used to create it.
* </p>
*
* @see EbeanServer#findIds(Query, Transaction)
*/
@Nonnull
<A> List<A> findIds();
@@ -732,8 +743,6 @@ public interface Query<T> {
* .findList();
*
* }</pre>
*
* @see EbeanServer#findList(Query, Transaction)
*/
@Nonnull
List<T> findList();
@@ -751,8 +760,6 @@ public interface Query<T> {
* .findSet();
*
* }</pre>
*
* @see EbeanServer#findSet(Query, Transaction)
*/
@Nonnull
Set<T> findSet();
@@ -774,8 +781,6 @@ public interface Query<T> {
* .findMap();
*
* }</pre>
*
* @see EbeanServer#findMap(Query, Transaction)
*/
@Nonnull
<K> Map<K, T> findMap();
@@ -828,6 +833,11 @@ public interface Query<T> {
*/
<A> A findSingleAttribute();
/**
* Return true if this is countDistinct query.
*/
boolean isCountDistinct();
/**
* Execute the query returning either a single bean or null (if no matching
* bean is found).
@@ -1272,6 +1282,37 @@ public interface Query<T> {
*/
Query<T> setDistinct(boolean isDistinct);
/**
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
*
* <pre>{@code
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* Ebean.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
*
* // fetch as single attribute with a COUNT
* .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
* .findSingleAttributeList();
*
* for (CountedValue<Order.Status> entry : orderStatusCount) {
* System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
* }
*
* // produces
*
* count:3 orderStatus:NEW
* count:1 orderStatus:SHIPPED
* count:1 orderStatus:COMPLETE
*
* }</pre>
*
*/
Query<T> setCountDistinct(CountDistinctOrder orderBy);
/**
* Return the first row value.
*/
@@ -1375,6 +1416,15 @@ public interface Query<T> {
*/
Query<T> setProfileLocation(ProfileLocation profileLocation);
/**
* Set a label on the query.
* <p>
* This label can be used to help identify query performance metrics but we can also use
* profile location enhancement on Finders so for some that would be a better option.
* </p>
*/
Query<T> setLabel(String label);
/**
* Set to true if this query should execute against the doc store.
* <p>
@@ -1,6 +1,7 @@
package io.ebean;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Builds RawSql instances from a SQL string and column mappings.
@@ -24,6 +25,13 @@ public interface RawSqlBuilder {
return XServiceProvider.rawSql().resultSet(resultSet, propertyNames);
}
/**
* Create and return a SqlRow based on the resultSet with dbTrueValue and binaryOptimizedUUID options.
*/
static SqlRow sqlRow(ResultSet resultSet, final String dbTrueValue, boolean binaryOptimizedUUID) throws SQLException {
return XServiceProvider.rawSql().sqlRow(resultSet, dbTrueValue, binaryOptimizedUUID);
}
/**
* 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
+40
View File
@@ -0,0 +1,40 @@
package io.ebean;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Used with SqlQuery to process potentially large queries reading directly from the JDBC ResultSet.
* <p>
* This provides a low level option that reads directly from the JDBC ResultSet.
* </p>
*
* <pre>{@code
*
* String sql = "select id, name, status from o_customer order by name desc";
*
* Ebean.createSqlQuery(sql)
* .findEachRow((resultSet, rowNum) -> {
*
* // read directly from ResultSet
*
* long id = resultSet.getLong(1);
* String name = resultSet.getString(2);
*
* // do something interesting with the data
*
* });
*
* }</pre>
*/
@FunctionalInterface
public interface RowConsumer {
/**
* Read the data from the ResultSet and process it.
*
* @param resultSet The JDBC ResultSet positioned to the current row
* @param rowNum The number of the current row being mapped.
*/
void accept(ResultSet resultSet, int rowNum) throws SQLException;
}
+62
View File
@@ -0,0 +1,62 @@
package io.ebean;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Used with SqlQuery to map raw JDBC ResultSet to objects.
* <p>
* This provides a low level mapping option with direct use of JDBC ResultSet
* with the option of having logic in the mapping. For example, only map some
* columns depending on the values read from other columns.
* </p>
* <p>
* For straight mapping into beans then DtoQuery would be the first choice as
* it can automatically map the ResultSet into beans.
* </p>
*
* <pre>{@code
*
* //
* // A mapper from ResultSet into our CustomerDto bean
* //
* class CustomerMapper implements RowMapper<CustomerDto> {
*
* @Override
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
*
* long id = rset.getLong(1);
* String name = rset.getString(2);
* String status = rset.getString(3);
*
* return new CustomerDto(id, name, status);
* }
* }
*
*
* //
* // Then use the mapper
* //
*
* String sql = "select id, name, status from o_customer where name = ?";
*
* CustomerDto rob = Ebean.createSqlQuery(sql)
* .setParameter(1, "Rob")
* .findOne(CUSTOMER_MAPPER);
*
*
* }</pre>
*
* @param <T> The type the row data is mapped into.
*/
@FunctionalInterface
public interface RowMapper<T> {
/**
* Read the data from the ResultSet and map to the return type.
*
* @param resultSet The JDBC ResultSet positioned to the current row
* @param rowNum The number of the current row being mapped.
*/
T map(ResultSet resultSet, int rowNum) throws SQLException;
}
+112
View File
@@ -3,6 +3,7 @@ package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
@@ -77,12 +78,118 @@ public interface SqlQuery extends Serializable {
@Nullable
SqlRow findOne();
/**
* Execute the query returning a single result using the mapper.
*
* @param mapper Used to map each ResultSet row into the result object.
*/
<T> T findOne(RowMapper<T> mapper);
/**
* Execute the query returning a list using the mapper.
*
* @param mapper Used to map each ResultSet row into the result object.
*/
<T> List<T> findList(RowMapper<T> mapper);
/**
* Execute the query reading each row from ResultSet using the RowConsumer.
* <p>
* This provides a low level option that reads directly from the JDBC ResultSet
* and is good for processing very large results where (unlike findList) we don't
* hold all the results in memory but instead can process row by row.
* </p>
*
* <pre>{@code
*
* String sql = "select id, name, status from customer order by name desc";
*
* Ebean.createSqlQuery(sql)
* .findEachRow((resultSet, rowNum) -> {
*
* // read directly from ResultSet
*
* long id = resultSet.getLong(1);
* String name = resultSet.getString(2);
*
* // do something interesting with the data
*
* });
*
* }</pre>
*
* @param consumer Used to read and process each ResultSet row.
*/
void findEachRow(RowConsumer consumer);
/**
* Execute the query returning an optional row.
*/
@Nonnull
Optional<SqlRow> findOneOrEmpty();
/**
* Execute the query returning a single scalar attribute.
* <pre>@{code
*
* String sql = "select max(unit_price) from o_order_detail where order_qty > ?";
*
* BigDecimal maxPrice = Ebean.createSqlQuery(sql)
* .setParameter(1, 2)
* .findSingleAttribute(BigDecimal.class);
*
* }</pre>
*
* <p>
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
* </p>
*
* @param attributeType The type of the returned value
*/
<T> T findSingleAttribute(Class<T> attributeType);
/**
* Execute the query returning a single BigDecimal value.
* <p>
* This is an alias for <code>findSingleAttribute(BigDecimal.class)</code>
* </p>
*/
BigDecimal findSingleDecimal();
/**
* Execute the query returning a single Long value.
* <p>
* This is an alias for <code>findSingleAttribute(Long.class)</code>
* </p>
*/
Long findSingleLong();
/**
* Execute the query returning a list of scalar attribute values.
*
* <pre>{@code
*
* String sql =
* " select (unit_price * order_qty) " +
* " from o_order_detail " +
* " where unit_price > ? " +
* " order by (unit_price * order_qty) desc";
*
* //
* List<BigDecimal> lineAmounts = Ebean.createSqlQuery(sql)
* .setParameter(1, 3)
* .findSingleAttributeList(BigDecimal.class);
*
* }</pre>
*
* <p>
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
* </p>
*
* @param attributeType The type of the returned value
*/
<T> List<T> findSingleAttributeList(Class<T> attributeType);
/**
* The same as bind for named parameters.
*/
@@ -115,6 +222,11 @@ public interface SqlQuery extends Serializable {
*/
SqlQuery setTimeout(int secs);
/**
* Set a label that can be put on performance metrics that are collected.
*/
SqlQuery setLabel(String label);
/**
* A hint which for JDBC translates to the Statement.fetchSize().
* <p>
+85 -8
View File
@@ -19,15 +19,73 @@ package io.ebean;
* <p>
* <pre>{@code
*
* // example that uses 'named' parameters
* String s = "UPDATE f_topic set post_count = :count where id = :id";
* SqlUpdate update = Ebean.createSqlUpdate(s);
* update.setParameter("id", 1);
* update.setParameter("count", 50);
* // example that uses 'named' parameters
*
* int modifiedCount = Ebean.execute(update);
* String s = "UPDATE f_topic set post_count = :count where id = :id";
*
* String msg = "There were " + modifiedCount + " rows updated";
* SqlUpdate update = Ebean.createSqlUpdate(s);
* update.setParameter("id", 1);
* update.setParameter("count", 50);
*
* int modifiedCount = update.execute();
*
* String msg = "There were " + modifiedCount + " rows updated";
*
* }</pre>
* <p>
* <h3>Example: Using setNextParameter()</h3>
* <pre>{@code
*
* String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
* SqlUpdate insert = Ebean.createSqlUpdate(sql);
*
* try (Transaction txn = Ebean.beginTransaction()) {
* txn.setBatchMode(true);
*
* insert.setNextParameter(10000);
* insert.setNextParameter("hello");
* insert.setNextParameter("rob");
* insert.execute();
*
* insert.setNextParameter(10001);
* insert.setNextParameter("goodbye");
* insert.setNextParameter("rob");
* insert.execute();
*
* insert.setNextParameter(10002);
* insert.setNextParameter("chow");
* insert.setNextParameter("bob");
* insert.execute();
*
* txn.commit();
* }
* }</pre>
* <p>
* An alternative to the batch mode on the transaction is to use addBatch() and executeBatch() like:
* </p>
* <pre>{@code
*
* try (Transaction txn = Ebean.beginTransaction()) {
*
* insert.setNextParameter(10000);
* insert.setNextParameter("hello");
* insert.setNextParameter("rob");
* insert.addBatch();
*
* insert.setNextParameter(10001);
* insert.setNextParameter("goodbye");
* insert.setNextParameter("rob");
* insert.addBatch();
*
* insert.setNextParameter(10002);
* insert.setNextParameter("chow");
* insert.setNextParameter("bob");
* insert.addBatch();
*
* int[] rows = insert.executeBatch();
*
* txn.commit();
* }
*
* }</pre>
*
@@ -54,6 +112,18 @@ public interface SqlUpdate {
*/
int execute();
/**
* Execute when addBatch() has been used to batch multiple bind executions.
*
* @return The row counts for each of the batched statements.
*/
int[] executeBatch();
/**
* Add the statement to batch processing to then later execute via executeBatch().
*/
void addBatch();
/**
* Return the generated key value.
*/
@@ -61,7 +131,7 @@ public interface SqlUpdate {
/**
* Execute and return the generated key. This is effectively a short cut for:
*
* <p>
* <pre>{@code
*
* sqlUpdate.execute();
@@ -137,6 +207,13 @@ public interface SqlUpdate {
*/
SqlUpdate setTimeout(int secs);
/**
* Set the next positioned parameter.
*
* @param value The value to bind
*/
SqlUpdate setNextParameter(Object value);
/**
* Set a parameter via its index position.
*/
+49 -7
View File
@@ -23,6 +23,7 @@ public interface Transaction extends AutoCloseable {
* This returns the current transaction for the 'default server'. If you are using
* multiple EbeanServer's then use {@link EbeanServer#currentTransaction()}.
* </p>
*
* @see Ebean#currentTransaction()
* @see EbeanServer#currentTransaction()
*/
@@ -136,6 +137,18 @@ public interface Transaction extends AutoCloseable {
*/
void rollback(Throwable e) throws PersistenceException;
/**
* Set when we want nested transactions to use Savepoint's.
* <p>
* This means that for a nested transaction:
* <ul>
* <li>begin transaction maps to creating a savepoint</li>
* <li>commit transaction maps to releasing a savepoint</li>
* <li>rollback transaction maps to rollback a savepoint</li>
* </ul>
*/
void setNestedUseSavepoint();
/**
* Mark the transaction for rollback only.
*/
@@ -151,7 +164,6 @@ public interface Transaction extends AutoCloseable {
*/
void end();
/**
* Synonym for end() to support AutoClosable.
*/
@@ -227,7 +239,7 @@ public interface Transaction extends AutoCloseable {
* Refer to {@link ServerConfig#setSkipCacheAfterWrite(boolean)} for configuring the default behavior
* for using the L2 bean cache in transactions spanning multiple query/persist requests.
* </p>
* <p>
*
* <pre>{@code
*
* // assume Customer has L2 bean caching enabled ...
@@ -309,7 +321,7 @@ public interface Transaction extends AutoCloseable {
* <p>
* Example: batch processing executing every 3 rows
* </p>
* <p>
*
* <pre>{@code
*
* String data = "This is a simple test of the batch processing"
@@ -347,7 +359,10 @@ public interface Transaction extends AutoCloseable {
void setBatchMode(boolean useBatch);
/**
* The JDBC batch mode to use for this transaction.
* Deprecated - migrate to {@link #setBatchMode(boolean)}.
* <p>
* Set the JDBC batch mode to use for this transaction.
* </p>
* <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.
@@ -356,15 +371,23 @@ public interface Transaction extends AutoCloseable {
* @param persistBatchMode the batch mode to use for this transaction
* @see io.ebean.config.ServerConfig#setPersistBatch(PersistBatch)
*/
@Deprecated
void setBatch(PersistBatch persistBatchMode);
/**
* Deprecated - migrate to {@link #isBatchMode()}.
* Return the batch mode at the transaction level.
*/
@Deprecated
PersistBatch getBatch();
/**
* Return the batch mode at the transaction level.
*/
PersistBatch getBatch();
boolean isBatchMode();
/**
* Set the JDBC batch mode to use for a save() or delete() request.
* Set the JDBC batch mode to use for a save() or delete() when cascading to children.
* <p>
* This only takes effect when batch mode on the transaction has not already meant that
* JDBC batch mode is being used.
@@ -373,17 +396,36 @@ public interface Transaction extends AutoCloseable {
* 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>
* <p>
* This is effectively already turned on for all platforms apart from older Sql Server.
* </p>
*
* @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete()
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
* @see io.ebean.config.ServerConfig#setPersistBatchOnCascade(PersistBatch)
*/
void setBatchOnCascade(boolean batchMode);
/**
* Set the batch mode when cascading.
* <p>
* Deprecated in favour of {@link #setBatchOnCascade(boolean)}
* </p>
*/
@Deprecated
void setBatchOnCascade(PersistBatch batchOnCascadeMode);
/**
* Deprecated - migrate to {@link #isBatchMode()}.
* Return the batch mode at the request level (for each save(), insert(), update() or delete()).
*/
@Deprecated
PersistBatch getBatchOnCascade();
/**
* Return the batch mode at the request level.
*/
boolean isBatchOnCascade();
/**
* Specify the number of statements before a batch is flushed automatically.
*/
+5
View File
@@ -152,6 +152,11 @@ public interface Update<T> {
*/
Update<T> setNullParameter(String name, int jdbcType);
/**
* Set a label meaning performance metrics will be collected for the execution of this update.
*/
Update<T> setLabel(String label);
/**
* Return the sql that is actually executed.
*/
@@ -1,5 +1,6 @@
package io.ebean;
import io.ebean.service.SpiFetchGroupService;
import io.ebean.service.SpiProfileLocationFactory;
import io.ebean.service.SpiRawSqlService;
@@ -15,6 +16,16 @@ class XServiceProvider {
private static SpiProfileLocationFactory profileLocationFactory = initProfileLocation();
private static SpiFetchGroupService fetchGroupService = initSpiFetchGroupService();
private static SpiFetchGroupService initSpiFetchGroupService() {
Iterator<SpiFetchGroupService> loader = ServiceLoader.load(SpiFetchGroupService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiFetchGroupService?");
}
private static SpiRawSqlService initRawSql() {
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
@@ -47,4 +58,17 @@ class XServiceProvider {
return profileLocationFactory;
}
/**
* Return the FetchGroup with the given select clause.
*/
static <T> FetchGroup<T> fetchGroupOf(Class<T> cls, String select) {
return fetchGroupService.of(cls, select);
}
/**
* Return the FetchGroupBuilder with the given select clause.
*/
static <T> FetchGroupBuilder<T> fetchGroupOf(Class<T> cls) {
return fetchGroupService.of(cls);
}
}
@@ -72,6 +72,11 @@ public interface BeanCollection<E> extends Serializable {
*/
boolean isSkipSave();
/**
* Return true if the collection holds modifications.
*/
boolean holdsModifications();
/**
* Return the bean that owns this collection.
*/
@@ -52,6 +52,8 @@ public final class EntityBeanIntercept implements Serializable {
*/
private int state;
private boolean forceUpdate;
private boolean readOnly;
private boolean dirty;
@@ -131,6 +133,20 @@ public final class EntityBeanIntercept implements Serializable {
this.nodeUsageCollector = usageCollector;
}
/**
* Return the ownerId (IdClass).
*/
public Object getOwnerId() {
return ownerId;
}
/**
* Set the ownerId (IdClass).
*/
public void setOwnerId(Object ownerId) {
this.ownerId = ownerId;
}
/**
* Return the owning bean for an embedded bean.
*/
@@ -292,6 +308,20 @@ public final class EntityBeanIntercept implements Serializable {
this.readOnly = readOnly;
}
/**
* Set the bean to be updated when persisted (for merge).
*/
public void setForceUpdate(boolean forceUpdate) {
this.forceUpdate = forceUpdate;
}
/**
* Return true if the entity should be updated.
*/
public boolean isUpdate() {
return forceUpdate || state == STATE_LOADED;
}
/**
* Return true if the entity has been loaded.
*/
@@ -529,6 +559,16 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Set old value but force it to be set regardless if it already has a value.
*/
private void setOriginalValueForce(int propertyIndex, Object value) {
if (origValues == null) {
origValues = new Object[owner._ebean_getPropertyNames().length];
}
origValues[propertyIndex] = value;
}
/**
* For forced update on a 'New' bean set all the loaded properties to changed.
*/
@@ -901,15 +941,19 @@ public final class EntityBeanIntercept implements Serializable {
if (setDirtyState) {
setOriginalValue(propertyIndex, origValue);
if (!dirty) {
dirty = true;
if (embeddedOwner != null) {
// Cascade dirty state from Embedded bean to parent bean
embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex);
}
if (nodeUsageCollector != null) {
nodeUsageCollector.setModified();
}
setDirtyStatus();
}
}
private void setDirtyStatus() {
if (!dirty) {
dirty = true;
if (embeddedOwner != null) {
// Cascade dirty state from Embedded bean to parent bean
embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex);
}
if (nodeUsageCollector != null) {
nodeUsageCollector.setModified();
}
}
}
@@ -1049,10 +1093,12 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
* Explicitly set an old value.
* Explicitly set an old value with force (the old value is forced even it is already set).
*/
public void setOldValue(int propertyIndex, Object oldValue) {
setChangedPropertyValue(propertyIndex, true, oldValue);
setChangedProperty(propertyIndex);
setOriginalValueForce(propertyIndex, oldValue);
setDirtyStatus();
}
/**
+54
View File
@@ -0,0 +1,54 @@
package io.ebean.cache;
import java.util.Set;
/**
* For query cache entries we additionally hold the dependent tables and timestamp for the query result.
* <p>
* We use the dependent tables and timestamp to validate that tables the query joins to have not been
* modified since the query cache entry was cached. If any dependent tables have since been modified
* the query cache entry is treated as invalid.
* </p>
*/
public class QueryCacheEntry {
private final Object value;
private final Set<String> dependentTables;
private final long timestamp;
/**
* Create with dependent tables and timestamp.
*
* @param value The query result being cached
* @param dependentTables The extra tables the query is dependent on (joins to)
* @param timestamp The timestamp that the query uses to check for modifications
*/
public QueryCacheEntry(Object value, Set<String> dependentTables, long timestamp) {
this.value = value;
this.dependentTables = dependentTables;
this.timestamp = timestamp;
}
/**
* Return the actual query result.
*/
public Object getValue() {
return value;
}
/**
* Return the tables the query result is dependent on.
*/
public Set<String> getDependentTables() {
return dependentTables;
}
/**
* Return the timestamp used to check for modifications on the dependent tables.
*/
public long getTimestamp() {
return timestamp;
}
}
@@ -0,0 +1,12 @@
package io.ebean.cache;
/**
* Used to validate that a query cache entry is still valid based on dependent tables.
*/
public interface QueryCacheEntryValidate {
/**
* Return true if the entry is still valid based on dependent tables.
*/
boolean isValid(QueryCacheEntry queryCacheEntry);
}
+65
View File
@@ -0,0 +1,65 @@
package io.ebean.cache;
import io.ebean.config.CurrentTenantProvider;
/**
* Configuration used to create ServerCache instances.
*/
public class ServerCacheConfig {
private final ServerCacheType type;
private final String cacheKey;
private final ServerCacheOptions cacheOptions;
private final CurrentTenantProvider tenantProvider;
private final QueryCacheEntryValidate queryCacheEntryValidate;
public ServerCacheConfig(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions, CurrentTenantProvider tenantProvider, QueryCacheEntryValidate queryCacheEntryValidate) {
this.type = type;
this.cacheKey = cacheKey;
this.cacheOptions = cacheOptions;
this.tenantProvider = tenantProvider;
this.queryCacheEntryValidate = queryCacheEntryValidate;
}
/**
* Return the cache type.
*/
public ServerCacheType getType() {
return type;
}
/**
* Return the name of the cache.
*/
public String getCacheKey() {
return cacheKey;
}
/**
* Return the tuning options.
*/
public ServerCacheOptions getCacheOptions() {
return cacheOptions;
}
/**
* Return the current tenant provider.
*/
public CurrentTenantProvider getTenantProvider() {
return tenantProvider;
}
/**
* Return the service that provides validation for query cache entries.
*/
public QueryCacheEntryValidate getQueryCacheEntryValidate() {
return queryCacheEntryValidate;
}
/**
* Return true if the cache is a query cache.
*/
public boolean isQueryCache() {
return type == ServerCacheType.QUERY;
}
}
+10 -3
View File
@@ -1,7 +1,5 @@
package io.ebean.cache;
import io.ebean.config.CurrentTenantProvider;
/**
* Defines method for constructing caches for beans and queries.
*/
@@ -10,6 +8,15 @@ public interface ServerCacheFactory {
/**
* Create the cache for the given type with options.
*/
ServerCache createCache(ServerCacheType type, String cacheKey, CurrentTenantProvider tenantProvider, ServerCacheOptions cacheOptions);
ServerCache createCache(ServerCacheConfig config);
/**
* Return a ServerCacheNotify that we will send ServerCacheNotification events to.
* <p>
* This is used if a ServerCacheNotifyPlugin is not supplied.
* </p>
*
* @param listener The listener that should be used to process the notification events.
*/
ServerCacheNotify createCacheNotify(ServerCacheNotify listener);
}
@@ -0,0 +1,29 @@
package io.ebean.cache;
import java.util.Set;
/**
* Notification event that dependent tables have been modified.
* <p>
* This is sent to other interested servers (in the cluster).
* </p>
*/
public class ServerCacheNotification {
private final long modifyTimestamp;
private final Set<String> dependentTables;
public ServerCacheNotification(long modifyTimestamp, Set<String> dependentTables) {
this.modifyTimestamp = modifyTimestamp;
this.dependentTables = dependentTables;
}
public long getModifyTimestamp() {
return modifyTimestamp;
}
public Set<String> getDependentTables() {
return dependentTables;
}
}
+12
View File
@@ -0,0 +1,12 @@
package io.ebean.cache;
/**
* Interface for both listening to notification changes and sending them to other members of the cluster.
*/
public interface ServerCacheNotify {
/**
* Notify other server cache members of the table modifications or process the notifications.
*/
void notify(ServerCacheNotification notification);
}
@@ -0,0 +1,17 @@
package io.ebean.cache;
import io.ebean.config.ServerConfig;
/**
* Plugin that provides a ServerCacheNotify implementation.
* <p>
* Is supplied this will be used to send the ServerCacheNotification event to other cluster members.
* </p>
*/
public interface ServerCacheNotifyPlugin {
/**
* Create a ServerCacheNotify implementation given the server configuration.
*/
ServerCacheNotify create(ServerConfig serverConfig);
}
@@ -6,7 +6,6 @@ import io.ebean.bean.BeanCollection;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import javax.persistence.PersistenceException;
import java.util.Set;
/**
@@ -64,7 +63,7 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.ebeanServerName = loader.getName();
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
this.readOnly = ownerBean != null && ownerBean._ebean_getIntercept().isReadOnly();
}
@Override
@@ -96,13 +95,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
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();
}
@@ -215,7 +207,8 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
/**
* Return true if there are underlying additions or removals.
*/
boolean holdsModifications() {
@Override
public boolean holdsModifications() {
return modifyHolder != null && modifyHolder.hasModifications();
}
@@ -6,10 +6,13 @@ package io.ebean.config;
public interface AutoConfigure {
/**
* Provide some configuration the ServerConfig prior to server creation.
* <p>
* Return true if the autoConfiguration applies to this ServerConfig.
* Perform configuration for the ServerConfig prior to properties load.
*/
void configure(ServerConfig serverConfig);
void preConfigure(ServerConfig serverConfig);
/**
* Provide some configuration the ServerConfig prior to server creation but after properties have been applied.
*/
void postConfigure(ServerConfig serverConfig);
}
@@ -102,6 +102,11 @@ public class DbMigrationConfig {
*/
protected Map<String, String> runPlaceholderMap;
/**
* DB schema used for the migration (and testing).
*/
protected String dbSchema;
/**
* DB user used to run the DB migration.
*/
@@ -373,6 +378,33 @@ public class DbMigrationConfig {
this.dbPassword = dbPassword;
}
/**
* Return the DB schema to use (for migration, testing etc).
*/
public String getDbSchema() {
String schema = readEnvironment("ddl.migration.schema");
if (schema != null) {
return schema;
}
return dbSchema;
}
/**
* Set the Db schema to use.
*/
public void setDbSchema(String dbSchema) {
this.dbSchema = dbSchema;
}
/**
* Set the Db schema if it hasn't already been defined.
*/
public void setDefaultDbSchema(String dbSchema) {
if (this.dbSchema == null) {
this.dbSchema = dbSchema;
}
}
/**
* Return migration versions that should be added to history without running.
*/
@@ -402,9 +434,20 @@ public class DbMigrationConfig {
* You can use placeholders like ${version} or ${timestamp} in properties file.
*/
public String getDdlHeader() {
if (ddlHeader != null && !ddlHeader.isEmpty()) {
ddlHeader = StringHelper.replaceString(ddlHeader, "${version}", EbeanVersion.getVersion());
ddlHeader = StringHelper.replaceString(ddlHeader, "${timestamp}", ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT ));
}
return ddlHeader;
}
/**
* Set the header prepended to the DDL.
*/
public void setDdlHeader(String ddlHeader) {
this.ddlHeader = ddlHeader;
}
/**
* Set migration versions that should have their checksum reset and not run.
* <p>
@@ -464,19 +507,18 @@ public class DbMigrationConfig {
runMigration = properties.getBoolean("migration.run", runMigration);
metaTable = properties.get("migration.metaTable", metaTable);
runPlaceholders = properties.get("migration.placeholders", runPlaceholders);
dbSchema = properties.get("migration.dbSchema", dbSchema);
String adminUser = properties.get("datasource." + serverName + ".username", dbUsername);
adminUser = properties.get("datasource." + serverName + ".adminusername", adminUser);
//Do not set user and pass from "datasource.db.username"
//There is a null test in MigrationRunner::getConnection to handle this
//String adminUser = properties.get("datasource." + serverName + ".username", dbUsername);
String adminUser = properties.get("datasource." + serverName + ".adminusername", dbUsername);
dbUsername = properties.get("migration.dbusername", adminUser);
String adminPwd = properties.get("datasource." + serverName + ".password", dbPassword);
adminPwd = properties.get("datasource." + serverName + ".adminpassword", adminPwd);
//String adminPwd = properties.get("datasource." + serverName + ".password", dbPassword);
String adminPwd = properties.get("datasource." + serverName + ".adminpassword", dbPassword);
dbPassword = properties.get("migration.dbpassword", adminPwd);
ddlHeader = properties.get("ddl.header", ddlHeader);
if (ddlHeader != null && !ddlHeader.isEmpty()) {
ddlHeader = StringHelper.replaceString(ddlHeader, "${version}", EbeanVersion.getVersion());
ddlHeader = StringHelper.replaceString(ddlHeader, "${timestamp}", ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT ));
}
}
/**
@@ -548,6 +590,7 @@ public class DbMigrationConfig {
runnerConfig.setRunPlaceholders(runPlaceholders);
runnerConfig.setDbUsername(getDbUsername());
runnerConfig.setDbPassword(getDbPassword());
runnerConfig.setDbSchema(getDbSchema());
runnerConfig.setClassLoader(classLoader);
if (patchInsertOn != null) {
runnerConfig.setPatchInsertOn(patchInsertOn);
@@ -1,124 +0,0 @@
package io.ebean.config;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.annotation.Platform;
import java.util.ArrayList;
import java.util.List;
/**
* Configuration for DB types such as UUID, Geometry etc.
*/
public class DbTypeConfig {
/**
* The Geometry SRID value (default 4326).
*/
private int geometrySRID = 4326;
/**
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
*/
private IdType idType;
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
*/
private ServerConfig.DbUuid dbUuid = ServerConfig.DbUuid.AUTO_VARCHAR;
/**
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
*/
private List<CustomDbTypeMapping> customDbTypeMappings = new ArrayList<>();
/**
* Return the Geometry SRID.
*/
public int getGeometrySRID() {
return geometrySRID;
}
/**
* Set the Geometry SRID.
*/
public void setGeometrySRID(int geometrySRID) {
this.geometrySRID = geometrySRID;
}
/**
* Return the DB type used to store UUID.
*/
public ServerConfig.DbUuid getDbUuid() {
return dbUuid;
}
/**
* Set the DB type used to store UUID.
*/
public void setDbUuid(ServerConfig.DbUuid dbUuid) {
this.dbUuid = dbUuid;
}
/**
* Return the IdType to use (or null for the default choice).
*/
public IdType getIdType() {
return idType;
}
/**
* Set the IdType to use (when the DB supports both SEQUENCE and IDENTITY and the default is not desired).
*/
public void setIdType(IdType idType) {
this.idType = idType;
}
/**
* Add a custom type mapping.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal.class/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String.class/varchar but only for Postgres
* serverConfig.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
* @param platform Optionally specify the platform this mapping should apply to.
*/
public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition, platform));
}
/**
* Add a custom type mapping that applies to all platforms.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String/varchar
* serverConfig.addCustomMapping(DbType.VARCHAR, "text");
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
*/
public void addCustomMapping(DbType type, String columnDefinition) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition));
}
/**
* Return the list of custom type mappings.
*/
public List<CustomDbTypeMapping> getCustomTypeMappings() {
return customDbTypeMappings;
}
}
@@ -0,0 +1,335 @@
package io.ebean.config;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.util.StringHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Configuration for DB types such as UUID, Geometry etc.
*/
public class PlatformConfig {
private boolean allQuotedIdentifiers;
/**
* The database boolean true value (typically either 1, T, or Y).
*/
private String databaseBooleanTrue;
/**
* The database boolean false value (typically either 0, F or N).
*/
private String databaseBooleanFalse;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
private int databaseSequenceBatchSize = 20;
/**
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
*/
private IdType idType;
/**
* The Geometry SRID value (default 4326).
*/
private int geometrySRID = 4326;
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
*/
private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
/**
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
*/
private List<CustomDbTypeMapping> customDbTypeMappings = new ArrayList<>();
/**
* Construct with defaults.
*/
public PlatformConfig() {
}
/**
* Construct based on given config - typically for DbMigration generation with many platforms.
*/
public PlatformConfig(PlatformConfig platformConfig) {
this.databaseBooleanFalse = platformConfig.databaseBooleanFalse;
this.databaseBooleanTrue = platformConfig.databaseBooleanTrue;
this.databaseSequenceBatchSize = platformConfig.databaseSequenceBatchSize;
this.idType = platformConfig.idType;
this.geometrySRID = platformConfig.geometrySRID;
this.dbUuid = platformConfig.dbUuid;
}
/**
* Return true if all DB column and table names should use quoted identifiers.
*/
public boolean isAllQuotedIdentifiers() {
return allQuotedIdentifiers;
}
/**
* Set to true if all DB column and table names should use quoted identifiers.
*/
public void setAllQuotedIdentifiers(boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
}
/**
* Return a value used to represent TRUE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value returned is either a Integer or a String (e.g. "1", or "T").
* </p>
*/
public String getDatabaseBooleanTrue() {
return databaseBooleanTrue;
}
/**
* Set the value to represent TRUE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value set is either a Integer or a String (e.g. "1", or "T").
* </p>
*/
public void setDatabaseBooleanTrue(String databaseBooleanTrue) {
this.databaseBooleanTrue = databaseBooleanTrue;
}
/**
* Return a value used to represent FALSE in the database.
*/
public String getDatabaseBooleanFalse() {
return databaseBooleanFalse;
}
/**
* Set the value used to represent FALSE in the database.
*/
public void setDatabaseBooleanFalse(String databaseBooleanFalse) {
this.databaseBooleanFalse = databaseBooleanFalse;
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return databaseSequenceBatchSize;
}
/**
* Set the number of DB sequence values that should be preallocated.
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
}
/**
* Return the Geometry SRID.
*/
public int getGeometrySRID() {
return geometrySRID;
}
/**
* Set the Geometry SRID.
*/
public void setGeometrySRID(int geometrySRID) {
this.geometrySRID = geometrySRID;
}
/**
* Return the DB type used to store UUID.
*/
public DbUuid getDbUuid() {
return dbUuid;
}
/**
* Set the DB type used to store UUID.
*/
public void setDbUuid(DbUuid dbUuid) {
this.dbUuid = dbUuid;
}
/**
* Return the IdType to use (or null for the default choice).
*/
public IdType getIdType() {
return idType;
}
/**
* Set the IdType to use (when the DB supports both SEQUENCE and IDENTITY and the default is not desired).
*/
public void setIdType(IdType idType) {
this.idType = idType;
}
/**
* Add a custom type mapping.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal.class/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String.class/varchar but only for Postgres
* serverConfig.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
* @param platform Optionally specify the platform this mapping should apply to.
*/
public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition, platform));
}
/**
* Add a custom type mapping that applies to all platforms.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String/varchar
* serverConfig.addCustomMapping(DbType.VARCHAR, "text");
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
*/
public void addCustomMapping(DbType type, String columnDefinition) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition));
}
/**
* Return the list of custom type mappings.
*/
public List<CustomDbTypeMapping> getCustomTypeMappings() {
return customDbTypeMappings;
}
public void loadSettings(PropertiesWrapper p) {
idType = p.getEnum(IdType.class, "idType", idType);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
if (dbUuid != null) {
setDbUuid(dbUuid);
}
if (p.getBoolean("uuidStoreAsBinary", false)) {
setDbUuid(DbUuid.BINARY);
}
int srid = p.getInt("geometrySRID", 0);
if (srid > 0) {
setGeometrySRID(srid);
}
// Mapping is specified in the form: BOOLEAN=int(1);BIT=int(1);
String mapping = p.get("mapping");
if (mapping != null && !mapping.isEmpty()) {
Map<String, String> map = StringHelper.delimitedToMap(mapping, ";", "=");
for (Entry<String, String> entry : map.entrySet()) {
addCustomMapping(DbType.valueOf(entry.getKey()), entry.getValue());
}
}
boolean quotedIdentifiers = p.getBoolean("allQuotedIdentifiers", allQuotedIdentifiers);
if (quotedIdentifiers != allQuotedIdentifiers) {
// potentially also set to use matching naming convention
setAllQuotedIdentifiers(quotedIdentifiers);
}
}
/**
* Specify how UUID is stored.
*/
public enum DbUuid {
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to VARCHAR(40).
*/
AUTO_VARCHAR(true, false, false),
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16).
*/
AUTO_BINARY(true, true, false),
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16) with optimized packing.
*/
AUTO_BINARY_OPTIMIZED(true, true, true),
/**
* Store using DB VARCHAR(40).
*/
VARCHAR(false, false, false),
/**
* Store using DB BINARY(16).
*/
BINARY(false, true, false),
/**
* Store using DB BINARY(16).
*/
BINARY_OPTIMIZED(false, true, true);
boolean nativeType;
boolean binary;
boolean binaryOptimized;
DbUuid(boolean nativeType, boolean binary, boolean binaryOptimized) {
this.nativeType = nativeType;
this.binary = binary;
this.binaryOptimized = binaryOptimized;
}
/**
* Return true if native UUID type is preferred.
*/
public boolean useNativeType() {
return nativeType;
}
/**
* Return true if BINARY(16) storage is preferred over VARCHAR(40).
*/
public boolean useBinary() {
return binary;
}
/**
* Return true, if optimized packing should be used.
*/
public boolean useBinaryOptimized() {
return binaryOptimized;
}
}
}
@@ -10,20 +10,23 @@ public class PropertiesWrapper {
protected final String serverName;
private final ClassLoadConfig classLoadConfig;
/**
* Construct with a prefix, serverName and properties.
*/
public PropertiesWrapper(String prefix, String serverName, Properties properties) {
public PropertiesWrapper(String prefix, String serverName, Properties properties, ClassLoadConfig classLoadConfig) {
this.serverName = serverName;
this.prefix = prefix;
this.properties = properties;
this.classLoadConfig = classLoadConfig;
}
/**
* Construct without prefix of serverName.
*/
public PropertiesWrapper(Properties properties) {
this(null, null, properties);
public PropertiesWrapper(Properties properties, ClassLoadConfig classLoadConfig) {
this(null, null, properties, classLoadConfig);
}
/**
@@ -115,4 +118,30 @@ public class PropertiesWrapper {
return (level == null) ? defaultValue : Enum.valueOf(enumType, level.toUpperCase());
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param pluginType the type of plugin
* @param key properties key
* @param instance existing instance
*/
public <T> T createInstance(Class<T> pluginType, String key, T instance) {
if (instance != null) {
return instance;
}
String classname = get(key, null);
return createInstance(pluginType, classname);
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param pluginType the type of plugin
* @param classname the implementation class as per properties
*/
@SuppressWarnings("unchecked")
public <T> T createInstance(Class<T> pluginType, String classname) {
return classname == null ? null : (T) classLoadConfig.newInstance(classname);
}
}
+300 -163
View File
@@ -33,6 +33,7 @@ import io.ebean.util.StringHelper;
import org.avaje.datasource.DataSourceConfig;
import javax.sql.DataSource;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -57,22 +58,24 @@ import java.util.ServiceLoader;
* <pre>{@code
*
* ServerConfig c = new ServerConfig();
* c.setName("db");
*
* // read the ebean.properties and load
* // those settings into this serverConfig object
* c.loadFromProperties();
*
* // add any classes found in the app.data package
* c.addPackage("com.myapp.domain");
*
* // register as the 'Default' server
* c.setDefaultServer(true);
* // explicitly register the entity beans to avoid classpath scanning
* c.addClass(Customer.class);
* c.addClass(User.class);
*
* EbeanServer server = EbeanServerFactory.create(c);
*
* }</pre>
*
* <p>
* Note that ServerConfigProvider provides a standard Java ServiceLoader mechanism that can
* be used to apply configuration to the ServerConfig.
* </p>
*
* @author emcgreal
* @author rbygrave
* @see EbeanServerFactory
@@ -183,16 +186,6 @@ public class ServerConfig {
*/
private DatabasePlatform databasePlatform;
/**
* The preferred IdType (to override the default Platform type).
*/
private IdType idType;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
private int databaseSequenceBatchSize = 20;
/**
* JDBC fetchSize hint when using findList. Defaults to 0 leaving it up to the JDBC driver.
*/
@@ -293,7 +286,7 @@ public class ServerConfig {
/**
* When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig.
* I believe this will default to true in some future release (as it has a nice performance benefit).
*
* <p>
* autoReadOnlyDataSource is an unfortunate name for this config option but I haven't come up with a better one.
*/
private boolean autoReadOnlyDataSource;
@@ -303,6 +296,11 @@ public class ServerConfig {
*/
private DataSourceConfig readOnlyDataSourceConfig = new DataSourceConfig();
/**
* Optional - the database schema that should be used to own the tables etc.
*/
private String dbSchema;
/**
* The db migration config (migration resource path etc).
*/
@@ -330,18 +328,6 @@ public class ServerConfig {
*/
private String dataSourceJndiName;
/**
* The database boolean true value (typically either 1, T, or Y).
*/
private String databaseBooleanTrue;
/**
* The database boolean false value (typically either 0, F or N).
*/
private String databaseBooleanFalse;
private boolean allQuotedIdentifiers;
/**
* The naming convention.
*/
@@ -368,9 +354,24 @@ public class ServerConfig {
private boolean updatesDeleteMissingChildren = true;
/**
* Database type configuration.
* Database platform configuration.
*/
private DbTypeConfig dbTypeConfig = new DbTypeConfig();
private PlatformConfig platformConfig = new PlatformConfig();
/**
* The UUID version to use.
*/
private UuidVersion uuidVersion = UuidVersion.VERSION4;
/**
* The UUID state file (for Version 1 UUIDs).
*/
private String uuidStateFile = "ebean-uuid.state";
/**
* The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
*/
private Clock clock = Clock.systemUTC();
private List<IdGenerator> idGenerators = new ArrayList<>();
private List<BeanFindController> findControllers = new ArrayList<>();
@@ -470,6 +471,13 @@ public class ServerConfig {
*/
private boolean disableL2Cache;
/**
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
*/
private boolean useJavaxValidationNotNull = true;
/**
* Generally we want to perform L2 cache notification in the background and not impact
* the performance of executing transactions.
@@ -493,6 +501,16 @@ public class ServerConfig {
*/
private boolean defaultOrderById = false;
/**
* The mappingLocations for searching xml mapping.
*/
private List<String> mappingLocations = new ArrayList<>();
/**
* When true we do not need explicit GeneratedValue mapping.
*/
private boolean idGeneratorAutomatic = true;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
@@ -500,6 +518,20 @@ public class ServerConfig {
}
/**
* Get the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
*/
public Clock getClock() {
return clock;
}
/**
* Set the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
*/
public void setClock(final Clock clock) {
this.clock = clock;
}
/**
* Return the slow query time in millis.
*/
@@ -929,7 +961,7 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
@@ -1093,6 +1125,25 @@ public class ServerConfig {
this.profilingConfig = profilingConfig;
}
/**
* Return the DB schema to use.
*/
public String getDbSchema() {
return dbSchema;
}
/**
* Set the DB schema to use. This specifies to use this schema for:
* <ul>
* <li>Running Database migrations - Create and use the DB schema</li>
* <li>Testing DDL - Create-all.sql DDL execution creates and uses schema</li>
* <li>Testing Docker - Set default schema on connection URL</li>
* </ul>
*/
public void setDbSchema(String dbSchema) {
this.dbSchema = dbSchema;
}
/**
* Return the DB migration configuration.
*/
@@ -1111,14 +1162,14 @@ public class ServerConfig {
* Return the Geometry SRID.
*/
public int getGeometrySRID() {
return dbTypeConfig.getGeometrySRID();
return platformConfig.getGeometrySRID();
}
/**
* Set the Geometry SRID.
*/
public void setGeometrySRID(int geometrySRID) {
dbTypeConfig.setGeometrySRID(geometrySRID);
platformConfig.setGeometrySRID(geometrySRID);
}
/**
@@ -1408,15 +1459,21 @@ public class ServerConfig {
* Return true if all DB column and table names should use quoted identifiers.
*/
public boolean isAllQuotedIdentifiers() {
return allQuotedIdentifiers;
return platformConfig.isAllQuotedIdentifiers();
}
/**
* Set to true if all DB column and table names should use quoted identifiers.
*/
public void setAllQuotedIdentifiers(boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
if (allQuotedIdentifiers && namingConvention instanceof UnderscoreNamingConvention) {
platformConfig.setAllQuotedIdentifiers(allQuotedIdentifiers);
if (allQuotedIdentifiers) {
adjustNamingConventionForAllQuoted();
}
}
private void adjustNamingConventionForAllQuoted() {
if (namingConvention instanceof UnderscoreNamingConvention) {
// we need to use matching naming convention
this.namingConvention = new MatchingNamingConvention();
}
@@ -1631,7 +1688,7 @@ public class ServerConfig {
* </p>
*/
public String getDatabaseBooleanTrue() {
return databaseBooleanTrue;
return platformConfig.getDatabaseBooleanTrue();
}
/**
@@ -1644,7 +1701,7 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseBooleanTrue(String databaseTrue) {
this.databaseBooleanTrue = databaseTrue;
platformConfig.setDatabaseBooleanTrue(databaseTrue);
}
/**
@@ -1657,7 +1714,7 @@ public class ServerConfig {
* </p>
*/
public String getDatabaseBooleanFalse() {
return databaseBooleanFalse;
return platformConfig.getDatabaseBooleanFalse();
}
/**
@@ -1670,14 +1727,14 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseBooleanFalse(String databaseFalse) {
this.databaseBooleanFalse = databaseFalse;
this.platformConfig.setDatabaseBooleanFalse(databaseFalse);
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return databaseSequenceBatchSize;
return platformConfig.getDatabaseSequenceBatchSize();
}
/**
@@ -1696,7 +1753,7 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseSequenceBatch(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
this.platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
@@ -1723,7 +1780,7 @@ public class ServerConfig {
* that you don't have access to.
* </p>
* <p>
* Values are oracle, h2, postgres, mysql, mssqlserver2005.
* Values are oracle, h2, postgres, mysql, sqlserver16, sqlserver17.
* </p>
*/
public void setDatabasePlatformName(String databasePlatformName) {
@@ -1752,14 +1809,14 @@ public class ServerConfig {
* Return the preferred DB platform IdType.
*/
public IdType getIdType() {
return idType;
return platformConfig.getIdType();
}
/**
* Set the preferred DB platform IdType.
*/
public void setIdType(IdType idType) {
this.idType = idType;
this.platformConfig.setIdType(idType);
}
/**
@@ -1872,15 +1929,50 @@ public class ServerConfig {
/**
* Return the configuration for DB types (such as UUID and custom mappings).
*/
public DbTypeConfig getDbTypeConfig() {
return dbTypeConfig;
public PlatformConfig getPlatformConfig() {
return platformConfig;
}
/**
* Set the configuration for DB platform (such as UUID and custom mappings).
*/
public void setPlatformConfig(PlatformConfig platformConfig) {
this.platformConfig = platformConfig;
}
/**
* Set the DB type used to store UUID.
*/
public void setDbUuid(DbUuid dbUuid) {
this.dbTypeConfig.setDbUuid(dbUuid);
public void setDbUuid(PlatformConfig.DbUuid dbUuid) {
this.platformConfig.setDbUuid(dbUuid);
}
/**
* Returns the UUID version mode.
*/
public UuidVersion getUuidVersion() {
return uuidVersion;
}
/**
* Sets the UUID version mode.
*/
public void setUuidVersion(UuidVersion uuidVersion) {
this.uuidVersion = uuidVersion;
}
/**
* Return the UUID state file.
*/
public String getUuidStateFile() {
return uuidStateFile;
}
/**
* Set the UUID state file.
*/
public void setUuidStateFile(String uuidStateFile) {
this.uuidStateFile = uuidStateFile;
}
/**
@@ -1920,6 +2012,16 @@ public class ServerConfig {
this.durationWithNanos = durationWithNanos;
}
/**
* Set to true to run DB migrations on server start.
* <p>
* This is the same as serverConfig.getMigrationConfig().setRunMigration(). We have added this method here
* as it is often the only thing we need to configure for migrations.
*/
public void setRunMigration(boolean runMigration) {
migrationConfig.setRunMigration(runMigration);
}
/**
* Set to true to generate the "create all" DDL on startup.
* <p>
@@ -2308,7 +2410,7 @@ public class ServerConfig {
* @param platform Optionally specify the platform this mapping should apply to.
*/
public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
dbTypeConfig.addCustomMapping(type, columnDefinition, platform);
platformConfig.addCustomMapping(type, columnDefinition, platform);
}
/**
@@ -2328,7 +2430,7 @@ public class ServerConfig {
* @param columnDefinition The column definition that should be used
*/
public void addCustomMapping(DbType type, String columnDefinition) {
dbTypeConfig.addCustomMapping(type, columnDefinition);
platformConfig.addCustomMapping(type, columnDefinition);
}
/**
@@ -2597,7 +2699,8 @@ public class ServerConfig {
* Load settings from ebean.properties.
*/
public void loadFromProperties() {
loadFromProperties(PropertiesLoader.load());
this.properties = PropertiesLoader.load();
configureFromProperties();
}
/**
@@ -2605,20 +2708,32 @@ public class ServerConfig {
*/
public void loadFromProperties(Properties properties) {
// keep the properties used for configuration so that these are available for plugins
this.properties = properties;
autoConfiguration();
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
loadSettings(p);
this.properties = PropertiesLoader.eval(properties);
configureFromProperties();
}
/**
* Load the settings from the given properties
*/
private void configureFromProperties() {
List<AutoConfigure> autoConfigures = autoConfiguration();
loadSettings(new PropertiesWrapper("ebean", name, properties, classLoadConfig));
for (AutoConfigure autoConfigure : autoConfigures) {
autoConfigure.postConfigure(this);
}
}
/**
* Use a 'plugin' to provide automatic configuration. Intended for automatic testing
* configuration with Docker containers via ebean-test-config.
*/
private void autoConfiguration() {
private List<AutoConfigure> autoConfiguration() {
List<AutoConfigure> list = new ArrayList<>();
for (AutoConfigure autoConfigure : serviceLoad(AutoConfigure.class)) {
autoConfigure.configure(this);
autoConfigure.preConfigure(this);
list.add(autoConfigure);
}
return list;
}
/**
@@ -2642,34 +2757,6 @@ public class ServerConfig {
return properties;
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param properties the properties
* @param pluginType the type of plugin
* @param key properties key
* @param instance existing instance
*/
protected <T> T createInstance(PropertiesWrapper properties, Class<T> pluginType, String key, T instance) {
if (instance != null) {
return instance;
}
String classname = properties.get(key, null);
return createInstance(pluginType, classname);
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param pluginType the type of plugin
* @param classname the implementation class as per properties
*/
@SuppressWarnings("unchecked")
protected <T> T createInstance(Class<T> pluginType, String classname) {
return classname == null ? null : (T) classLoadConfig.newInstance(classname);
}
/**
* loads the data source settings to preserve existing behaviour. IMHO, if someone has set the datasource config already,
* they don't want the settings to be reloaded and reset. This allows a descending class to override this behaviour and prevent it
@@ -2679,6 +2766,7 @@ public class ServerConfig {
*/
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.properties, name);
readOnlyDataSourceConfig.loadSettings(p.properties, name);
}
/**
@@ -2700,13 +2788,15 @@ public class ServerConfig {
*/
protected void loadSettings(PropertiesWrapper p) {
dbSchema = p.get("dbSchema", dbSchema);
if (dbSchema != null) {
migrationConfig.setDefaultDbSchema(dbSchema);
}
profilingConfig.loadSettings(p, name);
migrationConfig.loadSettings(p, name);
boolean quotedIdentifiers = p.getBoolean("allQuotedIdentifiers", allQuotedIdentifiers);
if (quotedIdentifiers != allQuotedIdentifiers) {
// potentially also set to use matching naming convention
setAllQuotedIdentifiers(quotedIdentifiers);
platformConfig.loadSettings(p);
if (platformConfig.isAllQuotedIdentifiers()) {
adjustNamingConventionForAllQuoted();
}
namingConvention = createNamingConvention(p, namingConvention);
if (namingConvention != null) {
@@ -2727,11 +2817,6 @@ public class ServerConfig {
}
loadDocStoreSettings(p);
int srid = p.getInt("geometrySRID", 0);
if (srid > 0) {
dbTypeConfig.setGeometrySRID(srid);
}
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
@@ -2740,24 +2825,24 @@ public class ServerConfig {
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);
backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform", databasePlatform);
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager", encryptKeyManager);
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
encryptor = createInstance(p, Encryptor.class, "encryptor", encryptor);
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt", dbEncrypt);
currentUserProvider = p.createInstance(CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
databasePlatform = p.createInstance(DatabasePlatform.class, "databasePlatform", databasePlatform);
encryptKeyManager = p.createInstance(EncryptKeyManager.class, "encryptKeyManager", encryptKeyManager);
encryptDeployManager = p.createInstance(EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
encryptor = p.createInstance(Encryptor.class, "encryptor", encryptor);
dbEncrypt = p.createInstance(DbEncrypt.class, "dbEncrypt", dbEncrypt);
dbOffline = p.getBoolean("dbOffline", dbOffline);
serverCachePlugin = createInstance(p, ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
serverCachePlugin = p.createInstance(ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
if (packages != null) {
String packagesProp = p.get("search.packages", p.get("packages", null));
packages = getSearchJarsPackages(packagesProp);
}
String packagesProp = p.get("search.packages", p.get("packages", null));
packages = getSearchList(packagesProp, packages);
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
@@ -2773,7 +2858,6 @@ public class ServerConfig {
throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'");
}
idType = p.getEnum(IdType.class, "idType", idType);
persistBatch = p.getEnum(PersistBatch.class, "persistBatch", persistBatch);
persistBatchOnCascade = p.getEnum(PersistBatch.class, "persistBatchOnCascade", persistBatchOnCascade);
@@ -2794,19 +2878,12 @@ public class ServerConfig {
dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName);
jdbcFetchSizeFindEach = p.getInt("jdbcFetchSizeFindEach", jdbcFetchSizeFindEach);
jdbcFetchSizeFindList = p.getInt("jdbcFetchSizeFindList", jdbcFetchSizeFindList);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
defaultOrderById = p.getBoolean("defaultOrderById", defaultOrderById);
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
if (dbUuid != null) {
dbTypeConfig.setDbUuid(dbUuid);
}
if (p.getBoolean("uuidStoreAsBinary", false)) {
dbTypeConfig.setDbUuid(DbUuid.BINARY);
}
uuidVersion = p.getEnum(UuidVersion.class, "uuidVersion", uuidVersion);
uuidStateFile = p.get("uuidStateFile", uuidStateFile);
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
@@ -2827,12 +2904,30 @@ public class ServerConfig {
ddlInitSql = p.get("ddl.initSql", ddlInitSql);
ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
// read tenant-configuration from config:
// tenant.mode = NONE | DB | SCHEMA | CATALOG | PARTITION
String mode = p.get("tenant.mode");
if (mode != null) {
for (TenantMode value : TenantMode.values()) {
if (value.name().equalsIgnoreCase(mode)) {
tenantMode = value;
break;
}
}
}
currentTenantProvider = p.createInstance(CurrentTenantProvider.class, "tenant.currentTenantProvider", currentTenantProvider);
tenantCatalogProvider = p.createInstance(TenantCatalogProvider.class, "tenant.catalogProvider", tenantCatalogProvider);
tenantSchemaProvider = p.createInstance(TenantSchemaProvider.class, "tenant.schemaProvider", tenantSchemaProvider);
tenantPartitionColumn = p.get("tenant.partitionColumn", tenantPartitionColumn);
classes = getClasses(p);
String mappingsProp = p.get("mappingLocations", null);
mappingLocations = getSearchList(mappingsProp, mappingLocations);
}
private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention", null);
NamingConvention nc = properties.createInstance(NamingConvention.class, "namingConvention", null);
return (nc != null) ? nc : namingConvention;
}
@@ -2865,17 +2960,17 @@ public class ServerConfig {
return classes;
}
private List<String> getSearchJarsPackages(String searchPackages) {
private List<String> getSearchList(String searchNames, List<String> defaultValue) {
if (searchPackages != null) {
String[] entries = StringHelper.splitNames(searchPackages);
if (searchNames != null) {
String[] entries = StringHelper.splitNames(searchNames);
List<String> hitList = new ArrayList<>(entries.length);
Collections.addAll(hitList, entries);
return hitList;
} else {
return new ArrayList<>();
return defaultValue;
}
}
@@ -2959,6 +3054,25 @@ public class ServerConfig {
this.disableL2Cache = disableL2Cache;
}
/**
* Returns if we use javax.validation.constraints.NotNull
*/
public boolean isUseJavaxValidationNotNull() {
return useJavaxValidationNotNull;
}
/**
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code>
* with respect to generating a <code>NOT NULL</code> column.
* <p>
* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL.
* Set this to <code>false</code> and the javax NotNull annotation is effectively ignored (and
* we instead use Ebean's own NotNull annotation or JPA Column(nullable=false) annotation.
*/
public void setUseJavaxValidationNotNull(boolean useJavaxValidationNotNull) {
this.useJavaxValidationNotNull = useJavaxValidationNotNull;
}
/**
* Return true if L2 cache notification should run in the foreground.
*/
@@ -3004,50 +3118,73 @@ public class ServerConfig {
}
/**
* Specify how UUID is stored.
* Create a new PlatformConfig based of the one held but with overridden properties by reading
* properties with the given path and prefix.
* <p>
* Typically used in Db Migration generation for many platform targets that might have different
* configuration for IdType, UUID, quoted identifiers etc.
* </p>
*
* @param propertiesPath The properties path used for loading and setting properties
* @param platformPrefix The prefix used for loading and setting properties
* @return A copy of the PlatformConfig with overridden properties
*/
public enum DbUuid {
public PlatformConfig newPlatformConfig(String propertiesPath, String platformPrefix) {
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to VARCHAR(40).
*/
AUTO_VARCHAR(true, false),
PropertiesWrapper p = new PropertiesWrapper(propertiesPath, platformPrefix, properties, classLoadConfig);
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16).
*/
AUTO_BINARY(true, true),
PlatformConfig config = new PlatformConfig(platformConfig);
config.loadSettings(p);
return config;
}
/**
* Store using DB VARCHAR(40).
*/
VARCHAR(false, false),
/**
* Store using DB BINARY(16).
*/
BINARY(false, true);
boolean nativeType;
boolean binary;
DbUuid(boolean nativeType, boolean binary) {
this.nativeType = nativeType;
this.binary = binary;
/**
* Add a mapping location to search for xml mapping via class path search.
*/
public void addMappingLocation(String mappingLocation) {
if (mappingLocations == null) {
mappingLocations = new ArrayList<>();
}
mappingLocations.add(mappingLocation);
}
/**
* Return true if native UUID type is preferred.
*/
public boolean useNativeType() {
return nativeType;
}
/**
* Return mapping locations to search for xml mapping via class path search.
*/
public List<String> getMappingLocations() {
return mappingLocations;
}
/**
* Return true if BINARY(16) storage is preferred over VARCHAR(40).
*/
public boolean useBinary() {
return binary;
}
/**
* Set mapping locations to search for xml mapping via class path search.
* <p>
* This is only used if classes have not been explicitly specified.
* </p>
*/
public void setMappingLocations(List<String> mappingLocations) {
this.mappingLocations = mappingLocations;
}
/**
* When false we need explicit <code>@GeneratedValue</code> mapping to assign
* Identity or Sequence generated values. When true Id properties are automatically
* assigned Identity or Sequence without the GeneratedValue mapping.
*/
public boolean isIdGeneratorAutomatic() {
return idGeneratorAutomatic;
}
/**
* Set to false such that Id properties require explicit <code>@GeneratedValue</code>
* mapping before they are assigned Identity or Sequence generation based on platform.
*/
public void setIdGeneratorAutomatic(boolean idGeneratorAutomatic) {
this.idGeneratorAutomatic = idGeneratorAutomatic;
}
public enum UuidVersion {
VERSION4,
VERSION1,
VERSION1RND
}
}
@@ -0,0 +1,39 @@
package io.ebean.config;
/**
* Provides a ServiceLoader based mechanism to configure a ServerConfig.
* <p>
* Provide an implementation and register it via the standard Java ServiceLoader mechanism
* via a file at <code>META-INF/services/io.ebean.config.ServerConfigProvider</code>.
* </p>
* <p>
* If you are using a DI container like Spring or Guice you are unlikely to use this but instead use a
* spring specific configuration. When we are not using a DI container we may use this mechanism to
* explicitly register the entity beans and avoid classpath scanning.
* </p>
* <pre>{@code
*
* public class EbeanConfigProvider implements ServerConfigProvider {
*
* @Override
* public void apply(ServerConfig config) {
*
* // register the entity bean classes explicitly
* config.addClass(Customer.class);
* config.addClass(User.class);
* ...
* }
* }
*
* }</pre>
*/
public interface ServerConfigProvider {
/**
* Apply the configuration to the ServerConfig.
* <p>
* Typically we explicitly register entity bean classes and thus avoid classpath scanning.
* </p>
*/
void apply(ServerConfig config);
}
+22 -6
View File
@@ -8,32 +8,40 @@ public enum TenantMode {
/**
* No multi-tenancy.
*/
NONE(false),
NONE(false, true),
/**
* Each Tenant has their own Database (javax.sql.DataSource)
*/
DB(true),
DB(true, false),
/**
* Each Tenant has their own Database schema.
*/
SCHEMA(true),
SCHEMA(true, false),
/**
* Each Tenant has their own Database but with in connection pool
*/
CATALOG(true),
CATALOG(true, false),
/**
* Tenants share tables but have a discriminator/partition column that partitions the data.
*/
PARTITION(false);
PARTITION(false, true),
/**
* Each Tenant has their own Database (javax.sql.DataSource), and there is also one master-database
* (that holds configuration e.g.)
*/
DB_WITH_MASTER(true, true);
boolean dynamicDataSource;
boolean ddlEnabled;
TenantMode(boolean dynamicDataSource) {
TenantMode(boolean dynamicDataSource, boolean ddlEnabled) {
this.dynamicDataSource = dynamicDataSource;
this.ddlEnabled = ddlEnabled;
}
/**
@@ -42,4 +50,12 @@ public enum TenantMode {
public boolean isDynamicDataSource() {
return dynamicDataSource;
}
/**
* Returns true, if DDL is enabled.
*/
public boolean isDdlEnabled() {
return ddlEnabled;
}
}
@@ -112,39 +112,7 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
* Convert and return the string to underscore from camel case.
*/
protected String toUnderscoreFromCamel(String camelCase) {
int lastUpper = -1;
StringBuilder sb = new StringBuilder(camelCase.length() + 4);
for (int i = 0; i < camelCase.length(); i++) {
char c = camelCase.charAt(i);
if ('_' == c) {
// Underscores should just be passed through
sb.append(c);
lastUpper = i;
} else if (Character.isDigit(c)) {
if (i > lastUpper + 1 && !digitsCompressed) {
sb.append("_");
}
sb.append(c);
lastUpper = i;
} else if (Character.isUpperCase(c)) {
if (i > lastUpper + 1) {
sb.append("_");
}
sb.append(Character.toLowerCase(c));
lastUpper = i;
} else {
sb.append(c);
}
}
String ret = sb.toString();
if (forceUpperCase) {
ret = ret.toUpperCase();
}
return ret;
return CamelCaseHelper.toUnderscoreFromCamel(camelCase, digitsCompressed, forceUpperCase);
}
/**
@@ -5,8 +5,7 @@ import io.ebean.Query;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.CustomDbTypeMapping;
import io.ebean.config.DbTypeConfig;
import io.ebean.config.ServerConfig;
import io.ebean.config.PlatformConfig;
import io.ebean.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,6 +16,7 @@ import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
/**
@@ -215,16 +215,16 @@ public class DatabasePlatform {
/**
* Configure the platform given the server configuration.
*/
public void configure(ServerConfig config) {
public void configure(PlatformConfig config) {
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
configureIdType(config.getIdType());
configure(config.getDbTypeConfig(), config.isAllQuotedIdentifiers());
configure(config, config.isAllQuotedIdentifiers());
}
/**
* Configure UUID Storage etc based on ServerConfig settings.
*/
protected void configure(DbTypeConfig config, boolean allQuotedIdentifiers) {
protected void configure(PlatformConfig config, boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
addGeoTypes(config.getGeometrySRID());
configureIdType(config.getIdType());
@@ -382,6 +382,13 @@ public class DatabasePlatform {
this.historySupport = historySupport;
}
/**
* So no except for Postgres and CockroachDB.
*/
public boolean isNativeArrayType() {
return false;
}
/**
* Return true if the DB supports native UUID.
*/
@@ -518,6 +525,13 @@ public class DatabasePlatform {
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
}
/**
* Normally not needed - overridden in CockroachPlatform.
*/
public boolean isDdlCommitOnCreateIndex() {
return false;
}
/**
* Return the DB identity/sequence features for this platform.
*
@@ -645,6 +659,39 @@ public class DatabasePlatform {
return persistBatchOnCascade;
}
/**
* Create the DB schema if it does not exist.
*/
public void createSchemaIfNotExists(String dbSchema, Connection connection) throws SQLException {
if (!schemaExists(dbSchema, connection)) {
Statement query = connection.createStatement();
try {
logger.info("create schema:{}", dbSchema);
query.executeUpdate("create schema " + dbSchema);
} finally {
JdbcClose.close(query);
}
}
}
/**
* Return true if the schema exists.
*/
public boolean schemaExists(String dbSchema, Connection connection) throws SQLException {
ResultSet schemas = connection.getMetaData().getSchemas();
try {
while (schemas.next()) {
String schema = schemas.getString(1);
if (schema.equalsIgnoreCase(dbSchema)) {
return true;
}
}
} finally {
JdbcClose.close(schemas);
}
return false;
}
/**
* Return true if the table exists.
*/
@@ -1,6 +1,6 @@
package io.ebean.config.dbplatform;
import io.ebean.config.ServerConfig;
import io.ebean.config.PlatformConfig;
import java.util.EnumMap;
import java.util.Map;
@@ -119,8 +119,8 @@ public class DbPlatformTypeMapping {
put(DbType.JSONCLOB, JSON_CLOB_PLACEHOLDER);
put(DbType.JSONBLOB, JSON_BLOB_PLACEHOLDER);
put(DbType.JSONVARCHAR, JSON_VARCHAR_PLACEHOLDER);
// use reasonable default of varchar(40) - ideally set via DatabasePlatform.configure(DbTypeConfig)
put(DbType.UUID, get(DbType.VARCHAR).withLength(40));
// default to native UUID and override on platform configure()
put(DbType.UUID, UUID_NATIVE);
}
}
@@ -198,9 +198,9 @@ public class DbPlatformTypeMapping {
/**
* Map the UUID appropriately based on native DB support and ServerConfig.DbUuid.
*/
public void config(boolean nativeUuidType, ServerConfig.DbUuid dbUuid) {
public void config(boolean nativeUuidType, PlatformConfig.DbUuid dbUuid) {
if (nativeUuidType && dbUuid.useNativeType()) {
put(DbType.UUID, UUID_NATIVE);
// native UUID already set by default
} else if (dbUuid.useBinary()) {
put(DbType.UUID, get(DbType.BINARY).withLength(16));
} else {
@@ -0,0 +1,28 @@
package io.ebean.config.dbplatform.cockroach;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
/**
* CockroachDB based platform.
*/
public class CockroachPlatform extends PostgresPlatform {
public CockroachPlatform() {
super();
this.platform = Platform.COCKROACH;
// no like escape clause supported
this.likeSpecialCharacters = new char[]{'%', '_'};
this.likeClauseRaw = "like ?";
this.likeClauseEscaped = "like ?";
}
/**
* Needs a commit after create index such that alter table add foreign key ... succeeds.
*/
@Override
public boolean isDdlCommitOnCreateIndex() {
return true;
}
}
@@ -88,6 +88,14 @@ public class PostgresPlatform extends DatabasePlatform {
return new DbPlatformType("geometry(" + type + "," + srid + ")");
}
/**
* So we can generate varchar[], int[], uuid[] column definitions and use the associated scalar types.
*/
@Override
public boolean isNativeArrayType() {
return true;
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@@ -0,0 +1,26 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
/**
* Microsoft SQL Server platform that has non-UTF8 types (char, varchar, text) and default to Identity rather than Sequence.
*/
public class SqlServer16Platform extends SqlServerBasePlatform {
public SqlServer16Platform() {
super();
this.platform = Platform.SQLSERVER16;
// default to use Identity rather than sequences
this.dbIdentity.setIdType(IdType.IDENTITY);
// non-utf8 column types
dbTypeMap.put(DbType.CHAR, new DbPlatformType("char", 1));
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("text"));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("text"));
}
}
@@ -0,0 +1,20 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
/**
* Microsoft SQL Server platform - NVarchar UTF types and Sequence preference.
*/
public class SqlServer17Platform extends SqlServerBasePlatform {
public SqlServer17Platform() {
super();
this.platform = Platform.SQLSERVER17;
this.columnAliasPrefix = null;
this.nativeUuidType = true;
this.dbTypeMap.put(DbType.UUID, new DbPlatformType("uniqueidentifier", false));
}
}
@@ -3,7 +3,7 @@ package io.ebean.config.dbplatform.sqlserver;
/**
* SQL Server platform using the older ROW_NUMBER() mechanism.
*/
public class SqlServer2005Platform extends SqlServerPlatform {
public class SqlServer2005Platform extends SqlServer16Platform {
public SqlServer2005Platform() {
this.sqlLimiter = new SqlServer2005SqlLimiter();
@@ -4,7 +4,7 @@ import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.config.PlatformConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -16,11 +16,11 @@ import javax.sql.DataSource;
import java.sql.Types;
/**
* Microsoft SQL Server platform.
* Base Microsoft SQL Server platform - NVarchar UTF types and Sequence preference by default.
*/
public class SqlServerPlatform extends DatabasePlatform {
abstract class SqlServerBasePlatform extends DatabasePlatform {
public SqlServerPlatform() {
SqlServerBasePlatform() {
super();
this.platform = Platform.SQLSERVER;
// disable persistBatchOnCascade mode for
@@ -31,7 +31,7 @@ public class SqlServerPlatform extends DatabasePlatform {
this.sqlLimiter = new SqlServerSqlLimiter();
this.basicSqlLimiter = new SqlServerBasicSqlLimiter();
this.historySupport = new SqlServerHistorySupport();
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(true);
@@ -63,22 +63,26 @@ public class SqlServerPlatform extends DatabasePlatform {
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(DbType.BLOB, new DbPlatformType("image"));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("text"));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("image"));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("text"));
dbTypeMap.put(DbType.DATE, new DbPlatformType("date"));
dbTypeMap.put(DbType.TIME, new DbPlatformType("time"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime2"));
// UTF8 aware types - overwritten in SqlServer16 platform
dbTypeMap.put(DbType.CHAR, new DbPlatformType("nchar", 1));
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("nvarchar", 255));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("nvarchar", Integer.MAX_VALUE));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("nvarchar", Integer.MAX_VALUE));
dbTypeMap.put(DbType.JSON, new DbPlatformType("nvarchar", Integer.MAX_VALUE));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("nvarchar", Integer.MAX_VALUE));
dbTypeMap.put(DbType.BLOB, new DbPlatformType("image"));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("image"));
}
@Override
public void configure(ServerConfig config) {
public void configure(PlatformConfig config) {
super.configure(config);
if (dbIdentity.getIdType() == IdType.SEQUENCE) {
this.persistBatchOnCascade = PersistBatch.ALL;
@@ -18,26 +18,26 @@ import java.util.Set;
*/
class LoadContext {
private static final Logger log = LoggerFactory.getLogger(LoadContext.class);
private static final Logger log = LoggerFactory.getLogger(LoadContext.class);
/**
* Map we are loading the properties into.
*/
private final Map<String, String> map = new LinkedHashMap<>();
private final Map<String, String> map = new LinkedHashMap<>();
/**
* Names of resources/files that were loaded.
*/
private final Set<String> loadedResources = new LinkedHashSet<>();
private final Set<String> loadedResources = new LinkedHashSet<>();
/**
* Return the input stream (maybe null) for the given source.
*/
InputStream resource(String resourcePath, Loader.Source source) {
InputStream resource(String resourcePath, Loader.Source source) {
InputStream is = null;
if (source == Loader.Source.RESOURCE) {
is = getClass().getResourceAsStream("/" + resourcePath);
is = resourceStream(resourcePath);
if (is != null) {
loadedResources.add(resourcePath);
}
@@ -46,43 +46,52 @@ class LoadContext {
if (file.exists()) {
try {
is = new FileInputStream(file);
loadedResources.add("file:"+resourcePath);
loadedResources.add("file:" + resourcePath);
} catch (FileNotFoundException e) {
throw new IllegalStateException(e);
}
}
}
return is;
}
return is;
}
private InputStream resourceStream(String resourcePath) {
InputStream is = getClass().getResourceAsStream("/" + resourcePath);
if (is == null) {
// search the module path for top level resource
is = ClassLoader.getSystemResourceAsStream(resourcePath);
}
return is;
}
/**
* Add a property entry.
*/
void put(String key, String val) {
if (val != null) {
val = val.trim();
void put(String key, String val) {
if (val != null) {
val = val.trim();
}
map.put(key, val);
}
map.put(key, val);
}
/**
* Evaluate all the expressions and return as a Properties object.
*/
Properties eval() {
Properties eval() {
log.info("loaded properties from {}", loadedResources);
log.info("loaded properties from {}", loadedResources);
Properties properties = new Properties();
Properties properties = new Properties();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = PropertyEval.eval(entry.getValue());
properties.setProperty(key, value);
}
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = PropertyEval.eval(entry.getValue());
properties.setProperty(key, value);
}
return properties;
}
return properties;
}
/**
* Read the special properties that can point to an external properties source.
@@ -16,10 +16,10 @@ import java.util.Properties;
*/
class Loader {
private static final Logger log = LoggerFactory.getLogger(Loader.class);
private static final Logger log = LoggerFactory.getLogger(Loader.class);
enum Source {
RESOURCE,
enum Source {
RESOURCE,
FILE
}
@@ -27,34 +27,36 @@ class Loader {
private YamlLoader yamlLoader;
Loader() {
try {
Class<?> exists = Class.forName("org.yaml.snakeyaml.Yaml");
if (exists != null) {
yamlLoader = new YamlLoader(loadContext);
}
} catch (Exception e) {
// ignored
}
}
Loader() {
String skipYaml = System.getProperty("ebeanSkipYaml");
if (!"true".equals(skipYaml)) {
try {
Class<?> exists = Class.forName("org.yaml.snakeyaml.Yaml");
if (exists != null) {
yamlLoader = new YamlLoader(loadContext);
}
} catch (Exception e) {
// ignored
}
}
}
/**
* Load the configuration with the expected ordering.
*/
void load() {
void load() {
loadMain(Source.RESOURCE);
// external file configuration overrides the resources configuration
loadMain(Source.FILE);
loadViaSystemProperty();
loadViaIndirection();
loadViaSystemProperty();
loadViaIndirection();
// test configuration (if found) overrides main configuration
// test configuration (if found) overrides main configuration
// we should only find these resources when running tests
loadTest();
}
loadTest();
}
/**
* Load test configuration.
@@ -87,7 +89,7 @@ class Loader {
}
private void loadViaSystemProperty() {
String fileName = System.getenv("EBEAN_PROPS_FILE");
String fileName = System.getenv("EBEAN_PROPS_FILE");
if (fileName == null) {
fileName = System.getProperty("ebean.props.file");
if (fileName != null) {
@@ -110,55 +112,66 @@ class Loader {
* Evaluate all the configuration entries and return as properties.
*/
Properties eval() {
return loadContext.eval();
}
return loadContext.eval();
}
void loadYaml(String resourcePath, Source source) {
if (yamlLoader != null) {
yamlLoader.load(resource(resourcePath, source));
}
}
void loadYaml(String resourcePath, Source source) {
if (yamlLoader != null) {
try {
try (InputStream is = resource(resourcePath, source)) {
yamlLoader.load(is);
}
} catch (Exception e) {
log.warn("Failed to read yml from:" + resourcePath, e);
}
}
}
void loadProperties(String resourcePath, Source source) {
InputStream is = resource(resourcePath, source);
if (is != null) {
loadProperties(is);
}
}
void loadProperties(String resourcePath, Source source) {
try {
try (InputStream is = resource(resourcePath, source)) {
if (is != null) {
loadProperties(is);
}
}
} catch (Exception e) {
log.warn("Failed to read properties from:" + resourcePath, e);
}
}
private InputStream resource(String resourcePath, Source source) {
return loadContext.resource(resourcePath, source);
}
private InputStream resource(String resourcePath, Source source) {
return loadContext.resource(resourcePath, source);
}
private void loadProperties(InputStream is) {
private void loadProperties(InputStream is) {
if (is != null) {
try {
Properties properties = new Properties();
properties.load(is);
put(properties);
} catch (IOException e) {
throw new RuntimeException("Failed to load properties?", e);
} finally {
close(is);
}
}
}
if (is != null) {
try {
Properties properties = new Properties();
properties.load(is);
put(properties);
} catch (IOException e) {
throw new RuntimeException("Failed to load properties?", e);
} finally {
close(is);
}
}
}
private void put(Properties properties) {
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
String property = properties.getProperty(key);
loadContext.put(key, property);
}
}
private void put(Properties properties) {
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
String property = properties.getProperty(key);
loadContext.put(key, property);
}
}
private void close(InputStream is) {
try {
is.close();
} catch (IOException e) {
log.warn("Error closing input stream for properties", e);
}
}
private void close(InputStream is) {
try {
is.close();
} catch (IOException e) {
log.warn("Error closing input stream for properties", e);
}
}
}
@@ -1,5 +1,6 @@
package io.ebean.config.properties;
import java.util.Enumeration;
import java.util.Properties;
/**
@@ -56,14 +57,18 @@ public class PropertiesLoader {
}
/**
* Set (override) a configuration property.
*
* This is expected to be only called by test configuration logic
* (ala automatic test configuration via ebean-test-config).
* Return a copy of the properties with 'eval' run on all the values.
* This resolves expressions like ${HOME} etc.
*/
public static synchronized void setProperty(String key, String value) {
load();
properties.setProperty(key, value);
}
public static Properties eval(Properties properties) {
Properties evalCopy = new Properties();
Enumeration<?> names = properties.propertyNames();
while (names.hasMoreElements()) {
String name = (String)names.nextElement();
String value = PropertyEval.eval(properties.getProperty(name));
evalCopy.setProperty(name, value);
}
return evalCopy;
}
}
@@ -21,9 +21,12 @@ class YamlLoader {
this.loadContext = loadContext;
}
@SuppressWarnings("unchecked")
void load(InputStream is) {
if (is != null) {
loadMap(yaml.load(is), null);
for (Object map : yaml.loadAll(is)) {
loadMap((Map<String, Object>)map, null);
}
}
}
@@ -93,6 +93,38 @@ public interface DbMigration {
*/
void setStrictMode(boolean strictMode);
/**
* Set to true to include a generated header comment in the DDL script.
*/
void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment);
/**
* Set the header that is included in the generated DDL script.
*/
void setHeader(String header);
/**
* Set the prefix for the version. Set this to "V" for use with Flyway.
*/
void setApplyPrefix(String applyPrefix);
/**
* Set the version of the migration to be generated.
*/
void setVersion(String version);
/**
* Set the name of the migration to be generated.
*/
void setName(String name);
/**
* Generate a migration for the version specified that contains pending drops.
*
* @param generatePendingDrop The version of a prior migration that holds pending drops.
*/
void setGeneratePendingDrop(String generatePendingDrop);
/**
* Add an additional platform to write the migration DDL.
* <p>
@@ -0,0 +1,38 @@
package io.ebean.event;
import io.ebeaninternal.server.lib.ShutdownManager;
import javax.servlet.ServletContextEvent;
/**
* Listens for webserver server starting and stopping events.
* <p>
* This should be used when the deployment is into a servlet container where the webapp
* can be shutdown or redeployed without the JVM stopping.
* </p>
* <p>
* If deployment is into a container where the JVM is completely shutdown (like spring boot,
* runnable war or when using a servlet container that only contains the single webapp and
* the JVM is shutdown then this isn't required. Instead we can just rely on the JVM shutdown
* hook that Ebean registers.
* </p>
*/
public class ServletContextListener implements javax.servlet.ServletContextListener {
/**
* The servlet container is stopping.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
ShutdownManager.shutdown();
}
/**
* Do nothing on startup.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
}
}
@@ -0,0 +1,43 @@
package io.ebean.meta;
/**
* An abstract MetricVisitor that handles the boolean flags - reset, collectTransactionMetrics and collectQueryMetrics.
*/
public abstract class AbstractMetricVisitor implements MetricVisitor {
private final boolean reset;
private final boolean collectTransactionMetrics;
private final boolean collectQueryMetrics;
public AbstractMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics) {
this.reset = reset;
this.collectTransactionMetrics = collectTransactionMetrics;
this.collectQueryMetrics = collectQueryMetrics;
}
@Override
public boolean isReset() {
return reset;
}
@Override
public boolean isCollectTransactionMetrics() {
return collectTransactionMetrics;
}
@Override
public boolean isCollectQueryMetrics() {
return collectQueryMetrics;
}
@Override
public void visitStart() {
// do nothing by default
}
@Override
public void visitEnd() {
// do nothing by default
}
}
@@ -0,0 +1,64 @@
package io.ebean.meta;
import java.util.ArrayList;
import java.util.List;
/**
* A simple MetricVisitor that can collect the desired metrics into lists.
*/
public class BasicMetricVisitor extends AbstractMetricVisitor {
private final List<MetaTimedMetric> timed = new ArrayList<>();
private final List<MetaQueryMetric> dtoQuery = new ArrayList<>();
private final List<MetaOrmQueryMetric> ormQuery = new ArrayList<>();
/**
* Construct to reset and collect everything.
*/
public BasicMetricVisitor() {
super(true, true, true);
}
/**
* Construct specifying reset and what to collect.
*/
public BasicMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics) {
super(reset, collectTransactionMetrics, collectQueryMetrics);
}
/**
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
*/
public List<MetaTimedMetric> getTimedMetrics() {
return timed;
}
/**
* Return the DTO query metrics.
*/
public List<MetaQueryMetric> getDtoQueryMetrics() {
return dtoQuery;
}
/**
* Return the ORM query metrics.
*/
public List<MetaOrmQueryMetric> getOrmQueryMetrics() {
return ormQuery;
}
@Override
public void visitTimed(MetaTimedMetric metric) {
timed.add(metric);
}
@Override
public void visitQuery(MetaQueryMetric metric) {
dtoQuery.add(metric);
}
@Override
public void visitOrmQuery(MetaOrmQueryMetric metric) {
ormQuery.add(metric);
}
}
@@ -8,18 +8,20 @@ import java.util.List;
public interface MetaInfoManager {
/**
* Collect and return the transaction execution metrics.
* Visit the metrics resetting and collecting/reporting as desired.
*/
List<MetaTimedMetric> collectTransactionStatistics(boolean reset);
void visitMetrics(MetricVisitor visitor);
/**
* Collect and return the non-empty query plan statistics for all the beans.
* <p>
* Note that this excludes the query plan statistics where there has been no
* executions (since the last collection with reset).
* </p>
* Run a visit collecting all the metrics and returning BasicMetricVisitor
* which holds all the metrics in simple lists.
*/
List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
BasicMetricVisitor visitBasic();
/**
* Just reset all the metrics. Maybe only useful for testing purposes.
*/
void resetAllMetrics();
/**
* Collect and return the ObjectGraphNode statistics.
@@ -31,6 +33,6 @@ public interface MetaInfoManager {
*
* @param reset Set to true to reset the underlying statistics after collection.
*/
List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset);
List<MetaOrmQueryNode> collectNodeStatistics(boolean reset);
}
@@ -0,0 +1,42 @@
package io.ebean.meta;
import io.ebean.ProfileLocation;
import java.util.List;
/**
* Query execution statistics for Orm queries.
*/
public interface MetaOrmQueryMetric extends MetaQueryMetric {
/**
* Return the profile location.
*/
ProfileLocation getProfileLocation();
/**
* Return true if this query plan was tuned by AutoTune.
*/
boolean isAutoTuned();
/**
* Return a string representation of the query plan hash.
*/
String getQueryPlanHash();
/**
* Return the time of the last query executed using this plan.
*/
long getLastQueryTime();
/**
* Return the 'origin' points and paths that resulted in the query being
* executed and the associated number of times the query was executed via that
* path.
* <p>
* This includes direct and lazy loading paths.
* </p>
*/
List<MetaOrmQueryOrigin> getOrigins();
}
@@ -8,10 +8,8 @@ import io.ebean.bean.ObjectGraphNode;
* These statistics can be used to identify origin queries that result in lots
* of lazy loading.
* </p>
*
* @see MetaInfoManager#collectNodeStatistics(boolean)
*/
public interface MetaObjectGraphNodeStats {
public interface MetaOrmQueryNode {
/**
* Return the ObjectGraphNode which has the origin point and relative path.
@@ -9,11 +9,8 @@ import io.ebean.bean.ObjectGraphNode;
* This basically points to the bit of original code and query that results in
* this query directly or via lazy loading.
* </p>
*
* @see MetaQueryPlanStatistic
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
*/
public interface MetaQueryPlanOriginCount {
public interface MetaOrmQueryOrigin {
/**
* The 'origin' and path which this query belongs to.
@@ -0,0 +1,23 @@
package io.ebean.meta;
/**
* Query execution metrics.
*/
public interface MetaQueryMetric extends MetaTimedMetric {
/**
* The type of entity or DTO bean.
*/
Class<?> getType();
/**
* The label for the query (can be null).
*/
String getLabel();
/**
* The actual SQL of the query.
*/
String getSql();
}
@@ -1,98 +0,0 @@
package io.ebean.meta;
import io.ebean.ProfileLocation;
import java.util.List;
/**
* Query execution statistics Meta data.
*
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
*/
public interface MetaQueryPlanStatistic {
/**
* Return the bean type this query plan is for.
*/
Class<?> getBeanType();
/**
* Return the profile location.
*/
ProfileLocation getProfileLocation();
/**
* Return true if this query plan was tuned by AutoTune.
*/
boolean isAutoTuned();
/**
* Return a string representation of the query plan hash.
*/
String getQueryPlanHash();
/**
* Return the sql executed.
*/
String getSql();
/**
* Return the total number of queries executed.
*/
long getExecutionCount();
/**
* Return the total number of beans loaded by the queries.
* <p>
* This excludes background fetching.
* </p>
*/
long getTotalLoadedBeans();
/**
* Return the total time taken by executions of this query.
*/
long getTotalTimeMicros();
/**
* Return the max execution time for this query.
*/
long getMaxTimeMicros();
/**
* Return the time collection started (or was last reset).
*/
long getCollectionStart();
/**
* Return the time of the last query executed using this plan.
*/
long getLastQueryTime();
/**
* Return the average query execution time in microseconds.
* <p>
* This excludes background fetching.
* </p>
*/
long getAvgTimeMicros();
/**
* Return the average number of bean loaded per query.
* <p>
* This excludes background fetching.
* </p>
*/
long getAvgLoadedBeans();
/**
* Return the 'origin' points and paths that resulted in the query being
* executed and the associated number of times the query was executed via that
* path.
* <p>
* This includes direct and lazy loading paths.
* </p>
*/
List<MetaQueryPlanOriginCount> getOrigins();
}
@@ -6,6 +6,11 @@ package io.ebean.meta;
*/
public interface MetaTimedMetric {
/**
* Return the metric type.
*/
MetricType getMetricType();
/**
* Return the metric name.
*/
@@ -27,17 +32,24 @@ public interface MetaTimedMetric {
long getCount();
/**
* Return the total execution time.
* Return the total execution time in micros.
*/
long getTotal();
/**
* Return the max execution time.
* Return the max execution time in micros.
*/
long getMax();
/**
* Return the mean execution time.
* Return the mean execution time in micros.
*/
long getMean();
/**
* Return the total beans or rows processed or loaded.
*
* This will be 0 if the metric isn't a query plan (like transaction execution statistics).
*/
long getBeanCount();
}
@@ -0,0 +1,30 @@
package io.ebean.meta;
/**
* The type of Metric.
*/
public enum MetricType {
/**
* Transactions.
*/
TXN,
/**
* ORM queries.
*/
ORM,
/**
* DTO queries.
*/
DTO,
/**
* SQL queries with a label will have metrics collected.
* <p>
* SqlQuery and SqlUpdate without a label have no metrics collected.
*/
SQL
}
@@ -0,0 +1,48 @@
package io.ebean.meta;
/**
* Defines visitor to read and report the transaction and query metrics.
*/
public interface MetricVisitor {
/**
* Return true if the metrics should be reset.
*/
boolean isReset();
/**
* Return true if we should visit the transaction metrics.
*/
boolean isCollectTransactionMetrics();
/**
* Return true if we should visit the ORM and SQL query metrics.
*/
boolean isCollectQueryMetrics();
/**
* Visit has started.
*/
void visitStart();
/**
* Visit transaction metrics (and L2 cache metrics in future).
*/
void visitTimed(MetaTimedMetric metric);
/**
* Visit DTO and SQL query metrics.
*/
void visitQuery(MetaQueryMetric metric);
/**
* Visit ORM query metrics.
*/
void visitOrmQuery(MetaOrmQueryMetric metric);
/**
* Visit has completed.
*/
void visitEnd();
}
@@ -0,0 +1,26 @@
package io.ebean.service;
import io.ebean.FetchGroup;
import io.ebean.FetchGroupBuilder;
/**
* Service that parses FetchGroup expressions.
*/
public interface SpiFetchGroupService {
/**
* Return the FetchGroup with the given select clause.
*
* @param beanType The type of entity bean the fetch group is for
* @param select The properties to select (top level properties)
*/
<T> FetchGroup<T> of(Class<T> beanType, String select);
/**
* Create and return a FetchGroupBuilder starting with a select() clause.
*
* @param beanType The type of entity bean the fetch group is for
* @return The FetchGroupBuilder to add additional select and fetch clauses
*/
<T> FetchGroupBuilder<T> of(Class<T> beanType);
}
@@ -2,8 +2,10 @@ package io.ebean.service;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import io.ebean.SqlRow;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Service provided by Ebean for parsing and column mapping raw SQL queries.
@@ -24,4 +26,13 @@ public interface SpiRawSqlService {
* Unparsed SQL so explicit column mapping expected.
*/
RawSqlBuilder unparsed(String sql);
/**
* Create based on a JDBC ResultSet.
*
* @param resultSet The ResultSet row to read as a SqlRow
* @param dbTrueValue The DB true value
* @param binaryOptimizedUUID Flag set to true if the UUID value is stored as optimised binary(16)
*/
SqlRow sqlRow(ResultSet resultSet, String dbTrueValue, boolean binaryOptimizedUUID) throws SQLException;
}
@@ -89,8 +89,7 @@ public class PathProperties implements FetchPath {
}
Props getProps(String path) {
Props props = pathMap.computeIfAbsent(path, p -> new Props(this, null, p));
return props;
return pathMap.computeIfAbsent(path, p -> new Props(this, null, p));
}
public Collection<Props> getPathProps() {
@@ -104,7 +104,7 @@ public class AnnotationUtil {
}
/**
* Finds all annotations recusively for a class and its superclasses.
* Finds all annotations recusively for a class and its superclasses or interfaces.
*/
public static <A extends Annotation> Set<A> findAnnotationsRecursive(Class<?> clazz, Class<A> annotationType) {
if (annotationType == null) {
@@ -112,13 +112,28 @@ public class AnnotationUtil {
}
Set<A> ret = new LinkedHashSet<>();
Set<Annotation> visited = new HashSet<>();
while (clazz != null && clazz != Object.class) {
findMetaAnnotations(clazz, annotationType, ret, visited);
Set<Class<?>> visitedInterfaces = new HashSet<>();
while (clazz != null && !clazz.getName().startsWith("java.lang.")) {
findMetaAnnotationsRecursive(clazz, annotationType, ret, visited, visitedInterfaces);
clazz = clazz.getSuperclass();
}
return ret;
}
/**
* Searches the interfaces for annotations.
*/
private static <A extends Annotation> void findMetaAnnotationsRecursive(Class<?> clazz,
Class<A> annotationType, Set<A> ret,
Set<Annotation> visited, Set<Class<?>> visitedInterfaces) {
findMetaAnnotations(clazz, annotationType, ret, visited);
for (Class<?> iface : clazz.getInterfaces()) {
if (!iface.getName().startsWith("java.lang.") && visitedInterfaces.add(iface)) {
findMetaAnnotationsRecursive(iface, annotationType, ret, visited, visitedInterfaces);
}
}
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
@@ -2,6 +2,52 @@ package io.ebean.util;
public class CamelCaseHelper {
/**
* To underscore from camel case using digits compressed true and force upper case false.
*/
public static String toUnderscoreFromCamel(String camelCase) {
return toUnderscoreFromCamel(camelCase, true, false);
}
/**
* Convert and return the string to underscore from camel case.
*/
public static String toUnderscoreFromCamel(String camelCase, boolean digitsCompressed, boolean forceUpperCase) {
int lastUpper = -1;
StringBuilder sb = new StringBuilder(camelCase.length() + 4);
for (int i = 0; i < camelCase.length(); i++) {
char c = camelCase.charAt(i);
if ('_' == c) {
// Underscores should just be passed through
sb.append(c);
lastUpper = i;
} else if (Character.isDigit(c)) {
if (i > lastUpper + 1 && !digitsCompressed) {
sb.append("_");
}
sb.append(c);
lastUpper = i;
} else if (Character.isUpperCase(c)) {
if (i > lastUpper + 1) {
sb.append("_");
}
sb.append(Character.toLowerCase(c));
lastUpper = i;
} else {
sb.append(c);
}
}
String ret = sb.toString();
if (forceUpperCase) {
ret = ret.toUpperCase();
}
return ret;
}
/**
* To camel from underscore.
*
@@ -0,0 +1,47 @@
package io.ebeaninternal.api;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
/**
* Context used to read binary format messages.
*/
public class BinaryReadContext {
private final DataInputStream in;
/**
* Create with protocol 0 and byte data.
*/
public BinaryReadContext(byte[] byteData) {
this(new DataInputStream(new ByteArrayInputStream(byteData)));
}
/**
* Create with protocol version and DataInputStream data.
*/
public BinaryReadContext(DataInputStream in) {
this.in = in;
}
public DataInputStream in() {
return in;
}
public boolean readBoolean() throws IOException {
return in.readBoolean();
}
public int readInt() throws IOException {
return in.readInt();
}
public String readUTF() throws IOException {
return in.readUTF();
}
public long readLong() throws IOException {
return in.readLong();
}
}
@@ -0,0 +1,23 @@
package io.ebeaninternal.api;
import java.io.IOException;
/**
* Messages that can be sent in binary form.
* <p>
* Mainly RemoteTransactionEvent which is sent to cluster members.
* </p>
*/
public interface BinaryWritable {
int TYPE_BEANIUD = 1;
int TYPE_TABLEIUD = 2;
int TYPE_CACHE = 3;
int TYPE_TABLEMOD = 4;
/**
* Write message in binary format.
*/
void writeBinary(BinaryWriteContext out) throws IOException;
}
@@ -0,0 +1,49 @@
package io.ebeaninternal.api;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Context used to write binary message (like RemoteTransactionEvent).
*/
public class BinaryWriteContext {
private final DataOutputStream out;
private long counter;
public BinaryWriteContext(DataOutputStream out) {
this.out = out;
}
/**
* Return the number of message parts that have been written.
*/
public long counter() {
return counter;
}
/**
* Return the output stream to write to.
*/
public DataOutputStream os() {
return out;
}
/**
* Start a message part with a given type code.
*/
public DataOutputStream start(int type) throws IOException {
counter++;
out.writeBoolean(true);
out.writeInt(type);
return out;
}
/**
* End of message parts.
*/
public void end() throws IOException {
out.writeBoolean(false);
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.persist.MultiValueWrapper;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import java.io.Serializable;
@@ -154,6 +155,10 @@ public class BindParams implements Serializable {
public void setParameter(int position, Object value) {
Param p = getParam(position);
if (value instanceof Collection) {
// use of postgres ANY with positioned parameter
value = new MultiValueWrapper((Collection)value);
}
p.setInValue(value);
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.api;
import io.ebean.EbeanServer;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.util.StringHelper;
@@ -124,11 +123,11 @@ public class LoadManyRequest extends LoadRequest {
return loadContext.getBeanProperty();
}
public SpiQuery<?> createQuery(EbeanServer server, int batchSize) {
public SpiQuery<?> createQuery(SpiEbeanServer server, int batchSize) {
BeanPropertyAssocMany<?> many = getMany();
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
SpiQuery<?> query = many.newQuery(server);
String orderBy = many.getLazyFetchOrderBy();
if (orderBy != null) {
query.orderBy(orderBy);
@@ -53,6 +53,10 @@ public class ScopeTrans {
*/
private boolean rolledBack;
/**
* Flag set when nested commit has occurred.
*/
private boolean nestedCommit;
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope) {
@@ -113,7 +117,6 @@ public class ScopeTrans {
complete();
}
/**
* Complete the transaction programmatically. Try to commit.
*/
@@ -124,7 +127,7 @@ public class ScopeTrans {
}
public void end() {
if (created) {
if (created || !nestedCommit) {
transaction.end();
}
}
@@ -133,6 +136,7 @@ public class ScopeTrans {
if (created) {
transaction.commit();
} else {
nestedCommit = true;
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
if (restoreBatch != null) {
transaction.setBatch(restoreBatch);
@@ -21,6 +21,11 @@ public class ScopedTransaction extends SpiTransactionProxy {
private ScopeTrans current;
/**
* Flag set when we clear the thread scope (on commit/rollback or end).
*/
private boolean scopeCleared;
public ScopedTransaction(TransactionScopeManager manager) {
this.manager = manager;
}
@@ -47,30 +52,51 @@ public class ScopedTransaction extends SpiTransactionProxy {
*/
public void complete(Object returnOrThrowable, int opCode) {
current.complete(returnOrThrowable, opCode);
// no finally here for pop() as we come in here twice if an
// error is thrown on commit (due to enhancement finally block)
pop();
}
/**
* Programmatic complete - finally block, try to commit.
* Internal programmatic complete - finally block, try to commit.
*/
public void complete() {
current.complete();
pop();
try {
current.complete();
} finally {
pop();
}
}
private void clearScopeOnce() {
if (!scopeCleared) {
manager.set(null);
scopeCleared = true;
}
}
private boolean clearScope() {
if (stack.isEmpty()) {
clearScopeOnce();
return true;
}
return false;
}
private void pop() {
if (!stack.isEmpty()) {
if (!clearScope()) {
current = stack.pop();
transaction = current.getTransaction();
} else {
manager.set(null);
}
}
@Override
public void end() throws PersistenceException {
current.end();
pop();
try {
current.end();
} finally {
pop();
}
}
@Override
@@ -80,17 +106,29 @@ public class ScopedTransaction extends SpiTransactionProxy {
@Override
public void commit() {
current.commitTransaction();
try {
current.commitTransaction();
} finally {
clearScope();
}
}
@Override
public void rollback() throws PersistenceException {
current.rollback(null);
try {
current.rollback(null);
} finally {
clearScope();
}
}
@Override
public void rollback(Throwable e) throws PersistenceException {
current.rollback(e);
try {
current.rollback(e);
} finally {
clearScope();
}
}
@Override
@@ -1,18 +0,0 @@
package io.ebeaninternal.api;
/**
* Plugin API available to invoke something prior to container bootup.
* <p>
* The initial intent is to provide a hook for 'docker-run' such that we can automatically ensure
* we have a test DB docker container running and setup ready to go.
* </p>
*/
public interface SpiContainerBootup {
/**
* Run something at bootup prior to the container starting.
*
* For example, start DB docker container(s).
*/
void bootup();
}
@@ -0,0 +1,52 @@
package io.ebeaninternal.api;
import io.ebean.DtoQuery;
import io.ebeaninternal.server.dto.DtoMappingRequest;
import io.ebeaninternal.server.dto.DtoQueryPlan;
/**
* Internal extension to DtoQuery.
*/
public interface SpiDtoQuery<T> extends DtoQuery<T>, SpiSqlBinding {
/**
* Return the key for query plan.
*/
String planKey();
/**
* Get the query plan for the cache.
*/
DtoQueryPlan getQueryPlan(Object planKey);
/**
* Build the query plan.
*/
DtoQueryPlan buildPlan(DtoMappingRequest request);
/**
* Put the query plan into the cache.
*/
void putQueryPlan(Object planKey, DtoQueryPlan plan);
/**
* Return true if the query is in relaxed mapping mode.
*/
boolean isRelaxedMode();
/**
* Return the label for the query.
*/
String getLabel();
/**
* Return the associated DTO bean type.
*/
Class<T> getType();
/**
* Return an underlying ORM query (if this query is built from an ORM query).
*/
SpiQuery<?> getOrmQuery();
}
@@ -1,8 +1,12 @@
package io.ebeaninternal.api;
import io.ebean.DtoQuery;
import io.ebean.EbeanServer;
import io.ebean.ExtendedServer;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
import io.ebean.RowConsumer;
import io.ebean.RowMapper;
import io.ebean.Transaction;
import io.ebean.TxScope;
import io.ebean.bean.BeanCollectionLoader;
@@ -13,18 +17,27 @@ import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetricVisitor;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.server.core.SpiResultSet;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.query.CQuery;
import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Service Provider extension to EbeanServer.
*/
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader, BeanCollectionLoader {
/**
* Return the log manager.
*/
SpiLogManager log();
/**
* Return the server extended Json context.
@@ -224,4 +237,79 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
*/
void scopedTransactionExit(Object returnOrThrowable, int opCode);
/**
* SqlQuery find single attribute.
*/
<T> T findSingleAttribute(SpiSqlQuery query, Class<T> cls);
/**
* SqlQuery find single attribute list.
*/
<T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls);
/**
* SqlQuery find one with mapper.
*/
<T> T findOneMapper(SpiSqlQuery query, RowMapper<T> mapper);
/**
* SqlQuery find list with mapper.
*/
<T> List<T> findListMapper(SpiSqlQuery query, RowMapper<T> mapper);
/**
* SqlQuery find each with consumer.
*/
void findEachRow(SpiSqlQuery query, RowConsumer consumer);
/**
* DTO findList query.
*/
<T> List<T> findDtoList(SpiDtoQuery<T> query);
/**
* DTO findOne query.
*/
<T> T findDtoOne(SpiDtoQuery<T> query);
/**
* DTO findEach query.
*/
<T> void findDtoEach(SpiDtoQuery<T> query, Consumer<T> consumer);
/**
* DTO findEachWhile query.
*/
<T> void findDtoEachWhile(SpiDtoQuery<T> query, Predicate<T> consumer);
/**
* Return / wrap the ORM query as a DTO query.
*/
<D> DtoQuery<D> findDto(Class<D> dtoType, SpiQuery<?> ormQuery);
/**
* Execute the underlying ORM query returning as a JDBC ResultSet to map to DTO beans.
*/
SpiResultSet findResultSet(SpiQuery<?> ormQuery, SpiTransaction transaction);
/**
* Visit all the metrics (typically reporting them).
*/
void visitMetrics(MetricVisitor visitor);
/**
* Return true if a row for the bean type and id exists.
*/
boolean exists(Class<?> beanType, Object beanId, Transaction transaction);
/**
* Add to JDBC batch for later execution.
*/
void addBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
/**
* Execute the batched statement.
*/
int[] executeBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
}
@@ -1,8 +1,8 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.core.DbExpressionHandler;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import java.util.List;
@@ -1,9 +1,11 @@
package io.ebeaninternal.api;
import com.fasterxml.jackson.core.JsonGenerator;
import io.ebean.plugin.BeanType;
import io.ebean.text.json.JsonContext;
import io.ebean.text.json.JsonWriteOptions;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import java.io.Writer;
@@ -21,4 +23,10 @@ public interface SpiJsonContext extends JsonContext {
* Create a Json Writer for writing beans as JSON supplying a writer.
*/
SpiJsonWriter createJsonWriter(Writer writer);
/**
* Create a Json Reader for reading the JSON content.
*/
SpiJsonReader createJsonRead(BeanType<?> beanType, String json);
}
@@ -0,0 +1,27 @@
package io.ebeaninternal.api;
/**
* Log manager for SQL, TXN and Summary logging.
* <p>
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
* the logged SQL during testing such that we can assert against the executed sql if desired.
* </p>
*/
public interface SpiLogManager {
/**
* Return the SQL logger.
*/
SpiLogger sql();
/**
* Return the TXN logger.
*/
SpiLogger txn();
/**
* Return the Summary logger.
*/
SpiLogger sum();
}
@@ -0,0 +1,31 @@
package io.ebeaninternal.api;
/**
* Logger for SQL, TXN and Summary logging.
* <p>
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
* the logged SQL during testing such that we can assert against the executed sql if desired.
* </p>
*/
public interface SpiLogger {
/**
* Is debug logging enabled.
*/
boolean isDebug();
/**
* Is trace logging enabled.
*/
boolean isTrace();
/**
* Log a debug level message.
*/
void debug(String msg);
/**
* Log a trace level message.
*/
void trace(String msg);
}
@@ -0,0 +1,12 @@
package io.ebeaninternal.api;
/**
* API for Plugins to define the logger for io.ebean.SQL, io.ebean.TXN and io.ebean.SUM.
*/
public interface SpiLoggerFactory {
/**
* Create the logger given the name.
*/
SpiLogger create(String name);
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.api;
import io.ebean.CacheMode;
import io.ebean.CountDistinctOrder;
import io.ebean.ExpressionList;
import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
@@ -178,6 +179,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
ProfileLocation getProfileLocation();
/**
* Return the label set on the query.
*/
String getLabel();
/**
* Return true if this is a "find by id" query. This includes a check for a single "equal to" expression for the Id.
*/
@@ -214,6 +220,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
BeanDescriptor<T> getBeanDescriptor();
/**
* Return the query plan key.
*/
Object getQueryPlanKey();
/**
* Return the RawSql that was set to use for this query.
*/
@@ -633,9 +644,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
boolean isBeanCachePut();
/**
* Return true if the bean cache is being explicitly loaded via RECACHE mode.
* Return true if we must hit the DB (Cache reload or select for update).
*/
boolean isBeanCacheReload();
boolean isForceHitDatabase();
/**
* Return the cache mode for using the bean cache (Get and Put).
@@ -747,6 +758,16 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
boolean isDistinct();
/**
* Return true if the Id property is manually included in the query (DTO queries).
*/
boolean isManualId();
/**
* Set to true when we only include the Id property if it is explicitly included in the select().
*/
void setManualId(boolean manualId);
/**
* Set default select clauses where none have been explicitly defined.
*/
@@ -822,4 +843,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
* Simplify nested expression lists where possible.
*/
void simplifyExpressions();
/**
* Returns the count distinct order setting.
*/
CountDistinctOrder getCountDistinctOrder();
}
@@ -0,0 +1,43 @@
package io.ebeaninternal.api;
/**
* SQL query binding (for SqlQuery and DtoQuery).
*/
public interface SpiSqlBinding {
/**
* Return the named or positioned parameters.
*/
BindParams getBindParams();
/**
* return the query.
*/
String getQuery();
/**
* Return the label (to collect metrics on when set).
*/
String getLabel();
/**
* Return the first row to fetch.
*/
int getFirstRow();
/**
* Return the maximum number of rows to fetch.
*/
int getMaxRows();
/**
* Return the query timeout.
*/
int getTimeout();
/**
* Return the hint for Statement.setFetchSize().
*/
int getBufferFetchSizeHint();
}
@@ -5,36 +5,6 @@ import io.ebean.SqlQuery;
/**
* SQL query - Internal extension to SqlQuery.
*/
public interface SpiSqlQuery extends SqlQuery {
/**
* Return the named or positioned parameters.
*/
BindParams getBindParams();
/**
* return the query.
*/
String getQuery();
/**
* Return the first row to fetch.
*/
int getFirstRow();
/**
* Return the maximum number of rows to fetch.
*/
int getMaxRows();
/**
* Return the query timeout.
*/
int getTimeout();
/**
* Return the hint for Statement.setFetchSize().
*/
int getBufferFetchSizeHint();
public interface SpiSqlQuery extends SqlQuery, SpiSqlBinding {
}
@@ -23,4 +23,9 @@ public interface SpiSqlUpdate extends SqlUpdate {
* Set the generated key value.
*/
void setGeneratedKey(Object idValue);
/**
* Reset bind position to be ready for another bind execute.
*/
void reset();
}
@@ -101,6 +101,11 @@ public interface SpiTransaction extends Transaction {
*/
String getId();
/**
* Return the start timestamp for the transaction (JVM side).
*/
long getStartMillis();
/**
* Return true if this transaction has updateAllLoadedProperties set.
* If null is returned the server default is used (set on ServerConfig).
@@ -179,7 +184,7 @@ public interface SpiTransaction extends Transaction {
* Return true if this request should be batched. Conversely returns false
* if this request should be executed immediately.
*/
boolean isBatchThisRequest(PersistRequest.Type type);
boolean isBatchThisRequest();
/**
* Return the BatchControl used to batch up persist requests.
@@ -319,4 +324,9 @@ public interface SpiTransaction extends Transaction {
* Return the profile location for this transaction.
*/
ProfileLocation getProfileLocation();
/**
* Return true when nested transactions should create Savepoints.
*/
boolean isNestedUseSavepoint();
}
@@ -21,7 +21,7 @@ import java.sql.SQLException;
/**
* Proxy for an underlying SpiTransaction (most of the API).
*/
abstract class SpiTransactionProxy implements SpiTransaction {
public abstract class SpiTransactionProxy implements SpiTransaction {
protected SpiTransaction transaction;
@@ -30,6 +30,11 @@ abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.translate(message, cause);
}
@Override
public long getStartMillis() {
return transaction.getStartMillis();
}
@Override
public void setLabel(String label) {
transaction.setLabel(label);
@@ -50,6 +55,16 @@ abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.isRollbackOnly();
}
@Override
public void setNestedUseSavepoint() {
transaction.setNestedUseSavepoint();
}
@Override
public boolean isNestedUseSavepoint() {
return transaction.isNestedUseSavepoint();
}
@Override
public long profileOffset() {
return transaction.profileOffset();
@@ -235,6 +250,11 @@ abstract class SpiTransactionProxy implements SpiTransaction {
transaction.setBatchMode(useBatch);
}
@Override
public boolean isBatchMode() {
return transaction.isBatchMode();
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
transaction.setBatch(persistBatchMode);
@@ -245,11 +265,21 @@ abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.getBatch();
}
@Override
public void setBatchOnCascade(boolean batchMode) {
transaction.setBatchOnCascade(batchMode);
}
@Override
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
transaction.setBatchOnCascade(batchOnCascadeMode);
}
@Override
public boolean isBatchOnCascade() {
return transaction.isBatchOnCascade();
}
@Override
public PersistBatch getBatchOnCascade() {
return transaction.getBatchOnCascade();
@@ -346,8 +376,8 @@ abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public boolean isBatchThisRequest(PersistRequest.Type type) {
return transaction.isBatchThisRequest(type);
public boolean isBatchThisRequest() {
return transaction.isBatchThisRequest();
}
@Override
@@ -43,6 +43,11 @@ public interface SpiUpdate<T> extends Update<T> {
*/
Class<?> getBeanType();
/**
* Return the label (for metrics collection).
*/
String getLabel();
/**
* Return the type of this - insert, update or delete.
*/

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