Compare commits

...
65 Commits
Author SHA1 Message Date
Rob Bygrave d051bba71f Version 13.20.1 2023-07-04 12:05:30 +12:00
Rob Bygrave f414f04033 Add note on lack of support for InTuples with SQLServer and DB2 2023-07-04 12:05:10 +12:00
Rob Bygrave ac466ecd79 #3125 Add query.usingMaster() for query beans 2023-07-03 23:30:02 +12:00
Rob Bygrave 262a7f9bab Bump ebean-agent version 2023-07-03 22:57:55 +12:00
Rob BygraveandGitHub bb02d74005 Merge pull request #3125 from ebean-orm/feature/query-usingMaster
Add query.usingMaster() to explicitly choose master data source
2023-07-03 22:48:47 +12:00
Rob BygraveandGitHub e9b2b8d266 Merge pull request #3017 from ebean-orm/feature/db2luw9
Initial DB2Luw9Platform using row_number() for row limiting sql
2023-07-03 22:43:55 +12:00
Rob Bygrave fc16bde227 Modify ImplicitReadOnlyTransaction, call deactivate in finally 2023-07-03 22:43:13 +12:00
Rob Bygrave d1775dfcc4 #3017 Change DB2LegacyPlatform to also use the row_number limiters 2023-07-03 22:35:46 +12:00
3d5937f6da Update platforms/db2/src/main/java/io/ebean/platform/db2/DB2RowNumberBasicLimiter.java
Bump StringBuilder to use 120

Co-authored-by: Roland Praml <roland.praml@foconis.de>
2023-07-03 22:31:01 +12:00
Rob BygraveandGitHub adca8cd8fa Merge branch 'master' into feature/db2luw9 2023-07-03 22:28:43 +12:00
Rob BygraveandGitHub 6d30e6310b Merge pull request #3127 from ebean-orm/feature/3126
#3126 - Incorrect complex indexes generation
2023-07-03 22:22:32 +12:00
Rob Bygrave 79ed0a5f8e #3126 - Incorrect complex indexes generation 2023-07-03 22:19:38 +12:00
Rob Bygrave 94b3a020d5 Modify ImplicitReadOnlyTransaction useCommit to check connection.getAutoCommit() 2023-07-03 21:40:21 +12:00
Rob Bygrave 3e241907e6 Modify internal ImplicitReadOnlyTransaction to also support calling commit()
Originally, ImplicitReadOnlyTransaction was built to support read-only DataSource where the connections are using autoCommit true. This change modified ImplicitReadOnlyTransaction to also support use with the main DataSource where the connection are not using autoCommit true.

This is also now needed for the query.usingMaster() case where we have an implicit read only transaction but use the main DataSource with underlying connection requiring explicit commit() at the end of the transaction.
2023-07-03 21:27:58 +12:00
Rob Bygrave c4af1d9b23 Add query.usingMaster() to explicitly choose master data source
When we have a read replica with potential for replication lag, and config a read only DataSource to use that read replica, by default a query executed without a current transaction will use the read replica.

We specify usingMaster() when we explicitly want to execute a query and use the master data source.
2023-07-03 16:41:22 +12:00
Rob BygraveandGitHub 7702169762 Merge pull request #3124 from ebean-orm/feature/InTuples
Add InTuples expression for multi-column IN expression
2023-07-03 12:48:21 +12:00
Rob Bygrave 8209d9e229 Add SpiInTuples to hide more details from InTuples 2023-07-03 12:41:31 +12:00
Rob Bygrave 664120f369 Add InTuples expression for multi-column IN expression 2023-07-03 11:54:38 +12:00
Rob Bygrave e952b555e0 Add InTuples expression for multi-column IN expression 2023-07-03 11:49:53 +12:00
Rob BygraveandGitHub d99c50da75 Merge pull request #3122 from ebean-orm/feature/3109-jta-txn-fix
#3109 Fix for JtaTransactionManager when no active transaction
2023-06-26 21:30:55 +12:00
Rob Bygrave d4bec166f7 #3109 Fix for JtaTransactionManager when no active transaction
The call to registry() can throw an exception so
pulling that inside the try catch block and handle
that as no active transaction returning null.
2023-06-26 21:30:01 +12:00
Rob Bygrave 9a4b9d4bec Bump to next snapshot version 2023-06-21 07:08:13 +12:00
Rob Bygrave 68a6e98292 Version 13.20.0 2023-06-20 20:35:04 +12:00
Rob Bygrave d61bdb028b Bump ebean-agent to 13.20.0 2023-06-20 20:32:29 +12:00
Rob BygraveandGitHub 9ddadb1338 Merge pull request #3118 from ebean-orm/feature/3109-JtaTransaction-currentTransaction
#3109 JtaTransactionManager - fix getCurrentTransaction()
2023-06-20 19:46:45 +12:00
Rob Bygrave fa1f225e67 #3109 follow up - refactor rename internal methods 2023-06-20 19:41:00 +12:00
Rob Bygrave 7a72adedfa #3109 JtaTransactionManager - fix getCurrentTransaction()
Fix for JtaTransactionManager to allow Wildfly (or other) to throw an Exception when TransactionSynchronizationRegistry.getResource() is called.
2023-06-20 19:39:08 +12:00
Rob BygraveandGitHub 3d3e5c2c34 Merge pull request #3117 from ebean-orm/feature/bump-test-containers
Bump ebean-test-containers to 7.1
2023-06-20 18:45:06 +12:00
Rob Bygrave 708f429c21 Bump ebean-test-containers to 7.1 2023-06-20 18:40:48 +12:00
Rob BygraveandGitHub bca49ed2e2 Merge pull request #3116 from ebean-orm/feature/bump-test-containers
Bump ebean-test-containers to 7.0
2023-06-20 17:47:15 +12:00
Rob Bygrave e103ec7706 Bump ebean-test-containers to 7.0 2023-06-20 17:46:36 +12:00
Rob BygraveandGitHub 9fc1bec6a9 Merge pull request #3046 from FOCONIS/bugfix/lazy_loading_findEach_garbage_collection
Bugfix for NPE when lazyLoading in findEach due to Garbage Collection
2023-06-17 11:56:25 +12:00
Rob BygraveandGitHub f712635860 Merge pull request #3107 from FOCONIS/json-tests
DB specific length support in JSONs/Varchars/binary data
2023-06-17 11:38:46 +12:00
Rob BygraveandGitHub 47a0e38eac Merge pull request #3114 from ebean-orm/feature/3113-followup
Followup to #3113, remove unused first BatchControl queue
2023-06-17 11:36:53 +12:00
Rob Bygrave 870d90d055 Followup to #3113, remove unused first BatchControl queue 2023-06-17 11:35:51 +12:00
Rob BygraveandGitHub ac1b30782a Merge pull request #3113 from FOCONIS/bugfix/batch_ordering_many2many_intersection
Bugfix for wrong batch statement ordering with many2many relations
2023-06-17 11:28:01 +12:00
Jonas Pöhler b63445e9c9 FIX: Put the delete statements into the correct flush-queue, so they do not conflict with the following inserts 2023-06-16 16:26:14 +02:00
Jonas Pöhler 09f616aae8 ADD: testcase for showing an issue with deleting and re-adding many-to-many elements (size > 20)
(cherry picked from commit e6a4bc4e0c31bb35ab5a13c958c7c4b1f68ca037)
2023-06-16 16:16:53 +02:00
Roland Praml 994fb267ad Merge branch 'master-upstream' into json-tests 2023-06-16 07:40:01 +02:00
Rob BygraveandGitHub 53b1e4d682 Merge pull request #3111 from ebean-orm/feature/3101-jpa-indexes
#3101 - Support JPA Indexes
2023-06-16 13:53:48 +12:00
robin.bygrave c7f6c508a6 #3101 - Support JPA Indexes 2023-06-16 13:44:03 +12:00
Rob BygraveandGitHub 9109fd356f Merge pull request #3108 from FOCONIS/regenerated-scripts
No code change: Regenerated scripts
2023-06-15 23:37:51 +12:00
Roland Praml 55477d4fab No code change: Regenerated scripts 2023-06-15 08:07:51 +02:00
Rob BygraveandGitHub 340281113f Merge pull request #3106 from focbenz/patch-1
Update CONFIGURATION.md with small corrections and comment for includeGeneratedFileComment
2023-06-15 08:05:26 +12:00
Roland Praml 77610672a9 Regenerated migration scripts again 2023-06-14 18:24:23 +02:00
Roland Praml d8238735c1 Fix also DbTypeMapTest, which uses the new varbinary type 2023-06-14 18:19:17 +02:00
Roland Praml f63b013ef9 Added and fixed tests 2023-06-14 18:14:12 +02:00
Roland Praml 6fe3ec9355 Migration scripts 2023-06-14 17:50:26 +02:00
Roland Praml d1c21bf50a Hana, NuoDb, SqlAnywhere: add limits (untested!) 2023-06-14 17:50:08 +02:00
Roland Praml 578560c2e2 Oracle: max varchar length = 4000, raw = 2000 2023-06-14 17:49:10 +02:00
Roland Praml 0f9462b7b5 Postgres: max varchar limit = 10MB 2023-06-14 17:32:00 +02:00
Roland Praml e89891038b Mysql: max varchar length = 4000, varbinary = 8000. Also refactored MySql(C|B)lob 2023-06-14 17:30:28 +02:00
Roland Praml 2a7a2d4dd4 DB2: max varchar length = 4000, varbinary = 8000 2023-06-14 17:27:58 +02:00
Roland Praml f499cc8c2c SQLSERVER: max nvarchar length = 4000, varbinary = 8000 (no longer using deprecated image type by default) 2023-06-14 17:19:48 +02:00
Roland Praml 0dc4c6b11b NEW: DbPlatformType supports fallback, when length exceeded (removed SqlServer workaround) 2023-06-14 17:18:06 +02:00
Roland Praml f5d0777b86 Added new test models with lengths 2023-06-14 17:17:31 +02:00
Roland Praml 81443b77bc Fixes for Clickhouse platform 2023-06-14 17:16:14 +02:00
Roland Praml 2867c50090 Do not convert explicit defined column definitions 2023-06-14 17:16:14 +02:00
Michael BenzandGitHub c04171c1a5 Update CONFIGURATION.md with small corrections
Updated configuration markdown with small corrections and removed TODO for includeGeneratedFileComment

See https://github.com/ebean-orm/ebean/issues/550
2023-06-14 14:34:09 +02:00
Rob BygraveandGitHub e19cbdc2f6 Merge pull request #3104 from FOCONIS/dbmigration-fixes
Fixed DbMigration classes (put them in the right place)
2023-06-14 07:58:50 +12:00
Roland Praml 3e2e84e8bf Fixed DbMigration classes (put them in the right place) 2023-06-13 17:20:02 +02:00
Jonas Pöhler edb3759b3d Potential fix for NPE in lazyLoad under HeapPressure
(cherry picked from commit 5aa163bb75)
2023-05-05 09:58:01 +02:00
Jonas Pöhler 7ee0610db1 ADD: Test for reproducing NPE with lazyloaing in findEach under HeapPressure
(cherry picked from commit 96f071e08b)
2023-05-05 09:57:43 +02:00
Rob Bygrave 63a23904ac Update for DB2Luw9 with rn column as last column 2023-04-13 21:52:09 +12:00
Rob Bygrave 1807fc201e Initial DB2Luw9Platform using row_number() for row limiting sql 2023-03-30 22:56:55 +13:00
297 changed files with 5581 additions and 1713 deletions
+1
View File
@@ -11,6 +11,7 @@ ebean-profiling*.xml
/db
/mydb.db
profiling/
.DS_Store
# Intellij project files
*.iml
+11 -12
View File
@@ -4,13 +4,13 @@ Overview of ebean.properties file
### DbMigration options
You can set individual values for different platforms when generating migrations
You can set individual values for different platforms when generating migrations.
`dbmigration.platform.<PLATFORM>.databaseSequenceBatchSize`
For DB's using sequences this is the number of sequence values prefetched.
`dbmigration.platform.<PLATFORM>.dbuuid`
Control, how UUID generation should work - it affects DDL which column type is generated. Possible values:
Control, how UUID generation should work - it affects which column type is generated in DDL. Possible values:
- BINARY enforces binary UUID
- VARCHAR enforces varchar UUID
- BINARY_OPTIMIZED enforces binary-optimized UUID (makes sense only with Type1 ID)
@@ -19,7 +19,7 @@ Control, how UUID generation should work - it affects DDL which column type is g
- AUTO_VARCHAR (default) use varchar when platform does not support UUID
`dbmigration.platform.<PLATFORM>.uuidStoreAsBinary`
Same as setting dbuuid to BINARY
Same as setting dbuuid to BINARY.
`dbmigration.platform.<PLATFORM>.geometrySRID`
The Geometry SRID value (default 4326).
@@ -30,7 +30,6 @@ The ID type (IDENTITY, SEQUENCE, GENERATOR, EXTERNAL)
`dbmigration.platform.<PLATFORM>.mapping`
Adjust the mapping. For example `BOOLEAN=integer(32);BIT=tinyint(3)`
`ebean.migration.applyPrefix`
Set this to "V" to be compatible with FlywayDB.
@@ -50,7 +49,7 @@ Set to true if the DB migration should be generated on server start.
The version of a pending drop that should be generated as the next migration.
`ebean.migration.includeGeneratedFileComment`
TODO
Adds the header about the migration files being generated when true. Reading 'THIS IS A GENERATED FILE - DO NOT MODIFY'
`ebean.migration.metaTable`
For running migration the DB table that holds migration execution status. Default 'db_migration'
@@ -65,17 +64,17 @@ Subdirectory the model xml files go into. Default 'model'
Suffix. Default '.model.xml'
`ebean.migration.name`
Description text that can be appended to the version to become the ddl script file name
Description text that can be appended to the version to become the ddl script file name.
`ebean.migration.patchInsertOn`
migration versions that should be added to history without running.
Migration versions that should be added to history without running.
`ebean.migration.patchResetChecksumOn`
migration versions that should have their checksum reset and not run.
Use this if you get a 'Checksum mismatch' error.
`ebean.migration.placeholders`
A comma and equals delimited placeholders that are substituted in SQL scripts when running migration (used by DB Migration runner only).
A comma and equals delimited map of placeholders that are substituted in SQL scripts when running migration (used by DB Migration runner only).
`ebean.migration.platform`
The database platform to generate migration DDL for.
@@ -90,7 +89,7 @@ The migration version name (typically FlywayDb compatible). Example: 1.1.1_2
### Ebean UUID options
`ebean.uuidVersion`
Controls, how the UUIDs are generated. Possible values
Controls how the UUIDs are generated. Possible values:
- VERSION4 (default) generate random V4 UUIDs,
- VERSION1 generate rfc4122 compliant Type 1 UUIDs (requires a state file)
- VERSION1RND generate fake Type 1 UUIDs
@@ -98,7 +97,7 @@ Controls, how the UUIDs are generated. Possible values
Note, that V1 UUIDs in conjunction with AUTO_BINARY_OPTIMIZED will give you the best index performance, but you MUST understand how this works to avoid collisions.
`ebean.uuidStateFile`
The state file that is Required to generate V1 UUIDs
The state file that is required to generate V1 UUIDs.
### DocStoreConfig
@@ -107,7 +106,7 @@ The state file that is Required to generate V1 UUIDs
True when the Document store integration is active/on.
`ebean.docstore.allowAllCertificates`
Set to true such that the client allows connections to invalid/self signed SSL certificates.
Set to true such that the client allows connections to invalid/self-signed SSL certificates.
`ebean.docstore.bulkBatchSize`
The default batch size to use for the Bulk API calls.
@@ -192,7 +191,7 @@ Suffix appended to the base table to derive the view that contains the union of
Set to true if the DataSource uses autoCommit. Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
`ebean.autoReadOnlyDataSource`
When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig
When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig.
`ebean.autostart`
Should the server start all
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-clickhouse</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-cockroach</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-db2</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-h2</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-hana</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-mariadb</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-mysql</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-nuodb</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-oracle</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-postgres</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-sqlite</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-sqlserver</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean-yugabyte</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>composites</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean (all platforms)</name>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -40,7 +40,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -59,13 +59,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<artifactId>composites</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean api</name>
@@ -0,0 +1,49 @@
package io.ebean;
import io.ebean.service.SpiInTuples;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Default implementation of SpiInTuples.
*/
final class DInTuples implements SpiInTuples {
private final String[] properties;
private final int propertyCount;
private final List<Object[]> entries = new ArrayList<>();
DInTuples(String[] properties) {
this.properties = properties;
this.propertyCount = properties.length;
}
@Override
public InTuples add(Object... values) {
if (values.length != propertyCount) {
throw new IllegalArgumentException("Require " + propertyCount + " values but got " + values.length);
}
entries.add(values);
return this;
}
/**
* Return the first property name.
*/
@Override
public String[] properties() {
return properties;
}
/**
* Return all the value pairs.
*/
@Override
public List<Object[]> entries() {
return Collections.unmodifiableList(entries);
}
}
@@ -205,4 +205,16 @@ public interface DtoQuery<T> extends CancelableQuery {
* Use the explicit transaction to execute the query.
*/
DtoQuery<T> usingTransaction(Transaction transaction);
/**
* Ensure that the master DataSource is used if there is a read only data source
* being used (that is using a read replica database potentially with replication lag).
* <p>
* When the database is configured with a read-only DataSource via
* say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then
* by default when a query is run without an active transaction, it uses the read-only data
* source. We we use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
DtoQuery<T> usingMaster();
}
@@ -369,6 +369,11 @@ public interface ExpressionFactory {
*/
Expression inPairs(Pairs pairs);
/**
* In expression using multiple columns.
*/
Expression inTuples(InTuples pairs);
/**
* In - property has a value in the array of values.
*/
@@ -1131,6 +1131,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> inPairs(Pairs pairs);
/**
* In expression using multiple columns.
*/
ExpressionList<T> inTuples(InTuples pairs);
/**
* EXISTS a raw SQL SubQuery.
*
@@ -0,0 +1,36 @@
package io.ebean;
/**
* IN expression using multiple columns.
* <p>
* Currently this is not supported with SQLServer or DB2.
* <p>
* Produces SQL expression in the form of (A,B,C) IN ((a0,b0,c0), (a1,b1,c1), ... )
* where A,B,C are the properties in the tuples.
*/
public interface InTuples {
/**
* Create given the properties in the tuples.
*/
static InTuples of(String... properties) {
return new DInTuples(properties);
}
/**
* Create given the properties in the tuples.
*/
static InTuples of(Query.Property<?>... properties) {
String[] props = new String[properties.length];
for (int i = 0; i < properties.length; i++) {
props[i] = properties[i].toString();
}
return new DInTuples(props);
}
/**
* Add a tuple entry. All values must be non-null.
*/
InTuples add(Object... values);
}
@@ -4,6 +4,7 @@ import io.avaje.lang.NonNullApi;
import io.avaje.lang.Nullable;
import javax.persistence.NonUniqueResultException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.List;
@@ -685,6 +686,18 @@ public interface Query<T> extends CancelableQuery {
*/
Query<T> usingDatabase(Database database);
/**
* Ensure that the master DataSource is used if there is a read only data source
* being used (that is using a read replica database potentially with replication lag).
* <p>
* When the database is configured with a read-only DataSource via
* say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then
* by default when a query is run without an active transaction, it uses the read-only data
* source. We we use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
Query<T> usingMaster();
/**
* Execute the query returning the list of Id's.
* <p>
@@ -45,6 +45,18 @@ public interface SqlQuery extends Serializable, CancelableQuery {
*/
SqlQuery usingTransaction(Transaction transaction);
/**
* Ensure that the master DataSource is used if there is a read only data source
* being used (that is using a read replica database potentially with replication lag).
* <p>
* When the database is configured with a read-only DataSource via
* say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then
* by default when a query is run without an active transaction, it uses the read-only data
* source. We we use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
SqlQuery usingMaster();
/**
* Execute the query returning a list.
*/
@@ -135,7 +135,7 @@ public class DbConstraintNaming {
*/
protected String joinColumnNames(String[] columns) {
if (columns.length == 1) {
return normaliseColumn(columns[0]);
return normaliseColumn(columns[0]).replace(" ", "_");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columns.length; i++) {
@@ -144,7 +144,7 @@ public class DbConstraintNaming {
}
sb.append(normaliseColumn(columns[i]));
}
return sb.toString();
return sb.toString().replace(" ", "_");
}
/**
@@ -27,7 +27,6 @@ public class DbConstraintNormalise {
* quoted identifier characters (",',[,] etc).
*/
public String normaliseTable(String tableName) {
tableName = trimQuotes(tableName);
int lastPeriod = tableName.lastIndexOf('.');
if (lastPeriod > -1) {
@@ -59,7 +58,6 @@ public class DbConstraintNormalise {
* Trim off the platform quoted identifier quotes like [ ' and ".
*/
public String trimQuotes(String identifier) {
if (identifier == null) {
return "";
}
@@ -28,6 +28,16 @@ public class DbPlatformType implements ExtraDbTypes {
*/
private final boolean canHaveLength;
/**
* The maximum length supported by this platform type. If length is too big, fallback is used.
*/
private final int maxLength;
/**
* Use this platform type if length exceedes
*/
private final DbPlatformType fallback;
/**
* Parse a type definition into a DbPlatformType.
* <p>
@@ -53,6 +63,41 @@ public class DbPlatformType implements ExtraDbTypes {
this(name, defaultLength, 0);
}
/**
* Construct without length, but with a max length limit and a fallback type, that is used if maxLength is exceeded.
* This can be used to use <ul>
* <li>"longtext" for unspecified length</li>
* <li>"text" for length up to 2^16-1</li>
* <li>"mediumtext" for length up to 2^24-1</li>
* <li>"longtext" else</li>
* </ul>
*/
public DbPlatformType(String name, int maxLength, DbPlatformType fallback) {
this.name = name;
this.defaultLength = 0;
this.defaultScale = 0;
this.canHaveLength = false;
this.maxLength = maxLength;
this.fallback = fallback;
}
/**
* Construct with a given default length, a max length limit and a fallback type, that is used if maxLength is exceeded.
* This can be used to use <ul>
* <li>"varchar(255)" for unspecified length</li>
* <li>"varchar(N)" for N <= maxLength</li>
* <li>"varchar(max)" else</li>
* </ul>
*/
public DbPlatformType(String name, int defaultPrecision, int maxLength, DbPlatformType fallback) {
this.name = name;
this.defaultLength = defaultPrecision;
this.defaultScale = 0;
this.canHaveLength = true;
this.maxLength = maxLength;
this.fallback = fallback;
}
/**
* Construct for Decimal with precision and scale.
*/
@@ -61,6 +106,8 @@ public class DbPlatformType implements ExtraDbTypes {
this.defaultLength = defaultPrecision;
this.defaultScale = defaultScale;
this.canHaveLength = true;
this.maxLength = Integer.MAX_VALUE;
this.fallback = null;
}
/**
@@ -74,6 +121,8 @@ public class DbPlatformType implements ExtraDbTypes {
this.defaultLength = 0;
this.defaultScale = 0;
this.canHaveLength = canHaveLength;
this.maxLength = Integer.MAX_VALUE;
this.fallback = null;
}
/**
@@ -124,33 +173,33 @@ public class DbPlatformType implements ExtraDbTypes {
*/
public String renderType(int deployLength, int deployScale, boolean strict) {
StringBuilder sb = new StringBuilder();
sb.append(name);
if (canHaveLength || !strict) {
renderLengthScale(deployLength, deployScale, sb);
}
int len = deployLength != 0 ? deployLength : defaultLength;
if (len > maxLength) {
return fallback.renderType(deployLength, deployScale, strict);
} else {
StringBuilder sb = new StringBuilder();
sb.append(name);
if ((canHaveLength || !strict) && len > 0) {
renderLengthScale(len, deployScale, sb);
}
return sb.toString();
return sb.toString();
}
}
/**
* Render the length and scale part of the column definition.
*/
protected void renderLengthScale(int deployLength, int deployScale, StringBuilder sb) {
protected void renderLengthScale(int len, int deployScale, StringBuilder sb) {
// see if there is a precision/scale to add (or not)
int len = deployLength != 0 ? deployLength : defaultLength;
if (len == Integer.MAX_VALUE) {
sb.append("(max)"); // TODO: this is sqlserver specific
} else if (len > 0) {
sb.append("(");
sb.append(len);
int scale = deployScale != 0 ? deployScale : defaultScale;
if (scale > 0) {
sb.append(",");
sb.append(scale);
}
sb.append(")");
sb.append('(');
sb.append(len);
int scale = deployScale != 0 ? deployScale : defaultScale;
if (scale > 0) {
sb.append(',');
sb.append(scale);
}
sb.append(')');
}
/**
@@ -0,0 +1,22 @@
package io.ebean.service;
import io.ebean.InTuples;
import java.util.List;
/**
* Internal extension to InTuples.
*/
public interface SpiInTuples extends InTuples {
/**
* Return the properties of the tuples.
*/
String[] properties();
/**
* Return all the tuple entries.
*/
List<Object[]> entries();
}
+25 -25
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean bom</name>
@@ -89,25 +89,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -125,13 +125,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -155,46 +155,46 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<!-- platforms -->
@@ -202,67 +202,67 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
+6 -6
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,7 +22,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -46,7 +46,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -159,21 +159,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
@@ -135,8 +135,9 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio
* Create a ServerTransaction for query purposes.
*
* @param tenantId For multi-tenant lazy loading provide the tenantId to use.
* @param useMaster Set to true when the query should use the master data source.
*/
SpiTransaction createReadOnlyTransaction(Object tenantId);
SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster);
/**
* An event from another server in the cluster used to notify local
@@ -235,6 +235,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
*/
String planLabel();
/**
* Return true if this query should not use the read only data source.
*/
boolean isUseMaster();
/**
* Return true if this is a "find by id" query. This includes a check for a single "equal to" expression for the Id.
*/
@@ -5,6 +5,11 @@ package io.ebeaninternal.api;
*/
public interface SpiSqlBinding extends SpiCancelableQuery {
/**
* Return true if this query should not use the read only data source.
*/
boolean isUseMaster();
/**
* Return the named or positioned parameters.
*/
@@ -48,7 +48,7 @@ public abstract class AbstractSqlQueryRequest implements CancelableQuery {
transaction = server.currentServerTransaction();
if (transaction == null || !transaction.isActive()) {
// create a local readOnly transaction
transaction = server.createReadOnlyTransaction(null);
transaction = server.createReadOnlyTransaction(null, query.isUseMaster());
createdTransaction = true;
}
}
@@ -137,7 +137,7 @@ final class DefaultBeanLoader {
private List<?> executeQuery(LoadRequest loadRequest, SpiQuery<?> query) {
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
// MySql - we need a different transaction to execute the secondary query
SpiTransaction extraTxn = server.createReadOnlyTransaction(query.tenantId());
SpiTransaction extraTxn = server.createReadOnlyTransaction(query.tenantId(), query.isUseMaster());
try {
return server.findList(query, extraTxn);
} finally {
@@ -2123,8 +2123,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
return transactionManager.createReadOnlyTransaction(tenantId);
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
return transactionManager.createReadOnlyTransaction(tenantId, useMaster);
}
/**
@@ -56,7 +56,10 @@ final class MultiTenantDbCatalogSupplier implements DataSourceSupplier {
}
@Override
public Connection readOnlyConnection(Object tenantId) throws SQLException {
public Connection readOnlyConnection(Object tenantId, boolean useMaster) throws SQLException {
if (readOnly == null || useMaster) {
return catalogDataSource.getConnectionForTenant(tenantId);
}
return readOnly.getConnectionForTenant(tenantId);
}
@@ -103,7 +106,6 @@ final class MultiTenantDbCatalogSupplier implements DataSourceSupplier {
*/
@Override
public Connection getConnection() throws SQLException {
Connection connection = dataSource.getConnection();
connection.setCatalog(tenantCatalog());
return connection;
@@ -56,8 +56,12 @@ final class MultiTenantDbSchemaSupplier implements DataSourceSupplier {
}
@Override
public Connection readOnlyConnection(Object tenantId) throws SQLException {
return readOnly.getConnectionForTenant(tenantId);
public Connection readOnlyConnection(Object tenantId, boolean useMaster) throws SQLException {
if (readOnly == null || useMaster) {
return schemaDataSource.getConnectionForTenant(tenantId);
} else {
return readOnly.getConnectionForTenant(tenantId);
}
}
@Override
@@ -106,7 +110,6 @@ final class MultiTenantDbSchemaSupplier implements DataSourceSupplier {
*/
@Override
public Connection getConnection() throws SQLException {
Connection connection = dataSource.getConnection();
connection.setSchema(tenantSchema());
return connection;
@@ -43,8 +43,8 @@ final class MultiTenantDbSupplier implements DataSourceSupplier {
}
@Override
public Connection readOnlyConnection(Object tenantId) throws SQLException {
throw new SQLException("Not currently supported");
public Connection readOnlyConnection(Object tenantId, boolean useMaster) throws SQLException {
return dataSourceProvider.dataSource(tenantId).getConnection();
}
@Override
@@ -220,7 +220,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
} else {
// create an implicit transaction to execute this query
// potentially using read-only DataSource with autoCommit
transaction = server.createReadOnlyTransaction(query.tenantId());
transaction = server.createReadOnlyTransaction(query.tenantId(), query.isUseMaster());
}
createdTransaction = true;
}
@@ -101,7 +101,7 @@ public interface Persister {
/**
* Execute or queue the update.
*/
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue);
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue, int queuePosition);
/**
* Queue the SqlUpdate for execution with position 0, 1 or 2 defining
@@ -41,8 +41,12 @@ final class SimpleDataSourceProvider implements DataSourceSupplier {
}
@Override
public Connection readOnlyConnection(Object tenantId) throws SQLException {
return readOnlyDataSource.getConnection();
public Connection readOnlyConnection(Object tenantId, boolean useMaster) throws SQLException {
if (readOnlyDataSource == null || useMaster) {
return dataSource.getConnection();
} else {
return readOnlyDataSource.getConnection();
}
}
@Override
@@ -27,17 +27,24 @@ public final class IndexDefinition {
}
/**
* Create a unique constraint given the column names.
* Create from JPA Index.
*/
public IndexDefinition(String name, String[] columns) {
public IndexDefinition(String name, String[] columns, boolean unique) {
this.columns = columns;
this.unique = true;
this.unique = unique;
this.name = name;
this.platforms = null;
this.concurrent = false;
this.definition = null;
}
/**
* Create a unique constraint given the column names.
*/
public IndexDefinition(String name, String[] columns) {
this(name, columns, true);
}
/**
* Return true if this can be used as a unique constraint.
*/
@@ -129,6 +129,10 @@ final class AnnotationClass extends AnnotationParser {
for (UniqueConstraint c : uniqueConstraints) {
descriptor.addIndex(new IndexDefinition(c.name(), convertColumnNames(c.columnNames())));
}
for (javax.persistence.Index index : table.indexes()) {
final String[] cols = index.columnList().split(",");
descriptor.addIndex(new IndexDefinition(index.name(), convertColumnNames(cols), index.unique()));
}
}
StorageEngine storage = typeGet(cls, StorageEngine.class);
if (storage != null) {
@@ -471,6 +471,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new InPairsExpression(pairs, false);
}
@Override
public Expression inTuples(InTuples pairs) {
return new InTuplesExpression(pairs, false);
}
/**
* In - property has a value in the array of values.
*/
@@ -902,6 +902,10 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.inPairs(pairs));
}
@Override
public ExpressionList<T> inTuples(InTuples pairs) {
return add(expr.inTuples(pairs));
}
@Override
public ExpressionList<T> exists(String sqlSubQuery, Object... bindValues) {
@@ -0,0 +1,116 @@
package io.ebeaninternal.server.expression;
import io.ebean.InTuples;
import io.ebean.service.SpiInTuples;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import java.util.List;
import static java.util.Objects.requireNonNull;
final class InTuplesExpression extends AbstractExpression {
private final boolean not;
private final String[] properties;
private final List<Object[]> entries;
InTuplesExpression(InTuples pairs, boolean not) {
super("");
SpiInTuples inTuples = (SpiInTuples)pairs;
this.properties = inTuples.properties();
// the entries might be modified on cache hit.
this.entries = inTuples.entries();
this.not = not;
}
@Override
public boolean naturalKey(NaturalKeyQueryData<?> data) {
return false;
}
@Override
public void writeDocQuery(DocQueryContext context) {
throw new RuntimeException("Not supported with document query");
}
@Override
public void addBindValues(SpiExpressionRequest request) {
for (Object[] entry : entries) {
for (Object value : entry) {
requireNonNull(value);
request.addBindValue(value);
}
}
}
@Override
public void addSql(SpiExpressionRequest request) {
if (entries.isEmpty()) {
request.append(not ? SQL_TRUE : SQL_FALSE);
return;
}
request.append("(");
for (int i = 0; i < properties.length; i++) {
if (i > 0) {
request.append(",");
}
request.property(properties[i]);
}
request.append(") in (");
final String eb = entryBinding();
for (int i = 0; i < entries.size(); i++) {
if (i > 0) {
request.append(",");
}
request.append(eb);
}
request.append(")");
}
private String entryBinding() {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (int i = 0; i < properties.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append('?');
}
return sb.append(')').toString();
}
/**
* Based on the number of values in the in clause.
*/
@Override
public void queryPlanHash(StringBuilder builder) {
if (not) {
builder.append("Not");
}
builder.append("InTuple[");
for (String property : properties) {
builder.append(property).append("-");
}
builder.append(entries.size()).append("]");
}
@Override
public void queryBindKey(BindValuesKey key) {
key.add(entries.size());
for (Object[] entry : entries) {
for (Object value : entry) {
key.add(value);
}
}
}
@Override
public boolean isSameByBind(SpiExpression other) {
InTuplesExpression that = (InTuplesExpression) other;
return this.entries.size() == that.entries.size() && entries.equals(that.entries);
}
}
@@ -710,6 +710,11 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.inPairs(pairs);
}
@Override
public ExpressionList<T> inTuples(InTuples pairs) {
return exprList.inTuples(pairs);
}
@Override
public ExpressionList<T> in(String propertyName, Collection<?> values) {
return exprList.in(propertyName, values);
@@ -81,7 +81,10 @@ public final class BatchControl {
*/
private int bufferMax;
private final Queue[] queues = new Queue[3];
private final Queue[] queues = new Queue[2];
static final int DELETE_QUEUE = 0;
static final int INSERT_QUEUE = 1;
/**
* Create for a given transaction, PersistExecute, default size and getGeneratedKeys.
@@ -265,10 +268,9 @@ public final class BatchControl {
}
private void flushBuffer(boolean reset) throws BatchedSqlException {
flushQueue(queues[0]);
flushInternal(reset);
flushQueue(queues[1]);
flushQueue(queues[2]);
flushQueue(queues[DELETE_QUEUE]);
flushQueue(queues[INSERT_QUEUE]);
}
private void flushQueue(Queue queue) throws BatchedSqlException {
@@ -106,9 +106,9 @@ public final class DefaultPersister implements Persister {
}
@Override
public void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue) {
public void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue, int queuePosition) {
if (queue) {
addToFlushQueue(update, t, 2);
addToFlushQueue(update, t, queuePosition);
} else {
executeSqlUpdate(update, t);
}
@@ -907,7 +907,7 @@ public final class DefaultPersister implements Persister {
void deleteManyIntersection(EntityBean bean, BeanPropertyAssocMany<?> many, SpiTransaction t, boolean publish, boolean queue) {
SpiSqlUpdate sqlDelete = deleteAllIntersection(bean, many, publish);
if (queue) {
addToFlushQueue(sqlDelete, t, 1);
addToFlushQueue(sqlDelete, t, BatchControl.DELETE_QUEUE);
} else {
executeSqlUpdate(sqlDelete, t);
}
@@ -48,7 +48,7 @@ abstract class SaveManyBase implements SaveMany {
final void preElementCollectionUpdate() {
if (!insertedParent) {
request.preElementCollectionUpdate();
persister.addToFlushQueue(many.deleteByParentId(request.beanId(), null), transaction, 1);
persister.addToFlushQueue(many.deleteByParentId(request.beanId(), null), transaction, BatchControl.DELETE_QUEUE);
}
}
@@ -294,7 +294,7 @@ final class SaveManyBeans extends SaveManyBase {
// build a intersection row for 'delete'
IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherDelete, publish);
SpiSqlUpdate sqlDelete = intRow.createDelete(server, DeleteMode.HARD);
persister.executeOrQueue(sqlDelete, transaction, queue);
persister.executeOrQueue(sqlDelete, transaction, queue, BatchControl.DELETE_QUEUE);
}
}
if (additions != null && !additions.isEmpty()) {
@@ -314,7 +314,7 @@ final class SaveManyBeans extends SaveManyBase {
// build a intersection row for 'insert'
IntersectionRow intRow = many.buildManyToManyMapBean(parentBean, otherBean, publish);
SpiSqlUpdate sqlInsert = intRow.createInsert(server);
persister.executeOrQueue(sqlInsert, transaction, queue);
persister.executeOrQueue(sqlInsert, transaction, queue, BatchControl.INSERT_QUEUE);
}
}
}
@@ -48,7 +48,7 @@ final class SaveManyElementCollection extends SaveManyBase {
final SpiSqlUpdate sqlInsert = proto.copy();
sqlInsert.setParameter(parentId);
many.bindElementValue(sqlInsert, value);
persister.addToFlushQueue(sqlInsert, transaction, 2);
persister.addToFlushQueue(sqlInsert, transaction, BatchControl.INSERT_QUEUE);
}
resetModifyState();
postElementCollectionUpdate();
@@ -51,7 +51,7 @@ final class SaveManyElementCollectionMap extends SaveManyBase {
sqlInsert.setParameter(parentId);
sqlInsert.setParameter(entry.getKey());
many.bindElementValue(sqlInsert, entry.getValue());
persister.addToFlushQueue(sqlInsert, transaction, 2);
persister.addToFlushQueue(sqlInsert, transaction, BatchControl.INSERT_QUEUE);
}
resetModifyState();
postElementCollectionUpdate();
@@ -385,7 +385,10 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
this.lazyLoadParentId = lazyLoadParentId;
}
// add the loadedBean to the appropriate collection of lazyLoadParentBean
lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, bean, true);
if (lazyLoadParentBean != null) {
// Note: parentBean can be null, when GC ran between construction of query and building the retrieved bean
lazyLoadManyProperty.addBeanToCollectionWithCreate(lazyLoadParentBean, bean, true);
}
}
}
@@ -234,6 +234,11 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> usingMaster() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public <A> List<A> findIds() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
@@ -18,6 +18,7 @@ public class AbstractQuery implements SpiCancelableQuery {
private final ReentrantLock lock = new ReentrantLock();
private boolean cancelled;
private CancelableQuery cancelableQuery;
protected boolean useMaster;
@Override
public final void cancel() {
@@ -48,6 +48,7 @@ public final class DefaultDtoQuery<T> extends AbstractQuery implements SpiDtoQue
this.server = server;
this.descriptor = descriptor;
this.ormQuery = ormQuery;
this.useMaster = ormQuery.isUseMaster();
this.label = ormQuery.label();
this.profileLocation = ormQuery.profileLocation();
}
@@ -88,6 +89,17 @@ public final class DefaultDtoQuery<T> extends AbstractQuery implements SpiDtoQue
return this;
}
@Override
public DtoQuery<T> usingMaster() {
this.useMaster = true;
return this;
}
@Override
public boolean isUseMaster() {
return useMaster;
}
@Override
public void findEach(Consumer<T> consumer) {
server.findDtoEach(this, consumer);
@@ -715,6 +715,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
// forUpdate is NOT copied - see #2762
DefaultOrmQuery<T> copy = new DefaultOrmQuery<>(beanDescriptor, server, expressionFactory);
copy.transaction = transaction;
copy.useMaster = useMaster;
copy.m2mIncludeJoin = m2mIncludeJoin;
copy.profilingListener = profilingListener;
copy.profileLocation = profileLocation;
@@ -1396,6 +1397,17 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
return this;
}
@Override
public Query<T> usingMaster() {
this.useMaster = true;
return this;
}
@Override
public boolean isUseMaster() {
return useMaster;
}
@Override
public final int delete() {
return server.delete(this, transaction);
@@ -49,6 +49,17 @@ public final class DefaultRelationalQuery extends AbstractQuery implements SpiSq
return this;
}
@Override
public SqlQuery usingMaster() {
this.useMaster = true;
return this;
}
@Override
public boolean isUseMaster() {
return useMaster;
}
private void transaction(Transaction transaction) {
this.transaction = transaction;
}
@@ -43,10 +43,11 @@ public interface DataSourceSupplier {
/**
* Return a connection from the read only DataSource taking into account a tenantId for multi-tenant lazy loading.
*
* @param tenantId Most often null but well supplied indicates a multi-tenant lazy loading query
* @param tenantId Most often null but well supplied indicates a multi-tenant lazy loading query
* @param useMaster When true use the master data source
* @return the connection to use
*/
Connection readOnlyConnection(Object tenantId) throws SQLException;
Connection readOnlyConnection(Object tenantId, boolean useMaster) throws SQLException;
/**
* Shutdown the datasource de-registering the JDBC driver if requested.
@@ -24,7 +24,7 @@ public final class DocStoreTransactionManager extends TransactionManager {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
return new DocStoreOnlyTransaction(false, this);
}
@@ -32,6 +32,10 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
private static final String illegalStateMessage = "Transaction is Inactive";
private static final String notExpectedMessage = "Not expected on read only transaction";
/**
* Set false when using autoCommit (as a performance optimisation for the read-only case).
*/
private final boolean useCommit;
private final TransactionManager manager;
private final SpiTxnLogger logger;
private final boolean logSql;
@@ -60,7 +64,7 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
/**
* Create without a tenantId.
*/
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection) {
ImplicitReadOnlyTransaction(boolean useCommit, TransactionManager manager, Connection connection) {
this.manager = manager;
this.logger = manager.loggerReadOnly();
this.logSql = logger.isLogSql();
@@ -69,13 +73,18 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
this.connection = connection;
this.persistenceContext = new DefaultPersistenceContext();
this.startNanos = System.nanoTime();
try {
this.useCommit = useCommit && !connection.getAutoCommit();
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
/**
* Create with a tenantId.
*/
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection, Object tenantId) {
this(manager, connection);
this(true, manager, connection);
this.tenantId = tenantId;
}
@@ -505,16 +514,22 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
// do nothing, expect AutoCommit
}
/**
* Commit the transaction.
*/
@Override
public void commit() {
if (!active) {
throw new IllegalStateException(illegalStateMessage);
}
// expect AutoCommit so just deactivate / put back into pool
deactivate();
try {
if (useCommit) {
try {
connection.commit();
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
} finally {
deactivate();
}
}
/**
@@ -556,8 +571,17 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
if (!active) {
throw new IllegalStateException(illegalStateMessage);
}
// expect AutoCommit so it really has already committed
deactivate();
try {
if (useCommit) {
try {
connection.rollback();
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
} finally {
deactivate();
}
}
/**
@@ -565,7 +589,7 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
*/
@Override
public void end() throws PersistenceException {
if (isActive()) {
if (active) {
rollback();
}
}
@@ -52,7 +52,7 @@ public final class JtaTransactionManager implements ExternalTransactionManager {
return transactionManager.dataSource();
}
private TransactionSynchronizationRegistry getSyncRegistry() {
private TransactionSynchronizationRegistry registry() {
try {
InitialContext ctx = new InitialContext();
return (TransactionSynchronizationRegistry) ctx.lookup("java:comp/TransactionSynchronizationRegistry");
@@ -61,7 +61,7 @@ public final class JtaTransactionManager implements ExternalTransactionManager {
}
}
private UserTransaction getUserTransaction() {
private UserTransaction userTransaction() {
try {
InitialContext ctx = new InitialContext();
return (UserTransaction) ctx.lookup("java:comp/UserTransaction");
@@ -78,11 +78,17 @@ public final class JtaTransactionManager implements ExternalTransactionManager {
*/
@Override
public Object getCurrentTransaction() {
TransactionSynchronizationRegistry syncRegistry = getSyncRegistry();
SpiTransaction t = (SpiTransaction) syncRegistry.getResource(EBEAN_TXN_RESOURCE);
if (t != null) {
// we have already seen this transaction
return t;
TransactionSynchronizationRegistry syncRegistry;
try {
syncRegistry = registry();
SpiTransaction t = (SpiTransaction) syncRegistry.getResource(EBEAN_TXN_RESOURCE);
if (t != null) {
// we have already seen this transaction
return t;
}
} catch (Exception e) {
// deem that there is no current transaction
return null;
}
// check current Ebean transaction
@@ -93,7 +99,7 @@ public final class JtaTransactionManager implements ExternalTransactionManager {
return currentEbeanTransaction;
}
UserTransaction ut = getUserTransaction();
UserTransaction ut = userTransaction();
if (ut == null) {
// no current JTA transaction
if (log.isLoggable(DEBUG)) {
@@ -21,9 +21,10 @@ abstract class TransactionFactory {
/**
* Return a new query only transaction.
*
* @param tenantId The tenantId for lazy loading queries.
* @param tenantId The tenantId for lazy loading queries.
* @param useMaster Explicitly use the master data source rather than read only data source
*/
abstract SpiTransaction createReadOnlyTransaction(Object tenantId);
abstract SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster);
/**
* Return a new transaction.
@@ -13,7 +13,7 @@ import java.sql.SQLException;
*/
class TransactionFactoryBasic extends TransactionFactory {
private final DataSource dataSource;
protected final DataSource dataSource;
TransactionFactoryBasic(TransactionManager manager, DataSourceSupplier dataSourceSupplier) {
super(manager);
@@ -21,12 +21,11 @@ class TransactionFactoryBasic extends TransactionFactory {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
Connection connection = null;
try {
connection = dataSource.getConnection();
return create(false, connection);
return new ImplicitReadOnlyTransaction(true, manager, connection);
} catch (PersistenceException ex) {
JdbcClose.close(connection);
throw ex;
@@ -26,11 +26,11 @@ final class TransactionFactoryBasicWithRead extends TransactionFactoryBasic {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
Connection connection = null;
try {
connection = readOnlyDataSource.getConnection();
return new ImplicitReadOnlyTransaction(manager, connection);
connection = useMaster ? dataSource.getConnection() : readOnlyDataSource.getConnection();
return new ImplicitReadOnlyTransaction(useMaster, manager, connection);
} catch (PersistenceException ex) {
JdbcClose.close(connection);
throw ex;
@@ -11,7 +11,6 @@ final class TransactionFactoryBuilder {
* Build and return based on multi-tenancy and read only DataSource.
*/
static TransactionFactory build(TransactionManager manager, DataSourceSupplier dataSourceSupplier, CurrentTenantProvider tenantProvider) {
boolean hasReadOnlyDataSource = dataSourceSupplier.readOnlyDataSource() != null;
if (tenantProvider == null) {
if (hasReadOnlyDataSource) {
@@ -24,7 +24,7 @@ class TransactionFactoryTenant extends TransactionFactory {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
return create(false, tenantId);
}
@@ -23,14 +23,14 @@ final class TransactionFactoryTenantWithRead extends TransactionFactoryTenant {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
Connection connection = null;
try {
if (tenantId == null) {
// obtain the tenantId if the DataSource requires it
tenantId = dataSourceSupplier.currentTenantId();
}
connection = dataSourceSupplier.readOnlyConnection(tenantId);
connection = dataSourceSupplier.readOnlyConnection(tenantId, useMaster);
return new ImplicitReadOnlyTransaction(manager, connection, tenantId);
} catch (PersistenceException ex) {
JdbcClose.close(connection);
@@ -274,7 +274,7 @@ public class TransactionManager implements SpiTransactionManager {
private SpiTransaction createTransaction(TxScope txScope) {
if (txScope.isReadonly()) {
return createReadOnlyTransaction(null);
return createReadOnlyTransaction(null, false);
} else {
return createTransaction(true, txScope.getIsolationLevel());
}
@@ -290,8 +290,8 @@ public class TransactionManager implements SpiTransactionManager {
/**
* Create a new Transaction for query only purposes (can use read only datasource).
*/
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
return transactionFactory.createReadOnlyTransaction(tenantId);
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
return transactionFactory.createReadOnlyTransaction(tenantId, useMaster);
}
/**
@@ -0,0 +1,62 @@
package io.ebeaninternal.server.transaction;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.SpiTxnLogger;
import org.junit.jupiter.api.Test;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class TransactionFactoryBasicWithReadTest {
@Test
void createReadOnlyTransaction_expect_usesMasterDataSource() throws SQLException {
var connection = mock(Connection.class);
var tm = mockTransactionManager();
var dataSource = mockDataSource(connection);
var dss = mock(DataSourceSupplier.class);
when(dss.dataSource()).thenReturn(dataSource);
var txnFactory = new TransactionFactoryBasicWithRead(tm, dss);
// act
SpiTransaction txn = txnFactory.createReadOnlyTransaction(null, true);
assertThat(txn).isNotNull();
verify(dataSource).getConnection(); // used the 'master' dataSource
}
@Test
void createReadOnlyTransaction_expect_usesReadOnlyDataSource() throws SQLException {
var tm = mockTransactionManager();
var connection = mock(Connection.class);
var readOnlyDataSource = mockDataSource(connection);
var dss = mock(DataSourceSupplier.class);
when(dss.readOnlyDataSource()).thenReturn(readOnlyDataSource);
var txnFactory = new TransactionFactoryBasicWithRead(tm, dss);
// act
SpiTransaction txn = txnFactory.createReadOnlyTransaction(null, false);
assertThat(txn).isNotNull();
verify(readOnlyDataSource).getConnection(); // used the 'read only' dataSource
}
private static DataSource mockDataSource(Connection dsConnection) throws SQLException {
var readOnlyDataSource = mock(DataSource.class);
when(readOnlyDataSource.getConnection()).thenReturn(dsConnection);
return readOnlyDataSource;
}
private static TransactionManager mockTransactionManager() {
var tm = mock(TransactionManager.class);
when(tm.loggerReadOnly()).thenReturn(mock(SpiTxnLogger.class));
return tm;
}
}
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean ddl generation</name>
@@ -28,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
@@ -72,7 +72,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
@@ -15,6 +15,7 @@ class ClickHouseDbArray {
mapping.put("integer[]", "Array(UInt32)");
mapping.put("bigint[]", "Array(UInt64)");
mapping.put("float[]", "Array(Float32)");
mapping.put("float4[]", "Array(Float32)");
mapping.put("decimal[]", "Array(Decimal)");
}
@@ -279,7 +279,13 @@ public class PlatformDdl {
if (type == null) {
return null;
}
type = extract(type);
String[] tmp = type.split(";", -1); // do not discard trailing empty strings
int index = extract(tmp);
if (index < tmp.length - 1) {
// this is a platform specific definition. So do not apply further conversions
return tmp[index];
}
type = tmp[index];
if (type.contains("[]")) {
return convertArrayType(type);
}
@@ -294,18 +300,22 @@ public class PlatformDdl {
return null;
}
String[] tmp = type.split(";", -1); // do not discard trailing empty strings
if (tmp.length % 2 == 0) {
throw new IllegalArgumentException("You need an odd number of arguments in '" + type + "'. See Issue #2559 for details");
return tmp[extract(tmp)]; // else
}
protected int extract(String[] types) {
if (types.length % 2 == 0) {
throw new IllegalArgumentException("You need an odd number of arguments in '" + String.join(";", types) + "'. See Issue #2559 for details");
}
for (int i = 0; i < tmp.length - 2; i += 2) {
String[] platforms = tmp[i].split(",");
for (int i = 0; i < types.length - 2; i += 2) {
String[] platforms = types[i].split(",");
for (String plat : platforms) {
if (platform.isPlatform(Platform.valueOf(plat.toUpperCase(Locale.ENGLISH)))) {
return tmp[i + 1];
return i + 1;
}
}
}
return tmp[tmp.length - 1]; // else
return types.length - 1; // else
}
/**
@@ -10,17 +10,23 @@ import static org.assertj.core.api.Assertions.assertThat;
public class MySqlPlatformTest {
MySqlPlatform mySqlPlatform = new MySqlPlatform();
private static int X = 0xFFFFFF;
@Test
public void testTypeConversion() {
PlatformDdl ddl = PlatformDdlBuilder.create(mySqlPlatform);
assertThat(ddl.convert("clob")).isEqualTo("longtext");
assertThat(ddl.convert("clob(65535)")).isEqualTo("text");
assertThat(ddl.convert("clob(65536)")).isEqualTo("mediumtext");
assertThat(ddl.convert("clob(16777215)")).isEqualTo("mediumtext");
assertThat(ddl.convert("clob(16777216)")).isEqualTo("longtext");
assertThat(ddl.convert("json")).isEqualTo("json");
assertThat(ddl.convert("jsonb")).isEqualTo("json");
assertThat(ddl.convert("varchar(20)")).isEqualTo("varchar(20)");
assertThat(ddl.convert("boolean")).isEqualTo("tinyint(1)");
assertThat(ddl.convert("bit")).isEqualTo("tinyint(1)");
assertThat(ddl.convert("decimal")).isEqualTo("decimal(16,3)");
}
}
@@ -19,7 +19,7 @@ public class SqlserverPlatformTest {
PlatformDdl ddl = PlatformDdlBuilder.create(platform);
assertThat(ddl.convert("clob")).isEqualTo("nvarchar(max)");
assertThat(ddl.convert("blob")).isEqualTo("image");
assertThat(ddl.convert("blob")).isEqualTo("varbinary(max)");
assertThat(ddl.convert("json")).isEqualTo("nvarchar(max)");
assertThat(ddl.convert("jsonb")).isEqualTo("nvarchar(max)");
@@ -32,6 +32,10 @@ public class SqlserverPlatformTest {
assertThat(ddl.convert("bit")).isEqualTo("bit");
assertThat(ddl.convert("tinyint")).isEqualTo("smallint");
assertThat(ddl.convert("binary(16)")).isEqualTo("binary(16)");
assertThat(ddl.convert("varchar")).isEqualTo("nvarchar(255)");
assertThat(ddl.convert("varchar(4000)")).isEqualTo("nvarchar(4000)");
assertThat(ddl.convert("varchar(4001)")).isEqualTo("nvarchar(max)");
}
@Test
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,7 +14,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean postgis</name>
@@ -22,14 +22,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
@@ -65,7 +65,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean querybean</name>
@@ -17,7 +17,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
@@ -63,21 +63,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
@@ -12,6 +12,7 @@ import io.ebean.text.PathProperties;
import io.ebeaninternal.api.SpiQueryFetch;
import io.ebeaninternal.server.util.ArrayStack;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.*;
@@ -1219,6 +1220,14 @@ public abstract class TQRootBean<T, R> {
return root;
}
/**
* In expression using multiple columns.
*/
public R inTuples(InTuples inTuples) {
peekExprList().inTuples(inTuples);
return root;
}
/**
* Marker that can be used to indicate that the order by clause is defined after this.
* <p>
@@ -1552,6 +1561,21 @@ public abstract class TQRootBean<T, R> {
return root;
}
/**
* Ensure that the master DataSource is used if there is a read only data source
* being used (that is using a read replica database potentially with replication lag).
* <p>
* When the database is configured with a read-only DataSource via
* say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then
* by default when a query is run without an active transaction, it uses the read-only data
* source. We we use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
public R usingMaster() {
query.usingMaster();
return root;
}
/**
* Execute the query returning true if a row is found.
* <p>
@@ -1,12 +1,35 @@
package org.querytest;
import io.ebean.InTuples;
import org.example.domain.query.QContact;
import org.junit.jupiter.api.Test;
import java.time.ZonedDateTime;
import static org.assertj.core.api.Assertions.assertThat;
public class QContactTest {
@Test
void findInTuples() {
var c = QContact.alias();
InTuples inTuples = InTuples.of(c.firstName, c.lastName)
.add("Rob", "B")
.add("Bob", "C")
.add("Mob", "D");
var query = new QContact()
.firstName.isNotNull()
.email.isNotNull()
.inTuples(inTuples)
.query();
query.findList();
assertThat(query.getGeneratedSql()).contains("(t0.first_name,t0.last_name) in ((?,?),(?,?),(?,?))");
}
@Test
public void test_oneToManyMap() {
findThem();
@@ -276,8 +276,15 @@ public class QCustomerTest {
}
@Test
public void usingTransaction() {
public void usingMaster() {
new QCustomer()
.registered.isNull()
.usingMaster()
.findList();
}
@Test
public void usingTransaction() {
try (Transaction transaction = DB.getDefault().createTransaction()) {
new QCustomer()
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<artifactId>ebean-redis</artifactId>
@@ -22,35 +22,35 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<artifactId>ebean-spring-txn</artifactId>
@@ -28,7 +28,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
@@ -77,7 +77,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.19.0</version>
<version>13.20.1</version>
</parent>
<name>ebean test</name>
@@ -28,20 +28,20 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
</dependency>
<dependency>
@@ -132,14 +132,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>13.19.0</version>
<version>13.20.1</version>
<scope>test</scope>
</dependency>
@@ -12,7 +12,7 @@ class ClickHouseSetup implements PlatformSetup {
config.setUsername("default");
config.setPassword("");
config.setUrl("jdbc:clickhouse://${host}:${port}/${databaseName}");
config.setDriver("ru.yandex.clickhouse.ClickHouseDriver");
config.setDriver("com.clickhouse.jdbc.ClickHouseDriver");
config.datasourceDefaults();
return dockerProperties(config);
@@ -117,6 +117,44 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
// assertSql(sql.get(0)).contains("select t0.id, t0.email, concat(t0.last_name, ISO_WEEK(?)) lastName from contact");
// }
@Test
public void asDto_usingMaster() {
ResetBasicData.reset();
LoggedSql.start();
DtoQuery<ContactDto> query = DB.find(Contact.class)
.select("id, email")
.where().isNotNull("email")
.asDto(ContactDto.class)
.usingMaster();
List<ContactDto> dtos = query.findList();
assertThat(dtos).isNotEmpty();
for (ContactDto dto : dtos) {
assertThat(dto.getEmail()).isNotNull();
}
}
@Test
public void asDto_usingMaster2() {
ResetBasicData.reset();
LoggedSql.start();
DtoQuery<ContactDto> query = DB.find(Contact.class)
.select("id, email")
.usingMaster()
.where().isNotNull("email")
.asDto(ContactDto.class);
List<ContactDto> dtos = query.findList();
assertThat(dtos).isNotEmpty();
for (ContactDto dto : dtos) {
assertThat(dto.getEmail()).isNotNull();
}
}
@Test
public void asDto_withExplicitId() {
@@ -31,6 +31,12 @@ public class DbConstraintNamingTest {
assertThat(naming.normaliseTable("foo_bar]")).isEqualTo("foo_bar");
}
@Test
public void testIndexNameWithSpaces() {
assertThat(naming.indexName("foo", new String[]{"name", "other desc"})).isEqualTo("ix_foo_name_other_desc");
assertThat(naming.indexName("foo", new String[]{"name desc"})).isEqualTo("ix_foo_name_desc");
}
@Test
public void testDefaultToLower() {
assertThat(naming.normaliseTable("SCH.FOO_BAR]")).isEqualTo("foo_bar");
@@ -62,7 +62,7 @@ class DbTypeMapTest {
assertThat(dbTypeMap.lookup("json", false).renderType(0, 0)).isEqualTo("nvarchar(max)");
assertThat(dbTypeMap.lookup("jsonb", false).renderType(0, 0)).isEqualTo("nvarchar(max)");
assertThat(dbTypeMap.lookup("jsonclob", false).renderType(0, 0)).isEqualTo("nvarchar(max)");
assertThat(dbTypeMap.lookup("jsonblob", false).renderType(0, 0)).isEqualTo("image");
assertThat(dbTypeMap.lookup("jsonblob", false).renderType(0, 0)).isEqualTo("varbinary(max)");
assertThat(dbTypeMap.lookup("jsonvarchar", false).renderType(200, 0)).isEqualTo("nvarchar(200)");
}
@@ -57,6 +57,10 @@ public class DbMigrationTest extends BaseTestCase {
"migtest_ckey_detail",
"migtest_ckey_parent",
"migtest_e_basic",
"migtest_e_test_binary",
"migtest_e_test_varchar",
"migtest_e_test_lob",
"migtest_e_test_json",
"migtest_e_enum",
"migtest_e_history",
"migtest_e_history2",
@@ -83,9 +87,14 @@ public class DbMigrationTest extends BaseTestCase {
"`migtest_QuOtEd`",
"migtest_QuOtEd",
"\"migtest_QuOtEd\"",
"drop_main_drop_ref_many",
"drop_ref_many",
"drop_ref_one",
"drop_main",
"table",
"\"table\"",
"`table`");
((DataSourcePool)server().dataSource()).offline();
((DataSourcePool)server().dataSource()).online();
@@ -215,7 +215,7 @@ public class TDSpiEbeanServer extends TDSpiServer implements SpiEbeanServer {
}
@Override
public SpiTransaction createReadOnlyTransaction(Object tenantId) {
public SpiTransaction createReadOnlyTransaction(Object tenantId, boolean useMaster) {
return null;
}
@@ -0,0 +1,10 @@
The migration dir contains 3 folders `v1_0`, `v1_1`, `v1_2`.
The general idea is, that we try to perform a migration (=`v1.0` -> `v1.1`) and also if it is revertable (= `v1.1` -> `v1.0`)
To achieve that, the directories `v1_0` and `v1_2` should be as equal as possible. Annomalies should be commented.
It is also important, if new models are added, that all table names (also for M2M) are added in the `DbMigrationTest`
cleanup routine.
@@ -1,4 +1,4 @@
package misc.migration.v1_0;
package misc.migration.v1_1;
import javax.persistence.*;
import java.util.List;
@@ -1,9 +1,8 @@
package misc.migration.v1_0;
package misc.migration.v1_1;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.util.List;
@@ -1,4 +1,4 @@
package misc.migration.v1_0;
package misc.migration.v1_1;
import javax.persistence.Entity;
import javax.persistence.Id;
@@ -0,0 +1,72 @@
package misc.migration.v1_1;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Size;
@Entity
@Table(name = "migtest_e_test_binary")
public class ETestBinary {
@Id
int id;
@Size(max = 16)
byte[] test_byte16;
@Size(max = 256)
byte[] test_byte256;
@Size(max = 512)
byte[] test_byte512;
@Size(max = 1024)
byte[] test_byte1k;
@Size(max = 2 * 1024)
byte[] test_byte2k;
@Size(max = 4 * 1024)
byte[] test_byte4k;
@Size(max = 8 * 1024)
byte[] test_byte8k;
@Size(max = 16 * 1024)
byte[] test_byte16k;
@Size(max = 32 * 1024)
byte[] test_byte32k;
@Size(max = 64 * 1024)
byte[] test_byte64k;
@Size(max = 128 * 1024)
byte[] test_byte128k;
@Size(max = 256 * 1024)
byte[] test_byte256k;
@Size(max = 512 * 1024)
byte[] test_byte512k;
@Size(max = 1024 * 1024)
byte[] test_byte1m;
@Size(max = 2 * 1024 * 1024)
byte[] test_byte2m;
@Size(max = 4 * 1024 * 1024)
byte[] test_byte4m;
@Size(max = 8 * 1024 * 1024)
byte[] test_byte8m;
@Size(max = 16 * 1024 * 1024)
byte[] test_byte16m;
@Size(max = 32 * 1024 * 1024)
byte[] test_byte32m;
}
@@ -0,0 +1,74 @@
package misc.migration.v1_1;
import io.ebean.annotation.DbJson;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.List;
@Entity
@Table(name = "migtest_e_test_json")
public class ETestJson {
@Id
int id;
@DbJson(length = 255)
List<String> json255;
@DbJson(length = 256)
List<String> json256;
@DbJson(length = 512)
List<String> json512;
@DbJson(length = 1024)
List<String> json1k;
@DbJson(length = 2 * 1024)
List<String> json2k;
@DbJson(length = 4 * 1024)
List<String> json4k;
@DbJson(length = 8 * 1024)
List<String> json8k;
@DbJson(length = 16 * 1024)
List<String> json16k;
@DbJson(length = 32 * 1024)
List<String> json32k;
@DbJson(length = 64 * 1024)
List<String> json64k;
@DbJson(length = 128 * 1024)
List<String> json128k;
@DbJson(length = 256 * 1024)
List<String> json256k;
@DbJson(length = 512 * 1024)
List<String> json512k;
@DbJson(length = 1024 * 1024)
List<String> json1m;
@DbJson(length = 2 * 1024 * 1024)
List<String> json2m;
@DbJson(length = 4 * 1024 * 1024)
List<String> json4m;
@DbJson(length = 8 * 1024 * 1024)
List<String> json8m;
@DbJson(length = 16 * 1024 * 1024)
List<String> json16m;
@DbJson(length = 32 * 1024 * 1024)
List<String> json32m;
}

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