Compare commits

...
Author SHA1 Message Date
Rob Bygrave 6f694eb6ca [maven-release-plugin] prepare release ebean-parent-12.15.0 2022-02-03 09:02:30 +13:00
Rob Bygrave 539633c963 Adjust Multi-database platform 2022-02-02 23:40:58 +13:00
Rob Bygrave 8e05956f6b Default tests to use H2 2022-02-02 23:39:15 +13:00
Rob Bygrave 94951a5476 Yugabyte - use distinct on clause with Yugabyte (like Postgres) 2022-02-02 23:38:50 +13:00
Rob BygraveandGitHub 2cd769d00f Merge pull request #2546 from ebean-orm/feature/yugabyte-mutlivaluebind
Modify tests to use isPostgresCompatible() (to effectively include Yugabyte)
2022-02-02 23:26:12 +13:00
Rob Bygrave 389f2bcd2f Modify tests to use isPostgresCompatible() (to effectively include Yugabyte) 2022-02-02 23:25:13 +13:00
Rob BygraveandGitHub 1ba259d15d Merge pull request #2545 from ebean-orm/feature/yugabyte-mutlivaluebind
Yugabyte - Use MultiValueBind like Postgres (that is, ANY(?) clause in sqlQuery/sqlUpdate)
2022-02-02 23:03:33 +13:00
Rob Bygrave 4899f23773 Yugabyte - Use MultiValueBind like Postgres (that is, ANY(?) clause in sqlQuery/sqlUpdate) 2022-02-02 23:02:32 +13:00
Rob Bygrave 771e9ff069 Tidy tests only - EqlParserTest, EbeanServer_deleteAllByIdTest 2022-02-02 23:00:06 +13:00
Rob Bygrave 7d27c06483 Bump jaxb-runtime dependency 2022-02-02 22:30:37 +13:00
Rob Bygrave d6a8643fe4 Support @DbArray of BigDecimal 2022-02-02 22:22:42 +13:00
Rob BygraveandGitHub 97c714d945 Merge pull request #2544 from ebean-orm/feature/dbarray_bigdecimal
Support @DbArray of BigDecimal
2022-02-02 22:08:48 +13:00
Rob Bygrave 57f6668b37 Support @DbArray of BigDecimal 2022-02-02 21:57:39 +13:00
Rob Bygrave 9d506b4397 Bump test dependencies 2022-02-02 20:48:55 +13:00
Rob Bygrave 5a12cc58d1 Bump Postgres test dependencies to 42.3.2 2022-02-02 17:18:14 +13:00
Rob Bygrave f7c83411c2 #2415 - SQL for Index and Foreign Key didn't get generated if I map ID column after @ManyToOne
Allow declaring the column twice with scalar type after ManyToOne type
2022-02-02 17:12:18 +13:00
Rob BygraveandGitHub a02fee7de0 Merge pull request #2539 from ebean-orm/feature/performance-pathmaps
#2507 - Script runner does not work with file system path
2022-02-02 13:03:45 +13:00
Rob Bygrave 170e323ee9 #2507 - Script runner does not work with file system path 2022-02-02 13:02:49 +13:00
Rob BygraveandGitHub 4555f3da51 Merge pull request #2538 from ebean-orm/feature/performance-pathmaps
Performance - cache the path maps used for load context
2022-02-02 12:30:56 +13:00
Rob Bygrave 376b3a9f13 #2538 - Tidy CQuery use of path() 2022-02-02 12:20:33 +13:00
Rob Bygrave 43af5343a4 Performance - cache the path maps used for load context
- Cache the path maps
- Include the ToOne paths
- Use empty map for null prefix
2022-02-02 12:10:02 +13:00
Rob Bygrave 886f539fc4 Refactor tidy SqlTreeNodeBean 2022-02-02 11:30:56 +13:00
Rob Bygrave 1035d74113 Bump to 12.15.0-SNAPSHOT 2022-02-02 11:20:59 +13:00
Rob Bygrave 26a4feb5ba Bump to 12.15.0-SNAPSHOT 2022-02-02 11:14:36 +13:00
Rob Bygrave a1c0e21234 Bump to 12.15.0-SNAPSHOT 2022-02-02 10:59:04 +13:00
Rob Bygrave cbe133dd68 #2509 - Bump jedis client to 4.1.1 2022-02-02 10:56:59 +13:00
Rob BygraveandGitHub 4b1fe063b0 Merge pull request #2531 from FOCONIS/db2/persist-batch-on-cascade
Set persistBatchOnCascade to ALL for newer DB2 versions
2022-02-02 09:06:39 +13:00
Rob Bygrave 15f1bd310b #2521 - Refactor inline index++ 2022-02-02 09:05:14 +13:00
Rob Bygrave 6e3e9bd4f7 git push origin masterMerge branch 'FOCONIS-db2/fix-get-generated-keys' 2022-02-02 09:00:01 +13:00
Rob Bygrave fddcc64741 #2521 - Move horrible DB2 hack into DB2GetKeys 2022-02-02 08:59:41 +13:00
Rob Bygrave ed917c55f1 Merge branch 'db2/fix-get-generated-keys' of https://github.com/FOCONIS/ebean into FOCONIS-db2/fix-get-generated-keys 2022-02-02 08:46:42 +13:00
Rob Bygrave 17fed0ebd8 Tidy test only - TestNestedBeginRequiresNew 2022-02-01 15:18:06 +13:00
Rob BygraveandGitHub 2dcd17830a Merge pull request #2536 from ebean-orm/feature/ebeantest-pg14
ebean-test - bump Postgres default container to 14
2022-02-01 13:46:09 +13:00
Rob Bygrave a412873da6 ebean-test - bump Postgres default container to 14 2022-02-01 13:36:37 +13:00
Rob BygraveandGitHub 7f5370c709 Merge pull request #2535 from ebean-orm/feature/yugabyte-workflow-build
Yugabyte - Add specific github workflow build
2022-02-01 13:33:28 +13:00
Rob Bygrave 9470a1376c Yugabyte - Add specific github workflow build 2022-02-01 13:32:55 +13:00
Rob BygraveandGitHub 7f5b65cf45 Merge pull request #2534 from ebean-orm/feature/yugabyte-test
Yugabyte - ebean-test support via ebean.test.platform=yugabyte
2022-02-01 13:30:49 +13:00
Rob Bygrave 6d0f5aea82 Yugabyte - ebean-test support via ebean.test.platform=yugabyte 2022-02-01 13:26:05 +13:00
Rob Bygrave 530a75b591 DB - test ddl changes 2022-02-01 13:24:50 +13:00
Rob Bygrave 13cfaba0c8 Yugabyte - Disable commit and continue test for Yugabyte (failing occasionally, need to review) 2022-02-01 11:55:26 +13:00
Rob Bygrave 1fa92efb63 Yugabyte - Adjust DbJsonTest wrt ordering in content, use JSON assertContainsResource()
Highlights that the DbJson assertContains() asserts are better longer term approach (over content matching asserts)
2022-02-01 11:55:15 +13:00
Rob Bygrave bc477c64cb Yugabyte - Disable commit and continue test for Yugabyte (failing occasionally, need to review) 2022-02-01 11:53:03 +13:00
Rob Bygrave 804098149e Yugabyte - Tidy tests with element collections not maintaining ordering 2022-02-01 11:51:46 +13:00
Rob Bygrave 567ad92050 Yugabyte - Tidy tests 2022-02-01 10:41:29 +13:00
Rob BygraveandGitHub 3a84cb8623 Merge pull request #2533 from ebean-orm/feature/yugabyte-json-hstore
Yugabyte - Fix some tests for running against Yugabyte
2022-02-01 09:50:05 +13:00
Rob Bygrave b4ddbe38ee Yugabyte - Fix some tests for running against Yugabyte 2022-02-01 09:49:23 +13:00
Rob BygraveandGitHub 01722b334b Merge pull request #2532 from FOCONIS/db2/unique_indices_with_null_columns
DB2 Support for unique indices with null columns
2022-02-01 08:16:43 +13:00
Rob BygraveandGitHub 0a780523cf Merge pull request #2529 from ebean-orm/feature/yugabyte-json-hstore
Yugabyte - Use Postgres ScalarTypes for JSON/JSONB but not HSTORE
2022-02-01 07:56:26 +13:00
Roland PramlandJonas Pöhler f2e04d9fbf DB2. Support for unique indices with null columns
(cherry picked from commit 23a5d577a6)
2022-01-31 13:58:40 +01:00
Jonas Pöhler 4b8565e7c7 FIX: we can set persistBatchOnCascade to ALL for newer DB2 versions 2022-01-31 11:32:22 +01:00
Rob Bygrave 1359d534d5 Yugabyte - Use Postgres ScalarTypes for JSON/JSONB but not HSTORE 2022-01-29 17:05:12 +13:00
Rob BygraveandGitHub 94ae513d85 Merge pull request #2522 from FOCONIS/db2/check-exception-chain
DB2: SqlCodeTranslator must check exception chain
2022-01-29 15:33:46 +13:00
Rob BygraveandGitHub d58c56484e Merge pull request #2517 from FOCONIS/fix-datasource
FIX: Datasource was not initialized in offline mode.
2022-01-29 15:28:53 +13:00
Rob BygraveandGitHub 75a27fa2d6 Merge pull request #2511 from FOCONIS/unselect-lobs
Unselect LOBs when making a distinct select
2022-01-29 15:25:00 +13:00
Rob BygraveandGitHub cc1c08cd34 Merge pull request #2528 from FOCONIS/db2/disabled-tests
DB2 Fix tests for DB2
2022-01-29 15:19:01 +13:00
Rob BygraveandGitHub 029cde12db Merge pull request #2527 from FOCONIS/db2/fix-sequence
DB2: Fix Sequence generator
2022-01-29 15:16:24 +13:00
Rob BygraveandGitHub b33914ab2f Merge pull request #2526 from FOCONIS/db2/no-null-values
DB2: Do not try to update deferred null values (Discovered by TestPrinterStateIssue)
2022-01-29 15:15:33 +13:00
Rob BygraveandGitHub d571a6c354 Merge pull request #2525 from FOCONIS/db2/truncate
DB2: truncate needs commit after each statement
2022-01-29 15:13:29 +13:00
Rob BygraveandGitHub 95b19569e4 Merge pull request #2524 from FOCONIS/db2/nocompressedid
DB2 does not support ids in compressed form
2022-01-29 15:12:25 +13:00
Rob BygraveandGitHub a9560c6664 Merge pull request #2523 from FOCONIS/db2/sqllimiter
DB2: FIX Db2SqlLimiter & tests
2022-01-29 15:11:33 +13:00
Rob BygraveandGitHub 235822bcb2 Merge pull request #2520 from FOCONIS/db2/fix-raw-like
DB2: Fix Raw like
2022-01-29 15:07:41 +13:00
Rob BygraveandGitHub 5a024ea19f Merge pull request #2519 from FOCONIS/db2/fix-default-values
DB2: Fix default values for auto generated ids
2022-01-29 15:06:54 +13:00
Rob BygraveandGitHub 399f8b92fa Merge pull request #2518 from FOCONIS/db2/fix-concat-operator
DB2: Fix Concat operator
2022-01-29 15:05:58 +13:00
Rob BygraveandGitHub be93263015 Merge pull request #2513 from FOCONIS/h2-backward-compat
Test against H2 1.4.199
2022-01-29 11:23:19 +13:00
Roland Praml b4fe4ec8b9 ADD.profile and autodetect code 2022-01-28 16:04:35 +01:00
Roland Praml 8c9f5f4a22 Disabled more tests for DB2 2022-01-28 11:38:11 +01:00
Roland Praml 953f8054e6 FIX: Clean up tests 2022-01-28 11:38:01 +01:00
Roland Praml aadb791980 FIX: isSame replaceed by isEqualTo 2022-01-28 11:37:48 +01:00
Roland Praml 7430f97c9c fix compile errors for EqlParserTest 2022-01-28 11:37:30 +01:00
Roland Praml ba21eeaadb DB2: Do not try to update deferred null values (Discovered by TestPrinterStateIssue) 2022-01-28 10:59:16 +01:00
Roland Praml c0e827ffda DB2: Fix Sequence generator 2022-01-28 10:57:41 +01:00
Roland Praml 3b30708dfc DB2: truncate needs commit after each statement 2022-01-28 10:56:01 +01:00
Roland Praml 9f9184ac6a DB2 does not support ids in compressed form 2022-01-28 10:53:51 +01:00
Roland Praml 5845f94303 DB2:ignored tests 2022-01-28 10:28:02 +01:00
Roland Praml dd824ffe52 DB2: Fix EqlParserTests similar to HANA 2022-01-28 10:27:50 +01:00
Roland Praml e5d29afa4c DB2: Disabled test for encryption, as it is not supported for Db2 2022-01-28 10:27:39 +01:00
Roland Praml 5ae5e408e3 DB2: FIX Db2SqlLimiter & tests 2022-01-28 10:23:22 +01:00
Roland Praml 90f6d0707f DB2: SqlCodeTranslator must check exception chain 2022-01-28 10:21:55 +01:00
Roland Praml 0310601e31 DB2: Fix getGenerateKeys (ungly workaround) 2022-01-28 10:06:34 +01:00
Roland Praml 015dd00b8e DB2: Fix Raw like 2022-01-28 10:04:20 +01:00
Roland Praml 6d978add29 DB2: Fix default values 2022-01-28 09:47:49 +01:00
Roland Praml e529232f76 DB2: Fix insert default values 2022-01-28 09:47:04 +01:00
Roland Praml d982ac73b7 DB2: Fix Concat operator 2022-01-28 09:38:41 +01:00
Roland Praml ddaff81b9c FIX: Datasource was not initialized in offline mode. 2022-01-28 09:29:12 +01:00
Rob BygraveandGitHub 1d11666945 Merge pull request #2516 from ebean-orm/feature/yugabyte-platform-ddl
Yugabyte platform support - Automatic detection and DDL support
2022-01-28 17:45:46 +13:00
Rob Bygrave 223e5160dc #2516 - Touch StartYugabyte 2022-01-28 17:26:58 +13:00
Rob Bygrave 64605b5090 Yugabyte platform support - Automatic detection and DDL support
- Automatic detection similar to Cockroach
- DDL very similar to Postgres but no CREATE TABLE LIKE support (for history DDL)
2022-01-28 17:04:19 +13:00
Rob Bygrave 425124d8f7 Adjust README for build badge changes 2022-01-28 10:55:22 +13:00
Rob Bygrave 6af8968280 Adjust workflow build for Oracle 18 2022-01-28 10:44:25 +13:00
Rob Bygrave a9bee03ba7 Add workflow build for Oracle 18 2022-01-28 09:57:49 +13:00
Rob BygraveandGitHub 302cc426bd Merge pull request #2515 from ebean-orm/feature/oracle-maxInBinding
Add maxInBinding - Oracle support for large delete by ids
2022-01-28 09:50:01 +13:00
Rob Bygrave 44462e3513 Add maxInBinding - Oracle support for large delete by ids 2022-01-28 09:46:03 +13:00
Rob Bygrave b748c598a1 build workflow rename for DB2 LUW 2022-01-28 09:04:56 +13:00
Rob Bygrave dbac184b4c Rename build workflows 2022-01-28 08:58:25 +13:00
Rob Bygrave 29c5916203 Add build workflow for db2 2022-01-28 08:54:21 +13:00
Rob BygraveandGitHub 827315a11d Merge pull request #2509 from ebean-orm/feature/jedis-4x
Support jedis 4.1.0 client (ScanParams, ScanResult in different package now)
2022-01-28 08:37:28 +13:00
Rob BygraveandGitHub 14ef8f6e0d Merge pull request #2512 from FOCONIS/introduce-db2
ADD: different DB2 platforms
2022-01-28 08:17:03 +13:00
Roland Praml 5b63434f13 reindent code and linked #2514 2022-01-27 16:43:35 +01:00
Roland Praml 3e8cc87f01 Add: github workflow for db2 2022-01-27 16:02:18 +01:00
Roland Praml 190b4ef588 Made some fixes in wrong ddl generation 2022-01-27 15:58:44 +01:00
Roland Praml 4a20c9eb99 Test against H2 1.4.199 2022-01-27 13:56:21 +01:00
Roland Praml d8c2b3d583 FIX: Compile errors 2022-01-27 10:34:25 +01:00
Roland Praml 48c8f5e2ca ADD: different DB2 platforms 2022-01-27 10:09:31 +01:00
Noemi SzemenyeiandRoland Praml 69a753fc4f DB2:Unselect LOBs when making a distinct select 2022-01-27 08:34:51 +01:00
Roland Praml b9ba6b2db5 DB2: Adapted Noemi's patch to new ebean version 2022-01-27 07:57:35 +01:00
Rob Bygrave 7a6071ac9a Support jedis 4.1.0 client (ScanParams, ScanResult in different package now) 2022-01-27 09:20:23 +13:00
Rob Bygrave 404b167bcc #2508 - Bump ebean-datasource to 7.5 2022-01-27 09:13:20 +13:00
Rob Bygrave 7278db56ea Modify test TestUuidGenerator to allow odd jvm variance in root error message 2022-01-27 09:12:57 +13:00
Rob BygraveandGitHub 1dc5ecc81f Merge pull request #2506 from FOCONIS/fix-localdate-databind
DB2: Fix: DataBind/Read of native LocalDates
2022-01-27 09:02:04 +13:00
Rob BygraveandGitHub c39661e2c2 Merge pull request #2505 from FOCONIS/refactor-uuid-v1-generator
Refactor UUID generator. Add uuid-property to serverConfig (#57)
2022-01-27 08:55:48 +13:00
Rob BygraveandGitHub a702bbb5ee Merge pull request #2504 from FOCONIS/sqlserver-delete-problem
Cannot delete bean with more than 2100 child beans on SqlServer
2022-01-27 08:54:27 +13:00
Roland PramlandRoland Praml 1982027300 Organized imports 2022-01-26 16:55:50 +01:00
Roland PramlandRoland Praml 2e1a6eec04 DB2: Fix: DataBind/Read of native LocalDates 2022-01-26 16:51:55 +01:00
Roland PramlandRoland Praml e5f37e638c Refactor UUID generator. Add uuid-property to serverConfig (#57)
Co-authored-by: Roland Praml <roland.praml@foconis.de>
2022-01-26 12:05:12 +01:00
Roland Praml 7ffea8e922 FIX: DeleteBatch for SQLServer 2022-01-26 11:59:35 +01:00
Roland Praml 315640df6b ADD: Failing test case 2022-01-26 11:59:17 +01:00
Rob Bygrave 78a647a72e Merge branch 'master' of github.com:ebean-orm/ebean 2022-01-26 22:42:10 +13:00
Rob Bygrave 7ffd2f1886 #2501 Modify pom use ${h2database.version} 2022-01-26 22:41:55 +13:00
Rob BygraveandGitHub 0087eabb4e Merge pull request #2502 from FOCONIS/fix-composite-id-in-select-distinct
Fix: composite id in select distinct queries
2022-01-26 22:22:18 +13:00
Rob BygraveandGitHub 5f0731f975 Merge pull request #2499 from ebean-orm/dependabot/maven/ebean-core/com.h2database-h2-2.1.210
Bump h2 from 1.4.199 to 2.1.210 in /ebean-core
2022-01-26 22:19:14 +13:00
Rob BygraveandGitHub 85e31899b9 Merge pull request #2498 from ebean-orm/dependabot/maven/ebean-test/com.h2database-h2-2.1.210
Bump h2 from 1.4.199 to 2.1.210 in /ebean-test
2022-01-26 22:18:50 +13:00
Rob BygraveandGitHub 3337541ac4 Merge pull request #2497 from ebean-orm/dependabot/maven/ebean-api/com.h2database-h2-2.1.210
Bump h2 from 1.4.199 to 2.1.210 in /ebean-api
2022-01-26 22:18:18 +13:00
Rob Bygrave b123b06f4d Merge branch 'wip/h2database-v2' 2022-01-26 22:08:52 +13:00
Rob Bygrave 17df5a2c16 Merge branch 'master' into wip/h2database-v2 2022-01-26 22:07:20 +13:00
Rob BygraveandGitHub e13aaefee1 Merge pull request #2500 from ebean-orm/feature/refactor-sqltreeload
Refactor internals split result load functionality from SqlTreeNode into SqlTreeLoad
2022-01-26 22:04:42 +13:00
Rob Bygrave 09ce693da2 Merge branch 'feature/refactor-sqltreeload' of github.com:ebean-orm/ebean into feature/refactor-sqltreeload 2022-01-26 22:00:40 +13:00
Rob Bygrave 17f7be72f4 Touch README.md 2022-01-26 22:00:28 +13:00
Rob Bygrave 23061473c7 Refactor internals split result load functionality from SqlTreeNode into SqlTreeLoad 2022-01-26 22:00:28 +13:00
Rob Bygrave 0944b87283 Merge branch 'wip/h2database-v2' of github.com:ebean-orm/ebean into wip/h2database-v2 2022-01-26 21:58:12 +13:00
Rob Bygrave 86768dfe4d Touch application-test.properties 2022-01-26 21:57:41 +13:00
Rob Bygrave ffdafc796b Touch README.md 2022-01-26 21:54:19 +13:00
Rob Bygrave 5466c6a350 Reduce logging when running Build tests 2022-01-26 21:51:48 +13:00
Rob Bygrave 7e0ac5a73a Fix issues with tests - tidy ResetBasicData 2022-01-26 21:47:00 +13:00
Noemi Szemenyei 008e1849a2 FIX: other queries for other platforms 2022-01-26 08:25:38 +01:00
Noemi Szemenyei dcf60a3d1c Trigger Build 2022-01-26 08:09:42 +01:00
Rob Bygrave 774cab327d Fix issues with test ordering - ordering in ResetBasicData 2022-01-26 17:23:18 +13:00
Rob Bygrave 2f824c99ea Fix issues with test ordering 2022-01-26 16:53:38 +13:00
Rob Bygrave 1a54fc12ad Test logging enabled for investigating CI Build issue 2022-01-26 15:48:03 +13:00
Rob Bygrave a1d6b280bc Bump test parent poms 2022-01-26 14:58:38 +13:00
Rob Bygrave fa561e5197 CI Build with single test and bump enhancement tiles 2022-01-26 14:52:32 +13:00
Rob Bygrave 4a8231a6cc CI Build using adopt distribution 2022-01-26 14:38:52 +13:00
Rob Bygrave 5cb486c745 CI Build without maven cache 2022-01-26 14:18:02 +13:00
Noemi Szemenyei dd26dc342d FIX: select properties of the embedded Id
(cherry picked from commit 58e952b1ec)
2022-01-25 09:42:27 +01:00
Noemi Szemenyei 7733e9382c ADD: failing test with select distinct with composite key and T0.NULL
(cherry picked from commit a08ba194b7)
2022-01-25 09:42:04 +01:00
Rob BygraveandGitHub bc6d2fb3be Merge branch 'master' into wip/h2database-v2 2022-01-25 21:30:36 +13:00
Rob Bygrave a9dbc036e9 Support H2database 2.x 2022-01-25 21:28:09 +13:00
Rob Bygrave 8d29a3a575 Bind LocalDate directly using JDBC driver to convert
This addresses issues with dates before 1900-01-01 with H2Database
2022-01-25 21:27:32 +13:00
Rob Bygrave 4158ac4665 Refactor internals split result load functionality from SqlTreeNode into SqlTreeLoad 2022-01-25 13:44:23 +13:00
dependabot[bot]andGitHub ddab6e65b7 Bump h2 from 1.4.199 to 2.1.210 in /ebean-core
Bumps [h2](https://github.com/h2database/h2database) from 1.4.199 to 2.1.210.
- [Release notes](https://github.com/h2database/h2database/releases)
- [Commits](https://github.com/h2database/h2database/compare/version-1.4.199...version-2.1.210)

---
updated-dependencies:
- dependency-name: com.h2database:h2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-21 23:47:28 +00:00
dependabot[bot]andGitHub bbede04a56 Bump h2 from 1.4.199 to 2.1.210 in /ebean-test
Bumps [h2](https://github.com/h2database/h2database) from 1.4.199 to 2.1.210.
- [Release notes](https://github.com/h2database/h2database/releases)
- [Commits](https://github.com/h2database/h2database/compare/version-1.4.199...version-2.1.210)

---
updated-dependencies:
- dependency-name: com.h2database:h2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-21 23:47:27 +00:00
dependabot[bot]andGitHub 9df96b0294 Bump h2 from 1.4.199 to 2.1.210 in /ebean-api
Bumps [h2](https://github.com/h2database/h2database) from 1.4.199 to 2.1.210.
- [Release notes](https://github.com/h2database/h2database/releases)
- [Commits](https://github.com/h2database/h2database/compare/version-1.4.199...version-2.1.210)

---
updated-dependencies:
- dependency-name: com.h2database:h2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-21 23:47:27 +00:00
Rob Bygrave 6a6b6b8200 Bump ebean-kotlin to 12.14.2-SNAPSHOT 2022-01-21 17:38:59 +13:00
Rob Bygrave e26e093680 Bump test poms to 12.14.2-SNAPSHOT 2022-01-21 17:38:03 +13:00
Rob BygraveandGitHub fc107115c6 Merge pull request #2494 from ebean-orm/FOCONIS-pr/enh/stored_procedures_mysql_drop_column
Optionally allow use of stored procedures for MySql to drop columns in db migration
2022-01-21 17:26:22 +13:00
Rob Bygrave 0139ad891e #2403 - table partitioning (Postgres only at this point) is always included + simplify skip logic 2022-01-21 17:20:21 +13:00
Rob Bygrave 4f70c09690 Merge branch 'pr/enh/stored_procedures_mysql_drop_column' of https://github.com/FOCONIS/ebean into FOCONIS-pr/enh/stored_procedures_mysql_drop_column 2022-01-21 17:06:48 +13:00
Rob Bygrave df8ae18bf6 [maven-release-plugin] prepare for next development iteration 2022-01-17 22:31:00 +13:00
Rob Bygrave 8edaabf755 [maven-release-plugin] prepare release ebean-parent-12.14.1 2022-01-17 22:30:53 +13:00
Rob Bygrave 25eea42211 Bump to ebean-migration 12.13.0 and ebean-agent 12.14.1 2022-01-17 22:28:36 +13:00
Rob Bygrave 263f6cdeba Tidy OraclePlatform only 2022-01-17 22:10:53 +13:00
Rob Bygrave 38f1967e60 #2473 - Oracle - column alias - Change such that OraclePlatform doesn't use column alias
So with #2254 we regressed OraclePlatform to use column alias. This really should have introduced an Oracle12 platform instead. 18c onwards and we don't want the column alias.
2022-01-17 22:10:21 +13:00
Rob Bygrave 6eb8eb0dd2 #2473 Tidy BasicSqlAnsiLimiter only, no functional change 2022-01-17 21:58:33 +13:00
Rob Bygrave 1165d857d8 Fix test ParentQueryTest 2022-01-17 21:57:14 +13:00
Rob Bygrave 874c2f5231 #2492 - Fix for Oracle platform when insert only default values 2022-01-17 21:42:09 +13:00
Rob Bygrave 88ab852855 No real change, tidy DbExpressionHandlerFactory only 2022-01-17 21:28:45 +13:00
Rob Bygrave b577331524 #2492 - Fix for Oracle platform when insert only default values 2022-01-17 21:27:42 +13:00
Rob Bygrave ec45fc1c86 Fix tests for Oracle - BITADD in @Where clause expression 2022-01-17 21:26:19 +13:00
Rob Bygrave e65876e8ed #2477 - Add failing test case (disable for the moment) 2022-01-17 20:58:19 +13:00
Rob BygraveandGitHub 5453037172 Merge pull request #2491 from ebean-orm/feature/2476-elementCollectionConstructor
#2476 - Fix for element collection on bean with default constructor
2022-01-17 15:41:33 +13:00
Rob Bygrave 9b116c9bf6 #2476 - tidy tests 2022-01-17 15:38:41 +13:00
Rob Bygrave 82862deccc #2476 - Fix for element collection on bean with default constructor
Note that we can look for a better longer term fix which might would look at treating element collection more like ToMany collections in terms of enhancement.
2022-01-17 15:36:26 +13:00
Rob Bygrave 1c83bbe0b0 #2476 - Failing test for element collection on bean with default constructor 2022-01-17 15:33:13 +13:00
Rob BygraveandGitHub f16f34f35f Merge pull request #2488 from ebean-orm/feature/2487
#2487 - ebean-test support Docker In Docker - docker container's hostname rather than hardcoded "localhost"
2022-01-14 16:32:04 +13:00
Rob Bygrave 38bc5b2fea #2488 - refactor extract logic into DockerHost helper, fix RedisSetup, pass docker host to ebean-test-docker 2022-01-14 16:27:04 +13:00
Rob Bygrave 356dc5eb19 #2487 - ebean-test support Docker In Docker - docker container's hostname rather than hardcoded "localhost" 2022-01-13 15:02:38 +13:00
Rob BygraveandGitHub c5779133bc Merge pull request #2485 from FOCONIS/fix-utf8-problems-with-specialchars
Fix utf8 problems with specialchars
2022-01-13 11:05:27 +13:00
Rob BygraveandGitHub b8b6311348 Merge pull request #2447 from FOCONIS/fix-scalartype-problem2
Traverse class hierarchy, when searching for ScalarType
2022-01-13 10:58:05 +13:00
FOCONIS\szemenyei 8eed983fbb restore old test 2022-01-12 08:37:46 +01:00
Rob BygraveandGitHub 7cfecb83a9 Merge pull request #2486 from bigjimlefou/fixWindows
Fix some tests failing on Windows
2022-01-12 09:17:37 +13:00
bigjimlefou e92939a34d Fix failing test on Windows platform 2022-01-11 15:41:20 +01:00
bigjimlefou 408b982eb3 Use of platform-specific specific file separator 2022-01-11 15:41:19 +01:00
bigjimlefou dd7e7711a8 Use of platform-specific specific file separator 2022-01-11 15:41:19 +01:00
bigjimlefou 5a3f7853a6 Fix failing test on Windows platform. 2022-01-11 15:41:19 +01:00
Roland PramlandFOCONIS\szemenyei 01847e44de Always use UTF-8 2022-01-11 10:11:13 +01:00
FOCONIS\szemenyei 0cf9f0bf58 Revert "at first revert solution"
This reverts commit b01b44af10.
2022-01-11 09:49:02 +01:00
FOCONIS\szemenyei b01b44af10 at first revert solution 2022-01-11 09:48:47 +01:00
FOCONIS\szemenyei 8d85000970 Fix Checksum calculate() with special characters
(cherry picked from commit e9efd5a702)
2022-01-11 09:47:44 +01:00
Jonas Pöhler e7f9c63502 Clear up some possible confusion in the test 2022-01-05 15:46:34 +01:00
Jonas Pöhler eb21b41fba Refactor and fixes for interfaces (like Path) + extend existing tests 2022-01-05 14:22:24 +01:00
Jonas Pöhler 3dc6c567a0 Merge remote-tracking branch 'ebean/master' into fix-scalartype-problem2 2022-01-05 13:34:29 +01:00
Rob BygraveandGitHub 18eed47306 Merge pull request #2475 from bigjimlefou/fixLocale
Fix locale problem with TestCsvReader and TestCsvReaderWithCallback.
2021-12-24 11:10:05 +13:00
Cédric Sougné 9ddf1333ed Fix locale problem with TestCsvReader and TestCsvReaderWithCallback. 2021-12-23 17:19:44 +01:00
Rob Bygrave 73896d375f Bump test modules to 12.14.1-SNAPSHOT after release 2021-12-20 16:49:18 +13:00
Rob Bygrave 3f645211a6 [maven-release-plugin] prepare for next development iteration 2021-12-20 16:34:23 +13:00
Rob Bygrave 860aad11ce WIP h2database v2 2021-12-17 23:22:25 +13:00
Roland Praml f830662cce Improve TypeManager - cache typeNotFound information. 2021-12-02 11:18:22 +01:00
Roland Praml 84ac737ceb ADD: Testcase for Calendar 2021-12-02 10:42:49 +01:00
Roland Praml 483f0398b2 Fix: 'No ScalarType registered for class java.util.GregorianCalendar' - check all registered scalar type if no exact match was found 2021-12-02 08:30:24 +01:00
Jonas Pöhler c67e66c0ca Merge remote-tracking branch 'ebean/master' into pr/enh/stored_procedures_mysql_drop_column 2021-11-26 15:58:50 +01:00
Jonas Pöhler b60f9201a9 FIX: MysqlGenerateMigrationTest now clears pendingDrops System-Property 2021-11-26 15:58:20 +01:00
Jonas Pöhler 3fe521c53a Merge remote-tracking branch 'ebean/master' into pr/enh/stored_procedures_mysql_drop_column 2021-09-29 16:13:49 +02:00
Jonas Pöhler 091b5fbf26 Optionally allow use of stored procedures for MySql to drop columns in db migration 2021-09-27 17:08:22 +02:00
273 changed files with 4052 additions and 1707 deletions
+5 -1
View File
@@ -31,6 +31,10 @@ jobs:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: Maven version
run: mvn --version
# - name: Maven single test
# run: mvn --batch-mode clean verify -Dtest="io.ebeaninternal.server.core.DefaultServer_getReferenceTest" -DfailIfNoTests=false
- name: Build with Maven
run: mvn package
run: mvn clean package
+38
View File
@@ -0,0 +1,38 @@
name: DB2 LUW
on:
workflow_dispatch:
schedule:
- cron: '10 8 * * 2,5'
jobs:
build:
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
java_version: [11]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Java
uses: actions/setup-java@v2
with:
java-version: ${{ matrix.java_version }}
distribution: 'adopt'
- name: Maven cache
uses: actions/cache@v2
env:
cache-name: maven-cache
with:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: db2
run: mvn clean test -Dprops.file=testconfig/ebean-db2.properties
@@ -1,5 +1,5 @@
name: MariaDB 10.6
name: MariaDB
on:
workflow_dispatch:
@@ -1,5 +1,5 @@
name: Multi-database platform test
name: Multi-database platform
on: [workflow_dispatch]
@@ -37,9 +37,17 @@ jobs:
run: mvn clean test -Dprops.file=testconfig/ebean-postgres.properties
- name: mysql
run: mvn clean test -Dprops.file=testconfig/ebean-mysql.properties
- name: sqlserver 2019
run: mvn clean test -Dprops.file=testconfig/ebean-sqlserver19.properties
- name: sqlserver 2017
run: mvn clean test -Dprops.file=testconfig/ebean-sqlserver17.properties
- name: mariadb
run: mvn clean test -Dprops.file=testconfig/ebean-mariadb.properties
- name: mariadb
run: mvn clean test -Dprops.file=testconfig/ebean-mariadb.properties
- name: sqlserver
run: mvn clean test -Dprops.file=testconfig/ebean-sqlserver17.properties
- name: yugabyte
run: mvn clean test -Dprops.file=testconfig/ebean-yugabyte.properties
# - name: sqlserver 2019
# run: mvn clean test -Dprops.file=testconfig/ebean-sqlserver19.properties
# - name: db2
# run: mvn clean test -Dprops.file=testconfig/ebean-db2.properties
# - name: oracle
# run: mvn clean test -Dprops.file=testconfig/ebean-oracle.properties
+38
View File
@@ -0,0 +1,38 @@
name: Oracle18
on:
workflow_dispatch:
schedule:
- cron: '10 6 * * 1,4'
jobs:
build:
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
java_version: [11]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Java
uses: actions/setup-java@v2
with:
java-version: ${{ matrix.java_version }}
distribution: 'zulu'
- name: Maven cache
uses: actions/cache@v2
env:
cache-name: maven-cache
with:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: oracle
run: mvn clean test -Dprops.file=testconfig/ebean-oracle.properties
@@ -1,5 +1,5 @@
name: SqlServer 2019 latest
name: SqlServer 2019
on: [workflow_dispatch]
@@ -1,5 +1,5 @@
name: SqlServer 2017 latest
name: SqlServer
on:
workflow_dispatch:
+38
View File
@@ -0,0 +1,38 @@
name: Yugabyte
on:
workflow_dispatch:
schedule:
- cron: '10 3 * * *'
jobs:
build:
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
java_version: [11]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Java
uses: actions/setup-java@v2
with:
java-version: ${{ matrix.java_version }}
distribution: 'adopt'
- name: Maven cache
uses: actions/cache@v2
env:
cache-name: maven-cache
with:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: yugabyte
run: mvn clean test -Dprops.file=testconfig/ebean-yugabyte.properties
+2 -5
View File
@@ -6,8 +6,8 @@
[![Postgres](https://github.com/ebean-orm/ebean/actions/workflows/postgres.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/postgres.yml)
[![MySql](https://github.com/ebean-orm/ebean/actions/workflows/mysql.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mysql.yml)
[![MariaDB](https://github.com/ebean-orm/ebean/actions/workflows/mariadb-10.6.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mariadb-10.6.yml)
[![SqlServer](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver-2017-latest.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver-2017-latest.yml)
[![MariaDB](https://github.com/ebean-orm/ebean/actions/workflows/mariadb.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/mariadb.yml)
[![SqlServer](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/sqlserver.yml)
# Sponsors
<table>
@@ -49,6 +49,3 @@ Goto [https://ebean.io/docs/](https://ebean.io/docs/)
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
## Other versions
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.14.0</version>
<version>12.15.0</version>
</parent>
<name>ebean api</name>
@@ -49,7 +49,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>7.3</version>
<version>7.6</version>
</dependency>
<dependency>
@@ -85,7 +85,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<version>${h2database.version}</version>
<optional>true</optional>
</dependency>
@@ -1,6 +1,7 @@
package io.ebean;
import java.net.URL;
import java.nio.file.Path;
import java.util.Map;
/**
@@ -23,10 +24,10 @@ public interface ScriptRunner {
/**
* Run a script given the resource path (that should start with "/").
*/
void run(String path);
void run(String resourcePath);
/**
* Run a script given the resource path (that should start with "/") and place holders.
* Run a script given the resource path (that should start with "/") and place-holders.
*
* <pre>{@code
*
@@ -38,7 +39,7 @@ public interface ScriptRunner {
*
* }</pre>
*/
void run(String path, Map<String, String> placeholderMap);
void run(String resourcePath, Map<String, String> placeholderMap);
/**
* Run a DDL or SQL script given the resource.
@@ -46,10 +47,20 @@ public interface ScriptRunner {
void run(URL resource);
/**
* Run a DDL or SQL script given the resource and place holders.
* Run a DDL or SQL script given the resource and place-holders.
*/
void run(URL resource, Map<String, String> placeholderMap);
/**
* Run a DDL or SQL script given the file.
*/
void run(Path file);
/**
* Run a DDL or SQL script given the file and place-holders.
*/
void run(Path file, Map<String, String> placeholderMap);
/**
* Run the raw provided DDL or SQL script.
*
@@ -371,6 +371,33 @@ public class DatabaseConfig {
*/
private String uuidStateFile;
/**
* The node id (=mac address) for Version 1 UUIDs. There are several options:
* <ul>
* <li><code>null</code> (default) The generator tries to get the hardwarwe MAC
* address. If this fails, it will fall back to 'generate' mode.</li>
* <li><code>"generate"</code> Hardware detection is skipped. It generates a
* random identifier and tries to persist this to the state file. This nodeId
* will be reused on next start. If persisting to the state file will fail also,
* it will fall back to 'random' mode.<br>
* This mode is good, if the MAC address is not reliable, e.g. if you run
* multiple ebean instances on the same machine.</li>
* <li><code>"random"</code> In this mode, a random node id is generated on each
* start. No stateFile is used. it will generate a new nodeId on each
* application start.<br>
* This mode is good, if you have no write access to save the state file.</li>
* <li><code>"xx-xx-xx-xx-xx-xx"</code> When an explicit nodeId is specified,
* this one is used.</li>
* </ul>
* Note: It is possible that multiple servers are sharing the same state file as
* long as they are in the <b>same</b> JVM/ClassLoader scope. In this case it is
* recommended to use the same uuidNodeId configuration.
*
* If you have multiple servers in different JVMs, do <b>not</b> share the state
* files!
*/
private String uuidNodeId;
/**
* The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
*/
@@ -2019,6 +2046,20 @@ public class DatabaseConfig {
public void setUuidStateFile(String uuidStateFile) {
this.uuidStateFile = uuidStateFile;
}
/**
* Returns the V1-UUID-NodeId
*/
public String getUuidNodeId() {
return uuidNodeId;
}
/**
* Sets the V1-UUID-NodeId.
*/
public void setUuidNodeId(String uuidNodeId) {
this.uuidNodeId = uuidNodeId;
}
/**
* Return true if LocalTime should be persisted with nanos precision.
@@ -2888,6 +2929,7 @@ public class DatabaseConfig {
uuidVersion = p.getEnum(UuidVersion.class, "uuidVersion", uuidVersion);
uuidStateFile = p.get("uuidStateFile", uuidStateFile);
uuidNodeId = p.get("uuidNodeId", uuidNodeId);
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
@@ -66,6 +66,8 @@ public class PlatformConfig {
private boolean caseSensitiveCollation = true;
private boolean useMigrationStoredProcedures = false;
/**
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
*/
@@ -90,6 +92,7 @@ public class PlatformConfig {
this.geometrySRID = platformConfig.geometrySRID;
this.dbUuid = platformConfig.dbUuid;
this.caseSensitiveCollation = platformConfig.caseSensitiveCollation;
this.useMigrationStoredProcedures = platformConfig.useMigrationStoredProcedures;
this.allQuotedIdentifiers = platformConfig.allQuotedIdentifiers;
this.databaseInetAddressVarchar = platformConfig.databaseInetAddressVarchar;
this.customDbTypeMappings = platformConfig.customDbTypeMappings;
@@ -142,6 +145,20 @@ public class PlatformConfig {
this.caseSensitiveCollation = caseSensitiveCollation;
}
/**
* Return true if force use of helper stored procedures for migrations.
*/
public boolean isUseMigrationStoredProcedures() {
return useMigrationStoredProcedures;
}
/**
* Set true to force use of helper stored procedures for migrations.
*/
public void setUseMigrationStoredProcedures(boolean useMigrationStoredProcedures) {
this.useMigrationStoredProcedures = useMigrationStoredProcedures;
}
/**
* Return true if Postgres FOR UPDATE should use the NO KEY option.
*/
@@ -317,6 +334,7 @@ public class PlatformConfig {
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
caseSensitiveCollation = p.getBoolean("caseSensitiveCollation", caseSensitiveCollation);
useMigrationStoredProcedures = p.getBoolean("useMigrationStoredProcedures", useMigrationStoredProcedures);
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
if (dbUuid != null) {
@@ -7,13 +7,10 @@ public class BasicSqlAnsiLimiter implements BasicSqlLimiter {
@Override
public String limit(String dbSql, int firstRow, int maxRows) {
StringBuilder sb = new StringBuilder(50 + dbSql.length());
sb.append(dbSql);
if (firstRow > 0) {
sb.append(" ").append("offset");
sb.append(" ").append(firstRow).append(" rows");
sb.append(" offset ").append(firstRow).append(" rows");
}
if (maxRows > 0) {
sb.append(" fetch next ").append(maxRows).append(" rows only");
@@ -15,6 +15,7 @@ import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@@ -53,6 +54,17 @@ public class DatabasePlatform {
protected boolean supportsSavepointId = true;
protected boolean useMigrationStoredProcedures = false;
/**
* Can we use native java time API objects in
* {@link ResultSet#getObject(int, Class)} and
* {@link PreparedStatement#setObject(int, Object)}.
*
* Not all drivers (DB2 e.g.) will support this.
*/
protected boolean supportsNativeJavaTime = true;
/**
* The behaviour used when ending a read only transaction at read committed isolation level.
*/
@@ -202,6 +214,8 @@ public class DatabasePlatform {
*/
protected PersistBatch persistBatchOnCascade = PersistBatch.ALL;
protected int maxInBinding;
/**
* The maximum length of table names - used specifically when derived
* default table names for intersection tables.
@@ -237,6 +251,7 @@ public class DatabasePlatform {
public void configure(PlatformConfig config) {
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
this.caseSensitiveCollation = config.isCaseSensitiveCollation();
this.useMigrationStoredProcedures = config.isUseMigrationStoredProcedures();
configureIdType(config.getIdType());
configure(config, config.isAllQuotedIdentifiers());
}
@@ -343,6 +358,13 @@ public class DatabasePlatform {
return supportsSavepointId;
}
/**
* Return true if migrations should use stored procedures.
*/
public boolean isUseMigrationStoredProcedures() {
return useMigrationStoredProcedures;
}
/**
* Return true if the platform supports LIMIT with sql update.
*/
@@ -350,6 +372,13 @@ public class DatabasePlatform {
return inlineSqlUpdateLimit;
}
/**
* Return the maximum number of bind values this database platform allows or zero for no limit.
*/
public int getMaxInBinding() {
return maxInBinding;
}
/**
* Return the maximum table name length.
* <p>
@@ -573,6 +602,10 @@ public class DatabasePlatform {
this.supportsResultSetConcurrencyModeUpdatable = supportsResultSetConcurrencyModeUpdatable;
}
public void setUseMigrationStoredProcedures(final boolean useMigrationStoredProcedures) {
this.useMigrationStoredProcedures = useMigrationStoredProcedures;
}
/**
* Normally not needed - overridden in CockroachPlatform.
*/
@@ -813,4 +846,8 @@ public class DatabasePlatform {
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append(likeEscapeChar).append(ch);
}
public boolean supportsNativeJavaTime() {
return supportsNativeJavaTime;
}
}
@@ -31,14 +31,39 @@ public class SqlCodeTranslator implements SqlExceptionTranslator {
this.map = Collections.emptyMap();
}
@Override
public PersistenceException translate(String message, SQLException e) {
private DataErrorType getErrorType(SQLException e) {
DataErrorType errorType = map.get(e.getSQLState());
if (errorType == null) {
// fall back to error code
errorType = map.get(String.valueOf(e.getErrorCode()));
}
return errorType;
}
@Override
public PersistenceException translate(String message, SQLException e) {
DataErrorType errorType = getErrorType(e);
// for DB2 we must inspect the sql exception chain to determine which
// persistence error occurred in a batch execution. We also concatenate
// the error messages to improve error analysis.
SQLException chain = e.getNextException();
if (chain != null) {
StringBuilder sb = new StringBuilder(message);
int i = 1;
while (chain != null && i < 100000) { // prevents from endless loop
sb.append("\n\t#").append(i++).append(": ").append(chain.getMessage());
if (errorType == null) {
errorType = getErrorType(chain);
if (errorType != null) {
sb.append(" (causing error)"); // mark the line, where we found a matching error code
}
}
chain = chain.getNextException();
}
message = sb.toString();
}
if (errorType != null) {
switch (errorType) {
case AcquireLock:
@@ -15,21 +15,19 @@ import java.sql.Types;
/**
* DB2 specific platform.
*/
public class DB2Platform extends DatabasePlatform {
public abstract class BaseDB2Platform extends DatabasePlatform {
public DB2Platform() {
public BaseDB2Platform() {
super();
this.platform = Platform.DB2;
// Note: DB2 (at least LUW supports length up to 128)
// TOOD: Check if we need to introduce a new platform (DB2_LUW_11 ?)
this.maxTableNameLength = 18;
this.maxConstraintNameLength = 18;
this.supportsNativeJavaTime = false;
this.truncateTable = "truncate table %s reuse storage ignore delete triggers immediate";
this.likeClauseRaw = "like ?";
this.sqlLimiter = new Db2SqlLimiter();
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.idInExpandedForm = true; // Db2 does not support (a,b) in ((?,?),(?,?))
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("40001","57033") // key -911/-913
@@ -45,7 +43,6 @@ public class DB2Platform extends DatabasePlatform {
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false));
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 16, 3));
persistBatchOnCascade = PersistBatch.NONE;
}
/**
@@ -0,0 +1,39 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.SqlErrorCodes;
import java.sql.Types;
/**
* DB2 specific platform for i Series.
*
* @author Cédric Sougné
*/
public class DB2ForIPlatform extends BaseDB2Platform {
public DB2ForIPlatform() {
super();
this.platform = Platform.DB2FORI;
// Note: IBM i from 7.1 allow up to to 128
// TODO: Check if we need to introduce older platform (DB2ForI_6 ? but older
// documentation is not anymore published on ibm.com),
this.maxTableNameLength = 128;
this.maxConstraintNameLength = 128;
this.dbIdentity.setSupportsIdentity(true);
this.exceptionTranslator = new SqlErrorCodes().addAcquireLock("57033") // key -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", "23507", "23511", "23512", "23513", "42917", "23515")
.build();
booleanDbType = Types.SMALLINT;
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("smallint default 0"));
}
}
@@ -0,0 +1,22 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
/**
* DB2 specific platform for older DB2 versions. This platform is here for
* compatibility reasons. It uses a length limit of 18 chars for table and
* constraint names. Newer DB2 versions will support up to 128. It is strongly
* recommended to migrate to db2luw/DB2ForI or db2zos platform.
*/
public class DB2LegacyPlatform extends BaseDB2Platform {
public DB2LegacyPlatform() {
super();
this.platform = Platform.DB2;
// Note: DB2 (at least LUW supports length up to 128)
// TOOD: Check if we need to introduce a new platform (DB2_LUW_11 ?)
this.maxTableNameLength = 18;
this.maxConstraintNameLength = 18;
this.persistBatchOnCascade = PersistBatch.NONE;
}
}
@@ -0,0 +1,17 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.annotation.Platform;
/**
* DB2 platform for Linux/Unix/Windows.
* @author Roland Praml, FOCONIS AG
*
*/
public class DB2LuwPlatform extends BaseDB2Platform {
public DB2LuwPlatform() {
super();
this.platform = Platform.DB2LUW;
this.maxTableNameLength = 128;
this.maxConstraintNameLength = 128;
}
}
@@ -19,7 +19,7 @@ public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator {
public DB2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "values nextval for " + seqName;
this.unionBaseSql = " union " + baseSql;
this.unionBaseSql = ", nextval for " + seqName;
}
@Override
@@ -0,0 +1,18 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.annotation.Platform;
/**
* DB2 platform for z/OS.
* Note: This platform is currently not tested!
* @author Roland Praml, FOCONIS AG
*
*/
public class DB2ZosPlatform extends BaseDB2Platform {
public DB2ZosPlatform() {
super();
this.platform = Platform.DB2ZOS;
this.maxTableNameLength = 128;
this.maxConstraintNameLength = 128;
}
}
@@ -15,10 +15,13 @@ public class Db2SqlLimiter implements SqlLimiter {
sb.append("distinct ");
}
sb.append(request.getDbSql());
int firstRow = request.getFirstRow();
if (firstRow > 0) {
sb.append(" offset ").append(firstRow).append(" rows");
}
int maxRows = request.getMaxRows();
if (maxRows > 0) {
sb.append(" ").append(NEW_LINE).append("FETCH FIRST ").append(maxRows).append(" ROWS ONLY");
sb.append(" fetch next ").append(maxRows).append(" rows only");
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
@@ -39,6 +39,7 @@ public class H2Platform extends DatabasePlatform {
this.dbIdentity.setSupportsIdentity(true);
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
}
/**
@@ -18,13 +18,12 @@ public class H2SequenceIdGenerator extends SequenceBatchIdGenerator {
*/
public H2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select " + seqName + ".nextval";
this.baseSql = "select nextval('" + seqName + "')";
this.unionBaseSql = " union " + baseSql;
}
@Override
public String getSql(int batchSize) {
StringBuilder sb = new StringBuilder();
sb.append(baseSql);
for (int i = 1; i < batchSize; i++) {
@@ -11,6 +11,7 @@ public class Oracle11Platform extends OraclePlatform {
public Oracle11Platform() {
super();
this.platform = Platform.ORACLE11;
this.columnAliasPrefix = "c";
this.sqlLimiter = new OracleRownumSqlLimiter();
this.basicSqlLimiter = new OracleRownumBasicLimiter();
dbIdentity.setIdType(IdType.SEQUENCE);
@@ -0,0 +1,13 @@
package io.ebean.config.dbplatform.oracle;
/**
* Oracle 12 platform using column alias.
*/
public class Oracle12Platform extends OraclePlatform {
public Oracle12Platform() {
super();
//this.platform = Platform.ORACLE12;
this.columnAliasPrefix = "c";
}
}
@@ -22,8 +22,8 @@ public class OraclePlatform extends DatabasePlatform {
public OraclePlatform() {
super();
this.platform = Platform.ORACLE;
this.columnAliasPrefix = "c";
this.supportsDeleteTableAlias = true;
this.maxInBinding = 1000;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
this.dbEncrypt = new OracleDbEncrypt();
@@ -35,11 +35,9 @@ public class OraclePlatform extends DatabasePlatform {
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 ?";
@@ -56,7 +54,6 @@ public class OraclePlatform extends DatabasePlatform {
booleanDbType = Types.INTEGER;
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("number(1)"));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("number", 10));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("number", 19));
dbTypeMap.put(DbType.REAL, new DbPlatformType("number", 19, 4));
@@ -65,12 +62,10 @@ public class OraclePlatform extends DatabasePlatform {
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("number", 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 16, 3));
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("varchar2", 255));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("blob"));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("clob"));
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("raw", 255));
dbTypeMap.put(DbType.BINARY, new DbPlatformType("raw", 255));
dbTypeMap.put(DbType.TIME, new DbPlatformType("timestamp"));
}
@@ -27,6 +27,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
// SQL Server unless we are using sequences
this.dbEncrypt = new SqlServerDbEncrypt();
this.persistBatchOnCascade = PersistBatch.NONE;
this.maxInBinding = 2000;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
this.selectCountWithColumnAlias = true;
@@ -119,4 +120,9 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
// for update are hints on from clause of base table
return sql;
}
@Override
public boolean isUseMigrationStoredProcedures() {
return true;
}
}
@@ -0,0 +1,12 @@
package io.ebean.config.dbplatform.yugabyte;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
public class YugabytePlaform extends PostgresPlatform {
public YugabytePlaform() {
super();
this.platform = Platform.YUGABYTE;
}
}
@@ -18,7 +18,7 @@ import java.util.Locale;
* try {
* File f = new File("src/test/resources/test1.csv");
*
* FileReader reader = new FileReader(f);
* FileReader reader = new FileReader(f, encoding);
*
* CsvReader<Customer> csvReader = DB.createCsvReader(Customer.class);
*
@@ -0,0 +1,48 @@
package io.ebean.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
/**
* Utilities for IO. It uses UTF-8 as encoding when reading/writing and uses
* buffered IO for better performance.
*/
public class IOUtils {
/**
* Read from stream as UTF-8.
*/
public static BufferedReader newReader(InputStream is) {
return new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
}
/**
* Read from file as UTF-8.
*/
public static BufferedReader newReader(File file) throws FileNotFoundException {
return newReader(new FileInputStream(file));
}
/**
* Write to stream as UTF-8
*/
public static BufferedWriter newWriter(OutputStream os) {
return new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
}
/**
* Write to file as UTF-8
*/
public static BufferedWriter newWriter(File file) throws FileNotFoundException {
return newWriter(new FileOutputStream(file));
}
}
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.14.0</version>
<version>12.15.0</version>
</parent>
<!-- <parent>-->
<!-- <groupId>org.avaje</groupId>-->
@@ -14,7 +14,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.14.0</tag>
<tag>ebean-parent-12.15.0</tag>
</scm>
<name>ebean autotune</name>
@@ -26,7 +26,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
<scope>provided</scope>
</dependency>
@@ -34,7 +34,7 @@
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
<version>2.3.6</version>
<scope>provided</scope>
</dependency>
@@ -63,7 +63,7 @@
<extensions>true</extensions>
<configuration>
<tiles>
<tile>io.ebean.tile:enhancement:12.11.3</tile>
<tile>io.ebean.tile:enhancement:12.15.0</tile>
<tile>io.avaje.tile:moditech-module:1.0</tile>
</tiles>
</configuration>
+15 -15
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.14.0</version>
<version>12.15.0</version>
</parent>
<name>ebean bom</name>
@@ -71,88 +71,88 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.14.0</version>
<version>12.15.0</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
@@ -47,6 +47,8 @@ public interface DataReader {
Object getObject() throws SQLException;
<T> T getObject(Class<T> cls) throws SQLException;
InputStream getBinaryStream() throws SQLException;
/**
+7 -7
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.14.0</version>
<version>12.15.0</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -15,7 +15,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.14.0</tag>
<tag>ebean-parent-12.15.0</tag>
</scm>
<dependencies>
@@ -41,19 +41,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
</dependency>
<dependency>
@@ -78,7 +78,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<version>${h2database.version}</version>
<scope>provided</scope>
</dependency>
@@ -136,7 +136,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.24</version>
<version>42.3.2</version>
<optional>true</optional>
</dependency>
@@ -167,6 +167,7 @@ public final class BindParams implements Serializable {
*/
@SuppressWarnings("rawtypes")
public void setParameter(int position, Object value) {
assert value != null : "use setNullParameter";
Param p = getParam(position);
if (value instanceof Collection) {
// use of postgres ANY with positioned parameter
@@ -218,6 +219,7 @@ public final class BindParams implements Serializable {
* Set a named In parameter that is not null.
*/
public Param setParameter(String name, Object value) {
assert value != null : "use setNullParameter";
Param p = getParam(name);
p.setInValue(value);
return p;
@@ -3,20 +3,20 @@ package io.ebeaninternal.server.core;
import io.ebean.ScriptRunner;
import io.ebean.ddlrunner.DdlRunner;
import io.ebean.ddlrunner.ScriptTransform;
import io.ebean.util.IOUtils;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.util.UrlHelper;
import javax.persistence.PersistenceException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.io.*;
import java.net.URL;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import static java.util.Objects.requireNonNull;
final class DScriptRunner implements ScriptRunner {
private static final String NEWLINE = "\n";
@@ -30,8 +30,8 @@ final class DScriptRunner implements ScriptRunner {
}
@Override
public void run(String path) {
run(path, null);
public void run(String resourcePath) {
run(resourcePath, null);
}
@Override
@@ -49,26 +49,42 @@ final class DScriptRunner implements ScriptRunner {
run(resource, null, placeholderMap);
}
@Override
public void run(Path file) {
run(file, null);
}
@Override
public void run(Path file, Map<String, String> placeholderMap) {
requireNonNull(file);
String scriptName = file.toFile().getName();
String content = fileContent(file);
runScript(content, scriptName, placeholderMap, false);
}
private void run(URL resource, String scriptName, Map<String, String> placeholderMap) {
if (resource == null) {
throw new IllegalArgumentException("resource is null?");
}
requireNonNull(resource);
if (scriptName == null) {
scriptName = resource.getFile();
}
String content = content(resource);
runScript(content, scriptName, placeholderMap, false);
}
private String content(URL resource) {
if (resource == null) {
throw new IllegalArgumentException("resource is null?");
private String fileContent(Path file) {
try (InputStream inputStream = new FileInputStream(file.toFile());
Reader reader = IOUtils.newReader(inputStream)) {
return readContent(reader);
} catch (IOException e) {
throw new PersistenceException("Failed to read script content", e);
}
}
try (InputStream inputStream = UrlHelper.openNoCache(resource)) {
return readContent(new InputStreamReader(inputStream));
private String content(URL resource) {
requireNonNull(resource);
try (InputStream inputStream = UrlHelper.openNoCache(resource);
Reader reader = IOUtils.newReader(inputStream)) {
return readContent(reader);
} catch (IOException e) {
throw new PersistenceException("Failed to read script content", e);
}
@@ -84,10 +100,9 @@ final class DScriptRunner implements ScriptRunner {
*/
private void runScript(String content, String scriptName, Map<String, String> placeholderMap, boolean useAutoCommit) {
try {
if (placeholderMap != null) {
if (placeholderMap != null && !placeholderMap.isEmpty()) {
content = ScriptTransform.build(null, placeholderMap).transform(content);
}
try (Connection connection = obtainConnection()) {
DdlRunner runner = new DdlRunner(useAutoCommit, scriptName, platformName);
runner.runAll(content, connection);
@@ -109,7 +124,6 @@ final class DScriptRunner implements ScriptRunner {
}
private String readContent(Reader reader) throws IOException {
StringBuilder buf = new StringBuilder();
try (LineNumberReader lineReader = new LineNumberReader(reader)) {
String line;
@@ -4,7 +4,10 @@ import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.db2.DB2ForIPlatform;
import io.ebean.config.dbplatform.db2.DB2LegacyPlatform;
import io.ebean.config.dbplatform.db2.DB2LuwPlatform;
import io.ebean.config.dbplatform.db2.DB2ZosPlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
@@ -13,6 +16,7 @@ import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.Oracle11Platform;
import io.ebean.config.dbplatform.oracle.Oracle12Platform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.Postgres9Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
@@ -20,16 +24,14 @@ import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform;
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
import io.ebean.config.dbplatform.yugabyte.YugabytePlaform;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.api.DbOffline;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.*;
import java.util.Locale;
/**
* Create a DatabasePlatform from the configuration.
@@ -87,6 +89,9 @@ public class DatabasePlatformFactory {
if (dbName.equals("oracle11") || dbName.equals("oracle10") || dbName.equals("oracle9")) {
return new Oracle11Platform();
}
if (dbName.equals("oracle12")) {
return new Oracle12Platform();
}
if (dbName.equals("oracle")) {
return new OraclePlatform();
}
@@ -103,7 +108,19 @@ public class DatabasePlatformFactory {
return new SqlAnywherePlatform();
}
if (dbName.equals("db2")) {
return new DB2Platform();
throw new IllegalArgumentException("Please choose the more specific db2luw/db2zos/db2fori platform. Refer to issue #2514 for details");
}
if (dbName.equals("db2legacy")) {
return new DB2LegacyPlatform();
}
if (dbName.equals("db2zos")) {
return new DB2ZosPlatform();
}
if (dbName.equals("db2fori")) {
return new DB2ForIPlatform();
}
if (dbName.equals("db2luw")) {
return new DB2LuwPlatform();
}
if (dbName.equals("clickhouse")) {
return new ClickHousePlatform();
@@ -149,7 +166,8 @@ public class DatabasePlatformFactory {
} else if (dbProductName.contains("hsql database engine")) {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
return readPostgres(connection, majorVersion);
String productVersion = metaData.getDatabaseProductVersion().toLowerCase(Locale.ENGLISH);
return readPostgres(connection, majorVersion, productVersion);
} else if (dbProductName.contains("mariadb")) {
return new MariaDbPlatform();
} else if (dbProductName.contains("mysql")) {
@@ -159,7 +177,7 @@ public class DatabasePlatformFactory {
} else if (dbProductName.contains("sqlite")) {
return new SQLitePlatform();
} else if (dbProductName.contains("db2")) {
return new DB2Platform();
throw new IllegalArgumentException("For DB2 please explicitly choose either db2legacy/db2luw/db2zos/db2fori platform. Refer to issue #2514 for details");
} else if (dbProductName.contains("sql anywhere")) {
return new SqlAnywherePlatform();
} else if (dbProductName.contains("hdb")) {
@@ -173,7 +191,13 @@ public class DatabasePlatformFactory {
}
private DatabasePlatform oracleVersion(int majorVersion) {
return majorVersion < 12 ? new Oracle11Platform() : new OraclePlatform();
if (majorVersion < 12) {
return new Oracle11Platform();
}
if (majorVersion < 13) {
return new Oracle12Platform();
}
return new OraclePlatform();
}
private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) {
@@ -186,11 +210,14 @@ public class DatabasePlatformFactory {
/**
* Use a select version() query as it could be Postgres or CockroachDB.
*/
private static DatabasePlatform readPostgres(Connection connection, int majorVersion) {
private static DatabasePlatform readPostgres(Connection connection, int majorVersion, String productVersion) {
if (productVersion.contains("-yb-")) {
return new YugabytePlaform();
}
try (PreparedStatement statement = connection.prepareStatement("select version() as \"version\"")) {
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
String productVersion = resultSet.getString("version").toLowerCase();
productVersion = resultSet.getString("version").toLowerCase();
if (productVersion.contains("cockroach")) {
return new CockroachPlatform();
}
@@ -613,6 +613,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
try (Connection connection = dataSource().getConnection()) {
for (String table : tables) {
executeSql(connection, databasePlatform.truncateStatement(table));
if (databasePlatform.getPlatform().base() == Platform.DB2) {
// DB2 requires commit after each truncate statement
connection.commit();
}
}
connection.commit();
} catch (SQLException e) {
@@ -99,7 +99,6 @@ final class InitDataSource {
if (config.getDatabasePlatformName() == null) {
throw new PersistenceException("You MUST specify a DatabasePlatformName on DatabaseConfig when offline");
}
return null;
}
attachAlert(dsConfig);
@@ -280,7 +280,7 @@ public final class InternalConfiguration {
private MultiValueBind createMultiValueBind(Platform platform) {
// only Postgres at this stage
if (platform.base() == Platform.POSTGRES) {
if (platform.base() == Platform.POSTGRES || platform.base() == Platform.YUGABYTE) {
return new PostgresMultiValueBind();
}
return new MultiValueBind();
@@ -40,6 +40,9 @@ public final class PersistDeferredRelationship {
// bind the set clause for the importedId
int pos = importedId.bind(1, sqlUpdate, assocBean);
if (pos == -1) {
return; // could not bind: TODO: should we log/throw an error?
}
// bind the where clause for the bean
Object[] idValues = beanDescriptor.idBinder().getIdValues(bean);
for (int j = 0; j < idValues.length; j++) {
@@ -86,6 +86,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
private final ConcurrentHashMap<String, ElPropertyDeploy> elDeployCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, ElComparator<T>> comparatorCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, STreeProperty> dynamicProperty = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Map<String,String>> pathMaps = new ConcurrentHashMap<>();
private final Map<String, SpiRawSql> namedRawSql;
private final Map<String, String> namedQuery;
private final boolean multiValueSupported;
@@ -2775,6 +2777,22 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
return draftableElement;
}
@Override
public Map<String, String> pathMap(String prefix) {
return pathMaps.computeIfAbsent(prefix, s -> {
HashMap<String, String> m = new HashMap<>();
for (STreePropertyAssocMany many : propsMany()) {
String name = many.name();
m.put(name, prefix + "." + name);
}
for (STreePropertyAssocOne one : propsOne()) {
String name = one.name();
m.put(name, prefix + "." + name);
}
return m.isEmpty() ? Collections.emptyMap() : m;
});
}
@Override
public boolean isEmbeddedPath(String propertyPath) {
ElPropertyDeploy elProp = elPropertyDeploy(propertyPath);
@@ -1160,7 +1160,26 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
public boolean isLob() {
return lob;
}
/**
* Returns true if this <code>isLob()</code> or the type will effectively map to a lob.
*/
public boolean isDbLob() {
if (lob) {
return true;
}
switch (dbType) {
case DbPlatformType.JSON:
case DbPlatformType.JSONB:
return dbLength == 0; // must be analog to DbPlatformTypeMapping.lookup
case DbPlatformType.JSONBlob:
case DbPlatformType.JSONClob:
return true;
default:
return false;
}
}
public static boolean isLobType(int type) {
switch (type) {
case Types.CLOB:
@@ -226,7 +226,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
*/
@Override
public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean, boolean withCheck) {
BeanCollection<?> bc = (BeanCollection<?>) super.getValue(parentBean);
BeanCollection<?> bc = beanCollection(parentBean);
if (bc == null) {
bc = help.createEmpty(parentBean);
setValue(parentBean, bc);
@@ -234,6 +234,15 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
help.add(bc, detailBean, withCheck);
}
private BeanCollection<?> beanCollection(EntityBean parentBean) {
try {
return (BeanCollection<?>) super.getValue(parentBean);
} catch (ClassCastException e) {
// fetching element collection, ok for now
return null;
}
}
/**
* Return true if this is considered 'empty' from a save perspective.
*/
@@ -62,4 +62,9 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase {
ctx.appendParseSelect(parsedFormula, alias);
}
@Override
public boolean isDbLob() {
return false;
}
}
@@ -85,6 +85,9 @@ public final class ImportedIdEmbedded implements ImportedId {
for (ImportedIdSimple anImported : imported) {
if (anImported.owner.isUpdateable()) {
Object scalarValue = anImported.foreignProperty.getValue(embedded);
if (scalarValue == null) {
return -1; // could not bind
}
update.setParameter(pos++, scalarValue);
}
}
@@ -151,6 +151,9 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
@Override
public int bind(int position, SqlUpdate update, EntityBean bean) {
Object value = getIdValue(bean);
if (value == null) {
return -1; // could not bind
}
update.setParameter(position, value);
return ++position;
}
@@ -775,7 +775,7 @@ public class DeployBeanDescriptor<T> {
this.identityMode.setIdType(IdType.EXTERNAL);
switch (config.getUuidVersion()) {
case VERSION1:
this.idGenerator = UuidV1IdGenerator.getInstance(config.getUuidStateFile());
this.idGenerator = UuidV1IdGenerator.getInstance(config.getUuidStateFile(), config.getUuidNodeId());
break;
case VERSION1RND:
this.idGenerator = UuidV1RndIdGenerator.INSTANCE;
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import java.util.LinkedHashMap;
@@ -105,8 +106,11 @@ public final class DeployInherit {
if (da != null) {
// lowercase the discriminator column for RawSql and JSON
info.setColumnName(da.name().toLowerCase());
info.setColumnType(da.discriminatorType());
info.setColumnLength(da.length());
DiscriminatorType discriminatorType = da.discriminatorType();
info.setColumnType(discriminatorType);
if (discriminatorType == DiscriminatorType.STRING) {
info.setColumnLength(da.length());
}
info.setColumnDefn(da.columnDefinition());
}
if (!info.isAbstract()) {
@@ -0,0 +1,13 @@
package io.ebeaninternal.server.expression.platform;
/**
* DB2 handling of platform specific expressions. ARRAY expressions not supported.
*/
final class Db2DbExpression extends BasicDbExpression {
@Override
public String concat(String property0, String separator, String property1, String suffix) {
return concatOperator(property0, separator, property1, suffix);
}
}
@@ -10,23 +10,22 @@ public final class DbExpressionHandlerFactory {
* Create and return the appropriate platform specific handing of expressions.
*/
public static DbExpressionHandler from(DatabasePlatform databasePlatform) {
Platform platform = databasePlatform.getPlatform();
Platform platform = databasePlatform.getPlatform().base();
switch (platform) {
case H2:
return new H2DbExpression();
case POSTGRES:
case POSTGRES9:
case YUGABYTE:
case COCKROACH:
return new PostgresDbExpression();
case MARIADB:
return new MariaDbExpression();
case MYSQL55:
case MYSQL:
return new MySqlDbExpression();
case ORACLE:
case ORACLE11:
return new OracleDbExpression();
case SQLSERVER16:
case SQLSERVER17:
case DB2:
return new Db2DbExpression();
case SQLSERVER:
return new SqlServerDbExpression();
case HANA:
@@ -10,6 +10,7 @@ final class H2DbExpression extends BasicDbExpression {
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
bitwiseFunction(request, propName, operator, compare);
String funcName = functionName(operator);
request.append(funcName).append("(").append(propName).append(", cast(? as long)) ").append(compare).append(" cast(? as long)");
}
}
@@ -12,7 +12,8 @@ import java.util.concurrent.ConcurrentHashMap;
* It extends the UuidV1RndIdGenerator so that it can generate rfc4122 compliant Type 1 UUIDs.
*
* This generator produces real Type 1 UUIDs (best for sqlserver) - You should use this generator only,
* if you can guarantee, that mac addess is uniqe.
* if you can guarantee, that mac addess is uniqe or specify 'ebean.uuidNodeId' in your config file.
*
*/
public class UuidV1IdGenerator extends UuidV1RndIdGenerator {
@@ -25,38 +26,36 @@ public class UuidV1IdGenerator extends UuidV1RndIdGenerator {
/**
* Returns an instance for given file.
*/
public static UuidV1IdGenerator getInstance(String file) {
return INSTANCES.computeIfAbsent(new File(file), UuidV1IdGenerator::new);
public static UuidV1IdGenerator getInstance(String file, String nodeId) {
return getInstance(new File(file), nodeId);
}
/**
* Returns an instance for given file.
*/
public static UuidV1IdGenerator getInstance(File file) {
return INSTANCES.computeIfAbsent(file, UuidV1IdGenerator::new);
public static UuidV1IdGenerator getInstance(File file, String nodeId) {
return INSTANCES.computeIfAbsent(file, f ->new UuidV1IdGenerator(f, nodeId == null ? null : nodeId.toLowerCase()));
}
/**
* Returns an alternative node id - set with the 'ebean.uuid.nodeId' system property.
*/
private static byte[] getAlternativeNodeId() {
private static byte[] parseAlternativeNodeId(String altNodeId) {
String[] components = altNodeId.split("-");
if (components.length != 6) {
throw new IllegalArgumentException(altNodeId + " is invalid. Expected format: xx-xx-xx-xx-xx-xx");
}
try {
String altNodeId = System.getProperty("ebean.uuid.nodeId");
if (altNodeId != null) {
String[] components = altNodeId.split("-");
if (components.length != 5) {
throw new IllegalArgumentException("Invalid nodeId string: " + altNodeId);
}
byte[] nodeId = new byte[6];
for (int i=0; i<5; i++) {
nodeId[i] = Byte.decode("0x"+components[i]).byteValue();
}
return nodeId;
byte[] nodeId = new byte[6];
for (int i = 0; i < 6; i++) {
// do not use Byte.parseByte
// https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6259307
nodeId[i] = (byte) Integer.parseInt(components[i], 16);
}
} catch (SecurityException se) {
// ignore
return nodeId;
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException(altNodeId + " is invalid.", iae);
}
return null;
}
/**
@@ -108,43 +107,85 @@ public class UuidV1IdGenerator extends UuidV1RndIdGenerator {
* Creates a new instance of UuidGenerator. Note that there should not be more
* than one instance per stateFile.
*/
private UuidV1IdGenerator(final File stateFile) {
private UuidV1IdGenerator(final File stateFile, String altNodeId) {
super();
this.stateFile = stateFile;
try {
// See, if there is an alternative MAC address set.
nodeId = getAlternativeNodeId();
if (nodeId != null) {
log.info("Using alternative MAC {} to generate Type 1 UUIDs", getNodeIdentifier());
if (altNodeId == null) {
// using hardware mode
tryHardwareId();
} else if (altNodeId.equals("generate")) {
tryGenerateMode();
} else if (altNodeId.equals("random")) {
useRandomMode();
} else {
nodeId = getHardwareId();
log.info("Using MAC {} to generate Type 1 UUIDs", getNodeIdentifier());
// See, if there is an alternative MAC address set.
nodeId = parseAlternativeNodeId(altNodeId);
restoreState();
log.info("Explicitly using ID {} to generate Type 1 UUIDs", getNodeIdentifier());
}
if (nodeId == null) {
canSaveState = false;
// RFC 4.5 use random portion for node
nodeId = super.getNodeIdBytes();
log.error("Have to fall back to random node identifier {} (Reason: No suitable network interface found)", getNodeIdentifier());
UUID uuid = nextId(null);
long ts = timeStamp.get();
ts -= UUID_EPOCH_OFFSET;
ts /= MILLIS_TO_UUID;
} else {
boolean flag = restoreState();
UUID uuid = nextId(null);
long ts = timeStamp.get();
ts -= UUID_EPOCH_OFFSET;
ts /= MILLIS_TO_UUID;
saveState();
log.debug("RestoreState: {}, ClockSeq {}, Timestamp {}, uuid {}, stateFile: {})", flag, clockSeq.get(),
new Date(ts), uuid, stateFile);
}
saveState();
log.debug("Saved state: clockSeq {}, timestamp {}, uuid {}, stateFile: {})", clockSeq.get(), new Date(ts), uuid, stateFile);
} catch (IOException e) {
canSaveState = false;
log.error("There was a problem while detecting the nodeId. Falling back to random mode. Try using to specify 'ebean.uuidNodeId' property", e);
useRandomMode();
}
}
/**
* Tries to initialize the generator by retrieving the MAC address from
* hardware. If there is no suitable network interface found, it will fall back
* to "generate" mode.
*
* @throws IOException if state file is not readable or tryGenerateMode also
* fails.
*/
private void tryHardwareId() throws IOException {
try {
nodeId = getHardwareId();
} catch (IOException e) {
log.error("Error while reading MAC address. Fall back to 'generate' mode", e);
tryGenerateMode();
}
if (nodeId != null) {
restoreState();
log.info("Using MAC {} to generate Type 1 UUIDs", getNodeIdentifier());
return;
}
log.warn("No suitable network interface found. Fall back to 'generate' mode");
tryGenerateMode();
}
/**
* Tries the "generate" mode. A nodeId is generated once and saved to the state
* file
*/
private void tryGenerateMode() throws IOException {
if (restoreState()) {
log.info("Using recently generated nodeId {} to generate Type 1 UUIDs", getNodeIdentifier());
} else {
// RFC 4.5 use random portion for node
nodeId = super.getNodeIdBytes();
log.error("Have to fall back to random node identifier {} (Reason: {} )", getNodeIdentifier(), e.getMessage());
log.info("Using a newly generated nodeId {} to generate Type 1 UUIDs", getNodeIdentifier());
}
}
/**
* Random mode. A nodeId is generated every start up. no state file is
* maintained. This should always work.
*/
private void useRandomMode() {
canSaveState = false;
nodeId = super.getNodeIdBytes();
log.info("Explicitly using a new random ID {} to generate Type 1 UUIDs", getNodeIdentifier());
}
/**
* Returns the Node-identifier (=MAC address) as string
*/
@@ -164,22 +205,38 @@ public class UuidV1IdGenerator extends UuidV1RndIdGenerator {
*/
private boolean restoreState() throws IOException {
Properties prop = new Properties();
if (stateFile.exists()) {
try (InputStream is = new FileInputStream(stateFile)) {
prop.load(is);
}
if (!stateFile.exists()) {
log.debug("State file '{}' does not exist", stateFile);
return false;
}
if (getNodeIdentifier().equals(prop.getProperty("nodeId"))) {
try {
Integer seq = Integer.valueOf(prop.getProperty("clockSeq")) & 0x3FFF;
Long ts = Long.valueOf(prop.getProperty("timeStamp"));
clockSeq.set(seq);
timeStamp.set(ts);
log.debug("Restored state from '{}'", stateFile);
return true;
} catch (NumberFormatException nfe) {
// nop
try (InputStream is = new FileInputStream(stateFile)) {
prop.load(is);
}
String propNodeId = prop.getProperty("nodeId");
if (propNodeId == null || propNodeId.isEmpty()) {
log.warn("State file '{}' is incomplete", stateFile);
return false; // we cannot restore
}
try {
if (nodeId == null) {
nodeId = parseAlternativeNodeId(propNodeId);
} else if (!getNodeIdentifier().equals(propNodeId)) {
log.warn(
"The nodeId in the state file '{}' has changed from {} to {}. "
+ "This can happen when MAC address changes or when two containers share the same state file",
stateFile, propNodeId, getNodeIdentifier());
return false;
}
Integer seq = Integer.valueOf(prop.getProperty("clockSeq")) & 0x3FFF;
Long ts = Long.valueOf(prop.getProperty("timeStamp"));
clockSeq.set(seq);
timeStamp.set(ts);
log.debug("State successfully restored: {}", prop);
return true;
} catch (IllegalArgumentException nfe) {
log.error("State file '{}' is corrupt", stateFile, nfe);
}
return false;
}
@@ -21,18 +21,12 @@ import java.util.List;
*/
public final class BatchedPstmt implements SpiProfileTransactionEvent {
/**
* The underlying statement.
*/
private static final DB2GetKeys DB2_HACK = new DB2GetKeys();
private PreparedStatement pstmt;
/**
* True if an insert that uses generated keys.
*/
private final boolean isGenKeys;
/**
* The list of BatchPostExecute used to perform post processing.
* The list of BatchPostExecute used to perform post-processing.
*/
private final List<BatchPostExecute> list = new ArrayList<>();
private final String sql;
@@ -170,12 +164,14 @@ public final class BatchedPstmt implements SpiProfileTransactionEvent {
}
private void getGeneratedKeys() throws SQLException {
if (DB2_HACK.getGeneratedKeys(pstmt, list)) {
return;
}
int index = 0;
try (ResultSet rset = pstmt.getGeneratedKeys()) {
while (rset.next()) {
Object idValue = rset.getObject(1);
list.get(index).setGeneratedKey(idValue);
index++;
list.get(index++).setGeneratedKey(idValue);
}
}
}
@@ -0,0 +1,55 @@
package io.ebeaninternal.server.persist;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* Horrible DB2 hack.
*/
final class DB2GetKeys {
private static Class<? extends PreparedStatement> DB2_PREPARED_STATEMENT;
private static Method GET_DB_GENERATED_KEYS;
static {
try {
DB2_PREPARED_STATEMENT = (Class<? extends PreparedStatement>) Class.forName("com.ibm.db2.jcc.DB2PreparedStatement");
GET_DB_GENERATED_KEYS = DB2_PREPARED_STATEMENT.getDeclaredMethod("getDBGeneratedKeys");
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException cnf) {
// NOP
}
}
static boolean getGeneratedKeys(PreparedStatement pstmt, List<BatchPostExecute> list) throws SQLException {
if (DB2_PREPARED_STATEMENT != null) {
PreparedStatement db2Stmt = null;
if (DB2_PREPARED_STATEMENT.isInstance(pstmt)) {
db2Stmt = pstmt;
} else if (pstmt.isWrapperFor(DB2_PREPARED_STATEMENT)) {
db2Stmt = pstmt.unwrap(DB2_PREPARED_STATEMENT);
}
if (db2Stmt != null) {
// WTF: https://stackoverflow.com/questions/41725492/how-to-get-auto-generated-keys-of-batch-insert-statement
ResultSet[] result;
try {
result = (ResultSet[]) GET_DB_GENERATED_KEYS.invoke(db2Stmt);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new SQLException("Could not get generated keys for DB2", e);
}
int index = 0;
for (ResultSet resultSet : result) {
while (resultSet.next()) {
Object idValue = resultSet.getObject(1);
list.get(index++).setGeneratedKey(idValue);
}
}
return true;
}
}
return false;
}
}
@@ -41,15 +41,15 @@ public final class DefaultPersister implements Persister {
* Actually does the persisting work.
*/
private final PersistExecute persistExecute;
private final SpiEbeanServer server;
private final BeanDescriptorManager beanDescriptorManager;
private final int maxInBinding;
public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr) {
this.server = server;
this.beanDescriptorManager = descMgr;
this.persistExecute = new DefaultPersistExecute(binder, server.config().getPersistBatchSize());
this.maxInBinding = server.databasePlatform().getMaxInBinding();
}
@Override
@@ -649,6 +649,20 @@ public final class DefaultPersister implements Persister {
* Delete by Id or a List of Id's.
*/
private int delete(BeanDescriptor<?> descriptor, Object id, List<Object> idList, Transaction transaction, DeleteMode deleteMode) {
int rows = 0;
if (maxInBinding > 0) {
// SqlServer has a 2100 parameter limit, so delete max 2000 ids at once
// this gives space up to 100 more query parameters.
while (idList != null && idList.size() > maxInBinding) {
rows += deleteBatch(descriptor, id, idList.subList(0, maxInBinding), transaction, deleteMode);
idList = idList.subList(maxInBinding, idList.size());
}
}
rows += deleteBatch(descriptor, id, idList, transaction, deleteMode);
return rows;
}
private int deleteBatch(BeanDescriptor<?> descriptor, Object id, List<Object> idList, Transaction transaction, DeleteMode deleteMode) {
SpiTransaction t = (SpiTransaction) transaction;
if (t.isPersistCascade()) {
BeanPropertyAssocOne<?>[] propImportDelete = descriptor.propertiesOneImportedDelete();
@@ -13,11 +13,8 @@ import io.ebeaninternal.server.persist.dmlbind.BindableList;
import java.sql.SQLException;
import static io.ebean.annotation.Platform.MARIADB;
import static io.ebean.annotation.Platform.MYSQL;
/**
* Meta data for insert handler. The meta data is for a particular bean type. It
* Metadata for insert handler. The metadata is for a particular bean type. It
* is considered immutable and is thread safe.
*/
final class InsertMeta {
@@ -153,31 +150,25 @@ final class InsertMeta {
private String genSql(boolean nullId, String table, boolean draftTable) {
GenerateDmlRequest request = new GenerateDmlRequest();
request.setInsertSetMode();
request.append("insert into ").append(table);
if (nullId && noColumnsForInsert(draftTable)) {
return request.append(defaultValues()).toString();
}
request.append(" (");
if (!nullId) {
id.dmlAppend(request);
}
if (shadowFKey != null) {
shadowFKey.dmlAppend(request);
}
if (discriminator != null) {
discriminator.dmlAppend(request);
}
if (draftTable) {
all.dmlAppend(request);
} else {
allExcludeDraftOnly.dmlAppend(request);
}
request.append(") values (");
request.append(request.getInsertBindBuffer());
request.append(")");
@@ -185,7 +176,16 @@ final class InsertMeta {
}
private String defaultValues() {
return platform.base() == MYSQL || platform.base() == MARIADB ? " values (default)" : " default values";
switch (platform.base()) {
case MYSQL:
case MARIADB:
case ORACLE:
return " values (default)";
case DB2:
return " (" + id.getIdentityColumn() + ") values (default)";
default:
return " default values";
}
}
/**
@@ -193,7 +193,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
// set the generated sql back to the query
// so its available to the user...
query.setGeneratedSql(queryPlan.getSql());
SqlTree sqlTree = queryPlan.getSqlTree();
SqlTreePlan sqlTree = queryPlan.getSqlTree();
this.rootNode = sqlTree.getRootNode();
this.manyProperty = sqlTree.getManyProperty();
this.sql = queryPlan.getSql();
@@ -581,20 +581,17 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
@Override
public void registerBeanInherit(BeanPropertyAssocOne<?> property, EntityBeanIntercept ebi) {
String path = getPath(property.name());
request.loadContext().register(path, ebi, property);
request.loadContext().register(path(property.name()), ebi, property);
}
@Override
public void register(String path, EntityBeanIntercept ebi) {
path = getPath(path);
request.loadContext().register(path, ebi);
request.loadContext().register(path(path), ebi);
}
@Override
public void register(BeanPropertyAssocMany<?> many, BeanCollection<?> bc) {
String path = getPath(many.name());
request.loadContext().register(path, many, bc);
request.loadContext().register(path(many.name()), many, bc);
}
/**
@@ -663,18 +660,14 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
return autoTuneProfiling && query.isUsageProfiling();
}
private String getPath(String propertyName) {
private String path(String propertyName) {
if (currentPrefix == null) {
return propertyName;
} else if (propertyName == null) {
return currentPrefix;
}
String path = currentPathMap.get(propertyName);
if (path != null) {
return path;
} else {
return currentPrefix + "." + propertyName;
}
return path != null ? path : currentPrefix + "." + propertyName;
}
@Override
@@ -110,7 +110,7 @@ final class CQueryBuilder {
sql = buildUpdateSql(request, rootTableAlias, predicates, sqlTree);
}
// cache the query plan
queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql());
queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryUpdate(request, predicates, queryPlan);
}
@@ -197,7 +197,7 @@ final class CQueryBuilder {
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(null, request, predicates, sqlTree);
queryPlan = new CQueryPlan(request, s.getSql(), sqlTree, predicates.getLogWhereSql());
queryPlan = new CQueryPlan(request, s.getSql(), sqlTree.plan(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryFetchSingleAttribute(request, predicates, queryPlan, query.isCountDistinct());
}
@@ -288,7 +288,7 @@ final class CQueryBuilder {
}
}
// cache the query plan
queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql());
queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryRowCount(queryPlan, request, predicates);
}
@@ -343,7 +343,7 @@ final class CQueryBuilder {
if (rawSql) {
queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql());
} else {
queryPlan = new CQueryPlan(request, res, sqlTree, false, predicates.getLogWhereSql());
queryPlan = new CQueryPlan(request, res, sqlTree.plan(), false, predicates.getLogWhereSql());
}
BeanDescriptor<T> desc = request.descriptor();
if (desc.isReadAuditing()) {
@@ -568,8 +568,16 @@ final class CQueryBuilder {
if (countSingleAttribute) {
sb.append("r1.attribute_, count(*) from (select ");
if (distinct) {
sb.append("distinct t0.");
sb.append(request.descriptor().idProperty().dbColumn()).append(", ");
sb.append("distinct ");
BeanProperty idProp = request.descriptor().idProperty();
if (idProp.isEmbedded()) {
BeanProperty[] props = ((BeanPropertyAssocOne<?>) idProp).properties();
for (BeanProperty prop : props) {
sb.append("t0.").append(prop.dbColumn()).append(", ");
}
} else {
sb.append("t0.").append(idProp.dbColumn()).append(", ");
}
}
sb.append(select.getSelectSql()).append(" as attribute_");
} else {
@@ -749,7 +757,11 @@ final class CQueryBuilder {
}
boolean isPlatformDistinctOn() {
return dbPlatform.isPlatform(Platform.POSTGRES);
return dbPlatform.isPlatform(Platform.POSTGRES) || dbPlatform.isPlatform(Platform.YUGABYTE);
}
boolean isPlatformDistinctNoLobs() {
return dbPlatform.isPlatform(Platform.DB2); // CHECKME: Also oracle?
}
/**
@@ -55,7 +55,7 @@ public class CQueryPlan implements SpiQueryPlan {
private final String sql;
private final String hash;
private final String logWhereSql;
private final SqlTree sqlTree;
private final SqlTreePlan sqlTree;
/**
* Encrypted properties required additional binding.
@@ -76,7 +76,7 @@ public class CQueryPlan implements SpiQueryPlan {
/**
* Create a query plan based on a OrmQueryRequest.
*/
CQueryPlan(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, boolean rawSql, String logWhereSql) {
CQueryPlan(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTreePlan sqlTree, boolean rawSql, String logWhereSql) {
this.server = request.server();
this.dataTimeZone = server.dataTimeZone();
this.beanType = request.descriptor().type();
@@ -101,7 +101,7 @@ public class CQueryPlan implements SpiQueryPlan {
/**
* Create a query plan for a raw sql query.
*/
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTree sqlTree, String logWhereSql) {
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTreePlan sqlTree, String logWhereSql) {
this.server = request.server();
this.dataTimeZone = server.dataTimeZone();
this.beanType = request.descriptor().type();
@@ -249,7 +249,7 @@ public class CQueryPlan implements SpiQueryPlan {
return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey();
}
final SqlTree getSqlTree() {
final SqlTreePlan getSqlTree() {
return sqlTree;
}
@@ -17,7 +17,7 @@ final class CQueryPlanRawSql extends CQueryPlan {
private final int[] rsetIndexPositions;
CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
super(request, sqlRes, sqlTree, true, logWhereSql);
super(request, sqlRes, sqlTree.plan(), true, logWhereSql);
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
}
@@ -28,6 +28,11 @@ public interface STreeProperty extends ScalarDataReader<Object> {
* Return true if the property is the Id.
*/
boolean isId();
/**
* Returns true, if this is a lob property from db-perspective.
*/
boolean isDbLob();
/**
* Return true if the property is an embedded type.
@@ -7,6 +7,8 @@ import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.id.IdBinder;
import java.util.Map;
/**
* Bean type interface for Sql query tree.
*/
@@ -136,4 +138,8 @@ public interface STreeType {
*/
void markAsDeleted(EntityBean bean);
/**
* Return the "path map" to toMany or toOne properties using the given prefix.
*/
Map<String, String> pathMap(String prefix);
}
@@ -56,6 +56,10 @@ final class SqlTree {
this.noJoins = !includeJoins;
}
SqlTreePlan plan() {
return new SqlTreePlan(rootNode.createLoad(), manyProperty, encryptedProps, dependentTables());
}
/**
* Return true if the query mandates SQL Distinct due to ToMany inclusion.
*/
@@ -123,10 +127,6 @@ final class SqlTree {
return inheritanceWhereSql;
}
SqlTreeRoot getRootNode() {
return (SqlTreeRoot)rootNode;
}
/**
* Return the property that is associated with the many. There can only be one
* per SqlSelect. This can be null.
@@ -135,10 +135,6 @@ final class SqlTree {
return manyProperty;
}
STreeProperty[] getEncryptedProps() {
return encryptedProps;
}
/**
* Return true if the query has a many join.
*/
@@ -153,7 +149,7 @@ final class SqlTree {
/**
* Return the tables that are joined in this query.
*/
Set<String> dependentTables() {
private Set<String> dependentTables() {
Set<String> tables = new LinkedHashSet<>();
rootNode.dependentTables(tables);
return tables;
@@ -53,6 +53,7 @@ public final class SqlTreeBuilder {
private final SpiQuery.TemporalMode temporalMode;
private SqlTreeNode rootNode;
private boolean sqlDistinct;
private final boolean distinctNoLobs;
/**
* Construct for RawSql query.
@@ -65,6 +66,7 @@ public final class SqlTreeBuilder {
this.query = null;
this.subQuery = false;
this.distinctOnPlatform = false;
this.distinctNoLobs = false;
this.queryDetail = queryDetail;
this.predicates = predicates;
this.temporalMode = SpiQuery.TemporalMode.CURRENT;
@@ -97,6 +99,7 @@ public final class SqlTreeBuilder {
this.predicates = predicates;
this.alias = new SqlTreeAlias(request.baseTableAlias(), temporalMode);
this.distinctOnPlatform = builder.isPlatformDistinctOn();
this.distinctNoLobs = builder.isPlatformDistinctNoLobs();
String fromForUpdate = builder.fromForUpdate(query);
CQueryHistorySupport historySupport = builder.getHistorySupport(query);
CQueryDraftSupport draftSupport = builder.getDraftSupport(query);
@@ -266,6 +269,9 @@ public final class SqlTreeBuilder {
if (joinList != null) {
joinList.add(selectNode);
}
if (sqlDistinct && distinctNoLobs) {
selectNode.unselectLobs();
}
return selectNode;
}
@@ -0,0 +1,28 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.EntityBean;
import io.ebean.core.type.ScalarDataReader;
import io.ebeaninternal.server.deploy.DbReadContext;
import java.sql.SQLException;
/**
* Tree node that loads an entity bean type.
*/
interface SqlTreeLoad {
/**
* Load the appropriate information from the SqlSelectReader.
* <p>
* At a high level this actually controls the reading of the data from the
* jdbc resultSet and putting it into the bean etc.
* </p>
*/
EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException;
/**
* Return the reader for the single attribute query.
*/
ScalarDataReader<?> getSingleAttributeReader();
}
@@ -0,0 +1,373 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
import io.ebean.core.type.ScalarDataReader;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuery.Mode;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.id.IdBinder;
import java.sql.SQLException;
import java.util.Map;
/**
* Normal bean included in the query.
*/
class SqlTreeLoadBean implements SqlTreeLoad {
final STreeType desc;
final IdBinder idBinder;
final SqlTreeLoad[] children;
private final boolean partialObject;
private final STreeProperty[] properties;
private final STreePropertyAssoc nodeBeanProp;
final boolean readId;
private final boolean readIdNormal;
private final boolean disableLazyLoad;
private final InheritInfo inheritInfo;
final String prefix;
private final Map<String, String> pathMap;
final STreePropertyAssocMany lazyLoadParent;
private final SpiQuery.TemporalMode temporalMode;
private final boolean temporalVersions;
final IdBinder lazyLoadParentIdBinder;
SqlTreeLoadBean(SqlTreeNodeBean node) {
this.lazyLoadParent = node.lazyLoadParent;
this.lazyLoadParentIdBinder = node.lazyLoadParentIdBinder;
this.prefix = node.prefix;
this.desc = node.desc;
this.inheritInfo = desc.inheritInfo();
this.idBinder = desc.idBinder();
this.temporalMode = node.temporalMode;
this.temporalVersions = node.temporalVersions;
this.nodeBeanProp = node.nodeBeanProp;
this.readId = node.readId;
this.readIdNormal = readId && !temporalVersions;
this.disableLazyLoad = node.disableLazyLoad;
this.partialObject = node.partialObject;
this.properties = node.properties;
this.pathMap = node.pathMap;
this.children = node.createLoadChildren();
}
boolean isRoot() {
return false;
}
@Override
public final ScalarDataReader<?> getSingleAttributeReader() {
if (properties == null || properties.length == 0) {
// if we have no property ask first children (in a distinct select with join)
if (children.length == 0) {
// expected to be a findIds query
return desc.idBinder().getBeanProperty();
}
return children[0].getSingleAttributeReader();
}
if (properties[0] instanceof STreePropertyAssocOne) {
STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0];
if (assocOne.isAssocId()) {
return assocOne.idReader();
}
}
return properties[0];
}
/**
* Load that takes into account inheritance.
*/
private final class LoadInherit extends Load {
private LoadInherit(DbReadContext ctx, EntityBean parentBean) {
super(ctx, parentBean);
}
@Override
void initBeanType() throws SQLException {
InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo();
if (localInfo == null) {
// the bean must be null
localIdBinder = idBinder;
localDesc = desc;
} else {
localBean = localInfo.createEntityBean();
localType = localInfo.getType();
localIdBinder = localInfo.getIdBinder();
localDesc = localInfo.desc();
}
}
@Override
void loadProperties() {
// take account of inheritance
for (STreeProperty property : properties) {
localDesc.inheritanceLoad(sqlBeanLoad, property, ctx);
}
}
}
/**
* Load a bean instance.
*/
class Load {
final DbReadContext ctx;
final EntityBean parentBean;
Object lazyLoadParentId;
Class<?> localType;
STreeType localDesc;
IdBinder localIdBinder;
EntityBean localBean;
Mode queryMode;
PersistenceContext persistenceContext;
Object id;
EntityBean contextBean;
SqlBeanLoad sqlBeanLoad;
boolean lazyLoadMany;
private Load(DbReadContext ctx, EntityBean parentBean) {
this.ctx = ctx;
this.parentBean = parentBean;
}
private void initLazyParent() throws SQLException {
if (lazyLoadParentIdBinder != null) {
lazyLoadParentId = lazyLoadParentIdBinder.read(ctx);
}
}
void initBeanType() throws SQLException {
localDesc = desc;
localBean = desc.createEntityBean();
localIdBinder = idBinder;
}
private void initPersistenceContext() {
queryMode = ctx.getQueryMode();
persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext();
}
private void readId() throws SQLException {
if (readId) {
id = localIdBinder.readSet(ctx, localBean);
if (id == null) {
readIdNullBean();
} else if (!temporalVersions) {
readIdBean();
}
}
}
private void readIdBean() {
// check the PersistenceContext to see if the bean already exists
contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean);
if (contextBean == null) {
// bean just added to the persistenceContext
contextBean = localBean;
} else {
// bean already exists in persistenceContext
if (queryMode.isLoadContextBean()) {
// if explicitly set loadContextBean to true, then reload
localBean = contextBean;
} else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) {
// reload if contextBean is partial object
localBean = contextBean;
// and switch to lazyLoad query mode in order not to overwrite
// existing properties in SqlBeanLoad::load
queryMode = Mode.LAZYLOAD_BEAN;
} else {
// ignore the DB data...
localBean = null;
}
}
}
private void readIdNullBean() {
// bean must be null...
localBean = null;
// ... but there may exist as reference bean in parent which has to be marked as deleted.
if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) {
contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean);
if (contextBean != null) {
desc.markAsDeleted(contextBean);
}
}
}
private void initSqlLoadBean() {
ctx.setCurrentPrefix(prefix, pathMap);
ctx.propagateState(localBean);
sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
}
void loadProperties() {
for (STreeProperty property : properties) {
property.load(sqlBeanLoad);
}
}
private void loadChildren() throws SQLException {
if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) {
// batch lazy load many into existing contextBean
localBean = contextBean;
lazyLoadMany = true;
}
for (SqlTreeLoad child : children) {
child.load(ctx, localBean, contextBean);
}
}
private boolean isLazyLoadManyRoot() {
return queryMode == Mode.LAZYLOAD_MANY && isRoot();
}
private EntityBean getContextBean() {
return contextBean;
}
private void postLoad() {
if (!lazyLoadMany && localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
if (readIdNormal) {
createListProxies();
}
if (temporalMode == SpiQuery.TemporalMode.DRAFT) {
localDesc.setDraft(localBean);
}
localDesc.postLoad(localBean);
EntityBeanIntercept ebi = localBean._ebean_getIntercept();
ebi.setPersistenceContext(persistenceContext);
if (Mode.LAZYLOAD_BEAN == queryMode) {
// Lazy Load does not reset the dirty state
ebi.setLoadedLazy();
} else if (readId) {
// normal bean loading
ebi.setLoaded();
}
if (disableLazyLoad) {
// bean does not have an Id or is SqlSelect based
ebi.setDisableLazyLoad(true);
} else if (partialObject) {
if (readId) {
// register for lazy loading
ctx.register(null, ebi);
}
} else {
ebi.setFullyLoadedBean(true);
}
if (ctx.isAutoTuneProfiling() && !disableLazyLoad) {
// collect autoTune profiling for this bean...
ctx.profileBean(ebi, prefix);
}
}
}
/**
* Create lazy loading proxies for the Many's except for the one that is
* included in the actual query.
*/
private void createListProxies() {
STreePropertyAssocMany fetchedMany = ctx.getManyProperty();
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
for (STreePropertyAssocMany many : localDesc.propsMany()) {
if (many != fetchedMany) {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = many.createReference(localBean, forceNewReference);
if (ref != null) {
if (disableLazyLoad) {
ref.setDisableLazyLoad(true);
}
if (!ref.isRegisteredWithLoadContext()) {
ctx.register(many.asMany(), ref);
}
}
}
}
}
private void setBeanToParent() {
if (parentBean != null) {
// set this back to the parentBean
nodeBeanProp.setValue(parentBean, contextBean);
}
}
private EntityBean complete() {
if (!readIdNormal) {
// a bean with no Id (never found in context)
if (lazyLoadParentId != null) {
ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId);
}
return localBean;
} else {
if (lazyLoadParentId != null) {
ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId);
}
return contextBean;
}
}
private void initialise() throws SQLException {
initLazyParent();
initBeanType();
initPersistenceContext();
readId();
initSqlLoadBean();
loadProperties();
loadChildren();
}
/**
* Perform the load returning the loaded bean.
*/
final EntityBean perform() throws SQLException {
initialise();
if (isLazyLoadManyRoot()) {
return getContextBean();
}
postLoad();
setBeanToParent();
return complete();
}
/**
* Return true if this bean was already in the context. If already in the
* context we need to check if it is already contained in the collection.
*/
final boolean isContextBean() {
return localBean == null;
}
}
/**
* read the properties from the resultSet.
*/
@Override
public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException {
return createLoad(ctx, parentBean).perform();
}
/**
* Create the loader with or without inheritance.
*/
final Load createLoad(DbReadContext ctx, EntityBean parentBean) {
return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
}
@Override
public String toString() {
return "SqlTreeLoadBean: " + desc;
}
}
@@ -0,0 +1,30 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.DbReadContext;
import java.sql.SQLException;
final class SqlTreeLoadManyRoot extends SqlTreeLoadBean {
private final STreePropertyAssocMany manyProp;
SqlTreeLoadManyRoot(SqlTreeNodeManyRoot node) {
super(node);
this.manyProp = node.manyProp;
}
@Override
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
// pass in null for parentBean because added to a collection rather than set to the parentBean
Load load = createLoad(cquery, null);
EntityBean detailBean = load.perform();
if (contextParent != null) {
// Add to the collection and initialise collection if needed
// A null detailBean may initialise an empty collection
// Check for bean existing in collection based on load.isContextBean()
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean());
}
return detailBean;
}
}
@@ -0,0 +1,43 @@
package io.ebeaninternal.server.query;
import io.ebean.Version;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.DbReadContext;
import java.sql.SQLException;
import java.sql.Timestamp;
/**
* Represents the root node of the Sql Tree.
*/
final class SqlTreeLoadRoot extends SqlTreeLoadBean implements SqlTreeRoot {
SqlTreeLoadRoot(SqlTreeNodeBean node) {
super(node);
}
@Override
protected boolean isRoot() {
return true;
}
@Override
public EntityBean load(DbReadContext ctx) throws SQLException {
return load(ctx, null, null);
}
/**
* Read the version bean.
*/
@Override
@SuppressWarnings("unchecked")
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
// read the sys period lower and upper bounds
// these are always the first 2 columns in the resultSet
Timestamp start = ctx.getDataReader().getTimestamp();
Timestamp end = ctx.getDataReader().getTimestamp();
T bean = (T) load(ctx, null, null);
return new Version<>(bean, start, end);
}
}
@@ -1,13 +1,8 @@
package io.ebeaninternal.server.query;
import io.ebean.Version;
import io.ebean.bean.EntityBean;
import io.ebean.core.type.ScalarDataReader;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
@@ -64,25 +59,11 @@ interface SqlTreeNode {
*/
void addSoftDeletePredicate(SpiQuery<?> query);
/**
* Load the appropriate information from the SqlSelectReader.
* <p>
* At a high level this actually controls the reading of the data from the
* jdbc resultSet and putting it into the bean etc.
* </p>
*/
EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException;
/**
* Return true if the query has a many join.
*/
boolean hasMany();
/**
* Return the reader for the single attribute query.
*/
ScalarDataReader<?> getSingleAttributeReader();
/**
* Return true if the query is known to only have a single property selected.
*/
@@ -92,4 +73,15 @@ interface SqlTreeNode {
* Add dependent tables to the given set.
*/
void dependentTables(Set<String> tables);
/**
* Create the loader for this node.
*/
SqlTreeLoad createLoad();
/**
* Unselect lobs (for distinct queries on DB2 and Oracle).
*/
default void unselectLobs() {
};
}
@@ -1,68 +1,57 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
import io.ebean.core.type.ScalarDataReader;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuery.Mode;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.id.IdBinder;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* Normal bean included in the query.
*/
class SqlTreeNodeBean implements SqlTreeNode {
private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0];
private static final SqlTreeLoad[] NO_LOAD_CHILDREN = new SqlTreeLoad[0];
final STreeType desc;
final IdBinder idBinder;
/**
* The children which will be other SelectBean or SelectProxyBean.
*/
final SqlTreeNode[] children;
final List<SqlTreeNode> children;
/**
* Set to true if this is a partial object fetch.
*/
private final boolean partialObject;
private final STreeProperty[] properties;
boolean partialObject;
STreeProperty[] properties;
/**
* Extra where clause added by Where annotation on associated many.
*/
private final String extraWhere;
private final STreePropertyAssoc nodeBeanProp;
final String extraWhere;
final STreePropertyAssoc nodeBeanProp;
/**
* False if report bean and has no id property.
*/
final boolean readId;
private final boolean readIdNormal;
private final boolean disableLazyLoad;
private final InheritInfo inheritInfo;
final boolean readIdNormal;
final boolean disableLazyLoad;
final InheritInfo inheritInfo;
final String prefix;
private final Map<String, String> pathMap;
final Map<String, String> pathMap;
final STreePropertyAssocMany lazyLoadParent;
private final SpiQuery.TemporalMode temporalMode;
private final boolean temporalVersions;
private final IdBinder lazyLoadParentIdBinder;
final SpiQuery.TemporalMode temporalMode;
final boolean temporalVersions;
final IdBinder lazyLoadParentIdBinder;
String baseTableAlias;
/**
* Table alias set if this bean node includes a join to a intersection
* table and that table has history support.
*/
private boolean intersectionAsOfTableAlias;
private final boolean aggregation;
boolean intersectionAsOfTableAlias;
final boolean aggregation;
/**
* Construct for leaf node.
@@ -104,53 +93,40 @@ class SqlTreeNodeBean implements SqlTreeNode {
this.disableLazyLoad = disableLazyLoad || !readIdNormal || desc.isRawSqlBased();
this.partialObject = props.isPartialObject();
this.properties = props.getProps();
this.children = myChildren == null ? NO_CHILDREN : myChildren.toArray(new SqlTreeNode[0]);
pathMap = createPathMap(prefix, desc);
this.children = myChildren == null ? Collections.emptyList() : myChildren;
this.pathMap = createPathMap(prefix, desc);
}
boolean isRoot() {
return false;
@Override
public SqlTreeLoad createLoad() {
return new SqlTreeLoadBean(this);
}
protected SqlTreeLoad[] createLoadChildren() {
if (children.isEmpty()) {
return NO_LOAD_CHILDREN;
}
List<SqlTreeLoad> loadChildren = new ArrayList<>(children.size());
for (SqlTreeNode child : children) {
SqlTreeLoad load = child.createLoad();
if (load != null) {
loadChildren.add(load);
}
}
return loadChildren.toArray(new SqlTreeLoad[0]);
}
@Override
public final boolean isSingleProperty() {
return properties != null && properties.length == 1 && children.length == 0;
}
@Override
public final ScalarDataReader<?> getSingleAttributeReader() {
if (properties == null || properties.length == 0) {
// if we have no property ask first children (in a distinct select with join)
if (children.length == 0) {
// expected to be a findIds query
return desc.idBinder().getBeanProperty();
}
return children[0].getSingleAttributeReader();
}
if (properties[0] instanceof STreePropertyAssocOne) {
STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0];
if (assocOne.isAssocId()) {
return assocOne.idReader();
}
}
return properties[0];
return properties != null && properties.length == 1 && children.isEmpty();
}
private Map<String, String> createPathMap(String prefix, STreeType desc) {
HashMap<String, String> m = new HashMap<>();
for (STreePropertyAssocMany many : desc.propsMany()) {
String name = many.name();
m.put(name, getPath(prefix, name));
}
return m;
return prefix == null ? Collections.emptyMap() : desc.pathMap(prefix);
}
private String getPath(String prefix, String propertyName) {
if (prefix == null) {
return propertyName;
} else {
return prefix + "." + propertyName;
}
private String path(String prefix, String propertyName) {
return prefix == null ? propertyName : prefix + "." + propertyName;
}
@Override
@@ -158,7 +134,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
if (readId) {
if (inheritInfo != null) {
// discriminator column always proceeds id column
selectChain.add(getPath(prefix, inheritInfo.getDiscriminatorColumn()));
selectChain.add(path(prefix, inheritInfo.getDiscriminatorColumn()));
}
idBinder.buildRawSqlSelectChain(prefix, selectChain);
}
@@ -173,293 +149,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
}
/**
* Load that takes into account inheritance.
*/
private final class LoadInherit extends Load {
private LoadInherit(DbReadContext ctx, EntityBean parentBean) {
super(ctx, parentBean);
}
@Override
void initBeanType() throws SQLException {
InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo();
if (localInfo == null) {
// the bean must be null
localIdBinder = idBinder;
localDesc = desc;
} else {
localBean = localInfo.createEntityBean();
localType = localInfo.getType();
localIdBinder = localInfo.getIdBinder();
localDesc = localInfo.desc();
}
}
@Override
void loadProperties() {
// take account of inheritance
for (STreeProperty property : properties) {
localDesc.inheritanceLoad(sqlBeanLoad, property, ctx);
}
}
}
/**
* Load a bean instance.
*/
class Load {
final DbReadContext ctx;
final EntityBean parentBean;
Object lazyLoadParentId;
Class<?> localType;
STreeType localDesc;
IdBinder localIdBinder;
EntityBean localBean;
Mode queryMode;
PersistenceContext persistenceContext;
Object id;
EntityBean contextBean;
SqlBeanLoad sqlBeanLoad;
boolean lazyLoadMany;
private Load(DbReadContext ctx, EntityBean parentBean) {
this.ctx = ctx;
this.parentBean = parentBean;
}
private void initLazyParent() throws SQLException {
if (lazyLoadParentIdBinder != null) {
lazyLoadParentId = lazyLoadParentIdBinder.read(ctx);
}
}
void initBeanType() throws SQLException {
localDesc = desc;
localBean = desc.createEntityBean();
localIdBinder = idBinder;
}
private void initPersistenceContext() {
queryMode = ctx.getQueryMode();
persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext();
}
private void readId() throws SQLException {
if (readId) {
id = localIdBinder.readSet(ctx, localBean);
if (id == null) {
readIdNullBean();
} else if (!temporalVersions) {
readIdBean();
}
}
}
private void readIdBean() {
// check the PersistenceContext to see if the bean already exists
contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean);
if (contextBean == null) {
// bean just added to the persistenceContext
contextBean = localBean;
} else {
// bean already exists in persistenceContext
if (queryMode.isLoadContextBean()) {
// if explicitly set loadContextBean to true, then reload
localBean = contextBean;
} else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) {
// reload if contextBean is partial object
localBean = contextBean;
// and switch to lazyLoad query mode in order not to overwrite
// existing properties in SqlBeanLoad::load
queryMode = Mode.LAZYLOAD_BEAN;
} else {
// ignore the DB data...
localBean = null;
}
}
}
private void readIdNullBean() {
// bean must be null...
localBean = null;
// ... but there may exist as reference bean in parent which has to be marked as deleted.
if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) {
contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean);
if (contextBean != null) {
desc.markAsDeleted(contextBean);
}
}
}
private void initSqlLoadBean() {
ctx.setCurrentPrefix(prefix, pathMap);
ctx.propagateState(localBean);
sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
}
void loadProperties() {
for (STreeProperty property : properties) {
property.load(sqlBeanLoad);
}
}
private void loadChildren() throws SQLException {
if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) {
// batch lazy load many into existing contextBean
localBean = contextBean;
lazyLoadMany = true;
}
for (SqlTreeNode child : children) {
child.load(ctx, localBean, contextBean);
}
}
private boolean isLazyLoadManyRoot() {
return queryMode == Mode.LAZYLOAD_MANY && isRoot();
}
private EntityBean getContextBean() {
return contextBean;
}
private void postLoad() {
if (!lazyLoadMany && localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
if (readIdNormal) {
createListProxies();
}
if (temporalMode == SpiQuery.TemporalMode.DRAFT) {
localDesc.setDraft(localBean);
}
localDesc.postLoad(localBean);
EntityBeanIntercept ebi = localBean._ebean_getIntercept();
ebi.setPersistenceContext(persistenceContext);
if (Mode.LAZYLOAD_BEAN == queryMode) {
// Lazy Load does not reset the dirty state
ebi.setLoadedLazy();
} else if (readId) {
// normal bean loading
ebi.setLoaded();
}
if (disableLazyLoad) {
// bean does not have an Id or is SqlSelect based
ebi.setDisableLazyLoad(true);
} else if (partialObject) {
if (readId) {
// register for lazy loading
ctx.register(null, ebi);
}
} else {
ebi.setFullyLoadedBean(true);
}
if (ctx.isAutoTuneProfiling() && !disableLazyLoad) {
// collect autoTune profiling for this bean...
ctx.profileBean(ebi, prefix);
}
}
}
/**
* Create lazy loading proxies for the Many's except for the one that is
* included in the actual query.
*/
private void createListProxies() {
STreePropertyAssocMany fetchedMany = ctx.getManyProperty();
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
for (STreePropertyAssocMany many : localDesc.propsMany()) {
if (many != fetchedMany) {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = many.createReference(localBean, forceNewReference);
if (ref != null) {
if (disableLazyLoad) {
ref.setDisableLazyLoad(true);
}
if (!ref.isRegisteredWithLoadContext()) {
ctx.register(many.asMany(), ref);
}
}
}
}
}
private void setBeanToParent() {
if (parentBean != null) {
// set this back to the parentBean
nodeBeanProp.setValue(parentBean, contextBean);
}
}
private EntityBean complete() {
if (!readIdNormal) {
// a bean with no Id (never found in context)
if (lazyLoadParentId != null) {
ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId);
}
return localBean;
} else {
if (lazyLoadParentId != null) {
ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId);
}
return contextBean;
}
}
private void initialise() throws SQLException {
initLazyParent();
initBeanType();
initPersistenceContext();
readId();
initSqlLoadBean();
loadProperties();
loadChildren();
}
/**
* Perform the load returning the loaded bean.
*/
final EntityBean perform() throws SQLException {
initialise();
if (isLazyLoadManyRoot()) {
return getContextBean();
}
postLoad();
setBeanToParent();
return complete();
}
/**
* Return true if this bean was already in the context. If already in the
* context we need to check if it is already contained in the collection.
*/
final boolean isContextBean() {
return localBean == null;
}
}
/**
* read the properties from the resultSet.
*/
@Override
public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException {
return createLoad(ctx, parentBean).perform();
}
/**
* Create the loader with or without inheritance.
*/
final Load createLoad(DbReadContext ctx, EntityBean parentBean) {
return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
}
@Override
public final void appendGroupBy(DbSqlContext ctx, boolean subQuery) {
ctx.pushJoin(prefix);
@@ -700,4 +389,33 @@ class SqlTreeNodeBean implements SqlTreeNode {
}
return false;
}
@Override
public void unselectLobs() {
if (children != null) {
for (SqlTreeNode child : children) {
child.unselectLobs();
}
}
if (hasLob()) {
List<STreeProperty> lst = new ArrayList<>();
for (STreeProperty prop : properties) {
if (!prop.isDbLob()) {
lst.add(prop);
}
}
properties = lst.toArray(new STreeProperty[0]);
partialObject = true;
}
}
private boolean hasLob() {
for (STreeProperty prop : properties) {
if (prop.isDbLob()) {
return true;
}
}
return false;
}
}
@@ -38,6 +38,11 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany;
}
@Override
public SqlTreeLoad createLoad() {
return null;
}
@Override
public boolean isSingleProperty() {
return false;
@@ -73,11 +78,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
// nothing to do here
}
@Override
public ScalarType<?> getSingleAttributeReader() {
throw new IllegalStateException("No expected");
}
/**
* Return true if the extra join is a many join.
* <p>
@@ -184,14 +184,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
public void appendWhere(DbSqlContext ctx) {
}
/**
* Does nothing.
*/
@Override
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
return null;
}
@Override
public boolean hasMany() {
return manyJoin;
@@ -33,13 +33,13 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode {
}
@Override
public boolean isSingleProperty() {
return true;
public SqlTreeLoad createLoad() {
return null;
}
@Override
public ScalarType<?> getSingleAttributeReader() {
throw new IllegalStateException("No expected");
public boolean isSingleProperty() {
return true;
}
@Override
@@ -98,12 +98,6 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode {
// nothing to do here
}
@Override
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
// nothing to do here
return null;
}
@Override
public boolean hasMany() {
return true;
@@ -1,16 +1,13 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import java.sql.SQLException;
import java.util.List;
final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
private final STreePropertyAssocMany manyProp;
final STreePropertyAssocMany manyProp;
SqlTreeNodeManyRoot(String prefix, STreePropertyAssocMany prop, SqlTreeProperties props, List<SqlTreeNode> myList,
boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
@@ -19,22 +16,13 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
}
@Override
public boolean hasMany() {
return true;
public SqlTreeLoad createLoad() {
return new SqlTreeLoadManyRoot(this);
}
@Override
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
// pass in null for parentBean because added to a collection rather than set to the parentBean
SqlTreeNodeBean.Load load = createLoad(cquery, null);
EntityBean detailBean = load.perform();
if (contextParent != null) {
// Add to the collection and initialise collection if needed
// A null detailBean may initialise an empty collection
// Check for bean existing in collection based on load.isContextBean()
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean());
}
return detailBean;
public boolean hasMany() {
return true;
}
/**
@@ -1,10 +1,7 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.EntityBean;
import io.ebean.core.type.ScalarType;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import io.ebeaninternal.server.deploy.TableJoin;
@@ -37,13 +34,13 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
}
@Override
public boolean isSingleProperty() {
return false;
public SqlTreeLoad createLoad() {
return null;
}
@Override
public ScalarType<?> getSingleAttributeReader() {
throw new IllegalStateException("No expected");
public boolean isSingleProperty() {
return false;
}
@Override
@@ -132,12 +129,6 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
// nothing to do here
}
@Override
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
// nothing to do here
return null;
}
@Override
public boolean hasMany() {
return true;
@@ -1,21 +1,16 @@
package io.ebeaninternal.server.query;
import io.ebean.Version;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.DbReadContext;
import io.ebeaninternal.server.deploy.DbSqlContext;
import io.ebeaninternal.server.deploy.TableJoin;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;
import java.util.Set;
/**
* Represents the root node of the Sql Tree.
*/
final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot {
final class SqlTreeNodeRoot extends SqlTreeNodeBean {
private final TableJoin includeJoin;
private final boolean sqlDistinct;
@@ -34,27 +29,8 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot {
}
@Override
protected boolean isRoot() {
return true;
}
@Override
public EntityBean load(DbReadContext ctx) throws SQLException {
return load(ctx, null, null);
}
/**
* Read the version bean.
*/
@Override
@SuppressWarnings("unchecked")
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
// read the sys period lower and upper bounds
// these are always the first 2 columns in the resultSet
Timestamp start = ctx.getDataReader().getTimestamp();
Timestamp end = ctx.getDataReader().getTimestamp();
T bean = (T) load(ctx, null, null);
return new Version<>(bean, start, end);
public SqlTreeLoad createLoad() {
return new SqlTreeLoadRoot(this);
}
@Override
@@ -0,0 +1,41 @@
package io.ebeaninternal.server.query;
import java.util.Set;
final class SqlTreePlan {
private final SqlTreeLoad rootNode;
private final STreePropertyAssocMany manyProperty;
private final STreeProperty[] encryptedProps;
private final Set<String> dependentTables;
SqlTreePlan(SqlTreeLoad rootNode, STreePropertyAssocMany manyProperty, STreeProperty[] encryptedProps, Set<String> dependentTables) {
this.rootNode = rootNode;
this.manyProperty = manyProperty;
this.encryptedProps = encryptedProps;
this.dependentTables = dependentTables;
}
SqlTreeRoot getRootNode() {
return (SqlTreeRoot)rootNode;
}
/**
* Return the property that is associated with the many. There can only be one
* per SqlSelect. This can be null.
*/
STreePropertyAssocMany getManyProperty() {
return manyProperty;
}
STreeProperty[] getEncryptedProps() {
return encryptedProps;
}
/**
* Return the tables that are joined in this query.
*/
Set<String> dependentTables() {
return dependentTables;
}
}
@@ -4,11 +4,13 @@ import io.ebean.ProfileLocation;
import io.ebean.config.ProfilingConfig;
import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;
import io.ebean.util.IOUtils;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.api.SpiProfileHandler;
import org.slf4j.Logger;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
@@ -123,7 +125,7 @@ public final class DefaultProfileHandler implements SpiProfileHandler, Plugin {
try {
String now = DTF.format(LocalDateTime.now());
File file = new File(dir, "txprofile-" + now + ".tprofile");
out = new BufferedWriter(new FileWriter(file));
out = IOUtils.newWriter(file);
} catch (IOException e) {
log.error("Not expected", e);
}
@@ -1,7 +1,9 @@
package io.ebeaninternal.server.type;
import io.ebean.core.type.ScalarType;
import io.ebeaninternal.server.core.BasicTypeConverter;
import java.math.BigDecimal;
import java.util.UUID;
/**
@@ -48,6 +50,8 @@ interface ArrayElementConverter<T> {
*/
ArrayElementConverter<Double> DOUBLE = new DoubleConverter();
ArrayElementConverter<BigDecimal> BIG_DECIMAL = new BigDecimalConverter();
class LongConverter implements ArrayElementConverter<Long> {
@Override
@@ -99,6 +103,23 @@ interface ArrayElementConverter<T> {
}
}
class BigDecimalConverter implements ArrayElementConverter<BigDecimal> {
@Override
public BigDecimal fromSerialized(Object rawValue) {
return fromDbArray(rawValue);
}
@Override
public BigDecimal fromDbArray(Object rawValue) {
if (rawValue instanceof BigDecimal) {
return (BigDecimal) rawValue;
} else {
return BasicTypeConverter.toBigDecimal(rawValue);
}
}
}
/**
* String converter (noop based).
*/
@@ -95,6 +95,7 @@ public final class DefaultTypeManager implements TypeManager {
private final TypeJsonManager jsonManager;
private final boolean offlineMigrationGeneration;
private final EnumType defaultEnumType;
private final DatabasePlatform databasePlatform;
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
@@ -132,7 +133,8 @@ public final class DefaultTypeManager implements TypeManager {
this.typeMap = new ConcurrentHashMap<>();
this.nativeMap = new ConcurrentHashMap<>();
this.logicalMap = new ConcurrentHashMap<>();
this.postgres = isPostgres(config.getDatabasePlatform());
this.databasePlatform = config.getDatabasePlatform();
this.postgres = isPostgresCompatible(config.getDatabasePlatform());
this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent();
this.objectMapper = (objectMapperPresent) ? initObjectMapper(config) : null;
this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.getJsonMutationDetection()) : null;
@@ -193,13 +195,18 @@ public final class DefaultTypeManager implements TypeManager {
List<? extends ScalarType<?>> types = plugin.createTypes(config, objectMapper);
for (ScalarType<?> type : types) {
log.debug("adding ScalarType {}", type.getClass());
addCustomType(type);
add(type);
}
}
}
private boolean isPostgres(DatabasePlatform databasePlatform) {
return databasePlatform.getPlatform().base() == Platform.POSTGRES;
private boolean isPostgresCompatible(DatabasePlatform databasePlatform) {
return databasePlatform.isPlatform(Platform.POSTGRES)
|| databasePlatform.isPlatform(Platform.YUGABYTE);
}
private boolean hstoreSupport() {
return databasePlatform.isPlatform(Platform.POSTGRES);
}
/**
@@ -211,25 +218,6 @@ public final class DefaultTypeManager implements TypeManager {
logAdd(scalarType);
}
/**
* Register the ScalarType for an enum. This is special in the sense that an Enum
* can have many classes if it uses method overrides and we need to register all
* the variations/classes for the enum.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void addEnumType(ScalarType<?> scalarType, Class<? extends Enum> enumClass) {
Set<Class<?>> mappedClasses = new HashSet<>();
mappedClasses.add(enumClass);
for (Object value : EnumSet.allOf(enumClass).toArray()) {
mappedClasses.add(value.getClass());
}
for (Class<?> cls : mappedClasses) {
typeMap.put(cls, scalarType);
}
logAdd(scalarType);
}
private void logAdd(ScalarType<?> scalarType) {
if (log.isTraceEnabled()) {
String msg = "ScalarType register [" + scalarType.getClass().getName() + "]";
@@ -275,16 +263,43 @@ public final class DefaultTypeManager implements TypeManager {
"ScalarType of Joda LocalTime not defined. You need to set DatabaseConfig.jodaLocalTimeMode to"
+ " either 'normal' or 'utc'. UTC is the old mode using UTC timezone but local time zone is now preferred as 'normal' mode.");
}
found = checkInterfaceTypes(type);
found = checkInheritedTypes(type);
}
return found;
return found != ScalarTypeNotFound.INSTANCE ? found : null; // Do not return ScalarTypeNotFound, otherwise checks will fail
}
private ScalarType<?> checkInterfaceTypes(Class<?> type) {
if (java.nio.file.Path.class.isAssignableFrom(type)) {
return typeMap.get(java.nio.file.Path.class);
/**
* Checks the typeMap for inherited types.
*
* If e.g. <code>type</code> is a <code>GregorianCalendar</code>, then this method
* will check the class hierarchy and will probably return a
* <code>ScalarTypeCalendar</code> To speed up a second lookup, it will write
* back the found scalarType to typeMap.
*
* @param type the for which to search for a <code>ScalarType</code>
* @return either a valid <code>ScalarType</code> if one could be found or {@link ScalarTypeNotFound#INSTANCE} if not
*/
private ScalarType<?> checkInheritedTypes(Class<?> type) {
// first step loop through inheritance chain
Class<?> parent = type;
while (parent != null && parent != Object.class) {
ScalarType<?> found = typeMap.get(parent);
if (found != null && found != ScalarTypeNotFound.INSTANCE) {
typeMap.put(type, found); // store type for next lookup
return found;
}
// second step - loop through interfaces of this type
for (Class<?> iface: parent.getInterfaces()) {
found = checkInheritedTypes(iface);
if (found != null && found != ScalarTypeNotFound.INSTANCE) {
typeMap.put(type, found); // store type for next lookup
return found;
}
}
parent = parent.getSuperclass();
}
return null;
typeMap.put(type, ScalarTypeNotFound.INSTANCE);
return ScalarTypeNotFound.INSTANCE; // no success
}
@Override
@@ -294,7 +309,7 @@ public final class DefaultTypeManager implements TypeManager {
@Override
public ScalarType<?> getDbMapScalarType() {
return (postgres) ? hstoreType : ScalarTypeJsonMap.typeFor(false, Types.VARCHAR, false);
return hstoreSupport() ? hstoreType : ScalarTypeJsonMap.typeFor(false, Types.VARCHAR, false);
}
@Override
@@ -389,7 +404,7 @@ public final class DefaultTypeManager implements TypeManager {
private DocPropertyType getDocType(Type genericType) {
if (genericType instanceof Class<?>) {
ScalarType<?> found = typeMap.get(genericType);
ScalarType<?> found = getScalarType((Class<?>)genericType);
if (found != null) {
return found.getDocType();
}
@@ -447,7 +462,7 @@ public final class DefaultTypeManager implements TypeManager {
return scalarType;
}
scalarType = typeMap.get(type);
scalarType = getScalarType(type);
if (scalarType != null) {
if (jdbcType == 0 || scalarType.getJdbcType() == jdbcType) {
// matching type
@@ -565,7 +580,7 @@ public final class DefaultTypeManager implements TypeManager {
// use JPA normal Enum type (without mapping)
scalarEnum = createEnumScalarTypePerSpec(enumType, type);
}
addEnumType(scalarEnum, enumType);
add(scalarEnum);
return scalarEnum;
}
@@ -655,16 +670,13 @@ public final class DefaultTypeManager implements TypeManager {
scalarType = cls.getDeclaredConstructor().newInstance();
}
}
addCustomType(scalarType);
add(scalarType);
} catch (Exception e) {
log.error("Error loading ScalarType [" + cls.getName() + "]", e);
}
}
}
private void addCustomType(ScalarType<?> scalarType) {
add(scalarType);
}
private Object initObjectMapper(DatabaseConfig config) {
Object objectMapper = config.getObjectMapper();
@@ -735,7 +747,7 @@ public final class DefaultTypeManager implements TypeManager {
jsonNodeVarchar = new ScalarTypeJsonNode.Varchar(mapper);
jsonNodeJson = jsonNodeClob; // Default for non-Postgres databases
jsonNodeJsonb = jsonNodeClob; // Default for non-Postgres databases
if (isPostgres(config.getDatabasePlatform())) {
if (postgres) {
jsonNodeJson = new ScalarTypeJsonNodePostgres.JSON(mapper);
jsonNodeJsonb = new ScalarTypeJsonNodePostgres.JSONB(mapper);
}
@@ -750,7 +762,11 @@ public final class DefaultTypeManager implements TypeManager {
typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
addType(java.time.Period.class, new ScalarTypePeriod());
addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate));
if (config.getDatabasePlatform().supportsNativeJavaTime()) {
addType(java.time.LocalDate.class, new ScalarTypeLocalDateNative(jsonDate));
} else {
addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate));
}
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime));
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime, zoneId));
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime, zoneId));
@@ -790,7 +806,11 @@ public final class DefaultTypeManager implements TypeManager {
log.debug("Registering Joda data types");
addType(LocalDateTime.class, new ScalarTypeJodaLocalDateTime(jsonDateTime));
addType(DateTime.class, new ScalarTypeJodaDateTime(jsonDateTime));
addType(LocalDate.class, new ScalarTypeJodaLocalDate(jsonDate));
if (config.getDatabasePlatform().supportsNativeJavaTime()) {
addType(LocalDate.class, new ScalarTypeJodaLocalDateNative(jsonDate));
} else {
addType(LocalDate.class, new ScalarTypeJodaLocalDate(jsonDate));
}
addType(org.joda.time.DateMidnight.class, new ScalarTypeJodaDateMidnight(jsonDate));
addType(org.joda.time.Period.class, new ScalarTypeJodaPeriod());
@@ -34,6 +34,14 @@ final class EnumToDbIntegerMap extends EnumToDbValueMap<Integer> {
addInternal(beanValue, dbValue, name);
}
@Override
public Object getBeanValue(Object dbValue) {
if (dbValue instanceof String) {
return super.getBeanValue(Integer.parseInt(dbValue.toString()));
}
return super.getBeanValue(dbValue);
}
@Override
public EnumToDbIntegerMap add(Object beanValue, String stringDbValue, String name) {
try {
@@ -116,7 +116,8 @@ abstract class EnumToDbValueMap<T> {
/**
* Return the Bean value given the DB value.
*/
public Object getBeanValue(T dbValue) {
@SuppressWarnings("SuspiciousMethodCalls")
public Object getBeanValue(Object dbValue) {
if (dbValue == null) {
return null;
}
@@ -125,8 +126,7 @@ abstract class EnumToDbValueMap<T> {
beanValue = nameMap.get(dbValue);
}
if (beanValue == null && !allowNulls) {
String msg = "Bean value for " + dbValue + " not found in " + valueMap;
throw new IllegalArgumentException(msg);
throw new IllegalArgumentException("Bean value for " + dbValue + " not found in " + valueMap);
}
return beanValue;
}
@@ -73,6 +73,11 @@ public class RsetDataReader implements DataReader {
return rset.getObject(pos());
}
@Override
public <T> T getObject(Class<T> cls) throws SQLException {
return rset.getObject(pos(), cls);
}
@Override
public BigDecimal getBigDecimal() throws SQLException {
return rset.getBigDecimal(pos());
@@ -13,6 +13,7 @@ import io.ebeaninternal.json.ModifyAwareList;
import javax.persistence.PersistenceException;
import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
@@ -59,6 +60,9 @@ class ScalarTypeArrayList extends ScalarTypeArrayBase<List> implements ScalarTyp
if (valueType.equals(Double.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayList(nullable, "float", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE));
}
if (valueType.equals(BigDecimal.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayList(nullable, "decimal", DocPropertyType.DOUBLE, ArrayElementConverter.BIG_DECIMAL));
}
if (valueType.equals(String.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayList(nullable, "varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING));
}
@@ -5,6 +5,7 @@ import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
@@ -50,6 +51,9 @@ final class ScalarTypeArrayListH2 extends ScalarTypeArrayList {
if (valueType.equals(Double.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayListH2(nullable, "float", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE));
}
if (valueType.equals(BigDecimal.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayListH2(nullable, "float", DocPropertyType.DOUBLE, ArrayElementConverter.BIG_DECIMAL));
}
if (valueType.equals(String.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayListH2(nullable, "varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING));
}
@@ -189,7 +189,12 @@ final class ScalarTypeEnumStandard {
if (dbValue == null || dbValue instanceof Enum<?>) {
return dbValue;
}
int ordinal = (Integer) dbValue;
int ordinal;
if (dbValue instanceof Integer) {
ordinal = (Integer) dbValue;
} else {
ordinal = Integer.parseInt(dbValue.toString());
}
if (ordinal < 0 || ordinal >= enumArray.length) {
String m = "Unexpected ordinal [" + ordinal + "] out of range [" + enumArray.length + "]";
throw new IllegalStateException(m);
@@ -1,17 +1,18 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import java.sql.Date;
import java.sql.Types;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
/**
* ScalarType for Joda LocalDate. This maps to a JDBC Date.
*/
final class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
ScalarTypeJodaLocalDate(JsonConfig.Date mode) {
super(mode, LocalDate.class, false, Types.DATE);
@@ -40,7 +41,7 @@ final class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
@SuppressWarnings("deprecation")
@Override
public Date convertToDate(LocalDate value) {
return new Date(value .getYear() - 1900, value .getMonthOfYear() - 1, value .getDayOfMonth());
return new Date(value.getYear() - 1900, value.getMonthOfYear() - 1, value.getDayOfMonth());
}
@Override
@@ -54,9 +55,8 @@ final class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
@Override
public LocalDate toBeanType(Object value) {
if (value instanceof java.util.Date) {
return LocalDate.fromDateFields((java.util.Date)value);
return LocalDate.fromDateFields((java.util.Date) value);
}
return (LocalDate) value;
}
}
@@ -0,0 +1,35 @@
package io.ebeaninternal.server.type;
import java.sql.SQLException;
import java.sql.Types;
import org.joda.time.LocalDate;
import io.ebean.config.JsonConfig;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
/**
* ScalarType for Joda LocalDate. This maps to a LocalDate. Not all drivers/platforms may support this.
*/
final class ScalarTypeJodaLocalDateNative extends ScalarTypeJodaLocalDate{
ScalarTypeJodaLocalDateNative(JsonConfig.Date mode) {
super(mode);
}
@Override
public void bind(DataBinder binder, LocalDate value) throws SQLException {
if (value == null) {
binder.setNull(Types.DATE);
} else {
binder.setObject(java.time.LocalDate.of(value.getYear(), value.getMonthOfYear(), value.getDayOfMonth()));
}
}
@Override
public LocalDate read(DataReader reader) throws SQLException {
java.time.LocalDate jtDate = reader.getObject(java.time.LocalDate.class);
return jtDate == null ? null : new org.joda.time.LocalDate(jtDate.getYear(), jtDate.getMonthValue(), jtDate.getDayOfMonth());
}
}
@@ -8,6 +8,7 @@ import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.text.TextException;
import io.ebean.text.json.EJson;
import io.ebean.util.IOUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
@@ -75,14 +76,14 @@ abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
try {
if (keepSource) {
StringWriter jsonBuffer = new StringWriter();
try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
try (Reader streamReader = IOUtils.newReader(is)) {
transferTo(streamReader, jsonBuffer);
}
String rawJson = jsonBuffer.toString();
reader.pushJson(rawJson);
return parse(rawJson);
} else {
try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
try (Reader streamReader = IOUtils.newReader(is)) {
return parse(streamReader);
}
}
@@ -8,12 +8,12 @@ import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.text.TextException;
import io.ebean.util.IOUtils;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
@@ -68,7 +68,7 @@ abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
if (is == null) {
return null;
}
try (InputStreamReader reader = new InputStreamReader(is)) {
try (Reader reader = IOUtils.newReader(is)) {
return parse(reader);
} catch (IOException e) {
throw new SQLException("Error reading Blob stream from DB", e);
@@ -1,8 +1,5 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
import java.sql.Date;
import java.sql.Timestamp;
import java.sql.Types;
@@ -10,10 +7,13 @@ import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import io.ebean.config.JsonConfig;
import io.ebeaninternal.server.core.BasicTypeConverter;
/**
* ScalarType for java.time.LocalDate. This maps to a JDBC Date.
*/
final class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
ScalarTypeLocalDate(JsonConfig.Date mode) {
super(mode, LocalDate.class, false, Types.DATE);
@@ -60,5 +60,4 @@ final class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
}
return (LocalDate) value;
}
}
@@ -0,0 +1,34 @@
package io.ebeaninternal.server.type;
import java.sql.SQLException;
import java.sql.Types;
import java.time.LocalDate;
import io.ebean.config.JsonConfig;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
/**
* ScalarType for java.time.LocalDate. This maps to a LocalDate. Not all drivers/platforms may support this.
*/
final class ScalarTypeLocalDateNative extends ScalarTypeLocalDate {
ScalarTypeLocalDateNative(JsonConfig.Date mode) {
super(mode);
}
@Override
public void bind(DataBinder binder, LocalDate value) throws SQLException {
if (value == null) {
binder.setNull(Types.DATE);
} else {
binder.setObject(value);
}
}
@Override
public LocalDate read(DataReader reader) throws SQLException {
return reader.getObject(LocalDate.class);
}
}
@@ -0,0 +1,140 @@
package io.ebeaninternal.server.type;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
/**
* Class is required as "null" key in ConcurrentHashMap.
* @author Roland Praml, FOCONIS AG
*
*/
class ScalarTypeNotFound implements ScalarType<Void> {
public static final ScalarTypeNotFound INSTANCE = new ScalarTypeNotFound();
private ScalarTypeNotFound() { }
@Override
public boolean isBinaryType() {
throw new UnsupportedOperationException();
}
@Override
public boolean isMutable() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDirty(Object value) {
throw new UnsupportedOperationException();
}
@Override
public int getLength() {
throw new UnsupportedOperationException();
}
@Override
public boolean isJdbcNative() {
throw new UnsupportedOperationException();
}
@Override
public int getJdbcType() {
throw new UnsupportedOperationException();
}
@Override
public Class<Void> getType() {
throw new UnsupportedOperationException();
}
@Override
public Void read(DataReader reader) throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public void loadIgnore(DataReader reader) {
throw new UnsupportedOperationException();
}
@Override
public void bind(DataBinder binder, Void value) throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public Object toJdbcType(Object value) {
throw new UnsupportedOperationException();
}
@Override
public Void toBeanType(Object value) {
throw new UnsupportedOperationException();
}
@Override
public String formatValue(Void value) {
throw new UnsupportedOperationException();
}
@Override
public String format(Object value) {
throw new UnsupportedOperationException();
}
@Override
public Void parse(String value) {
throw new UnsupportedOperationException();
}
@Override
public DocPropertyType getDocType() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDateTimeCapable() {
throw new UnsupportedOperationException();
}
@Override
public long asVersion(Void value) {
throw new UnsupportedOperationException();
}
@Override
public Void convertFromMillis(long dateTime) {
throw new UnsupportedOperationException();
}
@Override
public Void readData(DataInput dataInput) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void writeData(DataOutput dataOutput, Void v) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public Void jsonRead(JsonParser parser) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void jsonWrite(JsonGenerator writer, Void value) throws IOException {
throw new UnsupportedOperationException();
}
}
@@ -32,7 +32,7 @@ public final class SimpleAesEncryptor implements Encryptor {
}
private IvParameterSpec getIvParameterSpec(String initialVector) {
return new IvParameterSpec(initialVector.getBytes());
return new IvParameterSpec(initialVector.getBytes(StandardCharsets.UTF_8));
}
@Override
@@ -17,12 +17,6 @@ public interface TypeManager {
*/
void add(ScalarType<?> scalarType);
/**
* Register a ScalarType for an Enum with can have multiple classes.
*/
@SuppressWarnings("rawtypes")
void addEnumType(ScalarType<?> type, Class<? extends Enum> myEnumClass);
/**
* Return the scalar type for the given logical type.
*/
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.14.0</version>
<version>12.15.0</version>
</parent>
<name>ebean ddl generation</name>
@@ -22,20 +22,20 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>12.12.1</version>
<version>12.13.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.14.0</version>
<version>12.15.0</version>
<scope>provided</scope>
</dependency>
@@ -5,6 +5,7 @@ import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.ddlrunner.DdlRunner;
import io.ebean.ddlrunner.ScriptTransform;
import io.ebean.util.IOUtils;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.SpiDdlGenerator;
import io.ebeaninternal.api.SpiEbeanServer;
@@ -17,13 +18,11 @@ import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.io.Writer;
import java.sql.Connection;
import java.sql.SQLException;
@@ -50,6 +49,7 @@ public class DdlGenerator implements SpiDdlGenerator {
private final ScriptTransform scriptTransform;
private final Platform platform;
private final String platformName;
private final boolean useMigrationStoredProcedures;
private CurrentModel currentModel;
private String dropAllContent;
@@ -71,9 +71,11 @@ public class DdlGenerator implements SpiDdlGenerator {
log.warn("DDL can't be run on startup with TenantMode " + config.getTenantMode());
this.runDdl = false;
this.ddlAutoCommit = false;
this.useMigrationStoredProcedures = false;
} else {
this.runDdl = config.isDdlRun();
this.ddlAutoCommit = databasePlatform.isDdlAutoCommit();
this.useMigrationStoredProcedures = config.getDatabasePlatform().isUseMigrationStoredProcedures();
}
this.scriptTransform = createScriptTransform(config);
this.baseDir = initBaseDir();
@@ -187,7 +189,7 @@ public class DdlGenerator implements SpiDdlGenerator {
protected void runDropSql(Connection connection) throws IOException {
if (!createOnly) {
if (extraDdl && jaxbPresent) {
if (extraDdl && jaxbPresent && useMigrationStoredProcedures) {
String extraApply = ExtraDdlXmlReader.buildExtra(platform, true);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-ddl");
@@ -268,7 +270,7 @@ public class DdlGenerator implements SpiDdlGenerator {
if (is == null) {
log.warn("sql script {} was not found as a resource", sqlScript);
} else {
String content = readContent(new InputStreamReader(is));
String content = readContent(IOUtils.newReader(is)); // 'is' is closed
runScript(connection, false, content, sqlScript);
}
}
@@ -337,7 +339,7 @@ public class DdlGenerator implements SpiDdlGenerator {
protected void writeFile(String fileName, String fileContent) throws IOException {
File f = new File(baseDir, fileName);
try (FileWriter fw = new FileWriter(f)) {
try (Writer fw = IOUtils.newWriter(f)) {
fw.write(fileContent);
fw.flush();
}
@@ -348,7 +350,9 @@ public class DdlGenerator implements SpiDdlGenerator {
if (!f.exists()) {
return null;
}
return readContent(new FileReader(f));
try (Reader reader = IOUtils.newReader(f)) {
return readContent(reader);
}
}
protected String readContent(Reader reader) throws IOException {

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