Compare commits

...
Author SHA1 Message Date
Rob Bygrave 674617eb9e [maven-release-plugin] prepare release ebean-11.15.1 2018-03-14 00:02:58 +13:00
Rob Bygrave afead2da41 #1221 - ENH: Add a merge(bean) and merge(bean, paths) 2018-03-13 23:55:47 +13:00
Rob Bygrave 607cf3156c #1347 - Improve cleanup of inactive transactions from threadLocal when no end() used 2018-03-13 00:15:42 +13:00
Rob Bygrave af2b8976fa #1346 - Since ebean version 11.12.1 executing docStore query with findPagedList fails with error (due to order by id) 2018-03-12 18:28:17 +13:00
Rob Bygrave 85bfe3d03e #1345 - Refactor io.ebean.meta.MetaInfoManager API (query execution metrics). Breaking change for people collecting query execution metrics. 2018-03-10 02:51:51 +13:00
Rob Bygrave eda6350d89 #1344 - ENH: Add initial Cockroach DB support ... need to review JSON
Adds in the DB ARRAY support (exactly the same as Postgres)
2018-03-09 17:40:34 +13:00
Rob Bygrave 8ac1b745df #1344 - ENH: Add initial Cockroach DB support ... need to review JSON and DB ARRAY support 2018-03-09 16:54:38 +13:00
Rob Bygrave 6c8cf0c5b7 #1343 - Update for ... #1283 ENH: Support simplified test configuration (with docker setup typically via application-test.yml)
DefaultContainer with config.loadFromProperties(PropertiesLoader.load()); effectively runs eval twice on properties which is unnecessary.

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

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

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

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

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

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

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

* compile fixes

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

* Remove parameter (from other PR)

* FIX: Mac detection

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

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

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

* removed license header
2018-03-02 09:10:21 +13:00
Rob Bygrave 9e2ddc9f8c [maven-release-plugin] prepare for next development iteration 2018-03-01 13:43:27 +13:00
Rob Bygrave d4e72eefa0 [maven-release-plugin] prepare release ebean-11.12.1 2018-03-01 13:43:14 +13:00
Rob Bygrave 525afbc00f #1296 - where().exampleLike(bean) ... with 'empty bean' produces invalid SQL 2018-03-01 13:38:09 +13:00
Rob Bygrave 6f2e1c0248 #1295 - Multiple platform DbMigration generation with non-native boolean type (BIT, INTEGER etc) produces incorrect DDL for other DB Platform with native boolean 2018-03-01 12:55:22 +13:00
Rob Bygrave 0d0a3422fc #1283 - ENH: Support simplified test configuration (with docker setup typically via application-test.yml)
Refactor tidy - no effective change on this commit.
2018-02-28 21:11:49 +13:00
Rob Bygrave 57b0190cd2 #1283 - ENH: Support simplified test configuration (with docker setup typically via application-test.yml) 2018-02-27 18:28:03 +13:00
Rob Bygrave c8e199177c #1294 - NPE when delete cascade from bean with SoftDelete to imported OneToOne without SoftDelete 2018-02-27 15:31:26 +13:00
Rob Bygrave 68a470a086 Test change only - No effective change, remove noop ServerConfig.loadTestProperties() 2018-02-27 15:00:13 +13:00
Rob Bygrave 45a023f0e4 #1282 - Not supported for @DbArray mapping when using kotlin var instead of val 2018-02-27 14:25:44 +13:00
Rob Bygrave 06bce35f5a #1293 - Refactor - move JdbcClose into io.ebean.util ... from io.ebeaninternal.util as it is used in public API (platform code, sequences etc) 2018-02-27 12:56:20 +13:00
Roland PramlandRob Bygrave 3dfdd0e2e8 #1292 - Fix for non-native / number boolean types (MySql, SqlServer, Oracle, Sqlite, SqlAnywhere) removing "default 0" + Fix for DB2 add foreign key update clause 2018-02-27 11:58:10 +13:00
Rob Bygrave a569c7fc96 #1275 - Additional refactor of DefaultOrmQuery.prepareForPaging() for - Add ServerConfig.setDefaultOrderById() ...
Remove use of SpiOrmQueryRequest<T> request
2018-02-27 11:35:10 +13:00
Rob Bygrave e751f991cb #1275 - Additional refactor for - Add ServerConfig.setDefaultOrderById() ...
Removes the cast on DefaultOrmQuery.prepare()
2018-02-27 11:31:30 +13:00
Roland PramlandRob Bygrave 83cf768369 Append preconfigured database to dbMigration (#1291) 2018-02-27 11:09:44 +13:00
Roland PramlandRob Bygrave bb418456e3 Misc fixes (#1290) 2018-02-27 11:06:35 +13:00
Roland PramlandRob Bygrave ac9f3408b2 FIX: reset cache statistics (fixes test cases in eclipse when run in different order) (#1289) 2018-02-27 11:03:27 +13:00
Roland PramlandRob Bygrave 78f8d48e2e FIX: more timing issues - mysql does not support resolution in millis (#1288) 2018-02-27 11:00:58 +13:00
Roland PramlandRob Bygrave 3f8b9529e1 Fix clock sync (#1287)
Test only change - Fix clock sync timing issues with tests using -DdbClockDelta
2018-02-27 10:59:19 +13:00
Roland PramlandRob Bygrave 10f3d85d9f No effective code change - add serialize/serialversionuid (#1286) 2018-02-27 10:57:07 +13:00
Roland PramlandRob Bygrave 0e3f47ee82 No effective code change: Fixwarnings, overrides, generics etc. (#1285) 2018-02-27 10:15:19 +13:00
Roland PramlandRob Bygrave cdbbd749a3 FIX: h2Merge test - which may fail due timing issues (#1284)
Test only change - FIX: h2Merge test - which may fail due timing issues
2018-02-27 10:11:10 +13:00
Roland PramlandRob Bygrave a712be2917 auomatically append "order by id" when paging and query.orderById = true (#1275)
Add ServerConfig.setDefaultOrderById() and query.orderById(boolean) ... to control automatically append "order by id" with paging query
2018-02-27 10:09:52 +13:00
Rob Bygrave 0cba0664f8 #1281 - Test change only - default back from sql server to h2 2018-02-26 15:06:17 +13:00
Rob Bygrave a3e5c3a786 #1281 - Test change only - Update DbMigrationTest for SQL Server using SEQUENCE based Identity 2018-02-26 15:04:57 +13:00
Rob Bygrave 58def02cd1 #1280 - Update test for - Remove "order by" clause of RawSql query when executing findCount()
The code fix associated with this is in CQueryBuilder line 276
2018-02-26 14:41:56 +13:00
Rob Bygrave cec65cc888 #1279 - Change ObjectFactory method to match, tidy format (no real change here) 2018-02-26 14:32:38 +13:00
Roland PramlandRob Bygrave da7bb834e7 DbMigration supports altering UniqueConstraints and foreign keys (#1279)
* NEW: Ebean reads version from pop.properties and prints it at start up

* DDL generation and migration can append a header and platformMigration is prepared to append epilog & prolog to scripts

* FIX: Set identity only, if it is not the platform default type

* Updated reference scripts

* ADD basic support for foreign key migration

* DbMigration detects alter foreign keys

* tests
2018-02-26 14:24:52 +13:00
Rob Bygrave a64a244745 Bump pom to 11.12.1-SNAPSHOT (Event listener change #1063, Yaml properties change #1246) 2018-02-26 13:26:26 +13:00
Rob Bygrave 1c19d4cf7e #1278 - Multi-tenant delete by id ... doesn't return 0 when no rows deleted
Where -1 (jdbc batch) is returned as true as per current behaviour
2018-02-26 13:20:49 +13:00
Rob Bygrave 347225efb2 #1278 - Multi-tenant delete by id ... doesn't return 0 when no rows deleted 2018-02-26 13:16:20 +13:00
Roland PramlandRob Bygrave 91cb5a23fd Fix dbmigration (#1277)
ENH: Support adding a '-- Generated by' comment to DDL including version and timestamp
2018-02-26 12:37:54 +13:00
Rob Bygrave 0e219a9bc6 Revert #1263 - ENH: Make Transaction API Fluid such that we can set multiple thing like batchSize and label in a fluid fashion #1276 2018-02-26 11:45:49 +13:00
Rob Bygrave 21182da793 Fix test LoaderTest 2018-02-26 09:18:45 +13:00
Roland PramlandRob Bygrave 0de084caec removed escapeLikeCharacter method, as it is covered by parent implementation (#1274) 2018-02-26 08:42:18 +13:00
Roland PramlandRob Bygrave e3425a8850 FIX: possible resource leaking in testcases (all created transactions are closed by followed try statement) (#1273) 2018-02-26 08:40:53 +13:00
Rob Bygrave 8e9843ab8a #1246 - Support use of application.yml and application-test.yml ... (as alternative to ebean.properties, test-ebean.properties) 2018-02-26 08:35:47 +13:00
Rob Bygrave 75462e7410 #1272 - ENH: Add built in support for Joda Period and java time Period 2018-02-23 17:54:02 +13:00
Rob Bygrave 265bda8cf2 #1272 - ENH: Add built in support for Joda Period 2018-02-23 17:41:01 +13:00
Rob Bygrave 7d0ed261b5 #1271 - ENH: Add BeanFinder and BeanRepository ... such that there is a read to go base class for DI repository style 2018-02-23 16:55:07 +13:00
Rob Bygrave ff111eaf33 #1063 - Add Event Listeners capable of being notified for delete by id
Adds BeanDeleteIdRequest and adds a method to BeanPersistController and BeanPersistAdapter
2018-02-23 14:34:08 +13:00
Rob Bygrave 8b18e907af #1063 - Add Event Listeners capable of being notified for delete by query or update by query
Adds QueryType and adds method query.getQueryType() which then can be used in a BeanQueryAdapter
2018-02-23 14:04:36 +13:00
Rob Bygrave 40704e2ef7 No Effective change - change test to use assertJ 2018-02-23 13:37:43 +13:00
Rob Bygrave 0e62d0579e [maven-release-plugin] prepare for next development iteration 2018-02-23 12:50:59 +13:00
Rob Bygrave 47d55a2f29 [maven-release-plugin] prepare release ebean-11.11.1 2018-02-23 12:50:45 +13:00
Rob Bygrave b037be5d3e No Effective change - bump agent in pom (with query bean enhancement of interface default methods) 2018-02-23 12:48:06 +13:00
Rob Bygrave 60a3c24cba No Effective change - Tidy pom removing commented out old sqlserver jdbc driver 2018-02-23 10:31:22 +13:00
Rob Bygrave 4a243a76a7 #1270 - Multi-tenant - partition mode - Delete by id does not include tenantId value in predicate 2018-02-23 10:26:47 +13:00
Rob Bygrave 9aee7c892d Test only - copy/add multi-tenant partition tests 2018-02-23 10:14:27 +13:00
Rob Bygrave 18d944f228 Test only - copy/add multi-tenant partition tests 2018-02-23 10:13:57 +13:00
Rob Bygrave 29bb9d3b48 #1268 - SQL Server fix for "for update" plus Sequences support plus refactor of sequences 2018-02-22 22:46:17 +13:00
Rob Bygrave f8ea5619d0 #1268 - SQL Server fix for "for update" plus Sequences support plus refactor of sequences 2018-02-22 22:45:44 +13:00
Rob Bygrave e3fac7c622 #1269 - Paging queries should not ALWAYS append an order by ID ... but only when the order by is empty 2018-02-22 22:43:14 +13:00
Rob Bygrave 8ee68ac5e1 #1269 - Paging queries should not ALWAYS append an order by ID ... but only when the order by is empty 2018-02-22 22:40:22 +13:00
Rob Bygrave ec7b376612 Test only change - fix test assert for sql server 2018-02-22 22:12:30 +13:00
Rob Bygrave f2d438f21a Test only change - fix test assert for sql server 2018-02-22 22:11:56 +13:00
Rob Bygrave 20bd2241e4 Test only change - fix test assert to support sequence and identity insert 2018-02-22 22:11:36 +13:00
Rob Bygrave dd907cc4bc Test only change - fix keys for testing against Oracle 2018-02-22 22:10:59 +13:00
Rob Bygrave 4d122ea05b #1267 - DDL issue on @OneToMany @JoinTable ... extra unnecessary index created on fk column 2018-02-22 22:09:53 +13:00
Rob Bygrave 83384c6f0e #323 - DDL Generation support for foreign key cascade options
Tidy up, no GLOBAL_DEFAULT.
2018-02-21 11:04:04 +13:00
Rob Bygrave eaa48468cd #1174 - ENH: Support mapping @TenantId to an entity bean via @ManyToOne 2018-02-21 09:44:37 +13:00
Rob Bygrave 0b1c3a4233 Bump version to 11.11.1-SNAPSHOT 2018-02-21 09:44:07 +13:00
Rob Bygrave 3e8b06e38e #994 - @OneToOne cacade does not honor orphanRemoval = true 2018-02-20 16:59:55 +13:00
Rob Bygrave 81d48d67a7 #123 - Mapping - Add support for OneToMany JoinTables 2018-02-20 14:40:21 +13:00
Rob Bygrave 8d0df6541e Merge branch 'issue/1263-transaction-api' 2018-02-19 22:47:42 +13:00
Rob Bygrave 2be3099d55 #1228 - Add support to not generate FK Constraint when @JoinColumn(foreignKey=@ForeignKey(ConstraintMode.NO_CONSTRAINT)) 2018-02-19 22:45:47 +13:00
Rob Bygrave 183d079b58 #323 - DDL Generation support for foreign key cascade options 2018-02-19 22:24:51 +13:00
Rob Bygrave 40634412f5 #323 - DDL Generation support for foreign key cascade options 2018-02-19 22:24:37 +13:00
Roland PramlandRob Bygrave b8aa822a30 [utf8mb4-support] limited the size of all indexed string columns, as max key length on mysql/mariadb is 767 bytes (which are <= 191 chars on utf8mb4) (#1264) 2018-02-19 16:15:28 +13:00
Rob Bygrave a1d0fbb194 [maven-release-plugin] prepare for next development iteration 2018-02-16 14:45:29 +13:00
Rob Bygrave 70ccc6588d [maven-release-plugin] prepare release ebean-11.10.6 2018-02-16 14:45:16 +13:00
Rob Bygrave 3d62e84d59 #1263 - ENH: Make Transaction API Fluid such that we can set multiple thing like batchSize and label in a fluid fashion 2018-02-16 14:40:35 +13:00
Rob Bygrave ac36172cc6 #997 - TypeReflectHelper does not resolve type variables correctly in non-trivial cases
Part 2 - Fix the failing test, swap in use of TypeResolver and remove all the unused features.
2018-02-16 12:47:10 +13:00
Rob Bygrave 1829c74694 #997 - TypeReflectHelper does not resolve type variables correctly in non-trivial cases
Initial failing test ... and TypeResolver prior to cleanup
2018-02-16 12:25:07 +13:00
Rob Bygrave cc7e22014d #997 - TypeReflectHelper does not resolve type variables correctly in non-trivial cases
Initial failing test ... and TypeResolver prior to cleanup
2018-02-16 12:24:57 +13:00
Rob Bygrave a5c6f483b0 #1258 - ENH: Add ServerConfig notifyL2CacheInForeground option ... was ServerCachePlugin BackgroundExecutor problem 2018-02-16 11:18:17 +13:00
Rob Bygrave 5237e8ba66 #1261 - ENH: Support use of getGeneratedKeys with SqlUpdate 2018-02-16 09:45:49 +13:00
Rob Bygrave 9926cad7f1 #1262 - ENH: Support firstRow maxRows with findNative() 2018-02-16 00:11:27 +13:00
Rob Bygrave 6c1b295d41 [maven-release-plugin] prepare for next development iteration 2018-02-15 22:32:56 +13:00
Rob Bygrave 49b568e7d4 [maven-release-plugin] prepare release ebean-11.10.5 2018-02-15 22:32:43 +13:00
Rob Bygrave 628af31ddd #1142 - Mapping - Add support for @OrderColumn (Ebean automatically maintains an order column) 2018-02-15 22:31:41 +13:00
Rob Bygrave 31cac60393 #107 - Mapping - Add support for @PrimaryKeyJoinColumn/@PrimaryKeyJoinColumns
Adds Bi-directional support
2018-02-15 00:49:51 +13:00
Rob Bygrave 6a067c0cf7 #107 - Mapping - Add support for @PrimaryKeyJoinColumn/@PrimaryKeyJoinColumns
Extra test with UUID and optional OneToOne so has left join
2018-02-14 23:09:10 +13:00
Rob Bygrave 842292ef6b #107 - Mapping - Add support for @PrimaryKeyJoinColumn/@PrimaryKeyJoinColumns 2018-02-14 22:59:57 +13:00
Rob Bygrave 6db9a60f4f #107 - Fix existing test - Ah ??? ... this PrimaryKeyJoinColumn was ignored and should never have been in this test. 2018-02-14 17:24:14 +13:00
Rob Bygrave d9125db957 [maven-release-plugin] prepare for next development iteration 2018-02-14 15:29:26 +13:00
Rob Bygrave 4bd9e67703 [maven-release-plugin] prepare release ebean-11.10.4 2018-02-14 15:29:12 +13:00
Rob Bygrave 2a986ab590 #1260 - Mark query.setForUpdate(true) as deprecated - migrate to query.forUpdate() 2018-02-14 15:28:13 +13:00
Rob Bygrave 7c477827fe #1259 - Throw explicit mapping error when an Enum is mapped to both ORDINAL and STRING 2018-02-14 12:39:35 +13:00
Rob Bygrave b2967dec1e #1257 - ServerConfig with register = true, defaultServer = false ... fails as it tries to create the default server 2018-02-13 16:22:52 +13:00
Rob Bygrave a95fd063ae #1256 - Query where many isNotEmpty() ... on ManyToMany with SoftDelete produces incorrect SQL (Part 2) 2018-02-13 14:52:18 +13:00
Rob Bygrave 17a54e3804 No effective change - extra tests for DeployPropertyParser 2018-02-13 14:09:08 +13:00
Rob Bygrave f1b2a09b30 Testing - modify .travis to run mysql as service (bump MySql version to 5.7) 2018-02-10 01:16:40 +13:00
Rob Bygrave e4d9e4f2de Testing - modify .travis to run mysql as service (bump MySql version to 5.7) 2018-02-10 01:04:19 +13:00
Rob Bygrave 6474f306b5 #1255 - Regression on raw LIKE expression with slashes on Postgres
Related update for Sqlite
2018-02-10 00:57:19 +13:00
Rob Bygrave f1c2f134ad #1255 - Regression on raw LIKE expression with slashes on Postgres
Related update for Sqlite
2018-02-10 00:56:05 +13:00
Rob Bygrave 0f7158ea18 Testing - modify .travis to run mysql as service 2018-02-10 00:34:34 +13:00
Rob Bygrave 3fdb76d18f Testing - modify .travis to run mysql as service 2018-02-10 00:31:39 +13:00
Rob Bygrave b1027a2f25 Testing - modify .travis to run mysql as docker container 2018-02-10 00:18:33 +13:00
Rob Bygrave e5a1f9f7cc Testing - modify .travis adding docker service 2018-02-10 00:07:27 +13:00
Rob Bygrave f78f24136a [maven-release-plugin] prepare for next development iteration 2018-02-09 23:42:42 +13:00
Rob Bygrave 5b2aa0c74d [maven-release-plugin] prepare release ebean-11.10.3 2018-02-09 23:42:28 +13:00
Rob Bygrave 175714046a #1254 - SpringJdbcTransactionManager inifinite recursion #1254
Yeah, nah. This is the real fix. Expose:

   SpiTransaction TransctionManager.getInScope();

... and have ebean-spring-txn SpringJdbcTransactionManager call it instead of getActive().
2018-02-09 23:03:07 +13:00
Rob Bygrave 73b2cc55f3 #1254 - SpringJdbcTransactionManager inifinite recursion #1254
Fix. Note tested in ebean-spring-txn.
2018-02-09 22:22:08 +13:00
Rob Bygrave cebe375856 No effective change - initial docker testing setup for oracle and sqlserver 2018-02-09 21:31:40 +13:00
Rob Bygrave 2f47235018 #1255 - Regression on raw LIKE expression with slashes on Postgres 2018-02-09 21:28:39 +13:00
Rob Bygrave 17abd6d977 #1255 - Regression on raw LIKE expression with slashes on Postgres
Tests
2018-02-09 21:25:36 +13:00
Rob Bygrave cc5f3689f3 No effective change - modify tests for SqlServer 2018-02-09 21:10:18 +13:00
Rob Bygrave abcc651e7f #1253 - Query where many isNotEmpty() ... on ManyToMany with SoftDelete produces incorrect SQL
Fix
2018-02-09 15:00:58 +13:00
Rob Bygrave caa36262c1 #1253 - Query where many isNotEmpty() ... on ManyToMany with SoftDelete produces incorrect SQL
Failing test
2018-02-09 15:00:29 +13:00
Rob Bygrave 66b6dba921 [maven-release-plugin] prepare for next development iteration 2018-02-07 16:54:44 +13:00
Rob Bygrave fc01f230f3 [maven-release-plugin] prepare release ebean-11.10.2 2018-02-07 16:54:31 +13:00
Rob Bygrave 7fe3e657d7 No effective change - put default test db back to default to h2 2018-02-07 16:49:49 +13:00
Rob Bygrave 9bade12c9d #1251 - Delete cascade with 11+ and Postgres throws exception
Fix
2018-02-07 16:47:59 +13:00
Rob Bygrave 7a816ae2da #1251 - Delete cascade with 11+ and Postgres throws exception
Failing test
2018-02-07 16:40:12 +13:00
Rob Bygrave b83412281e #1251 - Delete cascade with 11+ and Postgres throws exception
Failing test
2018-02-07 16:39:58 +13:00
Rob Bygrave 478bba292d #1250 - Add explicit coverage for Enum with abstract method - no new issue 2018-02-07 14:01:11 +13:00
Rob Bygrave 77a4e684bb [maven-release-plugin] prepare for next development iteration 2018-02-02 23:39:51 +13:00
Rob Bygrave a8254c4295 [maven-release-plugin] prepare release ebean-11.10.1 2018-02-02 23:39:38 +13:00
Rob Bygrave 2a5d0084ef #1249 - Bump db-migration to 11.3.1 ... with fix for not running empty sql statements (empty strings) 2018-02-02 23:38:11 +13:00
Rob Bygrave f9404639d7 Fix test for H2 select for update - my bad. 2018-02-02 23:36:12 +13:00
Rob Bygrave bbef5a6bbb #1243 - Add IDENTITY support for Oracle platform (since 12c Oracle supports auto generated identity) 2018-02-02 23:18:23 +13:00
Rob Bygrave 4645fd9725 Cleanup tests for Oracle 2018-02-02 23:17:21 +13:00
Rob Bygrave 823cb45221 Cleanup tests for Oracle 2018-02-02 23:17:03 +13:00
Rob Bygrave b2bbb05b83 #1248 - ENH: Additionally read "db" system property to determine default database (to use when testing with docker against different databases) 2018-02-02 20:49:39 +13:00
Rob Bygrave 5ff71324d0 #1247 - Unique index or primary key violation - when jdbc batch cascading to PrivateOwned child with unique constraint 2018-02-02 20:43:55 +13:00
Rob Bygrave 851f1c98b0 #1245 - Refactor internals - move Transaction ProfileStream creation
Stops the profileId parameter pollution, enables to potentially use transaction label
and runtime toggling of collecting transaction profiling detail.
2018-01-31 14:02:50 +13:00
Rob Bygrave a92e620f7e #1245 - Refactor internals - move logic to TransactionManager from DefaultServer 2018-01-31 13:36:23 +13:00
Rob Bygrave f9cd955f7a #1245 - Refactor internals - Add to TransactionManager externalRemoveTransaction() and externalBeginTransaction()
These provide a better/tidier way for external transaction managers (like ebean-spring-txn) to begin and end externally
managed transactions.
2018-01-31 13:28:42 +13:00
Rob Bygrave 71f66005e7 Merge branch 'master' of github.com:ebean-orm/ebean 2018-01-31 11:57:10 +13:00
Rob Bygrave b682293a32 #1245 - Refactor internals - move logic to TransactionManager from DefaultServer 2018-01-31 11:56:38 +13:00
Rob Bygrave 5bd7853ce8 #1245 - Refactor internals - move TransactionMangerScope to be part of TransactionManager 2018-01-31 11:30:11 +13:00
André CamiloandRob Bygrave 276c56161a DiscriminatorColumn length should be 31 (#1244) 2018-01-31 08:32:21 +13:00
Rob Bygrave 6b01821623 #1242 - Bump avaje-classpath-scanner dependency to 3.1.1 2018-01-26 11:51:28 +13:00
Rob Bygrave 8495ccced6 #1241 - ENH: Support using ebean_db system property to control docker container for testing 2018-01-24 19:17:14 +13:00
Rob Bygrave 2d929837c4 No effective change - Update testing for profile location enhancement (#1227) 2018-01-24 19:11:59 +13:00
Rob Bygrave 6754992b1c [maven-release-plugin] prepare for next development iteration 2018-01-22 23:02:52 +13:00
Rob Bygrave 08b7f4a95f [maven-release-plugin] prepare release ebean-11.9.1 2018-01-22 23:02:37 +13:00
Rob Bygrave 07e2605a30 #1240 - ENH: Add support for @Transactional skipCache and label 2018-01-22 22:57:35 +13:00
Rob Bygrave 9a0b742f14 #1240 - ENH: Add support for @Transactional skipCache and label 2018-01-22 15:07:50 +13:00
Rob Bygrave bce745e88d #1240 - ENH: Add support for @Transactional skipCache and label 2018-01-22 15:04:13 +13:00
Rob Bygrave 51d2e4c600 ENH: Add ProfileLocation - Part 2 - performance metrics for transactions with label and profileLocation 2018-01-22 14:54:27 +13:00
Rob Bygrave 4e33027fdf Bump to 11.9.1-SNAPSHOT, add test dependency for logback 2018-01-19 11:10:18 +13:00
Rob Bygrave 0a809b0fb3 #1239 - When Transaction.setSkipCache(true) ... Query with useQueryCache is still use L2 query cache 2018-01-19 10:46:49 +13:00
Rob Bygrave 13ddb915c8 No effective change - add javadoc 2018-01-19 09:38:50 +13:00
Rob Bygrave 07cdf64ece #1238 - Refactor MetaInfoManager - collect query plan stats, fix collection of stats on UpdateQuery 2018-01-19 01:07:37 +13:00
Rob Bygrave b9ae76d465 #1237 - ENH: Add ProfileLocation - support for relating performance metrics for queries and transactions back to source line of code 2018-01-18 18:10:03 +13:00
Rob Bygrave 82f5c21d26 No effective change - Improve exception error message when property is not found on a bean type
Typically using IDE enhancement plugin with incremental compilation where part of an inheritance
hierarchy has been changed but not all related classes recompiled and re-enhanced.
2018-01-12 01:27:24 +13:00
Rob Bygrave 14105eb1eb #1236 - ENH: Add Transaction.current() ... as alternative to obtain the current transaction 2018-01-11 22:34:31 +13:00
Rob Bygrave bab27f25ca [maven-release-plugin] prepare for next development iteration 2018-01-09 23:19:09 +13:00
Rob Bygrave eed8d69715 [maven-release-plugin] prepare release ebean-11.8.1 2018-01-09 23:18:54 +13:00
Rob Bygrave 7b93df2df7 Bump pom version to 11.8.1-SNAPSHOT 2018-01-09 23:17:53 +13:00
Rob Bygrave bb205ced54 #1235 - Thread deadlock on L2 cache load of many property 2018-01-09 23:16:35 +13:00
Rob Bygrave 0d4c50e19c #1234 - Remove deprecated CacheMode.QUERY_ONLY (migrate to CacheMode.GET) 2018-01-09 19:56:55 +13:00
Rob Bygrave 8cff85fdda #1233 - Remove deprecated CacheMode.RECACHE (migrate to CacheMode.PUT) 2018-01-09 19:50:26 +13:00
Rob Bygrave 932986a6b5 #1232 - Performance: Add support for a second read-only DataSource (for implicit query-only transactions) 2018-01-09 16:35:22 +13:00
Rob Bygrave 01539091ef #1231 - Performance: Only use CallStack when using AutoTune (automatic query tuning or profiling) 2018-01-09 13:28:57 +13:00
Rob Bygrave 7e69f71b51 #1230 - Add DB Migration non strict mode - IllegalArgumentException: non-null column has no default value 2018-01-07 15:33:18 +13:00
Rob Bygrave 58d6b2c24e Merge branch 'master' of github.com:ebean-orm/ebean 2018-01-06 12:52:38 +13:00
Rob Bygrave 3c7ef7d46e #1229 - Implement equals and hashCode on ModifyAwareMap, ModifyAwareList and ModifyAwareSet 2018-01-06 12:51:50 +13:00
Rob Bygrave db5db26fca No effective change - rename parameter fix typo 2018-01-06 12:21:14 +13:00
rob bygrave 7110f4a59b No effective change - remove unused HashQueryPlan etc 2017-12-28 16:17:00 +13:00
rob bygrave 9f91d8bcf9 No effective change - modify specific test to run with Postgres specific syntax 2017-12-27 23:23:01 +13:00
Rob Bygrave ac97444ad1 No effective change - add test for @Aggregation with order by clause 2017-12-27 13:49:46 +13:00
Rob Bygrave f6e9afcab6 #1226 - Version range in dependency makes sbt resolve very slow. Change slf4j-api dependency to provided scope.
Part 2, bring in new version of avaje-classpath-scanner which also has slf4j-api as provided scope.
2017-12-27 13:48:22 +13:00
Rob Bygrave a7226079fe [maven-release-plugin] prepare for next development iteration 2017-12-14 00:00:04 +13:00
Rob Bygrave fc915010d9 [maven-release-plugin] prepare release ebean-11.7.1 2017-12-13 23:59:49 +13:00
Rob Bygrave c1818bc309 #1226 - Version range in dependency makes sbt resolve very slow. Change slf4j-api dependency to provided scope. 2017-12-13 22:51:23 +13:00
Rob Bygrave 4a4134ee93 #1227 - NPE when using L2 Query cache with findOne() or findOneOrEmpty() 2017-12-13 22:19:25 +13:00
Rob Bygrave e07bd8f736 #1223 - Support mapping @DbArray to a List or Set of enums 2017-12-13 21:15:19 +13:00
Rob Bygrave 4d0b29624e #1222 - Extend L2 ServerCache API with putAll(), getAll() and removeAll() methods 2017-12-13 19:58:28 +13:00
Rob Bygrave c7f4b56dd1 #1225 - ENH: Extend L2 server cache (near caching) for clearing "near caches" 2017-12-13 02:09:10 +13:00
Rob Bygrave 69c045443c #1218 - ENH: Bump to migration 11.2.1 with support for options patchInsertOn and patchResetChecksumOn 2017-11-23 19:05:35 +13:00
Rob Bygrave fd2d083971 #1217 - L2 query cache not caching empty result set 2017-11-22 22:03:35 +13:00
Rob Bygrave b9380aa5b8 No effective change - add property test for ServerConfig jsonDateTime 2017-11-22 21:21:09 +13:00
Rob Bygrave 3a1368a342 [maven-release-plugin] prepare for next development iteration 2017-11-19 20:34:54 +13:00
Rob Bygrave 0e9fc31531 [maven-release-plugin] prepare release ebean-11.6.1 2017-11-19 20:34:39 +13:00
Rob Bygrave 5132107748 Bump to 11.6.1-SNAPSHOT 2017-11-19 20:33:27 +13:00
Rob Bygrave 7ff3d703f7 #1215 - ENH: Add Query setBeanCacheMode(CacheMode) ... with a view to it replacing the existing setUseCache(boolean) and setLoadBeanCache(boolean) 2017-11-19 20:16:40 +13:00
Rob Bygrave ef91c643ed #1214 - ENH: Add inPairs() expression - support L2 cache hits for complex natural key with findList() and in pairs expression 2017-11-19 16:47:32 +13:00
Rob Bygrave f0ddf6c7e6 #1213 - NPE with TxType SUPPORTS 2017-11-19 01:51:03 +13:00
Rob Bygrave fcff571664 #1212 - ENH: Add query findSingleAttribute() 2017-11-18 22:36:08 +13:00
Rob Bygrave d5555ed9a9 #1211 - ENH: Support L2 cache complex natural keys include findList() queries 2017-11-18 22:20:42 +13:00
Rob Bygrave c5a11aa5a8 No effective change - format and javadoc 2017-11-18 10:48:37 +13:00
Rob Bygrave d1099c8309 Merge branch 'master' of github.com:ebean-orm/ebean 2017-11-15 01:02:35 +13:00
Rob Bygrave 7ff8758797 No effective change - fix javadoc 2017-11-15 01:02:11 +13:00
Koen De GrooteandRob Bygrave f1ebe7929b Internal refactor / tidy - Suggestions from intellij code analysis. (#1209) 2017-11-14 18:19:38 +13:00
Rob Bygrave 0056568bc7 [maven-release-plugin] prepare for next development iteration 2017-11-13 23:18:10 +13:00
Rob Bygrave 4b72924529 [maven-release-plugin] prepare release ebean-11.5.1 2017-11-13 23:17:54 +13:00
Rob Bygrave a75ed676f8 Bump pom version to 11.5.1-SNAPSHOT 2017-11-13 23:16:38 +13:00
Rob Bygrave c3438b888d #995 - @OneToMany with constraints on the many side 2017-11-13 22:43:56 +13:00
Rob Bygrave 56be274acc No effective change - change internal use of ArrayList to List 2017-11-13 22:42:39 +13:00
Rob Bygrave 84bb4ff78c #1207 - Regression added in 11.4.1 with NPE on @transactional(type = TxType.SUPPORTS) 2017-11-13 21:33:39 +13:00
Rob Bygrave 4054eda1f6 #1207 - Regression added in 11.4.1 with NPE on @transactional(type = TxType.SUPPORTS) 2017-11-13 21:32:52 +13:00
Rob Bygrave a4b9354e43 #1206 - NPE when using @Encrypted with Enum property that is null 2017-11-13 19:12:57 +13:00
Rob Bygrave 06e033f23f #1181 - ENH: Add @FetchPreference support - Secondary query generation is based on order of fetch statements 2017-11-08 23:20:08 +13:00
Rob Bygrave 156f505394 #1181 - ENH: Add @FetchPreference support - Secondary query generation is based on order of fetch statements 2017-11-08 23:19:58 +13:00
Rob Bygrave 9b2b2f7787 #1205 - ENH: Add support for raw expression array binding with Postgres - e.g. raw(" foo = any(?) ", collection) 2017-11-08 00:56:13 +13:00
Rob Bygrave e68dae0baa Update ebean-annotation to 3.3 and tests to use 11.5.1 agent 2017-11-07 22:57:04 +13:00
Rob Bygrave 2cf864954f #1204 - Check presence of JAXB and javax @PostConstruct (which may not be present in minimal Java 9) 2017-11-07 20:08:10 +13:00
Rob Bygrave 2b94487573 #1202 - Automatically remove old query plans from Ebean's internal query plan cache 2017-11-06 22:59:42 +13:00
Rob Bygrave fcf730ec39 [maven-release-plugin] prepare for next development iteration 2017-11-06 14:49:08 +13:00
Rob Bygrave 7d343455ac [maven-release-plugin] prepare release ebean-11.4.1 2017-11-06 14:48:55 +13:00
Rob Bygrave 21e0b748a6 #1199 - Remove deprecated findUnique() method - please migrate to findOne() 2017-11-03 03:35:19 +13:00
Rob Bygrave 2fab733e4e Update agent and maven plugin for 11.4.1 enhancement (@Transactional and PropertyChangeListener changes) 2017-11-03 03:30:53 +13:00
Rob Bygrave 7260a1b6eb #1198 - Remove support for PropertyChangeListener from entity beans 2017-11-03 03:08:43 +13:00
Rob Bygrave 434a005366 #1198 - Remove support for PropertyChangeListener from entity beans 2017-11-03 03:05:59 +13:00
Rob Bygrave ff67ef60b0 #1197 - Refactor how @Transactional methods internally managed 2017-11-03 02:13:56 +13:00
Rob Bygrave 0a1747d8e3 #1194 - Change from using Jetbrains @Nullable to ... class retention based javax annotation @Nonnull 2017-11-02 15:56:15 +13:00
Rob Bygrave f36499f71f No effective change - remove use of jetbrains @NotNull in tests 2017-11-02 15:26:35 +13:00
rob bygrave da818f17c2 #1190 - MySql throwing DataIntegrityException when it should throw the more specific DuplicateKeyException
Ah, remove the 23000 SQLState to just use the error codes for MySql.
2017-11-02 01:16:52 +13:00
rob bygrave 10eb2bc21f #1193 - For RawSql ignore the Postgres cast double colon like ::text 2017-11-02 00:03:38 +13:00
rob bygrave 66fe278e3f #1192 - findNative() with @Embedded beans ... the embedded bean isn't loaded properly 2017-11-01 23:03:57 +13:00
rob bygrave 10607385e0 #1191 - Trim logged SQL removing new line chars (for sql from SqlUpdate) 2017-11-01 22:00:55 +13:00
rob bygrave db6562df76 #1190 - MySql throwing DataIntegrityException when it should throw the more specific DuplicateKeyException 2017-11-01 21:54:48 +13:00
rob bygrave c0ec87d4cd #1189 - ExpressionList.isNull and SoftDelete 2017-11-01 21:48:23 +13:00
rob bygrave 0dbba7a211 #1189 - ExpressionList.isNull and SoftDelete 2017-11-01 21:26:15 +13:00
rob bygrave e75670f207 #1188 - When using inheritance getReference() on a leaf type ... should not hit the DB 2017-11-01 20:42:18 +13:00
rob bygrave fb8db14f3a #1186 - Tidy, remove H2MultiValueBind for now 2017-10-31 22:14:01 +13:00
rob bygrave c00b1849ef Merge branch 'feature/1186-MultiValueBind' 2017-10-31 22:11:30 +13:00
rob bygrave 1e1c518641 #1186 - ENH: Support using TableValues / Array for select prop in (?) queries
Use Postgres Array ANY and ALL without select unnest
2017-10-31 22:09:35 +13:00
Rob Bygrave d5b42f9ba1 [maven-release-plugin] prepare for next development iteration 2017-10-31 10:52:44 +13:00
Rob Bygrave bda564d2c8 [maven-release-plugin] prepare release ebean-11.3.1 2017-10-31 10:52:31 +13:00
Rob Bygrave dce124cabd #1187 - Fix connection leak regression introduced in 11.2.3 for findEach() with invalid SQL 2017-10-31 10:50:20 +13:00
Rob Bygrave f5cfcbd56e #1187 - Fix connection leak regression introduced in 11.2.3 for findEach() with invalid SQL 2017-10-31 10:49:35 +13:00
rob bygrave 5bccdccbd6 #1186 - ENH: Support using TableValues / Array for select prop in (?) queries 2017-10-31 02:53:34 +13:00
rob bygrave 6e62b72fb9 #1186 - ENH: Support using TableValues / Array for select prop in (?) queries 2017-10-31 02:53:20 +13:00
rob bygrave 89dac3820b #1182 - Error when applied disjunction to filterMany 2017-10-30 22:06:57 +13:00
rob bygrave ea836a0078 No effective change - rename variable 2017-10-30 21:29:57 +13:00
rob bygrave 1ffd7c7720 git push origin masterMerge branch 'OndrejPacay-feature/1179_bugCollectionL2CacheNotInvalidatedAfterExternalChange' 2017-10-30 21:25:43 +13:00
rob bygrave 387582543c Merge branch 'feature/1179_bugCollectionL2CacheNotInvalidatedAfterExternalChange' of https://github.com/OndrejPacay/ebean into OndrejPacay-feature/1179_bugCollectionL2CacheNotInvalidatedAfterExternalChange 2017-10-30 21:25:30 +13:00
rob bygrave a215878a7c #1183 - ENH: Add support for transaction profiling 2017-10-30 21:21:12 +13:00
rob bygrave dbedb65922 #1183 - ENH: Add support for transaction profiling 2017-10-30 21:19:58 +13:00
Rob Bygrave 1d52b3a818 #1183 - Support transaction profiling, adjust output format 2017-10-27 00:27:21 +13:00
Rob Bygrave 6ce2b91fff #1183 - Support transaction profiling, move away from binary encoding 2017-10-26 23:32:54 +13:00
Rob Bygrave 55d66ae851 #1183 - Support transaction profiling, move away from binary encoding 2017-10-26 23:32:44 +13:00
Rob Bygrave 37d001f754 #1185 - Modify JDBC batch so it does not "auto flush" on non-Id non-generated property 2017-10-26 00:03:58 +13:00
rob bygrave c121618c0c #1183 - ENH: Add support for transaction profiling 2017-10-25 01:31:09 +13:00
rob bygrave 7ebef51859 #1183 - ENH: Add support for transaction profiling 2017-10-25 01:07:00 +13:00
rob bygrave 093f82a87f #1178 - Make the new changeLogAsync property default to true in ServerConfig 2017-10-19 22:26:13 +13:00
Ondrej Pacay b60f1f978f Bug Collection IDs L2 cache not cleared on external sql modification of the table #1179 2017-10-19 01:06:43 +02:00
Roland PramlandRob Bygrave 6ac146cabe Fix beanchange deser (#1178)
Fix / support JSON marshalling of BeanChange + add changeLogAsync option to ServerConfig
2017-10-19 09:40:27 +13:00
Roland PramlandRob Bygrave 2263d31f46 Improved the performance of findAnnotation (#1177) 2017-10-19 09:34:57 +13:00
rob bygrave 4a66d0be6b [maven-release-plugin] prepare for next development iteration 2017-10-19 01:26:28 +13:00
rob bygrave e68e496207 [maven-release-plugin] prepare release ebean-11.2.3 2017-10-19 01:26:12 +13:00
rob bygrave 088110385f Bump ebean-migration dependency to 11.1.1 2017-10-19 01:19:51 +13:00
rob bygrave a355f66561 #1176 - Allow script in extra-ddl.xml ... to not have trailing semi-colon 2017-10-19 01:11:14 +13:00
rob bygrave 00fdb632d7 #1173 - Ebean server should read ebean.mf entity-packages to locate entity beans 2017-10-18 23:58:22 +13:00
rob bygrave a54c4157e6 Refactor as part of - Ensure that Ebean.currentTransaction is available in lifecycle handlers (#1166) #1175 2017-10-18 23:27:59 +13:00
Rob Bygrave a89a9f0202 #1166 - Refactor to use beginServerTransaction() to make Ebean.currentTransaction available in lifecycle handlers 2017-10-17 01:07:48 +13:00
Rob Bygrave 331f49824f #1172 - Refactor - remove internal unused DefaultOrmQuery logSecondaryQuery (as we use LoggedSqlCollector) 2017-10-16 23:49:23 +13:00
Rob Bygrave 94a857d345 #1171 - ENH: Add Finder.update() method ... returning a UpdateQuery<T> 2017-10-16 23:25:56 +13:00
Rob Bygrave 1005f177fd #1170 - ENH: Add isIn() methods as alias to in() on ExpressionList (for use with Kotlin where in is a keyword) 2017-10-16 23:13:49 +13:00
Rob Bygrave 96a4b93c75 #1167 - BUG: query.setId(...).delete() does not work 2017-10-16 22:58:48 +13:00
Rob Bygrave 5869187368 [maven-release-plugin] prepare for next development iteration 2017-10-16 22:26:39 +13:00
Rob Bygrave eac50b8f5b [maven-release-plugin] prepare release ebean-11.2.2 2017-10-16 22:26:21 +13:00
Rob Bygrave 6e2712ea49 #1169 - Memory leak - DefaultTransactionThreadLocal holding reference to transaction 2017-10-16 22:14:48 +13:00
Rob Bygrave f6e5446dab #1169 - Memory leak - DefaultTransactionThreadLocal holding reference to transaction - test case 2017-10-16 22:13:41 +13:00
Koen De GrooteandRob Bygrave 4879d44da4 Bit of cleanup suggestions from intellij. (#1168) 2017-10-16 20:16:30 +13:00
rob bygrave 2c5bf98d0b Merge branch 'master' of github.com:ebean-orm/ebean 2017-10-09 21:13:32 +13:00
rob bygrave 45a571bd97 #1165 - ENH: Add to Finder - currentTransaction() and flush() methods ... (as useful shortcuts) 2017-10-09 21:13:12 +13:00
Roland PramlandRob Bygrave d0ca75c25b FIX: connection leak in query-iterator (#1164) 2017-10-09 20:46:02 +13:00
Roland PramlandRob Bygrave fb1c7667cc FIX: order by must be added in findEach also (#1163) 2017-10-09 20:28:36 +13:00
Roland PramlandRob Bygrave 1909ba1557 Fix like escaping for DB2, Oracle and MySql (#1162)
* Extended test case to show problem

* FIX: Proper escaping of backslash in like/contains queries

* Had to replace "like ?" assertions in tests with "like " as mysql will do a "like binary ? escape'|'" now to be case sensitive
2017-10-06 09:46:56 +13:00
Roland PramlandRob Bygrave e4eca4bbe9 FIX: Oto relations must match datatype (#1161) 2017-10-06 09:44:06 +13:00
Roland PramlandRob Bygrave b2438ecd71 FIX: do not generate unique-constraint for PK as it is useless (and oracle does not accept this) (#1160) 2017-10-06 09:43:30 +13:00
Roland PramlandRob Bygrave ed08567d06 FIX: bundle symbolic name in manifest (#1159) 2017-10-06 09:42:42 +13:00
Roland PramlandRob Bygrave bbe8abcc2e Can handle one to many & many to one relations in docstore only entities (#1158) 2017-10-06 09:42:05 +13:00
Roland PramlandRob Bygrave 1651d5e119 Misc platform fixes (#1157)
* Oracle does not return row count - so we probably cannot detect Optimistic locking

* DB2 wants a varchar null value (other platfroms are happy with this, too)

* FIX Test for DB2 platform
2017-10-05 21:51:09 +13:00
Roland PramlandRob Bygrave 64bf6d6a35 commit transaction only if active (#1156) 2017-10-05 11:04:12 +13:00
Roland PramlandRob Bygrave 47b0a917a2 No effective code change - fix imports and override annotations (#1155) 2017-10-05 10:55:48 +13:00
rob bygrave 2fe1a349c3 [maven-release-plugin] prepare for next development iteration 2017-10-05 01:36:46 +13:00
rob bygrave f168a8fd0d [maven-release-plugin] prepare release ebean-11.2.1 2017-10-05 01:36:30 +13:00
rob bygrave dda98c1dda Bump to 11.2.1-SNAPSHOT 2017-10-05 01:35:30 +13:00
rob bygrave 88a13c5416 #1154 - Remove deprecated TxCallable and TxRunnable - migrate to use Callable and Runnable 2017-10-05 01:24:53 +13:00
rob bygrave 16b8f5da35 Refactor EbeanServerFactory internals - use SpiContainerFactory interface and service loader 2017-10-05 01:12:07 +13:00
rob bygrave 3471f6d206 Refactor EbeanServerFactory internals - use SpiContainerShutdown interface for container shutdown mechanism 2017-10-05 00:57:40 +13:00
rob bygrave 0bf4c84bc8 #1153 - Refactor move SplitName into io.ebean.util 2017-10-05 00:41:32 +13:00
rob bygrave d366336ee5 #1153 - Refactor move ExtraTypeFactory out of io.ebean.plugin into io.ebeaninternal 2017-10-05 00:38:42 +13:00
rob bygrave bf3f8ecff5 Refactor plugin.BeanType addInheritanceWhere() to just take Query and not SpiQuery 2017-10-05 00:35:12 +13:00
rob bygrave 7524903aea #1152 - Refactor EJson internals extracting io.ebean.service.SpiJsonService 2017-10-05 00:29:43 +13:00
rob bygrave 90cf3fddc9 #1151 - Refactor add util AnnotationUtil ... moving common annotation searching methods there 2017-10-05 00:00:44 +13:00
rob bygrave 8e0f9f7591 Refactor - remove unused methods from StringHelper 2017-10-04 23:35:57 +13:00
rob bygrave 24936c0a41 Refactor - remove unused class ClassUtil 2017-10-04 23:34:13 +13:00
rob bygrave f02a552cdf #1150 - Refactor convert RawSql and RawSqlBuilder into interfaces and push the parsers into io.ebeaninternal 2017-10-04 23:03:12 +13:00
rob bygrave 5a5cb7ffbd #1149 - Refactor move PersistenceContextUtil into ... io.ebeaninternal 2017-10-04 21:12:39 +13:00
rob bygrave f85fa772bf #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - move DbMigrationFactory.create() method 2017-10-04 21:06:55 +13:00
rob bygrave 9f9ec8ef9f #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - move IdentityType handling into internal from DbIdentity 2017-10-04 20:49:04 +13:00
rob bygrave 62707caf64 #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - move PlatformDdl into internal 2017-10-04 20:30:57 +13:00
rob bygrave c6fc7a032d #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - DbOffline & ServerConfig 2017-10-04 19:44:29 +13:00
rob bygrave 10faa1fae6 #1147 - Refactor move DbMigration internals into io.ebeaninternal - change to use DbMigration.create() - initial move 2017-10-04 19:36:27 +13:00
Rob Bygrave 764f5ce4d4 [maven-release-plugin] prepare for next development iteration 2017-10-03 23:20:50 +13:00
Rob Bygrave 65ba231fc0 [maven-release-plugin] prepare release ebean-11.1.1 2017-10-03 23:20:35 +13:00
Rob Bygrave 1da2382e45 No effective change - javadoc only 2017-10-03 15:38:17 +13:00
Rob Bygrave a86af94a8c Reduce logging level only - no functional change 2017-10-03 15:35:21 +13:00
Rob Bygrave 8301cb634e #1027 - ENH: Add a Slow query event listener ... to log or listen to queries that exceed some defined execution time 2017-10-03 15:04:30 +13:00
Rob Bygrave 721654d033 #1140 - Error loading embedded instance
The issue was due to the loaded status not being recursively set on embedded
beans after inserts. The test case invoked lazy loading due to that which
lead to the issue.
2017-10-03 12:47:29 +13:00
Rob Bygrave fe1817ef02 No effective change - update test 2017-10-03 12:34:56 +13:00
Rob Bygrave fc996659f2 #1139 - NoSuchMethodError: javax.persistence.Embedded.prefix()Ljava/lang/String; 2017-10-03 11:02:38 +13:00
rob bygrave 368d22baa6 #1145 - Refactor @ChangeLog based on JSON documents for 'new values' and 'old values' 2017-10-03 01:31:37 +13:00
rob bygrave daf3d97266 #1144 - Refactor internals - move generated property setting into PersistRequestBean 2017-10-03 00:30:34 +13:00
rob bygrave f2179fca2d #1123 - 11.1.1 API change - moved PersistBatch, Platform, TxIsolation and TxType to io.ebean.annotation package 2017-09-15 21:48:53 +12:00
rob bygrave 2c436c3af5 #1123 - 11.1.1 API change - moved PersistBatch, Platform, TxIsolation and TxType to io.ebean.annotation package 2017-09-15 21:48:37 +12:00
Roland PramlandRob Bygrave 8d334ff2e8 Feature/contitional test runner (#1132)
* FIX: warnings (add generics, overrides, fix imports) - should be no effecive code change

* replaced deprecated methods by default methods

* FIX: more compiler warnings

* FIX more warnings - no code changes

* FIX: deprecated call to JsonParseException

* Remove unused code, check unused variables

* ADD: ContitonaltestRunner to skip tests for certain platforms

* Use annotations to control on which platforms a test shoud run

* FIX: compile errors
2017-09-15 21:03:03 +12:00
rob bygrave f3e4696498 [maven-release-plugin] prepare for next development iteration 2017-09-14 01:03:21 +12:00
rob bygrave 221260fa44 [maven-release-plugin] prepare release ebean-10.4.7 2017-09-14 01:03:07 +12:00
rob bygrave f8e7a79e9a #1131 - Migration broken in 10.4.6 due different classpath-scanner version 2017-09-14 00:56:37 +12:00
Roland PramlandRob Bygrave b66f18bfd6 Fix warnings 2 (#1130)
* FIX: warnings (add generics, overrides, fix imports) - should be no effecive code change

* replaced deprecated methods by default methods

* FIX: more compiler warnings

* FIX more warnings - no code changes

* FIX: deprecated call to JsonParseException

* Remove unused code, check unused variables
2017-09-14 00:32:48 +12:00
Roland PramlandRob Bygrave ac6708e0d0 Fix warnings (#1129)
* FIX: warnings (add generics, overrides, fix imports) - should be no effecive code change

* replaced deprecated methods by default methods

* FIX: more compiler warnings

* FIX more warnings - no code changes
2017-09-14 00:29:01 +12:00
rob bygrave a5f46c095d [maven-release-plugin] prepare for next development iteration 2017-09-13 23:02:39 +12:00
rob bygrave 3ac7ea6e60 [maven-release-plugin] prepare release ebean-10.4.6 2017-09-13 23:02:25 +12:00
rob bygrave b2e0271ce7 #1128 - Allow "2 word SQL" like TRUNCATE t, ANALYSE t etc 2017-09-13 21:40:17 +12:00
rob bygrave c7a7651602 #1127 - Remove the ebean.query.globallimit of 1 million "backstop" 2017-09-13 21:22:03 +12:00
rob bygrave 1e6678cbb4 #1126 - Regression bug in 10.4.5 with new line character use with SqlQuery, SqlUpdate and CallableSql 2017-09-13 21:14:19 +12:00
Roland PramlandRob Bygrave a6e4e181bf Feature/cache improvements 2 (#1067)
* New Mode to control queryCache behavior

* Collections form the cache have to be read only

* findCount not queried in query cache

* return modifiable collections from the cache

* FIX: Compile errors

* No effective code change: reformat code / javadoc
2017-09-12 23:40:18 +12:00
Roland PramlandRob Bygrave b8e1dffca2 replaced findUnique by "default" method in interfaces. (#1124) 2017-09-12 23:20:04 +12:00
rob bygrave 7e8ed72b7b #1125 - ENH: Add a SpiContainerBootup API ... to enable automatic startup of docker containers (for testing usually) 2017-09-12 22:44:37 +12:00
rob bygrave ec8dd30a04 [maven-release-plugin] prepare for next development iteration 2017-09-11 23:12:43 +12:00
rob bygrave f81d5c9f3a [maven-release-plugin] prepare release ebean-10.4.5 2017-09-11 23:12:29 +12:00
rob bygrave 74b24b1db1 #1120 - Bump classpath scanner dependency to 2.2.4 (fix for NPE on empty directory) 2017-09-11 23:09:28 +12:00
rob bygrave 179ef8c8a0 No effective change - javadoc update on Ebean 2017-09-10 21:23:37 +12:00
rob bygrave f77f3c3d6e #1119 - Deprecate TxRunnable and TxCallable - migrate to plan JDK Runnable and Callable. Add explicit executeCall() method 2017-09-10 20:47:26 +12:00
Roland PramlandRob Bygrave 9a7646195c Featur/dbmigration 3 (#1118)
* ADD: more test classes for Softdelete and History

* update reference models after adding test classes

* DbMigration:Improved History generation for sqlserver

* FIX: When DB supports Sql2011 platform, we must not alter the history-table (as this is handled by DB itself)

* DbMigration: When dropping a table, drop the sequence also.

* DbMigration: dropColumn is handled by platformDdl now, as SqlServerDdl needs to delete dependent objects (default constraints...) first

* FIX: DbMigration is not executed on history table

* DbMigration: can drop comment now.

* No effective code change: completed ObjectFactory / fixed imports

* FIX: drop table comment

* Committed the reference models
2017-09-10 15:26:01 +12:00
rob bygrave 84d3578e33 No effective change - add a test for saveAll() with existing batch mode on
Assert that it does not flush the batch.  We want the batch to continue and flush normally.
2017-09-09 14:14:57 +12:00
rob bygrave ef05b81014 #1117 - Refactor - moved DbDefault and DbMigration annotations to ebean-annotation 2017-09-09 13:27:09 +12:00
rob bygrave 726c682984 #1116 - Remove references to annotation EbeanDDL which is no longer needed 2017-09-09 13:18:39 +12:00
rob bygrave 0c22a99fdf #1116 - Remove references to annotation EbeanDDL which is no longer needed 2017-09-09 13:12:40 +12:00
rob bygrave c062c8e001 #1054 Java 9 - adding "Automatic-Module-Name" to MANIFEST.MF initial step
Update dependency to ebean-migration 10.2.1, avaje-classpath-scanner and avaje-datasource to avoid package name clash
2017-09-09 01:53:47 +12:00
rob bygrave 06f3c000f5 #1115 - Trim SQL for SqlUpdate, SqlQuery and CallableSql - also replace \n with space 2017-09-08 23:45:28 +12:00
rob bygrave 8c0232fdb8 #1114 - Deprecate findUnique() - please migrate to findOne() - update tests 2017-09-08 22:48:46 +12:00
rob bygrave 2470486f20 #1114 - Deprecate findUnique() - please migrate to findOne() 2017-09-08 22:37:50 +12:00
rob bygrave 8fbf0374dd No effective change - tidy around Db migration changes 2017-09-08 22:02:56 +12:00
Roland PramlandRob Bygrave dd8fff0277 Feature/ddl migration (#1111)
* REFACTOR: use functions from ddl-help

* NEW: Default values are written to DDL

* fix testcase for sqlserver as default constraints will get names in future

* Reading DbMigration

* ADD: DbmIgrationGenerateTest

* Tables must be created first

* Sanity checks and some getters

* DdlMigration gets applied now

* ADD: Testcase and reference migration scripts

* no effective code changes: add comments
2017-09-08 21:47:33 +12:00
Rob Bygrave 73b3d1d977 [maven-release-plugin] prepare for next development iteration 2017-09-07 00:41:49 +12:00
Rob Bygrave 2a76ae2192 [maven-release-plugin] prepare release ebean-10.4.4 2017-09-07 00:41:36 +12:00
Rob Bygrave 39931a3a2c No effective change - add test example of markUnset on version property (to avoid occ check) 2017-09-07 00:40:19 +12:00
Rob Bygrave 58eb49617b #1113 - SqlUpdate with leading spaces leads to Invalid event detection (which table was update for L2 cache invalidation etc) 2017-09-07 00:36:52 +12:00
Rob Bygrave 9b621d2ec7 #1112 - Enhance JDBC batch behavior when cascade persist is turned off (manual master/detail batch persisting) 2017-09-06 23:57:54 +12:00
Roland PramlandRob Bygrave f2e8b652fa FIX: DDL process comment change correctly & drop references when index is dropped (#1109) 2017-09-01 20:45:06 +12:00
Roland PramlandRob Bygrave f342d9d561 API-CHANGE: DbMigration returns which version was generated (#1108) 2017-09-01 20:42:02 +12:00
Roland PramlandRob Bygrave 8009bdf099 REFACTOR: Created a "splitNames" in StringHelper and use it in various places. Also wrote some tests for StringHelper. (#1107) 2017-09-01 20:40:39 +12:00
Roland PramlandRob Bygrave 7336ce81bd FIX: Do not add AssocOne-generatedProperties if they should not. (#1106) 2017-09-01 20:38:13 +12:00
Roland PramlandRob Bygrave f805bec8f2 FIX: Using correct datatype for null value when json is serialized to varchar column (#1105) 2017-09-01 20:36:41 +12:00
Roland PramlandRob Bygrave 46cdc84e6b Refactor/findannotation (#1104)
* refactor: renamed findAnnotation method that searches recursive to findAnnotationRecursive

* CHG: searching namedQueries & indices also recursive in superclasses

* FIX: Using the AnnotationBase methods on several places and documented them
2017-09-01 20:25:10 +12:00
Roland PramlandRob Bygrave e908d16ba8 Pr/add tests (#1103)
* ADD: Testcase for Sqlserver-6.2.0 driver

* CHG: Removed Row-limit - so we need no special handling when sqlserver-platform generates a "select top 100" statement

* ADD: additional asserts to TestQueryCache

* CHG: Ignore test for sqlserver
2017-09-01 20:17:56 +12:00
rob bygrave 96e3f4138f #1064 - Query table alias not used in update() or delete() queries 2017-08-31 21:29:08 +12:00
rob bygrave 977dd52890 No effective change - code reformat only 2017-08-31 21:10:13 +12:00
Roland PramlandRob Bygrave a9b7bb2175 FIX: Passing MDC through background-executor (#1102) 2017-08-31 21:09:04 +12:00
rob bygrave f83c7fdac8 #1100 - Refactor OrmQueryPlanKey to use string expression for where 2017-08-31 02:11:49 +12:00
rob bygrave a695fb59b2 [maven-release-plugin] prepare for next development iteration 2017-08-31 01:50:20 +12:00
rob bygrave e197bb2900 [maven-release-plugin] prepare release ebean-10.4.3 2017-08-31 01:50:06 +12:00
rob bygrave b54b8815d9 Merge branch 'master' of github.com:ebean-orm/ebean 2017-08-30 21:59:08 +12:00
rob bygrave 1d1fde61be #1101 - findNative() on a bean with an associated @OneToOne (with the PK being derived from the parent) maps incorrectly 2017-08-30 21:58:33 +12:00
rob bygrave 683186cbe9 No effective change - pom whitespace only 2017-08-30 21:14:20 +12:00
Roy de BokxandRob Bygrave 5e6be40454 Only throwing warning for autocommit when server is not set to use autocommit-friendly TransactionManager. (#1085) 2017-08-30 20:57:49 +12:00
Roland PramlandRob Bygrave c00febd95b ENH: Abort all tests if connection to db could not be esablished (#1099) 2017-08-30 20:04:50 +12:00
Roland PramlandRob Bygrave 4c8e1113bf FIX: deadlock in TestCommitAndContiune for sqlserver & hsqldb (#1098) 2017-08-30 20:03:41 +12:00
Roland PramlandRob Bygrave 6f854da70b Fix/corrected error codes (#1097)
* FIX: db2plaform error codes and dbtypes

* FIX: H2: Add error code for locking

* FIX: Mysql: Add DataIntegrity-error code
2017-08-30 20:02:34 +12:00
Roland PramlandRob Bygrave 3f5a162a4e Fix/connection to different platforms (#1096)
* ADD: Profile for oracle and db2 to pom file. Downgraded mysql driver

* FIX: connection string for several platforms

* ADD: Documentation
2017-08-30 20:00:58 +12:00
Roland PramlandRob Bygrave d14529b602 FIX: optimized unique constraint handling for platforms that does support them on nullable columns (#1095) 2017-08-30 19:54:53 +12:00
Roland PramlandRob Bygrave f471961f06 FIX: extra-ddl modified to be compatible with different platfoms (#1094) 2017-08-30 17:51:41 +12:00
Roland PramlandRob Bygrave 457c1356a4 Fix/shortened tablenames (#1093)
* FIX: Shortened tablename (will fail on oracle)

* FIX: renamed table, otherwise, in db2 he_link_doc & hi_link_ are both generating the same index ix_h_lnk_dc_h_dc
2017-08-30 17:50:40 +12:00
Roland PramlandRob Bygrave 419ac2bf50 FIX: Possible NPE in StringHelper (#1092) 2017-08-30 17:49:34 +12:00
Roland PramlandRob Bygrave 727d088577 FIX: add MAX-DATE as default value for ROW END (#1091) 2017-08-30 17:48:36 +12:00
Roland PramlandRob Bygrave 972d2dcc6b FIX: nvarchar(max) length specified differently, otherwise this leads to a ddl like this "nvarchar(max)(700)" (#1090) 2017-08-30 17:47:21 +12:00
Roland PramlandRob Bygrave a5bcd49ecb FIX: Clean up before running tests - otherwise tests will fail if a previous test did not clean up well. (This fixes test failures in Eclipse where tests are executed in different order than in maven) (#1089) 2017-08-30 17:44:30 +12:00
Roland PramlandRob Bygrave ff496ad2d3 FIX: EnumToDbIntegerMap did not set 'integerType' - this leads to an incompatible quoting. Constraints like "check in ('1','2','3')" were not accepted by hsqldb on integer columns. (#1088) 2017-08-30 17:42:25 +12:00
Roland PramlandRob Bygrave 24d438ba23 FIX: H2HistoryTrigger honors schema (#1087) 2017-08-30 17:38:52 +12:00
Roland PramlandRob Bygrave 591067e39e FIX: Typo in classname (#1086) 2017-08-30 17:37:45 +12:00
rob bygrave 38a95dc5c4 No effective change - Reformat code - ScalarTypeEnumStandard 2017-08-24 22:37:41 +12:00
icodeandRob Bygrave 53426d8493 use jackson configuration [SD]eserialization enum (#1079)
* use jackson configuration [SD]eserialization enum

https://github.com/FasterXML/jackson-databind/wiki/Serialization-features 
https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features

keep jackson and ebean [SD]eserialization same
`objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);`

* Update ScalarTypeEnumStandard.java

* Update ScalarTypeEnumStandard.java

* Update ScalarTypeEnumStandard.java

* Update ScalarTypeEnumStandard.java
2017-08-24 22:35:36 +12:00
rob bygrave a06238558e No effective change - Jackson WRITE_ENUMS_USING_INDEX test 2017-08-24 22:34:57 +12:00
Yuan XueguiandRob Bygrave b3a3b19d5d Fix: query table entity or sql entity has no id (#1082)
When query table entity or sql entity,include NamedQuery which modified via code and RawSqlQuery which modified via code.
2017-08-24 22:18:46 +12:00
rob bygrave 57231c80a8 #1084 - NPE When DB Migration generation run with @WhoCreated mapping 2017-08-24 22:15:01 +12:00
rob bygrave dc96fb9ad5 No effective change - update test, better asserts 2017-08-15 21:47:21 +12:00
rob bygrave 567847b47a #1081 - Update jackson-core dependency to 2.9.0 2017-08-05 00:11:53 +12:00
rob bygrave b74fed7073 Bump to 10.4.1 agent (@Transient properties included in mapped superclass enhancement detection) 2017-08-04 22:59:53 +12:00
rob bygrave cdaf8ee6d5 [maven-release-plugin] prepare for next development iteration 2017-08-04 21:33:33 +12:00
rob bygrave c6c5073e47 [maven-release-plugin] prepare release ebean-10.4.2 2017-08-04 21:33:19 +12:00
rob bygrave 2a58b954e9 #1062 - Multi-tenant - Update statements should include tenantId in where clause 2017-08-04 21:16:14 +12:00
Koen De GrooteandRob Bygrave 9772623880 Added the Serializable interface to classes that extend/implement a Comparator with an anonymous/typed class. (#1078) 2017-08-03 23:29:56 +12:00
rob bygrave 27b6183aad Merge branch 'master' of github.com:ebean-orm/ebean 2017-08-03 23:28:35 +12:00
David HofmannandRob Bygrave 55e19618f8 fix connection leak in JTA managed transactions (#1077) 2017-08-03 23:28:21 +12:00
rob bygrave 5c60ba4d7f Update test for WriteJson to just use PathProperties 2017-08-03 23:01:22 +12:00
rob bygrave b9cc584086 #1080 - NOT IN expression using entity beans / reference beans uses "in" 2017-08-03 20:31:18 +12:00
Koen De GrooteandRob Bygrave bf5a8c2fad Changed some boolean checks to use the recommended method of comparing floating point values in Java. (#1075) 2017-07-22 13:00:24 +12:00
rob bygrave 66138e5ea2 #1070 Fix for - Create test for bean cache not being cleared with delete or update queries 2017-07-21 23:31:23 +12:00
Rien Nentjes 75fb89ef76 Create test for bean cache not being cleared with delete or update queries 2017-07-14 11:04:43 +02:00
rob bygrave a2cb468222 No effective change - add test resource ebean.mf to hint to enhancement agent packages to enhance 2017-07-11 21:11:54 +12:00
rob bygrave 166bcd7c45 #1061 - findCount query is not cached in L2 query cache 2017-07-06 21:51:52 +12:00
rob bygrave 95aed6af54 #1060 - findSingleAttributeList doesn't store result in L2 query cache 2017-07-06 21:35:20 +12:00
rob bygrave 7a697c12af No effective change - moved mssql-jdbc down with other test scope dependencies 2017-07-06 21:11:40 +12:00
rob bygrave 23278ce30b #1058 - Refactor - PlatformPrefix isn't actually used in PlatformDddlWriter, removing. 2017-07-06 20:30:20 +12:00
Koen De GrooteandRob Bygrave 36a5610e25 Using Regular expression objects for splitting and replacing in strings. (#1059)
* Where possible, replace .split() and .replace() methods with compiled Regex patterns.
This prevents a regular expression from being compiled multiple times during runtime.

* Forgot making one instance `private static final`.
2017-07-06 20:14:51 +12:00
rob bygrave 08263c4588 [maven-release-plugin] prepare for next development iteration 2017-07-05 21:41:12 +12:00
rob bygrave 1e88a52208 [maven-release-plugin] prepare release ebean-10.4.1 2017-07-05 21:40:58 +12:00
rob bygrave e770a42175 Bump to 10.4.1-SNAPSHOT (due to ServerConfig h2ProductionMode removal) 2017-07-05 21:39:12 +12:00
Thibault MeyerandRob Bygrave 35b467c04f JSON simple implementation for MSSQL (#1057)
- Use JSON_VALUE
  - Use NVARCHAR type

Signed-off-by: Thibault Meyer <meyer.thibault@gmail.com>
2017-07-03 20:45:48 +12:00
Roland PramlandRob Bygrave 2001539755 Sqlserver support (#1053)
* made some tests sqlserver ready (No effective code change in src/main)

* FIX enhance packages and let all testcases inherit from BaseTestCase, so that they can be launched directly from eclipse

* ADD sqldriver in pom and datasource

* FIX: DDL-generation for sqlserver - not yet all sqlserver tests passing
mvn verify -Ddatasource.default=mssql => Tests run: 1980, Failures: 22, Errors: 44, Skipped: 16

* BaseDdlHandlerTest checks against correct sqlserver-ddl now
2017-07-03 20:27:37 +12:00
rob bygrave e49c4bfe5a #867 - ENH: Add ability to globally use quoted identifiers on all tables and columns 2017-07-02 21:07:58 +12:00
rob bygrave 15be31fd5f #1051 - Remove ServerConfig h2ProductionMode ... means for testing with h2 explicitly set ddlGenerate and ddlRun 2017-07-02 11:42:56 +12:00
rob bygrave 4777ec46be #1050 - Bump db-migration to 10.1.10 2017-07-02 11:12:03 +12:00
rob bygrave 80ab731aec Use older postgres jdbc driver 2017-06-25 22:56:21 +12:00
rob bygrave 080bafe18f #1048 - Fix connection leak on failed findIterate() 2017-06-25 22:39:04 +12:00
rob bygrave e913f88f52 Fix test - TestCommitAndContinue ... close extra transactions used in asserts 2017-06-25 21:43:37 +12:00
rob bygrave e94c5484a0 #1047 - Fix connection leak on failed beginTransaction() 2017-06-25 21:42:51 +12:00
rob bygrave 4af00c51d3 travis - dist: trusty ... to get PG 9.6 2017-06-25 19:57:31 +12:00
rob bygrave 23d1256dbf Update to postgres 9.6 for travis test runs 2017-06-25 19:43:05 +12:00
rob bygrave ed0ef46d2e No effective change - update postgresql jdbc driver 2017-06-25 19:03:07 +12:00
rob bygrave 1860ded735 [maven-release-plugin] prepare for next development iteration 2017-06-25 16:01:50 +12:00
1315 changed files with 66489 additions and 21708 deletions
+1
View File
@@ -10,3 +10,4 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
spaces_around_operators = true
max_line_length = 130
+2 -1
View File
@@ -13,10 +13,11 @@ ebean-profiling*.xml
/db
/mydb.db
!src/test/ddl-review/*.sql
profiling/
# Intellij project files
*.iml
*.ipr
*.iws
.idea/
*uuid.state
+13 -1
View File
@@ -1,3 +1,4 @@
dist: trusty
sudo: required
language: java
jdk:
@@ -7,17 +8,28 @@ git:
depth: 3
addons:
postgresql: "9.4"
postgresql: "9.6"
# apt:
# packages:
# - mysql-server-5.7
# - mysql-client-core-5.7
# - mysql-client-5.7
services:
- docker
- postgresql
- mysql
before_script:
- ./.travis/setup_database
- mysql -u root -e 'CREATE DATABASE test_ebean;'
- mysql -u root -e "CREATE USER 'test_ebean'@'localhost' IDENTIFIED BY 'test';"
- mysql -u root -e "GRANT ALL ON test_ebean.* TO 'test_ebean'@'localhost';"
env:
- EBEAN_DB=h2
- EBEAN_DB=pg
# - EBEAN_DB=mysql
install: true
+298
View File
@@ -0,0 +1,298 @@
Overview of ebean.properties file
=================================
### DbMigration options
You can set individual values for different platforms when generating migrations
`dbmigration.platform.<PLATFORM>.databaseSequenceBatchSize`
For DB's using sequences this is the number of sequence values prefetched.
`dbmigration.platform.<PLATFORM>.dbuuid`
Control, how UUID generation should work - it affects DDL which column type is generated. Possible values:
- BINARY enforces binary UUID
- VARCHAR enforces varchar UUID
- BINARY_OPTIMIZED enforces binary-optimized UUID (makes sense only with Type1 ID)
- AUTO_BINARY use binary when platform does not support native UUID
- AUTO_BINARY_OPTIMIZED use binary-optimized when platform does not support native UUID
- AUTO_VARCHAR (default) use varchar when platform does not support UUID
`dbmigration.platform.<PLATFORM>.uuidStoreAsBinary`
Same as setting dbuuid to BINARY
`dbmigration.platform.<PLATFORM>.geometrySRID`
The Geometry SRID value (default 4326).
`dbmigration.platform.<PLATFORM>.idType`
The ID type (IDENTITY, SEQUENCE, GENERATOR, EXTERNAL)
`dbmigration.platform.<PLATFORM>.mapping`
Adjust the mapping. For example `BOOLEAN=integer(32);BIT=tinyint(3)`
`ebean.migration.applyPrefix`
Set this to "V" to be compatible with FlywayDB.
`ebean.migration.applySuffix`
Defaults to '.sql'
`ebean.migration.dbusername`
DB user used to run the DB migration.
`ebean.migration.dbpassword`
DB password used to run the DB migration.
`ebean.migration.generate`
Set to true if the DB migration should be generated on server start.
`ebean.migration.generatePendingDrop`
The version of a pending drop that should be generated as the next migration.
`ebean.migration.includeGeneratedFileComment`
TODO
`ebean.migration.metaTable`
For running migration the DB table that holds migration execution status. Default 'db_migration'
`ebean.migration.migrationPath`
Resource path for the migration xml and sql.
`ebean.migration.modelPath`
Subdirectory the model xml files go into. Default 'model'
`ebean.migration.modelSuffix`
Suffix. Default '.model.xml'
`ebean.migration.name`
Description text that can be appended to the version to become the ddl script file name
`ebean.migration.patchInsertOn`
migration versions that should be added to history without running.
`ebean.migration.patchResetChecksumOn`
migration versions that should have their checksum reset and not run.
Use this if you get a 'Checksum mismatch' error.
`ebean.migration.placeholders`
A comma and equals delimited placeholders that are substituted in SQL scripts when running migration (used by DB Migration runner only).
`ebean.migration.platform`
The database platform to generate migration DDL for.
`ebean.migration.run`
Flag set to true means to run any outstanding migrations on startup.
`ebean.migration.version`
The migration version name (typically FlywayDb compatible). Example: 1.1.1_2
### Ebean UUID options
`ebean.uuidVersion`
Controls, how the UUIDs are generated. Possible values
- VERSION4 (default) generate random V4 UUIDs,
- VERSION1 generate rfc4122 compliant Type 1 UUIDs (requires a state file)
- VERSION1RND generate fake Type 1 UUIDs
Note, that V1 UUIDs in conjunction with AUTO_BINARY_OPTIMIZED will give you the best index performance, but you MUST understand how this works to avoid collisions.
`ebean.uuidStateFile`
The state file that is Required to generate V1 UUIDs
### DocStoreConfig
`ebean.docstore.active`
True when the Document store integration is active/on.
`ebean.docstore.allowAllCertificates`
Set to true such that the client allows connections to invalid/self signed SSL certificates.
`ebean.docstore.bulkBatchSize`
The default batch size to use for the Bulk API calls.
`ebean.docstore.create`
When true the Document store should create any document indexes that don't already exist.
`ebean.docstore.dropCreate`
When true the Document store should drop and re-create document indexes.
`ebean.docstore.generateMapping`
Set to true means Ebean will generate mapping files on startup.
`ebean.docstore.mappingPath`
Resource path for the Document store mapping files.
`ebean.docstore.mappingSuffix`
Suffix used for mapping files.
`ebean.docstore.password`
Password credential that be used for authentication to document store.
`ebean.docstore.pathToResources`
Location of resources that mapping files are generated into.
`ebean.docstore.persist`
The default mode used by indexes. `DEFAULT|IGNORE|QUEUE|UPDATE`
`ebean.docstore.url`
The URL of the Document store server. For example: http://localhost:9200.
`ebean.docstore.username`
Credential that be used for authentication to document store.
### Ebean Autotune
`ebean.autoTune.mode`
The autoTune mode `DEFAULT_OFF|DEFAULT_ON|DEFAULT_ONIFEMPTY`
`ebean.autoTune.profiling`
TODO
`ebean.autoTune.profilingBase`
TODO
`ebean.autoTune.profilingFile`
TODO
`ebean.autoTune.profilingRate`
TODO
`ebean.autoTune.profilingUpdateFrequency`
TODO
`ebean.autoTune.queryTuning`
TODO
`ebean.autoTune.queryTuningAddVersion`
TODO
`ebean.autoTune.queryTuningFile`
TODO
`ebean.autoTune.skipGarbageCollectionOnShutdown`
TODO
`ebean.autoTune.skipProfileReportingOnShutdown`
TODO
### Ebean options
`ebean.allQuotedIdentifiers`
Quote all identifiers
`ebean.asOfSysPeriod`
Column used to support history and 'As of' queries. This column is a timestamp range or equivalent.
`ebean.asOfViewSuffix`
Suffix appended to the base table to derive the view that contains the union of the base table and the history table in order to support asOf queries.
`ebean.autoCommitMode`
Set to true if the DataSource uses autoCommit. Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
`ebean.autoReadOnlyDataSource`
When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig
`ebean.autostart`
Should the server start all
`ebean.backgroundExecutorSchedulePoolSize`
TODO
`ebean.backgroundExecutorShutdownSecs`
TODO
and some more todos:
`ebean.batch.mode`
`ebean.batch.size`
`ebean.changeLogAsync`
`ebean.changeLogIncludeInserts`
`ebean.classes`
`ebean.collectQueryOrigins`
`ebean.collectQueryStatsByNode`
`ebean.currentUserProvider`
`ebean.dataSourceJndiName`
`ebean.dataTimeZone`
`ebean.databaseBooleanFalse`
`ebean.databaseBooleanTrue`
`ebean.databasePlatform`
`ebean.databasePlatformName`
`ebean.databaseSequenceBatchSize`
ebean.datasource.h2.adminpassword
ebean.datasource.h2.adminusername
`ebean.dbEncrypt`
`ebean.dbOffline`
ebean.ddl.createOnly
ebean.ddl.generate
ebean.ddl.header
ebean.ddl.initSql
ebean.ddl.run
ebean.ddl.seedSql
ebean.defaultDeleteMissingChildren
ebean.defaultOrderById
ebean.disableClasspathSearch
ebean.disableL2Cache
ebean.docStoreOnly
ebean.docstore.active
ebean.docstore.allowAllCertificates
ebean.docstore.bulkBatchSize
ebean.docstore.create
ebean.docstore.dropCreate
ebean.docstore.generateMapping
ebean.docstore.mappingPath
ebean.docstore.mappingSuffix
ebean.docstore.password
ebean.docstore.pathToResources
ebean.docstore.persist
ebean.docstore.url
ebean.docstore.username
ebean.encryptDeployManager
ebean.encryptKeyManager
ebean.encryptor
ebean.explicitTransactionBeginMode
ebean.expressionEqualsWithNullAsNoop
ebean.expressionNativeIlike
ebean.geometrySRID
ebean.historyTableSuffix
ebean.jdbcFetchSizeFindEach
ebean.jdbcFetchSizeFindList
ebean.jodaLocalTimeMode
ebean.jsonDateTime
ebean.jsonInclude
ebean.lazyLoadBatchSize
ebean.localTimeWithNanos
ebean.namingConvention.schema
ebean.namingConvention.sequenceFormat
ebean.namingConvention.useForeignKeyPrefix
ebean.namingconvention
ebean.notifyL2CacheInForeground
ebean.packages
ebean.persistBatch
ebean.persistBatchOnCascade
ebean.persistBatchSize
ebean.persistBatching
ebean.persistenceContextScope
ebean.profiling
ebean.profiling.directory
ebean.profiling.includeProfileIds
ebean.profiling.minimumMicros
ebean.profiling.profilesPerFile
ebean.profiling.verbose
ebean.queryBatchSize
ebean.queryPlanTTLSeconds
ebean.search.packages
ebean.serverCachePlugin
ebean.skipCacheAfterWrite
ebean.slowQueryMillis
ebean.tenant.catalogProvider
ebean.tenant.currentTenantProvider
ebean.tenant.mode
ebean.tenant.partitionColumn
ebean.tenant.schemaProvider
ebean.updateAllPropertiesInBatch
ebean.updateChangesOnly
ebean.updatesDeleteMissingChildren
ebean.useJavaxValidationNotNull
ebean.useJtaTransactionManager
+153
View File
@@ -0,0 +1,153 @@
This is a short description how to run tests
============================================
Run tests from maven
--------------------
Just type `mvn test` - it will run the whole test suite with the settings
from `src/test/resources/ebean.properties`.
Run tests from eclipse
----------------------
Running tests requires, that the classes are enhanced. By default, the
`BaseTestCase` tries to load the enhancer-agent in a very early stage. This
also works for most test cases, but it does not work if you try to run a
complete test-suite, as classes that are loaded before the enhancer-agent
kicks in will not get enhanced. To solve this problem, you need either the
[Eclipse-Enhancer-Plugin](https://github.com/ebean-orm-tools/ebean-eclipse-enhancer)
or add the agent to your JVM arguments (Preferences->Installed JREs) or to
your run configuration:
`-javaagent:<USER_HOME>/.m2/repository/io/ebean/ebean-agent/<VERSION>/ebean-agent-<VERSION>.jar=debug=0`
Platform Tests
==============
By default, the `h2` platform is used. To test a different platform, add
`-Ddatasource.default=xxx` to your maven command or JVM arguments.
'h2' platform
-------------
Reqires no setup
run: `mvn clean test`
Current status: PASS
Tests run: 1986, Failures: 0, Errors: 0, Skipped: 16
'pg' platform
-------------
Reqirements
- a locally installed PostgreSQL server with PostGis.
- Create a user "unit" with password "unit" and a database "unit"
- Give the user all permissions to that db
run: `mvn clean test -Ddatasource.default=pg`
Current status: PASS
Tests run: 2166, Failures: 0, Errors: 0, Skipped: 25
'mysql' platform
-------------
Reqirements
- a locally installed MySQL server.
- Create a user "unit" with password "unit" and a database "unit"
- Give the user all permissions to that db
run: `mvn clean test -Ddatasource.default=mysql`
Current status: PASS
Tests run: 2166, Failures: 0, Errors: 0, Skipped: 33
'sqlserver' platform
--------------------
Reqires an installed sqlserver - e.g. https://hub.docker.com/r/microsoft/mssql-server-linux/
- Create a user "ebean" with password "ebean" and a database "ebean_unittest"
- Adjust the connection string and/or username in ebean.properties
run: `mvn clean test -Ddatasource.default=mssql`
Current status: PASS
Tests run: 2166, Failures: 0, Errors: 0, Skipped: 39
'oracle' platform
-----------------
Reqires a locally installed Oracle server.
- Create a user "unit" with password "unit" with all permissions
- The test wirtes directly to the "xe" SID - so do not use a productive server!
- As the Oracle JDBC driver is only available on an oracle repository,
you need a special maven setup as described [here](http://docs.oracle.com/middleware/1213/core/MAVEN/config_maven_repo.htm#MAVEN9010)
run: `mvn clean test -Ddatasource.default=ora -Poracle`
Current status: FAIL
Tests run: 1986, Failures: 17, Errors: 34, Skipped: 16
'db2' platform
--------------
Reqires a locally installed DB2 Express-C server.
- Create a user "unit" with password "unit" with all permissions
- Install the db2jcc4 driver (see pom.xml)
run: `mvn clean test -Ddatasource.default=db2 -Pdb2`
Current status: FAIL
Tests run: 2166, Failures: 29, Errors: 58, Skipped: 30
'sqlite' platform
-----------------
Reqires no setup
run: `mvn clean test -Ddatasource.default=sqlite`
Current status: FAIL
Tests run: 1986, Failures: 24, Errors: 86, Skipped: 16
After some time, the db locks up, every test takes 3 seconds and fails with
"the database file is locked"
'hsqldb' platform
-----------------
Reqires no setup
run: `mvn clean test -Ddatasource.default=hsqldb`
Current status: FAIL
Tests run: 1980, Failures: 3, Errors: 657, Skipped: 16
@Rob FYI
The problem is https://sourceforge.net/p/hsqldb/bugs/1364/
In `InsertHandler.getPstmt` the `meta.getIdentityDbColumns()` contains
'id' in lowercase. Changing that to uppercase gives me this result:
`Tests run: 1986, Failures: 2, Errors: 18, Skipped: 16`
(as quick hack I added temporary a toUpperCase to BindableIdScalar.getIdentityColumn)
'sqlanywhere' platform
----------------------
TODO: not yet tested
+119 -42
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>10.3.2</version>
<version>11.15.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -17,15 +17,90 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-10.3.2</tag>
<tag>ebean-11.15.1</tag>
</scm>
<profiles>
<profile>
<!-- Note: to use this profile, you need credentials for the oracle repository in your
settings.xml as described here:
http://docs.oracle.com/middleware/1213/core/MAVEN/config_maven_repo.htm#MAVEN9010 -->
<id>oracle</id>
<dependencies>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>maven.oracle.com</id>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<url>https://maven.oracle.com</url>
<layout>default</layout>
</repository>
</repositories>
</profile>
<profile>
<!-- Note: to use this profile, you need to download manually the db2jcc4 driver.
After that, install it into your local maven repository:
mvn install:install-file \
-Dfile=db2jcc4.jar \
-DgroupId=com.ibm.jdbc \
-DartifactId=db2jcc4 \
-Dversion=4.23.42 \
-Dpackaging=jar
-->
<id>db2</id>
<dependencies>
<dependency>
<groupId>com.ibm.jdbc</groupId>
<artifactId>db2jcc4</artifactId>
<version>4.23.42</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<!--
Projects are expected to explicit depend on version
of slf4j that they want to use
-->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
<scope>provided</scope>
</dependency>
<!--
Exclude snakeyaml if you desire - it is not strictly required.
-->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.19</version>
</dependency>
<!--
Class retention Nonnull and Nullable annotations
to assist with IDE auto-completion with Ebean API
-->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jsr305</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -37,63 +112,45 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-datasource-api</artifactId>
<version>1.1</version>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-datasource</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-classpath-scanner-api</artifactId>
<version>2.2</version>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-classpath-scanner</artifactId>
<version>2.2.2</version>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-dbmigration</artifactId>
<version>10.1.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>[1.7.1,1.7.99)</version>
<artifactId>ebean-migration</artifactId>
<version>11.5.1</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.6</version>
<version>4.7.1</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.5</version>
<version>2.9.0</version>
</dependency>
<!-- provided scope for JsonNode support -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.5</version>
<version>2.9.0</version>
<scope>provided</scope>
</dependency>
@@ -143,12 +200,19 @@
<!--<scope>test</scope>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>microsoft</groupId>-->
<!--<artifactId>sqlserver-jdbc</artifactId>-->
<!--<version>4.2</version>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-docker-run</artifactId>
<version>1.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
@@ -160,7 +224,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>10.3.1</version>
<version>11.10.1</version>
<scope>test</scope>
</dependency>
@@ -169,7 +233,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.193</version>
<version>1.4.196</version>
<scope>provided</scope>
</dependency>
@@ -187,10 +251,18 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.1.3.jre8-preview</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.5</version>
<!-- Do not upgrade to 6.0.6: https://bugs.mysql.com/bug.php?id=82896 -->
<version>5.1.43</version>
<scope>test</scope>
</dependency>
@@ -225,7 +297,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>10.3.1</version>
<version>11.10.1</version>
<executions>
<execution>
<id>test</id>
@@ -258,6 +330,11 @@
<name>datasource.default</name>
<value>${datasource.default}</value>
</property>
<property>
<!-- transfer dbClockDelta parameter -->
<name>dbClockDelta</name>
<value>${dbClockDelta}</value>
</property>
</systemProperties>
</configuration>
</plugin>
@@ -282,7 +359,7 @@
<overview>src/main/java/io/ebean/overview.html</overview>
<source>1.8</source>
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
<excludePackageNames>io.ebeaninternal.*:com.avaje.ebean.util:io.ebean.dbmigration.ddlgeneration:io.ebean.dbmigration.migration:io.ebean.dbmigration.migrationreader:io.ebean.dbmigration.model</excludePackageNames>
<excludePackageNames>io.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
<docletArtifact>
<groupId>org.avaje</groupId>
<artifactId>pygments-doclet</artifactId>
+169
View File
@@ -0,0 +1,169 @@
package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
/**
* Provides finder functionality for use with "Dependency Injection style" use of Ebean.
* <p>
* Note that typically users would extend BeanRepository rather than BeanFinder.
* </p>
* <pre>{@code
*
* public class CustomerFinder extends BeanFinder<Long,Customer> {
*
* @Inject
* public CustomerFinder(EbeanServer server) {
* super(Customer.class, server);
* }
*
* // ... add customer specific finders
* }
*
* }</pre>
*
* @param <I> The ID type
* @param <T> The Bean type
*/
public abstract class BeanFinder<I,T> {
protected final EbeanServer server;
protected final Class<T> type;
/**
* Create with the given bean type and EbeanServer instance.
*
* @param type The bean type
* @param server The EbeanServer instance typically created via Spring factory or equivalent.
*/
protected BeanFinder(Class<T> type, EbeanServer server) {
this.type = type;
this.server = server;
}
/**
* Return the EbeanServer to use.
*/
public EbeanServer db() {
return server;
}
/**
* Return the current transaction.
*/
public Transaction currentTransaction() {
return db().currentTransaction();
}
/**
* Flush the JDBC batch on the current transaction.
*/
public void flush() {
db().flush();
}
/**
* Return typically a different EbeanServer to the default.
* <p>
* This is equivalent to {@link Ebean#getServer(String)}
*
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is
* returned.
*/
public EbeanServer db(String server) {
return Ebean.getServer(server);
}
/**
* Creates an entity reference for this ID.
* <p>
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
*/
@Nonnull
public T ref(I id) {
return db().getReference(type, id);
}
/**
* Retrieves an entity by ID.
* <p>
* Equivalent to {@link EbeanServer#find(Class, Object)}
*/
@Nullable
public T findById(I id) {
return db().find(type, id);
}
/**
* Find an entity by ID returning an Optional.
*/
@Nullable
public Optional<T> findByIdOrEmpty(I id) {
return db().find(type).setId(id).findOneOrEmpty();
}
/**
* Delete a bean by Id.
* <p>
* Equivalent to {@link EbeanServer#delete(Class, Object)}
*/
public void deleteById(I id) {
db().delete(type, id);
}
/**
* Retrieves all entities of the given type.
*/
@Nonnull
public List<T> findAll() {
return query().findList();
}
/**
* Creates an update query.
*
* <pre>{@code
*
* int rows =
* updateQuery()
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* <p>
* Equivalent to {@link EbeanServer#update(Class)}
*/
protected UpdateQuery<T> updateQuery() {
return db().update(type);
}
/**
* Creates a query.
* <p>
* Equivalent to {@link EbeanServer#find(Class)}
*/
protected Query<T> query() {
return db().find(type);
}
/**
* Creates a native sql query.
*/
protected Query<T> nativeSql(String nativeSql) {
return db().findNative(type, nativeSql);
}
/**
* Creates a query using the ORM query language.
*/
protected Query<T> query(String ormQuery) {
return db().createQuery(type, ormQuery);
}
}
+178
View File
@@ -0,0 +1,178 @@
package io.ebean;
import io.ebean.bean.EntityBean;
/**
* Provides finder functionality for use with "Dependency Injection style" use of Ebean.
* <p>
* <pre>{@code
*
* @Repository
* public class CustomerRepository extends BeanRepository<Long,Customer> {
*
* @Inject
* public CustomerRepository(EbeanServer server) {
* super(Customer.class, server);
* }
*
* // ... add customer specific finders and persist logic
*
* public List<Customer> findByName(String nameStart) {
* return query().where()
* .istartsWith("name", nameStart)
* .findList();
* }
*
* }
* }</pre>
*
* @param <I> The ID type
* @param <T> The Bean type
*/
public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
/**
* Create with the given bean type and EbeanServer instance.
* <p>
* Typically users would extend BeanRepository rather than BeanFinder.
* </p>
* <pre>{@code
*
* @Inject
* public CustomerRepository(EbeanServer server) {
* super(Customer.class, server);
* }
*
* }</pre>
*
* @param type The bean type
* @param server The EbeanServer instance typically created via Spring factory or equivalent
*/
protected BeanRepository(Class<T> type, EbeanServer server) {
super(type, server);
}
/**
* Marks the entity bean as dirty.
* <p>
* This is used so that when a bean that is otherwise unmodified is updated the version
* property is updated.
* <p>
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
* dirty so that it is not skipped.
* <p>
* <pre>{@code
*
* Customer customer = customerRepository.byId(id);
*
* // mark the bean as dirty so that a save() or update() will
* // increment the version property
*
* customerRepository.markAsDirty(customer);
* customerRepository.save(customer);
*
* }</pre>
*
* @see EbeanServer#markAsDirty(Object)
*/
public void markAsDirty(T bean) {
db().markAsDirty(bean);
}
/**
* Mark the property as unset or 'not loaded'.
* <p>
* This would be used to specify a property that we did not wish to include in a stateless update.
* </p>
* <pre>{@code
*
* // populate an entity bean from JSON or whatever
* Customer customer = ...;
*
* // mark the email property as 'unset' so that it is not
* // included in a 'stateless update'
* customerRepository.markPropertyUnset(customer, "email");
*
* customerRepository.update(customer);
*
* }</pre>
*
* @param propertyName the name of the property on the bean to be marked as 'unset'
*/
public void markPropertyUnset(T bean, String propertyName) {
((EntityBean) bean)._ebean_getIntercept().setPropertyLoaded(propertyName, false);
}
/**
* Insert or update this entity depending on its state.
* <p>
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
* insert or an update based on that.
*
* @see EbeanServer#save(Object)
*/
public void save(T bean) {
db().save(bean);
}
/**
* Update this entity.
*
* @see EbeanServer#update(Object)
*/
public void update(T bean) {
db().update(bean);
}
/**
* Insert this entity.
*
* @see EbeanServer#insert(Object)
*/
public void insert(T bean) {
db().insert(bean);
}
/**
* Delete this bean.
* <p>
* This will return true if the bean was deleted successfully or JDBC batch is being used.
* </p>
* <p>
* If there is no current transaction one will be created and committed for
* you automatically.
* </p>
* <p>
* If the Bean does not have a version property (or loaded version property) and
* the bean does not exist then this returns false indicating that nothing was
* deleted. Note that, if JDBC batch mode is used then this always returns true.
* </p>
*
* @see EbeanServer#delete(Object)
*/
public boolean delete(T bean) {
return db().delete(bean);
}
/**
* Delete a bean permanently without soft delete.
* <p>
* This is used when the bean contains a <code>@SoftDelete</code> property and we
* want to perform a hard/permanent delete.
* </p>
*
* @see EbeanServer#deletePermanent(Object)
*/
public boolean deletePermanent(T bean) {
return db().deletePermanent(bean);
}
/**
* Refreshes this entity from the database.
*
* @see EbeanServer#refresh(Object)
*/
public void refresh(T bean) {
db().refresh(bean);
}
}
-11
View File
@@ -1,6 +1,5 @@
package io.ebean;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.Set;
@@ -99,16 +98,6 @@ public interface BeanState {
*/
void setReadOnly(boolean readOnly);
/**
* Add a propertyChangeListener.
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a propertyChangeListener.
*/
void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
+93
View File
@@ -0,0 +1,93 @@
package io.ebean;
/**
* Enum to control the different cache modes for queryCache and beanCache.
* <h3>Bean cache</h3>
* <p>
* The bean cache is automatically used by default on <code>@Cache</code> beans for
* the following queries:
* </p>
* <ul>
* <li>findOne() by id</li>
* <li>findOne() by natural key(s)</li>
* <li>findList() by ids</li>
* </ul>
* <p>
* Bean caching needs to be explicitly turned on for queries that are findList() by natural keys.
* </p>
* <h3>Query cache</h3>
* <p>
* For query cache use note that you must be careful, what you do with the returned collection.
* By default the returned collections are read only and you will get an exception if you try
* to change them.
* If you add ".setReadOnly(false)" to your query, you'll get a collection that is a clone from the
* one in the cache. That means, changing does not affect the cache.
* </p>
*
* @author Roland Praml, FOCONIS AG
*/
public enum CacheMode {
/**
* Do not use cache.
*/
OFF(false, false),
/**
* Use the cache and store a result when needed.
*/
ON(true, true),
/**
* Only used for bean caching.
* <p>
* The bean cache is automatically used by default on <code>@Cache</code> beans for
* the following queries:
* </p>
* <ul>
* <li>findOne() by id</li>
* <li>findOne() by natural key(s)</li>
* <li>findList() by ids</li>
* </ul>
* <p>
* Bean caching needs to be explicitly turned on for queries that are findList() by natural keys.
* </p>
*/
AUTO(true, true),
/**
* Do not read from cache, but put beans into the cache and invalidate parts of the cache as necessary.
* <p>
* Use this on a query if you want to get the fresh value from database and put it into the cache.
*/
PUT(false, true),
/**
* GET only from the cache.
* <p>
* This mode does not put entries into the cache or invalidate parts of the cache.
*/
GET(true, false);
private boolean get;
private boolean put;
CacheMode(boolean get, boolean put) {
this.get = get;
this.put = put;
}
/**
* Return true if value is read from cache.
*/
public boolean isGet() {
return get;
}
/**
* Return true if a newly loaded value (from database) is put into the cache.
*/
public boolean isPut() {
return put;
}
}
@@ -0,0 +1,27 @@
package io.ebean;
/**
* Enumeration to use with {@link Query#setCountDistinct(CountDistinctOrder)}.
* @author Roland Praml, FOCONIS AG
*
*/
public enum CountDistinctOrder {
NO_ORDERING,
/** order by attribute ascending */
ATTR_ASC,
/** order by attribute descending */
ATTR_DESC,
/** order by count ascending and attribute ascending */
COUNT_ASC_ATTR_ASC,
/** order by count ascending and attribute descending */
COUNT_ASC_ATTR_DESC,
/** order by count descending and attribute ascending */
COUNT_DESC_ATTR_ASC,
/** order by count descending and attribute descending */
COUNT_DESC_ATTR_DESC,
}
+33
View File
@@ -0,0 +1,33 @@
package io.ebean;
import java.io.Serializable;
/**
* Holds a distinct value with it's count.
* (Used with {@link Query#findSingleAttributeList()} and {@link Query#setCountDistinct(CountDistinctOrder)}.)
* @author Roland Praml, FOCONIS AG
*/
public class CountedValue<A> implements Serializable {
private static final long serialVersionUID = -2267971668356749695L;
private final A value;
private final long count;
public CountedValue(A value, long count) {
this.value = value;
this.count = count;
}
public long getCount() {
return count;
}
public A getValue() {
return value;
}
@Override
public String toString() {
return count + ": " + value;
}
}
+6 -9
View File
@@ -2,7 +2,7 @@ package io.ebean;
import io.ebeanservice.docstore.api.DocQueryRequest;
import io.ebeanservice.docstore.api.RawDoc;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
@@ -56,7 +56,7 @@ public interface DocumentStore {
* Return the bean by fetching it's content from the document store.
* If the document is not found null is returned.
* <p>
* Typically this is called indirectly by findUnique() on the query.
* Typically this is called indirectly by findOne() on the query.
* </p>
* <pre>{@code
*
@@ -64,7 +64,7 @@ public interface DocumentStore {
* server.find(Customer.class)
* .setUseDocStore(true)
* .setId(42)
* .findUnique();
* .findOne();
*
* }</pre>
*/
@@ -123,11 +123,8 @@ public interface DocumentStore {
* server.find(Order.class)
* .setUseDocStore(true)
* .where()... // perhaps add predicates
* .findEach(new QueryEachConsumer<Order>() {
* @Override
* public void accept(Order bean) {
* // process the bean
* }
* .findEach((Order order) -> {
* // process the bean ...
* });
*
* }</pre>
@@ -151,7 +148,7 @@ public interface DocumentStore {
* server.find(Order.class)
* .setUseDocStore(true)
* .where()... // perhaps add predicates
* .findEachWhile(new QueryEachWhileConsumer<Order>() {
* .findEachWhile(new Predicate<Order>() {
* @Override
* public void accept(Order bean) {
* // process the bean
+133
View File
@@ -0,0 +1,133 @@
package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Query for performing native SQL queries that return DTO Bean's.
* <p>
* These beans are just normal classes. They must have public constructors
* and setters.
* <p>
* Constructors with arguments are used if the number of constructor arguments
* matches the number of columns in the resultSet.
* </p>
* <p>
* If the number of columns in the resultSet is greater than the largest constructor
* then the largest constructor is used for the first columns and remaining columns
* are mapped by setter methods.
* </p>
*
* <pre>{@code
*
* // CustomerDto is just a 'bean like' class
* // with public constructor(s) and public setter methods
*
* String sql = "select id, name from customer where name like :name and status_code = :status";
*
* List<CustomerDto> beans =
* Ebean.findDto(CustomrDto.class, sql)
* .setParameter("name", "Acme%")
* .setParameter("status", "ACTIVE")
* .findList();
*
* }</pre>
*/
public interface DtoQuery<T> {
/**
* Execute the query returning a list.
*/
@Nonnull
List<T> findList();
/**
* Execute the query iterating a row at a time.
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEach(Consumer<T> consumer);
/**
* Execute the query iterating a row at a time with the ability to stop consuming part way through.
* <p>
* Returning false after processing a row stops the iteration through the query results.
* </p>
* <p>
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
* </p>
*/
void findEachWhile(Predicate<T> consumer);
/**
* Execute the query returning a single bean.
*/
@Nullable
T findOne();
/**
* Execute the query returning an optional bean.
*/
@Nonnull
Optional<T> findOneOrEmpty();
/**
* The same as bind for named parameters.
*/
DtoQuery<T> setParameter(String name, Object value);
/**
* The same as bind for positioned parameters.
*/
DtoQuery<T> setParameter(int position, Object value);
/**
* Set the index of the first row of the results to return.
*/
DtoQuery<T> setFirstRow(int firstRow);
/**
* Set the maximum number of query results to return.
*/
DtoQuery<T> setMaxRows(int maxRows);
/**
* When resultSet columns are not able to be mapped to a bean property then instead of
* throwing effectively skip reading that column.
*/
DtoQuery<T> setRelaxedMode();
/**
* Set a label on the query to make it easier to identify queries related to query execution statistics.
*
* @param label A label that is unique to the DTO bean type.
*/
DtoQuery<T> setLabel(String label);
/**
* Set a timeout on this query.
* <p>
* This will typically result in a call to setQueryTimeout() on a
* preparedStatement. If the timeout occurs an exception will be thrown - this
* will be a SQLException wrapped up in a PersistenceException.
* </p>
*
* @param secs the query timeout limit in seconds. Zero means there is no limit.
*/
DtoQuery<T> setTimeout(int secs);
/**
* A hint which for JDBC translates to the Statement.fetchSize().
* <p>
* Gives the JDBC driver a hint as to the number of rows that should be
* fetched from the database when more rows are needed for ResultSet.
* </p>
*/
DtoQuery<T> setBufferFetchSizeHint(int bufferFetchSizeHint);
}
+122 -35
View File
@@ -1,19 +1,24 @@
package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ServerConfig;
import io.ebean.plugin.Property;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -112,6 +117,10 @@ import java.util.concurrent.ConcurrentHashMap;
public final class Ebean {
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
static {
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
}
/**
* Manages creation and cache of EbeanServers.
*/
@@ -129,8 +138,7 @@ public final class Ebean {
private final ConcurrentHashMap<String, EbeanServer> concMap = new ConcurrentHashMap<>();
/**
* Cache for synchronized read, creation and put. Protected by the monitor
* object.
* Cache for synchronized read, creation and put. Protected by the monitor object.
*/
private final HashMap<String, EbeanServer> syncMap = new HashMap<>();
@@ -144,14 +152,7 @@ public final class Ebean {
private ServerManager() {
try {
// skipDefaultServer is set by EbeanServerFactory
// ... when it is creating the primaryServer
if (PrimaryServer.isSkip()) {
// primary server being created by EbeanServerFactory
// ... so we should not try and create it here
logger.debug("PrimaryServer.isSkip()");
} else {
if (!PrimaryServer.isSkip()) {
// look to see if there is a default server defined
String defaultName = PrimaryServer.getDefaultServerName();
logger.debug("defaultName:{}", defaultName);
@@ -289,7 +290,7 @@ public final class Ebean {
}
/**
* Backdoor for registering a mock implementation of EbeanServer as the default server.
* Backdoor for registering a mock implementation of EbeanServer as the default server.
*/
protected static EbeanServer mock(String name, EbeanServer server, boolean defaultServer) {
EbeanServer originalPrimaryServer = serverMgr.defaultServer;
@@ -371,9 +372,10 @@ public final class Ebean {
/**
* Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics.
* <p>
* Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or
* {@link #execute(TxScope, TxRunnable)}.
* Note that this provides an try finally alternative to using {@link #executeCall(TxScope, Callable)} or
* {@link #execute(TxScope, Runnable)}.
* </p>
* <p>
* <h3>REQUIRES_NEW example:</h3>
* <pre>{@code
* // Start a new transaction. If there is a current transaction
@@ -636,6 +638,16 @@ public final class Ebean {
serverMgr.getDefaultServer().updateAll(beans);
}
/**
* Merge the bean using the given merge options.
*
* @param bean The bean to merge
* @param options The options to control the merge
*/
public static void merge(Object bean, MergeOptions options) {
serverMgr.getDefaultServer().merge(bean, options);
}
/**
* Save all the beans from a Collection.
*/
@@ -643,6 +655,65 @@ public final class Ebean {
return serverMgr.getDefaultServer().saveAll(beans);
}
/**
* This method checks the uniqueness of a bean. I.e. if the save will work. It will return the
* properties that violates an unique / primary key. This may be done in an UI save action to
* validate if the user has entered correct values.
* <p>
* Note: This method queries the DB for uniqueness of all indices, so do not use it in a batch update.
* <p>
* Note: This checks only the root bean!
* <p>
* <pre>{@code
*
* // there is a unique constraint on title
*
* Document doc = new Document();
* doc.setTitle("One flew over the cuckoo's nest");
* doc.setBody("clashes with doc1");
*
* Set<Property> properties = server().checkUniqueness(doc);
*
* if (properties.isEmpty()) {
* // it is unique ... carry on
*
* } else {
* // build a user friendly message
* // to return message back to user
*
* String uniqueProperties = properties.toString();
*
* StringBuilder msg = new StringBuilder();
*
* properties.forEach((it)-> {
* Object propertyValue = it.getVal(doc);
* String propertyName = it.getName();
* msg.append(" property["+propertyName+"] value["+propertyValue+"]");
* });
*
* // uniqueProperties > [title]
* // custom msg > property[title] value[One flew over the cuckoo's nest]
*
* }
*
* }</pre>
*
* @param bean The entity bean to check uniqueness on
* @return a set of Properties if constraint validation was detected or empty list.
*/
@Nonnull
public static Set<Property> checkUniqueness(Object bean) {
return serverMgr.getDefaultServer().checkUniqueness(bean);
}
/**
* Same as {@link #checkUniqueness(Object)}. but with given transaction.
*/
@Nonnull
public static Set<Property> checkUniqueness(Object bean, Transaction transaction) {
return serverMgr.getDefaultServer().checkUniqueness(bean, transaction);
}
/**
* Delete the bean.
* <p>
@@ -822,7 +893,7 @@ public final class Ebean {
* }</pre>
* <p>
* If you want more control over the query then you can use createQuery() and
* Query.findUnique();
* Query.findOne();
* </p>
* <pre>{@code
*
@@ -843,7 +914,8 @@ public final class Ebean {
*
* // traverse the object graph...
*
* Order order = query.findUnique();
* Order order = query.findOne();
*
* Customer customer = order.getCustomer();
* Address shippingAddress = customer.getShippingAddress();
* List<OrderDetail> details = order.getDetails();
@@ -959,7 +1031,7 @@ public final class Ebean {
* predicates, order by, limits etc.
* </p>
* <p>
* You then use findList(), findSet(), findMap() and findUnique() to execute
* You then use findList(), findSet(), findMap() and findOne() to execute
* the query and return the collection or bean.
* </p>
* <p>
@@ -991,7 +1063,7 @@ public final class Ebean {
* Query<Order> query = Ebean.createQuery(Order.class, eql);
* query.setParameter("orderId", 2);
*
* Order order = query.findUnique();
* Order order = query.findOne();
*
* // This is the same as:
*
@@ -1000,7 +1072,7 @@ public final class Ebean {
* .fetch("details")
* .fetch("detail.product", "name")
* .setId(2)
* .findUnique();
* .findOne();
*
* }</pre>
*
@@ -1054,6 +1126,21 @@ public final class Ebean {
return serverMgr.getDefaultServer().findNative(beanType, nativeSql);
}
/**
* Create a Query for DTO beans.
* <p>
* DTO beans are just normal bean like classes with public constructor(s) and setters.
* They do not need to be registered with Ebean before use.
* </p>
*
* @param dtoType The type of the DTO bean the rows will be mapped into.
* @param sql The SQL query to execute.
* @param <T> The type of the DTO bean.
*/
public static <T> DtoQuery<T> findDto(Class<T> dtoType, String sql) {
return serverMgr.getDefaultServer().findDto(dtoType, sql);
}
/**
* Create an Update query to perform a bulk update.
* <p>
@@ -1181,20 +1268,20 @@ public final class Ebean {
*
* }</pre>
*/
public static void execute(TxScope scope, TxRunnable r) {
public static void execute(TxScope scope, Runnable r) {
serverMgr.getDefaultServer().execute(scope, r);
}
/**
* Execute a TxRunnable in a Transaction with the default scope.
* Execute a Runnable in a Transaction with the default scope.
* <p>
* The default scope runs with REQUIRED and by default will rollback on any
* exception (checked or runtime).
* </p>
* <pre>{@code
*
* Ebean.execute(new TxRunnable() {
* public void run() {
* Ebean.execute(() -> {
*
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
@@ -1203,17 +1290,17 @@ public final class Ebean {
*
* Ebean.save(u1);
* Ebean.save(u2);
* }
*
* });
*
* }</pre>
*/
public static void execute(TxRunnable r) {
public static void execute(Runnable r) {
serverMgr.getDefaultServer().execute(r);
}
/**
* Execute a TxCallable in a Transaction with an explicit scope.
* Execute a Callable in a Transaction with an explicit scope.
* <p>
* The scope can control the transaction type, isolation and rollback
* semantics.
@@ -1223,7 +1310,7 @@ public final class Ebean {
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* Ebean.execute(scope, new TxCallable<String>() {
* Ebean.executeCall(scope, new Callable<String>() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* ...
@@ -1233,12 +1320,12 @@ public final class Ebean {
*
* }</pre>
*/
public static <T> T execute(TxScope scope, TxCallable<T> c) {
return serverMgr.getDefaultServer().execute(scope, c);
public static <T> T executeCall(TxScope scope, Callable<T> c) {
return serverMgr.getDefaultServer().executeCall(scope, c);
}
/**
* Execute a TxCallable in a Transaction with the default scope.
* Execute a Callable in a Transaction with the default scope.
* <p>
* The default scope runs with REQUIRED and by default will rollback on any
* exception (checked or runtime).
@@ -1249,8 +1336,8 @@ public final class Ebean {
* </p>
* <pre>{@code
*
* Ebean.execute(new TxCallable<String>() {
* public String call() {
* Ebean.executeCall(() -> {
*
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
@@ -1261,13 +1348,13 @@ public final class Ebean {
* Ebean.save(u2);
*
* return u1.getEmail();
* }
*
* });
*
* }</pre>
*/
public static <T> T execute(TxCallable<T> c) {
return serverMgr.getDefaultServer().execute(c);
public static <T> T executeCall(Callable<T> c) {
return serverMgr.getDefaultServer().executeCall(c);
}
/**
+127 -48
View File
@@ -1,13 +1,16 @@
package io.ebean;
import io.ebean.annotation.TxIsolation;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ServerConfig;
import io.ebean.meta.MetaInfoManager;
import io.ebean.plugin.Property;
import io.ebean.plugin.SpiServer;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
@@ -16,6 +19,7 @@ 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;
@@ -238,7 +242,7 @@ public interface EbeanServer {
* Query<Order> query = Ebean.createQuery(Order.class, ormQuery);
* query.setParameter("orderId", 2);
*
* Order order = query.findUnique();
* Order order = query.findOne();
*
* // This is the same as:
*
@@ -247,7 +251,7 @@ public interface EbeanServer {
* .fetch("details")
* .fetch("detail.product", "name")
* .setId(2)
* .findUnique();
* .findOne();
*
* }</pre>
*
@@ -265,7 +269,7 @@ public interface EbeanServer {
* predicates, order by, limits etc.
* </p>
* <p>
* You then use findList(), findSet(), findMap() and findUnique() to execute
* You then use findList(), findSet(), findMap() and findOne() to execute
* the query and return the collection or bean.
* </p>
* <p>
@@ -285,7 +289,7 @@ public interface EbeanServer {
* .fetch("details")
* .fetch("detail.product", "name")
* .setId(2)
* .findUnique();
* .findOne();
*
* // find some new orders ... with firstRow/maxRows
* List<Order> orders =
@@ -420,6 +424,19 @@ public interface EbeanServer {
*/
<T> Update<T> createUpdate(Class<T> beanType, String ormUpdate);
/**
* Create a Query for DTO beans.
* <p>
* DTO beans are just normal bean like classes with public constructor(s) and setters.
* They do not need to be registered with Ebean before use.
* </p>
*
* @param dtoType The type of the DTO bean the rows will be mapped into.
* @param sql The SQL query to execute.
* @param <T> The type of the DTO bean.
*/
<T> DtoQuery<T> findDto(Class<T> dtoType, String sql);
/**
* Create a SqlQuery for executing native sql
* query statements.
@@ -560,8 +577,8 @@ public interface EbeanServer {
* Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics.
* <p>
* <p>
* Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or
* {@link #execute(TxScope, TxRunnable)}.
* Note that this provides an try finally alternative to using {@link #executeCall(TxScope, Callable)} or
* {@link #execute(TxScope, Runnable)}.
* </p>
* <p>
* <h3>REQUIRES_NEW example:</h3>
@@ -694,7 +711,7 @@ public interface EbeanServer {
* <p>
* <p>
* If you want more control over the query then you can use createQuery() and
* Query.findUnique();
* Query.findOne();
* </p>
* <p>
* <pre>{@code
@@ -714,7 +731,7 @@ public interface EbeanServer {
* query.fetch("details.product", "name");
*
*
* Order order = query.findUnique();
* Order order = query.findOne();
*
* // traverse the object graph...
*
@@ -730,6 +747,7 @@ public interface EbeanServer {
* @param beanType the type of entity bean to fetch
* @param id the id value
*/
@Nullable
<T> T find(Class<T> beanType, Object id);
/**
@@ -773,6 +791,7 @@ public interface EbeanServer {
* @param beanType the type of entity bean
* @param id the id value
*/
@Nonnull
<T> T getReference(Class<T> beanType, Object id);
/**
@@ -788,6 +807,7 @@ public interface EbeanServer {
*
* @see Query#findIds()
*/
@Nonnull
<A, T> List<A> findIds(Query<T> query, Transaction transaction);
/**
@@ -807,6 +827,7 @@ public interface EbeanServer {
* @see Query#findEach(Consumer)
* @see Query#findEachWhile(Predicate)
*/
@Nonnull
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
/**
@@ -843,7 +864,7 @@ public interface EbeanServer {
* Execute the query visiting the each bean one at a time.
* <p>
* Compared to findEach() this provides the ability to stop processing the query
* results early by returning false for the QueryEachWhileConsumer.
* results early by returning false for the Predicate.
* </p>
* <p>
* Unlike findList() this is suitable for processing a query that will return
@@ -883,6 +904,7 @@ public interface EbeanServer {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
/**
@@ -908,6 +930,7 @@ public interface EbeanServer {
* @return the list of fetched beans.
* @see Query#findList()
*/
@Nonnull
<T> List<T> findList(Query<T> query, Transaction transaction);
/**
@@ -923,6 +946,7 @@ public interface EbeanServer {
* @return a Future object for the row count query
* @see Query#findFutureCount()
*/
@Nonnull
<T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
/**
@@ -938,6 +962,7 @@ public interface EbeanServer {
* @return a Future object for the list of Id's
* @see Query#findFutureIds()
*/
@Nonnull
<T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
/**
@@ -954,6 +979,7 @@ public interface EbeanServer {
* @return a Future object for the list result of the query
* @see Query#findFutureList()
*/
@Nonnull
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
/**
@@ -985,6 +1011,7 @@ public interface EbeanServer {
* @return The PagedList
* @see Query#findPagedList()
*/
@Nonnull
<T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
/**
@@ -1010,6 +1037,7 @@ public interface EbeanServer {
* @return the set of fetched beans.
* @see Query#findSet()
*/
@Nonnull
<T> Set<T> findSet(Query<T> query, Transaction transaction);
/**
@@ -1026,6 +1054,7 @@ public interface EbeanServer {
* @return the map of fetched beans.
* @see Query#findMap()
*/
@Nonnull
<K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
/**
@@ -1058,6 +1087,7 @@ public interface EbeanServer {
* @return the list of values for the selected property
* @see Query#findSingleAttributeList()
*/
@Nonnull
<A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
/**
@@ -1085,18 +1115,9 @@ public interface EbeanServer {
/**
* Similar to findOne() but returns an Optional (rather than nullable).
*/
@Nonnull
<T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
<T> T findUnique(Query<T> query, Transaction transaction);
/**
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
@@ -1138,6 +1159,7 @@ public interface EbeanServer {
* @return the list of fetched MapBean.
* @see SqlQuery#findList()
*/
@Nonnull
List<SqlRow> findList(SqlQuery query, Transaction transaction);
/**
@@ -1166,7 +1188,7 @@ public interface EbeanServer {
* result.
* </p>
* <p>
* Generally you are able to use {@link SqlQuery#findUnique()} rather than
* Generally you are able to use {@link SqlQuery#findOne()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
@@ -1174,21 +1196,11 @@ public interface EbeanServer {
* @param query the query to execute.
* @param transaction the transaction to use (can be null).
* @return the fetched MapBean or null if none was found.
* @see SqlQuery#findUnique()
* @see SqlQuery#findOne()
*/
@Nullable
SqlRow findOne(SqlQuery query, Transaction transaction);
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
SqlRow findUnique(SqlQuery query, Transaction transaction);
/**
* Either Insert or Update the bean depending on its state.
* <p>
@@ -1462,6 +1474,61 @@ public interface EbeanServer {
*/
int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* This method checks the uniqueness of a bean. I.e. if the save will work. It will return the
* properties that violates an unique / primary key. This may be done in an UI save action to
* validate if the user has entered correct values.
* <p>
* Note: This method queries the DB for uniqueness of all indices, so do not use it in a batch update.
* <p>
* Note: This checks only the root bean!
* <p>
* <pre>{@code
*
* // there is a unique constraint on title
*
* Document doc = new Document();
* doc.setTitle("One flew over the cuckoo's nest");
* doc.setBody("clashes with doc1");
*
* Set<Property> properties = server().checkUniqueness(doc);
*
* if (properties.isEmpty()) {
* // it is unique ... carry on
*
* } else {
* // build a user friendly message
* // to return message back to user
*
* String uniqueProperties = properties.toString();
*
* StringBuilder msg = new StringBuilder();
*
* properties.forEach((it)-> {
* Object propertyValue = it.getVal(doc);
* String propertyName = it.getName();
* msg.append(" property["+propertyName+"] value["+propertyValue+"]");
* });
*
* // uniqueProperties > [title]
* // custom msg > property[title] value[One flew over the cuckoo's nest]
*
* }
*
* }</pre>
*
* @param bean The entity bean to check uniqueness on
* @return a set of Properties if constraint validation was detected or empty list.
*/
@Nonnull
Set<Property> checkUniqueness(Object bean);
/**
* Same as {@link #checkUniqueness(Object)}. but with given transaction.
*/
@Nonnull
Set<Property> checkUniqueness(Object bean, Transaction transaction);
/**
* Marks the entity bean as dirty.
* <p>
@@ -1548,6 +1615,22 @@ public interface EbeanServer {
*/
void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Merge the bean using the given merge options.
*
* @param bean The bean to merge
* @param options The options to control the merge
*/
void merge(Object bean, MergeOptions options);
/**
* Merge the bean using the given merge options and a transaction.
*
* @param bean The bean to merge
* @param options The options to control the merge
*/
void merge(Object bean, MergeOptions options, Transaction transaction);
/**
* Insert the bean.
* <p>
@@ -1585,7 +1668,7 @@ public interface EbeanServer {
int execute(CallableSql callableSql, Transaction transaction);
/**
* Execute a TxRunnable in a Transaction with an explicit scope.
* Execute a Runnable in a Transaction with an explicit scope.
* <p>
* The scope can control the transaction type, isolation and rollback
* semantics.
@@ -1596,7 +1679,7 @@ public interface EbeanServer {
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* ebeanServer.execute(scope, new TxRunnable() {
* ebeanServer.execute(scope, new Runnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* ...
@@ -1605,10 +1688,10 @@ public interface EbeanServer {
*
* }</pre>
*/
void execute(TxScope scope, TxRunnable runnable);
void execute(TxScope scope, Runnable runnable);
/**
* Execute a TxRunnable in a Transaction with the default scope.
* Execute a Runnable in a Transaction with the default scope.
* <p>
* The default scope runs with REQUIRED and by default will rollback on any
* exception (checked or runtime).
@@ -1616,8 +1699,8 @@ public interface EbeanServer {
* <p>
* <pre>{@code
*
* ebeanServer.execute(new TxRunnable() {
* public void run() {
* ebeanServer.execute(() -> {
*
* User u1 = ebeanServer.find(User.class, 1);
* User u2 = ebeanServer.find(User.class, 2);
*
@@ -1626,12 +1709,12 @@ public interface EbeanServer {
*
* ebeanServer.save(u1);
* ebeanServer.save(u2);
* }
*
* });
*
* }</pre>
*/
void execute(TxRunnable runnable);
void execute(Runnable runnable);
/**
* Execute a TxCallable in a Transaction with an explicit scope.
@@ -1645,7 +1728,7 @@ public interface EbeanServer {
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* ebeanServer.execute(scope, new TxCallable<String>() {
* ebeanServer.executeCall(scope, new Callable<String>() {
* public String call() {
* User u1 = ebeanServer.find(User.class, 1);
* ...
@@ -1655,7 +1738,7 @@ public interface EbeanServer {
*
* }</pre>
*/
<T> T execute(TxScope scope, TxCallable<T> callable);
<T> T executeCall(TxScope scope, Callable<T> callable);
/**
* Execute a TxCallable in a Transaction with the default scope.
@@ -1664,13 +1747,9 @@ public interface EbeanServer {
* exception (checked or runtime).
* </p>
* <p>
* This is basically the same as TxRunnable except that it returns an Object
* (and you specify the return type via generics).
* </p>
* <p>
* <pre>{@code
*
* ebeanServer.execute(new TxCallable<String>() {
* ebeanServer.executeCall(new Callable<String>() {
* public String call() {
* User u1 = ebeanServer.find(User.class, 1);
* User u2 = ebeanServer.find(User.class, 2);
@@ -1687,7 +1766,7 @@ public interface EbeanServer {
*
* }</pre>
*/
<T> T execute(TxCallable<T> callable);
<T> T executeCall(Callable<T> callable);
/**
* Return the manager of the server cache ("L2" cache).
+14 -17
View File
@@ -1,13 +1,14 @@
package io.ebean;
import io.ebean.common.SpiContainer;
import io.ebean.config.ContainerConfig;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.server.lib.ShutdownManager;
import io.ebean.service.SpiContainer;
import io.ebean.service.SpiContainerFactory;
import javax.persistence.PersistenceException;
import java.lang.reflect.Constructor;
import java.util.Iterator;
import java.util.Properties;
import java.util.ServiceLoader;
/**
* Creates EbeanServer instances.
@@ -30,10 +31,12 @@ import java.util.Properties;
public class EbeanServerFactory {
private static final String DEFAULT_CONTAINER = "io.ebeaninternal.server.core.DefaultContainer";
private static SpiContainer container;
static {
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
}
/**
* Initialise the container with clustering configuration.
* <p>
@@ -66,10 +69,8 @@ public class EbeanServerFactory {
EbeanServer server = createInternal(config);
if (config.isDefaultServer()) {
PrimaryServer.setSkip(true);
}
if (config.isRegister()) {
PrimaryServer.setSkip(true);
Ebean.register(server, config.isDefaultServer());
}
@@ -99,7 +100,7 @@ public class EbeanServerFactory {
* </p>
*/
public static synchronized void shutdown() {
ShutdownManager.shutdown();
container.shutdown();
}
@@ -135,14 +136,10 @@ public class EbeanServerFactory {
*/
protected static SpiContainer createContainer(ContainerConfig containerConfig) {
String implClassName = System.getProperty("ebean.container", DEFAULT_CONTAINER);
try {
Class<?> cls = Class.forName(implClassName);
Constructor<?> constructor = cls.getConstructor(ContainerConfig.class);
return (SpiContainer) constructor.newInstance(containerConfig);
} catch (Exception ex) {
throw new RuntimeException(ex);
Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator();
if (factories.hasNext()) {
return factories.next().create(containerConfig);
}
throw new IllegalStateException("Service loader didn't find a SpiContainerFactory?");
}
}
+43
View File
@@ -0,0 +1,43 @@
package io.ebean;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to determine the ebean version. (
* @author Roland Praml, FOCONIS AG
*
*/
public class EbeanVersion {
private EbeanVersion() {
}
private static final Logger logger = LoggerFactory.getLogger(EbeanVersion.class);
private static String version = "unknown";
static {
try {
Properties prop = new Properties();
InputStream in = Ebean.class.getResourceAsStream("/META-INF/maven/io.ebean/ebean/pom.properties");
if (in != null) {
prop.load(in);
in.close();
version = prop.getProperty("version");
}
logger.info("ebean version: {}", version);
} catch (IOException e) {
logger.warn("Could not determine ebean version: {}", e.getMessage());
}
}
/**
* Returns the ebean version (read from /META-INF/maven/io.ebean/ebean/pom.properties)
*/
public static String getVersion() {
return version;
}
}
@@ -246,6 +246,11 @@ public interface ExpressionFactory {
*/
Expression icontains(String propertyName, String value);
/**
* In expression using pairs of value objects.
*/
Expression inPairs(Pairs pairs);
/**
* In - property has a value in the array of values.
*/
+126 -15
View File
@@ -5,8 +5,9 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import java.sql.Timestamp;
import java.util.Collection;
@@ -192,6 +193,7 @@ public interface ExpressionList<T> {
*
* @see Query#findList()
*/
@Nonnull
List<T> findList();
/**
@@ -199,6 +201,7 @@ public interface ExpressionList<T> {
*
* @see Query#findIds()
*/
@Nonnull
<A> List<A> findIds();
/**
@@ -214,6 +217,7 @@ public interface ExpressionList<T> {
*
* @see Query#findSet()
*/
@Nonnull
Set<T> findSet();
/**
@@ -221,6 +225,7 @@ public interface ExpressionList<T> {
*
* @see Query#findMap()
*/
@Nonnull
<K> Map<K, T> findMap();
/**
@@ -253,8 +258,27 @@ public interface ExpressionList<T> {
*
* @return the list of values for the selected property
*/
@Nonnull
<A> List<A> findSingleAttributeList();
/**
* Execute a query returning a single value of a single property/column.
* <p>
* <pre>{@code
*
* String name =
* Ebean.find(Customer.class)
* .select("name")
* .where().eq("id", 42)
* .findSingleAttribute();
*
* }</pre>
*/
default <A> A findSingleAttribute() {
List<A> list = findSingleAttributeList();
return !list.isEmpty() ? list.get(0) : null;
}
/**
* Execute the query returning a single bean or null (if no matching
* bean is found).
@@ -264,7 +288,7 @@ public interface ExpressionList<T> {
* </p>
*
* @throws NonUniqueResultException if more than one result was found
* @see Query#findUnique()
* @see Query#findOne()
*/
@Nullable
T findOne();
@@ -272,18 +296,9 @@ public interface ExpressionList<T> {
/**
* Execute the query returning an optional bean.
*/
@Nonnull
Optional<T> findOneOrEmpty();
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
T findUnique();
/**
* Execute find row count query in a background thread.
* <p>
@@ -294,6 +309,7 @@ public interface ExpressionList<T> {
*
* @return a Future object for the row count query
*/
@Nonnull
FutureRowCount<T> findFutureCount();
/**
@@ -306,6 +322,7 @@ public interface ExpressionList<T> {
*
* @return a Future object for the list of Id's
*/
@Nonnull
FutureIds<T> findFutureIds();
/**
@@ -318,6 +335,7 @@ public interface ExpressionList<T> {
*
* @return a Future object for the list result of the query
*/
@Nonnull
FutureList<T> findFutureList();
/**
@@ -349,6 +367,7 @@ public interface ExpressionList<T> {
* @return The PagedList
* @see Query#findPagedList()
*/
@Nonnull
PagedList<T> findPagedList();
/**
@@ -358,6 +377,7 @@ public interface ExpressionList<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersions();
/**
@@ -367,11 +387,13 @@ public interface ExpressionList<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
/**
* Add some filter predicate expressions to the many property.
*/
@Nonnull
ExpressionList<T> filterMany(String prop);
/**
@@ -435,18 +457,67 @@ public interface ExpressionList<T> {
Query<T> setMapKey(String mapKey);
/**
* Set to true to use the query for executing this query.
* Set to true when this query should use the bean cache.
* <p>
* This is now the same as setUseBeanCache(CacheMode.ON) and will be deprecated.
* </p>
*
* @see Query#setUseCache(boolean)
*/
Query<T> setUseCache(boolean useCache);
/**
* Set to true to use the query for executing this query.
* Set the mode to use the bean cache when executing this query.
*
* @see Query#setBeanCacheMode(CacheMode)
*/
Query<T> setBeanCacheMode(CacheMode beanCacheMode);
/**
* Set the {@link CacheMode} to use the query cache for executing this query.
*
* @see Query#setUseQueryCache(boolean)
*/
Query<T> setUseQueryCache(boolean useCache);
Query<T> setUseQueryCache(CacheMode useCache);
/**
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
*
* <pre>{@code
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* Ebean.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
*
* // fetch as single attribute with a COUNT
* .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
* .findSingleAttributeList();
*
* for (CountedValue<Order.Status> entry : orderStatusCount) {
* System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
* }
*
* // produces
*
* count:3 orderStatus:NEW
* count:1 orderStatus:SHIPPED
* count:1 orderStatus:COMPLETE
*
* }</pre>
*/
Query<T> setCountDistinct(CountDistinctOrder orderBy);
/**
* Calls {@link #setUseQueryCache(CacheMode)} with <code>ON</code> or <code>OFF</code>.
*
* @see Query#setUseQueryCache(CacheMode)
*/
default Query<T> setUseQueryCache(boolean enabled) {
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
}
/**
* Set to true if this query should execute against the doc store.
@@ -474,6 +545,11 @@ public interface ExpressionList<T> {
*/
Query<T> setDisableReadAuditing();
/**
* Set a label on the query (to help identify query execution statistics).
*/
Query<T> setLabel(String label);
/**
* Add expressions to the having clause.
* <p>
@@ -780,6 +856,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> icontains(String propertyName, String value);
/**
* In expression using pairs of value objects.
*/
ExpressionList<T> inPairs(Pairs pairs);
/**
* In - using a subQuery.
*/
@@ -795,6 +876,36 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> in(String propertyName, Collection<?> values);
/**
* In - using a subQuery.
*
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
default ExpressionList<T> isIn(String propertyName, Query<?> subQuery) {
return in(propertyName, subQuery);
}
/**
* In - property has a value in the array of values.
*
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
default ExpressionList<T> isIn(String propertyName, Object... values) {
return in(propertyName, values);
}
/**
* In - property has a value in the collection of values.
*
* This is exactly the same as in() and provided due to "in" being a Kotlin keyword
* (and hence to avoid the slightly ugly escaping when using in() in Kotlin)
*/
default ExpressionList<T> isIn(String propertyName, Collection<?> values) {
return in(propertyName, values);
}
/**
* Not In - property has a value in the array of values.
*/
+42 -2
View File
@@ -1,6 +1,7 @@
package io.ebean;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
@@ -27,7 +28,7 @@ import java.util.List;
* // Add your customer finder methods ...
*
* public Customer byName(String name) {
* return query().eq("name", name).findUnique();
* return query().eq("name", name).findOne();
* }
*
* public List<Customer> findNew() {
@@ -92,6 +93,20 @@ public class Finder<I, T> {
this.serverName = serverName;
}
/**
* Return the current transaction.
*/
public Transaction currentTransaction() {
return db().currentTransaction();
}
/**
* Flush the JDBC batch on the current transaction.
*/
public void flush() {
db().flush();
}
/**
* Return the underlying 'default' EbeanServer.
* <p>
@@ -118,6 +133,7 @@ public class Finder<I, T> {
* <p>
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
*/
@Nonnull
public T ref(I id) {
return db().getReference(type, id);
}
@@ -144,10 +160,33 @@ public class Finder<I, T> {
/**
* Retrieves all entities of the given type.
*/
@Nonnull
public List<T> all() {
return query().findList();
}
/**
* Creates an update query.
*
* <pre>{@code
*
* int rows =
* finder.update()
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .gt("id", 1000)
* .update();
*
* }</pre>
*
* <p>
* Equivalent to {@link EbeanServer#update(Class)}
*/
public UpdateQuery<T> update() {
return db().update(type);
}
/**
* Creates a query.
* <p>
@@ -170,4 +209,5 @@ public class Finder<I, T> {
public Query<T> query(String ormQuery) {
return db().createQuery(type, ormQuery);
}
}
+29
View File
@@ -0,0 +1,29 @@
package io.ebean;
import java.util.Set;
/**
* Options used to control a merge. Use MergeOptionsBuilder to create an instance.
* <p>
* Instances of MergeOptions are thread safe and safe to share across threads.
*/
public interface MergeOptions {
/**
* Returns true if Id values are supplied by the client.
* <p>
* This would be the case when for example a mobile creates data in it's own local database
* and then sync's. In this case often the id values are UUID.
*/
boolean isClientGeneratedIds();
/**
* Return true if delete permanent should be used and false for 'normal' delete that allows soft deletes.
*/
boolean isDeletePermanent();
/**
* Return the paths included in the merge.
*/
Set<String> paths();
}
@@ -0,0 +1,80 @@
package io.ebean;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Builds a MergeOptions which is immutable and thread safe.
*/
public class MergeOptionsBuilder {
private Set<String> paths = new LinkedHashSet<>();
private boolean clientGeneratedIds;
private boolean deletePermanent = true;
/**
* Add a path that will included in the merge.
*
* @param path The path relative to the root type.
* @return The builder to chain another addPath() or build().
*/
public MergeOptionsBuilder addPath(String path) {
paths.add(path);
return this;
}
/**
* Set to true if Id values are supplied by the client.
* <p>
* This would be the case when for example a mobile creates data in it's own local database
* and then sync's. In this case often the id values are UUID.
*/
public MergeOptionsBuilder setClientGeneratedIds() {
this.clientGeneratedIds = true;
return this;
}
/**
* Set that deletions should use delete permanent (rather than default which allows soft deletes).
*/
public MergeOptionsBuilder setDeletePermanent() {
this.deletePermanent = true;
return this;
}
/**
* Build and return the MergeOptions instance.
*/
public MOptions build() {
return new MOptions(paths, clientGeneratedIds, deletePermanent);
}
private static class MOptions implements MergeOptions {
private final boolean clientGeneratedIds;
private final boolean deletePermanent;
private final Set<String> paths;
private MOptions(Set<String> paths, boolean clientGeneratedIds, boolean deletePermanent) {
this.paths = paths;
this.clientGeneratedIds = clientGeneratedIds;
this.deletePermanent = deletePermanent;
}
public Set<String> paths() {
return paths;
}
@Override
public boolean isClientGeneratedIds() {
return clientGeneratedIds;
}
@Override
public boolean isDeletePermanent() {
return deletePermanent;
}
}
}
+3
View File
@@ -1,5 +1,6 @@
package io.ebean;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.Future;
@@ -109,11 +110,13 @@ public interface PagedList<T> {
*
* }</pre>
*/
@Nonnull
Future<Integer> getFutureCount();
/**
* Return the list of entities for this page.
*/
@Nonnull
List<T> getList();
/**
+222
View File
@@ -0,0 +1,222 @@
package io.ebean;
import java.util.ArrayList;
import java.util.List;
/**
* Holds a list of value object pairs.
* <p>
* This feature is to enable use of L2 cache with complex natural keys with findList() queries in cases where the
* IN clause is not a single property but instead a pair of properties.
* </p>
* <p>
* These queries can have predicates that can be translated into a list of complex natural keys such that the L2
* cache can be hit with these keys to obtain some or all of the beans from L2 cache rather than the DB.
* </p>
* <pre>{@code
*
* // where a bean is annotated with a complex
* // natural key made of several properties
* @Cache(naturalKey = {"store","code","sku"})
*
*
* Pairs pairs = new Pairs("sku", "code");
* pairs.add("sj2", 1000);
* pairs.add("sj2", 1001);
* pairs.add("pf3", 1000);
*
* List<OCachedNatKeyBean3> list = Ebean.find(OCachedNatKeyBean3.class)
* .where()
* .eq("store", "def")
* .inPairs(pairs) // IN clause with 'pairs' of values
* .orderBy("sku desc")
*
* // query expressions cover the natural key properties
* // so we can choose to hit the L2 bean cache if we want
* .setUseCache(true)
* .findList();
*
* }</pre>
* <h3>Important implementation Note</h3>
* <p>
* When binding many pairs of values we want to be able to utilise a DB index (as this type of query usually means the
* pairs are a unique key/index or part of a unique key/index and highly selective). Currently we know we can do this
* on any DB that supports expression/formula based indexes.
* using a DB string concatenation formula
* </p>
* <p>
* This means, the implementation converts the list of pairs into a list of strings via concatenation and we use a
* DB concatenation formula to match. We see SQL like:
* </p>
* <pre>{@code sql
*
* ...
* where t0.store = ? and (t0.sku||'-'||t0.code) in (?, ? )
*
* // bind values like: "sj2-1000", "pf3-1000"
*
* }</pre>
* <p>
* We often create a DB expression index to match the DB concat formula like:
* </p>
* <pre>{@code sql
*
* create index ix_name on table_name ((sku || '-' || code));
*
* }</pre>
*/
public class Pairs {
private final String property0;
private final String property1;
private final List<Entry> entries = new ArrayList<>();
/**
* Character between the values when combined via DB varchar concatenation.
*/
private String concatSeparator = "-";
/**
* Optional suffix added to DB varchar concatenation formula.
*/
private String concatSuffix;
/**
* Create with 2 property names.
*
* @param property0 The property of the first value
* @param property1 The property of the second value
*/
public Pairs(String property0, String property1) {
this.property0 = property0;
this.property1 = property1;
}
/**
* Add a pair of value objects.
* <p>
* Both values are expected to be immutable with equals and hashCode implementations.
* </p>
*
* @param a Value of the first property
* @param b Value of the second property
*/
public Pairs add(Object a, Object b) {
entries.add(new Entry(a, b));
return this;
}
/**
* Return the first property name.
*/
public String getProperty0() {
return property0;
}
/**
* Return the second property name.
*/
public String getProperty1() {
return property1;
}
/**
* Return all the value pairs.
*/
public List<Entry> getEntries() {
return entries;
}
/**
* Return the separator character used with DB varchar concatenation to combine the 2 values.
*/
public String getConcatSeparator() {
return concatSeparator;
}
/**
* Set the separator character used with DB varchar concatenation to combine the 2 values.
*/
public Pairs setConcatSeparator(String concatSeparator) {
this.concatSeparator = concatSeparator;
return this;
}
/**
* Return a suffix used with DB varchar concatenation to combine the 2 values.
*/
public String getConcatSuffix() {
return concatSuffix;
}
/**
* Add a suffix used with DB varchar concatenation to combine the 2 values.
*/
public Pairs setConcatSuffix(String concatSuffix) {
this.concatSuffix = concatSuffix;
return this;
}
@Override
public String toString() {
return "p0:" + property0 + " p1:" + property1 + " entries:" + entries;
}
/**
* A pair of 2 value objects.
* <p>
* Used to support inPairs() expression.
*/
public static class Entry {
private final Object a;
private final Object b;
/**
* Create with values for property0 and property1 respectively.
*
* @param a Value of the first property
* @param b Value of the second property
*/
public Entry(Object a, Object b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return "{" + a + "," + b + "}";
}
/**
* Return the value for the first property.
*/
public Object getA() {
return a;
}
/**
* Return the value for the second property.
*/
public Object getB() {
return b;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Entry that = (Entry) o;
return a.equals(that.a) && b.equals(that.b);
}
@Override
public int hashCode() {
int result = a.hashCode();
result = 92821 * result + b.hashCode();
return result;
}
}
}
+5 -2
View File
@@ -1,6 +1,6 @@
package io.ebean;
import io.ebean.config.PropertyMap;
import io.ebean.config.properties.PropertiesLoader;
import java.util.Properties;
@@ -44,7 +44,7 @@ class PrimaryServer {
*/
static synchronized Properties getProperties() {
if (globalProperties == null) {
globalProperties = PropertyMap.defaultProperties();
globalProperties = PropertiesLoader.load();
}
if (defaultServerName == null) {
defaultServerName = determineDefaultServerName();
@@ -59,6 +59,8 @@ class PrimaryServer {
private static String determineDefaultServerName() {
String defaultServerName = System.getenv("EBEAN_DB");
defaultServerName = System.getProperty("db", defaultServerName);
defaultServerName = System.getProperty("ebean_db", defaultServerName);
if (isEmpty(defaultServerName)) {
defaultServerName = System.getProperty("datasource.default");
if (isEmpty(defaultServerName)) {
@@ -74,6 +76,7 @@ class PrimaryServer {
if (defaultServerName == null) {
defaultServerName = "db";
}
System.setProperty("ebean_db", defaultServerName);
return defaultServerName;
}
@@ -0,0 +1,47 @@
package io.ebean;
/**
* A location for profiling transactions and queries.
* <p>
* Typically represents a class method in the form of class file and line of code that started
* the transaction or invoked the query.
* </p>
*/
public interface ProfileLocation {
/**
* Create and return a new ProfileLocation.
*/
static ProfileLocation create() {
return XServiceProvider.profileLocationFactory().create();
}
/**
* Create and return a new ProfileLocation with a given lineNumber and label.
*/
static ProfileLocation create(int lineNumber, String label) {
return XServiceProvider.profileLocationFactory().create(lineNumber, label);
}
/**
* Create and return a new ProfileLocation with a given location.
*/
static ProfileLocation createAt(String location) {
return XServiceProvider.profileLocationFactory().createAt(location);
}
/**
* Obtain the location description.
*/
String obtain();
/**
* Return a short version of the location description.
*/
String shortDescription();
/**
* Add execution time.
*/
void add(long executionTime);
}
+153 -34
View File
@@ -1,7 +1,7 @@
package io.ebean;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import java.sql.Timestamp;
import java.util.List;
@@ -215,11 +215,6 @@ public interface Query<T> {
SKIPLOCKED
}
/**
* Return the RawSql that was set to use for this query.
*/
RawSql getRawSql();
/**
* Set RawSql to use for this query.
*/
@@ -241,6 +236,14 @@ public interface Query<T> {
*/
Query<T> asDraft();
/**
* Convert the query to a DTO bean query.
* <p>
* We effectively use the underlying ORM query to build the SQL and then execute
* and map it into DTO beans.
*/
<D> DtoQuery<D> asDto(Class<D> dtoClass);
/**
* Cancel the query execution if supported by the underlying database and
* driver.
@@ -602,6 +605,7 @@ public interface Query<T> {
*
* @see EbeanServer#findIds(Query, Transaction)
*/
@Nonnull
<A> List<A> findIds();
/**
@@ -642,6 +646,7 @@ public interface Query<T> {
*
* }</pre>
*/
@Nonnull
QueryIterator<T> findIterate();
/**
@@ -669,8 +674,7 @@ public interface Query<T> {
* </p>
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the QueryEachConsumer (SAM) interface which is better suited to use
* with Java8 closures.
* iterator uses the Consumer interface which is better suited to use with Java8 closures.
* </p>
* <pre>{@code
*
@@ -699,8 +703,7 @@ public interface Query<T> {
* </p>
* <p>
* This method is functionally equivalent to findIterate() but instead of using an
* iterator uses the QueryEachWhileConsumer (SAM) interface which is better suited to use
* with Java8 closures.
* iterator uses the Predicate (SAM) interface which is better suited to use with Java8 closures.
* </p>
* <pre>{@code
*
@@ -740,6 +743,7 @@ public interface Query<T> {
*
* @see EbeanServer#findList(Query, Transaction)
*/
@Nonnull
List<T> findList();
/**
@@ -758,6 +762,7 @@ public interface Query<T> {
*
* @see EbeanServer#findSet(Query, Transaction)
*/
@Nonnull
Set<T> findSet();
/**
@@ -780,6 +785,7 @@ public interface Query<T> {
*
* @see EbeanServer#findMap(Query, Transaction)
*/
@Nonnull
<K> Map<K, T> findMap();
/**
@@ -812,8 +818,29 @@ public interface Query<T> {
*
* @return the list of values for the selected property
*/
@Nonnull
<A> List<A> findSingleAttributeList();
/**
* Execute a query returning a single value of a single property/column.
* <p>
* <pre>{@code
*
* String name =
* Ebean.find(Customer.class)
* .select("name")
* .where().eq("id", 42)
* .findSingleAttribute();
*
* }</pre>
*/
<A> A findSingleAttribute();
/**
* Return true if this is countDistinct query.
*/
boolean isCountDistinct();
/**
* Execute the query returning either a single bean or null (if no matching
* bean is found).
@@ -831,7 +858,7 @@ public interface Query<T> {
* Product product =
* ebeanServer.find(Product.class)
* .where().eq("sku", "aa113")
* .findUnique();
* .findOne();
* ...
* }</pre>
* <p>
@@ -845,7 +872,7 @@ public interface Query<T> {
* ebeanServer.find(Order.class)
* .setId(1)
* .fetch("details")
* .findUnique();
* .findOne();
*
* // the order details were eagerly loaded
* List<OrderDetail> details = order.getDetails();
@@ -860,18 +887,9 @@ public interface Query<T> {
/**
* Execute the query returning an optional bean.
*/
@Nonnull
Optional<T> findOneOrEmpty();
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
T findUnique();
/**
* Return versions of a @History entity bean.
* <p>
@@ -884,6 +902,7 @@ public interface Query<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersions();
/**
@@ -893,6 +912,7 @@ public interface Query<T> {
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
@Nonnull
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
/**
@@ -930,6 +950,7 @@ public interface Query<T> {
*
* @return a Future object for the row count query
*/
@Nonnull
FutureRowCount<T> findFutureCount();
/**
@@ -942,6 +963,7 @@ public interface Query<T> {
*
* @return a Future object for the list of Id's
*/
@Nonnull
FutureIds<T> findFutureIds();
/**
@@ -953,6 +975,7 @@ public interface Query<T> {
*
* @return a Future object for the list result of the query
*/
@Nonnull
FutureList<T> findFutureList();
/**
@@ -982,6 +1005,7 @@ public interface Query<T> {
*
* @return The PagedList
*/
@Nonnull
PagedList<T> findPagedList();
/**
@@ -1028,7 +1052,7 @@ public interface Query<T> {
Query<T> setParameter(int position, Object value);
/**
* Set the Id value to query. This is used with findUnique().
* Set the Id value to query. This is used with findOne().
* <p>
* You can use this to have further control over the query. For example adding
* fetch joins.
@@ -1039,7 +1063,7 @@ public interface Query<T> {
* ebeanServer.find(Order.class)
* .setId(1)
* .fetch("details")
* .findUnique();
* .findOne();
*
* // the order details were eagerly fetched
* List<OrderDetail> details = order.getDetails();
@@ -1261,6 +1285,37 @@ public interface Query<T> {
*/
Query<T> setDistinct(boolean isDistinct);
/**
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
*
* <pre>{@code
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* Ebean.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
*
* // fetch as single attribute with a COUNT
* .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
* .findSingleAttributeList();
*
* for (CountedValue<Order.Status> entry : orderStatusCount) {
* System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
* }
*
* // produces
*
* count:3 orderStatus:NEW
* count:1 orderStatus:SHIPPED
* count:1 orderStatus:COMPLETE
*
* }</pre>
*
*/
Query<T> setCountDistinct(CountDistinctOrder orderBy);
/**
* Return the first row value.
*/
@@ -1294,7 +1349,7 @@ public interface Query<T> {
*
* // Assuming sku is unique for products...
*
* Map<?,Product> productMap =
* Map<String,Product> productMap =
* ebeanServer.find(Product.class)
* // use sku for keys...
* .setMapKey("sku")
@@ -1309,22 +1364,69 @@ public interface Query<T> {
/**
* Set this to false to not use the bean cache.
* <p>
* This method is now superseded by {@link #setBeanCacheMode(CacheMode)}
* which provides more explicit options controlled bean cache use.
* </p>
* <p>
* This method is likely to be deprecated in the future with migration
* over to setUseBeanCache().
* </p>
*/
default Query<T> setUseCache(boolean useCache) {
return setBeanCacheMode(useCache ? CacheMode.ON : CacheMode.OFF);
}
/**
* Set the mode to use the bean cache when executing this query.
* <p>
* By default "find by id" and "find by natural key" will use the bean cache
* when bean caching is enabled. Setting this to false means that the query
* will not use the bean cache and instead hit the database.
* </p>
* <p>
* In the case of other queries (findList(), findEach() etc) then setting this to
* false beans that the if lazy loading is invoked that lazy loading will not try
* to use the bean cache.
* By default findList() with natural keys will not use the bean cache. In that
* case we need to explicitly use the bean cache.
* </p>
*/
Query<T> setUseCache(boolean useCache);
Query<T> setBeanCacheMode(CacheMode beanCacheMode);
/**
* Set this to true to use the query cache.
* Set the {@link CacheMode} to use the query for executing this query.
*/
Query<T> setUseQueryCache(boolean useQueryCache);
Query<T> setUseQueryCache(CacheMode queryCacheMode);
/**
* Calls {@link #setUseQueryCache(CacheMode)} with <code>ON</code> or <code>OFF</code>.
*/
default Query<T> setUseQueryCache(boolean enabled) {
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
}
/**
* Set an id to identify this query for profiling purposes.
* <p>
* The profileId is expected to be unique for a given bean type.
* </p>
* <p>
* Note that the profileId is treated as a short internally and has a MAX value of 32,767.
* </p>
*/
Query<T> setProfileId(int profileId);
/**
* Set the profile location of this query. This is used to relate query execution metrics
* back to a location like a specific line of code.
*/
Query<T> setProfileLocation(ProfileLocation profileLocation);
/**
* Set a label on the query.
* <p>
* This label can be used to help identify query performance metrics but we can also use
* profile location enhancement on Finders so for some that would be a better option.
* </p>
*/
Query<T> setLabel(String label);
/**
* Set to true if this query should execute against the doc store.
@@ -1340,8 +1442,9 @@ public interface Query<T> {
Query<T> setReadOnly(boolean readOnly);
/**
* When set to true all the beans from this query are loaded into the bean
* cache.
* Will be deprecated - migrate to use setBeanCacheMode(CacheMode.RECACHE).
* <p>
* When set to true all the beans from this query are loaded into the bean cache.
*/
Query<T> setLoadBeanCache(boolean loadBeanCache);
@@ -1386,9 +1489,12 @@ 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);
/**
@@ -1432,6 +1538,11 @@ public interface Query<T> {
*/
Class<T> getBeanType();
/**
* Return the type of query being executed.
*/
QueryType getQueryType();
/**
* Set true if you want to disable lazy loading.
* <p>
@@ -1449,4 +1560,12 @@ public interface Query<T> {
*/
Set<String> validate();
/**
* Controls, if paginated queries should always append an 'order by id' statement at the end to
* guarantee a deterministic sort result. This may affect performance.
* If this is not enabled, and an orderBy is set on the query, it's up to the programmer that
* this query provides a deterministic result.
*/
Query<T> orderById(boolean orderById);
}
+22
View File
@@ -0,0 +1,22 @@
package io.ebean;
/**
* The type of the query being executed.
*/
public enum QueryType {
/**
* A find query.
*/
FIND,
/**
* An update query.
*/
UPDATE,
/**
* A delete query.
*/
DELETE
}
+1 -572
View File
@@ -1,16 +1,5 @@
package io.ebean;
import io.ebean.util.CamelCaseHelper;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Used to build object graphs based on a raw SQL statement (rather than
* generated by Ebean).
@@ -169,566 +158,6 @@ import java.util.Map;
* Note that lazy loading also works with object graphs built with RawSql.
* </p>
*/
public final class RawSql implements Serializable {
public interface RawSql {
private static final long serialVersionUID = 1L;
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
/**
* Construct with a ResultSet and properties that the columns map to.
* <p>
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
* <p>
* When a query executes this RawSql object then it will close the resultSet.
*/
public RawSql(ResultSet resultSet, String... propertyNames) {
this.resultSet = resultSet;
this.sql = null;
this.columnMapping = new ColumnMapping(propertyNames);
}
protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) {
this.resultSet = resultSet;
this.sql = sql;
this.columnMapping = columnMapping;
}
/**
* Return the Sql either unparsed or in parsed (broken up) form.
*/
public Sql getSql() {
return sql;
}
/**
* Return the key;
*/
public Key getKey() {
boolean parsed = sql != null && sql.parsed;
String unParsedSql = (sql == null) ? "" : sql.unparsedSql;
return new Key(parsed, unParsedSql, columnMapping);
}
/**
* Return the resultSet if this is a ResultSet based RawSql.
*/
public ResultSet getResultSet() {
return resultSet;
}
/**
* Return the column mapping for the SQL columns to bean properties.
*/
public ColumnMapping getColumnMapping() {
return columnMapping;
}
/**
* Represents the sql part of the query. For parsed RawSql the sql is broken
* up so that Ebean can insert extra WHERE and HAVING expressions into the
* SQL.
*/
public static final class Sql implements Serializable {
private static final long serialVersionUID = 1L;
private final boolean parsed;
private final String unparsedSql;
private final String preFrom;
private final String preWhere;
private final boolean andWhereExpr;
private final String preHaving;
private final boolean andHavingExpr;
private final String orderByPrefix;
private final String orderBy;
private final boolean distinct;
/**
* Construct for unparsed SQL.
*/
protected Sql(String unparsedSql) {
this.parsed = false;
this.unparsedSql = unparsedSql;
this.preFrom = null;
this.preHaving = null;
this.preWhere = null;
this.andHavingExpr = false;
this.andWhereExpr = false;
this.orderByPrefix = null;
this.orderBy = null;
this.distinct = false;
}
/**
* Construct for parsed SQL.
*/
protected Sql(String unparsedSql, String preFrom, String preWhere, boolean andWhereExpr,
String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) {
this.unparsedSql = unparsedSql;
this.parsed = true;
this.preFrom = preFrom;
this.preHaving = preHaving;
this.preWhere = preWhere;
this.andHavingExpr = andHavingExpr;
this.andWhereExpr = andWhereExpr;
this.orderByPrefix = orderByPrefix;
this.orderBy = orderBy;
this.distinct = distinct;
}
@Override
public String toString() {
if (!parsed) {
return "unparsed[" + unparsedSql + "]";
}
return "select[" + preFrom + "] preWhere[" + preWhere + "] preHaving[" + preHaving + "] orderBy[" + orderBy + "]";
}
public boolean isDistinct() {
return distinct;
}
/**
* Return true if the SQL is left completely unmodified.
* <p>
* This means Ebean can't add WHERE or HAVING expressions into the query -
* it will be left completely unmodified.
* </p>
*/
public boolean isParsed() {
return parsed;
}
/**
* Return the SQL when it is unparsed.
*/
public String getUnparsedSql() {
return unparsedSql;
}
/**
* Return the SQL prior to FROM clause.
*/
public String getPreFrom() {
return preFrom;
}
/**
* Return the SQL prior to WHERE clause.
*/
public String getPreWhere() {
return preWhere;
}
/**
* Return true if there is already a WHERE clause and any extra where
* expressions start with AND.
*/
public boolean isAndWhereExpr() {
return andWhereExpr;
}
/**
* Return the SQL prior to HAVING clause.
*/
public String getPreHaving() {
return preHaving;
}
/**
* Return true if there is already a HAVING clause and any extra having
* expressions start with AND.
*/
public boolean isAndHavingExpr() {
return andHavingExpr;
}
/**
* Return the 'order by' keywords.
* This can contain additional keywords, for example 'order siblings by' as Oracle syntax.
*/
public String getOrderByPrefix() {
return (orderByPrefix == null) ? "order by" : orderByPrefix;
}
/**
* Return the SQL ORDER BY clause.
*/
public String getOrderBy() {
return orderBy;
}
}
/**
* Defines the column mapping for raw sql DB columns to bean properties.
*/
public static final class ColumnMapping implements Serializable {
private static final long serialVersionUID = 1L;
private final LinkedHashMap<String, Column> dbColumnMap;
private final Map<String, String> propertyMap;
private final Map<String, Column> propertyColumnMap;
private final boolean parsed;
private final boolean immutable;
/**
* Construct from parsed sql where the columns have been identified.
*/
protected ColumnMapping(List<Column> columns) {
this.immutable = false;
this.parsed = true;
this.propertyMap = null;
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<>();
for (Column c : columns) {
dbColumnMap.put(c.getDbColumnKey(), c);
}
}
/**
* Construct for unparsed sql.
*/
protected ColumnMapping() {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
this.propertyColumnMap = null;
this.dbColumnMap = new LinkedHashMap<>();
}
/**
* Construct for ResultSet use.
*/
protected ColumnMapping(String... propertyNames) {
this.immutable = false;
this.parsed = false;
this.propertyMap = null;
this.dbColumnMap = new LinkedHashMap<>();
int pos = 0;
for (String prop : propertyNames) {
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
}
propertyColumnMap = dbColumnMap;
}
/**
* Construct an immutable ColumnMapping based on collected information.
*/
protected ColumnMapping(boolean parsed, LinkedHashMap<String, Column> dbColumnMap) {
this.immutable = true;
this.parsed = parsed;
this.dbColumnMap = dbColumnMap;
HashMap<String, Column> pcMap = new HashMap<>();
HashMap<String, String> pMap = new HashMap<>();
for (Column c : dbColumnMap.values()) {
pMap.put(c.getPropertyName(), c.getDbColumn());
pcMap.put(c.getPropertyName(), c);
}
this.propertyMap = Collections.unmodifiableMap(pMap);
this.propertyColumnMap = Collections.unmodifiableMap(pcMap);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ColumnMapping that = (ColumnMapping) o;
return dbColumnMap.equals(that.dbColumnMap);
}
@Override
public int hashCode() {
return dbColumnMap.hashCode();
}
/**
* Return true if the property is mapped.
*/
public boolean contains(String property) {
return this.propertyColumnMap.containsKey(property);
}
/**
* Creates an immutable copy of this ColumnMapping.
*
* @throws IllegalStateException when a propertyName has not been defined for a column.
*/
protected ColumnMapping createImmutableCopy() {
for (Column c : dbColumnMap.values()) {
c.checkMapping();
}
return new ColumnMapping(parsed, dbColumnMap);
}
protected void columnMapping(String dbColumn, String propertyName) {
if (immutable) {
throw new IllegalStateException("Should never happen");
}
if (!parsed) {
int pos = dbColumnMap.size();
dbColumnMap.put(dbColumn, new Column(pos, dbColumn, null, propertyName));
} else {
Column column = dbColumnMap.get(dbColumn);
if (column == null) {
String msg = "DB Column [" + dbColumn + "] not found in mapping. Expecting one of [" + dbColumnMap.keySet() + "]";
throw new IllegalArgumentException(msg);
}
column.setPropertyName(propertyName);
}
}
/**
* Returns true if the Columns where supplied by parsing the sql select
* clause.
* <p>
* In the case where the columns where parsed then we can do extra checks on
* the column mapping such as, is the column a valid one in the sql and
* whether all the columns in the sql have been mapped.
* </p>
*/
public boolean isParsed() {
return parsed;
}
/**
* Return the number of columns in this column mapping.
*/
public int size() {
return dbColumnMap.size();
}
/**
* Return the column mapping.
*/
protected Map<String, Column> mapping() {
return dbColumnMap;
}
/**
* Return the mapping by DB column.
*/
public Map<String, String> getMapping() {
return propertyMap;
}
/**
* Return the index position by bean property name.
*/
public int getIndexPosition(String property) {
Column c = propertyColumnMap.get(property);
return c == null ? -1 : c.getIndexPos();
}
/**
* Return an iterator of the Columns.
*/
public Iterator<Column> getColumns() {
return dbColumnMap.values().iterator();
}
/**
* Modify any column mappings with the given table alias to have the path prefix.
* <p>
* For example modify all mappings with table alias "c" to have the path prefix "customer".
* </p>
* <p>
* For the "Root type" you don't need to specify a tableAliasMapping.
* </p>
*/
public void tableAliasMapping(String tableAlias, String path) {
String startMatch = tableAlias + ".";
for (Map.Entry<String, Column> entry : dbColumnMap.entrySet()) {
if (entry.getKey().startsWith(startMatch)) {
entry.getValue().tableAliasMapping(path);
}
}
}
/**
* A Column of the RawSql that is mapped to a bean property (or ignored).
*/
public static class Column implements Serializable {
private static final long serialVersionUID = 1L;
private final int indexPos;
private final String dbColumn;
private final String dbAlias;
private String propertyName;
/**
* Construct a Column.
*/
public Column(int indexPos, String dbColumn, String dbAlias) {
this(indexPos, dbColumn, dbAlias, derivePropertyName(dbAlias, dbColumn));
}
private Column(int indexPos, String dbColumn, String dbAlias, String propertyName) {
this.indexPos = indexPos;
this.dbColumn = dbColumn;
this.dbAlias = dbAlias;
if (propertyName == null && dbAlias != null) {
this.propertyName = dbAlias;
} else {
this.propertyName = propertyName;
}
}
protected static String derivePropertyName(String dbAlias, String dbColumn) {
if (dbAlias != null) {
return CamelCaseHelper.toCamelFromUnderscore(dbAlias);
}
int dotPos = dbColumn.indexOf('.');
if (dotPos > -1) {
dbColumn = dbColumn.substring(dotPos + 1);
}
return CamelCaseHelper.toCamelFromUnderscore(dbColumn);
}
private void checkMapping() {
if (propertyName == null) {
String msg = "No propertyName defined (Column mapping) for dbColumn [" + dbColumn + "]";
throw new IllegalStateException(msg);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Column that = (Column) o;
if (indexPos != that.indexPos) return false;
if (!dbColumn.equals(that.dbColumn)) return false;
if (dbAlias != null ? !dbAlias.equals(that.dbAlias) : that.dbAlias != null) return false;
return propertyName != null ? propertyName.equals(that.propertyName) : that.propertyName == null;
}
@Override
public int hashCode() {
int result = indexPos;
result = 92821 * result + dbColumn.hashCode();
result = 92821 * result + (dbAlias != null ? dbAlias.hashCode() : 0);
result = 92821 * result + (propertyName != null ? propertyName.hashCode() : 0);
return result;
}
@Override
public String toString() {
return dbColumn + "->" + propertyName;
}
/**
* Return the index position of this column.
*/
public int getIndexPos() {
return indexPos;
}
/**
* Return the DB column alias if specified otherwise DB column.
* This is used as the key for mapping a column to a logical property.
*/
public String getDbColumnKey() {
return (dbAlias != null) ? dbAlias : dbColumn;
}
/**
* Return the DB column name including table alias (if it has one).
*/
public String getDbColumn() {
return dbColumn;
}
/**
* Return the bean property this column is mapped to.
*/
public String getPropertyName() {
return propertyName;
}
/**
* Set the property name mapped to this db column.
*/
private void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
/**
* Prepend the path to the property name.
* <p/>
* For example if path is "customer" then "name" becomes "customer.name".
*/
public void tableAliasMapping(String path) {
if (path != null) {
propertyName = path + "." + propertyName;
}
}
}
}
/**
* A key for the RawSql object using for the query plan.
*/
public static final class Key {
private final boolean parsed;
private final ColumnMapping columnMapping;
private final String unParsedSql;
Key(boolean parsed, String unParsedSql, ColumnMapping columnMapping) {
this.parsed = parsed;
this.unParsedSql = unParsedSql;
this.columnMapping = columnMapping;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key that = (Key) o;
return parsed == that.parsed
&& columnMapping.equals(that.columnMapping)
&& unParsedSql.equals(that.unParsedSql);
}
@Override
public int hashCode() {
int result = (parsed ? 1 : 0);
result = 92821 * result + columnMapping.hashCode();
result = 92821 * result + unParsedSql.hashCode();
return result;
}
}
}
+19 -55
View File
@@ -1,9 +1,7 @@
package io.ebean;
import io.ebean.RawSql.ColumnMapping;
import io.ebean.RawSql.Sql;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Builds RawSql instances from a SQL string and column mappings.
@@ -14,18 +12,7 @@ import java.sql.ResultSet;
*
* @see RawSql
*/
public class RawSqlBuilder {
/**
* Special property name assigned to a DB column that should be ignored.
*/
public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
private final ResultSet resultSet;
private final Sql sql;
private final ColumnMapping columnMapping;
public interface RawSqlBuilder {
/**
* Create and return a RawSql object based on the resultSet and list of properties the columns in
@@ -34,8 +21,15 @@ public class RawSqlBuilder {
* The properties listed in the propertyNames must be in the same order as the columns in the
* resultSet.
*/
public static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
return new RawSql(resultSet, propertyNames);
static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
return XServiceProvider.rawSql().resultSet(resultSet, propertyNames);
}
/**
* Create and return a SqlRow based on the resultSet with dbTrueValue and binaryOptimizedUUID options.
*/
static SqlRow sqlRow(ResultSet resultSet, final String dbTrueValue, boolean binaryOptimizedUUID) throws SQLException {
return XServiceProvider.rawSql().sqlRow(resultSet, dbTrueValue, binaryOptimizedUUID);
}
/**
@@ -43,10 +37,8 @@ public class RawSqlBuilder {
* modified - so no additional WHERE or HAVING expressions can be added to
* this query.
*/
public static RawSqlBuilder unparsed(String sql) {
Sql s = new Sql(sql);
return new RawSqlBuilder(s, new ColumnMapping());
static RawSqlBuilder unparsed(String sql) {
return XServiceProvider.rawSql().unparsed(sql);
}
/**
@@ -62,19 +54,8 @@ public class RawSqlBuilder {
* correct column names are entered into the mapping.
* </p>
*/
public static RawSqlBuilder parse(String sql) {
Sql sql2 = DRawSqlParser.parse(sql);
String select = sql2.getPreFrom();
ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
return new RawSqlBuilder(sql2, mapping);
}
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
this.sql = sql;
this.columnMapping = columnMapping;
this.resultSet = null;
static RawSqlBuilder parse(String sql) {
return XServiceProvider.rawSql().parsed(sql);
}
/**
@@ -87,17 +68,12 @@ public class RawSqlBuilder {
* @param dbColumn the DB column that we are mapping to a bean property
* @param propertyName the bean property that we are mapping the DB column to.
*/
public RawSqlBuilder columnMapping(String dbColumn, String propertyName) {
columnMapping.columnMapping(dbColumn, propertyName);
return this;
}
RawSqlBuilder columnMapping(String dbColumn, String propertyName);
/**
* Ignore this DB column. It is not mapped to any bean property.
*/
public RawSqlBuilder columnMappingIgnore(String dbColumn) {
return columnMapping(dbColumn, IGNORE_COLUMN);
}
RawSqlBuilder columnMappingIgnore(String dbColumn);
/**
* Modify any column mappings with the given table alias to have the path prefix.
@@ -108,24 +84,12 @@ public class RawSqlBuilder {
* For the "Root type" you don't need to specify a tableAliasMapping.
* </p>
*/
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
columnMapping.tableAliasMapping(tableAlias, path);
return this;
}
RawSqlBuilder tableAliasMapping(String tableAlias, String path);
/**
* Create the immutable RawSql object. Do this after all the column mapping
* has been defined.
*/
public RawSql create() {
return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
}
/**
* Return the internal parsed Sql object (for testing).
*/
protected Sql getSql() {
return sql;
}
RawSql create();
}
+10 -5
View File
@@ -1,5 +1,7 @@
package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
@@ -43,6 +45,7 @@ public interface SqlQuery extends Serializable {
/**
* Execute the query returning a list.
*/
@Nonnull
List<SqlRow> findList();
/**
@@ -71,18 +74,15 @@ public interface SqlQuery extends Serializable {
* PersistenceException.
* </p>
*/
@Nullable
SqlRow findOne();
/**
* Execute the query returning an optional row.
*/
@Nonnull
Optional<SqlRow> findOneOrEmpty();
/**
* Synonym for findOne().
*/
SqlRow findUnique();
/**
* The same as bind for named parameters.
*/
@@ -115,6 +115,11 @@ public interface SqlQuery extends Serializable {
*/
SqlQuery setTimeout(int secs);
/**
* Set a label that can be put on performance metrics that are collected.
*/
SqlQuery setLabel(String label);
/**
* A hint which for JDBC translates to the Statement.fetchSize().
* <p>
+24
View File
@@ -54,6 +54,25 @@ public interface SqlUpdate {
*/
int execute();
/**
* Return the generated key value.
*/
Object getGeneratedKey();
/**
* Execute and return the generated key. This is effectively a short cut for:
*
* <pre>{@code
*
* sqlUpdate.execute();
* Object key = sqlUpdate.getGeneratedKey();
*
* }</pre>
*
* @return The generated key value
*/
Object executeGetKey();
/**
* Return true if eBean should automatically deduce the table modification
* information and process it.
@@ -88,6 +107,11 @@ public interface SqlUpdate {
*/
SqlUpdate setLabel(String label);
/**
* Set to true when we want to use getGeneratedKeys with this statement.
*/
SqlUpdate setGetGeneratedKeys(boolean getGeneratedKeys);
/**
* Return the sql statement.
*/
+26
View File
@@ -1,6 +1,7 @@
package io.ebean;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
import io.ebean.config.DocStoreConfig;
import io.ebean.config.ServerConfig;
@@ -12,6 +13,23 @@ import java.sql.Connection;
*/
public interface Transaction extends AutoCloseable {
/**
* Return the current transaction (of the default server) or null if there is
* no current transaction in scope.
* <p>
* This is the same as <code>Ebean.currentTransaction()</code>
* </p>
* <p>
* This returns the current transaction for the 'default server'. If you are using
* multiple EbeanServer's then use {@link EbeanServer#currentTransaction()}.
* </p>
* @see Ebean#currentTransaction()
* @see EbeanServer#currentTransaction()
*/
static Transaction current() {
return Ebean.currentTransaction();
}
/**
* Read Committed transaction isolation. Same as
* java.sql.Connection.TRANSACTION_READ_COMMITTED.
@@ -41,6 +59,14 @@ public interface Transaction extends AutoCloseable {
*/
void register(TransactionCallback callback);
/**
* Set a label on the transaction.
* <p>
* This label is used to group transaction execution times for performance metrics reporting.
* </p>
*/
void setLabel(String label);
/**
* Return true if this transaction is read only.
*/
-47
View File
@@ -1,47 +0,0 @@
package io.ebean;
/**
* Execute a TxCallable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#execute(TxCallable)} method.
* </p>
* <p>
* Note that this is basically the same as TxRunnable except that it returns an
* Object (and you specify the return type via generics).
* </p>
* <p>
* See also {@link TxRunnable}.
* </p>
* <p>
* <pre>{@code
*
* Ebean.execute(new TxCallable<String>() {
* public String call() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* Ebean.save(u1);
* Ebean.save(u2);
*
* return u1.getEmail();
* }
* });
*
* }</pre>
*
* @see TxRunnable
*/
public interface TxCallable<T> {
/**
* Execute the method within a transaction scope returning the result.
* <p>
* If you do not want to return a result you should look to use TxRunnable
* instead.
* </p>
*/
T call();
}
-40
View File
@@ -1,40 +0,0 @@
package io.ebean;
/**
* Execute a TxRunnable in a Transaction scope.
* <p>
* Use this with the {@link Ebean#execute(TxRunnable)} method.
* </p>
* <p>
* See also {@link TxCallable}.
* </p>
* <p>
* <pre>{@code
*
* // this run method runs in a transaction scope
* // which by default is TxScope.REQUIRED
*
* Ebean.execute(new TxRunnable() {
* public void run() {
* User u1 = Ebean.find(User.class, 1);
* User u2 = Ebean.find(User.class, 2);
*
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* Ebean.save(u1);
* Ebean.save(u2);
* }
* });
*
* }</pre>
*
* @see TxCallable
*/
public interface TxRunnable {
/**
* Run the method in a transaction sope.
*/
void run();
}
+115 -17
View File
@@ -1,46 +1,62 @@
package io.ebean;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.TxIsolation;
import io.ebean.annotation.TxType;
import java.util.ArrayList;
import java.util.concurrent.Callable;
/**
* Holds the definition of how a transactional method should run.
* <p>
* This information matches the features of the Transactional annotation. You
* can use it directly with TxRunnable or TxCallable via
* {@link Ebean#execute(TxScope, TxCallable)} or
* {@link Ebean#execute(TxScope, TxRunnable)}.
* can use it directly with Runnable or Callable via
* {@link Ebean#execute(TxScope, Runnable)} or
* {@link Ebean#executeCall(TxScope, Callable)}.
* </p>
* <p>
* This object is used internally with the enhancement of a method with
* Transactional annotation.
* </p>
*
* @see TxCallable
* @see TxRunnable
* @see Ebean#execute(TxScope, TxCallable)
* @see Ebean#execute(TxScope, TxRunnable)
* @see Ebean#execute(TxScope, Runnable)
* @see Ebean#executeCall(TxScope, Callable)
*/
public final class TxScope {
TxType type;
private int profileId;
String serverName;
private TxType type;
TxIsolation isolation;
private String serverName;
PersistBatch batch;
private TxIsolation isolation;
PersistBatch batchOnCascade;
private PersistBatch batch;
int batchSize;
private PersistBatch batchOnCascade;
boolean skipGeneratedKeys;
private int batchSize;
boolean readOnly;
private boolean skipGeneratedKeys;
ArrayList<Class<? extends Throwable>> rollbackFor;
private boolean readOnly;
ArrayList<Class<? extends Throwable>> noRollbackFor;
/**
* Set this to false if the JDBC batch should not be automatically be flushed when a query is executed.
*/
private boolean flushOnQuery = true;
private boolean skipCache;
private String label;
private ArrayList<Class<? extends Throwable>> rollbackFor;
private ArrayList<Class<? extends Throwable>> noRollbackFor;
private ProfileLocation profileLocation;
/**
* Helper method to create a TxScope with REQUIRES.
@@ -160,6 +176,66 @@ public final class TxScope {
return this;
}
/**
* Return the transaction profile id.
*/
public int getProfileId() {
return profileId;
}
/**
* Set the transaction profile id.
*/
public TxScope setProfileId(int profileId) {
this.profileId = profileId;
return this;
}
/**
* Return the profile location.
*/
public ProfileLocation getProfileLocation() {
return profileLocation;
}
/**
* Set the profile location.
*/
public TxScope setProfileLocation(ProfileLocation profileLocation) {
this.profileLocation = profileLocation;
return this;
}
/**
* Return true if the L2 cache should be skipped for this transaction.
*/
public boolean isSkipCache() {
return skipCache;
}
/**
* Set to true if the transaction should skip L2 cache access.
*/
public TxScope setSkipCache(boolean skipCache) {
this.skipCache = skipCache;
return this;
}
/**
* Return the label for the transaction.
*/
public String getLabel() {
return label;
}
/**
* Set a label for the transaction.
*/
public TxScope setLabel(String label) {
this.label = label;
return this;
}
/**
* Return the batch mode.
*/
@@ -235,6 +311,28 @@ public final class TxScope {
return this;
}
/**
* Return false if the JDBC batch buffer should not be flushed automatically when a query is executed.
*/
public boolean isFlushOnQuery() {
return flushOnQuery;
}
/**
* Set flushOnQuery to be false to stop automatically flushing the JDBC batch buffer when a query is executed.
*/
public TxScope setFlushOnQuery(boolean flushOnQuery) {
this.flushOnQuery = flushOnQuery;
return this;
}
/**
* Return the isolation level.
*/
public int getIsolationLevel() {
return isolation != null ? isolation.getLevel() : -1;
}
/**
* Return the Isolation level this transaction should run with.
*/
+5
View File
@@ -152,6 +152,11 @@ public interface Update<T> {
*/
Update<T> setNullParameter(String name, int jdbcType);
/**
* Set a label meaning performance metrics will be collected for the execution of this update.
*/
Update<T> setLabel(String label);
/**
* Return the sql that is actually executed.
*/
+6
View File
@@ -148,6 +148,12 @@ public interface UpdateQuery<T> {
*/
UpdateQuery<T> setRaw(String propertyExpression, Object... values);
/**
* Set the profile location of this update query. This is used to relate query execution metrics
* back to a location like a specific line of code.
*/
UpdateQuery<T> setProfileLocation(ProfileLocation profileLocation);
/**
* Return the query expression list to add predicates to.
*/
@@ -0,0 +1,50 @@
package io.ebean;
import io.ebean.service.SpiProfileLocationFactory;
import io.ebean.service.SpiRawSqlService;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* Lookup internal services.
*/
class XServiceProvider {
private static SpiRawSqlService rawSqlService = initRawSql();
private static SpiProfileLocationFactory profileLocationFactory = initProfileLocation();
private static SpiRawSqlService initRawSql() {
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiRawSqlService?");
}
private static SpiProfileLocationFactory initProfileLocation() {
Iterator<SpiProfileLocationFactory> loader = ServiceLoader.load(SpiProfileLocationFactory.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiProfileLocationFactory?");
}
/**
* Return the RawSqlService implementation.
*/
static SpiRawSqlService rawSql() {
return rawSqlService;
}
/**
* Return the RawSqlService implementation.
*/
static SpiProfileLocationFactory profileLocationFactory() {
return profileLocationFactory;
}
}
@@ -191,6 +191,11 @@ public interface BeanCollection<E> extends Serializable {
*/
void setModifyListening(ModifyListenMode modifyListenMode);
/**
* Return the current modify listening mode. Can be null for on newly created beans.
*/
ModifyListenMode getModifyListening();
/**
* Add an object to the additions list.
* <p>
@@ -226,4 +231,14 @@ public interface BeanCollection<E> extends Serializable {
* additions and removals have been processed.
*/
void modifyReset();
/**
* Has been modified by an addition or removal.
*/
boolean wasTouched();
/**
* Return a shallow copy of this collection that is modifiable.
*/
BeanCollection<E> getShallowCopy();
}
@@ -0,0 +1,22 @@
package io.ebean.bean;
/**
* Visitor for collecting new/old values for a bean update.
*/
public interface BeanDiffVisitor {
/**
* Collect a new/old value pair.
*/
void visit(int position, Object newVal, Object oldVal);
/**
* Start processing an associated bean.
*/
void visitPush(int position);
/**
* Stop processing an associated bean.
*/
void visitPop();
}
+9 -2
View File
@@ -26,14 +26,16 @@ public final class CallStack implements Serializable {
private final StackTraceElement[] callStack;
private final int hc;
public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
this.callStack = callStack;
this.zeroHash = enc(zeroHash);
this.pathHash = enc(pathHash);
this.hc = computeHashCode();
}
@Override
public int hashCode() {
private int computeHashCode() {
int hc = 0;
for (StackTraceElement aCallStack : callStack) {
hc = 92821 * hc + aCallStack.hashCode();
@@ -41,6 +43,11 @@ public final class CallStack implements Serializable {
return hc;
}
@Override
public int hashCode() {
return hc;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
+6 -11
View File
@@ -1,6 +1,5 @@
package io.ebean.bean;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
/**
@@ -14,8 +13,14 @@ import java.io.Serializable;
*/
public interface EntityBean extends Serializable {
/**
* Return all the property names in defined order.
*/
String[] _ebean_getPropertyNames();
/**
* Return the property name at the given position.
*/
String _ebean_getPropertyName(int pos);
/**
@@ -32,16 +37,6 @@ public interface EntityBean extends Serializable {
*/
Object _ebean_newInstance();
/**
* Add a PropertyChangeListener to this bean.
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove a PropertyChangeListener from this bean.
*/
void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Generated method that sets the loaded state on all the embedded beans on
* this entity bean by using EntityBeanIntercept.setEmbeddedLoaded(Object o);
@@ -3,15 +3,12 @@ package io.ebean.bean;
import io.ebean.Ebean;
import io.ebean.ValuePair;
import java.util.Arrays;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.math.BigDecimal;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -34,8 +31,6 @@ public final class EntityBeanIntercept implements Serializable {
private transient NodeUsageCollector nodeUsageCollector;
private transient PropertyChangeSupport pcs;
private transient PersistenceContext persistenceContext;
private transient BeanLoader beanLoader;
@@ -57,6 +52,8 @@ public final class EntityBeanIntercept implements Serializable {
*/
private int state;
private boolean forceUpdate;
private boolean readOnly;
private boolean dirty;
@@ -95,6 +92,7 @@ public final class EntityBeanIntercept implements Serializable {
private int lazyLoadProperty = -1;
private Object ownerId;
private int sortOrder;
/**
* Create a intercept with a given entity.
@@ -128,46 +126,6 @@ public final class EntityBeanIntercept implements Serializable {
this.persistenceContext = persistenceContext;
}
/**
* Add a property change listener for this entity bean.
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
if (pcs == null) {
pcs = new PropertyChangeSupport(owner);
}
pcs.addPropertyChangeListener(listener);
}
/**
* Add a property change listener for this entity bean for a specific
* property.
*/
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
if (pcs == null) {
pcs = new PropertyChangeSupport(owner);
}
pcs.addPropertyChangeListener(propertyName, listener);
}
/**
* Remove a property change listener for this entity bean.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
if (pcs != null) {
pcs.removePropertyChangeListener(listener);
}
}
/**
* Remove a property change listener for this entity bean for a specific
* property.
*/
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
if (pcs != null) {
pcs.removePropertyChangeListener(propertyName, listener);
}
}
/**
* Turn on profile collection.
*/
@@ -336,6 +294,20 @@ public final class EntityBeanIntercept implements Serializable {
this.readOnly = readOnly;
}
/**
* Set the bean to be updated when persisted (for merge).
*/
public void setForceUpdate(boolean forceUpdate) {
this.forceUpdate = forceUpdate;
}
/**
* Return true if the entity should be updated.
*/
public boolean isUpdate() {
return forceUpdate || state == STATE_LOADED;
}
/**
* Return true if the entity has been loaded.
*/
@@ -507,6 +479,15 @@ public final class EntityBeanIntercept implements Serializable {
loadedProps[propertyIndex] = true;
}
/**
* Set all properties to be loaded (post insert).
*/
public void setLoadedPropertyAll() {
for (int i = 0; i < loadedProps.length; i++) {
loadedProps[i] = true;
}
}
/**
* Return true if the property is loaded.
*/
@@ -564,6 +545,16 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Set old value but force it to be set regardless if it already has a value.
*/
private void setOriginalValueForce(int propertyIndex, Object value) {
if (origValues == null) {
origValues = new Object[owner._ebean_getPropertyNames().length];
}
origValues[propertyIndex] = value;
}
/**
* For forced update on a 'New' bean set all the loaded properties to changed.
*/
@@ -694,6 +685,28 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Recursively add dirty properties.
*/
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
visitor.visit(i, newVal, oldVal);
} else if (embeddedDirty != null && embeddedDirty[i]) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
visitor.visitPush(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(visitor);
visitor.visitPop();
}
}
}
/**
* Return a dirty property hash taking into account embedded beans.
*/
@@ -707,6 +720,9 @@ public final class EntityBeanIntercept implements Serializable {
* Add and return a dirty property hash recursing into embedded beans.
*/
private void addDirtyPropertyKey(StringBuilder sb) {
if (sortOrder > 0) {
sb.append("s,");
}
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
@@ -860,9 +876,9 @@ public final class EntityBeanIntercept implements Serializable {
loadedProps[propertyIndex] = true;
}
private void preGetterCallback() {
private void preGetterCallback(int propertyIndex) {
if (preGetterCallback != null) {
preGetterCallback.preGetterTrigger();
preGetterCallback.preGetterTrigger(propertyIndex);
}
}
@@ -870,14 +886,14 @@ public final class EntityBeanIntercept implements Serializable {
* Called prior to Id property getter.
*/
public void preGetId() {
preGetterCallback();
preGetterCallback(-1);
}
/**
* Method that is called prior to a getter method on the actual entity.
*/
public void preGetter(int propertyIndex) {
preGetterCallback();
preGetterCallback(propertyIndex);
if (state == STATE_NEW || disableLazyLoad) {
return;
}
@@ -889,49 +905,17 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Called for "enhancement" postSetter processing. This is around a PUTFIELD
* so no need to check the newValue afterwards.
*/
public void postSetter(PropertyChangeEvent event) {
if (pcs != null && event != null) {
pcs.firePropertyChange(event);
}
}
/**
* Called for "subclassed" postSetter processing. Here the newValue has to be
* re-fetched (and passed into this method) in case there is code inside the
* setter that further mutates the value.
*/
public void postSetter(PropertyChangeEvent event, Object newValue) {
if (pcs != null && event != null) {
if (newValue != null && newValue.equals(event.getNewValue())) {
pcs.firePropertyChange(event);
} else {
pcs.firePropertyChange(event.getPropertyName(), event.getOldValue(), newValue);
}
}
}
/**
* OneToMany and ManyToMany don't have any interception so just check for
* PropertyChangeSupport.
*/
public PropertyChangeEvent preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setLoadedProperty(propertyIndex);
// Bean itself not considered dirty when many changed
if (pcs != null) {
return new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
} else {
return null;
}
}
private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
@@ -943,15 +927,19 @@ public final class EntityBeanIntercept implements Serializable {
if (setDirtyState) {
setOriginalValue(propertyIndex, origValue);
if (!dirty) {
dirty = true;
if (embeddedOwner != null) {
// Cascade dirty state from Embedded bean to parent bean
embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex);
}
if (nodeUsageCollector != null) {
nodeUsageCollector.setModified();
}
setDirtyStatus();
}
}
private void setDirtyStatus() {
if (!dirty) {
dirty = true;
if (embeddedOwner != null) {
// Cascade dirty state from Embedded bean to parent bean
embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex);
}
if (nodeUsageCollector != null) {
nodeUsageCollector.setModified();
}
}
}
@@ -960,168 +948,156 @@ public final class EntityBeanIntercept implements Serializable {
* Check to see if the values are not equal. If they are not equal then create
* the old values for use with ConcurrencyMode.ALL.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!areEqual(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* Check for primitive boolean.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* Check for primitive int.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* long.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* double.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
} else if (Double.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* float.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
} else if (Float.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* short.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* char.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* byte.
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* char[].
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* byte[].
*/
public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
}
return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* Explicitly set an old value with force (the old value is forced even it is already set).
*/
public void setOldValue(int propertyIndex, Object oldValue) {
setChangedProperty(propertyIndex);
setOriginalValueForce(propertyIndex, oldValue);
setDirtyStatus();
}
/**
* Return the sort order value for an order column.
*/
public int getSortOrder() {
return sortOrder;
}
/**
* Set the sort order value for an order column.
*/
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
}
@@ -7,7 +7,9 @@ package io.ebean.bean;
public interface PreGetterCallback {
/**
* Trigger the callback.
* Trigger the callback based on a getter on a property.
*
* @param propertyIndex The index of the property
*/
void preGetterTrigger();
void preGetterTrigger(int propertyIndex);
}
+32 -2
View File
@@ -1,5 +1,9 @@
package io.ebean.cache;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Represents part of the "L2" server side cache.
* <p>
@@ -13,20 +17,46 @@ package io.ebean.cache;
*/
public interface ServerCache {
default Map<Object, Object> getAll(Set<Object> keys) {
Map<Object, Object> map = new LinkedHashMap<>();
for (Object key : keys) {
Object value = get(key);
if (value != null) {
map.put(key, value);
}
}
return map;
}
/**
* Return the value given the key.
*/
Object get(Object id);
/**
* Put all the values in the cache.
*/
default void putAll(Map<Object, Object> keyValues) {
keyValues.forEach(this::put);
}
/**
* Put the value in the cache with a given id.
*/
Object put(Object id, Object value);
void put(Object id, Object value);
/**
* Remove the entries from the cache given the id values.
*/
default void removeAll(Set<Object> keys) {
keys.forEach(this::remove);
}
/**
* Remove a entry from the cache given its id.
*/
Object remove(Object id);
void remove(Object id);
/**
* Clear all entries from the cache.
+15
View File
@@ -44,4 +44,19 @@ public interface ServerCacheManager {
*/
void clearAll();
/**
* Clear all the local caches.
*
* This is used when the L2 Cache is based on clustered near-caches (Like Ebean-K8s-L2Cache).
* It is not used when the L2 cache is a distributed cache such as HazelCast or Ignite etc.
*/
void clearAllLocal();
/**
* Clear the local caches for this bean type.
*
* This is used when the L2 Cache is based on clustered near-caches (Like Ebean-K8s-L2Cache).
* It is not used when the L2 cache is a distributed cache such as HazelCast or Ignite etc.
*/
void clearLocal(Class<?> beanType);
}
@@ -140,6 +140,11 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
// Support for modify additions deletions etc - ManyToMany
// ---------------------------------------------------------
@Override
public ModifyListenMode getModifyListening() {
return modifyListenMode;
}
/**
* set modifyListening to be on or off.
*/
@@ -213,4 +218,21 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
boolean holdsModifications() {
return modifyHolder != null && modifyHolder.hasModifications();
}
@Override
public boolean wasTouched() {
return modifyHolder != null && modifyHolder.wasTouched();
}
/**
* Copies all relevant properties for a clone. See {@link #getShallowCopy()}
* @param other
*/
protected void setFromOriginal(AbstractBeanCollection<E> other) {
this.disableLazyLoad = other.disableLazyLoad;
this.ebeanServerName = other.ebeanServerName;
this.loader = other.loader;
this.ownerBean = other.ownerBean;
this.propertyName = other.propertyName;
}
}
@@ -541,4 +541,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
}
@Override
public BeanCollection<E> getShallowCopy() {
BeanList<E> copy = new BeanList<>(new CopyOnFirstWriteList<>(list));
copy.setFromOriginal(this);
return copy;
}
}
@@ -340,4 +340,10 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
return map.values();
}
@Override
public BeanCollection<E> getShallowCopy() {
BeanMap<K, E> copy = new BeanMap<>(new LinkedHashMap<>(map));
copy.setFromOriginal(this);
return copy;
}
}
+7 -1
View File
@@ -377,5 +377,11 @@ 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));
copy.setFromOriginal(this);
return copy;
}
}
@@ -0,0 +1,180 @@
package io.ebean.common;
import java.io.Serializable;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* List that copies itself on first write access. Needed to keep memory footprint low and the ability
* to modify lists from cache.
*
* @author Roland Praml, FOCONIS AG
*/
public final class CopyOnFirstWriteList<E> extends AbstractList<E> implements List<E>, Serializable {
private static final long serialVersionUID = 1L;
/**
* The underlying List implementation.
*/
private List<E> list;
public CopyOnFirstWriteList(List<E> list) {
super();
this.list = list;
}
private volatile boolean copied = false;
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean add(E e) {
checkCopyOnWrite();
return list.add(e);
}
@Override
public boolean remove(Object o) {
checkCopyOnWrite();
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
checkCopyOnWrite();
return list.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
checkCopyOnWrite();
return list.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
checkCopyOnWrite();
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
checkCopyOnWrite();
return list.retainAll(c);
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
checkCopyOnWrite();
list.replaceAll(operator);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
checkCopyOnWrite();
return list.removeIf(filter);
}
@Override
public void sort(Comparator<? super E> c) {
checkCopyOnWrite();
list.sort(c);
}
@Override
public void clear() {
if (!copied) {
list = new ArrayList<>();
copied = true;
}
}
@Override
public boolean equals(Object o) {
return list.equals(o);
}
@Override
public int hashCode() {
return list.hashCode();
}
@Override
public E get(int index) {
return list.get(index);
}
@Override
public E set(int index, E element) {
checkCopyOnWrite();
return list.set(index, element);
}
@Override
public void add(int index, E element) {
checkCopyOnWrite();
list.add(index, element);
}
@Override
public E remove(int index) {
checkCopyOnWrite();
return list.remove(index);
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
private void checkCopyOnWrite() {
if (!copied) {
synchronized (this) {
if (!copied) {
list = new ArrayList<>(list);
copied = true;
}
}
}
}
}
@@ -28,7 +28,10 @@ class ModifyHolder<E> implements Serializable {
*/
private Set<E> modifyAdditions = new LinkedHashSet<>();
private boolean touched;
void reset() {
touched = false;
modifyDeletions = new LinkedHashSet<>();
modifyAdditions = new LinkedHashSet<>();
}
@@ -50,6 +53,7 @@ class ModifyHolder<E> implements Serializable {
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
// Insert
@@ -65,6 +69,7 @@ class ModifyHolder<E> implements Serializable {
@SuppressWarnings("unchecked")
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
modifyDeletions.add((E) bean);
@@ -80,6 +85,14 @@ class ModifyHolder<E> implements Serializable {
return modifyDeletions;
}
/**
* Return true if the collection was touched in some way. This is still true even if
* a bean was removed and then added (which is not held as a modification).
*/
boolean wasTouched() {
return touched;
}
/**
* Return true if there additions or removals.
*/
@@ -1,11 +1,13 @@
package io.ebean.config;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.util.AnnotationUtil;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import javax.persistence.Table;
import java.lang.annotation.Annotation;
/**
* Provides some base implementation for NamingConventions.
@@ -14,16 +16,15 @@ import java.lang.annotation.Annotation;
*/
public abstract class AbstractNamingConvention implements NamingConvention {
private static final Pattern TABLE_REPLACE = Pattern.compile("{table}", Pattern.LITERAL);
private static final Pattern COLUMN_REPLACE = Pattern.compile("{column}", Pattern.LITERAL);
/**
* The Constant DEFAULT_SEQ_FORMAT.
*/
public static final String DEFAULT_SEQ_FORMAT = "{table}_seq";
/**
* Sequence Format that includes the Primary Key column
*/
public static final String TABLE_PKCOLUMN_SEQ_FORMAT = "{table}_{column}_seq";
/**
* The catalog.
*/
@@ -83,11 +84,11 @@ public abstract class AbstractNamingConvention implements NamingConvention {
@Override
public String getSequenceName(String tableName, String pkColumn) {
String s = sequenceFormat.replace("{table}", tableName);
String s = TABLE_REPLACE.matcher(sequenceFormat).replaceAll(Matcher.quoteReplacement(tableName));
if (pkColumn == null) {
pkColumn = "";
}
return s.replace("{column}", pkColumn);
return COLUMN_REPLACE.matcher(s).replaceAll(Matcher.quoteReplacement(pkColumn));
}
/**
@@ -218,15 +219,10 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Return true if this class is part of entity inheritance.
*/
protected boolean hasInheritance(Class<?> supCls) {
return hasAnnotation(supCls, Inheritance.class) || hasAnnotation(supCls, DiscriminatorValue.class);
return AnnotationUtil.findAnnotationRecursive(supCls, Inheritance.class) != null
|| AnnotationUtil.findAnnotation(supCls, DiscriminatorValue.class) != null;
}
/**
* Return true if the class has the given annotation.
*/
protected boolean hasAnnotation(Class<?> supCls, Class<? extends Annotation> annotation) {
return supCls.getAnnotation(annotation) != null;
}
@Override
public TableName getM2MJoinTableName(TableName lhsTable, TableName rhsTable) {
@@ -257,7 +253,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
*/
protected TableName getTableNameFromAnnotation(Class<?> beanClass) {
final Table t = findTableAnnotation(beanClass);
final Table t = AnnotationUtil.findAnnotationRecursive(beanClass, Table.class);
// Take the annotation if defined
if (t != null && !isEmpty(t.name())) {
@@ -270,22 +266,6 @@ public abstract class AbstractNamingConvention implements NamingConvention {
return null;
}
/**
* Search recursively for an @Table in the class hierarchy.
*/
protected Table findTableAnnotation(Class<?> cls) {
while (true) {
if (cls.equals(Object.class)) {
return null;
}
Table table = cls.getAnnotation(Table.class);
if (table != null) {
return table;
}
cls = cls.getSuperclass();
}
}
/**
* Replace back ticks (if they are used) with database platform specific
* quoted identifiers.
@@ -0,0 +1,18 @@
package io.ebean.config;
/**
* Used to provide some automatic configuration early in the creation of an EbeanServer.
*/
public interface AutoConfigure {
/**
* Perform configuration for the ServerConfig prior to properties load.
*/
void preConfigure(ServerConfig serverConfig);
/**
* Provide some configuration the ServerConfig prior to server creation but after properties have been applied.
*/
void postConfigure(ServerConfig serverConfig);
}
@@ -32,6 +32,15 @@ public class AutoTuneConfig {
public AutoTuneConfig() {
}
/**
* Return true if we are profiling or query tuning.
*
* If we are not doing either then we don't need a CallStack.
*/
public boolean isActive() {
return profiling || queryTuning;
}
/**
* Return the name of the file that holds the query tuning information.
*/
@@ -54,6 +54,22 @@ public class ClassLoadConfig {
return isPresent("javax.validation.constraints.NotNull");
}
/**
* Return true if javax PostConstruct annotation is present (maybe not in java9).
* If not we don't support PostConstruct lifecycle events.
*/
public boolean isJavaxPostConstructPresent() {
return isPresent("javax.annotation.PostConstruct");
}
/**
* Return true if javax JAXB is present (maybe not in java9).
* If not we don't try to parse or support 'extra ddl'.
*/
public boolean isJavaxJAXBPresent() {
return isPresent("javax.xml.bind.JAXBException");
}
/**
* Return true if Jackson annotations like JsonIgnore are present.
*/
@@ -10,22 +10,82 @@ import java.util.Properties;
*/
public class ContainerConfig {
protected boolean clusterActive;
private boolean active;
private String serviceName;
private String namespace;
private String podName;
private int port;
protected Properties properties;
private Properties properties;
/**
* Return the service name.
*/
public String getServiceName() {
return serviceName;
}
/**
* Set the service name.
*/
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
/**
* Return the namespace.
*/
public String getNamespace() {
return namespace;
}
/**
* Set the namespace.
*/
public void setNamespace(String namespace) {
this.namespace = namespace;
}
/**
* Return the pod name.
*/
public String getPodName() {
return podName;
}
/**
* Set the pod name.
*/
public void setPodName(String podName) {
this.podName = podName;
}
/**
* Return the port to use.
*/
public int getPort() {
return port;
}
/**
* Set the port to use.
*/
public void setPort(int port) {
this.port = port;
}
/**
* Return true if clustering is active.
*/
public boolean isClusterActive() {
return clusterActive;
public boolean isActive() {
return active;
}
/**
* Set to true for clustering to be active.
*/
public void setClusterActive(boolean clusterActive) {
this.clusterActive = clusterActive;
public void setActive(boolean active) {
this.active = active;
}
/**
@@ -47,7 +107,14 @@ public class ContainerConfig {
*/
public void loadFromProperties(Properties properties) {
this.properties = properties;
this.clusterActive = getProperty(properties, "ebean.cluster.active", clusterActive);
this.active = getProperty(properties, "ebean.cluster.active", active);
this.serviceName = properties.getProperty("ebean.cluster.serviceName", serviceName);
this.namespace = properties.getProperty("ebean.cluster.namespace", namespace);
this.podName = properties.getProperty("ebean.cluster.podName", podName);
String portParam = properties.getProperty("ebean.cluster.port");
if (portParam != null) {
this.port = Integer.parseInt(portParam);
}
}
/**
@@ -1,7 +1,7 @@
package io.ebean.config;
import io.ebean.config.dbplatform.DbType;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
/**
* Custom mappings for DB types that override the default.
@@ -1,11 +1,16 @@
package io.ebean.config;
import io.ebean.Platform;
import io.ebean.dbmigration.MigrationConfig;
import io.ebean.dbmigration.MigrationRunner;
import io.ebean.EbeanVersion;
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;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Properties;
@@ -16,6 +21,8 @@ public class DbMigrationConfig {
protected static final Logger logger = LoggerFactory.getLogger(DbMigrationConfig.class);
protected MigrationConfig runnerConfig = new MigrationConfig();
/**
* The database platform to generate migration DDL for.
*/
@@ -105,6 +112,20 @@ public class DbMigrationConfig {
*/
protected String dbPassword;
protected String patchInsertOn;
protected String patchResetChecksumOn;
/**
* Mode used to check non-null columns added via migration have a default value specified etc.
*/
protected boolean strictMode = true;
/**
* Contains the DDL-header information.
*/
protected String ddlHeader;
/**
* Return the DB platform to generate migration DDL for.
* <p>
@@ -352,6 +373,73 @@ public class DbMigrationConfig {
this.dbPassword = dbPassword;
}
/**
* Return migration versions that should be added to history without running.
*/
public String getPatchInsertOn() {
return patchInsertOn;
}
/**
* Set migration versions that should be added to history without running.
* <p>
* Value can be a string containing comma delimited list of version numbers.
* </p>
*/
public void setPatchInsertOn(String patchInsertOn) {
this.patchInsertOn = patchInsertOn;
}
/**
* Return migration versions that should have their checksum reset and not run.
*/
public String getPatchResetChecksumOn() {
return patchResetChecksumOn;
}
/**
* Returns a DDL header prepend for each DDL. E.g. for copyright headers
* You can use placeholders like ${version} or ${timestamp} in properties file.
*/
public String getDdlHeader() {
return ddlHeader;
}
/**
* Set migration versions that should have their checksum reset and not run.
* <p>
* Value can be a string containing comma delimited list of version numbers.
* </p>
*/
public void setPatchResetChecksumOn(String patchResetChecksumOn) {
this.patchResetChecksumOn = patchResetChecksumOn;
}
/**
* Return true if strict mode is used which includes a check that non-null columns have a default value.
*/
public boolean isStrictMode() {
String envValue = readEnvironment("ddl.migration.strictMode");
if (!isEmpty(envValue)) {
return Boolean.parseBoolean(envValue.trim());
}
return strictMode;
}
/**
* Set to false to turn off strict mode allowing non-null columns to not have a default value.
*/
public void setStrictMode(boolean strictMode) {
this.strictMode = strictMode;
}
/**
* Return the underlying migration runner configuration allowing for more advanced settings.
*/
public MigrationConfig getRunnerConfig() {
return runnerConfig;
}
/**
* Load the settings from the PropertiesWrapper.
*/
@@ -370,18 +458,27 @@ public class DbMigrationConfig {
generate = properties.getBoolean("migration.generate", generate);
version = properties.get("migration.version", version);
name = properties.get("migration.name", name);
patchInsertOn = properties.get("migration.patchInsertOn", patchInsertOn);
patchResetChecksumOn = properties.get("migration.patchResetChecksumOn", patchResetChecksumOn);
runMigration = properties.getBoolean("migration.run", runMigration);
metaTable = properties.get("migration.metaTable", metaTable);
runPlaceholders = properties.get("migration.placeholders", runPlaceholders);
String adminUser = properties.get("datasource." + serverName + ".username", dbUsername);
adminUser = properties.get("datasource." + serverName + ".adminusername", adminUser);
//Do not set user and pass from "datasource.db.username"
//There is a null test in MigrationRunner::getConnection to handle this
//String adminUser = properties.get("datasource." + serverName + ".username", dbUsername);
String adminUser = properties.get("datasource." + serverName + ".adminusername", dbUsername);
dbUsername = properties.get("migration.dbusername", adminUser);
String adminPwd = properties.get("datasource." + serverName + ".password", dbPassword);
adminPwd = properties.get("datasource." + serverName + ".adminpassword", adminPwd);
//String adminPwd = properties.get("datasource." + serverName + ".password", dbPassword);
String adminPwd = properties.get("datasource." + serverName + ".adminpassword", dbPassword);
dbPassword = properties.get("migration.dbpassword", adminPwd);
ddlHeader = properties.get("ddl.header", ddlHeader);
if (ddlHeader != null && !ddlHeader.isEmpty()) {
ddlHeader = StringHelper.replaceString(ddlHeader, "${version}", EbeanVersion.getVersion());
ddlHeader = StringHelper.replaceString(ddlHeader, "${timestamp}", ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT ));
}
}
/**
@@ -446,7 +543,6 @@ public class DbMigrationConfig {
*/
public MigrationRunner createRunner(ClassLoader classLoader, Properties properties) {
MigrationConfig runnerConfig = new MigrationConfig();
runnerConfig.setMetaTable(metaTable);
runnerConfig.setApplySuffix(applySuffix);
runnerConfig.setMigrationPath(migrationPath);
@@ -455,6 +551,12 @@ public class DbMigrationConfig {
runnerConfig.setDbUsername(getDbUsername());
runnerConfig.setDbPassword(getDbPassword());
runnerConfig.setClassLoader(classLoader);
if (patchInsertOn != null) {
runnerConfig.setPatchInsertOn(patchInsertOn);
}
if (patchResetChecksumOn != null) {
runnerConfig.setPatchResetChecksumOn(patchResetChecksumOn);
}
if (properties != null) {
runnerConfig.load(properties);
}
@@ -1,124 +0,0 @@
package io.ebean.config;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.Platform;
import java.util.ArrayList;
import java.util.List;
/**
* Configuration for DB types such as UUID, Geometry etc.
*/
public class DbTypeConfig {
/**
* The Geometry SRID value (default 4326).
*/
private int geometrySRID = 4326;
/**
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
*/
private IdType idType;
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
*/
private ServerConfig.DbUuid dbUuid = ServerConfig.DbUuid.AUTO_VARCHAR;
/**
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
*/
private List<CustomDbTypeMapping> customDbTypeMappings = new ArrayList<>();
/**
* Return the Geometry SRID.
*/
public int getGeometrySRID() {
return geometrySRID;
}
/**
* Set the Geometry SRID.
*/
public void setGeometrySRID(int geometrySRID) {
this.geometrySRID = geometrySRID;
}
/**
* Return the DB type used to store UUID.
*/
public ServerConfig.DbUuid getDbUuid() {
return dbUuid;
}
/**
* Set the DB type used to store UUID.
*/
public void setDbUuid(ServerConfig.DbUuid dbUuid) {
this.dbUuid = dbUuid;
}
/**
* Return the IdType to use (or null for the default choice).
*/
public IdType getIdType() {
return idType;
}
/**
* Set the IdType to use (when the DB supports both SEQUENCE and IDENTITY and the default is not desired).
*/
public void setIdType(IdType idType) {
this.idType = idType;
}
/**
* Add a custom type mapping.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal.class/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String.class/varchar but only for Postgres
* serverConfig.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
* @param platform Optionally specify the platform this mapping should apply to.
*/
public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition, platform));
}
/**
* Add a custom type mapping that applies to all platforms.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String/varchar
* serverConfig.addCustomMapping(DbType.VARCHAR, "text");
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
*/
public void addCustomMapping(DbType type, String columnDefinition) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition));
}
/**
* Return the list of custom type mappings.
*/
public List<CustomDbTypeMapping> getCustomTypeMappings() {
return customDbTypeMappings;
}
}
@@ -12,7 +12,7 @@ public interface EncryptKeyManager {
* This gives the EncryptKeyManager the opportunity to get keys etc.
* </p>
*/
default void initialise() {};
default void initialise() {}
/**
* Return the key used to encrypt and decrypt a property mapping to the given
@@ -31,7 +31,7 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
@Override
public String getColumnFromProperty(Class<?> beanClass, String propertyName) {
return propertyName;
return quoteIdentifiers(propertyName);
}
@Override
@@ -47,8 +47,11 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
@Override
public String getForeignKey(String prefix, String fkProperty) {
prefix = databasePlatform.unQuote(prefix);
fkProperty = databasePlatform.unQuote(fkProperty);
// add fkProperty as init caps
return prefix + fkProperty.substring(0, 1).toUpperCase() + fkProperty.substring(1);
String fullName = prefix + fkProperty.substring(0, 1).toUpperCase() + fkProperty.substring(1);
return quoteIdentifiers(fullName);
}
}
@@ -0,0 +1,335 @@
package io.ebean.config;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.util.StringHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Configuration for DB types such as UUID, Geometry etc.
*/
public class PlatformConfig {
private boolean allQuotedIdentifiers;
/**
* The database boolean true value (typically either 1, T, or Y).
*/
private String databaseBooleanTrue;
/**
* The database boolean false value (typically either 0, F or N).
*/
private String databaseBooleanFalse;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
private int databaseSequenceBatchSize = 20;
/**
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
*/
private IdType idType;
/**
* The Geometry SRID value (default 4326).
*/
private int geometrySRID = 4326;
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
*/
private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
/**
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
*/
private List<CustomDbTypeMapping> customDbTypeMappings = new ArrayList<>();
/**
* Construct with defaults.
*/
public PlatformConfig() {
}
/**
* Construct based on given config - typically for DbMigration generation with many platforms.
*/
public PlatformConfig(PlatformConfig platformConfig) {
this.databaseBooleanFalse = platformConfig.databaseBooleanFalse;
this.databaseBooleanTrue = platformConfig.databaseBooleanTrue;
this.databaseSequenceBatchSize = platformConfig.databaseSequenceBatchSize;
this.idType = platformConfig.idType;
this.geometrySRID = platformConfig.geometrySRID;
this.dbUuid = platformConfig.dbUuid;
}
/**
* Return true if all DB column and table names should use quoted identifiers.
*/
public boolean isAllQuotedIdentifiers() {
return allQuotedIdentifiers;
}
/**
* Set to true if all DB column and table names should use quoted identifiers.
*/
public void setAllQuotedIdentifiers(boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
}
/**
* Return a value used to represent TRUE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value returned is either a Integer or a String (e.g. "1", or "T").
* </p>
*/
public String getDatabaseBooleanTrue() {
return databaseBooleanTrue;
}
/**
* Set the value to represent TRUE in the database.
* <p>
* This is used for databases that do not support boolean natively.
* </p>
* <p>
* The value set is either a Integer or a String (e.g. "1", or "T").
* </p>
*/
public void setDatabaseBooleanTrue(String databaseBooleanTrue) {
this.databaseBooleanTrue = databaseBooleanTrue;
}
/**
* Return a value used to represent FALSE in the database.
*/
public String getDatabaseBooleanFalse() {
return databaseBooleanFalse;
}
/**
* Set the value used to represent FALSE in the database.
*/
public void setDatabaseBooleanFalse(String databaseBooleanFalse) {
this.databaseBooleanFalse = databaseBooleanFalse;
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return databaseSequenceBatchSize;
}
/**
* Set the number of DB sequence values that should be preallocated.
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
}
/**
* Return the Geometry SRID.
*/
public int getGeometrySRID() {
return geometrySRID;
}
/**
* Set the Geometry SRID.
*/
public void setGeometrySRID(int geometrySRID) {
this.geometrySRID = geometrySRID;
}
/**
* Return the DB type used to store UUID.
*/
public DbUuid getDbUuid() {
return dbUuid;
}
/**
* Set the DB type used to store UUID.
*/
public void setDbUuid(DbUuid dbUuid) {
this.dbUuid = dbUuid;
}
/**
* Return the IdType to use (or null for the default choice).
*/
public IdType getIdType() {
return idType;
}
/**
* Set the IdType to use (when the DB supports both SEQUENCE and IDENTITY and the default is not desired).
*/
public void setIdType(IdType idType) {
this.idType = idType;
}
/**
* Add a custom type mapping.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal.class/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String.class/varchar but only for Postgres
* serverConfig.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
* @param platform Optionally specify the platform this mapping should apply to.
*/
public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition, platform));
}
/**
* Add a custom type mapping that applies to all platforms.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String/varchar
* serverConfig.addCustomMapping(DbType.VARCHAR, "text");
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
*/
public void addCustomMapping(DbType type, String columnDefinition) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition));
}
/**
* Return the list of custom type mappings.
*/
public List<CustomDbTypeMapping> getCustomTypeMappings() {
return customDbTypeMappings;
}
public void loadSettings(PropertiesWrapper p) {
idType = p.getEnum(IdType.class, "idType", idType);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
if (dbUuid != null) {
setDbUuid(dbUuid);
}
if (p.getBoolean("uuidStoreAsBinary", false)) {
setDbUuid(DbUuid.BINARY);
}
int srid = p.getInt("geometrySRID", 0);
if (srid > 0) {
setGeometrySRID(srid);
}
// Mapping is specified in the form: BOOLEAN=int(1);BIT=int(1);
String mapping = p.get("mapping");
if (mapping != null && !mapping.isEmpty()) {
Map<String, String> map = StringHelper.delimitedToMap(mapping, ";", "=");
for (Entry<String, String> entry : map.entrySet()) {
addCustomMapping(DbType.valueOf(entry.getKey()), entry.getValue());
}
}
boolean quotedIdentifiers = p.getBoolean("allQuotedIdentifiers", allQuotedIdentifiers);
if (quotedIdentifiers != allQuotedIdentifiers) {
// potentially also set to use matching naming convention
setAllQuotedIdentifiers(quotedIdentifiers);
}
}
/**
* Specify how UUID is stored.
*/
public enum DbUuid {
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to VARCHAR(40).
*/
AUTO_VARCHAR(true, false, false),
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16).
*/
AUTO_BINARY(true, true, false),
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16) with optimized packing.
*/
AUTO_BINARY_OPTIMIZED(true, true, true),
/**
* Store using DB VARCHAR(40).
*/
VARCHAR(false, false, false),
/**
* Store using DB BINARY(16).
*/
BINARY(false, true, false),
/**
* Store using DB BINARY(16).
*/
BINARY_OPTIMIZED(false, true, true);
boolean nativeType;
boolean binary;
boolean binaryOptimized;
DbUuid(boolean nativeType, boolean binary, boolean binaryOptimized) {
this.nativeType = nativeType;
this.binary = binary;
this.binaryOptimized = binaryOptimized;
}
/**
* Return true if native UUID type is preferred.
*/
public boolean useNativeType() {
return nativeType;
}
/**
* Return true if BINARY(16) storage is preferred over VARCHAR(40).
*/
public boolean useBinary() {
return binary;
}
/**
* Return true, if optimized packing should be used.
*/
public boolean useBinaryOptimized() {
return binaryOptimized;
}
}
}
@@ -0,0 +1,148 @@
package io.ebean.config;
/**
* Configuration for transaction profiling.
*/
public class ProfilingConfig {
/**
* When true transaction profiling is enabled.
*/
private boolean enabled;
/**
* Set true for verbose mode.
*/
private boolean verbose;
/**
* The minimum transaction execution time to be included in profiling.
*/
private long minimumMicros;
/**
* A specific set of profileIds to include in profiling.
*/
private int[] includeProfileIds = {};
/**
* The number of profiles to write per file.
*/
private long profilesPerFile = 1000;
private String directory = "profiling";
/**
* Return true if transaction profiling is enabled.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Set to true to enable transaction profiling.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Return true if verbose mode is used.
*/
public boolean isVerbose() {
return verbose;
}
/**
* Set to true to use verbose mode.
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
/**
* Return the minimum transaction execution to be included in profiling.
*/
public long getMinimumMicros() {
return minimumMicros;
}
/**
* Set the minimum transaction execution to be included in profiling.
*/
public void setMinimumMicros(long minimumMicros) {
this.minimumMicros = minimumMicros;
}
/**
* Return the specific set of profileIds to include in profiling.
* When not set all transactions with profileIds are included.
*/
public int[] getIncludeProfileIds() {
return includeProfileIds;
}
/**
* Set a specific set of profileIds to include in profiling.
* When not set all transactions with profileIds are included.
*/
public void setIncludeProfileIds(int[] includeProfileIds) {
this.includeProfileIds = includeProfileIds;
}
/**
* Return the number of profiles to write to a single file.
*/
public long getProfilesPerFile() {
return profilesPerFile;
}
/**
* Set the number of profiles to write to a single file.
*/
public void setProfilesPerFile(long profilesPerFile) {
this.profilesPerFile = profilesPerFile;
}
/**
* Return the directory profiling files are put into.
*/
public String getDirectory() {
return directory;
}
/**
* Set the directory profiling files are put into.
*/
public void setDirectory(String directory) {
this.directory = directory;
}
/**
* Load setting from properties.
*/
public void loadSettings(PropertiesWrapper p, String name) {
enabled = p.getBoolean("profiling", enabled);
verbose = p.getBoolean("profiling.verbose", verbose);
directory = p.get("profiling.directory", directory);
profilesPerFile = p.getLong("profiling.profilesPerFile", profilesPerFile);
minimumMicros = p.getLong("profiling.minimumMicros", minimumMicros);
String includeIds = p.get("profiling.includeProfileIds");
if (includeIds != null) {
includeProfileIds = parseIds(includeIds);
}
}
private int[] parseIds(String includeIds) {
String[] ids = includeIds.split(",");
int[] vals = new int[ids.length];
for (int i = 0; i < ids.length; i++) {
vals[i] = Integer.parseInt(ids[i]);
}
return vals;
}
}
@@ -10,44 +10,23 @@ public class PropertiesWrapper {
protected final String serverName;
protected final PropertyMap propertyMap;
private final ClassLoadConfig classLoadConfig;
/**
* Construct with a prefix, serverName and properties.
*/
public PropertiesWrapper(String prefix, String serverName, Properties properties) {
public PropertiesWrapper(String prefix, String serverName, Properties properties, ClassLoadConfig classLoadConfig) {
this.serverName = serverName;
this.prefix = prefix;
this.propertyMap = PropertyMapLoader.load(null, properties);
this.properties = propertyMap.asProperties();
this.properties = properties;
this.classLoadConfig = classLoadConfig;
}
/**
* Construct without prefix of serverName.
*/
public PropertiesWrapper(Properties properties) {
this(null, null, properties);
}
/**
* Internal copy constructor when changing prefix.
*/
protected PropertiesWrapper(String prefix, String serverName, PropertyMap propertyMap, Properties properties) {
this.serverName = serverName;
this.prefix = prefix;
this.propertyMap = propertyMap;
this.properties = properties;
}
/**
* Return a PropertiesWrapper instance with a different prefix but same underlying properties.
* <p/>
* Used when wanting to use "datasource" as the prefix rather than "ebean".
* <p/>
* The returning instance should only be used in a read only fashion.
*/
public PropertiesWrapper withPrefix(String prefix) {
return new PropertiesWrapper(prefix, serverName, propertyMap, properties);
public PropertiesWrapper(Properties properties, ClassLoadConfig classLoadConfig) {
this(null, null, properties, classLoadConfig);
}
/**
@@ -57,16 +36,6 @@ public class PropertiesWrapper {
return serverName;
}
/**
* Return as Properties with lower case keys and after evaluation and additional properties loading has occurred.
* <p>
* Ebean has historically ignored the case of keys hence returning the Properties with all the keys lower cased.
* </p>
*/
public Properties asPropertiesLowerCase() {
return properties;
}
/**
* Get a property with no default value.
*/
@@ -90,17 +59,21 @@ public class PropertiesWrapper {
String value = null;
if (serverName != null && prefix != null) {
value = propertyMap.get(prefix + "." + serverName + "." + key, null);
value = internalGet(prefix + "." + serverName + "." + key);
}
if (value == null && prefix != null) {
value = propertyMap.get(prefix + "." + key, null);
value = internalGet(prefix + "." + key);
}
if (value == null) {
value = propertyMap.get(key, null);
value = internalGet(key);
}
return value == null ? defaultValue : value;
}
private String internalGet(String key) {
return properties.getProperty(key);
}
/**
* Return a double property value.
*/
@@ -145,4 +118,30 @@ public class PropertiesWrapper {
return (level == null) ? defaultValue : Enum.valueOf(enumType, level.toUpperCase());
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param pluginType the type of plugin
* @param key properties key
* @param instance existing instance
*/
public <T> T createInstance(Class<T> pluginType, String key, T instance) {
if (instance != null) {
return instance;
}
String classname = get(key, null);
return createInstance(pluginType, classname);
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param pluginType the type of plugin
* @param classname the implementation class as per properties
*/
@SuppressWarnings("unchecked")
public <T> T createInstance(Class<T> pluginType, String classname) {
return classname == null ? null : (T) classLoadConfig.newInstance(classname);
}
}
@@ -1,178 +0,0 @@
package io.ebean.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
* Helper used to evaluate expressions such as ${CATALINA_HOME}.
* <p>
* The expressions can contain environment variables, system properties or JNDI
* properties. JNDI expressions take the form ${jndi:propertyName} where you
* substitute propertyName with the name of the jndi property you wish to
* evaluate.
* </p>
*/
final class PropertyExpression {
private static final Logger logger = LoggerFactory.getLogger(PropertyExpression.class);
/**
* Prefix for looking up JNDI Environment variable.
*/
private static final String JAVA_COMP_ENV = "java:comp/env/";
/**
* Used to detect the start of an expression.
*/
private static final String START = "${";
/**
* Used to detect the end of an expression.
*/
private static final String END = "}";
/**
* Specify the PropertyHolder.
*/
private PropertyExpression() {
}
/**
* Return the property value evaluating and replacing any expressions such as
* ${CATALINA_HOME}.
*/
static String eval(String val, PropertyMap map) {
if (val == null) {
return null;
}
int sp = val.indexOf(START);
if (sp > -1) {
int ep = val.indexOf(END, sp + 1);
if (ep > -1) {
return eval(val, sp, ep, map);
}
}
return val;
}
/**
* Convert the expression using JNDI, Environment variables, System Properties
* or existing an property in SystemProperties itself.
*/
private static String evaluateExpression(String exp, PropertyMap map) {
if (isJndiExpression(exp)) {
// JNDI property lookup...
String val = getJndiProperty(exp);
if (val != null) {
return val;
}
}
// check Environment Variables first
String val = System.getenv(exp);
if (val == null) {
// then check system properties
val = System.getProperty(exp);
}
if (val == null && map != null) {
// then check PropertyMap
val = map.get(exp);
}
if (val != null) {
return val;
} else {
// unable to evaluate yet... but maybe later based on the order
// in which properties are being set/loaded. You can use
// GlobalProperties.evaluateExpressions() to get any unresolved
// expressions to be evaluated
logger.debug("Unable to evaluate expression [{}]", exp);
return null;
}
}
private static String eval(String val, int sp, int ep, PropertyMap map) {
StringBuilder sb = new StringBuilder();
sb.append(val.substring(0, sp));
String cal = evalExpression(val, sp, ep, map);
sb.append(cal);
eval(val, ep + 1, sb, map);
return sb.toString();
}
private static void eval(String val, int startPos, StringBuilder sb, PropertyMap map) {
if (startPos < val.length()) {
int sp = val.indexOf(START, startPos);
if (sp > -1) {
// append what is between the last token and the new one (if startPos ==
// sp nothing gets added)
sb.append(val.substring(startPos, sp));
int ep = val.indexOf(END, sp + 1);
if (ep > -1) {
String cal = evalExpression(val, sp, ep, map);
sb.append(cal);
eval(val, ep + 1, sb, map);
return;
}
}
}
// append what is left...
sb.append(val.substring(startPos));
}
private static String evalExpression(String val, int sp, int ep, PropertyMap map) {
// trim off start and end ${ and }
String exp = val.substring(sp + START.length(), ep);
// evaluate the variable
String evaled = evaluateExpression(exp, map);
if (evaled != null) {
return evaled;
} else {
// unable to evaluate at this stage (maybe later)
return START + exp + END;
}
}
private static boolean isJndiExpression(String exp) {
return exp.startsWith("JNDI:") || exp.startsWith("jndi:");
}
/**
* Returns null if JNDI is not setup or if the property is not found.
*
* @param key the key of the JNDI Environment property including a JNDI: prefix.
*/
private static String getJndiProperty(String key) {
try {
// remove the JNDI: prefix
key = key.substring(5);
return (String) getJndiObject(key);
} catch (NamingException ex) {
return null;
}
}
/**
* Similar to getProperty but throws NamingException if JNDI is not setup or
* if the property is not found.
*/
private static Object getJndiObject(String key) throws NamingException {
InitialContext ctx = new InitialContext();
return ctx.lookup(JAVA_COMP_ENV + key);
}
}
@@ -1,168 +0,0 @@
package io.ebean.config;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
/**
* A map like structure of properties.
* <p/>
* Handles evaluation of expressions like ${home} and provides convenience methods for int, long and boolean.
*/
public final class PropertyMap implements Serializable {
private static final long serialVersionUID = 1L;
private final LinkedHashMap<String, String> map = new LinkedHashMap<>();
/**
* Return properties loaded from test-ebean.properties.
*/
public static Properties testProperties() {
PropertyMap propertyMap = PropertyMapLoader.loadTestProperties();
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
}
public static Properties defaultProperties() {
PropertyMap propertyMap = PropertyMapLoader.loadGlobalProperties();
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
}
public static boolean loadTestProperties() {
String disable = System.getProperty("disableTestProperties");
return (disable == null || !disable.equalsIgnoreCase("true"));
}
@Override
public String toString() {
return map.toString();
}
/**
* Return as standard Properties.
*/
public Properties asProperties() {
Properties properties = new Properties();
for (Entry<String, String> e : entrySet()) {
properties.put(e.getKey(), e.getValue());
}
return properties;
}
/**
* Go through all the properties and evaluate any expressions that have not
* been resolved.
*/
public void evaluateProperties() {
for (Entry<String, String> e : entrySet()) {
String key = e.getKey();
String val = e.getValue();
String eval = eval(val);
if (eval != null && !eval.equals(val)) {
put(key, eval);
}
}
}
/**
* Returns the value with expressions like ${home} evaluated using system properties and environment variables.
*/
public synchronized String eval(String val) {
return PropertyExpression.eval(val, this);
}
/**
* Return the boolean property value with a given default.
*/
public synchronized boolean getBoolean(String key, boolean defaultValue) {
String value = get(key);
if (value == null) {
return defaultValue;
} else {
return Boolean.parseBoolean(value);
}
}
/**
* Return the int property value with a given default.
*/
public synchronized int getInt(String key, int defaultValue) {
String value = get(key);
if (value == null) {
return defaultValue;
} else {
return Integer.parseInt(value);
}
}
/**
* Return the long property value with a given default.
*/
public synchronized long getLong(String key, long defaultValue) {
String value = get(key);
if (value == null) {
return defaultValue;
} else {
return Long.parseLong(value);
}
}
/**
* Return the string property value with a given default.
*/
public synchronized String get(String key, String defaultValue) {
String value = map.get(key.toLowerCase());
return value == null ? defaultValue : value;
}
/**
* Return the property value returning null if there is no value defined.
*/
public synchronized String get(String key) {
return map.get(key.toLowerCase());
}
/**
* Put all evaluating any expressions in the values.
*/
public synchronized void putEvalAll(Map<String, String> keyValueMap) {
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
putEval(entry.getKey(), entry.getValue());
}
}
/**
* Put a single key value evaluating any expressions in the value.
*/
public synchronized String putEval(String key, String value) {
value = PropertyExpression.eval(value, this);
return map.put(key.toLowerCase(), value);
}
/**
* Put a single key value with no expression evaluation.
*/
public synchronized String put(String key, String value) {
return map.put(key.toLowerCase(), value);
}
/**
* Remove an entry.
*/
public synchronized String remove(String key) {
return map.remove(key.toLowerCase());
}
/**
* Return the entries.
*/
public synchronized Set<Entry<String, String>> entrySet() {
return map.entrySet();
}
}
@@ -1,159 +0,0 @@
package io.ebean.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
/**
* Helper used to load the ebean.properties into a PropertyMap.
*/
final class PropertyMapLoader {
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
/**
* Load the <code>test-ebean.properties</code>.
*/
public static PropertyMap loadTestProperties() {
if (!PropertyMap.loadTestProperties()) {
return new PropertyMap();
}
return load(null, "test-ebean.properties");
}
/**
* Load the ebean.properties (and test-ebean.properties if present).
*/
public static PropertyMap loadGlobalProperties() {
boolean loadTestProperties = false;
String fileName = System.getenv("EBEAN_PROPS_FILE");
if (fileName == null) {
fileName = System.getProperty("ebean.props.file");
if (fileName == null) {
loadTestProperties = PropertyMap.loadTestProperties();
fileName = "ebean.properties";
}
}
PropertyMap map = load(null, fileName);
if (loadTestProperties) {
// load test properties if present in classpath
map = load(map, "test-ebean.properties");
}
return map;
}
/**
* Load the file returning the property map.
*
* @param p an existing property map to load into.
* @param fileName the name of the properties file to load.
*/
public static PropertyMap load(PropertyMap p, String fileName) {
InputStream is = findInputStream(fileName);
if (is == null) {
return p;
} else {
return load(p, is);
}
}
/**
* Load the InputStream returning the property map.
*
* @param p an existing property map to load into.
* @param in the InputStream of the properties file to load.
*/
public static PropertyMap load(PropertyMap p, InputStream in) {
Properties props = new Properties();
try {
props.load(in);
in.close();
return load(p, props);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static PropertyMap load(PropertyMap p, Properties props) {
if (p == null) {
p = new PropertyMap();
}
// put values in initially without any evaluation
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = ((String) entry.getKey()).toLowerCase();
String val = ((String) entry.getValue());
if (val != null) {
val = val.trim();
}
p.put(key, val);
}
p.evaluateProperties();
String otherProps = p.remove("load.properties");
if (otherProps == null) {
otherProps = p.remove("load.properties.override");
}
if (otherProps != null) {
otherProps = otherProps.replace("\\", "/");
InputStream is = findInputStream(otherProps);
if (is != null) {
logger.debug("loading properties from {}", otherProps);
load(p, is);
} else {
logger.error("load.properties " + otherProps + " not found.");
}
}
return p;
}
/**
* Find the input stream given the file name.
*/
private static InputStream findInputStream(String fileName) {
if (fileName == null) {
throw new NullPointerException("fileName is null?");
}
try {
File f = new File(fileName);
if (f.exists()) {
logger.debug("{} found in file system", fileName);
return new FileInputStream(f);
} else {
InputStream in = findInClassPath(fileName);
if (in != null) {
logger.debug("{} found in classpath", fileName);
}
return in;
}
} catch (FileNotFoundException ex) {
// already made the check so this
// should never be thrown
throw new RuntimeException(ex);
}
}
private static InputStream findInClassPath(String fileName) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
}
}
+456 -183
View File
@@ -2,17 +2,18 @@ package io.ebean.config;
import com.fasterxml.jackson.core.JsonFactory;
import io.ebean.EbeanServerFactory;
import io.ebean.PersistBatch;
import io.ebean.PersistenceContextScope;
import io.ebean.Platform;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.annotation.Encrypted;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
import io.ebean.config.dbplatform.DbType;
import io.ebean.dbmigration.MigrationRunner;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.properties.PropertiesLoader;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
import io.ebean.event.BeanPersistListener;
@@ -27,10 +28,13 @@ import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
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.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -80,12 +84,6 @@ public class ServerConfig {
*/
private String name = "db";
/**
* When false (default) H2 automatically uses DDL generate and run
* (i.e. assumes we are running tests using in memory h2).
*/
private boolean h2ProductionMode;
/**
* Typically configuration type objects that are passed by this ServerConfig
* to plugins. For example - IgniteConfiguration passed to Ignite plugin.
@@ -185,11 +183,6 @@ public class ServerConfig {
*/
private DatabasePlatform databasePlatform;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
private int databaseSequenceBatchSize = 20;
/**
* JDBC fetchSize hint when using findList. Defaults to 0 leaving it up to the JDBC driver.
*/
@@ -277,11 +270,29 @@ public class ServerConfig {
*/
private DataSource dataSource;
/**
* The read only data source (can be null).
*/
private DataSource readOnlyDataSource;
/**
* The data source config.
*/
private DataSourceConfig dataSourceConfig = new DataSourceConfig();
/**
* When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig.
* I believe this will default to true in some future release (as it has a nice performance benefit).
* <p>
* autoReadOnlyDataSource is an unfortunate name for this config option but I haven't come up with a better one.
*/
private boolean autoReadOnlyDataSource;
/**
* Optional configuration for a read only data source.
*/
private DataSourceConfig readOnlyDataSourceConfig = new DataSourceConfig();
/**
* The db migration config (migration resource path etc).
*/
@@ -309,16 +320,6 @@ public class ServerConfig {
*/
private String dataSourceJndiName;
/**
* The database boolean true value (typically either 1, T, or Y).
*/
private String databaseBooleanTrue;
/**
* The database boolean false value (typically either 0, F or N).
*/
private String databaseBooleanFalse;
/**
* The naming convention.
*/
@@ -345,9 +346,19 @@ public class ServerConfig {
private boolean updatesDeleteMissingChildren = true;
/**
* Database type configuration.
* Database platform configuration.
*/
private DbTypeConfig dbTypeConfig = new DbTypeConfig();
private PlatformConfig platformConfig = new PlatformConfig();
/**
* The UUID version to use.
*/
private UuidVersion uuidVersion = UuidVersion.VERSION4;
/**
* The UUID state file (for Version 1 UUIDs).
*/
private String uuidStateFile = "ebean-uuid.state";
private List<IdGenerator> idGenerators = new ArrayList<>();
private List<BeanFindController> findControllers = new ArrayList<>();
@@ -370,6 +381,8 @@ public class ServerConfig {
private ChangeLogRegister changeLogRegister;
private boolean changeLogAsync = true;
private ReadAuditLogger readAuditLogger;
private ReadAuditPrepare readAuditPrepare;
@@ -435,11 +448,46 @@ public class ServerConfig {
private String jodaLocalTimeMode;
/**
* Time to live for query plans - defaults to 5 minutes.
*/
private int queryPlanTTLSeconds = 60 * 5;
/**
* Set to true to globally disable L2 caching (typically for performance testing).
*/
private boolean disableL2Cache;
/**
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
* If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
*/
private boolean useJavaxValidationNotNull = true;
/**
* Generally we want to perform L2 cache notification in the background and not impact
* the performance of executing transactions.
*/
private boolean notifyL2CacheInForeground;
/**
* The time in millis used to determine when a query is alerted for being slow.
*/
private long slowQueryMillis;
/**
* The listener for processing slow query events.
*/
private SlowQueryListener slowQueryListener;
private ProfilingConfig profilingConfig = new ProfilingConfig();
/**
* Controls the default order by id setting of queries. See {@link Query#orderById(boolean)}
*/
private boolean defaultOrderById = false;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
@@ -447,6 +495,49 @@ public class ServerConfig {
}
/**
* Return the slow query time in millis.
*/
public long getSlowQueryMillis() {
return slowQueryMillis;
}
/**
* Set the slow query time in millis.
*/
public void setSlowQueryMillis(long slowQueryMillis) {
this.slowQueryMillis = slowQueryMillis;
}
/**
* Return the slow query event listener.
*/
public SlowQueryListener getSlowQueryListener() {
return slowQueryListener;
}
/**
* Set the slow query event listener.
*/
public void setSlowQueryListener(SlowQueryListener slowQueryListener) {
this.slowQueryListener = slowQueryListener;
}
/**
* Sets the default orderById setting for queries.
*/
public void setDefaultOrderById(boolean defaultOrderById) {
this.defaultOrderById = defaultOrderById;
}
/**
* Returns the default orderById setting for queries.
*/
public boolean isDefaultOrderById() {
return defaultOrderById;
}
/**
* Put a service object into configuration such that it can be passed to a plugin.
* <p>
@@ -527,27 +618,6 @@ public class ServerConfig {
this.name = name;
}
/**
* Return true if H2 should be used in production mode.
* <p>
* Otherwise it is assumed we are using H2 for testing and DDL generate and run is turned on.
* </p>
*/
public boolean isH2ProductionMode() {
return h2ProductionMode;
}
/**
* Set to true for H2 to be used in production mode.
* <p>
* Do this when we want to use H2 and not have the DDL generation and run automatically turned on.
* Otherwise it is assumed we are using H2 for testing purposes.
* </p>
*/
public void setH2ProductionMode(boolean h2ProductionMode) {
this.h2ProductionMode = h2ProductionMode;
}
/**
* Return the container / clustering configuration.
* <p/>
@@ -854,7 +924,7 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
@@ -955,6 +1025,20 @@ public class ServerConfig {
this.changeLogIncludeInserts = changeLogIncludeInserts;
}
/**
* Return true (default) if the changelog should be written async.
*/
public boolean isChangeLogAsync() {
return changeLogAsync;
}
/**
* Sets if the changelog should be written async (default = true).
*/
public void setChangeLogAsync(boolean changeLogAsync) {
this.changeLogAsync = changeLogAsync;
}
/**
* Return the ReadAuditLogger to use.
*/
@@ -990,6 +1074,20 @@ public class ServerConfig {
this.readAuditPrepare = readAuditPrepare;
}
/**
* Return the configuration for profiling.
*/
public ProfilingConfig getProfilingConfig() {
return profilingConfig;
}
/**
* Set the configuration for profiling.
*/
public void setProfilingConfig(ProfilingConfig profilingConfig) {
this.profilingConfig = profilingConfig;
}
/**
* Return the DB migration configuration.
*/
@@ -1008,14 +1106,14 @@ public class ServerConfig {
* Return the Geometry SRID.
*/
public int getGeometrySRID() {
return dbTypeConfig.getGeometrySRID();
return platformConfig.getGeometrySRID();
}
/**
* Set the Geometry SRID.
*/
public void setGeometrySRID(int geometrySRID) {
dbTypeConfig.setGeometrySRID(geometrySRID);
platformConfig.setGeometrySRID(geometrySRID);
}
/**
@@ -1301,6 +1399,30 @@ public class ServerConfig {
this.namingConvention = namingConvention;
}
/**
* Return true if all DB column and table names should use quoted identifiers.
*/
public boolean isAllQuotedIdentifiers() {
return platformConfig.isAllQuotedIdentifiers();
}
/**
* Set to true if all DB column and table names should use quoted identifiers.
*/
public void setAllQuotedIdentifiers(boolean allQuotedIdentifiers) {
platformConfig.setAllQuotedIdentifiers(allQuotedIdentifiers);
if (allQuotedIdentifiers) {
adjustNamingConventionForAllQuoted();
}
}
private void adjustNamingConventionForAllQuoted() {
if (namingConvention instanceof UnderscoreNamingConvention) {
// we need to use matching naming convention
this.namingConvention = new MatchingNamingConvention();
}
}
/**
* Return true if this EbeanServer is a Document store only instance (has no JDBC DB).
*/
@@ -1371,6 +1493,28 @@ public class ServerConfig {
this.dataSource = dataSource;
}
/**
* Return the read only DataSource.
*/
public DataSource getReadOnlyDataSource() {
return readOnlyDataSource;
}
/**
* Set the read only DataSource.
* <p>
* Note that the DataSource is expected to use AutoCommit true mode avoiding the need
* for explicit commit (or rollback).
* </p>
* <p>
* This read only DataSource will be used for implicit query only transactions. It is not
* used if the transaction is created explicitly or if the query is an update or delete query.
* </p>
*/
public void setReadOnlyDataSource(DataSource readOnlyDataSource) {
this.readOnlyDataSource = readOnlyDataSource;
}
/**
* Return the configuration to build a DataSource using Ebean's own DataSource
* implementation.
@@ -1387,6 +1531,42 @@ public class ServerConfig {
this.dataSourceConfig = dataSourceConfig;
}
/**
* Return true if Ebean should create a DataSource for use with implicit read only transactions.
*/
public boolean isAutoReadOnlyDataSource() {
return autoReadOnlyDataSource;
}
/**
* Set to true if Ebean should create a DataSource for use with implicit read only transactions.
*/
public void setAutoReadOnlyDataSource(boolean autoReadOnlyDataSource) {
this.autoReadOnlyDataSource = autoReadOnlyDataSource;
}
/**
* Return the configuration for the read only DataSource.
* <p>
* This is only used if autoReadOnlyDataSource is true.
* </p>
* <p>
* The driver, url, username and password default to the configuration for the main DataSource if they are not
* set on this configuration. This means there is actually no need to set any configuration here and we only
* set configuration for url, username and password etc if it is different from the main DataSource.
* </p>
*/
public DataSourceConfig getReadOnlyDataSourceConfig() {
return readOnlyDataSourceConfig;
}
/**
* Set the configuration for the read only DataSource.
*/
public void setReadOnlyDataSourceConfig(DataSourceConfig readOnlyDataSourceConfig) {
this.readOnlyDataSourceConfig = readOnlyDataSourceConfig;
}
/**
* Return the JNDI name of the DataSource to use.
*/
@@ -1452,7 +1632,7 @@ public class ServerConfig {
* </p>
*/
public String getDatabaseBooleanTrue() {
return databaseBooleanTrue;
return platformConfig.getDatabaseBooleanTrue();
}
/**
@@ -1465,7 +1645,7 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseBooleanTrue(String databaseTrue) {
this.databaseBooleanTrue = databaseTrue;
platformConfig.setDatabaseBooleanTrue(databaseTrue);
}
/**
@@ -1478,7 +1658,7 @@ public class ServerConfig {
* </p>
*/
public String getDatabaseBooleanFalse() {
return databaseBooleanFalse;
return platformConfig.getDatabaseBooleanFalse();
}
/**
@@ -1491,14 +1671,14 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseBooleanFalse(String databaseFalse) {
this.databaseBooleanFalse = databaseFalse;
this.platformConfig.setDatabaseBooleanFalse(databaseFalse);
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return databaseSequenceBatchSize;
return platformConfig.getDatabaseSequenceBatchSize();
}
/**
@@ -1517,7 +1697,7 @@ public class ServerConfig {
* </p>
*/
public void setDatabaseSequenceBatch(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
this.platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
@@ -1544,7 +1724,7 @@ public class ServerConfig {
* that you don't have access to.
* </p>
* <p>
* Values are oracle, h2, postgres, mysql, mssqlserver2005.
* Values are oracle, h2, postgres, mysql, sqlserver16, sqlserver17.
* </p>
*/
public void setDatabasePlatformName(String databasePlatformName) {
@@ -1567,11 +1747,20 @@ public class ServerConfig {
*/
public void setDatabasePlatform(DatabasePlatform databasePlatform) {
this.databasePlatform = databasePlatform;
if (!h2ProductionMode && databasePlatform != null && databasePlatform.isPlatform(Platform.H2)) {
// we are using H2 to run tests so turn on DDL generation and run
this.ddlGenerate = true;
this.ddlRun = true;
}
}
/**
* Return the preferred DB platform IdType.
*/
public IdType getIdType() {
return platformConfig.getIdType();
}
/**
* Set the preferred DB platform IdType.
*/
public void setIdType(IdType idType) {
this.platformConfig.setIdType(idType);
}
/**
@@ -1684,15 +1873,50 @@ public class ServerConfig {
/**
* Return the configuration for DB types (such as UUID and custom mappings).
*/
public DbTypeConfig getDbTypeConfig() {
return dbTypeConfig;
public PlatformConfig getPlatformConfig() {
return platformConfig;
}
/**
* Set the configuration for DB platform (such as UUID and custom mappings).
*/
public void setPlatformConfig(PlatformConfig platformConfig) {
this.platformConfig = platformConfig;
}
/**
* Set the DB type used to store UUID.
*/
public void setDbUuid(DbUuid dbUuid) {
this.dbTypeConfig.setDbUuid(dbUuid);
public void setDbUuid(PlatformConfig.DbUuid dbUuid) {
this.platformConfig.setDbUuid(dbUuid);
}
/**
* Returns the UUID version mode.
*/
public UuidVersion getUuidVersion() {
return uuidVersion;
}
/**
* Sets the UUID version mode.
*/
public void setUuidVersion(UuidVersion uuidVersion) {
this.uuidVersion = uuidVersion;
}
/**
* Return the UUID state file.
*/
public String getUuidStateFile() {
return uuidStateFile;
}
/**
* Set the UUID state file.
*/
public void setUuidStateFile(String uuidStateFile) {
this.uuidStateFile = uuidStateFile;
}
/**
@@ -1732,6 +1956,16 @@ public class ServerConfig {
this.durationWithNanos = durationWithNanos;
}
/**
* Set to true to run DB migrations on server start.
* <p>
* This is the same as serverConfig.getMigrationConfig().setRunMigration(). We have added this method here
* as it is often the only thing we need to configure for migrations.
*/
public void setRunMigration(boolean runMigration){
migrationConfig.setRunMigration(runMigration);
}
/**
* Set to true to generate the "create all" DDL on startup.
* <p>
@@ -2120,7 +2354,7 @@ public class ServerConfig {
* @param platform Optionally specify the platform this mapping should apply to.
*/
public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
dbTypeConfig.addCustomMapping(type, columnDefinition, platform);
platformConfig.addCustomMapping(type, columnDefinition, platform);
}
/**
@@ -2140,7 +2374,7 @@ public class ServerConfig {
* @param columnDefinition The column definition that should be used
*/
public void addCustomMapping(DbType type, String columnDefinition) {
dbTypeConfig.addCustomMapping(type, columnDefinition);
platformConfig.addCustomMapping(type, columnDefinition);
}
/**
@@ -2409,7 +2643,8 @@ public class ServerConfig {
* Load settings from ebean.properties.
*/
public void loadFromProperties() {
loadFromProperties(PropertyMap.defaultProperties());
this.properties = PropertiesLoader.load();
configureFromProperties();
}
/**
@@ -2417,24 +2652,46 @@ public class ServerConfig {
*/
public void loadFromProperties(Properties properties) {
// keep the properties used for configuration so that these are available for plugins
this.properties = properties;
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
loadSettings(p);
this.properties = PropertiesLoader.eval(properties);
configureFromProperties();
}
/**
* Load the settings from the given properties
*/
private void configureFromProperties() {
List<AutoConfigure> autoConfigures = autoConfiguration();
loadSettings(new PropertiesWrapper("ebean", name, properties, classLoadConfig));
for (AutoConfigure autoConfigure : autoConfigures) {
autoConfigure.postConfigure(this);
}
}
/**
* Use a 'plugin' to provide automatic configuration. Intended for automatic testing
* configuration with Docker containers via ebean-test-config.
*/
private List<AutoConfigure> autoConfiguration() {
List<AutoConfigure> list = new ArrayList<>();
for (AutoConfigure autoConfigure : serviceLoad(AutoConfigure.class)) {
autoConfigure.preConfigure(this);
list.add(autoConfigure);
}
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() {
Properties properties = PropertyMap.testProperties();
if (!properties.isEmpty()) {
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
loadSettings(p);
}
// do nothing now ... as we always try to read test configuration and that should only
}
/**
@@ -2444,34 +2701,6 @@ public class ServerConfig {
return properties;
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param properties the properties
* @param pluginType the type of plugin
* @param key properties key
* @param instance existing instance
*/
protected <T> T createInstance(PropertiesWrapper properties, Class<T> pluginType, String key, T instance) {
if (instance != null) {
return instance;
}
String classname = properties.get(key, null);
return createInstance(pluginType, classname);
}
/**
* Return the instance to use (can be null) for the given plugin.
*
* @param pluginType the type of plugin
* @param classname the implementation class as per properties
*/
@SuppressWarnings("unchecked")
protected <T> T createInstance(Class<T> pluginType, String classname) {
return classname == null ? null : (T) classLoadConfig.newInstance(classname);
}
/**
* loads the data source settings to preserve existing behaviour. IMHO, if someone has set the datasource config already,
* they don't want the settings to be reloaded and reset. This allows a descending class to override this behaviour and prevent it
@@ -2481,6 +2710,7 @@ public class ServerConfig {
*/
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.properties, name);
readOnlyDataSourceConfig.loadSettings(p.properties, name);
}
/**
@@ -2502,8 +2732,12 @@ public class ServerConfig {
*/
protected void loadSettings(PropertiesWrapper p) {
profilingConfig.loadSettings(p, name);
migrationConfig.loadSettings(p, name);
platformConfig.loadSettings(p);
if (platformConfig.isAllQuotedIdentifiers()) {
adjustNamingConventionForAllQuoted();
}
namingConvention = createNamingConvention(p, namingConvention);
if (namingConvention != null) {
namingConvention.loadFromProperties(p);
@@ -2523,28 +2757,28 @@ public class ServerConfig {
}
loadDocStoreSettings(p);
int srid = p.getInt("geometrySRID", 0);
if (srid > 0) {
dbTypeConfig.setGeometrySRID(srid);
}
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform", databasePlatform);
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager", encryptKeyManager);
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
encryptor = createInstance(p, Encryptor.class, "encryptor", encryptor);
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt", dbEncrypt);
currentUserProvider = p.createInstance(CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
databasePlatform = p.createInstance(DatabasePlatform.class, "databasePlatform", databasePlatform);
encryptKeyManager = p.createInstance(EncryptKeyManager.class, "encryptKeyManager", encryptKeyManager);
encryptDeployManager = p.createInstance(EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
encryptor = p.createInstance(Encryptor.class, "encryptor", encryptor);
dbEncrypt = p.createInstance(DbEncrypt.class, "dbEncrypt", dbEncrypt);
dbOffline = p.getBoolean("dbOffline", dbOffline);
serverCachePlugin = createInstance(p, ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
serverCachePlugin = p.createInstance(ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
if (packages != null) {
String packagesProp = p.get("search.packages", p.get("packages", null));
@@ -2573,6 +2807,7 @@ public class ServerConfig {
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope", "TRANSACTION"));
changeLogAsync = p.getBoolean("changeLogAsync", changeLogAsync);
changeLogIncludeInserts = p.getBoolean("changeLogIncludeInserts", changeLogIncludeInserts);
expressionEqualsWithNullAsNoop = p.getBoolean("expressionEqualsWithNullAsNoop", expressionEqualsWithNullAsNoop);
expressionNativeIlike = p.getBoolean("expressionNativeIlike", expressionNativeIlike);
@@ -2584,18 +2819,12 @@ public class ServerConfig {
dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName);
jdbcFetchSizeFindEach = p.getInt("jdbcFetchSizeFindEach", jdbcFetchSizeFindEach);
jdbcFetchSizeFindList = p.getInt("jdbcFetchSizeFindList", jdbcFetchSizeFindList);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
defaultOrderById = p.getBoolean("defaultOrderById", defaultOrderById);
uuidVersion = p.getEnum(UuidVersion.class, "uuidVersion", uuidVersion);
uuidStateFile = p.get("uuidStateFile", uuidStateFile);
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
if (dbUuid != null) {
dbTypeConfig.setDbUuid(dbUuid);
}
if (p.getBoolean("uuidStoreAsBinary", false)) {
dbTypeConfig.setDbUuid(DbUuid.BINARY);
}
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
@@ -2610,19 +2839,33 @@ public class ServerConfig {
jsonDateTime = JsonConfig.DateTime.MILLIS;
}
h2ProductionMode = p.getBoolean("h2ProductionMode", h2ProductionMode);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
ddlInitSql = p.get("ddl.initSql", ddlInitSql);
ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
// read tenant-configuration from config:
// tenant.mode = NONE | DB | SCHEMA | CATALOG | PARTITION
String mode = p.get("tenant.mode");
if (mode != null) {
for (TenantMode value : TenantMode.values()) {
if (value.name().equalsIgnoreCase(mode)) {
tenantMode = value;
break;
}
}
}
currentTenantProvider = p.createInstance(CurrentTenantProvider.class, "tenant.currentTenantProvider", currentTenantProvider);
tenantCatalogProvider = p.createInstance(TenantCatalogProvider.class, "tenant.catalogProvider", tenantCatalogProvider);
tenantSchemaProvider = p.createInstance(TenantSchemaProvider.class, "tenant.schemaProvider", tenantSchemaProvider);
tenantPartitionColumn = p.get("tenant.partitionColumn", tenantPartitionColumn);
classes = getClasses(p);
}
private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention", null);
NamingConvention nc = properties.createInstance(NamingConvention.class, "namingconvention", null);
return (nc != null) ? nc : namingConvention;
}
@@ -2641,10 +2884,9 @@ public class ServerConfig {
List<Class<?>> classes = new ArrayList<>();
String[] split = classNames.split("[ ,;]");
for (String aSplit : split) {
String cn = aSplit.trim();
if (!cn.isEmpty() && !"class".equalsIgnoreCase(cn)) {
String[] split = StringHelper.splitNames(classNames);
for (String cn : split) {
if (!"class".equalsIgnoreCase(cn)) {
try {
classes.add(Class.forName(cn));
} catch (ClassNotFoundException e) {
@@ -2658,16 +2900,16 @@ public class ServerConfig {
private List<String> getSearchJarsPackages(String searchPackages) {
List<String> hitList = new ArrayList<>();
if (searchPackages != null) {
String[] entries = StringHelper.splitNames(searchPackages);
String[] entries = searchPackages.split("[ ,;]");
for (String entry : entries) {
hitList.add(entry.trim());
}
List<String> hitList = new ArrayList<>(entries.length);
Collections.addAll(hitList, entries);
return hitList;
} else {
return new ArrayList<>();
}
return hitList;
}
/**
@@ -2750,6 +2992,58 @@ public class ServerConfig {
this.disableL2Cache = disableL2Cache;
}
/**
* Returns if we use javax.validation.constraints.NotNull
*/
public boolean isUseJavaxValidationNotNull() {
return useJavaxValidationNotNull;
}
/**
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code>
* with respect to generating a <code>NOT NULL</code> column.
* <p>
* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL.
* Set this to <code>false</code> and the javax NotNull annotation is effectively ignored (and
* we instead use Ebean's own NotNull annotation or JPA Column(nullable=false) annotation.
*/
public void setUseJavaxValidationNotNull(boolean useJavaxValidationNotNull) {
this.useJavaxValidationNotNull = useJavaxValidationNotNull;
}
/**
* Return true if L2 cache notification should run in the foreground.
*/
public boolean isNotifyL2CacheInForeground() {
return notifyL2CacheInForeground;
}
/**
* Set this to true to run L2 cache notification in the foreground.
* <p>
* In general we don't want to do that as when we use a distributed cache (like Ignite, Hazelcast etc)
* we are making network calls and we prefer to do this in background and not impact the response time
* of the executing transaction.
* </p>
*/
public void setNotifyL2CacheInForeground(boolean notifyL2CacheInForeground) {
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
}
/**
* Return the query plan time to live.
*/
public int getQueryPlanTTLSeconds() {
return queryPlanTTLSeconds;
}
/**
* Set the query plan time to live.
*/
public void setQueryPlanTTLSeconds(int queryPlanTTLSeconds) {
this.queryPlanTTLSeconds = queryPlanTTLSeconds;
}
/**
* Run the DB migration against the DataSource.
*/
@@ -2762,50 +3056,29 @@ public class ServerConfig {
}
/**
* Specify how UUID is stored.
* Create a new PlatformConfig based of the one held but with overridden properties by reading
* properties with the given path and prefix.
* <p>
* Typically used in Db Migration generation for many platform targets that might have different
* configuration for IdType, UUID, quoted identifiers etc.
* </p>
*
* @param propertiesPath The properties path used for loading and setting properties
* @param platformPrefix The prefix used for loading and setting properties
* @return A copy of the PlatformConfig with overridden properties
*/
public enum DbUuid {
public PlatformConfig newPlatformConfig(String propertiesPath, String platformPrefix) {
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to VARCHAR(40).
*/
AUTO_VARCHAR(true, false),
PropertiesWrapper p = new PropertiesWrapper(propertiesPath, platformPrefix, properties, classLoadConfig);
/**
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16).
*/
AUTO_BINARY(true, true),
PlatformConfig config = new PlatformConfig(platformConfig);
config.loadSettings(p);
return config;
}
/**
* Store using DB VARCHAR(40).
*/
VARCHAR(false, false),
/**
* Store using DB BINARY(16).
*/
BINARY(false, true);
boolean nativeType;
boolean binary;
DbUuid(boolean nativeType, boolean binary) {
this.nativeType = nativeType;
this.binary = binary;
}
/**
* Return true if native UUID type is preferred.
*/
public boolean useNativeType() {
return nativeType;
}
/**
* Return true if BINARY(16) storage is preferred over VARCHAR(40).
*/
public boolean useBinary() {
return binary;
}
public enum UuidVersion {
VERSION4,
VERSION1,
VERSION1RND
}
}
@@ -0,0 +1,59 @@
package io.ebean.config;
import io.ebean.bean.ObjectGraphNode;
/**
* Slow query event.
*/
public class SlowQueryEvent {
private final String sql;
private final long timeMillis;
private final int rowCount;
private final ObjectGraphNode originNode;
/**
* Construct with the SQL and execution time in millis.
*/
public SlowQueryEvent(String sql, long timeMillis, int rowCount, ObjectGraphNode originNode) {
this.sql = sql;
this.timeMillis = timeMillis;
this.rowCount = rowCount;
this.originNode = originNode;
}
/**
* Return the SQL for the slow query.
*/
public String getSql() {
return sql;
}
/**
* Return the execution time in millis.
*/
public long getTimeMillis() {
return timeMillis;
}
/**
* Return the total row count associated with the query.
*/
public int getRowCount() {
return rowCount;
}
/**
* Return the origin point for the root query.
* <p>
* Typically the <code>originNode.getOriginQueryPoint().getFirstStackElement()</code> provides the stack line that
* shows the code that invoked the query.
* </p>
*/
public ObjectGraphNode getOriginNode() {
return originNode;
}
}
@@ -0,0 +1,13 @@
package io.ebean.config;
/**
* Listener for slow query events.
*/
@FunctionalInterface
public interface SlowQueryListener {
/**
* Process a slow query event.
*/
void process(SlowQueryEvent event);
}
@@ -16,5 +16,5 @@ public interface TenantDataSourceProvider {
/**
* Shutdown all the DataSources.
*/
default void shutdown(boolean deregisterDriver) {};
default void shutdown(boolean deregisterDriver) {}
}
+22 -6
View File
@@ -8,32 +8,40 @@ public enum TenantMode {
/**
* No multi-tenancy.
*/
NONE(false),
NONE(false, true),
/**
* Each Tenant has their own Database (javax.sql.DataSource)
*/
DB(true),
DB(true, false),
/**
* Each Tenant has their own Database schema.
*/
SCHEMA(true),
SCHEMA(true, false),
/**
* Each Tenant has their own Database but with in connection pool
*/
CATALOG(true),
CATALOG(true, false),
/**
* Tenants share tables but have a discriminator/partition column that partitions the data.
*/
PARTITION(false);
PARTITION(false, true),
/**
* Each Tenant has their own Database (javax.sql.DataSource), and there is also one master-database
* (that holds configuration e.g.)
*/
DB_WITH_MASTER(true, true);
boolean dynamicDataSource;
boolean ddlEnabled;
TenantMode(boolean dynamicDataSource) {
TenantMode(boolean dynamicDataSource, boolean ddlEnabled) {
this.dynamicDataSource = dynamicDataSource;
this.ddlEnabled = ddlEnabled;
}
/**
@@ -42,4 +50,12 @@ public enum TenantMode {
public boolean isDynamicDataSource() {
return dynamicDataSource;
}
/**
* Returns true, if DDL is enabled.
*/
public boolean isDdlEnabled() {
return ddlEnabled;
}
}
@@ -2,13 +2,11 @@ package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.CustomDbTypeMapping;
import io.ebean.config.DbTypeConfig;
import io.ebean.PersistBatch;
import io.ebean.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlHandler;
import io.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import io.ebean.config.PlatformConfig;
import io.ebean.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,6 +62,13 @@ public class DatabasePlatform {
*/
protected String closeQuote = "\"";
protected String concatOperator = "||";
/**
* When set to true all db column names and table names use quoted identifiers.
*/
protected boolean allQuotedIdentifiers;
/**
* For limit/offset, row_number etc limiting of SQL queries.
*/
@@ -94,6 +99,10 @@ public class DatabasePlatform {
*/
protected DbIdentity dbIdentity = new DbIdentity();
protected boolean sequenceBatchMode = true;
protected int sequenceBatchSize = 20;
/**
* The history support for this database platform.
*/
@@ -136,9 +145,25 @@ public class DatabasePlatform {
private static final char BACK_TICK = '`';
/**
* The like clause. Can be overridden to disable default escape character.
* The non-escaped like clause (to stop slash being escaped on some platforms).
* Used for the 'raw like' expression but not for startsWith, endsWith and contains expressions.
*/
protected String likeClause = "like ?";
protected String likeClauseRaw = "like ? escape''";
/**
* Escaped like clause for startsWith, endsWith and contains.
*/
protected String likeClauseEscaped = "like ? escape'|'";
/**
* Escape character used for startsWith, endsWith and contains.
*/
protected char likeEscapeChar = '|';
/**
* Characters escaped for startsWith, endsWith and contains.
*/
protected char[] likeSpecialCharacters = { '%', '_', '|' };
protected DbEncrypt dbEncrypt;
@@ -157,8 +182,6 @@ public class DatabasePlatform {
*/
protected PersistBatch persistBatchOnCascade = PersistBatch.ALL;
protected PlatformDdl platformDdl;
/**
* The maximum length of table names - used specifically when derived
* default table names for intersection tables.
@@ -174,8 +197,6 @@ public class DatabasePlatform {
protected boolean supportsNativeIlike;
protected SqlExceptionTranslator exceptionTranslator = new SqlCodeTranslator();
protected char[] specialLikeCharacters = { '%', '_' };
/**
* Instantiates a new database platform.
@@ -190,10 +211,20 @@ public class DatabasePlatform {
return exceptionTranslator.translate(message, e);
}
/**
* Configure the platform given the server configuration.
*/
public void configure(PlatformConfig config) {
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
configureIdType(config.getIdType());
configure(config, config.isAllQuotedIdentifiers());
}
/**
* Configure UUID Storage etc based on ServerConfig settings.
*/
public void configure(DbTypeConfig config) {
protected void configure(PlatformConfig config, boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
addGeoTypes(config.getGeometrySRID());
configureIdType(config.getIdType());
dbTypeMap.config(nativeUuidType, config.getDbUuid());
@@ -226,7 +257,7 @@ public class DatabasePlatform {
* Return true if this matches the given platform.
*/
public boolean isPlatform(Platform platform) {
return this.platform.equals(platform);
return this.platform == platform;
}
/**
@@ -246,6 +277,20 @@ public class DatabasePlatform {
return platform.name().toLowerCase();
}
/**
* Return true if we are using Sequence batch mode rather than STEP.
*/
public boolean isSequenceBatchMode() {
return sequenceBatchMode;
}
/**
* Set to false to not use sequence batch mode but instead STEP mode.
*/
public void setSequenceBatchMode(boolean sequenceBatchMode) {
this.sequenceBatchMode = sequenceBatchMode;
}
/**
* Return true if this database platform supports native ILIKE expression.
*/
@@ -270,20 +315,6 @@ public class DatabasePlatform {
return maxConstraintNameLength;
}
/**
* Return the platform specific DDL.
*/
public PlatformDdl getPlatformDdl() {
return platformDdl;
}
/**
* Create and return a DDL handler for generating DDL scripts.
*/
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
return platformDdl.createDdlHandler(serverConfig);
}
/**
* Return true if the JDBC driver does not allow additional queries to execute
* when a resultSet is being 'streamed' as is the case with findEach() etc.
@@ -301,10 +332,10 @@ public class DatabasePlatform {
* @param be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
* @param batchSize the number of sequences that should be loaded
*/
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return null;
}
@@ -350,6 +381,13 @@ public class DatabasePlatform {
this.historySupport = historySupport;
}
/**
* So no except for Postgres and CockroachDB.
*/
public boolean isNativeArrayType() {
return false;
}
/**
* Return true if the DB supports native UUID.
*/
@@ -403,8 +441,6 @@ public class DatabasePlatform {
/**
* Return the close quote for quoted identifiers.
*
* @return the close quote
*/
public String getCloseQuote() {
return closeQuote;
@@ -412,17 +448,20 @@ public class DatabasePlatform {
/**
* Return the open quote for quoted identifiers.
*
* @return the open quote
*/
public String getOpenQuote() {
return openQuote;
}
/**
* Return the DB concat operator.
*/
public String getConcatOperator() {
return concatOperator;
}
/**
* Return the JDBC type used to store booleans.
*
* @return the boolean db type
*/
public int getBooleanDbType() {
return booleanDbType;
@@ -485,6 +524,13 @@ public class DatabasePlatform {
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
}
/**
* Normally not needed - overridden in CockroachPlatform.
*/
public boolean isDdlCommitOnCreateIndex() {
return false;
}
/**
* Return the DB identity/sequence features for this platform.
*
@@ -535,24 +581,33 @@ public class DatabasePlatform {
* naming rules.
* </p>
*
* @param dbName the db name
* @return the string
* @param dbName the db table or column name
* @return the db table or column name with potentially platform specific quoted identifiers
*/
public String convertQuotedIdentifiers(String dbName) {
// Ignore null values e.g. schema name or catalog
if (dbName != null && !dbName.isEmpty()) {
if (dbName.charAt(0) == BACK_TICK) {
if (dbName.charAt(dbName.length() - 1) == BACK_TICK) {
String quotedName = getOpenQuote();
quotedName += dbName.substring(1, dbName.length() - 1);
quotedName += getCloseQuote();
return quotedName;
return openQuote + dbName.substring(1, dbName.length() - 1) + closeQuote;
} else {
logger.error("Missing backquote on [" + dbName + "]");
}
} else if (allQuotedIdentifiers) {
return openQuote + dbName + closeQuote;
}
}
return dbName;
}
/**
* Remove quoted identifier quotes from the table or column name if present.
*/
public String unQuote(String dbName) {
if (dbName != null && !dbName.isEmpty()) {
if (dbName.startsWith(openQuote)) {
// trim off the open and close quotes
return dbName.substring(1, dbName.length()-1);
}
}
return dbName;
@@ -573,6 +628,14 @@ public class DatabasePlatform {
return sql;
}
/**
* For update hint on the FROM clause (SQL server only).
*/
public String fromForUpdate(Query.ForUpdate forUpdateMode) {
// return null except for sql server
return null;
}
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// silently assume the database does not support the "for update" clause.
logger.info("it seems your database does not support the 'for update' clause");
@@ -584,8 +647,8 @@ public class DatabasePlatform {
* <p>
* This may include an escape clause to disable a default escape character.
*/
public String getLikeClause() {
return likeClause;
public String getLikeClause(boolean rawLikeExpression) {
return rawLikeExpression ? likeClauseRaw : likeClauseEscaped;
}
/**
@@ -605,18 +668,7 @@ public class DatabasePlatform {
try {
return tables.next();
} finally {
close(tables);
}
}
/**
* Close the resultSet.
*/
protected void close(ResultSet resultSet) {
try {
resultSet.close();
} catch (SQLException e) {
logger.error("Error closing resultSet", e);
JdbcClose.close(tables);
}
}
@@ -628,7 +680,7 @@ public class DatabasePlatform {
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
boolean escaped = false;
for (char escapeChar: specialLikeCharacters) {
for (char escapeChar: likeSpecialCharacters) {
if (ch == escapeChar) {
if (sb == null) {
sb = new StringBuilder(value.substring(0, i));
@@ -648,8 +700,8 @@ public class DatabasePlatform {
return sb.toString();
}
}
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('\\').append(ch);
sb.append(likeEscapeChar).append(ch);
}
}
@@ -1,8 +1,13 @@
package io.ebean.config.dbplatform;
import java.sql.Types;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.bind.DatatypeConverter;
import io.ebean.annotation.DbDefault;
/**
* DB Column default values mapping to database platform specific literals.
*/
@@ -22,6 +27,13 @@ public class DbDefaultValue {
* The key for the NOW / current timestamp.
*/
public static final String NOW = "now";
/**
* The 'null' literal.
*/
public static final String NULL = "null";
protected Map<String, String> map = new LinkedHashMap<>();
@@ -63,8 +75,128 @@ public class DbDefaultValue {
if (dbDefaultLiteral == null) {
return null;
}
if (dbDefaultLiteral.startsWith("$RAW:")) {
return dbDefaultLiteral.substring(5);
}
String val = map.get(dbDefaultLiteral);
return val != null ? val : dbDefaultLiteral;
}
/**
* This method checks & convert the {@link DbDefault#value()} to a valid SQL literal.
*
* This is mainly to quote string literals and verify integer/dates for correctness.
* <p>
* Note: There are some special cases:
* </p>
* <ul>
* <li>Normal Quoting: <code>@DbDefault("User's default")</code> on a String propery
* returns: <code>default 'User''s default'</code><br/>
* (the same on an integer property will throw a NumberFormatException)</li>
* <li>Special case null: <code>@DbDefault("null")</code> will return this: <code>default null</code><br/>
* If you need really the String "null", you have to specify <code>@DbDefault("'null'")</code>
* which gives you the <code>default 'null'</code> statement.</li>
* <li>Any statement, that begins and ends with single quote will not be checked or get quoted again.</li>
* <li>A statement that begins with "$RAW:", e.g <code>@DbDefault("$RAW:N'SANDNES'")</code> will lead to
* a <code>default N'SANDNES'</code> in DDL. Note that this is platform specific!</li>
* </ul>
*/
public static String toSqlLiteral(String defaultValue, Class<?> propertyType, int sqlType) {
if (propertyType == null
|| defaultValue == null
|| NULL.equals(defaultValue)
|| (defaultValue.startsWith("'") && defaultValue.endsWith("'"))
|| (defaultValue.startsWith("$RAW:"))) {
return defaultValue;
}
if (Boolean.class.isAssignableFrom(propertyType) || Boolean.TYPE.isAssignableFrom(propertyType)) {
return toBooleanLiteral(defaultValue);
}
if (Number.class.isAssignableFrom(propertyType)
|| Byte.TYPE.equals(propertyType)
|| Short.TYPE.equals(propertyType)
|| Integer.TYPE.equals(propertyType)
|| Long.TYPE.equals(propertyType)
|| Float.TYPE.equals(propertyType)
|| Double.TYPE.equals(propertyType)
|| (propertyType.isEnum() && sqlType == Types.INTEGER)) {
Double.valueOf(defaultValue); // verify if it is a number
return defaultValue;
}
// check if it is a date/time - in all other cases return quoted defaultValue
switch (sqlType) {
// date
case Types.DATE:
return toDateLiteral(defaultValue);
// time
case Types.TIME:
case Types.TIME_WITH_TIMEZONE:
return toTimeLiteral(defaultValue);
// timestamp
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
return toDateTimeLiteral(defaultValue);
default:
return toTextLiteral(defaultValue); // do not check other datatypes
}
}
/**
* Checks if specified value is either 'true' or 'false'. The literal is translated later.
*/
private static String toBooleanLiteral(String value) {
if (DbDefaultValue.FALSE.equals(value) || DbDefaultValue.TRUE.equals(value)) {
return value;
}
throw new IllegalArgumentException("'" + value + "' is not a valid value for boolean");
}
/**
* This adds single qoutes around the <code>value</code> and doubles single quotes.
* "User's home" will return "'User''s home'"
*/
private static String toTextLiteral(String value) {
StringBuilder sb = new StringBuilder(value.length()+10);
sb.append('\'');
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
if (ch == '\'') {
sb.append("''");
} else {
sb.append(ch);
}
}
sb.append('\'');
return sb.toString();
}
private static String toDateLiteral(String value) {
if (NOW.equals(value)) {
return value; // this will get translated later
}
DatatypeConverter.parseDate(value); // verify
return toTextLiteral(value);
}
private static String toTimeLiteral(String value) {
if (NOW.equals(value)) {
return value; // this will get translated later
}
DatatypeConverter.parseTime(value); // verify
return toTextLiteral(value);
}
private static String toDateTimeLiteral(String value) {
if (NOW.equals(value)) {
return value; // this will get translated later
}
DatatypeConverter.parseDateTime(value); // verify
return toTextLiteral(value);
}
}
@@ -1,12 +1,15 @@
package io.ebean.config.dbplatform;
import io.ebean.dbmigration.migration.IdentityType;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Defines the identity/sequence behaviour for the database.
*/
public class DbIdentity {
private static final Pattern TABLE_REPLACE = Pattern.compile("{table}", Pattern.LITERAL);
/**
* Set if this DB supports sequences. Note some DB's support both Sequences
* and Identity.
@@ -50,7 +53,8 @@ public class DbIdentity {
if (selectLastInsertedIdTemplate == null) {
return null;
}
return selectLastInsertedIdTemplate.replace("{table}", table);
return TABLE_REPLACE.matcher(selectLastInsertedIdTemplate).replaceAll(Matcher.quoteReplacement(table));
}
/**
@@ -116,28 +120,4 @@ public class DbIdentity {
this.idType = idType;
}
/**
* Determine the id type to use based on requested identityType and
* the support for that in the database platform.
*/
public IdType useIdentityType(IdentityType identityType) {
if (identityType == null) {
// use the default
return idType;
}
switch (identityType) {
case GENERATOR:
return IdType.GENERATOR;
case EXTERNAL:
return IdType.EXTERNAL;
case SEQUENCE:
return supportsSequence ? IdType.SEQUENCE : idType;
case IDENTITY:
return supportsIdentity ? IdType.IDENTITY : idType;
default:
return idType;
}
}
}
@@ -126,25 +126,33 @@ public class DbPlatformType implements ExtraDbTypes {
StringBuilder sb = new StringBuilder();
sb.append(name);
if (canHaveLength || !strict) {
// see if there is a precision/scale to add (or not)
int len = deployLength != 0 ? deployLength : defaultLength;
if (len > 0) {
sb.append("(");
sb.append(len);
int scale = deployScale != 0 ? deployScale : defaultScale;
if (scale > 0) {
sb.append(",");
sb.append(scale);
}
sb.append(")");
}
renderLengthScale(deployLength, deployScale, sb);
}
return sb.toString();
}
/**
* Render the length and scale part of the column definition.
*/
protected void renderLengthScale(int deployLength, int deployScale, StringBuilder sb) {
// see if there is a precision/scale to add (or not)
int len = deployLength != 0 ? deployLength : defaultLength;
if (len == Integer.MAX_VALUE) {
sb.append("(max)"); // TODO: this is sqlserver specific
} else if (len > 0) {
sb.append("(");
sb.append(len);
int scale = deployScale != 0 ? deployScale : defaultScale;
if (scale > 0) {
sb.append(",");
sb.append(scale);
}
sb.append(")");
}
}
/**
* Create a copy of the type with a new default length.
*/
@@ -1,6 +1,6 @@
package io.ebean.config.dbplatform;
import io.ebean.config.ServerConfig;
import io.ebean.config.PlatformConfig;
import java.util.EnumMap;
import java.util.Map;
@@ -10,8 +10,23 @@ import java.util.Map;
*/
public class DbPlatformTypeMapping {
/**
* Boolean type used for logical model.
*/
private static class BooleanLogicalType extends DbPlatformType {
BooleanLogicalType() {
super("boolean", false);
}
@Override
protected void renderLengthScale(int deployLength, int deployScale, StringBuilder sb) {
// do not have length - even if platform boolean type does like integer(1)
}
}
private static DbPlatformTypeLookup lookup = new DbPlatformTypeLookup();
private static final DbPlatformType BOOLEAN_LOGICAL = new BooleanLogicalType();
private static final DbPlatformType UUID_NATIVE = new DbPlatformType("uuid", false);
@SuppressWarnings("unused")
private static final DbPlatformType UUID_PLACEHOLDER = new DbPlatformType("uuidPlaceholder");
@@ -51,7 +66,7 @@ public class DbPlatformTypeMapping {
*/
private void loadDefaults(boolean logicalTypes) {
put(DbType.BOOLEAN);
put(DbType.BOOLEAN, BOOLEAN_LOGICAL);
put(DbType.BIT);
put(DbType.INTEGER);
put(DbType.BIGINT);
@@ -104,8 +119,8 @@ public class DbPlatformTypeMapping {
put(DbType.JSONCLOB, JSON_CLOB_PLACEHOLDER);
put(DbType.JSONBLOB, JSON_BLOB_PLACEHOLDER);
put(DbType.JSONVARCHAR, JSON_VARCHAR_PLACEHOLDER);
// use reasonable default of varchar(40) - ideally set via DatabasePlatform.configure(DbTypeConfig)
put(DbType.UUID, get(DbType.VARCHAR).withLength(40));
// default to native UUID and override on platform configure()
put(DbType.UUID, UUID_NATIVE);
}
}
@@ -183,9 +198,9 @@ public class DbPlatformTypeMapping {
/**
* Map the UUID appropriately based on native DB support and ServerConfig.DbUuid.
*/
public void config(boolean nativeUuidType, ServerConfig.DbUuid dbUuid) {
public void config(boolean nativeUuidType, PlatformConfig.DbUuid dbUuid) {
if (nativeUuidType && dbUuid.useNativeType()) {
put(DbType.UUID, UUID_NATIVE);
// native UUID already set by default
} else if (dbUuid.useBinary()) {
put(DbType.UUID, get(DbType.BINARY).withLength(16));
} else {
@@ -30,7 +30,8 @@ class DbPlatformTypeParser {
} else {
String type = columnDefinition.substring(0, openPos);
int scale = Integer.parseInt(columnDefinition.substring(openPos + 1, closePos));
String strScale = columnDefinition.substring(openPos + 1, closePos);
int scale = strScale.equalsIgnoreCase("max") ? Integer.MAX_VALUE : Integer.parseInt(strScale);
return new DbPlatformType(type, scale);
}
} catch (RuntimeException e) {
@@ -0,0 +1,57 @@
package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Database sequence based IdGenerator using Sequence Step 1 but batch fetch many sequence values.
*/
public abstract class SequenceBatchIdGenerator extends SequenceIdGenerator {
/**
* Construct where batchSize is the sequence step size.
*
*/
public SequenceBatchIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
}
/**
* If allocateSize is large load some sequences in a background thread.
* <p>
* For example, when inserting a bean with a cascade on a OneToMany with many
* beans Ebean can call this to ensure .
* </p>
*/
@Override
public void preAllocateIds(int requestSize) {
if (allocationSize > 1 && requestSize > allocationSize) {
// only bother if allocateSize is bigger than
// the normal loading batchSize
if (requestSize > 100) {
// max out at 100 for now
requestSize = 100;
}
loadInBackground(requestSize);
}
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException {
List<Long> newIds = new ArrayList<>(loadSize);
while (resultSet.next()) {
newIds.add(resultSet.getLong(1));
}
return newIds;
}
}
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import io.ebean.Transaction;
import io.ebean.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,13 +13,15 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Database sequence based IdGenerator.
*/
public abstract class SequenceIdGenerator implements PlatformIdGenerator {
private static final Logger logger = LoggerFactory.getLogger(SequenceIdGenerator.class);
protected static final Logger logger = LoggerFactory.getLogger("io.ebean.SEQ");
/**
* Used to synchronise the idList access.
@@ -41,18 +44,18 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
protected final ArrayList<Long> idList = new ArrayList<>(50);
protected final int batchSize;
protected final int allocationSize;
protected int currentlyBackgroundLoading;
protected boolean currentlyBackgroundLoading;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
protected SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int allocationSize) {
this.backgroundExecutor = be;
this.dataSource = ds;
this.seqName = seqName;
this.batchSize = batchSize;
this.allocationSize = allocationSize;
}
public abstract String getSql(int batchSize);
@@ -81,26 +84,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
* </p>
*/
@Override
public void preAllocateIds(int allocateSize) {
if (batchSize > 1 && allocateSize > batchSize) {
// only bother if allocateSize is bigger than
// the normal loading batchSize
if (allocateSize > 100) {
// max out at 100 for now
allocateSize = 100;
}
loadLargeAllocation(allocateSize);
}
}
/**
* Called by preAllocateIds when we know that a large number of Id's is going
* to be needed shortly.
*/
protected void loadLargeAllocation(final int allocateSize) {
// preAllocateIds was called with a relatively large batchSize
// so we will just go ahead and load those anyway in background
backgroundExecutor.execute(() -> loadMoreIds(allocateSize, null));
public void preAllocateIds(int requestSize) {
// do nothing by default
}
/**
@@ -113,80 +98,80 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
public Object nextId(Transaction t) {
synchronized (monitor) {
if (idList.isEmpty()) {
loadMoreIds(batchSize, t);
}
Long nextId = idList.remove(0);
if (batchSize > 1) {
if (idList.size() <= batchSize / 2) {
loadBatchInBackground();
}
int size = idList.size();
if (size > 0) {
maybeLoadMoreInBackground(size);
} else {
loadMore(allocationSize);
}
return nextId;
return idList.remove(0);
}
}
/**
* Load another batch of Id's using a background thread.
*/
protected void loadBatchInBackground() {
// single threaded processing...
synchronized (backgroundLoadMonitor) {
if (currentlyBackgroundLoading > 0) {
// skip as already background loading
logger.debug("... skip background sequence load (another load in progress)");
return;
private void maybeLoadMoreInBackground(int currentSize) {
if (allocationSize > 1) {
if (currentSize <= allocationSize / 2) {
loadInBackground(allocationSize);
}
currentlyBackgroundLoading = batchSize;
backgroundExecutor.execute(() -> {
loadMoreIds(batchSize, null);
synchronized (backgroundLoadMonitor) {
currentlyBackgroundLoading = 0;
}
});
}
}
protected void loadMoreIds(final int numberToLoad, Transaction t) {
ArrayList<Long> newIds = getMoreIds(numberToLoad, t);
if (logger.isDebugEnabled()) {
logger.debug("... seq:" + seqName + " loaded:" + numberToLoad + " ids:" + newIds);
}
private void loadMore(int requestSize) {
List<Long> newIds = getMoreIds(requestSize);
synchronized (monitor) {
idList.addAll(newIds);
}
}
/**
* Load another batch of Id's using a background thread.
*/
protected void loadInBackground(final int requestSize) {
// single threaded processing...
synchronized (backgroundLoadMonitor) {
if (currentlyBackgroundLoading) {
// skip as already background loading
logger.debug("... skip background sequence load (another load in progress)");
return;
}
currentlyBackgroundLoading = true;
backgroundExecutor.execute(() -> {
loadMore(requestSize);
synchronized (backgroundLoadMonitor) {
currentlyBackgroundLoading = false;
}
});
}
}
/**
* Read the resultSet returning the list of Id values.
*/
protected abstract List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException;
/**
* Get more Id's by executing a query and reading the Id's returned.
*/
protected ArrayList<Long> getMoreIds(int loadSize, Transaction t) {
protected List<Long> getMoreIds(int requestSize) {
String sql = getSql(loadSize);
String sql = getSql(requestSize);
ArrayList<Long> newIds = new ArrayList<>(loadSize);
boolean useTxnConnection = t != null;
Connection c = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
c = useTxnConnection ? t.getConnection() : dataSource.getConnection();
connection = dataSource.getConnection();
pstmt = c.prepareStatement(sql);
rset = pstmt.executeQuery();
while (rset.next()) {
newIds.add(rset.getLong(1));
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
List<Long> newIds = readIds(resultSet, requestSize);
if (logger.isTraceEnabled()) {
logger.trace("seq:{} loaded:{} sql:{}", seqName, newIds.size(), sql);
}
if (newIds.isEmpty()) {
throw new PersistenceException("Always expecting more than 1 row from " + sql);
@@ -197,46 +182,24 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
} catch (SQLException e) {
if (e.getMessage().contains("Database is already closed")) {
String msg = "Error getting SEQ when DB shutting down " + e.getMessage();
logger.info(msg);
logger.error(msg);
System.out.println(msg);
return newIds;
return Collections.emptyList();
} else {
throw new PersistenceException("Error getting sequence nextval", e);
}
} finally {
if (useTxnConnection) {
closeResources(null, pstmt, rset);
} else {
closeResources(c, pstmt, rset);
}
closeResources(connection, statement, resultSet);
}
}
/**
* Close the JDBC resources.
*/
protected void closeResources(Connection c, PreparedStatement pstmt, ResultSet rset) {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.error("Error closing ResultSet", e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.error("Error closing PreparedStatement", e);
}
try {
if (c != null) {
c.close();
}
} catch (SQLException e) {
logger.error("Error closing Connection", e);
}
private void closeResources(Connection connection, PreparedStatement statement, ResultSet resultSet) {
JdbcClose.close(resultSet);
JdbcClose.close(statement);
JdbcClose.close(connection);
}
}
@@ -0,0 +1,39 @@
package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Database sequence based IdGenerator using Sequence Step (e.g. step 50).
*/
public abstract class SequenceStepIdGenerator extends SequenceIdGenerator {
/**
* Construct with stepSize (typically 50).
*/
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int ignoreRequestSize) throws SQLException {
List<Long> newIds = new ArrayList<>(allocationSize);
if (resultSet.next()) {
long start = resultSet.getLong(1);
for (int i = 0; i < allocationSize; i++) {
newIds.add(start + i);
}
}
return newIds;
}
}
@@ -1,6 +1,7 @@
package io.ebean.config.dbplatform;
import io.ebean.Transaction;
import io.ebean.util.JdbcClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -82,27 +83,9 @@ public class SimpleSequenceIdGenerator implements PlatformIdGenerator {
}
private void closeResources(ResultSet rset, PreparedStatement pstmt, Connection c) {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.error("Error closing ResultSet", e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.error("Error closing PreparedStatement", e);
}
try {
if (c != null) {
c.close();
}
} catch (SQLException e) {
logger.error("Error closing Connection", e);
}
JdbcClose.close(rset);
JdbcClose.close(pstmt);
JdbcClose.close(c);
}
}
@@ -34,6 +34,10 @@ public class SqlCodeTranslator implements SqlExceptionTranslator {
public PersistenceException translate(String message, SQLException e) {
DataErrorType errorType = map.get(e.getSQLState());
if (errorType == null) {
// fall back to error code
errorType = map.get(String.valueOf(e.getErrorCode()));
}
if (errorType != null) {
switch (errorType) {
case AcquireLock:
@@ -0,0 +1,28 @@
package io.ebean.config.dbplatform.cockroach;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
/**
* CockroachDB based platform.
*/
public class CockroachPlatform extends PostgresPlatform {
public CockroachPlatform() {
super();
this.platform = Platform.COCKROACH;
// no like escape clause supported
this.likeSpecialCharacters = new char[]{'%', '_'};
this.likeClauseRaw = "like ?";
this.likeClauseEscaped = "like ?";
}
/**
* Needs a commit after create index such that alter table add foreign key ... succeeds.
*/
@Override
public boolean isDdlCommitOnCreateIndex() {
return true;
}
}
@@ -1,13 +1,13 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.DB2Ddl;
import javax.sql.DataSource;
import java.sql.Types;
@@ -23,22 +23,27 @@ public class DB2Platform extends DatabasePlatform {
this.maxTableNameLength = 18;
this.maxConstraintNameLength = 18;
this.sqlLimiter = new Db2SqlLimiter();
this.platformDdl = new DB2Ddl(this);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.exceptionTranslator =
new SqlErrorCodes()
//.addAcquireLock("")
.addDuplicateKey("-803")
.addDataIntegrity("-407","-530","-531","-532","-543","-544","-545","-603","-667")
.addAcquireLock("40001","57033") // key -911/-913
.addDuplicateKey("23505") // -803
// .addDataIntegrity("-407","-530","-531","-532","-543","-544","-545","-603","-667")
// we need SQLState, not code: https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/codes/src/tpc/db2z_n.html
.addDataIntegrity("23502","23503","23504","23511","23512","23511","42917","23515")
.build();
booleanDbType = Types.BOOLEAN;
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint", false));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false));
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 15));
persistBatchOnCascade = PersistBatch.NONE;
}
/**
@@ -46,10 +51,9 @@ public class DB2Platform extends DatabasePlatform {
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be,
DataSource ds, String seqName, int batchSize) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new DB2SequenceIdGenerator(be, ds, seqName, batchSize);
return new DB2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
}
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
public class DB2SequenceIdGenerator extends SequenceIdGenerator {
public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
@@ -49,7 +49,7 @@ public class H2HistoryTrigger implements Trigger {
// build the insert into history table SQL
StringBuilder insertSql = new StringBuilder(150);
insertSql.append("insert into ").append(tableName).append(HISTORY_SUFFIX).append(" (");
insertSql.append("insert into ").append(schemaName).append(".").append(tableName).append(HISTORY_SUFFIX).append(" (");
int count = 0;
while (rs.next()) {
@@ -1,15 +1,14 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.H2Ddl;
import javax.sql.DataSource;
@@ -22,7 +21,6 @@ public class H2Platform extends DatabasePlatform {
super();
this.platform = Platform.H2;
this.dbEncrypt = new H2DbEncrypt();
this.platformDdl = new H2Ddl(this);
this.historySupport = new H2HistorySupport();
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
@@ -30,7 +28,7 @@ public class H2Platform extends DatabasePlatform {
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("50200")
.addAcquireLock("50200","HYT00")
.addDuplicateKey("23001","23505")
.addDataIntegrity("22001","22003","22012","22018","22025","23000","23002","23003","23502","23503","23506","23507","23513")
.build();
@@ -48,10 +46,8 @@ public class H2Platform extends DatabasePlatform {
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
String seqName, int batchSize) {
return new H2SequenceIdGenerator(be, ds, seqName, batchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
@Override
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific sequence Id Generator.
*/
public class H2SequenceIdGenerator extends SequenceIdGenerator {
public class H2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
@@ -1,7 +1,7 @@
package io.ebean.config.dbplatform.hsqldb;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -9,7 +9,6 @@ import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.h2.H2DbEncrypt;
import io.ebean.config.dbplatform.h2.H2SequenceIdGenerator;
import io.ebean.dbmigration.ddlgeneration.platform.HsqldbDdl;
import javax.sql.DataSource;
@@ -22,7 +21,6 @@ public class HsqldbPlatform extends DatabasePlatform {
super();
this.platform = Platform.HSQLDB;
this.dbEncrypt = new H2DbEncrypt();
this.platformDdl = new HsqldbDdl(this);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
@@ -33,9 +31,8 @@ public class HsqldbPlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
return new H2SequenceIdGenerator(be, ds, seqName, batchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
}
@@ -1,17 +1,13 @@
package io.ebean.config.dbplatform.mysql;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.MySqlDdl;
import javax.sql.DataSource;
import java.sql.Types;
/**
@@ -32,7 +28,6 @@ public class MySqlPlatform extends DatabasePlatform {
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.selectCountWithAlias = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.platformDdl = new MySqlDdl(this);
this.historySupport = new MySqlHistorySupport();
this.columnAliasPrefix = null;
@@ -40,22 +35,30 @@ public class MySqlPlatform extends DatabasePlatform {
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(false);
this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1205")
.addDuplicateKey("1062")
.addDataIntegrity("630","839","840","893","1169","1215","1216","1217","1364","1451","1452","1557")
.addDuplicateKey("1062", "1169")
.addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557")
.build();
this.openQuote = "`";
this.closeQuote = "`";
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
this.likeClauseRaw = "like binary ? escape ''";
this.likeClauseEscaped = "like binary ? escape '|'";
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1) default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1) default 0"));
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
dbTypeMap.put(DbType.CLOB, new MySqlClob());
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
@@ -63,19 +66,10 @@ public class MySqlPlatform extends DatabasePlatform {
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
}
/**
* Return null in case there is a sequence annotation.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be,
DataSource ds, String seqName, int batchSize) {
return null;
}
@Override
protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) {
// NOWAIT and SKIP LOCKED currently not supported with MySQL
return sql + " for update";
}
}
@@ -1,8 +1,8 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.Query;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.BasicSqlAnsiLimiter;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
@@ -10,13 +10,13 @@ import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.RownumSqlLimiter;
import io.ebean.dbmigration.ddlgeneration.platform.Oracle10Ddl;
import io.ebean.config.dbplatform.SqlErrorCodes;
import javax.sql.DataSource;
import java.sql.Types;
/**
* Oracle10 and greater specific platform.
* Oracle specific platform.
*/
public class OraclePlatform extends DatabasePlatform {
@@ -28,22 +28,32 @@ public class OraclePlatform extends DatabasePlatform {
this.dbEncrypt = new OracleDbEncrypt();
this.sqlLimiter = new RownumSqlLimiter();
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
this.platformDdl = new Oracle10Ddl(this);
this.historySupport = new OracleDbHistorySupport();
// Not using getGeneratedKeys as instead we will
// batch load sequences which enables JDBC batch execution
dbIdentity.setSupportsGetGeneratedKeys(false);
dbIdentity.setIdType(IdType.SEQUENCE);
dbIdentity.setSupportsSequence(true);
dbIdentity.setSupportsIdentity(true);
dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.dbDefaultValue.setNow("current_timestamp");
this.treatEmptyStringsAsNull = true;
this.likeClauseRaw = "like ?";
this.exceptionTranslator =
new SqlErrorCodes()
//.addAcquireLock("")
.addDuplicateKey("1")
.addDataIntegrity("2291")
.build();
this.openQuote = "\"";
this.closeQuote = "\"";
booleanDbType = Types.INTEGER;
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("number(1) default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("number(1)"));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("number", 10));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("number", 19));
@@ -64,9 +74,8 @@ public class OraclePlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
return new OracleSequenceIdGenerator(be, ds, seqName, batchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new OracleSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
@Override
@@ -80,4 +89,5 @@ public class OraclePlatform extends DatabasePlatform {
return sql + " for update";
}
}
}
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Oracle specific sequence Id Generator.
*/
public class OracleSequenceIdGenerator extends SequenceIdGenerator {
public class OracleSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
@@ -1,17 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.Platform;
import io.ebean.Query;
import io.ebean.config.ServerConfig;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.DdlHandler;
import io.ebean.dbmigration.ddlgeneration.platform.PostgresDdl;
import javax.sql.DataSource;
import java.sql.Types;
@@ -36,13 +33,14 @@ public class PostgresPlatform extends DatabasePlatform {
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
this.platformDdl = new PostgresDdl(this);
// Use Identity and getGeneratedKeys
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbDefaultValue.setNow("current_timestamp");
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("55P03")
@@ -91,20 +89,20 @@ public class PostgresPlatform extends DatabasePlatform {
}
/**
* Return a DdlHandler instance for generating DDL for the specific platform.
* So we can generate varchar[], int[], uuid[] column definitions and use the associated scalar types.
*/
@Override
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
return this.platformDdl.createDdlHandler(serverConfig);
public boolean isNativeArrayType() {
return true;
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
return new PostgresSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
@Override
@@ -1,14 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceIdGenerator;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Postgres specific sequence Id Generator.
*/
public class PostgresSequenceIdGenerator extends SequenceIdGenerator {
public class PostgresSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
@@ -1,6 +1,6 @@
package io.ebean.config.dbplatform.sqlanywhere;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
@@ -27,7 +27,7 @@ public class SqlAnywherePlatform extends DatabasePlatform {
this.dbIdentity.setSelectLastInsertedIdTemplate("select @@IDENTITY as X");
this.dbIdentity.setSupportsIdentity(true);
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit"));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("numeric", 19));
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
@@ -1,11 +1,10 @@
package io.ebean.config.dbplatform.sqlite;
import io.ebean.Platform;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.dbmigration.ddlgeneration.platform.SQLiteDdl;
import java.sql.Types;
@@ -14,20 +13,27 @@ public class SQLitePlatform extends DatabasePlatform {
public SQLitePlatform() {
super();
this.platform = Platform.SQLITE;
this.platformDdl = new SQLiteDdl(this);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setSupportsSequence(false);
this.dbIdentity.setSelectLastInsertedIdTemplate("select last_insert_rowid()");
this.booleanDbType = Types.INTEGER;
this.likeClauseRaw = "like ?";
this.likeClauseEscaped = "like ?";
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.dbDefaultValue.setNow("CURRENT_TIMESTAMP");
dbTypeMap.put(DbType.BIT, new DbPlatformType("int default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("int default 0"));
dbTypeMap.put(DbType.BIT, new DbPlatformType("int"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("int"));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("integer"));
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("integer"));
}
@Override
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append(ch);
}
}

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