Compare commits

...
Author SHA1 Message Date
rob bygrave 0edf90056c [maven-release-plugin] prepare release ebean-11.32.1 2019-01-10 03:44:44 +13:00
Rob BygraveandGitHub 6b27a9d280 Support @ManyToOne inside @Embeddables - fix for #52 (#1608)
* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Update test with sql capture

* #52 - @ManyToOne support inside @Embeddables

Update test with cache clearing before hand (such that we hit DB on lazy load)
2019-01-10 03:40:02 +13:00
rob bygrave 2b0669c2ae #1594 - Support loading of beans with invalid JSON content
Adjust to be per query via query.setAllowLoadErrors() rather than a
global config via ServerConfig.
2019-01-09 20:03:26 +13:00
rob bygrave 9fc10e8e04 Merge branch 'FOCONIS-feature/load-error' 2019-01-09 19:26:13 +13:00
rob bygrave fb5eb0cdd2 Merge branch 'feature/load-error' of https://github.com/FOCONIS/ebean into FOCONIS-feature/load-error 2019-01-09 19:25:50 +13:00
rob bygrave 3351882915 #1602 - Adjust test adding sql checking for discriminator 2019-01-09 19:20:05 +13:00
Roland PramlandRob Bygrave 31c6d16e73 A query on inherited beans can be restricted to the subtype of one of its children (#1602)
ENH: A query on inherited beans can be restricted to the subtype of one of its children
2019-01-09 19:15:04 +13:00
rob bygrave cd8c12a9a8 #1566 - ENH: Support query.setBaseTable() ... to query against dynamic table (ala manual table partitioning) 2019-01-09 19:13:35 +13:00
rob bygrave 3207ae1f08 No effective change - tidy test 2019-01-09 19:05:16 +13:00
Roland PramlandRob Bygrave 5fe532c393 Refactor: relpaced system-property "ebean.ignoreExtraDdl" with serverConfig flag (#1597) 2019-01-09 16:17:11 +13:00
rob bygrave 93b9cbf9ac bump to 11.32.1-SNAPSHOT 2019-01-09 14:01:25 +13:00
rob bygrave 8a34d6050b #1607 - ENH: Add query.exists() query ... as a convenience for "does a row exist" check 2019-01-09 13:59:53 +13:00
rob bygrave ab3e6ccbf7 [maven-release-plugin] prepare for next development iteration 2019-01-09 12:43:01 +13:00
rob bygrave cc58fc8195 [maven-release-plugin] prepare release ebean-11.31.3 2019-01-09 12:42:48 +13:00
rob bygrave 1c34c09f3c #1606 - Bean with Inheritance and OneToMany with JoinTable and cascade ... doesn't cascade save on all bean types 2019-01-09 12:37:13 +13:00
rob bygrave 8544712448 #1606 - Bean with Inheritance and OneToMany with JoinTable and cascade ... doesn't cascade save on all bean types 2019-01-09 12:29:28 +13:00
rob bygrave 5e22079366 [maven-release-plugin] prepare for next development iteration 2019-01-08 23:04:14 +13:00
rob bygrave 903b0a3929 [maven-release-plugin] prepare release ebean-11.31.2 2019-01-08 23:03:59 +13:00
Roland PramlandRob Bygrave b18bc82b8a FIX: possible NPE in DeleteUnloadedForeignKeys.deleteCascade and in initPostTarget (#1601) 2019-01-08 22:41:43 +13:00
Roland PramlandRob Bygrave 50de32af29 Fixed tests, if SqlServer has "Latin1_General_100_BIN2" collation. (#1600)
This means, all names are interpreted case sensitive by the db
2019-01-08 22:40:14 +13:00
Roland PramlandRob Bygrave 0b18189e62 exposed the schedule methods in BackgroundExecutor (#1599) 2019-01-08 22:39:08 +13:00
Roland PramlandRob Bygrave 0b6e73d729 FIX: create uuid-state file in user's home directory (#1598) 2019-01-08 22:37:42 +13:00
Roland PramlandRob Bygrave 3108076953 SqlServer queryplan captured in transaction & fix of possible NPE (#1592)
* SqlServer queryplan captured in transaction & fix of possible NPE

* rolled back transaction earlier
2019-01-08 22:15:31 +13:00
Roland PramlandRob Bygrave 7ed4435ccc Fix some testcases fof MySql/SqlServer (#1591)
* FIX testcases for MariaDB+utf8mb4  (Failure was: "Specified key was too long; max key length is 767 bytes")

* Excluded tests that won't work on SqlServers generated ids
2019-01-08 22:13:55 +13:00
rob bygrave 7aef4ff1bd #1605 - RuntimeException: Nothing in batch? ... when lazy loading on partially populated L2 bean cache 2019-01-08 21:44:18 +13:00
rob bygrave 5bcad17ae1 No effective change - add a test for filterMany with isNotEmpty() 2019-01-08 16:50:28 +13:00
rob bygrave b1beb0b787 #1596 - NullPointerException at findOne() with filterMany() 2019-01-07 22:21:54 +13:00
rob bygrave 585eb2b57b #1604 - Open URL resources without caching 2019-01-07 22:10:17 +13:00
rob bygrave 2653ec50aa #1604 - Open URL resources without caching 2019-01-07 21:52:55 +13:00
rob bygrave cb4432a588 No effective change - Bump agent to 11.27.1 2019-01-07 21:46:14 +13:00
Roland Praml 4d93b70bf9 Supports loading of corrupt beans 2019-01-03 11:43:50 +01:00
rob bygrave 0b299edf0d [maven-release-plugin] prepare for next development iteration 2018-12-21 20:56:41 +13:00
rob bygrave cf10246783 [maven-release-plugin] prepare release ebean-11.31.1 2018-12-21 20:56:26 +13:00
rob bygrave babba914a0 No effective change - add toString() for ServerCacheNotification 2018-12-21 17:14:29 +13:00
rob bygrave 0397140e0b #1589 - Refactor internals move SqlQuery sqlDistinct ... to SqlTreeNodeRoot (query plan) 2018-12-21 15:57:07 +13:00
rob bygrave 82def2710e No effective change - adjust test to have at least 1 column on insert 2018-12-21 15:51:09 +13:00
rob bygrave 60ad417e1b No effective change - adjust test to have at least 1 column on insert 2018-12-21 15:21:01 +13:00
rob bygrave 3af1b574fd #1588 -Fix query plan capture excluding update and delete queries from query plan capture 2018-12-21 14:31:49 +13:00
rob bygrave 60c8683914 No effective change - test assert to contains rather than containsExactly 2018-12-21 14:29:50 +13:00
rob bygrave 72263c0af0 No effective change - test assert to contains rather than containsExactly 2018-12-21 13:57:00 +13:00
rob bygrave f66316138d #1584 - Fix for - Query cache bug, which produces wrong results 2018-12-21 13:53:05 +13:00
Roland PramlandRob Bygrave b403942491 Query cache bug, which produces wrong results (#1584) 2018-12-21 11:55:07 +13:00
rob bygrave baa3909c63 #1586 - ENH: Add TimedMetric.addSinceNanos() ... as a helper when using start nanos 2018-12-21 02:28:23 +13:00
rob bygrave 81f42f8e95 #1587 - Fix for propagation of @Cache(nearCache=true) 2018-12-21 02:19:21 +13:00
rob bygrave 0377e3ec5c #1586 - ENH: Add TimedMetric.addSinceNanos() ... as a helper when using start nanos 2018-12-21 02:10:16 +13:00
rob bygrave 58f6c67d78 #1585 - Change the L2 bean cache keys to be String based 2018-12-21 01:54:46 +13:00
rob bygrave 84f7fc5d81 No effective change - javadoc for ServerConfig getServiceObject 2018-12-19 21:20:37 +13:00
rob bygrave ea02bebc6f [maven-release-plugin] prepare for next development iteration 2018-12-19 21:16:42 +13:00
rob bygrave da874d84a8 [maven-release-plugin] prepare release ebean-11.30.1 2018-12-19 21:16:27 +13:00
rob bygrave 049cacad1c #1583 - ENH: Add type safe ServerConfig getServiceObject(Class<P> cls) and putServiceObject() 2018-12-19 21:14:25 +13:00
rob bygrave ced8c79458 #1582 - ENH: Pass @Cache nearCache option to ServerCacheOptions (for L2 plugins like Redis) 2018-12-19 20:57:45 +13:00
rob bygrave a66edf8077 #1477 Query plan capture - use explain analyze for Postgres
... so that we also get DB side timing
2018-12-19 16:55:24 +13:00
Rob BygraveandGitHub 94591954b3 1476 - Query plan capture, initial wip capturing bind params (#1477)
* 1476 - Query plan capture, initial wip capturing bind params

* No effective change - Reset test default db to H2

* #1476 - Query plan capture, initial wip capturing bind params #1477

Update with consumer

* #1476 - Query plan capture, initial wip capturing bind params #1477

Update with query plan request

* #1476 - Query plan capture, initial wip capturing bind params #1477

Extract interface

* #1476 - Query plan capture, initial wip capturing bind params #1477

Extract interface
2018-12-19 16:04:28 +13:00
rob bygrave e611353163 No effective change - update javadoc for QueryIterator to use try with resources 2018-12-19 15:22:32 +13:00
rob bygrave e7811910e9 No effective change - update javadoc 2018-12-19 15:15:38 +13:00
rob bygrave 138b587ff7 #1581 - ENH: Add query.asUpdate() ... to convert a query into an UpdateQuery (typically with query beans) 2018-12-19 14:45:33 +13:00
rob bygrave 8fc6c4c648 #1580 - Refactor move io.ebeaninternal.metric to public package of io.ebean.metric (such that it can be used by plugins like redis l2 cache plugin) 2018-12-19 14:16:36 +13:00
rob bygrave ebdc28e1cd #1579 - ENH: for UpdateQuery add setLabel() and update() as top level methods 2018-12-19 13:58:28 +13:00
rob bygrave 51f79e769c [maven-release-plugin] prepare for next development iteration 2018-12-17 23:39:34 +13:00
rob bygrave c2a69714ca [maven-release-plugin] prepare release ebean-11.29.1 2018-12-17 23:39:18 +13:00
rob bygrave f3213e7b90 Bump to 11.29.1-SNAPSHOT 2018-12-17 23:19:43 +13:00
rob bygrave 55de997297 #1578 - Remove unmaintained META-INF/ebean-version.mf manifest file ... use EbeanVersion.getVersion() instead 2018-12-17 23:18:29 +13:00
rob bygrave 65890f9777 No effective change - remove unused resources from test resources 2018-12-17 23:14:51 +13:00
rob bygrave 41c1e7260c #1577 - ENH: Add support for "dbinit" migrations (Sort of "checkpoint" or "fast forward" migrations) 2018-12-17 23:11:13 +13:00
rob bygrave 7d35789db4 #1573 - Incorrect CamelCase conversion when number proceeds uppercase letter 2018-12-16 16:12:34 +13:00
rob bygrave d266d6d440 #1574 - "NullPointerException" in query.copy().findList() but not in query.findList() 2018-12-16 00:29:18 +13:00
rob bygrave 6d5374e66a #1576 - ClusterManager creates ClusterBroadcast even when config is not active 2018-12-15 23:17:00 +13:00
rob bygrave 8410de8831 #1573 - Incorrect CamelCase conversion when number proceeds uppercase letter 2018-12-13 00:19:40 +13:00
rob bygrave 4b0230e862 Refactor - reduce visibility on DefaultDbMigration 2018-12-11 21:03:45 +13:00
rob bygrave fa96f81f8c [maven-release-plugin] prepare for next development iteration 2018-12-06 00:19:14 +13:00
rob bygrave a5fea5e9be [maven-release-plugin] prepare release ebean-11.28.3 2018-12-06 00:18:53 +13:00
rob bygrave bafba0babf #1572 - Support query.select("customer.id").findSingleAttributeList() ... in addition to the expected and working select("customer").findSingleAttributeList() 2018-12-06 00:15:06 +13:00
rob bygrave e4088c5fbe #1571 - NPE in cacheable bean with embeddedid on delete 2018-12-05 23:03:15 +13:00
RienandRob Bygrave 0405ceb96c NPE in cacheable bean with embeddedid on delete (#1570) 2018-12-05 21:49:27 +13:00
rob bygrave ebf96c87bf [maven-release-plugin] prepare for next development iteration 2018-12-03 22:14:59 +13:00
rob bygrave fcaf11f262 [maven-release-plugin] prepare release ebean-11.28.2 2018-12-03 22:14:46 +13:00
rob bygrave 05df47c0d7 #1569 - Ebean generates wrong ddl (datetime(6) vs timestamp) for H2 with @History annotation 2018-12-03 22:11:47 +13:00
rob bygrave a8892292fb #1569 - Ebean generates wrong ddl (datetime(6) vs timestamp) for H2 with @History annotation 2018-12-03 22:05:18 +13:00
rob bygrave e876ef8d87 #1559 - @Lob with @ElementCollection 2018-12-03 21:07:33 +13:00
rob bygrave 315b39813b #1568 - @OneToMany with orphanRemoval=true ... with @Cache (and not a @ManyToOne) doesn't remove orphans after cache hit 2018-11-30 23:46:11 +13:00
rob bygrave 6d5e0c4766 Add test only - Add test for L2 cache with orphanRemoval 2018-11-30 23:41:23 +13:00
rob bygrave a2ce17aec0 Add test only - Add test for L2 cache with orphanRemoval 2018-11-30 22:42:36 +13:00
rob bygrave fcffa92c55 #1567 - SqlUpdate addBatch() ... with named parameters errors with PersistenceException: No batched statement found for key 2018-11-30 21:47:23 +13:00
rob bygrave 7fda8079c2 [maven-release-plugin] prepare for next development iteration 2018-11-30 09:55:20 +13:00
rob bygrave cca9bb4427 [maven-release-plugin] prepare release ebean-11.28.1 2018-11-30 09:55:06 +13:00
rob bygrave f45089240a #1565 - Bump to ebean-datasource 4.3.1 ... with option for owner database credentials and InitDatabase 2018-11-30 09:44:07 +13:00
rob bygrave 8252a51545 #1565 - Bump to ebean-datasource 4.3.1 ... with option for owner database credentials and InitDatabase 2018-11-29 10:16:48 +13:00
rob bygrave c440c768cf #1564 - Remove deprecated io.ebeaninternal.server.core.ServletContextListener - migrate to io.ebean.event.ServletContextListener 2018-11-29 10:01:56 +13:00
rob bygrave c6ae501354 #1563 - Remove deprecated noop method ServerConfig.loadTestProperties() - migrate to loadProperties() 2018-11-29 09:59:16 +13:00
rob bygrave 70e2b1c2ae #1562 - Query cache use incorrect with RawExpression - incorrect bind hash 2018-11-29 09:54:24 +13:00
rob bygrave 211f28eea3 [maven-release-plugin] prepare for next development iteration 2018-11-27 14:58:40 +13:00
rob bygrave 174fe46485 [maven-release-plugin] prepare release ebean-11.27.1 2018-11-27 14:58:26 +13:00
rob bygrave 890cc6f66d Bump to 11.27.1-SNAPSHOT 2018-11-27 14:53:06 +13:00
rob bygrave 2f0e5e6e46 #1561 - ENH: Add ScriptRunner API ... for easier running of SQL and DDL scripts (for testing usually) 2018-11-27 14:50:48 +13:00
rob bygrave 54ba78218b #1558 - Bump ebean-migration to 11.11.1 - refer: https://github.com/ebean-orm/ebean-migration/pull/56 2018-11-23 14:36:41 +13:00
rob bygrave ba16cea405 #1556 - Query.setDisableLazyLoading(true) not working on @OneToMany Set (but does on List) 2018-11-23 12:20:18 +13:00
rob bygrave e81a61378c No effective change - javadoc 2018-11-23 11:37:38 +13:00
rob bygrave b4154031cc #1555 - Support .yaml extension (in addition to the existing .yml extension) for application.yaml and application-test.yaml 2018-11-20 11:51:27 +13:00
rob bygrave ecf5b56652 #1555 - Support .yaml extension (in addition to the existing .yml extension) for application.yaml and application-test.yaml 2018-11-20 11:45:45 +13:00
rob bygrave 426c6c8111 #1554 - Improve exception message for BeanNotEnhancedException 2018-11-20 11:33:16 +13:00
rob bygrave 9dbb69f4f4 #1553 - Remove deprecated method Query.setForUpdate(boolean) ... migrate to setForUpdate() 2018-11-19 23:40:00 +13:00
rob bygrave 83776a1eae #1552 - Remove deprecated methods on Transaction setBatch(PersistBatch) .. migrate to setBatchMode(boolean) 2018-11-19 23:36:15 +13:00
rob bygrave 6bc72ae6bb No effective change - format only 2018-11-19 22:25:01 +13:00
Roland PramlandRob Bygrave 4fe2016891 new comparison method Ine (caseInsensitive notEquals) (#1551) 2018-11-19 22:22:46 +13:00
Roland PramlandRob Bygrave 7be4675d3e NEW: ExpressionPath provides access to property and elName (#1550) 2018-11-19 22:16:01 +13:00
rob bygrave 06b71781f5 #1548 add global configuration disableLazyLoading
Add javadoc
2018-11-19 22:08:50 +13:00
rob bygrave 55e4c7bc26 #1548 add global configuration disableLazyLoading
Fix tests
2018-11-19 22:03:51 +13:00
ooknightandRob Bygrave b3584a81b4 add global configuration disableLazyLoading (#1548) 2018-11-19 21:45:05 +13:00
rob bygrave 68da8944ed [maven-release-plugin] prepare for next development iteration 2018-11-14 09:34:02 +13:00
rob bygrave 903ee35d63 [maven-release-plugin] prepare release ebean-11.26.1 2018-11-14 09:33:50 +13:00
rob bygrave 658ee3d39f Bump to 11.26.1-SNAPSHOT 2018-11-14 09:32:25 +13:00
rob bygrave c074b7c457 #1547 - Revert for - FIX for the limit query #1540 2018-11-14 09:29:36 +13:00
rob bygrave 528ad7c7fb [maven-release-plugin] prepare for next development iteration 2018-11-13 23:58:37 +13:00
rob bygrave c3f41f4157 [maven-release-plugin] prepare release ebean-11.25.1 2018-11-13 23:58:22 +13:00
rob bygrave 33d104e6aa Bump version to 11.25.1-SNAPSHOT 2018-11-13 23:52:35 +13:00
rob bygrave 4bd3a0f293 #1546 - Refactor: Swap from entityBean._ebean_intercept() to entityBean._ebean_getIntercept() 2018-11-13 23:50:13 +13:00
rob bygrave 54a4fd75f1 #1539 - FIX: Pairs must not be modified by query
Remove the (|| multiValueSupported) part
2018-11-13 23:44:16 +13:00
Roland PramlandRob Bygrave 105f08cca1 FIX: Pairs must not be modified by query (#1539)
* FIX: Pairs must not be modified by query

* Modification can also occur on inValues.

* fixed tests

* removed obsolete line
2018-11-13 23:41:36 +13:00
rob bygrave 01d9dd3fe8 #1533 - Enhanced bean can not use final fields in Java 9+. 2018-11-13 23:37:45 +13:00
rob bygrave f09b38c58c #1545 - Bump ebean-datasource to 4.2.2 fix for leak with SQLWarning and Postgres JDBC driver 2018-11-13 21:33:44 +13:00
rob bygrave cb6d2e7ec0 Merge branch 'master' of github.com:ebean-orm/ebean 2018-11-13 21:27:13 +13:00
rob bygrave 89c0324e42 #1544 - No effective change - use https://ebean.io for urls 2018-11-13 21:26:56 +13:00
Roland PramlandRob Bygrave 7b4215f4f8 Fix test data (#1541)
* FIX: Put correct dataType for anniversary

* NEW: Add some deterministic ship dates to reference data
2018-11-13 21:09:53 +13:00
Roland PramlandRob Bygrave e4cb5f0443 FIX for the limit query (#1540) 2018-11-13 21:08:53 +13:00
Roland PramlandRob Bygrave ec24374d72 Add some sanity checks when creating sort comparators (#1538) 2018-11-13 20:55:09 +13:00
Roland PramlandRob Bygrave cfba4d2deb Add Java 8 Time API support for BasicTypeConverter (#1537) 2018-11-13 20:53:38 +13:00
rob bygrave 0ab019ca2c Merge branch 'FOCONIS-extrawhere-on-join' 2018-11-13 20:51:55 +13:00
rob bygrave 8c9dae0f88 Merge branch 'extrawhere-on-join' of https://github.com/FOCONIS/ebean into FOCONIS-extrawhere-on-join 2018-11-13 20:51:29 +13:00
Roland PramlandRob Bygrave 88a0c34d37 FIX: use outer join on "is null" check (#1535) 2018-11-13 20:46:18 +13:00
Roland PramlandRob Bygrave ce403e72a7 FIX: improved support for @DbForeignKey(noConstraint = true) (#1527)
* FIX: improved support for DbForeignKey(noConstraint = true)

* FIX: Tests for mariadb and sqlserver

* FIX: SqlServer was still incorrect

* FIX: not ommit property for formula

* Update BeanDescriptor.java (typo fix)

fix typo column (no code change!)
2018-11-13 20:44:45 +13:00
rob bygrave 3c6a52da31 #1531 - Transform extra-ddl.xml files when running in ddl mode (no migration)
Update to ebean-migration to with ScriptTransform as public and reuse here in Ebean
2018-11-13 20:39:15 +13:00
Manuel DanischandRob Bygrave 2806972032 Transform extra-ddl.xml files when running in ddl mode (no migration) (#1531) 2018-11-13 20:20:01 +13:00
rob bygrave acd6ba0a42 Merge branch 'master' of github.com:ebean-orm/ebean 2018-11-13 17:52:09 +13:00
rob bygrave b386c9a5a8 #1543 - ENH: Allow ebean.dbSchema to have multiple schemas 2018-11-13 17:51:57 +13:00
Roland Praml c5536aaeec FIX: append extraWhere to join 2018-11-10 23:42:02 +01:00
Roland Praml 1b2bfc5df8 FIX: use outer join on "is null" check 2018-11-10 23:32:24 +01:00
Roland PramlandRob Bygrave 808004165c Code cleanup (#1534)
* removed unused imports (no code change)

* Add missing @Override / @SupressWarnings (no code change)

* FIX: serialization warnings

* FIX: Access to static member
2018-11-10 20:07:01 +13:00
Roland PramlandRob Bygrave 61eedb4e9a Fixed tests that modify BasicData (#1530) 2018-11-03 16:38:26 +13:00
Roland PramlandRob Bygrave 662ffdd070 FIX: update extra ddl also, if no migration was written (#1529) 2018-11-03 16:26:21 +13:00
Roland PramlandRob Bygrave 13a5511318 FIX: disableLazyLoading was not checked correctly (#1528) 2018-11-03 16:23:27 +13:00
Roland PramlandRob Bygrave 0f9c98964e FIX Typos (DLL -> DDL) (#1526) 2018-11-02 09:21:31 +13:00
rob bygrave b87e6d90f6 [maven-release-plugin] prepare for next development iteration 2018-11-01 17:22:36 +13:00
rob bygrave 166906a814 [maven-release-plugin] prepare release ebean-11.24.1 2018-11-01 17:22:22 +13:00
rob bygrave 1733d2382c Bump agent to 11.24.1 2018-11-01 17:20:41 +13:00
rob bygrave a6f9120905 #1525 - Check for presence of JAXB in classpath in CurrentModel (to support Java 11 without xml) 2018-11-01 13:31:08 +13:00
rob bygrave 32b599512b #1524 - @History on @OneToMany with asOf() query and fetch(many property) gives PersistenceException: Error with property[6] dt[93]data[2018-11-01 14:56:18.015][java.sql.Timestamp] 2018-11-01 00:48:46 +13:00
rob bygrave 34fd24d620 No effective change - add back SQL logging 2018-11-01 00:48:02 +13:00
Rob BygraveandGitHub f928923b5b #1519 - Memory leak when using @Cache(enableQueryCache=true) (#1523)
* #1519 - Memory leak when using @Cache(enableQueryCache=true)

* #1519 - Memory leak when using @Cache(enableQueryCache=true)

Remove unused TransactionEventBeans
2018-10-31 23:53:16 +13:00
rob bygrave 1f928e65a4 #1521 - Add API for using explicit transaction for UpdateQuery - e.g. ebeanServer.update(Customer.class)...update(activeTransaction); 2018-10-30 13:39:54 +13:00
rob bygrave d920d8eb76 #1522 - Refactor - Change io.ebean.Model to not be a annotated with @MappedSuperclass 2018-10-30 13:38:57 +13:00
Michael BenzandRob Bygrave 23d2c64046 JavaDoc Fix closing h3 tag in Transaction. no code change! (#1520) 2018-10-30 12:55:58 +13:00
rob bygrave 19d29cd836 #1514 - Add support for JacksonAnnotations at bean-properties
Fix where Jackson is not present on the classpath
2018-10-26 14:50:17 +13:00
rob bygrave f14c3e8cb4 Merge branch 'master' of github.com:ebean-orm/ebean 2018-10-26 01:34:36 +13:00
rob bygrave b9ca8be95a #1517 - Update for ebean-datasource maven groupId change to io.ebean 2018-10-26 01:34:16 +13:00
rob bygrave d627bb47e2 #1516 - More user friendly exceptions - BeanNotEnhancedException, BeanNotRegisteredException 2018-10-26 01:33:24 +13:00
rob bygrave d395c666fc #1516 - More user friendly exceptions - BeanNotEnhancedException, BeanNotRegisteredException 2018-10-26 01:32:22 +13:00
Roland PramlandRob Bygrave 939397acfc Add support for JacksonAnnotations at bean-properties (#1514) 2018-10-25 22:11:36 +13:00
Roland PramlandRob Bygrave fecb5fa731 There should be only one "Rob" in o_customer (#1515)
* FIX: CsvReader-Test

* Fixes for tests

* reversed changes

* tidy TestCsvReaderWithCallback
2018-10-25 10:35:02 +13:00
rob bygrave bdc9917811 [maven-release-plugin] prepare for next development iteration 2018-10-24 23:51:00 +13:00
446 changed files with 9960 additions and 2442 deletions
+11 -11
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.23.1</version>
<version>11.32.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.23.1</tag>
<tag>ebean-11.32.1</tag>
</scm>
<profiles>
@@ -117,13 +117,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>4.3</version>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-datasource</artifactId>
<version>3.2.2</version>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>4.3.2</version>
</dependency>
<dependency>
@@ -135,7 +135,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.9.1</version>
<version>11.13.1</version>
</dependency>
<dependency>
@@ -229,7 +229,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>11.11.1</version>
<version>11.27.1</version>
<scope>test</scope>
</dependency>
@@ -238,7 +238,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
<version>1.4.197</version>
<scope>provided</scope>
</dependency>
@@ -270,7 +270,7 @@
<version>5.1.43</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sap.cloud.db.jdbc</groupId>
<artifactId>ngdbc</artifactId>
@@ -309,7 +309,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>11.11.1</version>
<version>11.27.1</version>
<executions>
<execution>
<id>test</id>
@@ -1,6 +1,8 @@
package io.ebean;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
@@ -37,4 +39,21 @@ public interface BackgroundExecutor {
* </p>
*/
void executePeriodically(Runnable r, long delay, TimeUnit unit);
/**
* Schedules a Runnable for one-shot action that becomes enabled after the given delay.
*
* @return a ScheduledFuture representing pending completion of the task and
* whose get() method will return null upon completion
*/
ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit);
/**
* Schedules a Callable for one-shot action that becomes enabled after the given delay.
*
* @return a ScheduledFuture that can be used to extract result or cancel
*/
<V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit);
}
+8
View File
@@ -3,6 +3,8 @@ package io.ebean;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Provides access to the internal state of an entity bean.
*/
@@ -111,4 +113,10 @@ public interface BeanState {
* Reset the bean putting it into NEW state such that a save() results in an insert.
*/
void resetForInsert();
/**
* Returns a map with load erros.
*/
@Nullable
Map<String, Exception> getLoadErrors();
}
+1 -1
View File
@@ -30,7 +30,7 @@ import java.util.function.Predicate;
* String sql = "select id, name from customer where name like :name and status_code = :status";
*
* List<CustomerDto> beans =
* Ebean.findDto(CustomrDto.class, sql)
* Ebean.findDto(CustomerDto.class, sql)
* .setParameter("name", "Acme%")
* .setParameter("status", "ACTIVE")
* .findList();
+11
View File
@@ -2,7 +2,9 @@ package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.BeanNotEnhancedException;
import io.ebean.config.ServerConfig;
import io.ebean.datasource.DataSourceConfigurationException;
import io.ebean.plugin.Property;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
@@ -160,6 +162,15 @@ public final class Ebean {
defaultServer = getWithCreate(defaultName.trim());
}
}
} catch (BeanNotEnhancedException e) {
throw e;
} catch (DataSourceConfigurationException e) {
String msg = "Configuration error creating DataSource for the default EbeanServer." +
" This typically means a missing application-test.yaml or missing ebean-test-config dependency." +
" See https://ebean.io/docs/trouble-shooting#datasource";
throw new DataSourceConfigurationException(msg, e);
} catch (Throwable e) {
logger.error("Error trying to create the default EbeanServer", e);
throw new RuntimeException(e);
+7 -4
View File
@@ -11,17 +11,13 @@ 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;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Provides the API for fetching and saving beans to a particular DataSource.
@@ -1446,6 +1442,13 @@ public interface EbeanServer {
*/
JsonContext json();
/**
* Return a ScriptRunner for running SQL or DDL scripts.
* <p/>
* Intended to use mostly in testing to run seed SQL scripts or truncate table scripts etc.
*/
ScriptRunner script();
/**
* Return the Document store.
*/
@@ -133,11 +133,22 @@ public interface ExpressionFactory {
*/
Expression ieq(String propertyName, String value);
/**
* Case Insensitive Not Equal To - property not equal to the given value (typically
* using a lower() function to make it case insensitive).
*/
Expression ine(String propertyName, String value);
/**
* Case Insensitive Equal To that allows for named parameter use.
*/
Expression ieqObject(String propertyName, Object value);
/**
* Case Insensitive Not Equal To that allows for named parameter use.
*/
Expression ineObject(String propertyName, Object value);
/**
* Between - property between the two given values.
*/
@@ -128,6 +128,38 @@ public interface ExpressionList<T> {
*/
<D> DtoQuery<D> asDto(Class<D> dtoClass);
/**
* Return the underlying query as an UpdateQuery.
* <p>
* Typically this is used with query beans to covert a query bean
* query into an UpdateQuery like the examples below.
* </p>
*
* <pre>{@code
*
* int rowsUpdated = new QCustomer()
* .name.startsWith("Rob")
* .asUpdate()
* .set("active", false)
* .update();;
*
* }</pre>
*
* <pre>{@code
*
* int rowsUpdated = new QContact()
* .notes.note.startsWith("Make Inactive")
* .email.endsWith("@foo.com")
* .customer.id.equalTo(42)
* .asUpdate()
* .set("inactive", true)
* .setRaw("email = lower(email)")
* .update();
*
* }</pre>
*/
UpdateQuery<T> asUpdate();
/**
* Execute using "for update" clause which results in the DB locking the record.
*/
@@ -166,6 +198,18 @@ public interface ExpressionList<T> {
*/
int delete();
/**
* 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>
*
* @return the number of rows that were deleted.
*/
int delete(Transaction transaction);
/**
* Execute as a update query.
*
@@ -174,6 +218,40 @@ public interface ExpressionList<T> {
*/
int update();
/**
* Execute as a update query with the given transaction.
*
* @return the number of rows that were updated.
* @see UpdateQuery
*/
int update(Transaction transaction);
/**
* Execute the query returning true if a row is found.
* <p>
* The query is executed using max rows of 1 and will only select the id property.
* This method is really just a convenient way to optimise a query to perform a
* 'does a row exist in the db' check.
* </p>
*
* <h2>Example:</h2>
* <pre>{@code
*
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
*
* }</pre>
*
* <h2>Example using a query bean:</h2>
* <pre>{@code
*
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
*
* }</pre>
*
* @return True if the query finds a matching row in the database
*/
boolean exists();
/**
* Execute the query iterating over the results.
*
@@ -729,6 +807,12 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> ieq(String propertyName, String value);
/**
* Case Insensitive Not Equal To - property not equal to the given value (typically
* using a lower() function to make it case insensitive).
*/
ExpressionList<T> ine(String propertyName, String value);
/**
* Between - property between the two given values.
*/
@@ -1,5 +1,8 @@
package io.ebean;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiTransaction;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
@@ -42,6 +45,32 @@ public interface ExtendedServer {
*/
void setClock(Clock clock);
/**
* Execute the query returning true if a row is found.
* <p>
* The query is executed using max rows of 1 and will only select the id property.
* This method is really just a convenient way to optimise a query to perform a
* 'does a row exist in the db' check.
* </p>
*
* <h2>Example:</h2>
* <pre>{@code
*
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
*
* }</pre>
*
* <h2>Example using a query bean:</h2>
* <pre>{@code
*
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
*
* }</pre>
*
* @return True if the query finds a matching row in the database
*/
<T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction);
/**
* Return the number of 'top level' or 'root' entities this query should return.
*
+1 -1
View File
@@ -10,7 +10,7 @@ import javax.annotation.Nonnull;
* that can be defined separately from the query and combined with other FetchGroups.
* </p>
*
* <h3>Select example</h3>*
* <h3>Select example</h3>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
@@ -78,6 +78,7 @@ public class MergeOptionsBuilder {
this.deletePermanent = deletePermanent;
}
@Override
public Set<String> paths() {
return paths;
}
-2
View File
@@ -2,7 +2,6 @@ package io.ebean;
import io.ebean.bean.EntityBean;
import javax.persistence.MappedSuperclass;
/**
* A MappedSuperclass base class that provides convenience methods for inserting, updating and
@@ -74,7 +73,6 @@ import javax.persistence.MappedSuperclass;
*
* }</pre>
*/
@MappedSuperclass
public abstract class Model {
/**
+2 -1
View File
@@ -1,6 +1,7 @@
package io.ebean;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -125,7 +126,7 @@ public class Pairs {
* Return all the value pairs.
*/
public List<Entry> getEntries() {
return entries;
return Collections.unmodifiableList(entries);
}
/**
+121 -15
View File
@@ -244,6 +244,38 @@ public interface Query<T> {
*/
<D> DtoQuery<D> asDto(Class<D> dtoClass);
/**
* Convert the query to a UpdateQuery.
* <p>
* Typically this is used with query beans to covert a query bean
* query into an UpdateQuery like the examples below.
* </p>
*
* <pre>{@code
*
* int rowsUpdated = new QCustomer()
* .name.startsWith("Rob")
* .asUpdate()
* .set("active", false)
* .update();;
*
* }</pre>
*
* <pre>{@code
*
* int rowsUpdated = new QContact()
* .notes.note.startsWith("Make Inactive")
* .email.endsWith("@foo.com")
* .customer.id.equalTo(42)
* .asUpdate()
* .set("inactive", true)
* .setRaw("email = lower(email)")
* .update();
*
* }</pre>
*/
UpdateQuery<T> asUpdate();
/**
* Cancel the query execution if supported by the underlying database and
* driver.
@@ -340,6 +372,27 @@ public interface Query<T> {
*/
Query<T> setAutoTune(boolean autoTune);
/**
* Execute the query allowing properties with invalid JSON to be collected and not fail the query.
* <pre>{@code
*
* // fetch a bean with JSON content
* EBasicJsonList bean= Ebean.find(EBasicJsonList.class)
* .setId(42)
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
* .findOne();
*
*
* // get the invalid JSON errors from the bean state
* Map<String, Exception> errors = server().getBeanState(bean).getLoadErrors();
*
* // If this map is not empty tell we have invalid JSON
* // and should try and fix the JSON content or inform the user
*
* }</pre>
*/
Query<T> setAllowLoadErrors();
/**
* Set the default lazy loading batch size to use.
* <p>
@@ -636,15 +689,13 @@ public interface Query<T> {
* .where().eq("status", Status.NEW)
* .order().asc("id");
*
* QueryIterator<Customer> it = query.findIterate();
* try {
* // use try with resources to ensure QueryIterator is closed
*
* try (QueryIterator<Customer> it = query.findIterate()) {
* while (it.hasNext()) {
* Customer customer = it.next();
* // do something with customer ...
* }
* } finally {
* // close the underlying resources
* it.close();
* }
*
* }</pre>
@@ -838,6 +889,32 @@ public interface Query<T> {
*/
boolean isCountDistinct();
/**
* Execute the query returning true if a row is found.
* <p>
* The query is executed using max rows of 1 and will only select the id property.
* This method is really just a convenient way to optimise a query to perform a
* 'does a row exist in the db' check.
* </p>
*
* <h2>Example:</h2>
* <pre>{@code
*
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
*
* }</pre>
*
* <h2>Example using a query bean:</h2>
* <pre>{@code
*
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
*
* }</pre>
*
* @return True if the query finds a matching row in the database
*/
boolean exists();
/**
* Execute the query returning either a single bean or null (if no matching
* bean is found).
@@ -924,11 +1001,31 @@ public interface Query<T> {
*/
int delete();
/**
* Execute as a delete query returning the number of rows deleted using the given transaction.
* <p>
* Note that if the query includes joins then the generated delete statement may not be
* optimal depending on the database platform.
* </p>
*
* @return the number of beans/rows that were deleted.
*/
int delete(Transaction transaction);
/**
* Execute the UpdateQuery returning the number of rows updated.
*
* @return the number of beans/rows updated.
*/
int update();
/**
* Execute the UpdateQuery returning the number of rows updated using the given transaction.
*
* @return the number of beans/rows updated.
*/
int update(Transaction transaction);
/**
* Return the count of entities this query should return.
* <p>
@@ -1309,7 +1406,6 @@ public interface Query<T> {
* count:1 orderStatus:COMPLETE
*
* }</pre>
*
*/
Query<T> setCountDistinct(CountDistinctOrder orderBy);
@@ -1485,15 +1581,6 @@ public interface Query<T> {
*/
String getGeneratedSql();
/**
* Deprecated - migrate to forUpdate().
*
* executed the select with "for update" which should lock the record
* "on read"
*/
@Deprecated
Query<T> setForUpdate(boolean forUpdate);
/**
* Execute using "for update" clause which results in the DB locking the record.
*/
@@ -1530,11 +1617,30 @@ public interface Query<T> {
*/
Query<T> alias(String alias);
/**
* Set the base table to use for this query.
* <p>
* Typically this is used when a table has partitioning and we wish to specify a specific
* partition/table to query against.
* </p>
*/
Query<T> setBaseTable(String baseTable);
/**
* Return the type of beans being queried.
*/
Class<T> getBeanType();
/**
* Sets the inherit type. Must be a subtype of getBeanType
*/
Query<T> setInheritType(Class<? extends T> type);
/**
* Returns the inherit type. This is normally the same as getBeanType() returns as long as no other type is set.
*/
Class<? extends T> getInheritType();
/**
* Return the type of query being executed.
*/
+65
View File
@@ -0,0 +1,65 @@
package io.ebean;
import java.net.URL;
import java.util.Map;
/**
* Runs DDL and SQL scripts.
* <p/>
* Typically these are scripts used for testing such as seed SQL scripts or truncate SQL scripts.
* <p/>
* Scripts are executed in their own transaction and committed on successful completion.
*
* <h3>Example of simple use</h3>
* <pre>{@code
*
* EbeanServer server = Ebean.getDefaultServer();
* server.script().run("/scripts/test-script.sql");
*
* }</pre>
*
*
* <h3>Example using place holders in the script</h3>
* <pre>{@code
*
* Map<String,String> placeholders = new HashMap<>();
* placeholders.put("tableName", "e_basic");
*
* EbeanServer server = Ebean.getDefaultServer();
* server.script().run("/scripts/test-script.sql");
*
* }</pre>
*/
public interface ScriptRunner {
/**
* Run a script given the resource path (that should start with "/").
*/
void run(String path);
/**
* Run a script given the resource path (that should start with "/") and place holders.
*
* <pre>{@code
*
* Map<String,String> placeholders = new HashMap<>();
* placeholders.put("tableName", "e_basic");
*
* EbeanServer server = Ebean.getDefaultServer();
* server.script().run("/scripts/test-script.sql");
*
* }</pre>
*/
void run(String path, Map<String, String> placeholderMap);
/**
* Run a DDL or SQL script given the resource.
*/
void run(URL resource);
/**
* Run a DDL or SQL script given the resource and place holders.
*/
void run(URL resource, Map<String, String> placeholderMap);
}
+3 -42
View File
@@ -84,7 +84,7 @@ public interface Transaction extends AutoCloseable {
* <p>
* This is similar to commit() but leaves the transaction "Active".
* </p>
* <h3>Functions/h3>
* <h3>Functions</h3>
* <ul>
* <li>Flush the JDBC batch buffer</li>
* <li>Call commit on the underlying JDBC connection</li>
@@ -100,7 +100,7 @@ public interface Transaction extends AutoCloseable {
* This performs commit and completes the transaction closing underlying resources and
* marking the transaction as "In active".
* </p>
* <h3>Functions/h3>
* <h3>Functions</h3>
* <ul>
* <li>Flush the JDBC batch buffer</li>
* <li>Call commit on the underlying JDBC connection</li>
@@ -117,7 +117,7 @@ public interface Transaction extends AutoCloseable {
* <p>
* This performs rollback, closes underlying resources and marks the transaction as "In active".
* </p>
* <h3>Functions/h3>
* <h3>Functions</h3>
* <ul>
* <li>Call rollback on the underlying JDBC connection</li>
* <li>Trigger any registered TransactionCallbacks</li>
@@ -358,29 +358,6 @@ public interface Transaction extends AutoCloseable {
*/
void setBatchMode(boolean useBatch);
/**
* 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.
* </p>
*
* @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.
*/
@@ -405,22 +382,6 @@ public interface Transaction extends AutoCloseable {
*/
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.
*/
+7
View File
@@ -432,4 +432,11 @@ public final class TxScope {
return this;
}
public boolean isBatchMode() {
return PersistBatch.ALL.equals(batch);
}
public boolean isBatchOnCascade() {
return PersistBatch.ALL.equals(batchOnCascade);
}
}
+10
View File
@@ -154,9 +154,19 @@ public interface UpdateQuery<T> {
*/
UpdateQuery<T> setProfileLocation(ProfileLocation profileLocation);
/**
* Set the label on the update query.
*/
UpdateQuery<T> setLabel(String label);
/**
* Return the query expression list to add predicates to.
*/
ExpressionList<T> where();
/**
* Execute the update returning the number of rows updated.
*/
int update();
}
@@ -98,6 +98,8 @@ public final class EntityBeanIntercept implements Serializable {
private Object[] origValues;
private Exception[] loadErrors;
private int lazyLoadProperty = -1;
private Object ownerId;
@@ -829,7 +831,7 @@ public final class EntityBeanIntercept implements Serializable {
if (lazyLoadFailure) {
// failed when batch lazy loaded by another bean in the batch
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
}
if (lazyLoadProperty == -1) {
@@ -1115,4 +1117,36 @@ public final class EntityBeanIntercept implements Serializable {
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
/**
* Set the load error that happened on this property.
*/
public void setLoadError(int propertyIndex, Exception t) {
if (loadErrors == null) {
loadErrors = new Exception[owner._ebean_getPropertyNames().length];
}
loadErrors[propertyIndex] = t;
flags[propertyIndex] |= FLAG_LOADED_PROP;
}
/**
* Returns the loadErrors.
*/
public Map<String, Exception> getLoadErrors() {
if (loadErrors == null) {
return null;
}
Map<String, Exception> ret = null;
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
Exception loadError = loadErrors[i];
if (loadError != null) {
if (ret == null) {
ret = new LinkedHashMap<>();
}
ret.put(getProperty(i), loadError);
}
}
return ret;
}
}
@@ -19,6 +19,11 @@ public class ServerCacheNotification {
this.dependentTables = dependentTables;
}
@Override
public String toString() {
return "ts:" + modifyTimestamp + " tables:" + dependentTables;
}
public long getModifyTimestamp() {
return modifyTimestamp;
}
+33
View File
@@ -12,6 +12,7 @@ public class ServerCacheOptions {
private int maxIdleSecs;
private int maxSecsToLive;
private int trimFrequency;
private boolean nearCache;
/**
* Construct with no set options.
@@ -40,6 +41,14 @@ public class ServerCacheOptions {
this.trimFrequency = cacheTuning.trimFrequency();
}
/**
* Create with nearCache option.
*/
public ServerCacheOptions(boolean nearCache, CacheBeanTuning tuning) {
this(tuning);
this.nearCache = nearCache;
}
/**
* Apply any settings from the default settings that have not already been
* specifically set.
@@ -70,9 +79,33 @@ public class ServerCacheOptions {
copy.maxIdleSecs = maxIdleSecs;
copy.maxSecsToLive = maxSecsToLive;
copy.trimFrequency = trimFrequency;
copy.nearCache = this.nearCache;
return copy;
}
/**
* Return a copy of this object with nearCache option.
*/
public ServerCacheOptions copy(boolean nearCache) {
ServerCacheOptions copy = copy();
copy.nearCache = nearCache;
return copy;
}
/**
* Return true if nearCache was explicitly turned on.
*/
public boolean isNearCache() {
return nearCache;
}
/**
* Turn on nearCache option.
*/
public void setNearCache(boolean nearCache) {
this.nearCache = nearCache;
}
/**
* Return the maximum cache size.
*/
+6 -2
View File
@@ -123,7 +123,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
private void initClear() {
synchronized (this) {
if (map == null) {
if (modifyListening) {
if (!disableLazyLoad && modifyListening) {
lazyLoadCollection(true);
} else {
map = new LinkedHashMap<>();
@@ -135,7 +135,11 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
private void init() {
synchronized (this) {
if (map == null) {
lazyLoadCollection(false);
if (disableLazyLoad) {
map = new LinkedHashMap<>();
} else {
lazyLoadCollection(false);
}
}
}
}
+7 -3
View File
@@ -116,7 +116,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
private void initClear() {
synchronized (this) {
if (set == null) {
if (modifyListening) {
if (!disableLazyLoad && modifyListening) {
lazyLoadCollection(true);
} else {
set = new LinkedHashSet<>();
@@ -128,7 +128,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
private void init() {
synchronized (this) {
if (set == null) {
lazyLoadCollection(true);
if (disableLazyLoad) {
set = new LinkedHashSet<>();
} else {
lazyLoadCollection(true);
}
}
}
}
@@ -377,7 +381,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
throw new IllegalStateException("This collection is in ReadOnly mode");
}
}
@Override
public BeanCollection<E> getShallowCopy() {
BeanSet<E> copy = new BeanSet<>(new LinkedHashSet<>(set));
@@ -0,0 +1,15 @@
package io.ebean.config;
/**
* Throw when an processing an entity bean that is not bytecode enhanced.
*
* Refer: https://ebean.io/docs/trouble-shooting#not-enhanced
*/
public class BeanNotEnhancedException extends IllegalStateException {
private static final long serialVersionUID = 3008101919425876799L;
public BeanNotEnhancedException(String msg) {
super(msg);
}
}
@@ -0,0 +1,15 @@
package io.ebean.config;
/**
* Throw when an processing thinks a bean is not registered.
*
* Refer: https://ebean.io/docs/trouble-shooting#not-registered
*/
public class BeanNotRegisteredException extends IllegalStateException {
private static final long serialVersionUID = -1411298126011136552L;
public BeanNotRegisteredException(String msg) {
super(msg);
}
}
@@ -5,7 +5,6 @@ import io.ebean.annotation.Platform;
import io.ebean.migration.MigrationConfig;
import io.ebean.migration.MigrationRunner;
import io.ebean.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +32,8 @@ public class DbMigrationConfig {
*/
protected String migrationPath = "dbmigration";
protected String migrationInitPath = "dbinit";
/**
* Subdirectory the model xml files go into.
*/
@@ -118,6 +119,15 @@ public class DbMigrationConfig {
this.platform = platform;
}
/**
* Return the path for normal migrations or dbinit migrations.
*
* @param dbinitMigration When true return the path for dbinit migrations.
*/
public String getMigrationPath(boolean dbinitMigration) {
return dbinitMigration ? migrationInitPath : migrationPath;
}
/**
* Return the resource path for db migrations.
*/
@@ -419,6 +429,7 @@ public class DbMigrationConfig {
public void loadSettings(PropertiesWrapper properties, String serverName) {
migrationPath = properties.get("migration.migrationPath", migrationPath);
migrationInitPath = properties.get("migration.migrationInitPath", migrationInitPath);
modelPath = properties.get("migration.modelPath", modelPath);
applyPrefix = properties.get("migration.applyPrefix", applyPrefix);
applySuffix = properties.get("migration.applySuffix", applySuffix);
@@ -472,6 +483,7 @@ public class DbMigrationConfig {
runnerConfig.setMetaTable(metaTable);
runnerConfig.setApplySuffix(applySuffix);
runnerConfig.setMigrationPath(migrationPath);
runnerConfig.setMigrationInitPath(migrationInitPath);
runnerConfig.setRunPlaceholderMap(runPlaceholderMap);
runnerConfig.setRunPlaceholders(runPlaceholders);
runnerConfig.setDbUsername(getDbUsername());
+104 -17
View File
@@ -14,6 +14,7 @@ import io.ebean.config.dbplatform.DbEncrypt;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.properties.PropertiesLoader;
import io.ebean.datasource.DataSourceConfig;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
import io.ebean.event.BeanPersistListener;
@@ -30,7 +31,6 @@ import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
import io.ebean.migration.MigrationRunner;
import io.ebean.util.StringHelper;
import org.avaje.datasource.DataSourceConfig;
import javax.sql.DataSource;
import java.time.Clock;
@@ -229,6 +229,8 @@ public class ServerConfig {
private int persistBatchSize = 20;
private boolean disableLazyLoading;
/**
* The default batch size for lazy loading
*/
@@ -250,6 +252,8 @@ public class ServerConfig {
private boolean ddlRun;
private boolean ddlExtra = true;
private boolean ddlCreateOnly;
private String ddlInitSql;
@@ -364,9 +368,10 @@ public class ServerConfig {
private UuidVersion uuidVersion = UuidVersion.VERSION4;
/**
* The UUID state file (for Version 1 UUIDs).
* The UUID state file (for Version 1 UUIDs). By default, the file is created in
* ${HOME}/.ebean/${servername}-uuid.state
*/
private String uuidStateFile = "ebean-uuid.state";
private String uuidStateFile;
/**
* The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
@@ -484,6 +489,11 @@ public class ServerConfig {
*/
private boolean notifyL2CacheInForeground;
/**
* Set to true to support query plan capture.
*/
private boolean collectQueryPlans;
/**
* The time in millis used to determine when a query is alerted for being slow.
*/
@@ -592,6 +602,47 @@ public class ServerConfig {
return serviceObject.get(key);
}
/**
* Put a service object into configuration such that it can be passed to a plugin.
*
* <pre>{@code
*
* JedisPool jedisPool = ..
*
* serverConfig.putServiceObject(jedisPool);
*
* }</pre>
*/
public void putServiceObject(Object configObject) {
String key = serviceObjectKey(configObject);
serviceObject.put(key, configObject);
}
private String serviceObjectKey(Object configObject) {
return serviceObjectKey(configObject.getClass());
}
private String serviceObjectKey(Class<?> cls) {
String simpleName = cls.getSimpleName();
return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
}
/**
* Used by plugins to obtain service objects.
*
* <pre>{@code
*
* JedisPool jedisPool = serverConfig.getServiceObject(JedisPool.class);
*
* }</pre>
*
* @param cls The type of the service object to obtain
* @return The service object given the class type
*/
public <P> P getServiceObject(Class<P> cls) {
return (P) serviceObject.get(serviceObjectKey(cls));
}
/**
* Return the Jackson JsonFactory to use.
* <p>
@@ -927,6 +978,22 @@ public class ServerConfig {
this.queryBatchSize = queryBatchSize;
}
/**
* Return true if lazy loading is disabled on queries by default.
*/
public boolean isDisableLazyLoading() {
return disableLazyLoading;
}
/**
* Set to true to disable lazy loading by default.
* <p>
* It can be turned on per query via {@link Query#setDisableLazyLoading(boolean)}.
*/
public void setDisableLazyLoading(boolean disableLazyLoading) {
this.disableLazyLoading = disableLazyLoading;
}
/**
* Return the default batch size for lazy loading of beans and collections.
*/
@@ -2051,6 +2118,16 @@ public class ServerConfig {
this.ddlRun = ddlRun;
}
/**
* Set to false if you not want to run the extra-ddl.xml scripts. (default = true)
* <p>
* Typically we want this on when we are running tests.
*/
public void setDdlExtra(boolean ddlExtra) {
this.ddlExtra = ddlExtra;
}
/**
* Return true if the "drop all ddl" should be skipped.
* <p>
@@ -2121,6 +2198,13 @@ public class ServerConfig {
return ddlRun;
}
/**
* Return true, if extra-ddl.xml should be executed.
*/
public boolean isDdlExtra() {
return ddlExtra;
}
/**
* Return true if the class path search should be disabled.
*/
@@ -2745,20 +2829,6 @@ public class ServerConfig {
return list;
}
/**
* Deprecated - this does nothing now, we always try to read test configuration.
* <p>
* Load settings from test-ebean.properties and do nothing if the properties is not found.
* <p>
* This is typically used when test-ebean.properties is put into the test class path and used
* to configure Ebean for running tests.
* </p>
*/
@Deprecated
public void loadTestProperties() {
// do nothing now ... as we always try to read test configuration and that should only
}
/**
* Return the properties that we used for configuration and were set via a call to loadFromProperties().
*/
@@ -2828,6 +2898,7 @@ public class ServerConfig {
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
@@ -2896,6 +2967,7 @@ public class ServerConfig {
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
disableLazyLoading = p.getBoolean("disableLazyLoading", disableLazyLoading);
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
@@ -2909,6 +2981,7 @@ public class ServerConfig {
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
ddlExtra = p.getBoolean("ddl.extra", ddlExtra);
ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
ddlInitSql = p.get("ddl.initSql", ddlInitSql);
ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
@@ -3192,6 +3265,20 @@ public class ServerConfig {
this.idGeneratorAutomatic = idGeneratorAutomatic;
}
/**
* Return true if query plan capture is enabled.
*/
public boolean isCollectQueryPlans() {
return collectQueryPlans;
}
/**
* Set to true to enable query plan capture.
*/
public void setCollectQueryPlans(boolean collectQueryPlans) {
this.collectQueryPlans = collectQueryPlans;
}
public enum UuidVersion {
VERSION4,
VERSION1,
@@ -136,8 +136,9 @@ public class PostgresPlatform extends DatabasePlatform {
/**
* Return SQL using built in partition helper functions to create some initial partitions.
*
* Only use this if extra-dll doesn't have some initial partitions defined (which it should).
* Only use this if extra-ddl doesn't have some initial partitions defined (which it should).
*/
@Override
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String pkey) {
if (property == null) {
property = "";
@@ -63,6 +63,7 @@ class Loader {
*/
private void loadTest() {
loadProperties("application-test.properties", Source.RESOURCE);
loadYaml("application-test.yaml", Source.RESOURCE);
loadYaml("application-test.yml", Source.RESOURCE);
loadProperties("test-ebean.properties", Source.RESOURCE);
}
@@ -83,6 +84,7 @@ class Loader {
* Load the main configuration for the given source.
*/
private void loadMain(Source source) {
loadYaml("application.yaml", source);
loadYaml("application.yml", source);
loadProperties("application.properties", source);
loadProperties("ebean.properties", source);
@@ -99,7 +101,7 @@ class Loader {
}
void loadWithExtensionCheck(String fileName) {
if (fileName.endsWith("yml")) {
if (fileName.endsWith("yaml") || fileName.endsWith("yml")) {
loadYaml(fileName, Source.FILE);
} else if (fileName.endsWith("properties")) {
loadProperties(fileName, Source.FILE);
@@ -18,14 +18,14 @@ public class PropertiesLoader {
* <p>Firstly loads from main resources</p>
* <pre>
* - application.properties
* - application.yml
* - application.yaml
* - ebean.properties
* </pre>
* <p>
* <p>Then loads from local files</p>
* <pre>
* - application.properties
* - application.yml
* - application.yaml
* - ebean.properties
* </pre>
* <p>
@@ -41,7 +41,7 @@ public class PropertiesLoader {
* <p>Loads from test resources</p>
* <pre>
* - application-test.properties
* - application-test.yml
* - application-test.yaml
* - test-ebean.properties
* </pre>
*/
@@ -154,7 +154,7 @@ public interface DbMigration {
List<String> getPendingDrops();
/**
* Generate the next migration xml file and associated apply and rollback sql scripts.
* Generate the next migration sql script and associated model xml.
* <p>
* This does not run the migration or ddl scripts but just generates them.
* </p>
@@ -186,4 +186,15 @@ public interface DbMigration {
* @return the version of the generated migration or null
*/
String generateMigration() throws IOException;
/**
* Generate an "init" migration which has all changes.
* <p>
* An "init" migration can only be executed and used on a database that has had no
* prior migrations run on it.
* </p>
* @return the version of the generated migration
*/
String generateInitMigration() throws IOException;
}
@@ -7,6 +7,11 @@ import java.util.List;
*/
public interface MetaInfoManager {
/**
* Collect query plans.
*/
List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request);
/**
* Visit the metrics resetting and collecting/reporting as desired.
*/
@@ -0,0 +1,23 @@
package io.ebean.meta;
public interface MetaQueryPlan {
Class<?> getBeanType();
/**
* Return a string representation of the query plan hash.
*/
String getQueryPlanHash();
String getLabel();
String getSql();
String getBind();
String getPlan();
long getQueryTimeMicros();
long getCaptureCount();
}
+6 -1
View File
@@ -25,6 +25,11 @@ public enum MetricType {
* <p>
* SqlQuery and SqlUpdate without a label have no metrics collected.
*/
SQL
SQL,
/**
* L2 cache metrics.
*/
L2
}
@@ -0,0 +1,97 @@
package io.ebean.meta;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Request used to capture query plans.
*/
public class QueryPlanRequest {
private List<MetaQueryPlan> plans = new ArrayList<>();
private Connection connection;
private boolean store;
private long since;
private Set<Class<?>> includedBeanTypes;
private Set<String> includedLabels;
public List<MetaQueryPlan> getPlans() {
return plans;
}
/**
* Return the connection to use to capture the query plans.
*/
public Connection getConnection() {
return connection;
}
/**
* Set the connection to use to capture the query plans.
*/
public void setConnection(Connection connection) {
this.connection = connection;
}
/**
* Return true if the captured query plan is stored.
*/
public boolean isStore() {
return store;
}
/**
* Set to true to store the captured query plan.
*/
public void setStore(boolean store) {
this.store = store;
}
/**
* Return the epoch time after which the query plan was capture (to be included).
*/
public long getSince() {
return since;
}
/**
* Set the epoch time after which the query plan was captured.
* <p>
* This is used to only capture plans that have changed since a given time (like the time of last capture).
* </p>
*
* @param since The time after which the query plan was captured to be included
*/
public void setSince(long since) {
this.since = since;
}
/**
* Process consume the query plan.
*/
public void process(MetaQueryPlan plan) {
plans.add(plan);
}
/**
* Return true if the bean type should be included in the query plan capture.
*/
public boolean includeType(Class<?> beanType) {
return includedBeanTypes == null || includedBeanTypes.isEmpty() || includedBeanTypes.contains(beanType);
}
/**
* Return true if the label should be included in the query plan capture.
*/
public boolean includeLabel(String label) {
return includedLabels == null || includedLabels.isEmpty() || includedLabels.contains(label);
}
}
@@ -1,4 +1,4 @@
package io.ebeaninternal.metric;
package io.ebean.metric;
import io.ebean.meta.MetricType;
@@ -1,4 +1,4 @@
package io.ebeaninternal.metric;
package io.ebean.metric;
import io.ebeaninternal.server.profile.DMetricFactory;
@@ -1,4 +1,4 @@
package io.ebeaninternal.metric;
package io.ebean.metric;
import io.ebean.meta.MetaQueryMetric;
@@ -1,4 +1,4 @@
package io.ebeaninternal.metric;
package io.ebean.metric;
import io.ebean.meta.MetricVisitor;
@@ -1,4 +1,4 @@
package io.ebeaninternal.metric;
package io.ebean.metric;
import io.ebean.meta.MetricVisitor;
@@ -17,6 +17,16 @@ public interface TimedMetric {
*/
void add(long micros, long beans);
/**
* Add a time event given the start nanos.
*/
void addSinceNanos(long startNanos);
/**
* Add a time event given the start nanos and bean count.
*/
void addSinceNanos(long startNanos, long beans);
/**
* Return true if there are no metrics collected since the last collection.
*/
@@ -1,4 +1,4 @@
package io.ebeaninternal.metric;
package io.ebean.metric;
import io.ebean.meta.MetricVisitor;
@@ -7,6 +7,16 @@ import io.ebean.meta.MetricVisitor;
*/
public interface TimedMetricMap {
/**
* Add a time event given the start nanos.
*/
void addSinceNanos(String key, long startNanos);
/**
* Add a time event given the start nanos and beans.
*/
void addSinceNanos(String key, long startNanos, int beans);
/**
* Add an execution for the given key.
*/
@@ -1,4 +1,4 @@
package io.ebeaninternal.metric;
package io.ebean.metric;
import io.ebean.meta.MetaTimedMetric;
@@ -73,4 +73,14 @@ public interface ExpressionPath {
* Return the Id values for the given bean value.
*/
Object[] getAssocIdValues(EntityBean bean);
/**
* Return the underlying bean property.
*/
public Property getProperty();
/**
* The ElPrefix plus name.
*/
String getElName();
}
@@ -0,0 +1,14 @@
package io.ebean.plugin;
import io.ebean.bean.EntityBean;
/**
* Errorhandler to handle load errors and may be recover correct value.
*
* @author Roland Praml, FOCONIS AG
*
*/
@FunctionalInterface
public interface LoadErrorHandler {
void handleLoadError(EntityBean bean, Property prop, String fullName, Exception e);
}
@@ -6,6 +6,7 @@ package io.ebean.text;
public class TextException extends RuntimeException {
private static final long serialVersionUID = 1601310159486033148L;
private String text;
/**
* Construct with an error message.
@@ -27,4 +28,41 @@ public class TextException extends RuntimeException {
public TextException(Exception e) {
super(e);
}
/**
* Constructor for a detailed exception.
*
* @param message
* the message. The placeholder {} will be replaced by
* <code>text</code>
* @param text
* the fault text.
* @param cause
* the case
*/
public TextException(String message, String text, Exception cause) {
super(message.replace("{}", String.valueOf(text)), cause);
this.text = text;
}
/**
* Constructor for a detailed exception.
*
* @param message
* the message. The placeholder {} will be replaced by
* <code>text</code>
* @param text
* the fault text.
*/
public TextException(String message, String text) {
super(message.replace("{}", String.valueOf(text)));
this.text = text;
}
/**
* Return the text, that caused the error. (e.g. the JSON). May be null.
*/
public String getText() {
return text;
}
}
@@ -26,9 +26,9 @@ public class CamelCaseHelper {
} else if (Character.isDigit(c)) {
if (i > lastUpper + 1 && !digitsCompressed) {
sb.append("_");
lastUpper = i;
}
sb.append(c);
lastUpper = i;
} else if (Character.isUpperCase(c)) {
if (i > lastUpper + 1) {
@@ -168,13 +168,8 @@ public class LoadBeanRequest extends LoadRequest {
// necessary but allow processing to continue until it is accessed by client code
Object id = desc.getId(ebi.getOwner());
if (!loadedIds.contains(id)) {
if (desc.isSoftDelete()) {
// assume this is logically deleted (hence not found)
desc.setSoftDeleteValue(ebi.getOwner());
} else {
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted");
ebi.setLazyLoadFailure(id);
}
// assume this is logically deleted (hence not found)
desc.markAsDeleted(ebi.getOwner());
}
}
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
import io.ebean.Pairs;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -25,7 +24,7 @@ public class NaturalKeyQueryData<T> {
private List<Pairs.Entry> inPairs;
// IN clause - only one allowed
private Collection<?> inValues;
private List<Object> inValues;
private String inProperty;
// normal EQ expressions
@@ -52,36 +51,36 @@ public class NaturalKeyQueryData<T> {
/**
* Match for In Pairs expression. We only allow one IN clause.
*/
public boolean matchInPairs(Pairs pairs) {
public List<Pairs.Entry> matchInPairs(String property0, String property1, List<Pairs.Entry> inPairs) {
if (hasIn) {
// only 1 IN allowed (to project naturalIds)
return false;
return null;
}
if (matchProperty(pairs.getProperty0()) && matchProperty(pairs.getProperty1())) {
if (matchProperty(property0) && matchProperty(property1)) {
this.hasIn = true;
this.inProperty0 = pairs.getProperty0();
this.inProperty1 = pairs.getProperty1();
this.inPairs = pairs.getEntries();
return true;
this.inProperty0 = property0;
this.inProperty1 = property1;
this.inPairs = new ArrayList<>(inPairs); // will be modified
return this.inPairs;
}
return false;
return null;
}
/**
* Match for IN expression. We only allow one IN clause.
*/
public boolean matchIn(String propName, Collection<?> sourceValues) {
public List<Object> matchIn(String propName, List<Object> sourceValues) {
if (hasIn) {
// only 1 IN allowed (to project naturalIds)
return false;
return null;
}
if (matchProperty(propName)) {
this.hasIn = true;
this.inProperty = propName;
this.inValues = sourceValues;
return true;
this.inValues = new ArrayList<>(sourceValues);
return this.inValues;
}
return false;
return null;
}
/**
@@ -1,7 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.TxScope;
import io.ebean.annotation.PersistBatch;
import java.util.ArrayList;
@@ -38,9 +37,9 @@ public class ScopeTrans {
*/
private final ArrayList<Class<? extends Throwable>> rollbackFor;
private PersistBatch restoreBatch;
private Boolean restoreBatch;
private PersistBatch restoreBatchOnCascade;
private Boolean restoreBatchOnCascade;
private int restoreBatchSize;
@@ -68,20 +67,20 @@ public class ScopeTrans {
if (transaction != null) {
if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
restoreBatch = transaction.getBatch();
restoreBatchOnCascade = transaction.getBatchOnCascade();
restoreBatch = transaction.isBatchMode();
restoreBatchOnCascade = transaction.isBatchOnCascade();
restoreBatchSize = transaction.getBatchSize();
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
restoreBatchFlushOnQuery = transaction.isBatchFlushOnQuery();
}
if (txScope.isBatchSet()) {
transaction.setBatch(txScope.getBatch());
transaction.setBatchMode(txScope.isBatchMode());
}
if (!txScope.isFlushOnQuery()) {
transaction.setBatchFlushOnQuery(false);
}
if (txScope.isBatchOnCascadeSet()) {
transaction.setBatchOnCascade(txScope.getBatchOnCascade());
transaction.setBatchOnCascade(txScope.isBatchOnCascade());
}
if (txScope.isBatchSizeSet()) {
transaction.setBatchSize(txScope.getBatchSize());
@@ -139,7 +138,7 @@ public class ScopeTrans {
nestedCommit = true;
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
if (restoreBatch != null) {
transaction.setBatch(restoreBatch);
transaction.setBatchMode(restoreBatch);
}
if (restoreBatchOnCascade != null) {
transaction.setBatchOnCascade(restoreBatchOnCascade);
@@ -37,6 +37,7 @@ public interface SpiDtoQuery<T> extends DtoQuery<T>, SpiSqlBinding {
/**
* Return the label for the query.
*/
@Override
String getLabel();
/**
@@ -749,23 +749,6 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
boolean isDisableLazyLoading();
/**
* Internally set by Ebean when this query must use the DISTINCT keyword.
* <p>
* This does not exclude/remove the use of the id property.
*/
void setSqlDistinct(boolean sqlDistinct);
/**
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
*/
boolean isDistinctQuery();
/**
* Return true if this was internally set to sql distinct (ie. many where predicate).
*/
boolean isSqlDistinct();
/**
* Return true if this query has been specified by a user to use DISTINCT.
*/
@@ -837,6 +820,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
boolean isCancelled();
/**
* Return the base table to use if user defined on the query.
*/
String getBaseTable();
/**
* Return root table alias set by {@link #alias(String)} command.
*/
@@ -861,4 +849,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
* Returns the count distinct order setting.
*/
CountDistinctOrder getCountDistinctOrder();
/**
* Handles load errors.
*/
void handleLoadError(String fullName, Exception e);
}
@@ -7,7 +7,6 @@ import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.transaction.ProfileStream;
@@ -3,12 +3,10 @@ package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.transaction.ProfileStream;
@@ -255,36 +253,16 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.isBatchMode();
}
@Override
public void setBatch(PersistBatch persistBatchMode) {
transaction.setBatch(persistBatchMode);
}
@Override
public PersistBatch getBatch() {
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();
}
@Override
public void setBatchSize(int batchSize) {
transaction.setBatchSize(batchSize);
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.transaction.TransactionManager;
import io.ebeanservice.docstore.api.DocStoreUpdates;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
@@ -29,16 +30,21 @@ public class TransactionEvent implements Serializable {
*/
private final transient boolean local;
private final long startMillis;
private TransactionEventTable eventTables;
private transient TransactionEventBeans eventBeans;
private transient List<PersistRequestBean<?>> listenerNotify;
private transient DeleteByIdMap deleteByIdMap;
private transient CacheChangeSet changeSet;
/**
* Create the TransactionEvent, one per Transaction.
*/
public TransactionEvent() {
public TransactionEvent(long startMillis) {
this.startMillis = startMillis;
this.local = true;
}
@@ -71,8 +77,8 @@ public class TransactionEvent implements Serializable {
/**
* Return the list of PersistRequestBean's for this transaction.
*/
public List<PersistRequestBean<?>> getPersistRequestBeans() {
return (eventBeans == null) ? null : eventBeans.getRequests();
public List<PersistRequestBean<?>> getListenerNotify() {
return (listenerNotify == null) ? null : listenerNotify;
}
public TransactionEventTable getEventTables() {
@@ -94,17 +100,13 @@ public class TransactionEvent implements Serializable {
}
/**
* Add a inserted updated or deleted bean to the event.
* Add post commit listeners. Watch this for large transactions.
*/
public void add(PersistRequestBean<?> request) {
if (request.isNotify()) {
// either a BeanListener or Cache is interested
if (eventBeans == null) {
eventBeans = new TransactionEventBeans();
}
eventBeans.add(request);
public void addListenerNotify(PersistRequestBean<?> request) {
if (listenerNotify == null) {
listenerNotify = new ArrayList<>();
}
listenerNotify.add(request);
}
/**
@@ -112,11 +114,13 @@ public class TransactionEvent implements Serializable {
*/
public CacheChangeSet buildCacheChanges(TransactionManager manager) {
if (eventBeans == null && deleteByIdMap == null && eventTables == null) {
if (changeSet == null && deleteByIdMap == null && eventTables == null) {
return null;
}
CacheChangeSet changeSet = new CacheChangeSet(manager.clockNowMillis());
if (changeSet == null) {
changeSet = new CacheChangeSet(manager.clockNowMillis());
}
if (eventTables != null && !eventTables.isEmpty()) {
// notify cache with table based changes
BeanDescriptorManager dm = manager.getBeanDescriptorManager();
@@ -124,9 +128,6 @@ public class TransactionEvent implements Serializable {
dm.cacheNotify(tableIUD, changeSet);
}
}
if (eventBeans != null) {
eventBeans.notifyCache(changeSet);
}
if (deleteByIdMap != null) {
deleteByIdMap.notifyCache(changeSet);
}
@@ -137,12 +138,25 @@ public class TransactionEvent implements Serializable {
* Add any relevant PersistRequestBean's to DocStoreUpdates for later processing.
*/
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
List<PersistRequestBean<?>> persistRequestBeans = getPersistRequestBeans();
if (persistRequestBeans != null) {
for (PersistRequestBean<?> persistRequestBean : persistRequestBeans) {
List<PersistRequestBean<?>> requests = getListenerNotify();
if (requests != null) {
for (PersistRequestBean<?> persistRequestBean : requests) {
persistRequestBean.addDocStoreUpdates(docStoreUpdates);
}
}
}
/**
* Return the CacheChangeSet that we add cache notification messages to.
*
* We want to add to this change set as we process requests allowing the
* PersistRequestBean to be garbage collected for large transactions.
*/
public CacheChangeSet obtainCacheChangeSet() {
if (changeSet == null) {
changeSet = new CacheChangeSet(startMillis);
}
return changeSet;
}
}
@@ -1,44 +0,0 @@
package io.ebeaninternal.api;
import io.ebeaninternal.server.cache.CacheChangeSet;
import io.ebeaninternal.server.core.PersistRequestBean;
import java.util.ArrayList;
import java.util.List;
/**
* Lists of inserted updated and deleted beans that have a BeanPersistListener.
* <p>
* These beans will be sent to the appropriate BeanListeners after a successful
* commit of the transaction.
* </p>
*/
public class TransactionEventBeans {
final ArrayList<PersistRequestBean<?>> requests = new ArrayList<>();
/**
* Return the list of PersistRequests that BeanListeners are interested in.
*/
public List<PersistRequestBean<?>> getRequests() {
return requests;
}
/**
* Add a bean for BeanListener notification.
*/
public void add(PersistRequestBean<?> request) {
requests.add(request);
}
/**
* Collect the cache changes.
*/
public void notifyCache(CacheChangeSet changeSet) {
for (PersistRequestBean<?> request : requests) {
request.notifyCache(changeSet);
}
}
}
@@ -1,8 +1,10 @@
package io.ebeaninternal.dbmigration;
import io.ebean.config.DbMigrationConfig;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.migration.ddl.DdlRunner;
import io.ebean.migration.runner.ScriptTransform;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.model.CurrentModel;
@@ -38,10 +40,12 @@ public class DdlGenerator {
private final boolean generateDdl;
private final boolean runDdl;
private final boolean extraDdl;
private final boolean createOnly;
private final boolean jaxbPresent;
private final boolean ddlCommitOnCreateIndex;
private final String dbSchema;
private final ScriptTransform scriptTransform;
private CurrentModel currentModel;
private String dropAllContent;
@@ -51,6 +55,7 @@ public class DdlGenerator {
this.server = server;
this.jaxbPresent = serverConfig.getClassLoadConfig().isJavaxJAXBPresent();
this.generateDdl = serverConfig.isDdlGenerate();
this.extraDdl = serverConfig.isDdlExtra();
this.createOnly = serverConfig.isDdlCreateOnly();
this.dbSchema = serverConfig.getDbSchema();
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
@@ -61,6 +66,7 @@ public class DdlGenerator {
this.runDdl = serverConfig.isDdlRun();
this.ddlCommitOnCreateIndex = server.getDatabasePlatform().isDdlCommitOnCreateIndex();
}
this.scriptTransform = createScriptTransform(serverConfig.getMigrationConfig());
}
/**
@@ -126,7 +132,9 @@ public class DdlGenerator {
private void createSchemaIfRequired(Connection connection) {
try {
server.getDatabasePlatform().createSchemaIfNotExists(dbSchema, connection);
for (String schema : dbSchema.split(",")) {
server.getDatabasePlatform().createSchemaIfNotExists(schema, connection);
}
} catch (SQLException e) {
throw new PersistenceException("Failed to create DB Schema", e);
}
@@ -144,7 +152,7 @@ public class DdlGenerator {
} else if (ddlCommitOnCreateIndex) {
runner.setCommitOnCreateIndex();
}
int count = runner.runAll(content, connection);
int count = runner.runAll(scriptTransform.transform(content), connection);
if (expectErrors) {
connection.setAutoCommit(false);
}
@@ -160,11 +168,10 @@ public class DdlGenerator {
protected void runDropSql(Connection connection) throws IOException {
if (!createOnly) {
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
if (extraDdl && jaxbPresent) {
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), true);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-dll");
runScript(connection, false, extraApply, "extra-ddl");
}
}
@@ -181,18 +188,17 @@ public class DdlGenerator {
}
runScript(connection, false, createAllContent, getCreateFileName());
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
if (extraDdl && jaxbPresent) {
if (currentModel.isTablePartitioning()) {
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
runScript(connection, false, extraPartitioning, "builtin-partitioning-dll");
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
}
}
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), false);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-dll");
runScript(connection, false, extraApply, "extra-ddl");
}
if (currentModel.isTablePartitioning()) {
@@ -203,7 +209,7 @@ public class DdlGenerator {
/**
* Check if table partitions exist and if not create some. The expectation is that
* extra-dll.xml should have some partition initialisation but this helps people get going.
* extra-ddl.xml should have some partition initialisation but this helps people get going.
*/
private void checkInitialTablePartitions(Connection connection) {
@@ -342,8 +348,15 @@ public class DdlGenerator {
buf.append(s).append("\n");
}
return buf.toString();
}
}
/**
* Create the ScriptTransform for placeholder key/value replacement.
*/
private ScriptTransform createScriptTransform(DbMigrationConfig config) {
return ScriptTransform.build(config.getRunPlaceholders(), config.getRunPlaceholderMap());
}
}
@@ -271,6 +271,15 @@ public class DefaultDbMigration implements DbMigration {
*/
@Override
public String generateMigration() throws IOException {
return generateMigrationFor(false);
}
@Override
public String generateInitMigration() throws IOException {
return generateMigrationFor(true);
}
private String generateMigrationFor(boolean dbinitMigration) throws IOException {
// use this flag to stop other plugins like full DDL generation
if (!online) {
@@ -286,9 +295,18 @@ public class DefaultDbMigration implements DbMigration {
configurePlatforms();
}
try {
Request request = createRequest();
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning());
Request request = createRequest(dbinitMigration);
if (!dbinitMigration) {
// repeatable migrations
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning());
} else {
for (Pair pair : platforms) {
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(request.migrationDir, pair.prefix);
generateExtraDdl(subPath, pair.platform, request.isTablePartitioning());
}
}
}
String pendingVersion = generatePendingDrop();
@@ -308,13 +326,14 @@ public class DefaultDbMigration implements DbMigration {
/**
* Return the versions containing pending drops.
*/
@Override
public List<String> getPendingDrops() {
if (!online) {
DbOffline.setGenerateMigration();
}
setDefaults();
try {
return createRequest().getPendingDrops();
return createRequest(false).getPendingDrops();
} finally {
if (!online) {
DbOffline.reset();
@@ -335,7 +354,7 @@ public class DefaultDbMigration implements DbMigration {
/**
* Generate "repeatable" migration scripts.
* <p>
* These take scrips from extra-dll.xml (typically views) and outputs "repeatable"
* These take scrips from extra-ddl.xml (typically views) and outputs "repeatable"
* migration scripts (starting with "R__") to be run by FlywayDb or Ebean's own
* migration runner.
* </p>
@@ -426,36 +445,52 @@ public class DefaultDbMigration implements DbMigration {
return version;
}
private Request createRequest() {
return new Request();
private Request createRequest(boolean dbinitMigration) {
return new Request(dbinitMigration);
}
private class Request {
final boolean dbinitMigration;
final File migrationDir;
final File modelDir;
final MigrationModel migrationModel;
final CurrentModel currentModel;
final ModelContainer migrated;
final ModelContainer current;
private Request() {
this.migrationDir = getMigrationDirectory();
this.modelDir = getModelDirectory(migrationDir);
this.migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
this.migrated = migrationModel.read();
private Request(boolean dbinitMigration) {
this.dbinitMigration = dbinitMigration;
this.currentModel = new CurrentModel(server, constraintNaming);
this.current = currentModel.read();
this.migrationDir = getMigrationDirectory(dbinitMigration);
if (dbinitMigration) {
this.modelDir = null;
this.migrated = new ModelContainer();
} else {
this.modelDir = getModelDirectory(migrationDir);
MigrationModel migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
this.migrated = migrationModel.read(dbinitMigration);
}
}
boolean isTablePartitioning() {
return current.isTablePartitioning();
}
/**
* Return the next migration version (based on existing migration versions).
*/
String nextVersion() {
// always read the next version using the main migration directory (not dbinit)
File migDirectory = getMigrationDirectory(false);
File modelDir = getModelDirectory(migDirectory);
return LastMigration.nextVersion(migDirectory, modelDir, dbinitMigration);
}
/**
* Return the migration for the pending drops for a given version.
*/
public Migration migrationForPendingDrop(String pendingVersion) {
Migration migrationForPendingDrop(String pendingVersion) {
Migration migration = migrated.migrationForPendingDrop(pendingVersion);
@@ -474,7 +509,7 @@ public class DefaultDbMigration implements DbMigration {
/**
* Create and return the diff of the current model to the migration model.
*/
public Migration createDiffMigration() {
Migration createDiffMigration() {
ModelDiff diff = new ModelDiff(migrated);
diff.compareTo(current);
return diff.isEmpty() ? null : diff.getMigration();
@@ -483,10 +518,10 @@ public class DefaultDbMigration implements DbMigration {
private String generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException {
String fullVersion = getFullVersion(request.migrationModel, dropsFor);
String fullVersion = getFullVersion(request.nextVersion(), dropsFor);
logger.info("generating migration:{}", fullVersion);
if (!writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
if (!request.dbinitMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
logger.warn("migration already exists, not generating DDL");
return null;
} else {
@@ -521,11 +556,11 @@ public class DefaultDbMigration implements DbMigration {
* <p>
* The full version can contain a comment suffix after a "__" double underscore.
*/
private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
private String getFullVersion(String nextVersion, String dropsFor) {
String version = getVersion();
if (version == null) {
version = migrationModel.getNextVersion(initialVersion);
version = (nextVersion != null) ? nextVersion : initialVersion;
}
String fullVersion = migrationConfig.getApplyPrefix() + version;
@@ -552,15 +587,13 @@ public class DefaultDbMigration implements DbMigration {
/**
* Write any extra platform ddl.
*/
protected void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
for (Pair pair : platforms) {
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
generateExtraDdl(subPath, pair.platform, currentModel.isTablePartitioning());
}
}
@@ -571,7 +604,7 @@ public class DefaultDbMigration implements DbMigration {
/**
* Write the migration xml.
*/
protected boolean writeMigrationXml(Migration dbMigration, File resourcePath, String fullVersion) {
private boolean writeMigrationXml(Migration dbMigration, File resourcePath, String fullVersion) {
String modelFile = fullVersion + migrationConfig.getModelSuffix();
File file = new File(resourcePath, modelFile);
@@ -587,7 +620,7 @@ public class DefaultDbMigration implements DbMigration {
/**
* Set default server and platform if necessary.
*/
protected void setDefaults() {
private void setDefaults() {
if (server == null) {
setServer(Ebean.getDefaultServer());
}
@@ -666,14 +699,21 @@ public class DefaultDbMigration implements DbMigration {
return val;
}
/**
* Return the main migration directory.
*/
File getMigrationDirectory() {
return getMigrationDirectory(false);
}
/**
* Return the file path to write the xml and sql to.
*/
protected File getMigrationDirectory() {
File getMigrationDirectory(boolean dbinitMigration) {
// path to src/main/resources in typical maven project
File resourceRootDir = new File(pathToResources);
String resourcePath = migrationConfig.getMigrationPath();
String resourcePath = migrationConfig.getMigrationPath(dbinitMigration);
// expect to be a path to something like - src/main/resources/dbmigration/model
File path = new File(resourceRootDir, resourcePath);
@@ -688,7 +728,7 @@ public class DefaultDbMigration implements DbMigration {
/**
* Return the model directory (relative to the migration directory).
*/
protected File getModelDirectory(File migrationDirectory) {
private File getModelDirectory(File migrationDirectory) {
String modelPath = migrationConfig.getModelPath();
if (modelPath == null || modelPath.isEmpty()) {
return migrationDirectory;
@@ -741,19 +781,19 @@ public class DefaultDbMigration implements DbMigration {
* Holds a platform and prefix. Used to generate multiple platform specific DDL
* for a single migration.
*/
public static class Pair {
static class Pair {
/**
* The platform to generate the DDL for.
*/
public final DatabasePlatform platform;
final DatabasePlatform platform;
/**
* A prefix included into the file/resource names indicating the platform.
*/
public final String prefix;
final String prefix;
public Pair(DatabasePlatform platform, String prefix) {
Pair(DatabasePlatform platform, String prefix) {
this.platform = platform;
this.prefix = prefix;
}
@@ -0,0 +1,91 @@
package io.ebeaninternal.dbmigration;
import io.ebeaninternal.dbmigration.model.MigrationVersion;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Utility to determine the last sql migration version and next version.
*/
class LastMigration {
private static final String SQL = ".sql";
private static final String MODEL_XML = ".model.xml";
/**
* Return the next migation version given the migration directory.
*/
static String nextVersion(File migDir, File modelDir, boolean dbinitMigration) {
String last = lastVersion(migDir, modelDir);
if (last == null) {
return null;
}
return (dbinitMigration) ? last : MigrationVersion.parse(last).nextVersion();
}
/**
* Return the last migation version given the migration directory.
*/
static String lastVersion(File migDirectory, File modelDir) {
List<String> fileNames = new ArrayList<>();
File[] sqlFiles = migDirectory.listFiles(pathname -> includeSqlFile(pathname.getName().toLowerCase()));
if (sqlFiles != null) {
for (File file : sqlFiles) {
fileNames.add(trim(file.getName()));
}
}
if (modelDir != null) {
File[] xmlFiles = modelDir.listFiles(pathname -> includeModelFile(pathname.getName().toLowerCase()));
if (xmlFiles != null) {
for (File file : xmlFiles) {
fileNames.add(trim(file.getName()));
}
}
}
Collections.sort(fileNames);
if (!fileNames.isEmpty()) {
return fileNames.get(fileNames.size() - 1);
}
return null;
}
private static boolean includeSqlFile(String lowerFileName) {
if (lowerFileName.startsWith("r") || lowerFileName.startsWith("i") || !lowerFileName.endsWith(SQL)) {
return false;
}
return true;
}
private static boolean includeModelFile(String lowerFileName) {
return lowerFileName.endsWith(MODEL_XML);
}
private static String trim(String name) {
name = name.toLowerCase();
char c = name.charAt(0);
if (c == 'v') {
name = name.substring(1);
}
int p = name.indexOf("__");
if (p > -1) {
name = name.substring(0, p);
}
if (name.endsWith(SQL)) {
name = name.substring(0, name.length() - 4);
}
if (name.endsWith(MODEL_XML)) {
name = name.substring(0, name.length() - 10);
}
return name;
}
}
@@ -909,7 +909,7 @@ public class BaseTableDdl implements TableDdl {
if (hasValue(ddl)) {
writer.apply().append(ddl).endOfStatement();
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == historySupport.TRIGGER_BASED) {
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == HistorySupport.TRIGGER_BASED) {
// mysql and sql server column type change allowing nulls in the history table column
regenerateHistoryTriggers(alter.getTableName(), HistoryTableUpdate.Change.ALTER, alter.getColumnName());
AlterColumn alterHistoryColumn = new AlterColumn();
@@ -15,6 +15,8 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
private static final String TRIGGER_CLASS = H2HistoryTrigger.class.getName();
public H2HistoryDdl() {
this.sysPeriodType = "timestamp";
this.currentTimestamp = "now()";
}
@Override
@@ -1,10 +1,10 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.io.IOException;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import java.io.IOException;
public class HanaColumnStoreDdl extends AbstractHanaDdl {
public HanaColumnStoreDdl(DatabasePlatform platform) {
@@ -1,8 +1,5 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.io.IOException;
import java.util.List;
import io.ebean.config.PropertiesWrapper;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
@@ -12,6 +9,9 @@ import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.model.MTable;
import java.io.IOException;
import java.util.List;
public class HanaTableDdl extends BaseTableDdl {
private final HanaHistoryDdl historyDdl;
@@ -20,7 +20,6 @@ import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.dbmigration.model.MTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -78,7 +77,7 @@ public class PlatformDdl {
protected String dropIndexIfExists = "drop index if exists ";
protected String alterColumn = "alter column";
protected String alterColumnSuffix = "";
protected String dropUniqueConstraint = "drop constraint";
@@ -86,7 +85,7 @@ public class PlatformDdl {
protected String addConstraint = "add constraint";
protected String addColumn = "add column";
protected String addColumnSuffix = "";
protected String columnSetType = "";
@@ -100,11 +99,11 @@ public class PlatformDdl {
protected String columnSetNull = "set null";
protected String updateNullWithDefault = "update ${table} set ${column} = ${default} where ${column} is null";
protected String createTable = "create table";
protected String dropColumn = "drop column";
protected String dropColumnSuffix = "";
/**
@@ -674,12 +673,12 @@ public class PlatformDdl {
public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
}
/**
* Returns the database-specific "create table" command prefix. For HANA this is
* either "create column table" or "create row table", for all other databases
* it is "create table".
*
*
* @return The "create table" command prefix
*/
public String getCreateTableCommandPrefix() {
@@ -29,6 +29,8 @@ public class CurrentModel {
private final boolean platformTypes;
private final boolean jaxbPresent;
private ModelContainer model;
private ChangeSet changeSet;
@@ -58,6 +60,7 @@ public class CurrentModel {
this.constraintNaming = constraintNaming;
this.maxLength = maxLength(server, constraintNaming);
this.platformTypes = platformTypes;
this.jaxbPresent = server.getServerConfig().getClassLoadConfig().isJavaxJAXBPresent();
}
/**
@@ -130,7 +133,9 @@ public class CurrentModel {
ddl.append(header).append('\n');
}
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
if (jaxbPresent) {
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
}
ddl.append(write.apply().getBuffer());
ddl.append(write.applyForeignKeys().getBuffer());
@@ -1,6 +1,5 @@
package io.ebeaninternal.dbmigration.model;
import io.ebean.annotation.PartitionMode;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns;
import io.ebeaninternal.dbmigration.migration.AddColumn;
@@ -31,14 +31,16 @@ public class MigrationModel {
/**
* Read all the migrations returning the model with all
* the migrations applied in version order.
*
* @param dbinitMigration If true we don't apply model changes, migration is from scratch.
*/
public ModelContainer read() {
public ModelContainer read(boolean dbinitMigration) {
readMigrations();
readMigrations(dbinitMigration);
return model;
}
private void readMigrations() {
private void readMigrations(boolean dbinitMigration) {
// find all the migration xml files
File[] xmlFiles = modelDirectory.listFiles(pathname -> pathname.getName().toLowerCase().endsWith(modelSuffix));
@@ -53,9 +55,11 @@ public class MigrationModel {
// sort into version order before applying
Collections.sort(resources);
for (MigrationResource migrationResource : resources) {
logger.debug("read {}", migrationResource);
model.apply(migrationResource.read(), migrationResource.getVersion());
if (!dbinitMigration) {
for (MigrationResource migrationResource : resources) {
logger.debug("read {}", migrationResource);
model.apply(migrationResource.read(), migrationResource.getVersion());
}
}
// remember the last version
@@ -147,7 +147,11 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
visitScalar(p);
if (p instanceof BeanPropertyAssocOne) {
visitOneImported((BeanPropertyAssocOne)p);
} else {
visitScalar(p);
}
if (embedded.isId()) {
// compound primary key
lastColumn.setPrimaryKey(true);
@@ -10,14 +10,14 @@ import java.util.Map;
class CacheChangeBeanUpdate implements CacheChange {
private final BeanDescriptor<?> desc;
private final Object id;
private final String key;
private final Map<String, Object> changes;
private final boolean updateNaturalKey;
private final long version;
CacheChangeBeanUpdate(BeanDescriptor<?> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
CacheChangeBeanUpdate(BeanDescriptor<?> desc, String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
this.desc = desc;
this.id = id;
this.key = key;
this.changes = changes;
this.updateNaturalKey = updateNaturalKey;
this.version = version;
@@ -25,6 +25,6 @@ class CacheChangeBeanUpdate implements CacheChange {
@Override
public void apply() {
desc.cacheApplyBeanUpdate(id, changes, updateNaturalKey, version);
desc.cacheApplyBeanUpdate(key, changes, updateNaturalKey, version);
}
}
@@ -8,17 +8,17 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
class CacheChangeNaturalKeyPut implements CacheChange {
private final BeanDescriptor<?> descriptor;
private final Object id;
private final Object newKey;
private final String key;
private final String newKey;
CacheChangeNaturalKeyPut(BeanDescriptor<?> descriptor, Object id, Object newKey) {
CacheChangeNaturalKeyPut(BeanDescriptor<?> descriptor, String key, String newKey) {
this.descriptor = descriptor;
this.id = id;
this.key = key;
this.newKey = newKey;
}
@Override
public void apply() {
descriptor.cacheNaturalKeyPut(id, newKey);
descriptor.cacheNaturalKeyPut(key, newKey);
}
}
@@ -152,16 +152,16 @@ public class CacheChangeSet {
/**
* Update a bean entry.
*/
public <T> void addBeanUpdate(BeanDescriptor<T> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
public <T> void addBeanUpdate(BeanDescriptor<T> desc, String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
touchedTables.add(desc.getBaseTable());
entries.add(new CacheChangeBeanUpdate(desc, id, changes, updateNaturalKey, version));
entries.add(new CacheChangeBeanUpdate(desc, key, changes, updateNaturalKey, version));
}
/**
* Update a natural key.
*/
public <T> void addNaturalKeyPut(BeanDescriptor<T> desc, Object id, Object val) {
entries.add(new CacheChangeNaturalKeyPut(desc, id, val));
public <T> void addNaturalKeyPut(BeanDescriptor<T> desc, String key, String val) {
entries.add(new CacheChangeNaturalKeyPut(desc, key, val));
}
/**
@@ -66,7 +66,7 @@ public class CachedBeanDataFromBean {
Object v = aPropertiesNonTransient.getValue(bean);
aPropertiesNonTransient.setValue(sharableBean, v);
}
EntityBeanIntercept intercept = sharableBean._ebean_intercept();
EntityBeanIntercept intercept = sharableBean._ebean_getIntercept();
intercept.setReadOnly(true);
intercept.setLoaded();
return sharableBean;
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.cache;
import io.ebean.annotation.Cache;
import io.ebean.annotation.CacheBeanTuning;
import io.ebean.annotation.CacheQueryTuning;
import io.ebean.cache.QueryCacheEntryValidate;
@@ -123,11 +124,15 @@ class DefaultCacheHolder {
}
private ServerCacheOptions getBeanOptions(Class<?> cls) {
Cache cache = cls.getAnnotation(Cache.class);
boolean nearCache = (cache != null && cache.nearCache());
CacheBeanTuning tuning = cls.getAnnotation(CacheBeanTuning.class);
if (tuning != null) {
return new ServerCacheOptions(tuning).applyDefaults(beanDefault);
return new ServerCacheOptions(nearCache, tuning).applyDefaults(beanDefault);
}
return beanDefault.copy();
return beanDefault.copy(nearCache);
}
}
@@ -19,6 +19,7 @@ public class DefaultServerQueryCache extends DefaultServerCache {
this.queryCacheEntryValidate = config.getQueryCacheEntryValidate();
}
@Override
protected Object unwrapEntry(CacheEntry entry) {
return ((QueryCacheEntry) entry.getValue()).getValue();
}
@@ -29,7 +29,7 @@ public class ClusterManager implements ServerLookup {
public ClusterManager(ContainerConfig config) {
ClusterBroadcastFactory factory = createFactory();
if (factory != null) {
if (factory != null && config.isActive()) {
broadcast = factory.create(this, config);
} else {
broadcast = null;
@@ -59,6 +59,7 @@ public class ClusterManager implements ServerLookup {
}
}
@Override
public EbeanServer getServer(String name) {
synchronized (monitor) {
return serverMap.get(name);
@@ -11,7 +11,6 @@ import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.lib.util.Str;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.persist.TrimLogSql;
import io.ebeaninternal.server.transaction.TransactionManager;
import io.ebeaninternal.server.util.BindParamsParser;
import java.sql.Connection;
@@ -7,6 +7,9 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.UUID;
@@ -341,6 +344,9 @@ public final class BasicTypeConverter implements Serializable {
} else if (value instanceof String) {
return Timestamp.valueOf((String) value);
} else if (value instanceof LocalDateTime) {
return Timestamp.valueOf((LocalDateTime) value);
} else if (value instanceof Number) {
return new Timestamp(((Number) value).longValue());
@@ -361,6 +367,9 @@ public final class BasicTypeConverter implements Serializable {
} else if (value instanceof String) {
return java.sql.Time.valueOf((String) value);
} else if (value instanceof LocalTime) {
return java.sql.Time.valueOf((LocalTime) value);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
@@ -390,6 +399,9 @@ public final class BasicTypeConverter implements Serializable {
} else if (value instanceof Number) {
return new java.sql.Date(((Number) value).longValue());
} else if (value instanceof LocalDate) {
return java.sql.Date.valueOf((LocalDate)value);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
@@ -0,0 +1,117 @@
package io.ebeaninternal.server.core;
import io.ebean.ScriptRunner;
import io.ebean.migration.ddl.DdlRunner;
import io.ebean.migration.runner.ScriptTransform;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.util.UrlHelper;
import javax.persistence.PersistenceException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
class DScriptRunner implements ScriptRunner {
private static final String NEWLINE = "\n";
private final SpiEbeanServer server;
DScriptRunner(SpiEbeanServer server) {
this.server = server;
}
@Override
public void run(String path) {
run(path, null);
}
@Override
public void run(String path, Map<String, String> placeholderMap) {
run(this.getClass().getResource(path), path, placeholderMap);
}
@Override
public void run(URL resource) {
run(resource, null, null);
}
@Override
public void run(URL resource, Map<String, String> placeholderMap) {
run(resource, null, placeholderMap);
}
private void run(URL resource, String scriptName, Map<String, String> placeholderMap) {
if (resource == null) {
throw new IllegalArgumentException("resource is null?");
}
if (scriptName == null) {
scriptName = resource.getFile();
}
String content = content(resource);
runScript(content, scriptName, placeholderMap);
}
private String content(URL resource) {
if (resource == null) {
throw new IllegalArgumentException("resource is null?");
}
try (InputStream inputStream = UrlHelper.openNoCache(resource)) {
return readContent(new InputStreamReader(inputStream));
} catch (IOException e) {
throw new PersistenceException("Failed to read script content", e);
}
}
/**
* Execute all the DDL statements in the script.
*/
private void runScript(String content, String scriptName, Map<String, String> placeholderMap) {
try {
if (placeholderMap != null) {
content = ScriptTransform.build(null, placeholderMap).transform(content);
}
try (Connection connection = obtainConnection()) {
DdlRunner runner = new DdlRunner(false, scriptName);
runner.runAll(content, connection);
connection.commit();
}
} catch (SQLException e) {
throw new PersistenceException("Failed to run script", e);
}
}
private Connection obtainConnection() {
try {
return server.getPluginApi().getDataSource().getConnection();
} catch (SQLException e) {
throw new PersistenceException("Failed to obtain connection to run script", e);
}
}
private String readContent(Reader reader) throws IOException {
StringBuilder buf = new StringBuilder();
try (LineNumberReader lineReader = new LineNumberReader(reader)) {
String line;
while ((line = lineReader.readLine()) != null) {
buf.append(line).append(NEWLINE);
}
return buf.toString();
}
}
}
@@ -6,6 +6,8 @@ import io.ebeaninternal.server.lib.DaemonScheduleThreadPool;
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
@@ -64,6 +66,46 @@ public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
}
}
@Override
public ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit unit) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return schedulePool.schedule(r, delay, unit);
} else {
return schedulePool.schedule(() -> {
MDC.setContextMap(map);
try {
r.run();
} finally {
MDC.clear();
}
}, delay, unit);
}
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> c, long delay, TimeUnit unit) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return schedulePool.schedule(c, delay, unit);
} else {
return schedulePool.schedule(new Callable<V>() {
@Override
public V call() throws Exception {
MDC.setContextMap(map);
try {
return c.call();
} finally {
MDC.clear();
}
}
}, delay, unit);
}
}
@Override
public void shutdown() {
pool.shutdown();
@@ -88,4 +88,10 @@ public class DefaultBeanState implements BeanState {
public void resetForInsert() {
intercept.setNew();
}
@Override
public Map<String, Exception> getLoadErrors() {
return intercept.getLoadErrors();
}
}
@@ -7,6 +7,10 @@ import io.ebean.config.TenantMode;
import io.ebean.config.UnderscoreNamingConvention;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.datasource.DataSourceAlertFactory;
import io.ebean.datasource.DataSourceConfig;
import io.ebean.datasource.DataSourceFactory;
import io.ebean.datasource.DataSourcePoolListener;
import io.ebean.service.SpiContainer;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
@@ -15,10 +19,6 @@ import io.ebeaninternal.server.cluster.ClusterManager;
import io.ebeaninternal.server.core.bootup.BootupClassPathSearch;
import io.ebeaninternal.server.core.bootup.BootupClasses;
import io.ebeaninternal.server.lib.ShutdownManager;
import org.avaje.datasource.DataSourceAlertFactory;
import org.avaje.datasource.DataSourceConfig;
import org.avaje.datasource.DataSourceFactory;
import org.avaje.datasource.DataSourcePoolListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -249,7 +249,7 @@ public class DefaultContainer implements SpiContainer {
return null;
}
DataSourceFactory factory = config.service(DataSourceFactory.class);
DataSourceFactory factory = DataSourceFactory.get();
if (factory == null) {
throw new IllegalStateException("No DataSourceFactory service implementation found in class path."
+ " Probably missing dependency to avaje-datasource?");
@@ -6,8 +6,10 @@ import io.ebean.meta.MetaInfoManager;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaOrmQueryNode;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaQueryPlan;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanRequest;
import java.util.ArrayList;
import java.util.List;
@@ -23,6 +25,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
this.server = server;
}
@Override
public List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request) {
return server.collectQueryPlans(request);
}
@Override
public void visitMetrics(MetricVisitor visitor) {
server.visitMetrics(visitor);
@@ -22,6 +22,7 @@ import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.RowConsumer;
import io.ebean.RowMapper;
import io.ebean.ScriptRunner;
import io.ebean.SqlQuery;
import io.ebean.SqlRow;
import io.ebean.SqlUpdate;
@@ -53,13 +54,16 @@ import io.ebean.event.BeanPersistController;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
import io.ebean.meta.MetaQueryPlan;
import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanRequest;
import io.ebean.plugin.BeanType;
import io.ebean.plugin.Plugin;
import io.ebean.plugin.Property;
import io.ebean.plugin.SpiServer;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.LoadBeanRequest;
import io.ebeaninternal.api.LoadManyRequest;
import io.ebeaninternal.api.ScopedTransaction;
@@ -119,6 +123,8 @@ import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Clock;
import java.util.Collection;
import java.util.Collections;
@@ -185,6 +191,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final DdlGenerator ddlGenerator;
private final ScriptRunner scriptRunner;
private final ExpressionFactory expressionFactory;
private final SpiBackgroundExecutor backgroundExecutor;
@@ -291,6 +299,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.serverPlugins = config.getPlugins();
this.ddlGenerator = new DdlGenerator(this, serverConfig);
this.scriptRunner = new DScriptRunner(this);
configureServerPlugins();
@@ -370,6 +379,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return databasePlatform;
}
@Override
public ScriptRunner script() {
return scriptRunner;
}
@Override
public DdlHandler createDdlHandler() {
return PlatformDdlBuilder.create(databasePlatform).createDdlHandler(serverConfig);
@@ -1349,6 +1363,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return !ids.isEmpty();
}
@Override
public <T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction) {
ormQuery.setMaxRows(1);
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQuery, transaction);
try {
request.initTransIfRequired();
List<Object> ids = request.findIds();
return !ids.isEmpty();
} finally {
request.endTransIfRequired();
}
}
@Override
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
@@ -1430,10 +1459,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return queryFuture;
}
public <T> FutureRowCount<T> findFutureRowCount(Query<T> q, Transaction t) {
return findFutureCount(q, t);
}
@Override
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
@@ -2326,10 +2351,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void slowQueryCheck(long timeMicros, int rowCount, SpiQuery<?> query) {
if (timeMicros > slowQueryMicros) {
if (slowQueryListener != null) {
slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.getParentNode()));
}
if (timeMicros > slowQueryMicros && slowQueryListener != null) {
slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.getParentNode()));
}
}
@@ -2351,7 +2374,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
Object id = idProperty.getVal(entityBean);
if (entityBean._ebean_intercept().isNew() && id != null) {
if (entityBean._ebean_getIntercept().isNew() && id != null) {
// Primary Key is changeable only on new models - so skip check if we are not
// new.
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
@@ -2381,7 +2404,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
Query<?> query = new DefaultOrmQuery<>(beanDesc, this, expressionFactory);
ExpressionList<?> exprList = query.where();
if (!entityBean._ebean_intercept().isNew()) {
if (!entityBean._ebean_getIntercept().isNew()) {
// if model is not new, exclude ourself.
exprList.ne(idProperty.getName(), idProperty.getVal(entityBean));
}
@@ -2416,4 +2439,19 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
visitor.visitEnd();
}
public List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request) {
Connection connection = null;
try {
connection = getDataSource().getConnection();
request.setConnection(connection);
beanDescriptorManager.collectQueryPlans(request);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcClose.close(connection);
}
return request.getPlans();
}
}
@@ -239,6 +239,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
return this;
}
@Override
public SqlUpdate setNextParameter(Object value) {
setParameter(++addPos, value);
return this;
@@ -15,6 +15,7 @@ import io.ebean.config.ServerConfig;
import io.ebean.config.SlowQueryListener;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.datasource.DataSourcePool;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
@@ -87,7 +88,6 @@ import io.ebeanservice.docstore.api.DocStoreFactory;
import io.ebeanservice.docstore.api.DocStoreIntegration;
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import io.ebeanservice.docstore.none.NoneDocStoreFactory;
import org.avaje.datasource.DataSourcePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -2,8 +2,8 @@ package io.ebeaninternal.server.core;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.TenantCatalogProvider;
import io.ebean.datasource.DataSourcePool;
import io.ebeaninternal.server.transaction.DataSourceSupplier;
import org.avaje.datasource.DataSourcePool;
import javax.sql.DataSource;
import java.io.PrintWriter;
@@ -2,8 +2,8 @@ package io.ebeaninternal.server.core;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.TenantSchemaProvider;
import io.ebean.datasource.DataSourcePool;
import io.ebeaninternal.server.transaction.DataSourceSupplier;
import org.avaje.datasource.DataSourcePool;
import javax.sql.DataSource;
import java.io.PrintWriter;
@@ -173,6 +173,16 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private int pendingPostUpdateNotify;
/**
* Set to true when post execute has occured (so includes batch flush).
*/
private boolean postExecute;
/**
* Set to true after many properties have been persisted (so includes element collections).
*/
private boolean complete;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
@@ -441,10 +451,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
/**
* Return true if this change should notify cache, listener or doc store.
* Return true if this change should notify persist listener or doc store (and keep the request).
*/
public boolean isNotify() {
return notifyCache || isNotifyPersistListener() || isDocStoreNotify();
private boolean isNotifyListeners() {
return isNotifyPersistListener() || isDocStoreNotify();
}
/**
@@ -918,7 +928,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
@Override
public void postExecute() {
postExecute = true;
if (controller != null) {
controllerPost();
}
@@ -939,7 +949,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
postInsert();
}
addEvent();
addPostCommitListeners();
notifyCacheOnPostExecute();
if (isLogSummary()) {
logSummary();
@@ -1017,14 +1028,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
/**
* Add the bean to the TransactionEvent. This will be used by TransactionManager to sync Cache,
* Cluster and text indexes.
* Add the request to TransactionEvent if there are post commit listeners.
*/
private void addEvent() {
private void addPostCommitListeners() {
TransactionEvent event = transaction.getEvent();
if (event != null) {
event.add(this);
if (event != null && isNotifyListeners()) {
event.addListenerNotify(this);
}
}
@@ -1087,23 +1096,62 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
/**
* Check if any of its many properties where cascade saved and hence we need to update related
* many property caches.
* Completed insert or delete request. Do cache notify in non-batched case.
*/
public void checkUpdatedManysOnly() {
public void complete() {
notifyCacheOnComplete();
}
/**
* Completed update request handling cases for element collection and where ONLY
* many properties were updated.
*/
public void completeUpdate() {
if (!dirty && updatedManys != null) {
// set the flag and register for post commit processing if there
// is caching or registered listeners
if (idValue == null) {
this.idValue = beanDescriptor.getId(entityBean);
}
// not dirty so postExecute() will never be called
// set true to trigger cache notify if needed
postExecute = true;
updatedManysOnly = true;
setNotifyCache();
addEvent();
addPostCommitListeners();
}
notifyCacheOnComplete();
postUpdateNotify();
}
/**
* Notify cache when using batched persist.
*/
private void notifyCacheOnPostExecute() {
postExecute = true;
if (notifyCache && complete) {
// add cache notification (on batch persist)
TransactionEvent event = transaction.getEvent();
if (event != null) {
notifyCache(event.obtainCacheChangeSet());
}
}
}
/**
* Notify cache when not use batched persist.
*/
private void notifyCacheOnComplete() {
complete = true;
if (notifyCache && postExecute) {
// add cache notification (on non-batch persist)
TransactionEvent event = transaction.getEvent();
if (event != null) {
notifyCache(event.obtainCacheChangeSet());
}
}
}
/**
* For requests that update document store add this event to either the list
* of queue events or list of update events.
@@ -1357,6 +1405,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
boolean updateNaturalKey = false;
String key = beanDescriptor.cacheKey(idValue);
Map<String, Object> changes = new LinkedHashMap<>();
EntityBean bean = getEntityBean();
boolean[] dirtyProperties = getDirtyProperties();
@@ -1369,7 +1419,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
changes.put(property.getName(), val);
if (property.isNaturalKey()) {
updateNaturalKey = true;
changeSet.addNaturalKeyPut(beanDescriptor, idValue, val);
changeSet.addNaturalKeyPut(beanDescriptor, key, val.toString());
}
}
}
@@ -1380,7 +1430,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
changes.putAll(collectionChanges);
}
changeSet.addBeanUpdate(beanDescriptor, idValue, changes, updateNaturalKey, getVersion());
changeSet.addBeanUpdate(beanDescriptor, key, changes, updateNaturalKey, getVersion());
}
}
@@ -1390,7 +1440,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public void setImportedOrphanForRemoval(BeanPropertyAssocOne<?> prop) {
Object orphan = getOrigValue(prop);
if (orphan instanceof EntityBean) {
orphanBean = (EntityBean)orphan;
orphanBean = (EntityBean) orphan;
}
}
@@ -1,35 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebeaninternal.server.lib.ShutdownManager;
import javax.servlet.ServletContextEvent;
/**
* Deprecated - migrate to io.ebean.event.ServletContextListener.
* <p>
* Listens for webserver server starting and stopping events.
* <p>
* Register this listener in the web.xml configuration file. This will listen
* for startup and shutdown events.
* </p>
*/
@Deprecated
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) {
}
}
@@ -1,7 +1,7 @@
package io.ebeaninternal.server.core;
import io.ebean.datasource.DataSourcePool;
import io.ebeaninternal.server.transaction.DataSourceSupplier;
import org.avaje.datasource.DataSourcePool;
import javax.sql.DataSource;
import java.sql.Connection;
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.core.bootup;
import io.ebean.util.StringHelper;
import io.ebeaninternal.util.UrlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,7 +60,7 @@ class ManifestReader {
try {
Enumeration<URL> resources = classLoader.getResources(resourcePath);
while (resources.hasMoreElements()) {
try (InputStream is = resources.nextElement().openStream()) {
try (InputStream is = UrlHelper.openNoCache(resources.nextElement())) {
read(new Manifest(is));
}
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.type.DataReader;
import java.sql.SQLException;
@@ -16,9 +17,16 @@ abstract class AssocOneHelp {
protected final BeanDescriptor<?> target;
private final String path;
AssocOneHelp(BeanPropertyAssocOne<?> property) {
this(property, null);
}
AssocOneHelp(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
this.property = property;
this.target = property.targetDescriptor;
this.path = (embeddedPrefix == null) ? property.name : embeddedPrefix + "." + property.name;
}
/**
@@ -28,6 +36,24 @@ abstract class AssocOneHelp {
property.targetIdBinder.loadIgnore(ctx);
}
/**
* Read and return the property.
*/
Object read(DataReader reader) throws SQLException {
return property.read(reader);
}
/**
* Read and return the property setting value into the bean.
*/
Object readSet(DataReader reader, EntityBean bean) throws SQLException {
Object val = read(reader);
if (bean != null) {
property.setValue(bean, val);
}
return val;
}
/**
* Read and return the bean.
*/
@@ -48,7 +74,7 @@ abstract class AssocOneHelp {
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (!disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
ctx.register(path, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.deploy;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.type.DataReader;
import java.sql.SQLException;
@@ -9,14 +10,41 @@ import java.sql.SQLException;
*/
final class AssocOneHelpEmbedded extends AssocOneHelp {
public AssocOneHelpEmbedded(BeanPropertyAssocOne<?> property) {
AssocOneHelpEmbedded(BeanPropertyAssocOne<?> property) {
super(property);
}
@Override
void loadIgnore(DbReadContext ctx) {
for (int i = 0; i < property.embeddedProps.length; i++) {
property.embeddedProps[i].loadIgnore(ctx);
for (BeanProperty property : property.embeddedProps) {
property.loadIgnore(ctx);
}
}
@Override
Object readSet(DataReader reader, EntityBean bean) throws SQLException {
Object dbVal = read(reader);
if (bean != null) {
property.setValue(bean, dbVal);
}
return dbVal;
}
@Override
Object read(DataReader reader) throws SQLException {
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
boolean notNull = false;
for (BeanProperty property : property.embeddedProps) {
Object value = property.readSet(reader, embeddedBean);
if (value != null) {
notNull = true;
}
}
if (notNull) {
return embeddedBean;
} else {
return null;
}
}
@@ -39,8 +67,8 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
boolean notNull = false;
for (int i = 0; i < property.embeddedProps.length; i++) {
Object value = property.embeddedProps[i].readSet(ctx, embeddedBean);
for (BeanProperty property : property.embeddedProps) {
Object value = property.readSet(ctx, embeddedBean);
if (value != null) {
notNull = true;
}
@@ -55,8 +83,8 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
for (int i = 0; i < property.embeddedProps.length; i++) {
property.embeddedProps[i].appendSelect(ctx, subQuery);
for (BeanProperty property : property.embeddedProps) {
property.appendSelect(ctx, subQuery);
}
}
}
@@ -54,7 +54,7 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
// for inheritance hierarchy create the correct type for this row...
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (disableLazyLoading) {
if (!disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
@@ -5,8 +5,8 @@ package io.ebeaninternal.server.deploy;
*/
class AssocOneHelpRefSimple extends AssocOneHelp {
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property) {
super(property);
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
super(property, embeddedPrefix);
}
/**
@@ -42,6 +42,7 @@ abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {
}
}
@SuppressWarnings("rawtypes")
@Override
public Collection underlying(Object value) {
if (value instanceof BeanCollection) {
@@ -39,6 +39,7 @@ public interface BeanCollectionHelp<T> extends CQueryCollectionAdd<T> {
/**
* Create an empty collection of the correct type without a parent bean.
*/
@Override
BeanCollection<T> createEmptyNoParent();
/**
@@ -49,6 +50,7 @@ public interface BeanCollectionHelp<T> extends CQueryCollectionAdd<T> {
/**
* Add a bean to the List Set or Map.
*/
@Override
void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck);
/**
@@ -22,7 +22,7 @@ public class BeanCollectionUtil {
}
/**
* Return the details of the collection or map taking care to avoid
* Return the details (map entry set) of the collection or map taking care to avoid
* unnecessary fetching of the data.
*/
public static Collection<?> getActualEntries(Object o) {
@@ -48,4 +48,32 @@ public class BeanCollectionUtil {
}
throw new PersistenceException("expecting a Map or Collection but got [" + o.getClass().getName() + "]");
}
/**
* Return the details (map values) of the collection or map taking care to avoid
* unnecessary fetching of the data.
*/
public static Collection<?> getActualDetails(Object o) {
if (o == null) {
return null;
}
if (o instanceof BeanCollection<?>) {
BeanCollection<?> bc = (BeanCollection<?>) o;
if (!bc.isPopulated()) {
return null;
}
// For maps this is a collection of Map.Entry, otherwise it
// returns a collection of beans
return bc.getActualDetails();
}
if (o instanceof Map<?, ?>) {
// yes, we want the entrySet (to set the keys)
return ((Map<?, ?>) o).values();
} else if (o instanceof Collection<?>) {
return ((Collection<?>) o);
}
throw new PersistenceException("expecting a Map or Collection but got [" + o.getClass().getName() + "]");
}
}
@@ -7,7 +7,6 @@ import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.ValuePair;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PartitionMode;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -30,6 +29,7 @@ import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanRequest;
import io.ebean.plugin.BeanDocType;
import io.ebean.plugin.BeanType;
import io.ebean.plugin.ExpressionPath;
@@ -61,6 +61,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists;
import io.ebeaninternal.server.el.ElComparator;
import io.ebeaninternal.server.el.ElComparatorCompound;
import io.ebeaninternal.server.el.ElComparatorNoop;
import io.ebeaninternal.server.el.ElComparatorProperty;
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
import io.ebeaninternal.server.el.ElPropertyDeploy;
@@ -497,7 +498,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
this.softDeleteProperty = listHelper.getSoftDeleteProperty();
this.softDelete = (softDeleteProperty != null);
// if formula is set, the property is virtual only (there is no column in db) the formula must evaluate to true,
// if there is a join to a deleted bean. Example: '@Formula(select = "${ta}.user_id is null")'
// this is required to support markAsDelete on beans that may have no FK constraint.
this.softDelete = (softDeleteProperty != null && !softDeleteProperty.isFormula());
this.idProperty = listHelper.getId();
this.versionProperty = listHelper.getVersionProperty();
this.unmappedJson = listHelper.getUnmappedJson();
@@ -720,16 +724,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
inheritInfo.setDescriptor(this);
}
if (isEmbedded()) {
// initialise all the properties
for (BeanProperty prop : propertiesAll()) {
prop.initialise(initContext);
}
} else {
// initialise just the Id properties
if (idProperty != null) {
idProperty.initialise(initContext);
}
// initialise just the Id property only
if (idProperty != null) {
idProperty.initialise(initContext);
}
}
@@ -755,14 +752,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
}
if (!isEmbedded()) {
// initialise all the non-id properties
for (BeanProperty prop : propertiesAll()) {
if (!prop.isId()) {
prop.initialise(initContext);
}
prop.registerColumn(this, null);
// initialise all the non-id properties
for (BeanProperty prop : propertiesAll()) {
if (!prop.isId()) {
prop.initialise(initContext);
}
prop.registerColumn(this, null);
}
if (unidirectional != null) {
@@ -1483,30 +1478,33 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return a bean from the bean cache (or null).
*/
public T cacheBeanGet(Object id, Boolean readOnly, PersistenceContext context) {
return cacheHelp.beanCacheGet(id, readOnly, context);
return cacheHelp.beanCacheGet(cacheKey(id), readOnly, context);
}
/**
* Remove a collection of beans from the cache given the ids.
*/
public void cacheApplyInvalidate(Collection<Object> ids) {
cacheHelp.beanCacheApplyInvalidate(ids);
List<String> keys = new ArrayList<>(ids.size());
for (Object id : ids) {
keys.add(cacheKey(id));
}
cacheHelp.beanCacheApplyInvalidate(keys);
}
/**
* Hit the bean cache trying to load a list/batch of entities.
* Return the set of entities that were successfully loaded from L2 cache.
*/
public Set<EntityBeanIntercept> cacheBeanLoadAll(List<EntityBeanIntercept> list, PersistenceContext persistenceContext, int lazyLoadProperty, String propertyName) {
return cacheHelp.beanCacheLoadAll(list, persistenceContext, lazyLoadProperty, propertyName);
}
/**
* Returns true if it managed to populate/load the bean from the cache.
*/
public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
return cacheHelp.beanCacheLoad(bean, ebi, id, context);
}
/**
* Returns true if it managed to populate/load the bean from the cache.
*/
public boolean cacheBeanLoad(EntityBeanIntercept ebi, PersistenceContext context) {
EntityBean bean = ebi.getOwner();
Object id = getId(bean);
return cacheBeanLoad(bean, ebi, id, context);
return cacheHelp.beanCacheLoad(bean, ebi, cacheKey(id), context);
}
/**
@@ -1516,8 +1514,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return cacheHelp.naturalKeyLookup(context, keys);
}
public void cacheNaturalKeyPut(Object id, Object newKey) {
cacheHelp.cacheNaturalKeyPut(id, newKey);
public void cacheNaturalKeyPut(String key, String newKey) {
cacheHelp.cacheNaturalKeyPut(key, newKey);
}
/**
@@ -1565,8 +1563,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Apply the update to the cache.
*/
public void cacheApplyBeanUpdate(Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
cacheHelp.cacheBeanUpdate(id, changes, updateNaturalKey, version);
public void cacheApplyBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
cacheHelp.cacheBeanUpdate(key, changes, updateNaturalKey, version);
}
/**
@@ -1648,6 +1646,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return new DeployUpdateParser(this).parse(ormUpdateStatement);
}
public void collectQueryPlans(QueryPlanRequest request) {
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (request.includeLabel(queryPlan.getLabel())) {
queryPlan.collectQueryPlan(request);
}
}
}
/**
* Visit all the ORM query plan metrics (includes UpdateQuery with updates and deletes).
*/
@@ -1835,6 +1841,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Return the IdBinder which is helpful for handling the various types of Id.
*/
@Override
public IdBinder getIdBinder() {
return idBinder;
}
@@ -1943,6 +1950,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Creates a new entity bean without invoking {@link BeanPostConstructListener#postCreate(Object)}
*/
@Override
public EntityBean createEntityBean() {
return createEntityBean(false);
}
@@ -1975,7 +1983,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
CachedBeanData d = cacheHelp.beanCacheGetData(id);
CachedBeanData d = cacheHelp.beanCacheGetData(cacheKey(id));
if (d != null) {
Object shareableBean = d.getSharableBean();
if (shareableBean != null) {
@@ -2213,6 +2221,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Put the bean into the persistence context if it is absent.
*/
@Override
public Object contextPutIfAbsent(PersistenceContext pc, Object id, EntityBean localBean) {
return pc.putIfAbsent(rootBeanType, id, localBean);
}
@@ -2255,6 +2264,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return (idProperty == null) ? null : idProperty.getValueIntercept(bean);
}
/**
* Return the cache key for the given bean (based on id value).
*/
public String cacheKeyForBean(EntityBean bean) {
return cacheKey(idProperty.getValue(bean));
}
/**
* Return the cache key for the given id value.
*/
public String cacheKey(Object id) {
return idBinder.cacheKey(id);
}
@Override
public Object beanId(Object bean) {
return getId((EntityBean) bean);
@@ -2437,7 +2460,15 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private ElComparator<T> createPropertyComparator(SortByClause.Property sortProp) {
ElPropertyValue elGetValue = getElGetValue(sortProp.getName());
if (elGetValue == null) {
logger.error("Sort property [" + sortProp + "] not found in " + beanType + ". Cannot sort.");
return new ElComparatorNoop<>();
}
if (elGetValue.isAssocMany()) {
logger.error("Sort property [" + sortProp + "] in " + beanType + " is a many-property. Cannot sort.");
return new ElComparatorNoop<>();
}
Boolean nullsHigh = sortProp.getNullsHigh();
if (nullsHigh == null) {
nullsHigh = Boolean.TRUE;
@@ -2639,6 +2670,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return autoTunable;
}
@Override
public boolean isElementType() {
return false;
}
@@ -2647,6 +2679,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Returns the Inheritance mapping information. This will be null if this type
* of bean is not involved in any ORM inheritance mapping.
*/
@Override
public InheritInfo getInheritInfo() {
return inheritInfo;
}
@@ -2815,6 +2848,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Returns true if this bean is based on RawSql.
*/
@Override
public boolean isRawSqlBased() {
return EntityType.SQL == entityType;
}
@@ -2869,6 +2903,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Return the base table to use given the query temporal mode.
*/
@Override
public String getBaseTable(SpiQuery.TemporalMode mode) {
switch (mode) {
case DRAFT:
@@ -2896,6 +2931,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return readAuditing;
}
@Override
public boolean isSoftDelete() {
return softDelete;
}
@@ -2908,10 +2944,25 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return softDeleteProperty.getSoftDeleteDbSet();
}
@Override
public String getSoftDeletePredicate(String tableAlias) {
return softDeleteProperty.getSoftDeleteDbPredicate(tableAlias);
}
@Override
public void markAsDeleted(EntityBean bean) {
if (softDeleteProperty == null) {
Object id = getId(bean);
logger.info("(Lazy) loading unsuccessful for type:{} id:{} - expecting when bean has been deleted", getName(), id);
bean._ebean_getIntercept().setLazyLoadFailure(id);
} else {
setSoftDeleteValue(bean);
bean._ebean_getIntercept().setLoaded();
setAllLoaded(bean);
}
}
/**
* Return true if this entity type is draftable.
*/
@@ -2980,6 +3031,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Set the draft to true for this entity bean instance.
* This bean is being loaded via asDraft() query.
*/
@Override
public void setDraft(EntityBean entityBean) {
if (draft != null) {
draft.setValue(entityBean, true);
@@ -3033,6 +3085,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Return true if this entity bean has history support.
*/
@Override
public boolean isHistorySupport() {
return historySupport;
}
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -282,7 +283,7 @@ final class BeanDescriptorCacheHelp<T> {
if (many.isElementCollection()) {
// held as part of the bean cache so skip
return false;
return false;
}
CachedManyIds entry = manyPropGet(parentId, many.getName());
@@ -317,7 +318,7 @@ final class BeanDescriptorCacheHelp<T> {
try {
// add as JSON to bean cache
String asJson = many.jsonWriteCollection(details);
Map<String,Object> changes = new HashMap<>();
Map<String, Object> changes = new HashMap<>();
changes.put(many.getName(), asJson);
CachedBeanData newData = data.update(changes, data.getVersion());
@@ -348,13 +349,12 @@ final class BeanDescriptorCacheHelp<T> {
private CachedManyIds createManyIds(BeanPropertyAssocMany<?> many, Object details) {
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
Collection<?> actualDetails = BeanCollectionUtil.getActualEntries(details);
Collection<?> actualDetails = BeanCollectionUtil.getActualDetails(details);
if (actualDetails == null) {
return null;
}
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
List<Object> idList = new ArrayList<>(actualDetails.size());
for (Object bean : actualDetails) {
idList.add(targetDescriptor.getId((EntityBean) bean));
@@ -476,7 +476,7 @@ final class BeanDescriptorCacheHelp<T> {
private Class<?> theClassOf(Collection<EntityBean> beans) {
if (beans instanceof List) {
return ((List<?>)beans).get(0).getClass();
return ((List<?>) beans).get(0).getClass();
}
return beans.iterator().next().getClass();
}
@@ -495,20 +495,20 @@ final class BeanDescriptorCacheHelp<T> {
void beanCachePutAllDirect(Collection<EntityBean> beans) {
Map<Object,Object> natKeys = null;
Map<Object, Object> natKeys = null;
if (naturalKey != null) {
natKeys = new LinkedHashMap<>();
}
Map<Object,Object> map = new LinkedHashMap<>();
Map<Object, Object> map = new LinkedHashMap<>();
for (EntityBean bean : beans) {
CachedBeanData beanData = beanExtractData(desc, bean);
Object id = desc.getId(bean);
map.put(id, beanData);
String key = desc.cacheKeyForBean(bean);
map.put(key, beanData);
if (naturalKey != null) {
Object naturalKey = calculateNaturalKey(beanData);
if (naturalKey != null) {
natKeys.put(naturalKey, id);
natKeys.put(naturalKey, key);
}
}
}
@@ -526,32 +526,33 @@ final class BeanDescriptorCacheHelp<T> {
}
/**
* Put the bean into the bean cache.
*/
* Put the bean into the bean cache.
*/
void beanCachePutDirect(EntityBean bean) {
CachedBeanData beanData = beanExtractData(desc, bean);
Object id = desc.getId(bean);
String key = desc.cacheKeyForBean(bean);
if (beanLog.isDebugEnabled()) {
beanLog.debug(" PUT {}({}) data:{}", cacheName, id, beanData);
beanLog.debug(" PUT {}({}) data:{}", cacheName, key, beanData);
}
getBeanCache().put(id, beanData);
getBeanCache().put(key, beanData);
if (naturalKey != null) {
Object naturalKey = calculateNaturalKey(beanData);
String naturalKey = calculateNaturalKey(beanData);
if (naturalKey != null) {
if (natLog.isDebugEnabled()) {
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, id);
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, key);
}
naturalKeyCache.put(naturalKey, id);
naturalKeyCache.put(naturalKey, key);
}
}
}
private Object calculateNaturalKey(CachedBeanData beanData) {
private String calculateNaturalKey(CachedBeanData beanData) {
if (naturalKey.length == 1) {
return beanData.getData(naturalKey[0]);
Object data = beanData.getData(naturalKey[0]);
return (data == null) ? null : data.toString();
}
StringBuilder sb = new StringBuilder();
for (String key : naturalKey) {
@@ -564,12 +565,12 @@ final class BeanDescriptorCacheHelp<T> {
return sb.toString();
}
CachedBeanData beanCacheGetData(Object id) {
return (CachedBeanData) getBeanCache().get(id);
CachedBeanData beanCacheGetData(String key) {
return (CachedBeanData) getBeanCache().get(key);
}
T beanCacheGet(Object id, Boolean readOnly, PersistenceContext context) {
T bean = beanCacheGetInternal(id, readOnly, context);
T beanCacheGet(String key, Boolean readOnly, PersistenceContext context) {
T bean = beanCacheGetInternal(key, readOnly, context);
if (bean != null) {
setupContext(bean, context);
}
@@ -579,19 +580,19 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Return a bean from the bean cache.
*/
private T beanCacheGetInternal(Object id, Boolean readOnly, PersistenceContext context) {
private T beanCacheGetInternal(String key, Boolean readOnly, PersistenceContext context) {
CachedBeanData data = (CachedBeanData) getBeanCache().get(id);
CachedBeanData data = (CachedBeanData) getBeanCache().get(key);
if (data == null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - cache miss", cacheName, id);
beanLog.trace(" GET {}({}) - cache miss", cacheName, key);
}
return null;
}
if (beanLog.isTraceEnabled()) {
beanLog.trace(" GET {}({}) - hit", cacheName, id);
beanLog.trace(" GET {}({}) - hit", cacheName, key);
}
return convertToBean(id, readOnly, context, data);
return convertToBean(key, readOnly, context, data);
}
@SuppressWarnings("unchecked")
@@ -687,12 +688,12 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Remove a bean from the cache given its Id.
*/
void beanCacheApplyInvalidate(Collection<Object> ids) {
void beanCacheApplyInvalidate(Collection<String> keys) {
if (beanCache != null) {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" REMOVE {}({})", cacheName, ids);
beanLog.debug(" REMOVE {}({})", cacheName, keys);
}
beanCache.removeAll(new HashSet<>(ids));
beanCache.removeAll(new HashSet<>(keys));
}
for (BeanPropertyAssocOne<?> imported : propertiesOneImported) {
imported.cacheClear();
@@ -700,28 +701,77 @@ final class BeanDescriptorCacheHelp<T> {
}
/**
* Returns true if it managed to populate/load the bean from the cache.
* Load a batch of entities from L2 bean cache checking the lazy loaded property is loaded.
*/
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
Set<EntityBeanIntercept> beanCacheLoadAll(List<EntityBeanIntercept> list, PersistenceContext context, int lazyLoadProperty, String propertyName) {
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id);
Map<Object, EntityBeanIntercept> ebis = new HashMap<>();
for (EntityBeanIntercept ebi : list) {
ebis.put(desc.cacheKeyForBean(ebi.getOwner()), ebi);
}
Map<Object, Object> hits = getBeanCache().getAll(ebis.keySet());
if (beanLog.isTraceEnabled()) {
beanLog.trace(" LOAD ALL {}({}) - got hits ({})", cacheName, ebis.keySet(), hits.size());
}
Set<EntityBeanIntercept> loaded = new HashSet<>();
Iterator<Map.Entry<Object, Object>> iterator = hits.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> hit = iterator.next();
Object key = hit.getKey();
EntityBeanIntercept ebi = ebis.remove(key);
CachedBeanData cacheData = (CachedBeanData) hit.getValue();
if (lazyLoadProperty > -1 && !cacheData.isLoaded(propertyName)) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, key, propertyName);
}
iterator.remove();
} else {
CachedBeanDataToBean.load(desc, ebi.getOwner(), cacheData, context);
loaded.add(ebi);
if (beanLog.isDebugEnabled()) {
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
}
}
}
if (!ebis.isEmpty() && beanLog.isTraceEnabled()) {
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, ebis.keySet());
}
return loaded;
}
/**
* Returns true if it managed to populate/load the single bean from the cache.
*/
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, String key, PersistenceContext context) {
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(key);
if (cacheData == null) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, id);
beanLog.trace(" LOAD {}({}) - cache miss", cacheName, key);
}
return false;
}
int lazyLoadProperty = ebi.getLazyLoadPropertyIndex();
if (lazyLoadProperty > -1 && !cacheData.isLoaded(ebi.getLazyLoadProperty())) {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, id, ebi.getLazyLoadProperty());
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, key, ebi.getLazyLoadProperty());
}
return false;
}
CachedBeanDataToBean.load(desc, bean, cacheData, context);
if (beanLog.isDebugEnabled()) {
beanLog.debug(" LOAD {}({}) - hit", cacheName, id);
beanLog.debug(" LOAD {}({}) - hit", cacheName, key);
}
return true;
}
@@ -737,7 +787,7 @@ final class BeanDescriptorCacheHelp<T> {
*/
void persistDeleteIds(Collection<Object> ids, CacheChangeSet changeSet) {
if (invalidateQueryCache) {
changeSet.addInvalidate(desc);
changeSet.addInvalidate(desc);
} else {
queryCacheClear(changeSet);
if (beanCache != null) {
@@ -794,7 +844,6 @@ final class BeanDescriptorCacheHelp<T> {
// query caching only
return;
}
List<BeanPropertyAssocMany<?>> manyCollections = updateRequest.getUpdatedManyCollections();
if (manyCollections != null) {
for (BeanPropertyAssocMany<?> many : manyCollections) {
@@ -835,42 +884,42 @@ final class BeanDescriptorCacheHelp<T> {
}
}
void cacheNaturalKeyPut(Object id, Object newKey) {
void cacheNaturalKeyPut(String key, String newKey) {
if (newKey != null) {
naturalKeyCache.put(newKey, id);
naturalKeyCache.put(newKey, key);
}
}
/**
* Apply changes to the bean cache entry.
*/
void cacheBeanUpdate(Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
void cacheBeanUpdate(String key, Map<String, Object> changes, boolean updateNaturalKey, long version) {
ServerCache cache = getBeanCache();
CachedBeanData existingData = (CachedBeanData) cache.get(id);
CachedBeanData existingData = (CachedBeanData) cache.get(key);
if (existingData != null) {
long currentVersion = existingData.getVersion();
if (version > 0 && version < currentVersion) {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, id, currentVersion, version);
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, key, currentVersion, version);
}
cache.remove(id);
cache.remove(key);
} else {
if (version == 0) {
version = currentVersion;
}
CachedBeanData newData = existingData.update(changes, version);
if (beanLog.isDebugEnabled()) {
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, id, changes);
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, key, changes);
}
cache.put(id, newData);
cache.put(key, newData);
}
if (updateNaturalKey) {
Object oldKey = calculateNaturalKey(existingData);
if (oldKey != null) {
if (natLog.isDebugEnabled()) {
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, id);
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, key);
}
naturalKeyCache.remove(oldKey);
}
@@ -46,6 +46,7 @@ class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
return (EntityBean)prototype._ebean_newInstance();
}
@Override
public void bindElementValue(SqlUpdate insert, Object value) {
targetDescriptor.bindElementValue(insert, value);
}

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