Compare commits

...
Author SHA1 Message Date
Rob Bygrave 2c4b8492da ebean-test: Add PersistenceContextAccess.clear() for tests that need to clear the Persistence Context
To support tests that use a transaction that wraps test setup and running the test logic. The need for this case is to clear the persistence context after the test setup phase.
2024-03-16 14:56:24 +13:00
Rob BygraveandGitHub 02dafc9838 Merge pull request #3360 from ebean-orm/feature/refactor-DbExpressionRequest
Refactor extract DbExpressionRequest interface for db platform specific expressions
2024-03-08 00:53:56 +13:00
Rob Bygrave 0816810d3b Refactor extract DbExpressionRequest interface for db platform specific expressions
This provides a simpler DbExpressionRequest interface for db platform specific expression adapters (rather than the SpiExpressionRequest which has more features that we don't wish to expose to those expression adapters.
2024-03-07 22:45:21 +13:00
Rob Bygrave 1f6e2ee844 No effective change - update test only ClusterTest, increase wait to 200ms 2024-03-07 20:59:41 +13:00
Rob Bygrave dc00765a98 Merge branch 'fix-for-db2' 2024-03-07 20:45:57 +13:00
Rob Bygrave a48b2f4ebe #3354 Use 4000 for DB Lob detection with distinct query
- Use 4000 to match the DB2 logic for considering a column a lob (for distinct etc)
- Rename distinctNoLobs -> platformDistinctNoLobs
- Rename  isDbLob() -> isLobForPlatform()
- Rename unselectLobs() -> unselectLobsForPlatform()
2024-03-07 20:45:40 +13:00
Rob BygraveandGitHub 837563544f Merge pull request #3357 from ebean-orm/feature/avaje-config-dependency-ContainerConfig
Modify ContainerConfig to remove the avaje-config dependency
2024-03-07 20:23:36 +13:00
Rob Bygrave fd199f406b Modify ContainerConfig to remove the avaje-config dependency
This actually reverts a change that was made in commit:
https://github.com/ebean-orm/ebean/commit/803f1d86428ce3b653276d72207e5a0009b57d16
2024-03-05 21:18:27 +13:00
Roland Praml a48e96c7e9 FIX broken test for DB2 2024-03-04 10:02:05 +01:00
Rob Bygrave 7ba6e5f67d Add howto-deploy-to-central.md 2024-03-03 22:08:47 +13:00
Rob Bygrave 6ce42b1ca3 Version 14.0.1 2024-03-03 20:24:26 +13:00
Rob BygraveandGitHub 89617cefbe Merge pull request #3353 from ebean-orm/feature/bump-migration-14
dep: Bump ebean-migration to 14.0.0, no effective change
2024-03-03 20:19:40 +13:00
Rob Bygrave dd377621a4 dep: Bump ebean-migration to 14.0.0, no effective change
No effective change in that ebean-migration 14.0.0 has no change, it was released to align version numbers with 14.x
2024-03-03 20:19:17 +13:00
Rob Bygrave d844855462 Bump ebean-agent to 14.0.1 2024-03-03 18:32:14 +13:00
Rob BygraveandGitHub f68c25d5f3 Merge pull request #3352 from ebean-orm/feature/dep-common-components
dep: Update the common components to 14.0.0 ebean-joda-types, ebean-jackson-jsonnode
2024-03-03 18:29:15 +13:00
Rob Bygrave 61cfc7a979 dep: Update the common components to 14.0.0 ebean-joda-types, ebean-jackson-jsonnode 2024-03-03 17:01:36 +13:00
Rob BygraveandGitHub 3c98ee7f1b Merge pull request #3351 from ebean-orm/dep/bump-config
Bump test dependencies only - logback and ebean-test-containers
2024-03-03 16:54:56 +13:00
Rob Bygrave 0738316f15 Bump test dependencies only - logback and ebean-test-containers 2024-03-03 16:54:22 +13:00
Rob BygraveandGitHub ceb83befa3 Merge pull request #3350 from ebean-orm/dep/bump-config
dep: Bump avaje-config to 3.12 - supports AWS AppConfig & Dynamic Logback
2024-03-03 15:42:59 +13:00
Rob Bygrave a0b04cceda dep: Bump avaje-config to 3.12 - supports AWS AppConfig & Dynamic Logback
This version of avaje-config supports using AWS AppConfig as a configuration source (via avaje-aws-appconfig). It also supports dynamic changing Logback logging levels (via avaje-dynamic-logback).
2024-03-03 15:38:14 +13:00
Rob Bygrave 03edfb5454 git workflow trigger on push to master and pull_request 2024-03-03 15:33:39 +13:00
Rob BygraveandGitHub 62015451df Merge pull request #3349 from ebean-orm/feature/dep-ebean-datasource
dep: Bump ebean-datasource to 8.12
2024-03-03 15:32:36 +13:00
Rob Bygrave ed9113efe6 dep: Bump ebean-datasource to 8.12
This adds the option to register a JVM shutdown hook (which we generally will not use with ebean as the ebean io.ebean.Database shutdown wants to shutdown the DataSource after any background tasks have completed).

This also adds an experimental feature for use with AWS Lambda to check for old idle connections due to lambda suspension.
2024-03-03 15:24:44 +13:00
Rob Bygrave 334793667e Add deployment error message for ManyToMany with one of the related beans missing an @Id property 2024-03-03 14:14:23 +13:00
Rob BygraveandGitHub 351a1c4739 Merge pull request #3348 from ebean-orm/feature/3313-fetchGroup-with-config
#3313 Ability to configure fetch batch size when using FetchGroups and query beans
2024-03-03 13:53:52 +13:00
Rob Bygrave b2de333dcc #3313 Ability to configure fetch batch size when using FetchGroups and query beans
We can do this currently using string paths and properties, this adds support for doing this when using query beans and strong types
2024-03-03 13:47:49 +13:00
Rob Bygrave 6091f7b683 No effective change - tidy test TestJsonImplicitLoaded 2024-03-03 12:25:25 +13:00
Rob BygraveandGitHub 43fb84cbc0 Merge pull request #3345 from FOCONIS/jsonwrite-loaded-props
NEW: Allow control of jsonwrite-loadedprops
2024-03-03 12:23:17 +13:00
Rob BygraveandGitHub 52a4e9e19b Merge pull request #3347 from ebean-orm/feature/3337-querybean-imports
#3337 Fix querybean-generation to not import the entity bean type
2024-03-03 12:17:42 +13:00
Rob Bygrave a2cdea24ed #3337 Fix querybean-generation to not import the entity bean type
Use the full canonical name of the entity bean in the generated code.
2024-03-03 12:13:49 +13:00
Rob Bygrave cd780208e5 Javadoc format only for io.ebean.Database 2024-03-03 11:45:36 +13:00
Rob BygraveandGitHub 9ce0e923d7 Merge pull request #3346 from ebean-orm/feature/lengthCheck-postgres
#3341 String length validation with Postgres PGobject (JSON/JSONB)
2024-03-02 10:26:52 +13:00
Rob Bygrave 9e461e4708 #3341 String length validation with Postgres PGobject (JSON/JSONB) 2024-03-02 10:06:22 +13:00
Rob Bygrave 5fa09a6f90 #3341 String length validation with Postgres PGobject (JSON/JSONB) 2024-03-02 09:52:04 +13:00
Rob Bygrave 6babb2e6f5 Add ebean.lengthCheck for mariadb test run 2024-03-02 09:39:25 +13:00
Rob Bygrave 26abe27e42 Add ebean.lengthCheck for sqlserver17 test run 2024-03-02 09:38:33 +13:00
Rob Bygrave 9b18d46b78 Add ebean.lengthCheck for mysql test run 2024-03-02 09:27:05 +13:00
Roland Praml eed487ad26 NEW: Allow control of jsonwrite-loadedprops 2024-02-29 13:26:22 +01:00
Rob BygraveandGitHub c10865dfaf Merge pull request #3344 from ebean-orm/feature/bump-avaje-config-311
Bump avaje-config to 3.11
2024-02-27 21:22:38 +13:00
Rob Bygrave 2352df309c Bump avaje-config to 3.11 2024-02-27 21:21:58 +13:00
Rob Bygrave feb2bd1e15 String length validation tidy up 2024-02-27 21:04:19 +13:00
Rob Bygrave 11c9bdfc6f Squashed commit of the following:
commit 20152e16891582f8ff466a93ec2cf01871824d6d
Author: Rob Bygrave <robin.bygrave@gmail.com>
Date:   Tue Feb 27 20:59:51 2024 +1300

    #3341 Update DatabaseConfig for fluid style + javadoc

commit 1c495a7384
Author: Roland Praml <roland.praml@foconis.de>
Date:   Mon Feb 26 10:25:42 2024 +0100

    Fix: Compile errors

commit 574876b758
Merge: df7b04877 77634fc3e
Author: Roland Praml <roland.praml@foconis.de>
Date:   Fri Feb 23 16:16:39 2024 +0100

    Merge branch 'master-rob' into FOCONIS-string-length-validation

commit df7b04877d
Author: Rob Bygrave <robin.bygrave@gmail.com>
Date:   Mon Jun 26 21:16:32 2023 +1200

    #3121 BindMaxLength validation

    At deploy time derive a BindMaxLength property to use
    per BeanProperty

commit 4683877e0b
Merge: 9a4b9d4be d0020ab8c
Author: Rob Bygrave <robin.bygrave@gmail.com>
Date:   Fri Jun 23 16:51:34 2023 +1200

    Merge branch 'string-length-validation' of github.com:FOCONIS/ebean into FOCONIS-string-length-validation

commit d0020ab8ce
Author: Roland Praml <roland.praml@foconis.de>
Date:   Tue Jun 20 14:47:20 2023 +0200

    Length validation less invasive

commit 9b3b8ad46a
Author: Roland Praml <roland.praml@foconis.de>
Date:   Tue Jun 20 13:03:04 2023 +0200

    extended DataBind, so that it could return the last bound object

commit ca3c02bc1c
Author: Roland Praml <roland.praml@foconis.de>
Date:   Mon Jun 19 09:52:52 2023 +0200

    Failing test for SqlServer
2024-02-27 21:04:06 +13:00
Rob BygraveandGitHub 4a6fc98395 Merge pull request #3339 from ebean-orm/fix/3338
Fix for #3338 Potential shutdown hang with SpringContextShutdownHook …
2024-02-26 21:58:39 +13:00
Rob Bygrave 7322030e90 Fix for #3338 Potential shutdown hang with SpringContextShutdownHook and ebean ShutdownHook 2024-02-26 08:32:44 +13:00
Rob BygraveandGitHub 77634fc3eb Merge pull request #3336 from ebean-orm/feature/bump-postgresql-4272
Bump postgresql dependency to 42.7.2
2024-02-21 16:11:24 +13:00
robin.bygrave ed8d4b5fc4 Bump postgresql dependency to 42.7.2 2024-02-21 16:05:13 +13:00
Rob BygraveandGitHub 187cc0903d Merge pull request #3329 from ebean-orm/feature/bump-test-dependencies
Bump test dependencies for assertj and bytebuddy
2024-02-19 22:04:56 +13:00
Rob Bygrave 1d7a14c8f4 Bump test dependencies for assertj and bytebuddy 2024-02-19 21:51:38 +13:00
Rob Bygrave c58b7d3da3 Test ebean.properties config values 2024-02-15 22:51:26 +13:00
Rob Bygrave 6b2cdc14aa Version 14.0.0 2024-02-15 22:48:38 +13:00
Rob Bygrave 6d712a92a0 Bump ebean-agent to 14.0.0, no functional change 2024-02-15 22:21:09 +13:00
Rob BygraveandGitHub ea11b0c3b6 Merge pull request #3289 from FOCONIS/fix-nested-user-objects
Change semantic of userObjects in nested transaction
2024-02-15 22:10:45 +13:00
Rob BygraveandGitHub 08e8402d05 Merge pull request #3301 from ebean-orm/feature/3295-PC-clear-on-bulk-update
#3295 Clear PersistenceContext on execution of bulk updates or deletes
2024-02-15 22:09:16 +13:00
Rob BygraveandGitHub 006ad79379 Merge branch 'master' into feature/3295-PC-clear-on-bulk-update 2024-02-15 22:09:03 +13:00
Rob BygraveandGitHub 0e063549cd Merge pull request #3327 from ebean-orm/feature/remove-deprecated-commitTransaction
Remove deprecated database.commitTransaction(), migrate to transaction.commit()
2024-02-15 22:07:32 +13:00
Rob BygraveandGitHub 4f0d2cabfb Merge pull request #3325 from ebean-orm/feature/default-batchSize-100
Change default persistBatchSize from 20 to 100 (as a better default value)
2024-02-15 22:07:14 +13:00
Rob Bygrave 80b2d9f983 Remove deprecated database.commitTransaction(), migrate to transaction.commit()
Remove the deprecated methods:
- DB.commitTransaction()
- DB.rollbackTransaction()
- DB.endTransaction()
- database.commitTransaction()
- database.rollbackTransaction()
- database.endTransaction()

Migrate to using try-with-resources and transaction.commit(), transaction.rollback()
2024-02-15 21:56:57 +13:00
Rob Bygrave 287d0c467d #3326 Update the test example of Postgres DML with RETURNING clause to use explicit transactions and transaction.addModification() 2024-02-11 14:25:01 +13:00
Rob Bygrave a8835379e7 #3326 Add test as example of Postgres DML with RETURNING clause 2024-02-11 13:18:19 +13:00
Rob Bygrave e4df20b9dc Update test TestPersistCascade with default batch size 100 2024-02-10 13:28:04 +13:00
Rob Bygrave 261b13ef4b Version 13.26.1 2024-02-10 12:40:30 +13:00
Rob Bygrave 7639034d7c Bump ebean-agent to 13.26.1 (no functional change here) 2024-02-10 12:17:47 +13:00
Rob Bygrave e6e53e0d93 Change default persistBatchSize from 20 to 100 (as a better default value)
This is the batch size used with PreparedStatement executeBatch() with
batched inserts, updates and deletes. 20 is really on the low side and
bumping this default to 100 seems good and right.
2024-02-10 12:15:50 +13:00
Rob BygraveandGitHub c71971eb5c Merge pull request #3322 from ebean-orm/fix/3319
(fix) Future queries do not trigger flush on BatchedPstmtHolder for #3319
2024-02-10 12:12:47 +13:00
Rob BygraveandGitHub 357af58db4 Merge pull request #3324 from ebean-orm/feature/postgres_insertOnConflict
Add support for Postgres INSERT ON CONFLICT update | nothing
2024-02-10 12:08:44 +13:00
Rob BygraveandGitHub 0abdb1550a Merge pull request #3310 from Ichtil/one_to_many_list_clear_test
Previous entities are not deleted when replacing OneToMany collection with a new one
2024-02-10 12:03:16 +13:00
Rob Bygrave c32fc814f6 Add support for insert/insertAll with both options and explicit transaction 2024-02-10 12:00:32 +13:00
Rob Bygrave 8e035939dc Add support for Postgres INSERT ON CONFLICT update | nothing
Adds InsertOptions with ability to control the options used
for insert with Postgres around ON CONFLICT.
2024-02-10 01:07:44 +13:00
Rob Bygrave edf98ee250 Fix test OneToManyListMarkAsDirtyTest with some wait time 2024-02-09 15:46:34 +13:00
Rob Bygrave 5f0a75a130 No effective change, update test renaming internal variables 2024-02-09 15:26:28 +13:00
Rob Bygrave d2f16cbf6a #3310 Fix for orphanRemoval with updated parent and 'vanilla collection'
The issue fixed here is a timing one where the parent bean is being updated.
The updated parent bean has its internal state reset before the cascade down
to the SaveManyBeans where for this case it needs to identify is the 'vanilla'
collection has set (via setChildren(new ArrayList()).

This fix is a change that for updated beans the dirtyProperties is always obtained
and then use this for the isChangedProperty() check.
2024-02-09 15:24:47 +13:00
Rob Bygrave 04ba81d12f #3310 Update test only, reuse existing entity beans
- Reuse the existing OmBeanListParent and child
- Simplify the test setup code
- Rename test methods to maybe better reflect what I think is failing

Noting that markAsDirty doesn't specifically have anything to do with this bug but it's more on whether the parent bean is dirty or dirty (markAsDirty is just a way to make the parent bean dirty).
2024-02-09 08:30:42 +13:00
Rob Bygrave f63e7ed5af Add test only for orphan removal to TestOrphanRemovalOverwrite
Using clear() and add() when the same id value is used
2024-02-08 00:14:22 +13:00
Rob Bygrave 66940c0f14 Update tests only for BeanList BeanSet BeanMap
Use Product entity with equals/hashCode implementation added via ebean enhancement
2024-02-07 22:29:05 +13:00
Rob Bygrave 1f48b3a6b3 (fix) Future queries do not trigger flush on BatchedPstmtHolder for #3319
This change is that findFutureCount, findFutureList, findFutureIds do not trigger a flush on BatchedPstmtHolder.

This is to address the possible ConcurrentModificationException that could occur at BatchedPstmtHolder.closeStatements(BatchedPstmtHolder.java:153)
2024-02-03 08:20:58 +13:00
Rob BygraveandGitHub 7d368097e9 Merge pull request #3321 from ebean-orm/feature/tidy-OrmQueryRequest-readOnly
Tidy OrmQueryRequest isReadOnly, just use query.isReadOnly instead
2024-02-02 14:44:06 +13:00
Rob Bygrave c849e29316 Tidy OrmQueryRequest isReadOnly, just use query.isReadOnly instead 2024-02-02 14:43:31 +13:00
Rob BygraveandGitHub c78a3dd6c4 Merge pull request #3318 from FOCONIS/fix-db2-test
Exclude test for DB2
2024-01-31 22:57:49 +13:00
Noemi Praml a4455ddbf2 Exclude test for DB2 2024-01-31 10:17:16 +01:00
Rob BygraveandGitHub 9787af77e4 Merge pull request #3315 from FOCONIS/db2-select-for-update
ADD: DB2 supports select .. for update queries
2024-01-30 22:53:19 +13:00
Noemi Praml 3786fb09a5 ADD: DB2 supports select .. for update queries 2024-01-29 15:12:21 +01:00
Rob Bygrave f34e7b6003 Version 13.26.0 2024-01-20 22:01:30 +13:00
Rob BygraveandGitHub 089dd8e364 Merge pull request #3311 from ebean-orm/feature/findSingleAttributeOrEmpty
enh: Add findSingleAttributeOrEmpty() returning Optional
2024-01-19 15:23:27 +13:00
Rob Bygrave 44770382fb enh: Add findSingleAttributeOrEmpty() returning Optional 2024-01-19 10:41:54 +13:00
Rob Bygrave 6645e5b9ec Update the test comments in TestPersistenceContextQueryScope
To reflect the behaviour with #3295
2024-01-19 08:58:19 +13:00
Rob BygraveandGitHub 2a62d413cc Merge pull request #3308 from Incanus3/fix/querybean_or_with_exists
fix TQRootBean.exists() when used with .or()
2024-01-19 08:29:29 +13:00
Rob Bygrave 0046d983cb Adjust test such that table alias does not clash
The .raw("contact.customer_id = customer.id") clashes as "customer.id" is
a logical path from contact
2024-01-19 08:14:58 +13:00
Jan Klička 006e33cb3e One to many replacing collection failing test cases 2024-01-18 17:29:24 +01:00
Jan Klička e80e8e027a One to many replacing collection failing test cases 2024-01-18 16:49:04 +01:00
Jakub Kaláb 2e1b52d7d4 WIP: fix TQRootBean.exists() when used with .or() 2024-01-18 11:32:34 +01:00
Rob BygraveandGitHub 09d5163396 Merge pull request #3307 from ebean-orm/feature/bump-avaje-config-310
Bump avaje-config to version 3.10
2024-01-18 22:30:42 +13:00
Rob Bygrave a6a38b9a92 Bump avaje-config to version 3.10 2024-01-18 22:30:09 +13:00
Rob BygraveandGitHub bd4521b240 Merge pull request #3306 from ebean-orm/feature/querybean-deprecated
Add forRemoval=true for existing Deprecated methods in query beans
2024-01-18 22:28:23 +13:00
Rob Bygrave 8a7379879b Add forRemoval=true for existing Deprecated methods in query beans 2024-01-18 20:15:57 +13:00
Rob BygraveandGitHub b78dd6f79a Merge pull request #3304 from ebean-orm/feature/3296-querybean-copy
#3296 Add copy() method to query beans
2024-01-15 22:38:30 +13:00
Rob Bygrave 4ebb5fd112 #3296 Add copy() method to query beans 2024-01-15 22:30:17 +13:00
Rob Bygrave 487368976f Tidy SimpleQueryBeanWriter internals 2024-01-15 22:03:17 +13:00
Rob Bygrave c6defceec9 Add additional test for filterMany() with fetch() 2024-01-15 21:02:47 +13:00
Rob Bygrave 3576aafffc #3295 When isAutoPersistUpdates is used do NOT clear the PersistenceContext
Also make improvements to the test asserts in TestPersistenceContextQueryScope
2024-01-10 23:23:47 +13:00
Rob Bygrave 66234249c6 #3295 Clear PersistenceContext on execution of bulk updates or deletes
Using SqlUpdate or an ORM Update query clear the appropriate part of
the PersistenceContext. The effect is that ORM queries executed after
a bulk update will effectively load a fresh copy of the data from the
database and will not reuse an instance from the persistence context
if the bean in question had already been loaded.
2024-01-09 07:44:27 +13:00
Rob BygraveandGitHub 8ed3b765ec Merge pull request #3299 from ebean-orm/feature/3294-warnings
#3294 Suppress compiler warnings on generated query bean `filterMany()`
2023-12-22 08:33:31 +13:00
Rob BygraveandGitHub fcbcc53e27 Merge pull request #3298 from ebean-orm/feature/add-usingConnection-dtoQuery
Add usingConnection() to DtoQuery and SqlQuery
2023-12-22 08:25:48 +13:00
Rob Bygrave b377e1a618 #3294 Suppress compiler warnings on generated query bean filterMany() 2023-12-22 08:24:54 +13:00
Rob Bygrave a0f612ed71 Add usingConnection() to DtoQuery and SqlQuery
For the case where we have a java.sql.Connection and wish
to execute a DtoQuery or SqlQuery using that connection.
2023-12-21 21:24:02 +13:00
Roland Praml 6f64821584 Change semantic of userObjects in nested transaction 2023-12-08 14:50:30 +01:00
Rob Bygrave 47e97a77a8 Version 13.25.2 2023-12-07 20:18:40 +13:00
Rob Bygrave e8d3f50bb4 Bump ebean-agent to 13.25.2 2023-12-07 19:57:55 +13:00
Rob BygraveandGitHub 716cfaf938 Merge pull request #3286 from ebean-orm/feature/dbarray-instant-and-localdate
Add support for LocalDate and Instant with DbArray
2023-12-07 08:57:40 +13:00
robin.bygrave 7cd9e5ee03 Add support for LocalDate and Instant with DbArray 2023-12-06 14:54:30 +13:00
Rob Bygrave f9c9bea623 Follow up for #3248 - add betweenProperties on query bean 2023-12-06 00:40:57 +13:00
Rob Bygrave 720832122c Follow up for #3248 - add betweenProperties on query bean 2023-12-05 23:32:16 +13:00
Rob BygraveandGitHub 7b635acf56 Merge pull request #3284 from raphaelNguyen/master
Expose betweenProperties on query bean
2023-12-05 23:21:26 +13:00
Raphael Nguyen 6b7efe65d7 Expose betweenProperties on query bean 2023-12-05 15:08:16 +11:00
Rob BygraveandGitHub 03cd4000f0 Merge pull request #3282 from ebean-orm/dependabot/maven/ch.qos.logback-logback-classic-1.3.12
Bump ch.qos.logback:logback-classic from 1.2.11 to 1.3.12
2023-11-30 13:59:21 +13:00
Rob BygraveandGitHub dfd6434b59 Merge pull request #3281 from ebean-orm/dependabot/maven/tests/test-java16/ch.qos.logback-logback-classic-1.3.12
Bump ch.qos.logback:logback-classic from 1.2.11 to 1.3.12 in /tests/test-java16
2023-11-30 13:59:08 +13:00
Rob BygraveandGitHub 4e7480237b Merge pull request #3283 from ebean-orm/dependabot/maven/tests/test-kotlin/ch.qos.logback-logback-classic-1.3.12
Bump ch.qos.logback:logback-classic from 1.2.11 to 1.3.12 in /tests/test-kotlin
2023-11-30 13:58:52 +13:00
dependabot[bot]andGitHub 9bfcf07486 Bump ch.qos.logback:logback-classic in /tests/test-kotlin
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.2.11 to 1.3.12.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.2.11...v_1.3.12)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 22:15:53 +00:00
dependabot[bot]andGitHub b1f1344057 Bump ch.qos.logback:logback-classic from 1.2.11 to 1.3.12
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.2.11 to 1.3.12.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.2.11...v_1.3.12)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 22:13:26 +00:00
dependabot[bot]andGitHub 07201c1519 Bump ch.qos.logback:logback-classic in /tests/test-java16
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.2.11 to 1.3.12.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.2.11...v_1.3.12)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 22:13:04 +00:00
Rob BygraveandGitHub 455a9fdffc Merge pull request #3280 from ebean-orm/feature/typequery-imports-tqrootbean
QueryBean generation - use full class names for TQRootBean, TQAssocBean
2023-11-29 01:21:00 +13:00
Rob Bygrave 3f67425727 QueryBean generation - conditionally fully qualify the property types
If there is an entity bean short name clash like PString, PLong, etc then
fully qualify all the property types in the generated code, otherwise use
imports for the property types.
2023-11-29 00:46:56 +13:00
Rob Bygrave 3fd9ae06db QueryBean generation - use fully qualify use of DB 2023-11-29 00:22:45 +13:00
Rob Bygrave a8ce726e47 QueryBean generation - use full class names for TQRootBean, TQAssocBean
Rather than imports, such that these are not possible name clashes with
beans of the exact same name.
2023-11-28 21:51:07 +13:00
Rob BygraveandGitHub 29cd1a143d Merge pull request #3277 from ebean-orm/feature/bump-datasource2
Bump ebean-datasource to 8.11 with support for Driver by class
2023-11-23 23:34:23 +13:00
Rob Bygrave 6a19c6a0ce Bump ebean-datasource to 8.11 with support for Driver by class
- Can specify explicit driver by class or instance
- Can use a DataSource as connection source
2023-11-23 23:31:29 +13:00
Rob BygraveandGitHub eaec515cc1 Merge pull request #3276 from ebean-orm/feature/skipCheckForReadOnly
skipDataSourceCheck() when using readOnlyDatabase
2023-11-23 23:27:21 +13:00
Rob Bygrave 97709d3493 skipDataSourceCheck() when using readOnlyDatabase
As we don't want the warning logged in this case:
DataSource [{0}] has autoCommit defaulting to true!
2023-11-23 23:23:53 +13:00
Rob Bygrave 87ad874621 Read the readOnlyDatabase property via loadSettings() 2023-11-23 23:15:58 +13:00
Rob Bygrave dfe95f6fc0 Version 13.25.1 2023-11-22 17:50:09 +13:00
Rob Bygrave 05feccfeb6 Bump ebean-agent 2023-11-22 17:46:54 +13:00
Rob BygraveandGitHub 196cb09c66 Merge pull request #3274 from ebean-orm/feature/3269
#3268 - @DbComment on @ManyToOne fields are ignored (ddl-generator)
2023-11-21 23:54:26 +13:00
Rob Bygrave 589e198301 #3268 - @DbComment on @ManyToOne fields are ignored (ddl-generator) 2023-11-21 23:53:29 +13:00
Rob BygraveandGitHub b2b81677e8 Merge pull request #3273 from ebean-orm/feature/3269
#3269 - @JoinColumns set ForeignKey is none in ddl , but is not work
2023-11-21 23:42:06 +13:00
Rob Bygrave f032aad22e #3269 - @JoinColumns set ForeignKey is none in ddl , but is not work 2023-11-21 23:40:21 +13:00
Rob Bygrave c48e5428c5 #3265 Fix for Update Query logging to trim large values in bind log 2023-11-21 22:32:35 +13:00
Rob Bygrave cbca940611 Merge branch 'FOCONIS-findsingleattribute-beanfindcontroller' 2023-11-21 22:05:12 +13:00
Rob Bygrave 0da3772ae9 #3270 Fix for use with BeanFindController and attribute queries
The existence of a BeanFindController should not break the use of
single attribute queries + still allow auto-tuning.
2023-11-21 22:04:47 +13:00
Rob Bygrave 496ef36b11 Merge branch 'findsingleattribute-beanfindcontroller' of github.com:FOCONIS/ebean into FOCONIS-findsingleattribute-beanfindcontroller 2023-11-21 21:40:09 +13:00
Rob BygraveandGitHub a4a2aba56f Merge pull request #3272 from ebean-orm/feature/readOnlyDatabase
Refactor tidy internals of DbPrimary, DatabaseFactory, ContainerConfi…
2023-11-21 21:20:35 +13:00
Rob Bygrave 803f1d8642 Refactor tidy internals of DbPrimary, DatabaseFactory, ContainerConfig, ClusterManager 2023-11-20 22:18:53 +13:00
Rob BygraveandGitHub ff1c73e20d Merge pull request #3266 from ebean-orm/feature/readOnlyDatabase
Add read only database configuration/builder option
2023-11-17 14:10:51 +13:00
Noemi Praml 7afd37cd98 Failing Tests with CustomerFindController 2023-11-14 17:05:42 +01:00
Rob Bygrave 24bb364d40 Add read only database configuration/builder option
When using DatabaseBuilder.readOnlyDatabase(true) then
ebean will:
- Set the DataSourceBuilder to use autoCommit=true and readOnly=true
- Use the same DataSource instance for both dataSource and readOnlyDataSource

This is to simplify the setup/configuration for creating a Database that
will only have read-only use.

Note that readOnly=true is a JDBC hint and for example H2 database effectively
ignores that hint where as Postgres will enforce the read-only true nature.
2023-11-13 23:12:05 +13:00
Rob Bygrave 5d54a238bd Bump ebean-agent to 13.25.0 2023-11-06 20:09:35 +13:00
246 changed files with 3689 additions and 1093 deletions
+4 -1
View File
@@ -1,7 +1,10 @@
name: Build
on: [push, pull_request]
on:
pull_request:
push:
branches: master
jobs:
build:
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+7 -7
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -14,7 +14,7 @@
<properties>
<postgis.jdbc.version>2.5.1</postgis.jdbc.version>
<postgres.jdbc.version>42.6.0</postgres.jdbc.version>
<postgres.jdbc.version>42.7.2</postgres.jdbc.version>
</properties>
<dependencies>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+8 -8
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,31 +17,31 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-joda-time</artifactId>
<version>13.24.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-jsonnode</artifactId>
<version>13.24.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -60,13 +60,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</parent>
<artifactId>composites</artifactId>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</parent>
<name>ebean api</name>
@@ -39,7 +39,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-config</artifactId>
<version>3.9</version>
<version>3.12</version>
</dependency>
<dependency>
-49
View File
@@ -266,55 +266,6 @@ public final class DB {
getDefault().register(transactionCallback);
}
/**
* Deprecated for removal migrate using try-with-resources and commit on the transaction itself.
* <p>
* Commit the current transaction.
*/
@Deprecated(forRemoval = true)
public static void commitTransaction() {
getDefault().commitTransaction();
}
/**
* Deprecated for removal migrate to using try-with-resources and rollback on the transaction itself.
* <p>
* Rollback the current transaction.
*/
@Deprecated(forRemoval = true)
public static void rollbackTransaction() {
getDefault().rollbackTransaction();
}
/**
* If the current transaction has already been committed do nothing otherwise
* rollback the transaction.
* <p>
* It is preferable to use <em>try with resources</em> rather than this.
* <p>
* Useful to put in a finally block to ensure the transaction is ended, rather
* than a rollbackTransaction() in each catch block.
* <p>
* Code example:
*
* <pre>{@code
* DB.beginTransaction();
* try {
* // do some fetching and or persisting
*
* // commit at the end
* DB.commitTransaction();
*
* } finally {
* // if commit didn't occur then rollback the transaction
* DB.endTransaction();
* }
* }</pre>
*/
public static void endTransaction() {
getDefault().endTransaction();
}
/**
* Mark the current transaction as rollback only.
*/
@@ -0,0 +1,104 @@
package io.ebean;
final class DInsertOptionsBuilder implements InsertOptions.Builder {
private Boolean getGeneratedKeys;
private boolean onConflictUpdate;
private boolean onConflictNothing;
private String constraint;
private String uniqueColumns;
private String updateSet;
@Override
public InsertOptions.Builder onConflictNothing() {
this.onConflictNothing = true;
return this;
}
@Override
public InsertOptions.Builder onConflictUpdate() {
this.onConflictUpdate = true;
return this;
}
@Override
public InsertOptions.Builder constraint(String constraint) {
this.constraint = constraint;
return this;
}
@Override
public InsertOptions.Builder uniqueColumns(String uniqueColumns) {
this.uniqueColumns = uniqueColumns;
return this;
}
@Override
public InsertOptions.Builder updateSet(String updateSet) {
this.updateSet = updateSet;
return this;
}
@Override
public InsertOptions.Builder getGeneratedKeys(boolean getGeneratedKeys) {
this.getGeneratedKeys = getGeneratedKeys;
return this;
}
@Override
public InsertOptions build() {
return new Options(constraint, uniqueColumns, updateSet, onConflictUpdate, onConflictNothing, getGeneratedKeys);
}
static final class Options implements InsertOptions {
private static final String UPDATE = "U";
private static final String NOTHING = "N";
private static final String NORMAL = "_";
private final String key;
private final Boolean getGeneratedKeys;
private final String constraint;
private final String uniqueColumns;
private final String updateSet;
Options(String constraint, String uniqueColumns, String updateSet, boolean onConflictUpdate, boolean onConflictNothing, Boolean getGeneratedKeys) {
this.constraint = constraint;
this.uniqueColumns = uniqueColumns;
this.updateSet = updateSet;
this.getGeneratedKeys = getGeneratedKeys;
this.key = (onConflictUpdate ? UPDATE : onConflictNothing ? NOTHING : NORMAL)
+ '+' + plus(constraint)
+ '+' + plus(uniqueColumns)
+ '+' + plus(updateSet);
}
private String plus(String val) {
return val == null ? "" : val;
}
@Override
public String key() {
return key;
}
@Override
public String constraint() {
return constraint;
}
@Override
public String uniqueColumns() {
return uniqueColumns;
}
@Override
public String updateSet() {
return updateSet;
}
@Override
public Boolean getGetGeneratedKeys() {
return getGeneratedKeys;
}
}
}
@@ -3,7 +3,7 @@ package io.ebean;
import jakarta.persistence.PersistenceException;
/**
* Thrown when a foreign key constraint is enforced.
* Thrown when a foreign key constraint is enforced or a field is too large.
*/
public class DataIntegrityException extends PersistenceException {
private static final long serialVersionUID = -6740171949170180970L;
@@ -14,4 +14,11 @@ public class DataIntegrityException extends PersistenceException {
public DataIntegrityException(String message, Throwable cause) {
super(message, cause);
}
/**
* Create with message only.
*/
public DataIntegrityException(String message) {
super(message);
}
}
+41 -53
View File
@@ -88,16 +88,16 @@ public interface Database {
/**
* Return a new database builder.
* <pre>{@code
*
* // build the 'default' database using configuration
* // from application.properties / application.yaml
*
* Database db = Database.builder()
* .loadFromProperties()
* .build();
*
* }</pre>
* <pre>{@code
*
* // build the 'default' database using configuration
* // from application.properties / application.yaml
*
* Database db = Database.builder()
* .loadFromProperties()
* .build();
*
* }</pre>
*/
static DatabaseBuilder builder() {
return new DatabaseConfig();
@@ -666,49 +666,6 @@ public interface Database {
*/
void flush();
/**
* Deprecated for removal migrate using try-with-resources and commit on the transaction itself.
* <p>
* Commit the current transaction.
*/
@Deprecated(forRemoval = true)
void commitTransaction();
/**
* Deprecated for removal migrate to using try-with-resources and rollback on the transaction itself.
* <p>
* Rollback the current transaction.
*/
@Deprecated(forRemoval = true)
void rollbackTransaction();
/**
* If the current transaction has already been committed do nothing otherwise
* rollback the transaction.
* <p>
* Useful to put in a finally block to ensure the transaction is ended, rather
* than a rollbackTransaction() in each catch block.
* <p>
* Code example:
* <p>
* <pre>{@code
*
* database.beginTransaction();
* try {
* // do some fetching and or persisting ...
*
* // commit at the end
* database.commitTransaction();
*
* } finally {
* // if commit didn't occur then rollback the transaction
* database.endTransaction();
* }
*
* }</pre>
*/
void endTransaction();
/**
* Refresh the values of a bean.
* <p>
@@ -1227,22 +1184,53 @@ public interface Database {
*/
void insert(Object bean);
/**
* Insert the bean with options (ON CONFLICT DO UPDATE | DO NOTHING).
* <p>
* Currently, this is limited to use with Postgres only,
* <p>
* When using this ebean will look to determine the unique columns by looking at
* the mapping like {@code @Column(unique=true} and {@code @Index(unique=true}.
*/
void insert(Object bean, InsertOptions insertOptions);
/**
* Insert the bean with a transaction.
*/
void insert(Object bean, Transaction transaction);
/**
* Insert the beans with options (ON CONFLICT DO UPDATE | DO NOTHING) and transaction.
* <p>
* Currently, this is limited to use with Postgres only,
*/
void insert(Object bean, InsertOptions insertOptions, Transaction transaction);
/**
* Insert a collection of beans. If there is no current transaction one is created and used to
* insert all the beans in the collection.
*/
void insertAll(Collection<?> beans);
/**
* Insert the beans with options - typically ON CONFLICT DO UPDATE | DO NOTHING.
* <p>
* Currently, this is limited to use with Postgres only,
*/
void insertAll(Collection<?> beans, InsertOptions options);
/**
* Insert a collection of beans with an explicit transaction.
*/
void insertAll(Collection<?> beans, Transaction transaction);
/**
* Insert the beans with options (ON CONFLICT DO UPDATE | DO NOTHING) and transaction.
* <p>
* Currently, this is limited to use with Postgres only,
*/
void insertAll(Collection<?> beans, InsertOptions options, Transaction transaction);
/**
* Execute explicitly passing a transaction.
*/
@@ -87,6 +87,7 @@ public interface DatabaseBuilder {
}
return this;
}
/**
* Set the name of the Database.
*/
@@ -1053,6 +1054,14 @@ public interface DatabaseBuilder {
@Deprecated
DatabaseBuilder setSkipDataSourceCheck(boolean skipDataSourceCheck);
/**
* Set to true if this database is used in a read only way.
* <p>
* The DataSource and read-only DataSource are expected to be the same
* and use readOnly=true and autoCommit=true.
*/
DatabaseBuilder readOnlyDatabase(boolean readOnlyDatabase);
/**
* Set a DataSource.
*/
@@ -2213,6 +2222,11 @@ public interface DatabaseBuilder {
@Deprecated
DatabaseBuilder setMetricNaming(Function<String, String> metricNaming);
/**
* Sets the length check mode.
*/
DatabaseConfig lengthCheck(LengthCheck lengthCheck);
/**
* Provides read access (getters) for the DatabaseBuilder configuration
* that has been set.
@@ -2607,6 +2621,14 @@ public interface DatabaseBuilder {
*/
boolean skipDataSourceCheck();
/**
* Return true if this database is used in a read only way.
* <p>
* The DataSource and read-only DataSource are expected to be the same
* and use readOnly=true and autoCommit=true.
*/
boolean readOnlyDatabase();
/**
* Return the DataSource.
*/
@@ -3087,5 +3109,9 @@ public interface DatabaseBuilder {
*/
Function<String, String> getMetricNaming();
/**
* Returns the length check mode.
*/
LengthCheck getLengthCheck();
}
}
@@ -3,10 +3,9 @@ package io.ebean;
import io.ebean.config.ContainerConfig;
import io.ebean.service.SpiContainer;
import io.ebean.service.SpiContainerFactory;
import jakarta.persistence.PersistenceException;
import java.util.Iterator;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.concurrent.locks.ReentrantLock;
@@ -146,12 +145,9 @@ public final class DatabaseFactory {
if (container != null) {
return container;
}
if (containerConfig == null) {
// effectively load configuration from ebean.properties
Properties properties = DbPrimary.getProperties();
containerConfig = new ContainerConfig();
containerConfig.loadFromProperties(properties);
}
container = createContainer(containerConfig);
return container;
@@ -160,7 +156,7 @@ public final class DatabaseFactory {
/**
* Create the container instance using the configuration.
*/
protected static SpiContainer createContainer(ContainerConfig containerConfig) {
private static SpiContainer createContainer(ContainerConfig containerConfig) {
Iterator<SpiContainerFactory> factories = ServiceLoader.load(SpiContainerFactory.class).iterator();
if (factories.hasNext()) {
return factories.next().create(containerConfig);
@@ -44,25 +44,12 @@ final class DbPrimary {
* Return the default database name.
*/
static String getDefaultServerName() {
lock.lock();
try {
getProperties();
return defaultServerName;
} finally {
lock.unlock();
}
}
/**
* Return the default configuration Properties.
*/
static Properties getProperties() {
lock.lock();
try {
if (defaultServerName == null) {
defaultServerName = determineDefaultServerName();
}
return Config.asProperties();
return defaultServerName;
} finally {
lock.unlock();
}
@@ -3,6 +3,8 @@ package io.ebean;
import io.avaje.lang.NonNullApi;
import io.avaje.lang.Nullable;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@@ -206,14 +208,19 @@ public interface DtoQuery<T> extends CancelableQuery {
*/
DtoQuery<T> usingTransaction(Transaction transaction);
/**
* Execute the query using the given connection.
*/
DtoQuery<T> usingConnection(Connection connection);
/**
* 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
* say {@link io.ebean.DatabaseBuilder#readOnlyDataSource(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
* source. We use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
DtoQuery<T> usingMaster();
@@ -0,0 +1,119 @@
package io.ebean;
import io.avaje.lang.Nullable;
/**
* Options to be used with insert such as ON CONFLICT DO UPDATE | NOTHING.
*/
public interface InsertOptions {
/**
* Use ON CONFLICT UPDATE with automatic determination of the unique columns to conflict on.
* <p>
* Uses mapping to determine the unique columns - {@code @Column(unique=true)} and {@code @Index(unique=true)} .
*/
InsertOptions ON_CONFLICT_UPDATE = InsertOptions.builder()
.onConflictUpdate()
.build();
/**
* Use ON CONFLICT DO NOTHING with automatic determination of the unique columns to conflict on.
* <p>
* Uses mapping to determine the unique columns - {@code @Column(unique=true)} and {@code @Index(unique=true)} .
*/
InsertOptions ON_CONFLICT_NOTHING = InsertOptions.builder()
.onConflictNothing()
.build();
/**
* Return a builder for InsertOptions.
*/
static Builder builder() {
return new DInsertOptionsBuilder();
}
/**
* Return the constraint name that is used for ON CONFLICT.
*/
@Nullable
String constraint();
/**
* Return the unique columns that is used for ON CONFLICT.
* <p>
* When not explicitly set will use mapping like {@code @Column(unique=true)} to determine the
* non-unique columns.
*/
@Nullable
String uniqueColumns();
/**
* Return the ON CONFLICT UPDATE SET clause.
* <p>
* When not set will use the non-unique columns.
*/
@Nullable
String updateSet();
/**
* Return if GetGeneratedKeys should be used to fetch the generated keys after insert.
*/
@Nullable
Boolean getGetGeneratedKeys();
/**
* Return the key for these build options.
*/
String key();
/**
* The builder for InsertOptions.
*/
interface Builder {
/**
* Use a ON CONFLICT UPDATE automatically determining the unique columns.
*/
Builder onConflictUpdate();
/**
* Use a ON CONFLICT DO NOTHING automatically determining the unique columns.
*/
Builder onConflictNothing();
/**
* Specify an explicit conflict constraint name.
* <p>
* When this is used then unique columns will not be used.
*/
Builder constraint(String constraint);
/**
* Specify the unique columns for the conflict target.
* <p>
* When not specified and constraint is also not specified then
* it will automatically determine the unique columns
* based on mapping like {@code @Column(unique=true)} and
* {@code @Index(unique=true)} .
*/
Builder uniqueColumns(String uniqueColumns);
/**
* Specify the ON CONFLICT DO UPDATE SET clause.
* <p>
* When not specified ebean will include all the non-unique columns.
*/
Builder updateSet(String updateSet);
/**
* Specify if GetGeneratedKeys should be used to return generated keys.
*/
Builder getGeneratedKeys(boolean getGeneratedKeys);
/**
* Build and return the insert options.
*/
InsertOptions build();
}
}
+21 -2
View File
@@ -702,9 +702,9 @@ public interface Query<T> extends CancelableQuery {
* 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
* say {@link io.ebean.DatabaseBuilder#readOnlyDataSource(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
* source. We use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
Query<T> usingMaster();
@@ -970,6 +970,25 @@ public interface Query<T> extends CancelableQuery {
@Nullable
<A> A findSingleAttribute();
/**
* Execute the query returning a single optional attribute value.
* <p>
* <h3>Example</h3>
* <pre>{@code
*
* Optional<String> maybeName =
* new QCustomer()
* .select(name)
* .id.eq(42)
* .status.eq(NEW)
* .findSingleAttributeOrEmpty();
*
* }</pre>
*
* @return an optional value for the selected property
*/
<A> Optional<A> findSingleAttributeOrEmpty();
/**
* Return true if this is countDistinct query.
*/
@@ -3,7 +3,9 @@ package io.ebean;
import io.avaje.lang.NonNullApi;
import io.avaje.lang.Nullable;
import javax.sql.DataSource;
import java.io.Serializable;
import java.sql.Connection;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
@@ -45,14 +47,19 @@ public interface SqlQuery extends Serializable, CancelableQuery {
*/
SqlQuery usingTransaction(Transaction transaction);
/**
* Execute the query using the given connection.
*/
SqlQuery usingConnection(Connection connection);
/**
* 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
* say {@link io.ebean.DatabaseBuilder#readOnlyDataSource(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
* source. We use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
SqlQuery usingMaster();
@@ -15,7 +15,6 @@ public class ContainerConfig {
private String namespace;
private String podName;
private int port;
private Properties properties;
/**
@@ -123,5 +122,4 @@ public class ContainerConfig {
protected boolean getProperty(Properties properties, String key, boolean defaultValue) {
return "true".equalsIgnoreCase(properties.getProperty(key, Boolean.toString(defaultValue)));
}
}
@@ -236,7 +236,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
*/
private PersistBatch persistBatchOnCascade = PersistBatch.INHERIT;
private int persistBatchSize = 20;
private int persistBatchSize = 100;
private EnumType defaultEnumType = EnumType.ORDINAL;
@@ -304,6 +304,8 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
private boolean skipDataSourceCheck;
private boolean readOnlyDatabase;
/**
* The data source (if programmatically provided).
*/
@@ -543,6 +545,8 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
private String dumpMetricsOptions;
private LengthCheck lengthCheck = LengthCheck.OFF;
private Function<String, String> metricNaming = MetricNamingMatch.INSTANCE;
/**
@@ -1334,7 +1338,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
@Override
public boolean skipDataSourceCheck() {
return skipDataSourceCheck;
return skipDataSourceCheck || readOnlyDatabase;
}
@Override
@@ -1343,6 +1347,17 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
return this;
}
@Override
public DatabaseBuilder readOnlyDatabase(boolean readOnlyDatabase) {
this.readOnlyDatabase = readOnlyDatabase;
return this;
}
@Override
public boolean readOnlyDatabase() {
return readOnlyDatabase;
}
@Override
public DataSource getDataSource() {
return dataSource;
@@ -2115,6 +2130,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
loadDocStoreSettings(p);
defaultServer = p.getBoolean("defaultServer", defaultServer);
readOnlyDatabase = p.getBoolean("readOnlyDatabase", readOnlyDatabase);
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
@@ -2181,6 +2197,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
jdbcFetchSizeFindEach = p.getInt("jdbcFetchSizeFindEach", jdbcFetchSizeFindEach);
jdbcFetchSizeFindList = p.getInt("jdbcFetchSizeFindList", jdbcFetchSizeFindList);
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
lengthCheck = p.getEnum(LengthCheck.class, "lengthCheck", lengthCheck);
uuidVersion = p.getEnum(UuidVersion.class, "uuidVersion", uuidVersion);
uuidStateFile = p.get("uuidStateFile", uuidStateFile);
@@ -2557,6 +2574,19 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
return this;
}
/**
* Returns the length check mode.
*/
public LengthCheck getLengthCheck() {
return lengthCheck;
}
@Override
public DatabaseConfig lengthCheck(LengthCheck lengthCheck) {
this.lengthCheck = lengthCheck;
return this;
}
public enum UuidVersion {
VERSION4,
VERSION1,
@@ -0,0 +1,22 @@
package io.ebean.config;
/**
* Defines the length-check mode.
*
* @author Roland Praml, FOCONIS AG
*/
public enum LengthCheck {
/**
* By default, length checking is off. This means, strings/jsons and files are passed to the DB and the DB might or might not check the length.
* The DB has to check the data length. Note this is not possible for certain datatypes (e.g. clob without size)
*/
OFF,
/**
* When enabling length check, ebean validates strings/json strings and files before saving them to DB.
*/
ON,
/**
* Same as "ON", but take the UTF8-bytelength for validation. This may be useful, if you have an UTF8 based charset (default for DB2)
*/
UTF8
}
@@ -8,6 +8,7 @@ import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
@@ -23,7 +24,7 @@ public final class ShutdownManager {
private static final System.Logger log = EbeanVersion.log;
private static final ReentrantLock lock = new ReentrantLock();
private static final List<Database> databases = new ArrayList<>();
private static final List<Database> databases = Collections.synchronizedList(new ArrayList<>());
private static final ShutdownHook shutdownHook = new ShutdownHook();
private static boolean stopping;
@@ -177,12 +178,7 @@ public final class ShutdownManager {
* Register an ebeanServer to be shutdown when the JVM is shutdown.
*/
public static void registerDatabase(Database server) {
lock.lock();
try {
databases.add(server);
} finally {
lock.unlock();
}
databases.add(server);
}
/**
@@ -192,12 +188,7 @@ public final class ShutdownManager {
* </p>
*/
public static void unregisterDatabase(Database server) {
lock.lock();
try {
databases.remove(server);
} finally {
lock.unlock();
}
databases.remove(server);
}
private static class ShutdownHook extends Thread {
@@ -22,6 +22,8 @@ public class JsonWriteOptions {
protected JsonConfig.Include include;
protected boolean includeLoadedImplicit = true;
protected Map<String, JsonWriteBeanVisitor<?>> visitorMap;
/**
@@ -73,6 +75,20 @@ public class JsonWriteOptions {
this.include = include;
}
/**
* Should loaded properties be included implicit, if no other fetch path is specified (default = true).
*/
public boolean isIncludeLoadedImplicit() {
return includeLoadedImplicit;
}
/**
* Set include loaded properties implicit (default = true).
*/
public void setIncludeLoadedImplicit(boolean includeLoadedImplicit) {
this.includeLoadedImplicit = includeLoadedImplicit;
}
/**
* Register a JsonWriteBeanVisitor for the root level.
*/
@@ -21,9 +21,4 @@ class DbPrimaryTest {
assertEquals("h2", DbPrimary.getDefaultServerName());
}
@Test
void testLoadProperties() {
Properties properties = DbPrimary.getProperties();
assertFalse(properties.isEmpty());
}
}
@@ -74,6 +74,8 @@ class DatabaseConfigTest {
props.setProperty("forUpdateNoKey", "true");
props.setProperty("defaultServer", "false");
props.setProperty("skipDataSourceCheck", "true");
props.setProperty("readOnlyDatabase", "true");
props.setProperty("lengthCheck", "ON");
props.setProperty("queryPlan.enable", "true");
props.setProperty("queryPlan.thresholdMicros", "10000");
@@ -93,6 +95,8 @@ class DatabaseConfigTest {
assertTrue(settings.isAutoLoadModuleInfo());
assertTrue(settings.isLoadModuleInfo());
assertTrue(settings.skipDataSourceCheck());
assertTrue(settings.readOnlyDatabase());
assertThat(settings.getLengthCheck()).isEqualTo(LengthCheck.ON);
assertTrue(settings.isIdGeneratorAutomatic());
assertFalse(settings.getPlatformConfig().isCaseSensitiveCollation());
@@ -126,8 +130,11 @@ class DatabaseConfigTest {
assertThat(settings.getMappingLocations()).containsExactly("classpath:/foo","bar");
config.setPersistBatch(PersistBatch.NONE);
config.setPersistBatchOnCascade(PersistBatch.NONE);
config.persistBatch(PersistBatch.NONE)
.persistBatchOnCascade(PersistBatch.NONE)
.lengthCheck(LengthCheck.ON)
.lengthCheck(LengthCheck.UTF8);
Properties props1 = new Properties();
props1.setProperty("ebean.persistBatch", "ALL");
@@ -144,6 +151,7 @@ class DatabaseConfigTest {
assertEquals(PersistBatch.ALL, settings.getPersistBatch());
assertEquals(PersistBatch.ALL, settings.getPersistBatchOnCascade());
assertEquals(LengthCheck.UTF8, settings.getLengthCheck());
config.setEnabledL2Regions("r0,orgs");
assertEquals("r0,orgs", settings.getEnabledL2Regions());
@@ -172,6 +180,7 @@ class DatabaseConfigTest {
assertEquals(600, config.getQueryPlanCapturePeriodSecs());
assertEquals(10000L, config.getQueryPlanCaptureMaxTimeMillis());
assertEquals(10, config.getQueryPlanCaptureMaxCount());
assertThat(config.getLengthCheck()).isEqualTo(LengthCheck.OFF);
config.setLoadModuleInfo(false);
assertFalse(config.isAutoLoadModuleInfo());
+31 -31
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</parent>
<name>ebean bom</name>
@@ -89,103 +89,103 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-joda-time</artifactId>
<version>13.24.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-jsonnode</artifactId>
<version>13.24.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>13.18.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>13.18.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>13.18.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<!-- platforms -->
@@ -193,79 +193,79 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
</dependencies>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -30,7 +30,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
<version>42.7.2</version>
<optional>true</optional>
</dependency>
@@ -174,4 +174,9 @@ public interface DataBinder {
*/
String popJson();
/**
* Returns the last bound object (e.g. for BindValidation). Note for InputStreams you'll get an InputStreamInfo.
*/
Object popLastObject();
}
@@ -0,0 +1,27 @@
package io.ebean.core.type;
import java.io.InputStream;
/**
* Helper to transports length info of DataBind.setBinaryStream(stream, length) to BindValidation
*
* @author Roland Praml, FOCONIS AG
*/
public final class InputStreamInfo {
private final InputStream stream;
private final long length;
public InputStreamInfo(InputStream stream, long length) {
this.stream = stream;
this.length = length;
}
public InputStream stream() {
return stream;
}
public long length() {
return length;
}
}
+8 -8
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,7 +22,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
@@ -46,13 +46,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>13.18.0</version>
<version>14.0.0</version>
</dependency>
<dependency>
@@ -151,7 +151,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
<version>42.7.2</version>
<optional>true</optional>
</dependency>
@@ -159,21 +159,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>13.25.0-jakarta</version>
<version>14.0.1</version>
<scope>test</scope>
</dependency>
@@ -3,6 +3,8 @@ package io.ebeaninternal.api;
import io.ebean.TxScope;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Used internally to handle the scoping of transactions for methods.
@@ -47,6 +49,11 @@ public final class ScopeTrans {
private boolean nestedCommit;
private boolean nestedUseSavepoint;
/**
* The UserObjects when using in nested transactions.
*/
private Map<String, Object> userObjects;
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope) {
this.rollbackOnChecked = rollbackOnChecked;
this.created = created;
@@ -218,4 +225,17 @@ public final class ScopeTrans {
return e instanceof RuntimeException || rollbackOnChecked;
}
public void putUserObject(String name, Object value) {
if (userObjects == null) {
userObjects = new HashMap<>();
}
userObjects.put(name, value);
}
public Object getUserObject(String name) {
if (userObjects == null) {
return null;
}
return userObjects.get(name);
}
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.api;
import io.ebeaninternal.server.transaction.TransactionScopeManager;
import io.ebeaninternal.server.util.ArrayStack;
import jakarta.persistence.PersistenceException;
/**
@@ -173,4 +172,31 @@ public final class ScopedTransaction extends SpiTransactionProxy {
return current.caughtThrowable(e);
}
/**
* New user objects are always written to the current ScopeTrans.
*/
@Override
public void putUserObject(String name, Object value) {
current.putUserObject(name, value);
}
/**
* Returns the userObject in the stack, Herew we search
* the stack and return the first found userObject
*/
@Override
public Object getUserObject(String name) {
Object obj = current.getUserObject(name);
if (obj != null) {
return obj;
}
for (ScopeTrans trans : stack) {
obj = trans.getUserObject(name);
if (obj != null) {
return obj;
}
}
return transaction.getUserObject(name);
}
}
@@ -86,6 +86,11 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio
*/
SpiTransactionManager transactionManager();
/**
* End the current transaction if it is active.
*/
void endTransaction();
/**
* Return all the descriptors.
*/
@@ -3,13 +3,14 @@ package io.ebeaninternal.api;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
import io.ebeaninternal.server.expression.platform.DbExpressionRequest;
import java.util.List;
/**
* Request object used for gathering expression sql and bind values.
*/
public interface SpiExpressionRequest {
public interface SpiExpressionRequest extends DbExpressionRequest {
/**
* Return the DB specific handler for JSON and ARRAY expressions.
@@ -34,11 +35,13 @@ public interface SpiExpressionRequest {
/**
* Append to the expression sql without any parsing.
*/
@Override
SpiExpressionRequest append(String expression);
/**
* Append to the expression sql without any parsing.
*/
@Override
SpiExpressionRequest append(char c);
/**
@@ -47,6 +50,7 @@ public interface SpiExpressionRequest {
* This is a fast path case when expression is a bean property path and falls back to using parse()
* when that isn't the case.
*/
@Override
SpiExpressionRequest property(String expression);
/**
@@ -654,6 +654,16 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
*/
ObjectGraphNode parentNode();
/**
* Set that this is a future query that will execute in the background.
*/
void usingFuture();
/**
* Return true if this is a future query.
*/
boolean isUsingFuture();
/**
* Return false when this is a lazy load or refresh query for a bean.
* <p>
@@ -1,10 +1,14 @@
package io.ebeaninternal.server.bind;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.InputStreamInfo;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
@@ -23,6 +27,8 @@ public class DataBind implements DataBinder {
protected int pos;
private String json;
private Object lastObject = null;
public DataBind(DataTimeZone dataTimeZone, PreparedStatement pstmt, Connection connection) {
this.dataTimeZone = dataTimeZone;
this.pstmt = pstmt;
@@ -65,16 +71,19 @@ public class DataBind implements DataBinder {
@Override
public void setObject(Object value) throws SQLException {
pstmt.setObject(++pos, value);
lastObject = value;
}
@Override
public final void setObject(Object value, int sqlType) throws SQLException {
pstmt.setObject(++pos, value, sqlType);
lastObject = value;
}
@Override
public void setNull(int jdbcType) throws SQLException {
pstmt.setNull(++pos, jdbcType);
lastObject = null;
}
@Override
@@ -96,7 +105,7 @@ public class DataBind implements DataBinder {
}
}
private void closeInputStreams() {
public void closeInputStreams() {
if (inputStreams != null) {
for (InputStream inputStream : inputStreams) {
try {
@@ -117,36 +126,43 @@ public class DataBind implements DataBinder {
@Override
public void setString(String value) throws SQLException {
pstmt.setString(++pos, value);
lastObject = value;
}
@Override
public final void setInt(int value) throws SQLException {
pstmt.setInt(++pos, value);
lastObject = null;
}
@Override
public final void setLong(long value) throws SQLException {
pstmt.setLong(++pos, value);
lastObject = null;
}
@Override
public final void setShort(short value) throws SQLException {
pstmt.setShort(++pos, value);
lastObject = null;
}
@Override
public final void setFloat(float value) throws SQLException {
pstmt.setFloat(++pos, value);
lastObject = null;
}
@Override
public final void setDouble(double value) throws SQLException {
pstmt.setDouble(++pos, value);
lastObject = null;
}
@Override
public final void setBigDecimal(BigDecimal value) throws SQLException {
pstmt.setBigDecimal(++pos, value);
lastObject = null;
}
@Override
@@ -157,6 +173,7 @@ public class DataBind implements DataBinder {
} else {
pstmt.setDate(++pos, value);
}
lastObject = null;
}
@Override
@@ -167,6 +184,7 @@ public class DataBind implements DataBinder {
} else {
pstmt.setTimestamp(++pos, value);
}
lastObject = null;
}
@Override
@@ -177,26 +195,31 @@ public class DataBind implements DataBinder {
} else {
pstmt.setTime(++pos, value);
}
lastObject = null;
}
@Override
public void setBoolean(boolean value) throws SQLException {
pstmt.setBoolean(++pos, value);
lastObject = null;
}
@Override
public void setBytes(byte[] value) throws SQLException {
pstmt.setBytes(++pos, value);
lastObject = value;
}
@Override
public void setByte(byte value) throws SQLException {
pstmt.setByte(++pos, value);
lastObject = null;
}
@Override
public void setChar(char value) throws SQLException {
pstmt.setString(++pos, String.valueOf(value));
lastObject = null;
}
@Override
@@ -211,21 +234,31 @@ public class DataBind implements DataBinder {
}
inputStreams.add(inputStream);
pstmt.setBinaryStream(++pos, inputStream, length);
lastObject = new InputStreamInfo(inputStream, length);
}
@Override
public void setBlob(byte[] bytes) throws SQLException {
pstmt.setBinaryStream(++pos, new ByteArrayInputStream(bytes), bytes.length);
lastObject = bytes;
}
@Override
public void setClob(String content) throws SQLException {
pstmt.setCharacterStream(++pos, new StringReader(content), content.length());
lastObject = content;
}
@Override
public void setArray(String arrayType, Object[] elements) throws SQLException {
pstmt.setArray(++pos, connection.createArrayOf(arrayType, elements));
lastObject = null;
}
@Override
public Object popLastObject() {
Object ret = lastObject;
lastObject = null;
return ret;
}
}
@@ -5,7 +5,6 @@ import io.ebean.Database;
import io.ebean.config.ContainerConfig;
import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@@ -20,38 +19,22 @@ public class ClusterManager implements ServerLookup {
private static final System.Logger clusterLogger = AppLog.getLogger("io.ebean.Cluster");
private final ReentrantLock lock = new ReentrantLock();
private final ConcurrentHashMap<String, Database> serverMap = new ConcurrentHashMap<>();
private final Object monitor = new Object();
private final ClusterBroadcast broadcast;
private boolean started;
private boolean shutdown;
public ClusterManager(ContainerConfig config) {
ClusterBroadcastFactory factory = createFactory();
if (factory != null && config.isActive()) {
broadcast = factory.create(this, config);
if (config.isActive()) {
ClusterBroadcastFactory factory = createFactory();
broadcast = factory != null ? factory.create(this, config) : null;
} else {
broadcast = null;
}
}
/**
* Return the ClusterTransportFactory via ServiceLoader.
*/
private ClusterBroadcastFactory createFactory() {
ServiceLoader<ClusterBroadcastFactory> load = ServiceLoader.load(ClusterBroadcastFactory.class);
ClusterBroadcastFactory factory = null;
Iterator<ClusterBroadcastFactory> iterator = load.iterator();
if (iterator.hasNext()) {
factory = iterator.next();
}
return factory;
return ServiceLoader.load(ClusterBroadcastFactory.class).findFirst().orElse(null);
}
public void registerServer(Database server) {
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.Database;
import io.ebean.DatabaseBuilder;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -13,11 +14,13 @@ import io.ebeaninternal.server.cluster.ClusterManager;
import io.ebeaninternal.server.core.bootup.BootupClassPathSearch;
import io.ebeaninternal.server.core.bootup.BootupClasses;
import io.ebeaninternal.server.executor.DefaultBackgroundExecutor;
import jakarta.persistence.PersistenceException;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
@@ -33,12 +36,22 @@ public final class DefaultContainer implements SpiContainer {
private final ReentrantLock lock = new ReentrantLock();
private final ClusterManager clusterManager;
private final List<EntityClassRegister> entityClassRegisters;
public DefaultContainer(ContainerConfig containerConfig) {
this.clusterManager = new ClusterManager(containerConfig);
// register so that we can shutdown any Ebean wide
// resources such as clustering
ShutdownManager.registerContainer(this);
entityClassRegisters = initEntityRegisters();
}
private List<EntityClassRegister> initEntityRegisters() {
var entityClassRegisters = new ArrayList<EntityClassRegister>();
for (EntityClassRegister entityClassRegister : ServiceLoader.load(EntityClassRegister.class)) {
entityClassRegisters.add(entityClassRegister);
}
return entityClassRegisters;
}
@Override
@@ -52,10 +65,8 @@ public final class DefaultContainer implements SpiContainer {
*/
@Override
public SpiEbeanServer createServer(String name) {
DatabaseBuilder config = new DatabaseConfig();
config.setName(name);
config.loadFromProperties();
return createServer(config);
var builder = Database.builder().name(name).loadFromProperties();
return createServer(builder);
}
private SpiBackgroundExecutor createBackgroundExecutor(DatabaseBuilder builder) {
@@ -82,7 +93,7 @@ public final class DefaultContainer implements SpiContainer {
boolean online = true;
if (config.isDocStoreOnly()) {
config.setDatabasePlatform(new DatabasePlatform());
config.databasePlatform(new DatabasePlatform());
} else {
TenantMode tenantMode = config.getTenantMode();
if (TenantMode.DB != tenantMode) {
@@ -126,12 +137,10 @@ public final class DefaultContainer implements SpiContainer {
}
if (config.isLoadModuleInfo()) {
// auto register entity classes
boolean found = false;
for (EntityClassRegister loader : ServiceLoader.load(EntityClassRegister.class)) {
for (EntityClassRegister loader : entityClassRegisters) {
config.addAll(loader.classesFor(config.getName(), config.isDefaultServer()));
found = true;
}
if (!found) {
if (entityClassRegisters.isEmpty()) {
checkMissingModulePathProvides();
}
}
@@ -193,7 +202,7 @@ public final class DefaultContainer implements SpiContainer {
*/
private void setNamingConvention(DatabaseBuilder.Settings config) {
if (config.getNamingConvention() == null) {
config.setNamingConvention(new UnderscoreNamingConvention());
config.namingConvention(new UnderscoreNamingConvention());
}
}
@@ -208,7 +217,7 @@ public final class DefaultContainer implements SpiContainer {
}
// automatically determine the platform
platform = new DatabasePlatformFactory().create(config);
config.setDatabasePlatform(platform);
config.databasePlatform(platform);
}
platform.configure(config.getPlatformConfig());
}
@@ -232,11 +241,9 @@ public final class DefaultContainer implements SpiContainer {
* Check the autoCommit and Transaction Isolation levels of the DataSource.
* <p>
* If autoCommit is true this could be a real problem.
* </p>
* <p>
* If the Isolation level is not READ_COMMITTED then optimistic concurrency
* checking may not work as expected.
* </p>
*/
private boolean checkDataSource(DatabaseBuilder.Settings config) {
if (isOfflineMode(config)) {
@@ -756,16 +756,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
currentTransaction().flush();
}
@Override
public void commitTransaction() {
currentTransaction().commit();
}
@Override
public void rollbackTransaction() {
currentTransaction().rollback();
}
@Override
public void endTransaction() {
Transaction transaction = transactionManager.inScope();
@@ -961,7 +951,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
if (!query.isRawSql()) {
query.setDefaultRawSqlIfRequired();
if (query.isAutoTunable() && !autoTuneService.tuneQuery(query)) {
if (!query.isAutoTunable() || !autoTuneService.tuneQuery(query)) {
// use deployment FetchType.LAZY/EAGER annotations
// to define the 'default' select clause
query.setDefaultSelectClause();
@@ -1273,6 +1263,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public <T> FutureRowCount<T> findFutureCount(SpiQuery<T> query) {
SpiQuery<T> copy = query.copy();
copy.usingFuture();
boolean createdTransaction = false;
SpiTransaction transaction = query.transaction();
if (transaction == null) {
@@ -1291,6 +1282,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public <T> FutureIds<T> findFutureIds(SpiQuery<T> query) {
SpiQuery<T> copy = query.copy();
copy.usingFuture();
boolean createdTransaction = false;
SpiTransaction transaction = query.transaction();
if (transaction == null) {
@@ -1309,6 +1301,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public <T> FutureList<T> findFutureList(SpiQuery<T> query) {
SpiQuery<T> spiQuery = query.copy();
spiQuery.usingFuture();
// FutureList query always run in it's own persistence content
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
if (!spiQuery.isDisableReadAudit()) {
@@ -1651,41 +1644,50 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}, transaction);
}
/**
* Insert the bean.
*/
@Override
public void insert(Object bean) {
insert(bean, null);
persister.insert(checkEntityBean(bean), null, null);
}
@Override
public void insert(Object bean, @Nullable InsertOptions insertOptions) {
persister.insert(checkEntityBean(bean), insertOptions, null);
}
/**
* Insert the bean with a transaction.
*/
@Override
public void insert(Object bean, @Nullable Transaction transaction) {
persister.insert(checkEntityBean(bean), transaction);
persister.insert(checkEntityBean(bean), null, transaction);
}
@Override
public void insert(Object bean, InsertOptions insertOptions, Transaction transaction) {
persister.insert(checkEntityBean(bean), insertOptions, transaction);
}
/**
* Insert all beans in the collection.
*/
@Override
public void insertAll(Collection<?> beans) {
insertAll(beans, null);
insertAll(beans, null, null);
}
@Override
public void insertAll(Collection<?> beans, InsertOptions options) {
insertAll(beans, options, null);
}
/**
* Insert all beans in the collection with a transaction.
*/
@Override
public void insertAll(@Nullable Collection<?> beans, @Nullable Transaction transaction) {
insertAll(beans, null, transaction);
}
@Override
public void insertAll(@Nullable Collection<?> beans, InsertOptions options, @Nullable Transaction transaction) {
if (beans == null || beans.isEmpty()) {
return;
}
executeInTrans((txn) -> {
txn.checkBatchEscalationOnCollection();
for (Object bean : beans) {
persister.insert(checkEntityBean(bean), txn);
persister.insert(checkEntityBean(bean), options, txn);
}
return 0;
}, transaction);
@@ -38,15 +38,19 @@ final class InitDataSource {
* Initialise the "main" read write DataSource from configuration.
*/
private DataSource initDataSource() {
return createFromConfig(config.getDataSourceConfig(), false);
return createFromConfig(config.getDataSourceConfig(), config.readOnlyDatabase(), false);
}
/**
* Initialise the "read only" DataSource from configuration.
*/
private DataSource initReadOnlyDataSource() {
if (config.readOnlyDatabase()) {
// using the same DataSource instance
return config.getDataSource();
}
var roConfig = readOnlyConfig();
return roConfig == null ? null : createFromConfig(roConfig, true);
return roConfig == null ? null : createFromConfig(roConfig, false, true);
}
DataSourceBuilder.Settings readOnlyConfig() {
@@ -76,20 +80,21 @@ final class InitDataSource {
return url != null && !"none".equalsIgnoreCase(url) && !url.trim().isEmpty();
}
private DataSource createFromConfig(DataSourceBuilder.Settings dsConfig, boolean readOnly) {
private DataSource createFromConfig(DataSourceBuilder.Settings dsConfig, boolean readOnlyDB, boolean readOnly) {
if (dsConfig == null) {
throw new PersistenceException("No DataSourceBuilder defined for " + config.getName());
}
if (dsConfig.isOffline()) {
if (config.getDatabasePlatformName() == null) {
throw new PersistenceException("You MUST specify a DatabasePlatformName on DatabaseConfig when offline");
}
if (dsConfig.isOffline() && config.getDatabasePlatformName() == null) {
throw new PersistenceException("You MUST specify a DatabasePlatformName on DatabaseConfig when offline");
}
attachAlert(dsConfig);
attachListener(dsConfig);
if (readOnly) {
if (readOnlyDB) {
dsConfig.autoCommit(true);
dsConfig.readOnly(true);
} else if (readOnly) {
// setup to use AutoCommit such that we skip explicit commit
var mainSettings = config.getDataSourceConfig();
dsConfig.autoCommit(true);
@@ -35,7 +35,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
private final OrmQueryEngine queryEngine;
private final SpiQuery<T> query;
private final BeanFindController finder;
private final Boolean readOnly;
private LoadContext loadContext;
private PersistenceContext persistenceContext;
private HashQuery cacheKey;
@@ -52,7 +51,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
this.finder = beanDescriptor.beanFinder();
this.queryEngine = queryEngine;
this.query = query;
this.readOnly = query.isReadOnly();
this.persistenceContext = query.persistenceContext();
}
@@ -135,13 +133,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
return loadContext.secondaryQueriesMinBatchSize();
}
/**
* Return the Normal, sharedInstance, ReadOnly state of this query.
*/
public Boolean isReadOnly() {
return readOnly;
}
/**
* Return the BeanDescriptor for the associated bean.
*/
@@ -782,4 +773,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
public int forwardOnlyFetchSize() {
return queryEngine.forwardOnlyFetchSize();
}
public void clearContext() {
if (!transaction.isAutoPersistUpdates()) {
beanDescriptor.contextClear(transaction.persistenceContext());
}
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.InsertOptions;
import io.ebean.ValuePair;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.EntityBean;
@@ -124,6 +125,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Many-to-many intersection table changes that are held for later batch processing.
*/
private List<SaveMany> saveMany;
private InsertOptions insertOptions;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
@@ -380,6 +382,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return intercept.dirtyPropertyNames();
}
public boolean isChangedProperty(int propertyIndex) {
if (dirtyProperties == null) {
return intercept.isChangedProperty(propertyIndex);
} else {
return dirtyProperties[propertyIndex];
}
}
/**
* Return the dirty properties on this request.
*/
@@ -865,8 +875,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
setNotifyCache();
boolean isChangeLog = beanDescriptor.isChangeLog();
if (type == Type.UPDATE && (isChangeLog || notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
// get the dirty properties for update notification to the doc store
if (type == Type.UPDATE) {
// get the dirty properties for notify cache & orphanRemoval of vanilla collection detection
dirtyProperties = intercept.dirtyProperties();
}
if (isChangeLog) {
@@ -1408,4 +1418,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private void setGeneratedId() {
beanDescriptor.setGeneratedId(entityBean, transaction);
}
public void setInsertOptions(InsertOptions insertOptions) {
this.insertOptions = insertOptions;
}
public InsertOptions insertOptions() {
return insertOptions;
}
}
@@ -77,10 +77,13 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
*/
@Override
public void postExecute() {
OrmUpdateType ormUpdateType = ormUpdate.ormUpdateType();
if (OrmUpdateType.INSERT != ormUpdateType && !transaction.isAutoPersistUpdates()) {
beanDescriptor.contextClear(transaction.persistenceContext());
}
if (startNanos > 0) {
persistExecute.collectOrmUpdate(label, startNanos);
}
OrmUpdateType ormUpdateType = ormUpdate.ormUpdateType();
String tableName = ormUpdate.baseTable();
if (transaction.isLogSummary()) {
transaction.logSummary("{0} table[{1}] rows[{2}] bind[{3}]", ormUpdateType, tableName, rowCount, bindLog);
@@ -3,10 +3,13 @@ package io.ebeaninternal.server.core;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiSqlUpdate;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.persist.PersistExecute;
import io.ebeaninternal.server.persist.TrimLogSql;
import java.util.List;
/**
* Persist request specifically for CallableSql.
*/
@@ -156,6 +159,14 @@ public final class PersistRequestUpdateSql extends PersistRequest {
*/
@Override
public void postExecute() {
if (sqlType != SqlType.SQL_INSERT && !transaction.isAutoPersistUpdates()) {
List<BeanDescriptor<?>> descriptors = server.descriptors(tableName);
if (descriptors != null) {
for (BeanDescriptor<?> descriptor : descriptors) {
descriptor.contextClear(transaction.persistenceContext());
}
}
}
if (startNanos > 0) {
persistExecute.collectSqlUpdate(label, startNanos);
}
@@ -1,11 +1,7 @@
package io.ebeaninternal.server.core;
import io.ebean.CallableSql;
import io.ebean.MergeOptions;
import io.ebean.Query;
import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.Update;
import io.avaje.lang.Nullable;
import io.ebean.*;
import io.ebean.bean.EntityBean;
import io.ebean.meta.MetricVisitor;
import io.ebeaninternal.api.SpiSqlUpdate;
@@ -31,9 +27,9 @@ public interface Persister {
void update(EntityBean entityBean, Transaction t);
/**
* Force an Insert using the given bean.
* Perform an Insert using the given bean.
*/
void insert(EntityBean entityBean, Transaction t);
void insert(EntityBean entityBean, @Nullable InsertOptions insertOptions, @Nullable Transaction t);
/**
* Insert or update the bean depending on its state.
@@ -44,7 +40,6 @@ public interface Persister {
* Delete a bean given it's type and id value.
* <p>
* This will also cascade delete one level of children.
* </p>
*/
int delete(Class<?> beanType, Object id, Transaction transaction, boolean permanent);
@@ -278,7 +278,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
this.partitionMeta = deploy.getPartitionMeta();
this.tablespaceMeta = deploy.getTablespaceMeta();
this.storageEngine = deploy.getStorageEngine();
this.autoTunable = beanFinder == null && (entityType == EntityType.ORM || entityType == EntityType.VIEW);
this.autoTunable = entityType == EntityType.ORM || entityType == EntityType.VIEW;
// helper object used to derive lists of properties
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
this.softDeleteProperty = listHelper.getSoftDeleteProperty();
@@ -2056,6 +2056,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
pc.clear(rootBeanType, idValue);
}
/**
* Clear a bean from the persistence context.
*/
public void contextClear(PersistenceContext pc) {
pc.clear(rootBeanType);
}
/**
* Delete a bean from the persistence context (such that we don't fetch it in the same transaction).
*/
@@ -106,6 +106,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private final String asOfViewSuffix;
private final boolean jacksonCorePresent;
private final int queryPlanTTLSeconds;
private final BindMaxLength bindMaxLength;
private int entityBeanCount;
private List<BeanDescriptor<?>> immutableDescriptorList;
/**
@@ -160,6 +161,19 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
this.changeLogListener = config.changeLogListener(bootupClasses.getChangeLogListener());
this.changeLogRegister = config.changeLogRegister(bootupClasses.getChangeLogRegister());
this.jacksonCorePresent = config.isJacksonCorePresent();
this.bindMaxLength = initMaxLength();
}
BindMaxLength initMaxLength() {
LengthCheck lengthCheck = this.config.getLengthCheck();
switch (lengthCheck) {
case OFF:
return null;
case UTF8:
return BindMaxLength.ofUtf8();
default:
return BindMaxLength.ofStandard();
}
}
@Override
@@ -399,7 +413,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
* Return the BeanDescriptors mapped to the table.
*/
public List<BeanDescriptor<?>> descriptors(String tableName) {
return tableToDescMap.get(tableName.toLowerCase());
return tableName == null ? Collections.emptyList() : tableToDescMap.get(tableName.toLowerCase());
}
/**
@@ -1501,6 +1515,10 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
return list;
}
public BindMaxLength bindMaxLength() {
return bindMaxLength;
}
/**
* Comparator to sort the BeanDescriptors by name.
*/
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.deploy;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.DataIntegrityException;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -40,8 +41,8 @@ import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
import io.ebeanservice.docstore.api.mapping.DocPropertyOptions;
import io.ebeanservice.docstore.api.support.DocStructure;
import jakarta.persistence.PersistenceException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@@ -166,6 +167,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
*/
private final String dbComment;
private final DbEncryptFunction dbEncryptFunction;
private final BindMaxLength bindMaxLength;
private int deployOrder;
final boolean jsonSerialize;
final boolean jsonDeserialize;
@@ -258,6 +260,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
}
this.jsonSerialize = deploy.isJsonSerialize();
this.jsonDeserialize = deploy.isJsonDeserialize();
this.bindMaxLength = deploy.bindMaxLength();
}
private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) {
@@ -345,6 +348,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn);
this.jsonSerialize = source.jsonSerialize;
this.jsonDeserialize = source.jsonDeserialize;
this.bindMaxLength = source.bindMaxLength;
}
/**
@@ -555,6 +559,18 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
@SuppressWarnings("unchecked")
public void bind(DataBind b, Object value) throws SQLException {
scalarType.bind(b, value);
if (bindMaxLength != null) {
Object obj = b.popLastObject();
long length = bindMaxLength.length(dbLength, obj);
if (length > dbLength) {
b.closeInputStreams();
String s = String.valueOf(value); // take original bind value here.
if (s.length() > 50) {
s = s.substring(0, 47) + "...";
}
throw new DataIntegrityException("Cannot bind value '" + s + "' (effective length=" + length + ") to column '" + dbColumn + "' (length=" + dbLength + ")");
}
}
}
@SuppressWarnings(value = "unchecked")
@@ -1166,14 +1182,14 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
* Returns true if this <code>isLob()</code> or the type will effectively map to a lob.
*/
@Override
public boolean isDbLob() {
public boolean isLobForPlatform() {
if (lob) {
return true;
}
switch (dbType) {
case DbPlatformType.JSON:
case DbPlatformType.JSONB:
return dbLength == 0; // must be analog to DbPlatformTypeMapping.lookup
return dbLength == 0 || dbLength > 4000; // must be analog to DbPlatformTypeMapping.lookup
case DbPlatformType.JSONBlob:
case DbPlatformType.JSONClob:
return true;
@@ -379,6 +379,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
for (ExportedProperty exportedProperty : exportedProperties) {
row.addColumn(exportedProperty.getForeignDbColumn());
}
if (importedId == null) {
throw new PersistenceException("Missing @Id property on bean related to ManyToMany " + fullName());
}
importedId.buildImport(row);
return row.build();
}
@@ -0,0 +1,90 @@
package io.ebeaninternal.server.deploy;
import io.ebean.core.type.InputStreamInfo;
import java.nio.charset.StandardCharsets;
/**
* The max length check on bind values.
*/
public interface BindMaxLength {
/**
* Return a UTF8 based implementation.
*/
static BindMaxLength ofUtf8() {
return new UTF8();
}
/**
* Return a standard implementation.
*/
static BindMaxLength ofStandard() {
return new Standard();
}
/**
* Return the length of the object.
*/
long length(int dbLength, Object obj);
/**
* Length check based on UTF8 bytes.
*/
final class UTF8 implements BindMaxLength {
@Override
public long length(int dbLength, Object obj) {
if (obj == null) {
return -1;
} else if (obj instanceof String) {
String s = (String) obj;
return utf8String(dbLength, s);
} else if (obj instanceof byte[]) {
return ((byte[]) obj).length;
} else if (obj instanceof InputStreamInfo) {
return ((InputStreamInfo) obj).length();
} else if ("org.postgresql.util.PGobject".equals(obj.getClass().getCanonicalName())) {
String value = ((org.postgresql.util.PGobject) obj).getValue();
return value == null ? -1 : utf8String(dbLength, value);
} else {
return -1;
}
}
private static int utf8String(int dbLength, String s) {
int stringLength = s.length();
if (stringLength > dbLength) {
return stringLength;
} else if (stringLength * 4 <= dbLength) {
return -1;
} else {
return s.getBytes(StandardCharsets.UTF_8).length;
}
}
}
/**
* Standard string length implementation.
*/
final class Standard implements BindMaxLength {
@Override
public long length(int dbLength, Object obj) {
if (obj == null) {
return -1;
} else if (obj instanceof String) {
return ((String) obj).length();
} else if (obj instanceof byte[]) {
return ((byte[]) obj).length;
} else if (obj instanceof InputStreamInfo) {
return ((InputStreamInfo) obj).length();
} else if ("org.postgresql.util.PGobject".equals(obj.getClass().getCanonicalName())) {
String value = ((org.postgresql.util.PGobject) obj).getValue();
return value == null ? -1 : value.length();
} else {
return -1;
}
}
}
}
@@ -63,7 +63,7 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase {
}
@Override
public boolean isDbLob() {
public boolean isLobForPlatform() {
return false;
}
@@ -1,10 +1,10 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.DatabaseBuilder;
import io.ebean.annotation.Cache;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.Identity;
import io.ebean.DatabaseBuilder;
import io.ebean.config.TableName;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
@@ -21,7 +21,6 @@ import io.ebeaninternal.server.idgen.UuidV1IdGenerator;
import io.ebeaninternal.server.idgen.UuidV1RndIdGenerator;
import io.ebeaninternal.server.idgen.UuidV4IdGenerator;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import jakarta.persistence.Entity;
import jakarta.persistence.MappedSuperclass;
@@ -139,6 +138,10 @@ public class DeployBeanDescriptor<T> {
this.beanType = beanType;
}
public BindMaxLength bindMaxLength() {
return manager.bindMaxLength();
}
private String[] readPropertyNames() {
try {
Field field = beanType.getField("_ebean_props");
@@ -1,14 +1,17 @@
package io.ebeaninternal.server.deploy.meta;
import io.avaje.lang.Nullable;
import io.ebean.annotation.*;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbEncrypt;
import io.ebean.config.dbplatform.DbEncryptFunction;
import io.ebean.config.dbplatform.ExtraDbTypes;
import io.ebean.core.type.ScalarType;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BindMaxLength;
import io.ebeaninternal.server.deploy.DbMigrationInfo;
import io.ebeaninternal.server.deploy.DeployDocPropertyOptions;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
@@ -1133,4 +1136,20 @@ public class DeployBeanProperty {
boolean isJsonType() {
return mutationDetection != null;
}
@Nullable
public BindMaxLength bindMaxLength() {
if (dbLength == 0) {
return null;
}
switch (dbType) {
case Types.VARCHAR:
case Types.BLOB:
case ExtraDbTypes.JSON:
return desc.bindMaxLength();
default:
return null;
}
}
}
@@ -99,6 +99,10 @@ final class AnnotationFields extends AnnotationParser {
if (formula != null) {
prop.setSqlFormula(processFormula(formula.select()), processFormula(formula.join()));
}
DbComment comment = get(prop, DbComment.class);
if (comment != null) {
prop.setDbComment(comment.value());
}
initWhoProperties(prop);
initDbMigration(prop);
}
@@ -81,7 +81,7 @@ final class BetweenPropertyExpression extends NonPrepareExpression {
@Override
public void addSql(SpiExpressionRequest request) {
request.append(" ?").append(BETWEEN).property(name(lowProperty)).append(" and ").property(name(highProperty));
request.append("?").append(BETWEEN).property(name(lowProperty)).append(" and ").property(name(highProperty));
}
@Override
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
/**
@@ -9,7 +8,7 @@ import io.ebeaninternal.server.expression.BitwiseOp;
abstract class BaseDbExpression implements DbExpressionHandler {
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
public void bitwise(DbExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
final String bitOp = bitOp(operator);
request.append('(').property(propName).append(' ').append(bitOp).append(" ? ").append(compare).append(" ?)");
}
@@ -28,7 +27,7 @@ abstract class BaseDbExpression implements DbExpressionHandler {
/**
* Common alternative where the bitwise operation is a function (specifically bitand is used - H2 and Oracle).
*/
protected void bitwiseFunction(SpiExpressionRequest request, String propName, BitwiseOp operator, String compare) {
protected void bitwiseFunction(DbExpressionRequest request, String propName, BitwiseOp operator, String compare) {
final String funcName = functionName(operator);
request.append(funcName).append('(').property(propName).append(", ?) ").append(compare).append(" ?");
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
/**
@@ -9,17 +8,17 @@ import io.ebeaninternal.server.expression.Op;
class BasicDbExpression extends BaseDbExpression {
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
public void json(DbExpressionRequest request, String propName, String path, Op operator, Object value) {
throw new RuntimeException("JSON expressions only supported on Postgres and Oracle");
}
@Override
public void arrayContains(SpiExpressionRequest request, String propName, boolean contains, Object... values) {
public void arrayContains(DbExpressionRequest request, String propName, boolean contains, Object... values) {
throw new RuntimeException("ARRAY expressions only supported on Postgres");
}
@Override
public void arrayIsEmpty(SpiExpressionRequest request, String propName, boolean empty) {
public void arrayIsEmpty(DbExpressionRequest request, String propName, boolean empty) {
throw new RuntimeException("ARRAY expressions only supported on Postgres");
}
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
import io.ebeaninternal.server.expression.Op;
@@ -12,22 +11,22 @@ public interface DbExpressionHandler {
/**
* Write the db platform specific json expression.
*/
void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value);
void json(DbExpressionRequest request, String propName, String path, Op operator, Object value);
/**
* Add SQL for ARRAY CONTAINS expression.
*/
void arrayContains(SpiExpressionRequest request, String propName, boolean contains, Object... values);
void arrayContains(DbExpressionRequest request, String propName, boolean contains, Object... values);
/**
* Add SQL for ARRAY IS EMPTY expression.
*/
void arrayIsEmpty(SpiExpressionRequest request, String propName, boolean empty);
void arrayIsEmpty(DbExpressionRequest request, String propName, boolean empty);
/**
* Add the bitwise expression.
*/
void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match);
void bitwise(DbExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match);
/**
* Performs a "CONCAT" operation for that platform.
@@ -0,0 +1,22 @@
package io.ebeaninternal.server.expression.platform;
/**
* Request building the expression sql.
*/
public interface DbExpressionRequest {
/**
* Append to the expression sql without any parsing.
*/
DbExpressionRequest append(String expression);
/**
* Append to the expression sql without any parsing.
*/
DbExpressionRequest append(char c);
/**
* Append to the expression sql with logical property parsing to db columns with logical path prefix.
*/
DbExpressionRequest property(String expression);
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
/**
@@ -9,7 +8,7 @@ import io.ebeaninternal.server.expression.BitwiseOp;
final class H2DbExpression extends BasicDbExpression {
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
public void bitwise(DbExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
final String funcName = functionName(operator);
request.append(funcName).append('(').property(propName).append(", cast(? as long)) ").append(compare).append(" cast(? as long)");
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
import io.ebeaninternal.server.expression.Op;
@@ -10,17 +9,17 @@ import io.ebeaninternal.server.expression.Op;
final class HanaDbExpression extends BaseDbExpression {
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
public void bitwise(DbExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
bitwiseFunction(request, propName, operator, compare);
}
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
public void json(DbExpressionRequest request, String propName, String path, Op operator, Object value) {
request.append("json_value(").property(propName).append(", '$.").append(path).append("')").append(operator.bind());
}
@Override
public void arrayIsEmpty(SpiExpressionRequest request, String propName, boolean empty) {
public void arrayIsEmpty(DbExpressionRequest request, String propName, boolean empty) {
request.append("cardinality(").property(propName).append(')');
if (empty) {
request.append(" = 0");
@@ -41,7 +40,7 @@ final class HanaDbExpression extends BaseDbExpression {
}
@Override
public void arrayContains(SpiExpressionRequest request, String propName, boolean contains, Object... values) {
public void arrayContains(DbExpressionRequest request, String propName, boolean contains, Object... values) {
for (int i = 0; i < values.length; i++) {
if (i > 0) {
request.append(" and ");
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
/**
@@ -9,7 +8,7 @@ import io.ebeaninternal.server.expression.Op;
final class MariaDbExpression extends BasicDbExpression {
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
public void json(DbExpressionRequest request, String propName, String path, Op operator, Object value) {
request.append('(').property(propName).append(" ->> '$.").append(path).append("')").append(operator.bind());
}
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
/**
@@ -9,7 +8,7 @@ import io.ebeaninternal.server.expression.Op;
final class MySqlDbExpression extends BasicDbExpression {
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
public void json(DbExpressionRequest request, String propName, String path, Op operator, Object value) {
request.append('(').property(propName).append(" ->> '$.").append(path).append("')").append(operator.bind());
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.BitwiseOp;
import io.ebeaninternal.server.expression.Op;
@@ -15,7 +14,7 @@ final class OracleDbExpression extends BaseDbExpression {
}
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
public void json(DbExpressionRequest request, String propName, String path, Op operator, Object value) {
if (operator == Op.EXISTS) {
request.append("json_exists(").property(propName).append(", '$.").append(path).append("')");
} else if (operator == Op.NOT_EXISTS) {
@@ -26,17 +25,17 @@ final class OracleDbExpression extends BaseDbExpression {
}
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
public void bitwise(DbExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
bitwiseFunction(request, propName, operator, compare);
}
@Override
public void arrayContains(SpiExpressionRequest request, String propName, boolean contains, Object... values) {
public void arrayContains(DbExpressionRequest request, String propName, boolean contains, Object... values) {
throw new IllegalStateException("ARRAY expressions not supported on Oracle");
}
@Override
public void arrayIsEmpty(SpiExpressionRequest request, String propName, boolean empty) {
public void arrayIsEmpty(DbExpressionRequest request, String propName, boolean empty) {
throw new IllegalStateException("ARRAY expressions not supported on Oracle");
}
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
/**
@@ -14,7 +13,7 @@ final class PostgresDbExpression extends BaseDbExpression {
}
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
public void json(DbExpressionRequest request, String propName, String path, Op operator, Object value) {
String[] paths = path.split("\\.");
if (paths.length == 1) {
// (t0.content ->> 'title') = 'Some value'
@@ -34,7 +33,7 @@ final class PostgresDbExpression extends BaseDbExpression {
}
@Override
public void arrayContains(SpiExpressionRequest request, String propName, boolean contains, Object... values) {
public void arrayContains(DbExpressionRequest request, String propName, boolean contains, Object... values) {
if (!contains) {
request.append("not (");
}
@@ -49,7 +48,7 @@ final class PostgresDbExpression extends BaseDbExpression {
}
@Override
public void arrayIsEmpty(SpiExpressionRequest request, String propName, boolean empty) {
public void arrayIsEmpty(DbExpressionRequest request, String propName, boolean empty) {
request.append("coalesce(cardinality(").property(propName).append("),0)");
if (empty) {
request.append(" = 0");
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.expression.platform;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
/**
@@ -9,19 +8,19 @@ import io.ebeaninternal.server.expression.Op;
final class SqlServerDbExpression extends BaseDbExpression {
@Override
public void json(final SpiExpressionRequest request, final String propName,
public void json(final DbExpressionRequest request, final String propName,
final String path, final Op operator, final Object value) {
request.append("json_value(").property(propName).append(", '$.").append(path).append("')").append(operator.bind());
}
@Override
public void arrayContains(final SpiExpressionRequest request, final String propName,
public void arrayContains(final DbExpressionRequest request, final String propName,
final boolean contains, final Object... values) {
throw new RuntimeException("ARRAY expressions not supported on Microsoft SQL Server");
}
@Override
public void arrayIsEmpty(final SpiExpressionRequest request, final String propName, final boolean empty) {
public void arrayIsEmpty(final DbExpressionRequest request, final String propName, final boolean empty) {
throw new RuntimeException("ARRAY expressions not supported on Microsoft SQL Server");
}
}
@@ -402,7 +402,13 @@ public final class DJsonContext implements SpiJsonContext {
private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) {
FetchPath pathProps = (options == null) ? null : options.getPathProperties();
Map<String, JsonWriteBeanVisitor<?>> visitors = (options == null) ? null : options.getVisitorMap();
return new WriteJson(server, gen, pathProps, visitors, determineObjectMapper(options), determineInclude(options));
return new WriteJson(server,
gen,
pathProps,
visitors,
determineObjectMapper(options),
determineInclude(options),
options == null || options.isIncludeLoadedImplicit());
}
private <T> void toJsonFromCollection(Collection<T> collection, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException {
@@ -490,4 +496,5 @@ public final class DJsonContext implements SpiJsonContext {
JsonConfig.Include include = options.getInclude();
return (include != null) ? include : defaultInclude;
}
}
@@ -31,12 +31,15 @@ public final class WriteJson implements SpiJsonWriter {
private final ArrayStack<Object> parentBeans;
private final Object objectMapper;
private final JsonConfig.Include include;
private final boolean includeLoadedImplicit;
/**
* Construct for full bean use (normal).
*/
public WriteJson(SpiEbeanServer server, JsonGenerator generator, FetchPath fetchPath,
Map<String, JsonWriteBeanVisitor<?>> visitors, Object objectMapper, JsonConfig.Include include) {
Map<String, JsonWriteBeanVisitor<?>> visitors, Object objectMapper, JsonConfig.Include include,
boolean includeLoadedImplicit) {
this.server = server;
this.generator = generator;
@@ -44,6 +47,7 @@ public final class WriteJson implements SpiJsonWriter {
this.visitors = visitors;
this.objectMapper = objectMapper;
this.include = include;
this.includeLoadedImplicit = includeLoadedImplicit;
this.parentBeans = new ArrayStack<>();
this.pathStack = new PathStack();
}
@@ -54,6 +58,7 @@ public final class WriteJson implements SpiJsonWriter {
public WriteJson(JsonGenerator generator, JsonConfig.Include include) {
this.generator = generator;
this.include = include;
this.includeLoadedImplicit = true;
this.visitors = null;
this.server = null;
this.fetchPath = null;
@@ -443,7 +448,7 @@ public final class WriteJson implements SpiJsonWriter {
currentIncludeProps = null;
}
}
return new WriteBean(desc, explicitAllProps, currentIncludeProps, bean, visitor);
return new WriteBean(desc, explicitAllProps, includeLoadedImplicit, currentIncludeProps, bean, visitor);
}
@Override
@@ -477,6 +482,7 @@ public final class WriteJson implements SpiJsonWriter {
public static class WriteBean {
final boolean explicitAllProps;
final boolean includeLoadedImplicit;
final Set<String> currentIncludeProps;
final BeanDescriptor<?> desc;
final EntityBean currentBean;
@@ -485,14 +491,15 @@ public final class WriteJson implements SpiJsonWriter {
final JsonWriteBeanVisitor visitor;
WriteBean(BeanDescriptor<?> desc, EntityBean currentBean, JsonWriteBeanVisitor<?> visitor) {
this(desc, false, null, currentBean, visitor);
this(desc, false, true, null, currentBean, visitor);
}
WriteBean(BeanDescriptor<?> desc, boolean explicitAllProps, Set<String> currentIncludeProps, EntityBean currentBean, JsonWriteBeanVisitor<?> visitor) {
WriteBean(BeanDescriptor<?> desc, boolean explicitAllProps, boolean includeLoadedImplicit, Set<String> currentIncludeProps, EntityBean currentBean, JsonWriteBeanVisitor<?> visitor) {
super();
this.desc = desc;
this.currentBean = currentBean;
this.explicitAllProps = explicitAllProps;
this.includeLoadedImplicit = includeLoadedImplicit;
this.currentIncludeProps = currentIncludeProps;
this.visitor = visitor;
}
@@ -507,9 +514,11 @@ public final class WriteJson implements SpiJsonWriter {
if (currentIncludeProps != null) {
// explicitly controlled by pathProperties
return currentIncludeProps.contains(prop.name());
} else {
} else if (includeLoadedImplicit){
// include only loaded properties
return currentBean._ebean_getIntercept().isLoadedProperty(prop.propertyIndex());
} else {
return prop.isId();
}
}
@@ -119,7 +119,11 @@ public final class Binder {
if (param.isEncryptionKey()) {
bindLog.append("****");
} else {
bindLog.append(value);
String sv = String.valueOf(value);
if (sv.length() > 50) {
sv = sv.substring(0, 47) + "...";
}
bindLog.append(sv);
}
}
if (value instanceof Collection) {
@@ -411,7 +411,7 @@ public final class DefaultPersister implements Persister {
if (bean._ebean_getIntercept().isUpdate()) {
update(bean, t);
} else {
insert(bean, t);
insert(bean, null, t);
}
}
@@ -419,12 +419,15 @@ public final class DefaultPersister implements Persister {
* Insert this bean.
*/
@Override
public void insert(EntityBean bean, Transaction t) {
public void insert(EntityBean bean, InsertOptions insertOptions, Transaction t) {
PersistRequestBean<?> req = createRequest(bean, t, PersistRequest.Type.INSERT);
if (req.isSkipReference()) {
// skip insert on reference bean
return;
}
if (insertOptions != null) {
req.setInsertOptions(insertOptions);
}
try {
req.initTransIfRequiredWithBatchCascade();
insert(req);
@@ -328,7 +328,7 @@ final class SaveManyBeans extends SaveManyBase {
}
private boolean isChangedProperty() {
return parentBean._ebean_getIntercept().isChangedProperty(many.propertyIndex());
return request.isChangedProperty(many.propertyIndex());
}
private void removeAssocManyOrphans() {
@@ -1,11 +1,15 @@
package io.ebeaninternal.server.persist.dml;
import java.util.ArrayList;
import java.util.List;
/**
* Helper to support the generation of DML statements.
*/
public final class GenerateDmlRequest {
private final StringBuilder sb = new StringBuilder(100);
private final List<String> columns = new ArrayList<>();
private StringBuilder insertBindBuffer;
private String prefix;
private String prefix2;
@@ -26,7 +30,7 @@ public final class GenerateDmlRequest {
++bindColumnCount;
sb.append(prefix);
sb.append(column);
//sb.append(expr);
columns.add(column);
if (insertMode > 0) {
if (insertMode++ > 1) {
insertBindBuffer.append(',');
@@ -75,4 +79,8 @@ public final class GenerateDmlRequest {
public boolean isUpdate() {
return insertMode == 0;
}
public List<String> columns() {
return columns;
}
}
@@ -69,7 +69,7 @@ public final class InsertHandler extends DmlHandler {
SpiTransaction t = persistRequest.transaction();
// get the appropriate sql
sql = meta.getSql(withId, persistRequest.isPublish());
sql = meta.sql(withId, persistRequest.isPublish(), persistRequest.insertOptions());
PreparedStatement pstmt;
if (persistRequest.isBatched()) {
pstmt = pstmtBatch(t, sql, persistRequest, useGeneratedKeys);
@@ -134,7 +134,8 @@ public final class InsertHandler extends DmlHandler {
if (idValue != null) {
persistRequest.setGeneratedKey(idValue);
}
} else {
} else if (persistRequest.insertOptions() == null) {
// insert on conflict do nothing can not return generated key
throw new PersistenceException("Autoincrement getGeneratedKeys() returned no rows?");
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.persist.dml;
import io.ebean.InsertOptions;
import io.ebean.annotation.Platform;
import io.ebean.bean.EntityBean;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -36,9 +37,11 @@ final class InsertMeta {
private final Bindable shadowFKey;
private final String[] identityDbColumns;
private final Platform platform;
private final InsertMetaOptions options;
InsertMeta(DatabasePlatform dbPlatform, BeanDescriptor<?> desc, Bindable shadowFKey, BindableId id, BindableList all) {
this.platform = dbPlatform.platform();
this.options = InsertMetaPlatform.create(platform, desc, this);
this.discriminator = discriminator(desc);
this.id = id;
this.all = all;
@@ -47,8 +50,8 @@ final class InsertMeta {
String tableName = desc.baseTable();
String draftTableName = desc.draftTable();
this.sqlWithId = genSql(false, tableName, false);
this.sqlDraftWithId = desc.isDraftable() ? genSql(false, draftTableName, true) : sqlWithId;
this.sqlWithId = sql(false, tableName, false);
this.sqlDraftWithId = desc.isDraftable() ? sql(false, draftTableName, true) : sqlWithId;
// only available for single Id property
if (id.isConcatenated()) {
@@ -72,8 +75,8 @@ final class InsertMeta {
this.supportsGetGeneratedKeys = dbPlatform.dbIdentity().isSupportsGetGeneratedKeys();
this.supportsSelectLastInsertedId = desc.supportsSelectLastInsertedId();
}
this.sqlNullId = genSql(true, tableName, false);
this.sqlDraftNullId = desc.isDraftable() ? genSql(true, draftTableName, true) : sqlNullId;
this.sqlNullId = sql(true, tableName, false);
this.sqlDraftNullId = desc.isDraftable() ? sql(true, draftTableName, true) : sqlNullId;
}
}
@@ -137,9 +140,20 @@ final class InsertMeta {
}
/**
* get the sql based whether the id value(s) are null.
* Return the sql for the given options.
*/
public String getSql(boolean withId, boolean publish) {
public String sql(boolean withId, boolean publish, InsertOptions insertOptions) {
if (insertOptions == null) {
return sql(withId, publish);
}
return options.sql(withId, insertOptions);
}
String sqlFor(boolean withId) {
return withId ? sqlWithId : sqlNullId;
}
private String sql(boolean withId, boolean publish) {
if (withId) {
return publish ? sqlWithId : sqlDraftWithId;
} else {
@@ -147,12 +161,18 @@ final class InsertMeta {
}
}
private String genSql(boolean nullId, String table, boolean draftTable) {
private String sql(boolean nullId, String table, boolean draftTable) {
GenerateDmlRequest request = new GenerateDmlRequest();
sql(request, nullId, table, draftTable);
return request.toString();
}
void sql(GenerateDmlRequest request, boolean nullId, String table, boolean draftTable) {
request.setInsertSetMode();
request.append("insert into ").append(table);
if (nullId && noColumnsForInsert(draftTable)) {
return request.append(defaultValues()).toString();
request.append(defaultValues());
return;
}
request.append(" (");
if (!nullId) {
@@ -172,7 +192,6 @@ final class InsertMeta {
request.append(") values (");
request.append(request.insertBindBuffer());
request.append(")");
return request.toString();
}
private String defaultValues() {
@@ -196,5 +215,4 @@ final class InsertMeta {
&& discriminator == null
&& (draftTable ? all.isEmpty() : allExcludeDraftOnly.isEmpty());
}
}
@@ -0,0 +1,14 @@
package io.ebeaninternal.server.persist.dml;
import io.ebean.InsertOptions;
/**
* Generator for insert SQL with options.
*/
interface InsertMetaOptions {
/**
* Generate the SQL for the given insert options.
*/
String sql(boolean withId, InsertOptions insertOptions);
}
@@ -0,0 +1,113 @@
package io.ebeaninternal.server.persist.dml;
import io.ebean.InsertOptions;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* Postgres specific generation of insert on conflict.
*/
final class InsertMetaOptionsPostgres implements InsertMetaOptions {
private final InsertMeta meta;
private final BeanDescriptor<?> desc;
private final String baseTable;
private final Map<String, String> sqlCache = new ConcurrentHashMap<>();
InsertMetaOptionsPostgres(InsertMeta meta, BeanDescriptor<?> desc) {
this.meta = meta;
this.desc = desc;
this.baseTable = desc.baseTable();
}
@Override
public String sql(boolean withId, InsertOptions options) {
String key = withId + options.key();
return sqlCache.computeIfAbsent(key, k -> generate(withId, options));
}
private String generate(boolean withId, InsertOptions options) {
char type = options.key().charAt(0);
switch (type) {
case 'U':
return generate(withId, false, options);
case 'N':
return generate(withId, true, options);
default:
return meta.sqlFor(withId);
}
}
private String generate(boolean withId, boolean doNothing, InsertOptions options) {
GenerateDmlRequest request = new GenerateDmlRequest();
meta.sql(request, !withId, baseTable, false);
request.append(" on conflict ");
List<String> uniqueColumns = desc.uniqueProps().stream()
.flatMap(Arrays::stream)
.map(BeanProperty::dbColumn)
.collect(Collectors.toList());
String constraintName = options.constraint();
if (constraintName != null) {
request.append("on constraint ").append(constraintName);
} else {
request.append("(");
String cols = options.uniqueColumns();
if (cols != null) {
request.append(cols);
} else {
appendUniqueColumns(uniqueColumns, request);
}
request.append(")");
}
if (doNothing) {
request.append(" do nothing");
return request.toString();
}
request.append(" do update set ");
String updateSet = options.updateSet();
if (updateSet != null) {
request.append(updateSet);
} else {
setColumns(withId, request, uniqueColumns);
}
return request.toString();
}
private void setColumns(boolean withId, GenerateDmlRequest request, List<String> uniqueColumns) {
List<String> columns = request.columns();
columns.removeAll(uniqueColumns);
if (withId) {
BeanProperty idProperty = desc.idProperty();
if (idProperty != null && !idProperty.isEmbedded()) {
columns.remove(idProperty.dbColumn());
}
}
for (int i = 0; i < columns.size(); i++) {
if (i > 0) {
request.append(", ");
}
String col = columns.get(i);
request.append(col).append("=excluded.").append(col);
}
}
private static void appendUniqueColumns(List<String> uniqueColumns, GenerateDmlRequest request) {
if (uniqueColumns.isEmpty()) {
throw new IllegalStateException("Unable to identify unique columns for INSERT ON CONFLICT - Add mapping like @Column(unique=true) or @Index(unique=true)");
}
for (int i = 0; i < uniqueColumns.size(); i++) {
if (i > 0) {
request.append(", ");
}
request.append(uniqueColumns.get(i));
}
}
}
@@ -0,0 +1,29 @@
package io.ebeaninternal.server.persist.dml;
import io.ebean.InsertOptions;
import io.ebean.annotation.Platform;
import io.ebeaninternal.server.deploy.BeanDescriptor;
final class InsertMetaPlatform {
private static final NotSupported NOT_SUPPORTED = new NotSupported();
static InsertMetaOptions create(Platform platform, BeanDescriptor<?> desc, InsertMeta meta) {
switch (platform.base()) {
case POSTGRES:
case YUGABYTE:
case COCKROACH:
return new InsertMetaOptionsPostgres(meta, desc);
default:
return NOT_SUPPORTED;
}
}
static final class NotSupported implements InsertMetaOptions {
@Override
public String sql(boolean withId, InsertOptions insertOptions) {
throw new UnsupportedOperationException("InsertOptions not supported on this database platform");
}
}
}
@@ -186,7 +186,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
this.queryMode = query.mode();
this.loadContextBean = queryMode.isLoadContextBean() || query.getForUpdateLockType() != null;
this.lazyLoadManyProperty = query.lazyLoadMany();
this.readOnly = request.isReadOnly();
this.readOnly = query.isReadOnly();
this.disableLazyLoading = query.isDisableLazyLoading();
this.objectGraphNode = query.parentNode();
this.profilingListener = query.profilingListener();
@@ -74,6 +74,9 @@ public final class CQueryEngine {
if (request.logSql()) {
request.logSql("{0}; --bind({1}) --micros({2}) --rows({3})", query.generatedSql(), query.bindLog(), query.micros(), rows);
}
if (rows > 0) {
request.clearContext();
}
return rows;
} catch (SQLException e) {
throw translate(request, query.bindLog(), query.generatedSql(), e);
@@ -61,6 +61,7 @@ final class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery
/**
* Execute the update or delete statement returning the row count.
*/
@SuppressWarnings("resource")
public int execute() throws SQLException {
long startNano = System.nanoTime();
try {
@@ -109,6 +110,7 @@ final class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery
pstmt = null;
}
@SuppressWarnings("resource")
@Override
public void profile() {
transaction()
@@ -305,6 +305,11 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public <A> Optional<A> findSingleAttributeOrEmpty() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public boolean isCountDistinct() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
@@ -51,6 +51,10 @@ public final class DefaultOrmQueryEngine implements OrmQueryEngine {
* Flushes the jdbc batch by default unless explicitly turned off on the transaction.
*/
private <T> void flushJdbcBatchOnQuery(OrmQueryRequest<T> request) {
if (request.query().isUsingFuture()) {
// future queries never invoke a flush
return;
}
SpiTransaction t = request.transaction();
if (t.isFlushOnQuery()) {
// before we perform a query, we need to flush any
@@ -33,7 +33,7 @@ public interface STreeProperty extends ScalarDataReader<Object> {
/**
* Returns true, if this is a lob property from db-perspective.
*/
boolean isDbLob();
boolean isLobForPlatform();
/**
* Return true if the property is an embedded type.
@@ -48,7 +48,7 @@ public final class SqlTreeBuilder {
private final SpiQuery.TemporalMode temporalMode;
private SqlTreeNode rootNode;
private boolean sqlDistinct;
private final boolean distinctNoLobs;
private final boolean platformDistinctNoLobs;
private final SqlTreeCommon common;
/**
@@ -63,7 +63,7 @@ public final class SqlTreeBuilder {
this.query = null;
this.subQuery = false;
this.distinctOnPlatform = false;
this.distinctNoLobs = false;
this.platformDistinctNoLobs = false;
this.queryDetail = queryDetail;
this.predicates = predicates;
this.temporalMode = SpiQuery.TemporalMode.CURRENT;
@@ -98,7 +98,7 @@ public final class SqlTreeBuilder {
this.predicates = predicates;
this.alias = new SqlTreeAlias(request.baseTableAlias(), temporalMode);
this.distinctOnPlatform = builder.isPlatformDistinctOn();
this.distinctNoLobs = builder.isPlatformDistinctNoLobs();
this.platformDistinctNoLobs = builder.isPlatformDistinctNoLobs();
String fromForUpdate = builder.fromForUpdate(query);
CQueryHistorySupport historySupport = builder.historySupport(query);
CQueryDraftSupport draftSupport = builder.draftSupport(query);
@@ -269,8 +269,8 @@ public final class SqlTreeBuilder {
if (joinList != null) {
joinList.add(selectNode);
}
if (sqlDistinct && distinctNoLobs) {
selectNode.unselectLobs();
if (sqlDistinct && platformDistinctNoLobs) {
selectNode.unselectLobsForPlatform();
}
return selectNode;
}
@@ -82,6 +82,6 @@ interface SqlTreeNode {
/**
* Unselect lobs (for distinct queries on DB2 and Oracle).
*/
default void unselectLobs() {
default void unselectLobsForPlatform() {
}
}
@@ -379,16 +379,16 @@ class SqlTreeNodeBean implements SqlTreeNode {
@Override
public void unselectLobs() {
public void unselectLobsForPlatform() {
if (children != null) {
for (SqlTreeNode child : children) {
child.unselectLobs();
child.unselectLobsForPlatform();
}
}
if (hasLob()) {
List<STreeProperty> lst = new ArrayList<>();
for (STreeProperty prop : properties) {
if (!prop.isDbLob()) {
if (!prop.isLobForPlatform()) {
lst.add(prop);
}
}
@@ -399,7 +399,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
private boolean hasLob() {
for (STreeProperty prop : properties) {
if (prop.isDbLob()) {
if (prop.isLobForPlatform()) {
return true;
}
}
@@ -9,8 +9,10 @@ import io.ebeaninternal.api.*;
import io.ebeaninternal.server.dto.DtoBeanDescriptor;
import io.ebeaninternal.server.dto.DtoMappingRequest;
import io.ebeaninternal.server.dto.DtoQueryPlan;
import io.ebeaninternal.server.transaction.ExternalJdbcTransaction;
import javax.annotation.Nullable;
import java.sql.Connection;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@@ -86,6 +88,12 @@ public final class DefaultDtoQuery<T> extends AbstractQuery implements SpiDtoQue
return this;
}
@Override
public DtoQuery<T> usingConnection(Connection connection) {
this.transaction = new ExternalJdbcTransaction(connection);
return this;
}
@Override
public DtoQuery<T> usingMaster() {
this.useMaster = true;
@@ -58,6 +58,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
private Type type;
private String label;
private Mode mode = Mode.NORMAL;
private boolean usingFuture;
private Object tenantId;
/**
* Holds query in structured form.
@@ -971,6 +972,16 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
this.mode = mode;
}
@Override
public final void usingFuture() {
this.usingFuture = true;
}
@Override
public final boolean isUsingFuture() {
return usingFuture;
}
@Override
public final boolean isUsageProfiling() {
return usageProfiling;
@@ -1161,7 +1172,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
// add the rawSql statement - if any
if (orderByIsEmpty()) {
if (rawSql != null && rawSql.getSql() != null) {
order(rawSql.getSql().getOrderBy());
orderBy(rawSql.getSql().getOrderBy());
}
}
if (checkPagingOrderBy()) {
@@ -1517,7 +1528,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
}
@Override
@SuppressWarnings("unchecked")
public final <A> List<A> findSingleAttributeList() {
return server.findSingleAttributeList(this);
}
@@ -1533,6 +1543,11 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
return !list.isEmpty() ? list.get(0) : null;
}
@Override
public final <A> Optional<A> findSingleAttributeOrEmpty() {
return Optional.ofNullable(findSingleAttribute());
}
@Override
public final T findOne() {
return server.findOne(this);
@@ -7,7 +7,9 @@ import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiSqlQuery;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.transaction.ExternalJdbcTransaction;
import java.sql.Connection;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
@@ -50,6 +52,12 @@ public final class DefaultRelationalQuery extends AbstractQuery implements SpiSq
return this;
}
@Override
public SqlQuery usingConnection(Connection connection) {
this.transaction = new ExternalJdbcTransaction(connection);
return this;
}
@Override
public SqlQuery usingMaster() {
this.useMaster = true;

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