Compare commits

...
Author SHA1 Message Date
Rob Bygrave 4bcfa80df2 Merge pull request #3594 from ebean-orm/feature/improved-readOnly-immutable-DisabledLazyLoad
Modify disableLazyLoad to throw LazyInitialisationException instead o…
2025-04-29 23:32:46 +12:00
Rob Bygrave c583879d57 Modify disableLazyLoad to throw LazyInitialisationException instead of returning null
Modify such that accessing an unloaded property throws a LazyInitialisationException. This includes ToMany collections that where previously initialised as empty collections (that didn't lazy load).

This change brings the disableLazyLoad behaviour in line with the unmodifiable behaviour.

It does mean that the use case of bulk mapping from entities to DTOs via something like MapStruct will no longer work nicely (mapping nulls and empty lists versus LazyInitialisationException).
2025-03-18 23:46:42 +13:00
Rob Bygrave 6d791c7b50 Add BeanAccessException extends UnsupportedOperationException
And:

LazyInitialisationException extends BeanAccessException ...
UnmodifiableEntityException extends BeanAccessException ...
2025-03-18 22:54:34 +13:00
Rob Bygrave 1afe42f4c6 Merge branch 'master' into feature/improved-readOnly-immutable 2025-03-18 22:43:00 +13:00
Rob Bygrave 4e7f3c13a0 Merge pull request #3592 from ebean-orm/feature/improved-readOnly-immutable-part2
Remove readOnly, migrate to unmodifiable (or use normal mutable)
2025-03-18 22:41:12 +13:00
Rob Bygrave d7366dec0f Tidy readOnly removal 2025-03-18 20:59:04 +13:00
Rob Bygrave ceee1d7489 Remove readOnly from Query 2025-03-18 20:49:26 +13:00
Rob Bygrave 1b23319fa4 Remove readOnly from EntityBeanIntercept and BeanCollection 2025-03-18 20:32:22 +13:00
Rob Bygrave 6f726973ea Remove most internal use of readOnly 2025-03-18 19:53:13 +13:00
Rob Bygrave 82284b19d5 Bean cache sharable instances as unmodifiable + bean cache queries can also be unmodifiable
Entities without *any* relationships can support "sharable instances" with bean caching. This change restores that and uses unmodifiable instances (rather than the old ReadOnly).

Currently, for a query to get shared instances it now needs to explicitly use setUnmodifiable(true) where as before it defaulted to shared instances so that is a behaviour change.

Additionally, bean cache queries were not honouring setUnmodifiable(true) and with these changes they now do.
2025-03-16 23:19:54 +13:00
Rob Bygrave 798d01abd1 LazyInitialisationException, plus javadoc 2025-03-16 21:12:25 +13:00
Rob Bygrave 5136a5a708 Support unmodifiable Embedded and EmbeddedId 2025-03-13 22:52:45 +13:00
Rob Bygrave fa25d7cc8a Using Query Cache now implies unmodifiable
- Query Cache now holds unmodifiable collections
- Can no longer have readOnly=false with queryCache=true
- Reference beans now also honor unmodifiable
- Effectively no longer does bean cache lookup for reference beans (which it defaulted to when cacheSharableBeans true, e.g. Country entity bean)
2025-03-11 23:50:34 +13:00
Rob Bygrave c13e09257c Version 14.10.0 2025-03-11 21:26:00 +13:00
Rob Bygrave ed65433640 Merge pull request #3574 from ebean-orm/feature/3459-postgres-varchar
#3459 Postgres varchar default to no length (aka text)
2025-03-11 20:01:26 +13:00
Rob Bygrave 08890e9d5a Merge pull request #3590 from ebean-orm/feature/3580-lookwriter-logWarn
Adjust LookupWriter to log at WARN rather than ERROR due to Quarkus dev mode
2025-03-10 23:56:19 +13:00
Rob Bygrave 4273c4af6a Adjust LookupWriter to log at WARN rather than ERROR due to Quarkus dev mode
Die to Quarkus dev mode (#3541 #3582) we want to log FilerException at WARN level rather than ERROR. This is because Quarkus dev mode will invoke the annotation processor more than usual, so we expect to fail with the FilerException.
2025-03-10 23:42:38 +13:00
Rob Bygrave 51424d9eea Merge pull request #3580 from SentryMan/feature/lookup-generation
[querybean-generator] Generate lookup for modules
2025-03-10 23:31:35 +13:00
Rob Bygrave cf2ab33903 Merge pull request #3589 from ebean-orm/feature/bump-ebean-agent-dependency
Bump ebean-agent to 14.10.0
2025-03-10 23:28:05 +13:00
Rob Bygrave 90d8b4fb04 Bump ebean-agent to 14.10.0 2025-03-10 23:27:02 +13:00
Rob Bygrave ff4eeff264 Merge pull request #3588 from ebean-orm/feature/3586-querybean-gen
[query bean generation] #3586 public constructor for Embedded bean
2025-03-10 08:02:03 +13:00
Rob Bygrave 64ba343e35 [query bean generation] #3586 public constructor for Embedded bean 2025-03-10 07:59:33 +13:00
Rob Bygrave af3c59c0fd Merge pull request #3587 from ebean-orm/feature/bump-test-containers-77
Bump ebean-test-containers dependency - improved SQL Server handling
2025-03-10 07:40:31 +13:00
Rob Bygrave ae55d28026 Bump ebean-test-containers dependency - improved SQL Server handling
Improvments around collation changes that end up making SQL Server startup flaky
2025-03-10 07:40:02 +13:00
Rob Bygrave ddffc8eee2 Merge pull request #3581 from apflieger/#3573-initfields-prevent-secondary-query
OneToMany initialisation via List.of() - was Tests only - new test on multiple many joins
2025-03-10 07:30:40 +13:00
Rob Bygrave 7f1a3a46cf #3581 ebean-agent 14.10.0 handling for OneToMany initialisation 2025-03-10 07:12:20 +13:00
Rob Bygrave a59f75a468 Merge pull request #3585 from nedge/feature/Create_test_entities
Simple Test Entities in preparation for type scoped loading
2025-03-05 08:06:39 +13:00
Eddie Mc Greal 6f1c66ecd6 Simple Test Entities in preparation for type scoped loading 2025-03-04 14:17:08 +01:00
Rob Bygrave 25f6fb6d6f test only - Almost fix the model for test_manyNonRoot_RootHasNoMany
The test was using field access??? when that isn't valid unless we specifically turn on enhancement to support that.

Using proper method access though stops the test from failing.
2025-03-03 21:42:15 +13:00
Rob Bygrave 852def8536 test only - use assertThat hasSize instead of assertEquals for test_manyNonRoot_RootHasNoMany 2025-03-03 21:34:22 +13:00
Rob Bygrave dfb29fd75f Merge pull request #3578 from mkurz/fix_3577
Work around `mysqldump` bug by moving comment one line up
2025-03-03 21:26:40 +13:00
Rob Bygrave 80c3501197 Merge pull request #3583 from ebean-orm/feature/revert-3541-3582
[querybean-generator] #3582 - Revert #3541 "FilerException trying to write EntityClassRegister with Quarkus dev mode / hot reload "
2025-03-03 21:25:39 +13:00
Rob Bygrave b4ff5748fc Merge pull request #3579 from FOCONIS/rollbac-or-commit-before-close
Rollback/Commit raw connections before close
2025-03-03 21:25:12 +13:00
Rob Bygrave 64f01f0543 [querybean-generator] #3582 - Revert #3541 "FilerException trying to write EntityClassRegister with Quarkus dev mode / hot reload "
This reverts commit 69c25309 and applies the required "Fix" in the initModuleInfoBean() method to catch FilerException and log it at WARN level
2025-03-03 20:49:19 +13:00
Arnaud Pflieger fee6e0f480 Tests only - new test on multiple many joins
This test reproduce the problem of #3573
The field initialization `= List.of();` prevents eager secondary query.
2025-02-28 12:20:18 +01:00
Josiah Noel 50b44c4529 Update LookupWriter.java 2025-02-28 00:29:03 -05:00
Josiah Noel f0c2ba0e28 [Feature] Generate lookup for modules
Generate Lookup Class for modular projects
2025-02-27 20:47:16 -05:00
Roland Praml 7643dc9b22 Rollback/Commit raw transactions before close 2025-02-27 10:56:41 +01:00
Matthias Kurz c49a1b3d71 Work around mysqldump bug by moving comment one line up
It seems like mysqldump just adds ;; to the last line and does not
check if that line is a comment. To work around that it's good enough
to move the comment one line up - as long as the END does not end with ;
(which here it does not so that is ok)
2025-02-26 15:36:20 +01:00
Rob Bygrave 11e783938c Merge pull request #3576 from ebean-orm/feature/bump-ebean-test-containers
#3511 bump ebean-test-containers dependency
2025-02-26 23:18:43 +13:00
Rob Bygrave 3845861e24 test only - Fix tests due to automatic inline comment included in generated sql like:
select /* TestQueryDefaultBatchSize.test_findEach_details__lazy */ t0.order_id, t0.id,

select /* TestQueryDefaultBatchSize.test_findEach_details__lazy */ t0.order_id
2025-02-26 23:15:51 +13:00
Rob Bygrave d04181865c test only - update TestOrderByWithMany which is flaky 2025-02-26 23:10:55 +13:00
Rob Bygrave 00d7db31d6 test only - update TestQueryFindIterate which is flaky 2025-02-26 23:07:53 +13:00
Rob Bygrave 6880b5673a #3511 bump ebean-test-containers dependency
ebean-test-containers version 7.6 supports extraDb for MySql and MariaDb, resolving #3511 with extraDb configuration for each extra schema/database required with MySql or MariaDb.
2025-02-26 22:24:05 +13:00
Rob Bygrave e18b59a9dd Remove deprecated ExtendedServer.setClock() migrate to DatabaseBuilder.clock() (#3566)
So we can no longer dynamically change the Clock being used and instead for testing purposes need to create the Database with a test clock instance as needed.

Refer to the ExtendedServerTest for an example
2025-02-26 22:16:12 +13:00
Rob Bygrave cc48dced09 #3572 DDL support for non-nullable JoinColumn column (#3575) 2025-02-26 21:13:50 +13:00
Rob Bygrave b13bdc95be #3459 Postgres varchar default to no length (aka text)
Change Postgres platform to default a String to varchar [with no length defined] rather than varchar(255)
2025-02-26 18:53:08 +13:00
Rob Bygrave 0a5c3ced21 Test only - update TestQueryMultiManyOrder sql asserts
No effective change.
2025-02-26 00:59:57 +13:00
Rob Bygrave 8ff5f7f72a Recursive freeze on entity beans
Noting that the freeze needs to occur after secondary queries have executed
2025-02-26 00:00:07 +13:00
Rob Bygrave 71944bb8fa Rename flags -> loaded, add message with property name to UnmodifiableEntityException 2025-02-23 22:25:46 +13:00
Rob Bygrave 24888fc01e Change InterceptReadWrite to use UnmodifiableEntityException
Add unmodified to DefaultOrmQuery query plan description
Fix incorrect merge conflict
2025-02-21 22:24:48 +13:00
Rob Bygrave 645d5bb613 Merge branch 'master' into feature/improved-readOnly-immutable 2025-02-21 22:08:38 +13:00
Rob Bygrave 4a6f1a39e0 Add UnloadedPropertyException and UnmodifiableEntityException, change existing use of IllegalStateException
- Adds new exceptions UnloadedPropertyException and UnmodifiableEntityException
- Change InterceptReadOnly to use these exception instead of IllegalStateException
- Change collections BeanSet, BeanList, BeanMap from using IllegalStateException to UnsupportedOperationException [to bring these in line with JDK unmodifiable collections]
2025-02-21 22:05:49 +13:00
Rob Bygrave 40637bd68c Allow OneToOne to map to ManyToOne (#3569) 2025-02-19 20:00:15 +13:00
Rob BygraveandJonas Pöhler 8dd438be61 #3564 Fix for nested transaction with batch-mode does not flush (#3565)
* ADD: Testcase for showing nested transaction with batch-mode not flushing batch on commit

* Update test only - TestNestedTransaction.test_txn_nestedWithInnerBatch()

Update the test to show that we desire the nested transaction changes to be "visible" after a commit regardless of flushOnQuery etc

* #3564 Fix for nested transaction with batch-mode does not flush

- Adds flush() into ScopeTrans.commitTransaction()
- Related fixes in DefaultServer execute() and executeCall() where it was performing extra unnecessary nesting on PersistenceException

* Touch

---------

Co-authored-by: Jonas Pöhler <jonas.poehler@foconis.de>
2025-02-18 07:33:48 +13:00
Rob Bygrave 5e1ad29c5d Build - add workflow_dispatch trigger to main build 2025-02-17 07:46:20 +13:00
Rob Bygrave 9eeb137907 Tests only - update test-kotlin dependency 2025-02-17 07:33:56 +13:00
Rob Bygrave 578a041c9e Version 14.9.0 2025-02-12 19:56:14 +13:00
Rob Bygrave b0b2a0d320 Bump ebean-agent to 14.9.0 with module path support for @Transactional 2025-02-12 19:36:48 +13:00
Josiah NoelandRob Bygrave de81dfc067 Feature: Add a MethodHandles.Lookup SPI (#3535)
* ServiceLoad lookups

* remove more reflection

* address comments

* Update DefaultTypeManager.java

* Javadoc and format changes

---------

Co-authored-by: Rob Bygrave <robin.bygrave@gmail.com>
2025-02-12 19:30:05 +13:00
Rob Bygrave 2bd53cb52b Merge pull request #3560 from ebean-orm/feature/3533-aop-transactional
#3533 Support @Transactional on module path
2025-02-12 18:53:57 +13:00
Rob Bygrave a6d792de64 Merge pull request #3563 from ebean-orm/feature/remove-deprecated-queryOrder
Remove deprecated order() migrate to orderBy()
2025-02-12 18:52:53 +13:00
Rob Bygrave c73d83ef33 Merge pull request #3562 from ebean-orm/feature/remove-deprecated-queryUpdTxn
Remove deprecated query.update(txn) & query.delete(txn)
2025-02-12 18:52:18 +13:00
Rob Bygrave 9f1b4ba758 Remove deprecated order() migrate to orderBy() 2025-02-12 18:47:16 +13:00
Rob Bygrave 1f7117da25 Merge pull request #3561 from ebean-orm/feature/remove-deprecated-servletContextListener
Remove deprecated ServletContextListener
2025-02-12 18:43:55 +13:00
Rob Bygrave f0ceab43bf Remove deprecated query.update(txn) & query.delete(txn)
Migrate to query.usingTransaction(txn)
2025-02-12 18:42:23 +13:00
Rob Bygrave 1107e4ea4c Remove deprecated ServletContextListener 2025-02-12 18:38:17 +13:00
Rob Bygrave 4dd8a5dfea Merge pull request #3558 from ebean-orm/feature/3557-filterMany-removal
#3557 [Query beans] Remove deprecated filterMany(String expressions, Object... params) from generated query beans
2025-02-12 18:01:39 +13:00
Rob Bygrave 195edf0355 Merge pull request #3514 from ebean-orm/feature/querybean-txn-ctor
Remove deprecated QueryBean constructor that takes transaction, migrate to query.usingTransaction()
2025-02-12 18:01:03 +13:00
Rob Bygrave e9c438adab Merge branch 'master' into feature/querybean-txn-ctor 2025-02-12 15:05:57 +13:00
Rob Bygrave e45cf5fab9 #3533 Bump ebean-agent to use the new AOPTransactionScope 2025-02-12 00:27:07 +13:00
Rob Bygrave 1bf9d1dd4f Merge pull request #3559 from ebean-orm/feature/findCount-noId
FindCount on entity bean without @Id + platform wants column alias
2025-02-12 00:01:24 +13:00
Rob Bygrave 657920c4a2 #3533 Support @Transactional on module path
As HelpScopeTrans isn't in an exported package it's use fails with an error in module path. This adds AOPTransactionScope in the exported package io.ebean.plugin and moves the required methods from SpiEbeanServer to SpiServer to support this move.

This also requires an associate change in ebean-agent to support this feature which will be in ebean-agent 14.9.0.
2025-02-11 23:57:18 +13:00
Rob Bygrave 6c47107436 FindCount on entity bean without @Id + platform wants column alias
Internally in CQueryBuilder only use query.setSelectId(); for findCount when the entity bean has a @Id property [which it doesn't strictly need and likely won't have for view based entities]
2025-02-11 23:04:02 +13:00
Rob Bygrave 88cf4e001a #3557 [Query beans] Remove deprecated filterMany(String expressions, Object... params) from generated query beans, migrate to filterManyRaw() 2025-02-11 22:06:20 +13:00
Rob Bygrave 7f1fcbcf92 Merge pull request #3556 from ebean-orm/feature/test-containers-bump
Bump ebean-test-containers to 7.5
2025-02-10 21:55:18 +13:00
Rob Bygrave 95d5d5e467 Bump ebean-test-containers to 7.5 2025-02-10 21:54:54 +13:00
Rob Bygrave 2dde23d577 Merge pull request #3555 from ebean-orm/feature/3503-kotlin-querybean-gen-DbMap
#3503 - Fix kotlin-querybean-generator for use of @DbMap
2025-02-10 14:01:09 +13:00
Rob Bygrave 1ec26d13f9 #3503 - Fix kotlin-querybean-generator for use of @DbMap 2025-02-10 11:35:44 +13:00
Rob Bygrave bb1b55e897 Reduce frequency of running yugabyte CI test 2025-02-09 22:45:10 +13:00
Rob Bygrave fb07447df4 Fix test only - test-kotlin bump kotlin-querybean-generator version 2025-02-09 22:43:02 +13:00
Rob Bygrave ad18b6573c Version 14.8.2 2025-02-09 22:07:28 +13:00
Rob Bygrave 11632c3a6e Merge pull request #3554 from ebean-orm/feature/3536-kotlin-querybean-generator
#3536 kotlin-querybean-generator update for filterMany() changes
2025-02-09 21:37:38 +13:00
Rob Bygrave b066f9c072 #3536 kotlin-querybean-generator update for filterMany() changes
Brings the kotlin-querybean-generator up to date with the [java] querybean-generator in terms of how it has specific "associated beans" for ToOne and ToMany type relationships
2025-02-09 21:24:51 +13:00
Rob Bygrave 1e38ed9efc Test only, add Embedded bean as an inner class for querybean-generation 2025-02-09 21:19:39 +13:00
Roland Praml 2d48dd2fef Pass create/configOptions to DB2 test container (#3550)
This allows to pass collation/pagesize settings via the
`ebean.test.createOptions=` property to the container
2025-02-08 19:47:20 +13:00
Rob Bygrave 60aa899a34 #3552 Column alias required for SQL Server + findCount + distinct (#3553)
* #3552 Column alias required for SQL Server + findCount + distinct

- Modifies CQueryBuilder with the main fix which is to use the database platform selectCountWithColumnAlias for all distinct queries [and later disable it for single column case]

- Modifies SqlTreeBuilder because we need to NOT use the column alias for the count distinct SINGLE COLUMN case. So change here to:
  - build the root SqlTreeNode first as part of constructor
  - use rootNode.isSingleProperty() to NOT use column alias for the single column case
  - Hence need to initialise DefaultDbSqlContext AFTER the root SqlTreeNode

  - Modifies DefaultDbSqlContext with no effective change, just columnIndex++ ONLY when we are actually using column alias

 - Modifies H2Platform to also use selectCountWithColumnAlias true

* #3552 selectCountWithColumnAlias true for MariaDB and MySql

Plus fix TestRowCount for Oracle platform
2025-02-08 19:33:03 +13:00
Rob Bygrave c38c75fdec Merge pull request #3549 from ebean-orm/feature/3546
#3546 Reduce compiler warning on query bean generated code
2025-01-28 23:34:23 +13:00
Rob Bygrave cf275f4edd #3546 Reduce compiler warning on query bean generated code
This method is effectively internal and called by generated code that also is Deprecated(forRemoval = true)
2025-01-28 23:31:23 +13:00
Rob Bygrave 121f4afed8 #3545 Support @SQL entity with a OneToOne with EmbeddedId
- @Sql entity
- Has a OneToOne or ManyToOne to a bean that uses @EmbeddedId
2025-01-28 23:26:09 +13:00
Rob Bygrave c8621c3a29 Put back the querybean-generator version 2025-01-16 21:42:02 +13:00
Rob Bygrave f83da15d5e Merge pull request #3542 from ebean-orm/feature/3542-quarkus
[querybean-generator] FilerException trying to write EntityClassRegister with Quarkus dev mode / hot reload #3541
2025-01-16 16:46:46 +13:00
Rob Bygrave 52729d9802 Missing import and adjust warning message 2025-01-16 16:40:43 +13:00
Rob Bygrave 69c25309da [querybean-generator] FilerException trying to write EntityClassRegister with Quarkus dev mode / hot reload #3541
Drop to log at WARN level when we catch FilerException such that Quarkus dev mode / hot reload works
2025-01-16 16:31:52 +13:00
Rob Bygrave 723a12fd66 #3538 Restoring batchMode etc with nested transaction scopes
This change is a simplification such that these batch attributes are always stored and restored for nested ScopeTrans.

There are no expensive side effects of doing it this way.

This fixes issues where the batch attributes are changed after the ScopeTrans is created (like the test for this PR).
2025-01-16 08:15:22 +13:00
Noemi Praml 5cf74f1ee9 Add: failing Unittest 2025-01-08 14:43:49 +01:00
Rob Bygrave 1fef4f7c11 Add explicit query.setUnmodifiable(true) [for readOnly=true + disableLazyLoad=true]
Although this is ok, makes me think that another option is just to have query.setReadOnly(true)
to mean ... readOnly + disableLazyLoad + error reading unloaded property or collection. As in,
readOnly true without these extra things does not that good [as in the existing readOnly does
not seem very good/safe/useful to use].
2024-11-20 23:01:59 +13:00
Rob Bygrave ddb4b9d3e5 Remove deprecated QueryBean constructor that takes transaction, migrate to query.usingTransaction()
Remove the constructor for QueryBean that takes transaction. Migrate code to use query.usingTransaction() instead.
2024-10-31 18:22:43 +13:00
Rob Bygrave e6f92eaceb ReadOnly Immutable query WIP 2024-10-24 16:02:50 +13:00
357 changed files with 3250 additions and 2620 deletions
+1
View File
@@ -2,6 +2,7 @@
name: Build
on:
workflow_dispatch:
pull_request:
push:
branches: master
+1 -1
View File
@@ -4,7 +4,7 @@ name: Yugabyte
on:
workflow_dispatch:
schedule:
- cron: '10 3 * * *'
- cron: '10 3 * * 3'
jobs:
build:
-5
View File
@@ -59,11 +59,6 @@ Work at the highest level of abstraction and drop down levels as needed.
<img width="222px" src="https://group.foconis.com/download/ci/logo/png-72dpi/logo-quer/foconis-analytics-quer.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.payintech.com/" target="_blank">
<img width="222px" src="https://ebean.io/images/sponsor_PayinTech-logo-noir.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.premium-minds.com" target="_blank">
<img width="222px" src="https://ebean.io/images/logo-med-principal.png">
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-net-postgis-types</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -41,7 +41,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -60,13 +60,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
</parent>
<artifactId>composites</artifactId>
+1 -18
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
</parent>
<name>ebean api</name>
@@ -86,23 +86,6 @@
<optional>true</optional>
</dependency>
<!-- JAVAX-DEPENDENCY-START ___
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<optional>true</optional>
</dependency>
____ JAVAX-DEPENDENCY-END -->
<!-- JAKARTA-DEPENDENCY-START -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<optional>true</optional>
</dependency>
<!-- JAKARTA-DEPENDENCY-END -->
</dependencies>
<build>
@@ -0,0 +1,25 @@
package io.ebean;
/**
* Unsupported access of a property on an entity bean.
* <p>
* Attempted a lazy load operation on a bean that has disabled lazy loading
* or attempt to mutate an unmodifiable bean.
*/
public class BeanAccessException extends UnsupportedOperationException {
private static final long serialVersionUID = 1;
/**
* Create with no message.
*/
public BeanAccessException() {
super();
}
/**
* Create with message.
*/
public BeanAccessException(String message) {
super(message);
}
}
@@ -89,12 +89,7 @@ public interface BeanState {
* <p>
* If a setter is called on a readOnly bean it will throw an exception.
*/
boolean isReadOnly();
/**
* Set the readOnly status for the bean.
*/
void setReadOnly(boolean readOnly);
boolean isUnmodifiable();
/**
* Advanced - Used to programmatically build a partially or fully loaded
+1 -1
View File
@@ -605,7 +605,7 @@ public final class DB {
* // find orders and their customers
* List<Order> list = DB.find(Order.class)
* .fetch("customer")
* .order("id")
* .orderBy("id")
* .findList();
*
* // sort by customer name ascending, then by order shipDate
+1 -13
View File
@@ -400,7 +400,7 @@ public interface Database {
* // find orders and their customers
* List<Order> list = database.find(Order.class)
* .fetch("customer")
* .order("id")
* .orderBy("id")
* .findList();
*
* // sort by customer name ascending, then by order shipDate
@@ -772,18 +772,6 @@ public interface Database {
*/
<T> T reference(Class<T> beanType, Object id);
/**
* Return the extended API for Database.
* <p>
* The extended API has the options for executing queries that take an explicit
* transaction as an argument.
* <p>
* Typically, we only need to use the extended API when we do NOT want to use the
* usual ThreadLocal based mechanism to obtain the current transaction but instead
* supply the transaction explicitly.
*/
ExtendedServer extended();
/**
* Either Insert or Update the bean depending on its state.
* <p>
@@ -53,14 +53,6 @@ public interface ExpressionList<T> {
*/
Query<T> orderById(boolean orderById);
/**
* @deprecated migrate to {@link #orderBy(String)}
*/
@Deprecated(since = "13.19", forRemoval = true)
default ExpressionList<T> order(String orderByClause) {
return orderBy(orderByClause);
}
/**
* Set the order by clause replacing the existing order by clause if there is
* one.
@@ -71,14 +63,6 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> orderBy(String orderBy);
/**
* @deprecated migrate to {@link #orderBy()}.
*/
@Deprecated(forRemoval = true)
default OrderBy<T> order() {
return orderBy();
}
/**
* Return the OrderBy so that you can append an ascending or descending
* property to the order by clause.
@@ -221,21 +205,6 @@ public interface ExpressionList<T> {
*/
int delete();
/**
* @deprecated migrate to {@link #usingTransaction(Transaction)} then delete().
* <p>
* Execute as a delete query deleting the 'root level' beans that match the predicates
* in the query.
* <p>
* Note that if the query includes joins then the generated delete statement may not be
* optimal depending on the database platform.
* </p>
*
* @return the number of rows that were deleted.
*/
@Deprecated(forRemoval = true, since = "13.1.0")
int delete(Transaction transaction);
/**
* Execute as a update query.
*
@@ -244,17 +213,6 @@ public interface ExpressionList<T> {
*/
int update();
/**
* @deprecated migrate to {@link #usingTransaction(Transaction)} then update().
* <p>
* Execute as a update query with the given transaction.
*
* @return the number of rows that were updated.
* @see UpdateQuery
*/
@Deprecated(forRemoval = true, since = "13.1.0")
int update(Transaction transaction);
/**
* Execute the query returning true if a row is found.
* <p>
@@ -355,7 +313,7 @@ public interface ExpressionList<T> {
* List<String> names =
* DB.find(Customer.class)
* .select("name")
* .order().asc("name")
* .orderBy().asc("name")
* .findSingleAttributeList();
*
* }</pre>
@@ -368,7 +326,7 @@ public interface ExpressionList<T> {
* .setDistinct(true)
* .select("name")
* .where().eq("status", Customer.Status.NEW)
* .order().asc("name")
* .orderBy().asc("name")
* .setMaxRows(100)
* .findSingleAttributeList();
*
@@ -1713,7 +1671,7 @@ public interface ExpressionList<T> {
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endAnd()
* .order().asc("name")
* .orderBy().asc("name")
* .findList();
* }</pre>
*/
@@ -1734,7 +1692,7 @@ public interface ExpressionList<T> {
* .or()
* .eq("status", Customer.Status.ACTIVE)
* .isNull("anniversary")
* .order().asc("name")
* .orderBy().asc("name")
* .findList();
*
* }</pre>
@@ -1754,7 +1712,7 @@ public interface ExpressionList<T> {
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endAnd()
* .order().asc("name")
* .orderBy().asc("name")
* .findList();
*
* }</pre>
@@ -1788,7 +1746,7 @@ public interface ExpressionList<T> {
* .gt("id", 1)
* .eq("anniversary", onAfter)
* .endNot()
* .order()
* .orderBy()
* .asc("name")
* .findList();
*
@@ -1,21 +0,0 @@
package io.ebean;
import java.time.Clock;
/**
* The extended API for Database.
*/
public interface ExtendedServer {
/**
* Deprecated but no yet determined suitable replacement (to support testing only change of clock).
* <p>
* Set the Clock to use for <code>@WhenCreated</code> and <code>@WhenModified</code>.
* <p>
* Note that we only expect to change the Clock for testing purposes.
* </p>
*/
@Deprecated
void setClock(Clock clock);
}
+1 -1
View File
@@ -35,7 +35,7 @@ import java.util.List;
* public List<Customer> findNew() {
* return query().where()
* .eq("status", Customer.Status.NEW)
* .order("name")
* .orderBy("name")
* .findList()
* }
* }
@@ -61,7 +61,7 @@ package io.ebean;
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endAnd()
* .order().asc("name");
* .orderBy().asc("name");
*
* q.findList();
* String s = q.getGeneratedSql();
@@ -0,0 +1,19 @@
package io.ebean;
/**
* Thrown when trying to access a property that isn't loaded on an entity
* that is unmodifiable or has disabled lazy loading.
* <p>
* On a normal mutable entity accessing the property would invoke lazy loading. On
* a unmodifiable entity with lazy loading disabled, accessing an unloaded property
* throws this LazyInitialisationException instead.
*/
public class LazyInitialisationException extends BeanAccessException {
/**
* Create specifying the property that was being accessed.
*/
public LazyInitialisationException(String message) {
super(message);
}
}
@@ -16,4 +16,7 @@ public interface ModifyAwareType {
*/
void setMarkedDirty(boolean markedDirty);
default Object freeze() {
return this; // throw new UnsupportedOperationException();
}
}
@@ -25,7 +25,7 @@ import java.util.concurrent.Future;
*
* PagedList<Order> pagedList = DB.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .orderBy().asc("id")
* .setFirstRow(0)
* .setMaxRows(50)
* .findPagedList();
+1 -1
View File
@@ -30,7 +30,7 @@ import java.util.Objects;
* .where()
* .eq("store", "def")
* .inPairs(pairs) // IN clause with 'pairs' of values
* .order("sku desc")
* .orderBy("sku desc")
*
* // query expressions cover the natural key properties
* // so we can choose to hit the L2 bean cache if we want
+1 -48
View File
@@ -13,7 +13,7 @@ import org.jspecify.annotations.NullMarked;
* .where()
* .like("customer.name","rob%")
* .gt("orderDate",lastWeek)
* .order("customer.id, id desc")
* .orderBy("customer.id, id desc")
* .setMaxRows(50)
* .findList();
*
@@ -218,29 +218,6 @@ public interface Query<T> extends CancelableQuery, QueryBuilder<Query<T>, T> {
*/
boolean isCountDistinct();
/**
* @deprecated migrate to {@link #usingTransaction(Transaction)} then delete().
* <p>
* Execute as a delete query returning the number of rows deleted using the given transaction.
* <p>
* Note that if the query includes joins then the generated delete statement may not be
* optimal depending on the database platform.
*
* @return the number of beans/rows that were deleted.
*/
@Deprecated(forRemoval = true, since = "14.1.0")
int delete(Transaction transaction);
/**
* @deprecated migrate to {@link #usingTransaction(Transaction)} then update().
* <p>
* Execute the UpdateQuery returning the number of rows updated using the given transaction.
*
* @return the number of beans/rows updated.
*/
@Deprecated(forRemoval = true, since = "14.1.0")
int update(Transaction transaction);
/**
* Execute the UpdateQuery returning the number of rows updated.
*
@@ -440,30 +417,6 @@ public interface Query<T> extends CancelableQuery, QueryBuilder<Query<T>, T> {
*/
Query<T> having(Expression addExpressionToHaving);
/**
* @deprecated migrate to {@link #orderBy()}.
*/
@Deprecated(since = "13.19", forRemoval = true)
default Query<T> order(String orderByClause) {
return orderBy(orderByClause);
}
/**
* @deprecated migrate to {@link #orderBy()}.
*/
@Deprecated(since = "13.19", forRemoval = true)
default OrderBy<T> order() {
return orderBy();
}
/**
* @deprecated migrate to {@link #setOrderBy(OrderBy)}.
*/
@Deprecated(since = "13.19", forRemoval = true)
default Query<T> setOrder(OrderBy<T> orderBy) {
return setOrderBy(orderBy);
}
/**
* Return the OrderBy so that you can append an ascending or descending
* property to the order by clause.
@@ -388,9 +388,13 @@ public interface QueryBuilder<SELF extends QueryBuilder<SELF, T>, T> extends Que
SELF setUseDocStore(boolean useDocStore);
/**
* When set to true when you want the returned beans to be read only.
* When set to true when you want the returned beans to be unmodifiable read only.
* <p>
* This means that the returning graph can't be mutated via setters, all the collections
* are unmodifiable collections, lazy loading is disabled and that the query uses
* {@link PersistenceContextScope#QUERY}.
*/
SELF setReadOnly(boolean readOnly);
SELF setUnmodifiable(boolean unmodifiable);
/**
* Set a timeout on this query.
@@ -25,7 +25,7 @@ import java.util.Iterator;
*
* Query<Customer> query = database.find(Customer.class)
* .where().gt("id", 0)
* .order("id")
* .orderBy("id")
* .setMaxRows(2);
*
* QueryIterator<Customer> it = query.findIterate();
+1 -1
View File
@@ -111,7 +111,7 @@ package io.ebean;
* .fetch("order.customer", "name")
* .where().gt("order.id", 0)
* .having().gt("totalAmount", 20)
* .order().desc("totalAmount")
* .orderBy().desc("totalAmount")
* .setMaxRows(10)
* .findList();
*
@@ -0,0 +1,22 @@
package io.ebean;
/**
* Attempted to modify a read only entity.
*/
public class UnmodifiableEntityException extends BeanAccessException {
private static final long serialVersionUID = 1;
/**
* Create with no message.
*/
public UnmodifiableEntityException() {
super();
}
/**
* Create with message.
*/
public UnmodifiableEntityException(String message) {
super(message);
}
}
@@ -119,18 +119,6 @@ public interface BeanCollection<E> extends Serializable, ToStringAware {
*/
void setLoader(BeanCollectionLoader beanLoader);
/**
* Set to true if you want the BeanCollection to be treated as read only. This
* means no elements can be added or removed etc.
*/
void setReadOnly(boolean readOnly);
/**
* Return true if the collection should be treated as readOnly and no elements
* can be added or removed etc.
*/
boolean isReadOnly();
/**
* Add the bean to the collection. This is disallowed for BeanMap.
*/
@@ -237,7 +225,7 @@ public interface BeanCollection<E> extends Serializable, ToStringAware {
boolean wasTouched();
/**
* Return a shallow copy of this collection that is modifiable.
* Freeze the collection returning an unmodifiable version.
*/
BeanCollection<E> shallowCopy();
Object freeze();
}
@@ -148,18 +148,6 @@ public interface EntityBeanIntercept extends Serializable {
*/
boolean isLoadedFromCache();
/**
* Return true if the bean should be treated as readOnly. If a setter method
* is called when it is readOnly an Exception is thrown.
*/
boolean isReadOnly();
/**
* Set the readOnly status. If readOnly then calls to setter methods through
* an exception.
*/
void setReadOnly(boolean readOnly);
/**
* Set the bean to be updated when persisted (for merge).
*/
@@ -393,11 +381,6 @@ public interface EntityBeanIntercept extends Serializable {
*/
void loadBean(int loadProperty);
/**
* Invoke the lazy loading. This method is synchronised externally.
*/
void loadBeanInternal(int loadProperty, BeanLoader loader);
/**
* Called when a BeanCollection is initialised automatically.
*/
@@ -552,4 +535,9 @@ public interface EntityBeanIntercept extends Serializable {
* Update the 'next' mutable info returning the content that was obtained via dirty detection.
*/
String mutableNext(int propertyIndex);
/**
* Return true if this entity bean should be frozen. Used to handle recursive freezing.
*/
boolean freeze();
}
@@ -0,0 +1,70 @@
package io.ebean.bean;
/**
* Common base features for EntityBeanIntercept.
*/
abstract class InterceptBase implements EntityBeanIntercept {
final EntityBean owner;
boolean fullyLoadedBean;
/**
* Create with a given entity.
*/
InterceptBase(Object ownerBean) {
this.owner = (EntityBean) ownerBean;
}
/**
* EXPERIMENTAL - Constructor only for use by serialization frameworks.
*/
InterceptBase() {
this.owner = null;
}
@Override
public final EntityBean owner() {
return owner;
}
@Override
public final boolean isFullyLoadedBean() {
return fullyLoadedBean;
}
@Override
public final void setFullyLoadedBean(boolean fullyLoadedBean) {
this.fullyLoadedBean = fullyLoadedBean;
}
@Override
public final String property(int propertyIndex) {
if (propertyIndex == -1) {
return null;
}
return owner._ebean_getPropertyName(propertyIndex);
}
@Override
public final int findProperty(String propertyName) {
final String[] names = owner._ebean_getPropertyNames();
for (int i = 0; i < names.length; i++) {
if (names[i].equals(propertyName)) {
return i;
}
}
return -1;
}
@Override
public final StringBuilder loadedPropertyKey() {
final StringBuilder sb = new StringBuilder();
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if (isLoadedProperty(i)) {
sb.append(i).append(',');
}
}
return sb;
}
}
@@ -1,37 +1,44 @@
package io.ebean.bean;
import io.ebean.LazyInitialisationException;
import io.ebean.UnmodifiableEntityException;
import io.ebean.ValuePair;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* EntityBeanIntercept optimised for read only use.
* <p>
* For the read only use this intercept doesn't need to hold any state that is normally
* required for updates such as per property changed, loaded, dirty state, original values
* required for updates such as per property changed, dirty state, original values
* bean state etc.
*/
public class InterceptReadOnly implements EntityBeanIntercept {
public final class InterceptReadOnly extends InterceptBase {
private final EntityBean owner;
private final boolean[] loaded;
private boolean frozen;
/**
* Create with a given entity.
*/
public InterceptReadOnly(Object ownerBean) {
this.owner = (EntityBean) ownerBean;
super(ownerBean);
this.loaded = new boolean[owner._ebean_getPropertyNames().length];
}
@Override
public boolean freeze() {
if (frozen) {
return false;
} else {
frozen = true;
return true;
}
}
@Override
public String toString() {
return "InterceptReadOnly{" + owner + '}';
}
@Override
public EntityBean owner() {
return owner;
return "InterceptReadOnly{frozen:" + frozen + " loaded:" + loadedPropertyNames() + '}';
}
@Override
@@ -94,18 +101,13 @@ public class InterceptReadOnly implements EntityBeanIntercept {
}
@Override
public boolean isFullyLoadedBean() {
return false;
}
@Override
public void setFullyLoadedBean(boolean fullyLoadedBean) {
}
@Override
public boolean isPartial() {
for (boolean flag : loaded) {
if (!flag) {
return true;
}
}
return false;
}
@@ -136,7 +138,14 @@ public class InterceptReadOnly implements EntityBeanIntercept {
@Override
public boolean hasIdOnly(int idIndex) {
return false;
for (int i = 0; i < loaded.length; i++) {
if (i == idIndex) {
if (!loaded[i]) return false;
} else if (loaded[i]) {
return false;
}
}
return true;
}
@Override
@@ -159,16 +168,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
return false;
}
@Override
public boolean isReadOnly() {
return true;
}
@Override
public void setReadOnly(boolean readOnly) {
}
@Override
public void setForceUpdate(boolean forceUpdate) {
@@ -234,44 +233,38 @@ public class InterceptReadOnly implements EntityBeanIntercept {
return null;
}
@Override
public int findProperty(String propertyName) {
return 0;
}
@Override
public String property(int propertyIndex) {
return null;
}
@Override
public int propertyLength() {
return 0;
return loaded.length;
}
@Override
public void setPropertyLoaded(String propertyName, boolean loaded) {
final int position = findProperty(propertyName);
if (position == -1) {
throw new IllegalArgumentException("Property not found - " + propertyName);
}
this.loaded[position] = loaded;
}
@Override
public void setPropertyUnloaded(int propertyIndex) {
loaded[propertyIndex] = false;
}
@Override
public void setLoadedProperty(int propertyIndex) {
loaded[propertyIndex] = true;
}
@Override
public void setLoadedPropertyAll() {
Arrays.fill(loaded, true);
}
@Override
public boolean isLoadedProperty(int propertyIndex) {
return false;
return loaded[propertyIndex];
}
@Override
@@ -321,7 +314,16 @@ public class InterceptReadOnly implements EntityBeanIntercept {
@Override
public Set<String> loadedPropertyNames() {
return Collections.emptySet();
if (fullyLoadedBean) {
return null;
}
final Set<String> props = new LinkedHashSet<>();
for (int i = 0; i < loaded.length; i++) {
if (loaded[i]) {
props.add(property(i));
}
}
return props;
}
@Override
@@ -369,14 +371,11 @@ public class InterceptReadOnly implements EntityBeanIntercept {
}
@Override
public StringBuilder loadedPropertyKey() {
return null;
}
@Override
public boolean[] loaded() {
return new boolean[0];
final boolean[] ret = new boolean[loaded.length];
System.arraycopy(loaded, 0, ret, 0, ret.length);
return ret;
}
@Override
@@ -394,14 +393,9 @@ public class InterceptReadOnly implements EntityBeanIntercept {
}
@Override
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
}
@Override
public void initialisedMany(int propertyIndex) {
loaded[propertyIndex] = true;
}
@Override
@@ -416,12 +410,14 @@ public class InterceptReadOnly implements EntityBeanIntercept {
@Override
public void preGetter(int propertyIndex) {
if (!loaded[propertyIndex]) {
throw new LazyInitialisationException("Property not loaded: " + property(propertyIndex));
}
}
@Override
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
@@ -436,57 +432,57 @@ public class InterceptReadOnly implements EntityBeanIntercept {
@Override
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
throw new UnmodifiableEntityException("Attempting to modify " + property(propertyIndex));
}
@Override
@@ -2,6 +2,7 @@ package io.ebean.bean;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.LazyInitialisationException;
import io.ebean.ValuePair;
import jakarta.persistence.EntityNotFoundException;
@@ -22,7 +23,7 @@ import java.util.concurrent.locks.ReentrantLock;
* This provides the mechanisms to support deferred fetching of reference beans
* and oldValues generation for concurrency checking.
*/
public final class InterceptReadWrite implements EntityBeanIntercept {
public final class InterceptReadWrite extends InterceptBase {
private static final long serialVersionUID = -3664031775464862649L;
@@ -56,18 +57,13 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
private String ebeanServerName;
private boolean deletedFromCollection;
/**
* The actual entity bean that 'owns' this intercept.
*/
private final EntityBean owner;
private EntityBean embeddedOwner;
private int embeddedOwnerIndex;
/**
* One of NEW, REF, UPD.
* One of NEW, REFERENCE, LOADED.
*/
private int state;
private boolean forceUpdate;
private boolean readOnly;
private boolean dirty;
/**
* Flag set to disable lazy loading.
@@ -77,7 +73,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
*/
private boolean lazyLoadFailure;
private boolean fullyLoadedBean;
private boolean loadedFromCache;
private final byte[] flags;
private Object[] origValues;
@@ -101,7 +96,7 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
* Create with a given entity.
*/
public InterceptReadWrite(Object ownerBean) {
this.owner = (EntityBean) ownerBean;
super(ownerBean);
this.flags = new byte[owner._ebean_getPropertyNames().length];
}
@@ -109,16 +104,20 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
* EXPERIMENTAL - Constructor only for use by serialization frameworks.
*/
public InterceptReadWrite() {
this.owner = null;
super();
this.flags = null;
}
@Override
public boolean freeze() {
throw new UnsupportedOperationException("never expected");
}
@Override
public String toString() {
return "InterceptReadWrite@" + hashCode() + "{state=" + state +
(dirty ? " dirty;" : "") +
(forceUpdate ? " forceUpdate;" : "") +
(readOnly ? " readOnly;" : "") +
(disableLazyLoad ? " disableLazyLoad;" : "") +
(lazyLoadFailure ? " lazyLoadFailure;" : "") +
(fullyLoadedBean ? " fullyLoadedBean;" : "") +
@@ -131,11 +130,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
'}';
}
@Override
public EntityBean owner() {
return owner;
}
@Override
public PersistenceContext persistenceContext() {
return persistenceContext;
@@ -200,16 +194,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
this.ebeanServerName = beanLoader.name();
}
@Override
public boolean isFullyLoadedBean() {
return fullyLoadedBean;
}
@Override
public void setFullyLoadedBean(boolean fullyLoadedBean) {
this.fullyLoadedBean = fullyLoadedBean;
}
@Override
public boolean isPartial() {
for (byte flag : flags) {
@@ -298,16 +282,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
return loadedFromCache;
}
@Override
public boolean isReadOnly() {
return readOnly;
}
@Override
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
@Override
public void setForceUpdate(boolean forceUpdate) {
this.forceUpdate = forceUpdate;
@@ -411,25 +385,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
return origValues[propertyIndex];
}
@Override
public int findProperty(String propertyName) {
final String[] names = owner._ebean_getPropertyNames();
for (int i = 0; i < names.length; i++) {
if (names[i].equals(propertyName)) {
return i;
}
}
return -1;
}
@Override
public String property(int propertyIndex) {
if (propertyIndex == -1) {
return null;
}
return owner._ebean_getPropertyName(propertyIndex);
}
@Override
public int propertyLength() {
return flags.length;
@@ -668,18 +623,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
}
}
@Override
public StringBuilder loadedPropertyKey() {
final StringBuilder sb = new StringBuilder();
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if (isLoadedProperty(i)) {
sb.append(i).append(',');
}
}
return sb;
}
@Override
public boolean[] loaded() {
final boolean[] ret = new boolean[flags.length];
@@ -703,12 +646,15 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
public void loadBean(int loadProperty) {
lock.lock();
try {
if (disableLazyLoad) {
throw new LazyInitialisationException("Property not loaded: " + property(loadProperty));
}
if (beanLoader == null) {
final Database database = DB.byName(ebeanServerName);
if (database == null) {
throw new PersistenceException(ebeanServerName == null ? "No registered default server" : "Database [" + ebeanServerName + "] is not registered");
}
// For stand alone reference bean or after deserialisation lazy load
// For stand-alone reference bean or after deserialisation lazy load
// using the ebeanServer. Synchronise only on the bean.
loadBeanInternal(loadProperty, database.pluginApi().beanLoader());
return;
@@ -726,8 +672,7 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
}
}
@Override
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
private void loadBeanInternal(int loadProperty, BeanLoader loader) {
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
// race condition where multiple threads calling preGetter concurrently
return;
@@ -829,7 +774,7 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
@Override
public void preGetter(int propertyIndex) {
preGetterCallback(propertyIndex);
if (state == STATE_NEW || disableLazyLoad) {
if (state == STATE_NEW) {
return;
}
if (!isLoadedProperty(propertyIndex)) {
@@ -845,18 +790,12 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setChangeLoaded(propertyIndex);
}
}
@Override
public void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setChangedProperty(propertyIndex);
if (setDirtyState) {
setOriginalValue(propertyIndex, origValue);
@@ -17,7 +17,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
private static final long serialVersionUID = 3365725236140187588L;
protected final ReentrantLock lock = new ReentrantLock();
protected boolean readOnly;
protected boolean disableLazyLoad;
/**
* The Database this is associated with. (used for lazy fetch).
@@ -55,7 +54,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.ebeanServerName = loader.name();
this.ownerBean = ownerBean;
this.propertyName = propertyName;
this.readOnly = ownerBean != null && ownerBean._ebean_getIntercept().isReadOnly();
}
@Override
@@ -103,22 +101,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.ebeanServerName = loader.name();
}
@Override
public boolean isReadOnly() {
return readOnly;
}
@Override
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
void checkReadOnly() {
if (readOnly) {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
}
// ---------------------------------------------------------
// Support for modify additions deletions etc - ManyToMany
// ---------------------------------------------------------
@@ -212,14 +194,4 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
return modifyHolder != null && modifyHolder.wasTouched();
}
/**
* Copies all relevant properties for a clone. See {@link #shallowCopy()}
*/
protected void setFromOriginal(AbstractBeanCollection<E> other) {
this.disableLazyLoad = other.disableLazyLoad;
this.ebeanServerName = other.ebeanServerName;
this.loader = other.loader;
this.ownerBean = other.ownerBean;
this.propertyName = other.propertyName;
}
}
@@ -2,7 +2,6 @@ package io.ebean.common;
import io.ebean.bean.*;
import java.io.Serializable;
import java.util.*;
/**
@@ -39,6 +38,17 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
super(loader, ownerBean, propertyName);
}
@Override
public List<E> freeze() {
if (list == null) {
return null;
} else if (list.isEmpty()) {
return List.of();
} else {
return Collections.unmodifiableList(list);
}
}
@Override
public void toString(ToStringBuilder builder) {
builder.addCollection(list);
@@ -223,7 +233,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public void add(int index, E element) {
checkReadOnly();
init();
if (modifyListening) {
modifyAddition(element);
@@ -238,7 +247,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public boolean add(E bean) {
checkReadOnly();
init();
if (modifyListening) {
if (list.add(bean)) {
@@ -253,7 +261,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public boolean addAll(Collection<? extends E> beans) {
checkReadOnly();
init();
if (modifyListening) {
// all elements in c are added (no contains checking)
@@ -264,7 +271,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public boolean addAll(int index, Collection<? extends E> beans) {
checkReadOnly();
init();
if (modifyListening) {
// all elements in c are added (no contains checking)
@@ -275,7 +281,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public void clear() {
checkReadOnly();
// TODO: when clear() and not initialised could be more clever
// and fetch just the Id's
initClear();
@@ -320,9 +325,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public Iterator<E> iterator() {
init();
if (readOnly) {
return new ReadOnlyListIterator<>(list.listIterator());
}
if (modifyListening) {
return new ModifyIterator<>(this, list.iterator());
}
@@ -338,9 +340,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public ListIterator<E> listIterator() {
init();
if (readOnly) {
return new ReadOnlyListIterator<>(list.listIterator());
}
if (modifyListening) {
return new ModifyListIterator<>(this, list.listIterator());
}
@@ -350,9 +349,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public ListIterator<E> listIterator(int index) {
init();
if (readOnly) {
return new ReadOnlyListIterator<>(list.listIterator(index));
}
if (modifyListening) {
return new ModifyListIterator<>(this, list.listIterator(index));
}
@@ -368,7 +364,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public E remove(int index) {
checkReadOnly();
init();
if (modifyListening) {
E o = list.remove(index);
@@ -380,7 +375,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public boolean remove(Object bean) {
checkReadOnly();
init();
if (modifyListening) {
boolean isRemove = list.remove(bean);
@@ -394,7 +388,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public boolean removeAll(Collection<?> beans) {
checkReadOnly();
init();
if (modifyListening) {
boolean changed = false;
@@ -412,7 +405,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public boolean retainAll(Collection<?> retainBeans) {
checkReadOnly();
init();
if (modifyListening) {
boolean changed = false;
@@ -433,7 +425,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public E set(int index, E element) {
checkReadOnly();
init();
if (modifyListening) {
E o = list.set(index, element);
@@ -453,9 +444,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
@Override
public List<E> subList(int fromIndex, int toIndex) {
init();
if (readOnly) {
return Collections.unmodifiableList(list.subList(fromIndex, toIndex));
}
if (modifyListening) {
return new ModifyList<>(this, list.subList(fromIndex, toIndex));
}
@@ -475,67 +463,4 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
return list.toArray(array);
}
private static final class ReadOnlyListIterator<E> implements ListIterator<E>, Serializable {
private static final long serialVersionUID = 3097271091406323699L;
private final ListIterator<E> i;
ReadOnlyListIterator(ListIterator<E> i) {
this.i = i;
}
@Override
public void add(E o) {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
@Override
public void remove() {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
@Override
public void set(E o) {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public boolean hasPrevious() {
return i.hasPrevious();
}
@Override
public E next() {
return i.next();
}
@Override
public int nextIndex() {
return i.nextIndex();
}
@Override
public E previous() {
return i.previous();
}
@Override
public int previousIndex() {
return i.previousIndex();
}
}
@Override
public BeanCollection<E> shallowCopy() {
BeanList<E> copy = new BeanList<>(new CopyOnFirstWriteList<>(list));
copy.setFromOriginal(this);
return copy;
}
}
@@ -37,6 +37,11 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
super(ebeanServer, ownerBean, propertyName);
}
@Override
public Map<K, E> freeze() {
return map == null ? null : Collections.unmodifiableMap(map);
}
@Override
public void toString(ToStringBuilder builder) {
if (map == null || map.isEmpty()) {
@@ -217,7 +222,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
@Override
public void clear() {
checkReadOnly();
initClear();
if (modifyListening) {
// add all beans to the removal list
@@ -243,9 +247,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
@Override
public Set<Entry<K, E>> entrySet() {
init();
if (readOnly) {
return Collections.unmodifiableSet(map.entrySet());
}
return modifyListening ? new ModifyEntrySet<>(this, map.entrySet()) : map.entrySet();
}
@@ -264,15 +265,11 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
@Override
public Set<K> keySet() {
init();
if (readOnly) {
return Collections.unmodifiableSet(map.keySet());
}
return modifyListening ? new ModifyKeySet<>(this, map.keySet()) : map.keySet();
}
@Override
public E put(K key, E value) {
checkReadOnly();
init();
if (modifyListening) {
E oldBean = map.put(key, value);
@@ -289,7 +286,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
@Override
public void putAll(Map<? extends K, ? extends E> puts) {
checkReadOnly();
init();
if (modifyListening) {
for (Entry<? extends K, ? extends E> entry : puts.entrySet()) {
@@ -306,17 +302,16 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
@Override
public void addBean(E bean) {
throw new IllegalStateException("Method not allowed on Map. Please use List instead.");
throw new UnsupportedOperationException("Method not allowed on Map. Please use List instead.");
}
@Override
public void removeBean(E bean) {
throw new IllegalStateException("Method not allowed on Map. Please use List instead.");
throw new UnsupportedOperationException("Method not allowed on Map. Please use List instead.");
}
@Override
public E remove(Object key) {
checkReadOnly();
init();
if (modifyListening) {
E o = map.remove(key);
@@ -335,16 +330,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
@Override
public Collection<E> values() {
init();
if (readOnly) {
return Collections.unmodifiableCollection(map.values());
}
return modifyListening ? new ModifyCollection<>(this, map.values()) : map.values();
}
@Override
public BeanCollection<E> shallowCopy() {
BeanMap<K, E> copy = new BeanMap<>(new LinkedHashMap<>(map));
copy.setFromOriginal(this);
return copy;
}
}
@@ -3,10 +3,7 @@ package io.ebean.common;
import io.ebean.bean.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.*;
/**
* Set capable of lazy loading and modification aware.
@@ -38,6 +35,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
super(loader, ownerBean, propertyName);
}
@Override
public Set<E> freeze() {
return set == null ? null : Collections.unmodifiableSet(set);
}
@Override
public void toString(ToStringBuilder builder) {
builder.addCollection(set);
@@ -211,7 +213,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public boolean add(E bean) {
checkReadOnly();
init();
if (modifyListening) {
if (set.add(bean)) {
@@ -226,7 +227,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public boolean addAll(Collection<? extends E> beans) {
checkReadOnly();
init();
if (modifyListening) {
boolean changed = false;
@@ -244,7 +244,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public void clear() {
checkReadOnly();
initClear();
if (modifyListening) {
for (E bean : set) {
@@ -275,9 +274,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public Iterator<E> iterator() {
init();
if (readOnly) {
return new ReadOnlyIterator<>(set.iterator());
}
if (modifyListening) {
return new ModifyIterator<>(this, set.iterator());
}
@@ -286,7 +282,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public boolean remove(Object bean) {
checkReadOnly();
init();
if (modifyListening) {
if (set.remove(bean)) {
@@ -300,7 +295,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public boolean removeAll(Collection<?> beans) {
checkReadOnly();
init();
if (modifyListening) {
boolean changed = false;
@@ -317,7 +311,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
@Override
public boolean retainAll(Collection<?> beans) {
checkReadOnly();
init();
if (modifyListening) {
boolean changed = false;
@@ -355,36 +348,4 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
return set.toArray(array);
}
private static final class ReadOnlyIterator<E> implements Iterator<E>, Serializable {
private static final long serialVersionUID = 2577697326745352605L;
private final Iterator<E> it;
ReadOnlyIterator(Iterator<E> it) {
this.it = it;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public E next() {
return it.next();
}
@Override
public void remove() {
throw new IllegalStateException("This collection is in ReadOnly mode");
}
}
@Override
public BeanCollection<E> shallowCopy() {
BeanSet<E> copy = new BeanSet<>(new LinkedHashSet<>(set));
copy.setFromOriginal(this);
return copy;
}
}
@@ -1,185 +0,0 @@
package io.ebean.common;
import java.io.Serializable;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* List that copies itself on first write access. Needed to keep memory footprint low and the ability
* to modify lists from cache.
*
* @author Roland Praml, FOCONIS AG
*/
public final class CopyOnFirstWriteList<E> extends AbstractList<E> implements List<E>, Serializable {
private static final long serialVersionUID = 1L;
private final ReentrantLock lock = new ReentrantLock();
/**
* The underlying List implementation.
*/
private List<E> list;
public CopyOnFirstWriteList(List<E> list) {
super();
this.list = list;
}
private volatile boolean copied = false;
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean add(E e) {
checkCopyOnWrite();
return list.add(e);
}
@Override
public boolean remove(Object o) {
checkCopyOnWrite();
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
checkCopyOnWrite();
return list.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
checkCopyOnWrite();
return list.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
checkCopyOnWrite();
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
checkCopyOnWrite();
return list.retainAll(c);
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
checkCopyOnWrite();
list.replaceAll(operator);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
checkCopyOnWrite();
return list.removeIf(filter);
}
@Override
public void sort(Comparator<? super E> c) {
checkCopyOnWrite();
list.sort(c);
}
@Override
public void clear() {
if (!copied) {
list = new ArrayList<>();
copied = true;
}
}
@Override
public boolean equals(Object o) {
return list.equals(o);
}
@Override
public int hashCode() {
return list.hashCode();
}
@Override
public E get(int index) {
return list.get(index);
}
@Override
public E set(int index, E element) {
checkCopyOnWrite();
return list.set(index, element);
}
@Override
public void add(int index, E element) {
checkCopyOnWrite();
list.add(index, element);
}
@Override
public E remove(int index) {
checkCopyOnWrite();
return list.remove(index);
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
private void checkCopyOnWrite() {
if (!copied) {
lock.lock();
try {
if (!copied) {
list = new ArrayList<>(list);
copied = true;
}
} finally {
lock.unlock();
}
}
}
}
@@ -1,5 +1,7 @@
package io.ebean.config;
import io.ebean.plugin.Lookups;
/**
* Helper to find classes taking into account the context class loader.
*/
@@ -73,9 +75,8 @@ public class ClassLoadConfig {
*/
public Object newInstance(String className) {
try {
Class<?> cls = forName(className);
return cls.getDeclaredConstructor().newInstance();
} catch (Exception e) {
return Lookups.newDefaultInstance(forName(className));
} catch (Throwable e) {
throw new IllegalArgumentException("Error constructing " + className, e);
}
}
@@ -0,0 +1,15 @@
package io.ebean.config;
import java.lang.invoke.MethodHandles.Lookup;
/**
* Provides a Lookup instance for accessing entity/dto fields.
*/
public interface LookupProvider {
/**
* Return the Lookup.
*/
Lookup provideLookup();
}
@@ -172,7 +172,6 @@ public class DbPlatformType implements ExtraDbTypes {
* </p>
*/
public String renderType(int deployLength, int deployScale, boolean strict) {
int len = deployLength != 0 ? deployLength : defaultLength;
if (len > maxLength) {
return fallback.renderType(deployLength, deployScale, strict);
@@ -182,7 +181,6 @@ public class DbPlatformType implements ExtraDbTypes {
if ((canHaveLength || !strict) && len > 0) {
renderLengthScale(len, deployScale, sb);
}
return sb.toString();
}
}
@@ -157,7 +157,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
if (newIds.isEmpty()) {
throw new PersistenceException("Always expecting more than 1 row from " + sql);
}
connection.commit();
return newIds;
} catch (SQLException e) {
@@ -1,5 +1,6 @@
package io.ebean.event;
import io.ebean.plugin.Lookups;
/**
* Helper to find classes taking into account the context class loader.
@@ -12,8 +13,8 @@ class ClassUtil {
static Object newInstance(String className) {
try {
Class<?> cls = forName(className);
return cls.getDeclaredConstructor().newInstance();
} catch (Exception e) {
return Lookups.newDefaultInstance(cls);
} catch (Throwable e) {
String msg = "Error constructing " + className;
throw new IllegalArgumentException(msg, e);
}
@@ -1,40 +0,0 @@
package io.ebean.event;
import jakarta.servlet.ServletContextEvent;
/**
* Deprecated for removal - no intention to replace. Create your own copy of this
* ServletContextListener if needed for your application.
* <p>
* Listens for webserver server starting and stopping events.
* <p>
* This should be used when the deployment is into a servlet container where the webapp
* can be shutdown or redeployed without the JVM stopping.
* </p>
* <p>
* If deployment is into a container where the JVM is completely shutdown (like spring boot,
* runnable war or when using a servlet container that only contains the single webapp and
* the JVM is shutdown then this isn't required. Instead we can just rely on the JVM shutdown
* hook that Ebean registers.
* </p>
*/
@Deprecated(forRemoval = true)
public class ServletContextListener implements jakarta.servlet.ServletContextListener {
/**
* The servlet container is stopping.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
ShutdownManager.shutdown();
}
/**
* Do nothing on startup.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
}
}
@@ -0,0 +1,43 @@
package io.ebean.plugin;
import io.ebean.DB;
import io.ebean.TxScope;
/**
* Helper object to make AOP generated code simpler.
*/
public final class AOPTransactionScope {
private static boolean enabled = true;
/**
* Entering an enhanced transactional method.
*/
public static void enter(TxScope txScope) {
if (enabled) {
server().scopedTransactionEnter(txScope);
}
}
/**
* Exiting an enhanced transactional method.
*/
public static void exit(Object returnOrThrowable, int opCode) {
if (enabled) {
server().scopedTransactionExit(returnOrThrowable, opCode);
}
}
private static SpiServer server() {
return (SpiServer) DB.getDefault();
}
/**
* Defines if the @Transactional does what is supposed to do or is disabled
* (useful only unit testing)
*
* @param enabled if set to false, @Transactional will not create a transaction
*/
public static void setEnabled(boolean enabled) {
AOPTransactionScope.enabled = enabled;
}
}
@@ -0,0 +1,41 @@
package io.ebean.plugin;
import static java.util.stream.Collectors.toMap;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.Map;
import java.util.ServiceLoader;
import io.ebean.config.LookupProvider;
/**
* Provides Lookup instances using potentially module specific Lookups.
*/
public final class Lookups {
private static final Map<String, Lookup> LOOKUP_MAP =
ServiceLoader.load(LookupProvider.class).stream()
.collect(toMap(p -> p.type().getModule().getName(), p -> p.get().provideLookup()));
private static final Lookup DEFAULT_LOOKUP = MethodHandles.publicLookup();
private static final MethodType VOID = MethodType.methodType(void.class);
/**
* Return a Lookup ideally for the module associated with the given type.
*/
public static Lookup getLookup(Class<?> type) {
return LOOKUP_MAP.getOrDefault(type.getModule().getName(), DEFAULT_LOOKUP);
}
/**
* Find the default constructor and return a new instance for the given type
* potentially using a module specific Lookup instance.
*/
@SuppressWarnings("unchecked")
public static <T> T newDefaultInstance(Class<?> type) throws Throwable {
return (T) getLookup(type).findConstructor(type, VOID).invoke();
}
}
@@ -1,6 +1,7 @@
package io.ebean.plugin;
import io.ebean.Database;
import io.ebean.TxScope;
import io.ebean.bean.BeanLoader;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.DatabaseBuilder;
@@ -63,4 +64,15 @@ public interface SpiServer extends Database {
* Typically due to serialisation or multiple stateless updates.
*/
void loadBean(EntityBeanIntercept ebi);
/**
* Start an enhanced transactional method.
*/
void scopedTransactionEnter(TxScope txScope);
/**
* Handle the end of an enhanced Transactional method.
*/
void scopedTransactionExit(Object returnOrThrowable, int opCode);
}
@@ -15,7 +15,7 @@
* .fetch("billingAddress","line1, city")
* .fetch("billingAddress.country", "*")
* .fetch("contacts", "firstName,email")
* .order().desc("id")
* .orderBy().desc("id")
* .findList();
*
* JsonContext json = DB.json();
+4 -4
View File
@@ -1,6 +1,7 @@
module io.ebean.api {
uses io.ebean.config.AutoConfigure;
uses io.ebean.config.LookupProvider;
uses io.ebean.datasource.DataSourceAlertFactory;
uses io.ebean.service.BootstrapService;
uses io.ebean.service.SpiJsonService;
@@ -17,20 +18,19 @@ module io.ebean.api {
requires static io.ebean.types;
requires static com.fasterxml.jackson.core;
requires static com.fasterxml.jackson.databind;
requires static jakarta.servlet;
exports io.ebean;
exports io.ebean.bean;
exports io.ebean.cache;
exports io.ebean.meta;
exports io.ebean.common;
exports io.ebean.config;
exports io.ebean.config.dbplatform;
exports io.ebean.docstore;
exports io.ebean.event;
exports io.ebean.event.readaudit;
exports io.ebean.event.changelog;
exports io.ebean.common;
exports io.ebean.docstore;
exports io.ebean.plugin;
exports io.ebean.meta;
exports io.ebean.metric;
exports io.ebean.search;
exports io.ebean.service;
@@ -1 +1 @@
ebean-version: 148
ebean-version: 149
+26 -26
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
</parent>
<name>ebean bom</name>
@@ -89,25 +89,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -125,13 +125,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -155,37 +155,37 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<!-- platforms -->
@@ -193,79 +193,79 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -7,6 +7,8 @@ import java.sql.SQLException;
public interface DataReader {
boolean unmodifiable();
void close() throws SQLException;
boolean next() throws SQLException;
+6 -6
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.8.1</version>
<version>14.10.0</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,7 +22,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -46,7 +46,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
</dependency>
<dependency>
@@ -159,21 +159,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>14.8.1</version>
<version>14.10.0</version>
<scope>test</scope>
</dependency>
@@ -1,34 +1,25 @@
package io.ebeaninternal.api;
import io.ebean.DB;
import io.ebean.TxScope;
import io.ebean.plugin.AOPTransactionScope;
/**
* Helper object to make AOP generated code simpler.
*/
public final class HelpScopeTrans {
private static boolean enabled = true;
/**
* Entering an enhanced transactional method.
*/
public static void enter(TxScope txScope) {
if (enabled) {
server().scopedTransactionEnter(txScope);
}
AOPTransactionScope.enter(txScope);
}
/**
* Exiting an enhanced transactional method.
*/
public static void exit(Object returnOrThrowable, int opCode) {
if (enabled) {
server().scopedTransactionExit(returnOrThrowable, opCode);
}
}
private static SpiEbeanServer server() {
return (SpiEbeanServer) DB.getDefault();
AOPTransactionScope.exit(returnOrThrowable, opCode);
}
/**
@@ -38,6 +29,6 @@ public final class HelpScopeTrans {
* @param enabled if set to false, @Transactional will not create a transaction
*/
public static void setEnabled(boolean enabled) {
HelpScopeTrans.enabled = enabled;
AOPTransactionScope.setEnabled(enabled);
}
}
@@ -55,4 +55,9 @@ public interface LoadContext {
* Use soft-references for streaming queries, so unreachable entries can be garbage collected.
*/
void useReferences(boolean useReferences);
/**
* Return true to include a many as a secondary query for unmodified.
*/
boolean includeSecondary(BeanPropertyAssocMany<?> many);
}
@@ -34,8 +34,8 @@ public final class ScopeTrans {
* Explicit set of Exceptions that DO cause a rollback to occur.
*/
private final ArrayList<Class<? extends Throwable>> rollbackFor;
private Boolean restoreBatch;
private Boolean restoreBatchOnCascade;
private boolean restoreBatch;
private boolean restoreBatchOnCascade;
private int restoreBatchSize;
private Boolean restoreBatchGeneratedKeys;
private boolean restoreBatchFlushOnQuery;
@@ -61,7 +61,7 @@ public final class ScopeTrans {
this.noRollbackFor = txScope.getNoRollbackFor();
this.rollbackFor = txScope.getRollbackFor();
if (transaction != null) {
if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
if (!created) {
restoreBatch = transaction.isBatchMode();
restoreBatchOnCascade = transaction.isBatchOnCascade();
restoreBatchSize = transaction.getBatchSize();
@@ -142,16 +142,12 @@ public final class ScopeTrans {
transaction.commit();
} else {
nestedCommit = true;
transaction.flush();
// restore the batch settings
transaction.setFlushOnQuery(restoreBatchFlushOnQuery);
if (restoreBatch != null) {
transaction.setBatchMode(restoreBatch);
}
if (restoreBatchOnCascade != null) {
transaction.setBatchOnCascade(restoreBatchOnCascade);
}
if (restoreBatchSize > 0) {
transaction.setBatchSize(restoreBatchSize);
}
transaction.setBatchMode(restoreBatch);
transaction.setBatchOnCascade(restoreBatchOnCascade);
transaction.setBatchSize(restoreBatchSize);
if (restoreBatchGeneratedKeys != null) {
transaction.setGetGeneratedKeys(restoreBatchGeneratedKeys);
}
@@ -168,7 +168,7 @@ public final class ScopedTransaction extends SpiTransactionProxy {
/**
* Maybe rollback based on TxScope rollback on settings.
*/
public Exception caughtThrowable(Exception e) {
public <T extends Exception> T caughtThrowable(T e) {
return current.caughtThrowable(e);
}
@@ -26,7 +26,7 @@ import java.util.stream.Stream;
/**
* Service Provider extension to EbeanServer.
*/
public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectionLoader {
public interface SpiEbeanServer extends SpiServer, BeanCollectionLoader {
/**
* Return the NOW time from the Clock.
@@ -216,16 +216,6 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio
*/
void slowQueryCheck(long executionTimeMicros, int rowCount, SpiQuery<?> query);
/**
* Start an enhanced transactional method.
*/
void scopedTransactionEnter(TxScope txScope);
/**
* Handle the end of an enhanced Transactional method.
*/
void scopedTransactionExit(Object returnOrThrowable, int opCode);
/**
* SqlQuery find single attribute.
*/
@@ -780,9 +780,9 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
CacheMode queryCacheMode();
/**
* Return true if the beans returned by this query should be read only.
* Return true if the beans returned by this query should be unmodifiable.
*/
Boolean isReadOnly();
boolean isUnmodifiable();
/**
* Return the query timeout.
@@ -3,12 +3,7 @@ package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.*;
/**
* Modify aware wrapper of a list.
@@ -52,6 +47,11 @@ public final class ModifyAwareList<E> implements List<E>, ModifyAwareType, Seria
return list.hashCode();
}
@Override
public List<E> freeze() {
return Collections.unmodifiableList(list);
}
@Override
public boolean isMarkedDirty() {
return owner.isMarkedDirty();
@@ -3,11 +3,7 @@ package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
/**
* Map that is wraps an underlying map for the purpose of detecting changes.
@@ -51,6 +47,11 @@ public final class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareType, S
return map.hashCode();
}
@Override
public Map<K, V> freeze() {
return Collections.unmodifiableMap(map);
}
@Override
public boolean isMarkedDirty() {
return owner.isMarkedDirty();
@@ -3,10 +3,7 @@ package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.*;
/**
* Wraps a Set for the purposes of detecting modifications.
@@ -33,6 +30,11 @@ public final class ModifyAwareSet<E> implements Set<E>, ModifyAwareType, Seriali
this.set = underlying;
}
@Override
public Set<E> freeze() {
return Collections.unmodifiableSet(set);
}
@Override
public boolean isMarkedDirty() {
return owner.isMarkedDirty();
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.cache;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.InterceptReadOnly;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -48,25 +49,21 @@ public final class CachedBeanDataFromBean {
if (!desc.isCacheSharableBeans() || !beanEbi.isFullyLoadedBean()) {
return null;
}
if (beanEbi.isReadOnly()) {
if (beanEbi instanceof InterceptReadOnly) {
return bean;
}
// create a readOnly sharable instance by copying the data
EntityBean sharableBean = desc.createEntityBean();
EntityBean sharableBean = desc.createEntityBean2(true);
BeanProperty idProp = desc.idProperty();
if (idProp != null) {
Object v = idProp.getValue(bean);
idProp.setValue(sharableBean, v);
}
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
for (BeanProperty aPropertiesNonTransient : propertiesNonTransient) {
Object v = aPropertiesNonTransient.getValue(bean);
aPropertiesNonTransient.setValue(sharableBean, v);
for (BeanProperty nonTransient : desc.propertiesNonTransient()) {
Object v = nonTransient.getValue(bean);
nonTransient.setValue(sharableBean, v);
}
EntityBeanIntercept intercept = sharableBean._ebean_getIntercept();
intercept.setReadOnly(true);
intercept.setLoaded();
desc.freeze(sharableBean);
return sharableBean;
}
@@ -1,8 +1,6 @@
package io.ebeaninternal.server.cache;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
import io.ebean.bean.*;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -25,10 +23,11 @@ public final class CachedBeanDataToBean {
for (BeanProperty prop : desc.propertiesNonMany()) {
loadProperty(bean, cacheBeanData, ebi, prop, context);
}
final boolean addManyReferences = ebi instanceof InterceptReadWrite;
for (BeanPropertyAssocMany<?> prop : desc.propertiesMany()) {
if (prop.isElementCollection()) {
loadProperty(bean, cacheBeanData, ebi, prop, context);
} else {
} else if (addManyReferences) {
prop.createReferenceIfNull(bean);
}
}
@@ -1,29 +0,0 @@
package io.ebeaninternal.server.core;
import java.time.Clock;
/**
* Wraps the Clock such that we can change the Clock for testing purposes.
*/
public class ClockService {
private Clock clock;
public ClockService(Clock clock) {
this.clock = clock;
}
/**
* Change the clock for testing purposes.
*/
public void setClock(Clock clock) {
this.clock = clock;
}
/**
* Return the Clock current timestamp in millis.
*/
public long nowMillis() {
return clock.millis();
}
}
@@ -77,7 +77,12 @@ public class DatabasePlatformFactory {
*/
private DatabasePlatform byDataSource(DataSource dataSource) {
try (Connection connection = dataSource.getConnection()) {
return byDatabaseMeta(connection.getMetaData(), connection);
DatabasePlatform platform = byDatabaseMeta(connection.getMetaData(), connection);
if (!connection.getAutoCommit()) {
// we must roll back before close.
connection.rollback();
}
return platform;
} catch (SQLException ex) {
throw new PersistenceException(ex);
}
@@ -67,12 +67,8 @@ final class DefaultBeanLoader {
parentDesc.contextPutIfAbsent(pc, parentId, parentBean);
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching() && many.isUseCache();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
final String parentKey = parentDesc.cacheKey(parentId);
if (parentDesc.cacheManyPropLoad(many, beanCollection, parentKey, readOnly)) {
if (parentDesc.cacheManyPropLoad(many, beanCollection, parentKey)) {
return;
}
}
@@ -102,9 +98,6 @@ final class DefaultBeanLoader {
query.setMode(Mode.LAZYLOAD_MANY);
query.setLazyLoadManyPath(many.name());
query.setPersistenceContext(pc);
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
if (many.hasOrderColumn()) {
query.orderBy(many.path() + "." + many.fetchOrderBy());
}
@@ -212,9 +205,6 @@ final class DefaultBeanLoader {
// make sure the query doesn't use the cache
query.setBeanCacheMode(CacheMode.OFF);
}
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
if (Mode.REFRESH_BEAN == mode) {
// explicitly state to load all properties on REFRESH.
// Lobs default to fetch lazy so this forces lobs to be
@@ -4,6 +4,7 @@ import io.ebean.BeanState;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.InterceptReadOnly;
import java.util.Map;
import java.util.Set;
@@ -60,13 +61,8 @@ public final class DefaultBeanState implements BeanState {
}
@Override
public boolean isReadOnly() {
return intercept.isReadOnly();
}
@Override
public void setReadOnly(boolean readOnly) {
intercept.setReadOnly(readOnly);
public boolean isUnmodifiable() {
return intercept instanceof InterceptReadOnly;
}
@Override
@@ -262,6 +262,8 @@ public final class DefaultContainer implements SpiContainer {
try (Connection connection = config.getDataSource().getConnection()) {
if (connection.getAutoCommit()) {
log.log(WARNING, "DataSource [{0}] has autoCommit defaulting to true!", config.getName());
} else {
connection.rollback();
}
return true;
} catch (SQLException ex) {
@@ -8,7 +8,6 @@ import io.ebean.annotation.TxIsolation;
import io.ebean.bean.*;
import io.ebean.bean.PersistenceContext.WithOption;
import io.ebean.cache.ServerCacheManager;
import io.ebean.common.CopyOnFirstWriteList;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.BeanPersistController;
@@ -83,7 +82,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final QueryPlanManager queryPlanManager;
private final ExtraMetrics extraMetrics;
private final DataTimeZone dataTimeZone;
private final ClockService clockService;
private final Clock clock;
private final CallOriginFactory callStackFactory;
private final Persister persister;
private final OrmQueryEngine queryEngine;
@@ -151,7 +150,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
this.dataTimeZone = config.getDataTimeZone();
this.clockService = config.getClockService();
this.clock = config.clock();
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
@@ -413,19 +412,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return serverName;
}
@Override
public ExtendedServer extended() {
return this;
}
@Override
public long clockNow() {
return clockService.nowMillis();
}
@Override
public void setClock(Clock clock) {
this.clockService.setClock(clock);
return clock.millis();
}
@Override
@@ -620,7 +609,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
InheritInfo inheritInfo = desc.inheritInfo();
if (inheritInfo == null || inheritInfo.isConcrete()) {
return (T) desc.contextRef(pc, null, false, id);
return (T) desc.contextRef(pc, id);
}
return referenceFindOne(type, id, desc);
}
@@ -680,6 +669,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return callable.call();
} catch (Error e) {
throw scopeTrans.caughtError(e);
} catch (PersistenceException e) {
throw scopeTrans.caughtThrowable(e);
} catch (Exception e) {
throw new PersistenceException(scopeTrans.caughtThrowable(e));
} finally {
@@ -699,6 +690,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
runnable.run();
} catch (Error e) {
throw t.caughtError(e);
} catch (PersistenceException e) {
throw t.caughtThrowable(e);
} catch (Exception e) {
throw new PersistenceException(t.caughtThrowable(e));
} finally {
@@ -1000,7 +993,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return null;
}
// Hit the L2 bean cache
return desc.cacheBeanGet(id, query.isReadOnly(), pc);
return desc.cacheBeanGet(id, query.isUnmodifiable(), pc);
}
/**
@@ -1211,11 +1204,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, query);
Object result = request.getFromQueryCache();
if (result != null) {
if (Boolean.FALSE.equals(request.query().isReadOnly())) {
return new CopyOnFirstWriteList<>((List<A>) result);
} else {
return (List<A>) result;
}
return (List<A>) result;
}
try {
request.initTransIfRequired();
@@ -67,7 +67,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
@Override
protected void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException {
this.resultSet = resultSet;
this.dataReader = new RsetDataReader(server.dataTimeZone(), resultSet);
this.dataReader = new RsetDataReader(false, server.dataTimeZone(), resultSet);
obtainPlan(queryPlanKey);
}
@@ -55,6 +55,7 @@ import io.ebeanservice.docstore.api.DocStoreIntegration;
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import io.ebeanservice.docstore.none.NoneDocStoreFactory;
import java.time.Clock;
import java.util.*;
import static java.lang.System.Logger.Level.*;
@@ -75,7 +76,7 @@ public final class InternalConfiguration {
private final DeployInherit deployInherit;
private final TypeManager typeManager;
private final DtoBeanManager dtoBeanManager;
private final ClockService clockService;
private final Clock clock;
private final DataTimeZone dataTimeZone;
private final Binder binder;
private final DeployCreateProperties deployCreateProperties;
@@ -103,7 +104,7 @@ public final class InternalConfiguration {
this.online = online;
this.config = config;
this.jacksonCorePresent = config.getClassLoadConfig().isJacksonCorePresent();
this.clockService = new ClockService(config.settings().getClock());
this.clock = config.settings().getClock();
this.tableModState = new TableModState();
this.logManager = initLogManager();
this.docStoreFactory = initDocStoreFactory(service(DocStoreFactory.class));
@@ -189,8 +190,8 @@ public final class InternalConfiguration {
return docStoreFactory;
}
ClockService getClockService() {
return clockService;
Clock clock() {
return clock;
}
public ExtraMetrics getExtraMetrics() {
@@ -391,7 +392,7 @@ public final class InternalConfiguration {
TransactionManagerOptions options =
new TransactionManagerOptions(server, notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
tableModState, cacheNotify, clockService);
tableModState, cacheNotify);
if (config.isDocStoreOnly()) {
return new DocStoreTransactionManager(options);
@@ -25,9 +25,9 @@ public interface OrmQueryEngine {
<T> T findId(OrmQueryRequest<T> request);
/**
* Execute the findList, findSet, findMap query returning an appropriate BeanCollection.
* Execute the findList, findSet, findMap query returning an appropriate Collection.
*/
<T> BeanCollection<T> findMany(OrmQueryRequest<T> request);
<T> Object findMany(OrmQueryRequest<T> request);
/**
* Execute the findSingleAttributeCollection query.
@@ -2,11 +2,11 @@ package io.ebeaninternal.server.core;
import io.ebean.*;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import io.ebean.cache.QueryCacheEntry;
import io.ebean.common.BeanList;
import io.ebean.common.BeanMap;
import io.ebean.common.CopyOnFirstWriteList;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanQueryAdapter;
import io.ebean.text.json.JsonReadOptions;
@@ -546,7 +546,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
if (orderBy != null && !orderBy.isEmpty()) {
beanDescriptor.sort(cacheBeans, orderBy.toStringFormat());
}
return cacheBeans;
return query.isUnmodifiable() ? Collections.unmodifiableList(cacheBeans) : cacheBeans;
}
@Override
@@ -565,7 +565,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
for (T bean : cacheBeans) {
map.put((K) property.pathGet(bean), bean);
}
return map;
return query.isUnmodifiable() ? Collections.unmodifiableMap(map) : map;
}
private ElPropertyValue mapProperty() {
@@ -583,7 +583,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
if (orderBy != null && !orderBy.isEmpty()) {
beanDescriptor.sort(cacheBeans, orderBy.toStringFormat());
}
return new LinkedHashSet<>(cacheBeans);
var set = new LinkedHashSet<>(cacheBeans);
return query.isUnmodifiable() ? Collections.unmodifiableSet(set) : set;
}
@Override
@@ -600,9 +601,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
//
CacheIdLookup<T> idLookup = query.cacheIdLookup();
if (idLookup != null) {
BeanCacheResult<T> cacheResult = beanDescriptor.cacheIdLookup(persistenceContext, idLookup.idValues());
BeanCacheResult<T> cacheResult = beanDescriptor.cacheIdLookup(persistenceContext, query.isUnmodifiable(), idLookup.idValues());
// adjust the query (IN clause) based on the cache hits
this.cacheBeans = idLookup.removeHits(cacheResult);
if (query.isUnmodifiable()) {
unmodifiableFreeze(cacheBeans);
}
return idLookup.allHits();
}
if (!beanDescriptor.isNaturalKeyCaching()) {
@@ -613,7 +617,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
NaturalKeySet naturalKeySet = data.buildKeys();
if (naturalKeySet != null) {
// use the natural keys to lookup Ids to then hit the bean cache
BeanCacheResult<T> cacheResult = beanDescriptor.naturalKeyLookup(persistenceContext, naturalKeySet.keys());
BeanCacheResult<T> cacheResult = beanDescriptor.naturalKeyLookup(persistenceContext, query.isUnmodifiable(), naturalKeySet.keys());
// adjust the query (IN clause) based on the cache hits
this.cacheBeans = data.removeHits(cacheResult);
return data.allHits();
@@ -639,31 +643,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
return null;
}
Object cached = beanDescriptor.queryCacheGet(cacheKey);
if (cached != null && isAuditReads() && readAuditQueryType()) {
if (cached instanceof BeanCollection) {
// raw sql can't use L2 cache so normal queries only in here
Collection<T> actualDetails = ((BeanCollection<T>) cached).actualDetails();
List<Object> ids = new ArrayList<>(actualDetails.size());
for (T bean : actualDetails) {
ids.add(beanDescriptor.idForJson(bean));
}
beanDescriptor.readAuditMany(queryPlanKey.partialKey(), "l2-query-cache", ids);
}
}
if (Boolean.FALSE.equals(query.isReadOnly())) {
// return shallow copies if readonly is explicitly set to false
if (cached instanceof BeanCollection) {
cached = ((BeanCollection<?>) cached).shallowCopy();
} else if (cached instanceof List) {
cached = new CopyOnFirstWriteList<>((List<?>) cached);
} else if (cached instanceof Set) {
cached = new LinkedHashSet<>((Set<?>) cached);
} else if (cached instanceof Map) {
cached = new LinkedHashMap<>((Map<?, ?>) cached);
}
}
return cached;
return beanDescriptor.queryCacheGet(cacheKey);
}
/**
@@ -780,4 +760,30 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
beanDescriptor.contextClear(transaction.persistenceContext());
}
}
public void unmodifiableFreeze(Collection<T> beans) {
if (query.isUnmodifiable()) {
if (beans != null) {
for (T bean : beans) {
beanDescriptor.freeze((EntityBean) bean);
}
}
}
}
public void unmodifiableFreeze(BeanCollection<T> beanCollection) {
if (query.isUnmodifiable()) {
if (beanCollection != null) {
for (T bean : beanCollection.actualDetails()) {
beanDescriptor.freeze((EntityBean) bean);
}
}
}
}
public void unmodifiableFreeze(EntityBean bean) {
if (query.isUnmodifiable() && bean != null) {
beanDescriptor.freeze(bean);
}
}
}
@@ -11,14 +11,15 @@ import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.plugin.Lookups;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.api.CoreLog;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
@@ -94,9 +95,9 @@ public class BootupClasses implements Predicate<Class<?>> {
public void runServerConfigStartup(DatabaseBuilder config) {
for (Class<?> cls : serverConfigStartupCandidates) {
try {
ServerConfigStartup newInstance = (ServerConfigStartup) cls.getDeclaredConstructor().newInstance();
ServerConfigStartup newInstance = Lookups.newDefaultInstance(cls);
newInstance.onStart(config);
} catch (Exception e) {
} catch (Throwable e) {
// assume that the desired behavior is to fail - add your own try catch if needed
throw new IllegalStateException("Error running ServerConfigStartup " + cls, e);
}
@@ -206,12 +207,12 @@ public class BootupClasses implements Predicate<Class<?>> {
*/
private <T> T create(Class<T> cls, boolean logOnException) {
try {
return cls.getConstructor().newInstance();
return Lookups.newDefaultInstance(cls);
} catch (NoSuchMethodException e) {
log.log(DEBUG, "Ignore/expected - no default constructor: {0}", e.getMessage());
return null;
} catch (Exception e) {
} catch (Throwable e) {
if (logOnException) {
// not expected but we log and carry on
log.log(ERROR, "Error creating " + cls, e);
@@ -14,7 +14,7 @@ import java.sql.SQLException;
abstract class AssocOneHelp {
final BeanPropertyAssocOne<?> property;
private final BeanDescriptor<?> target;
protected final BeanDescriptor<?> target;
private final String path;
AssocOneHelp(BeanPropertyAssocOne<?> property) {
@@ -66,9 +66,8 @@ abstract class AssocOneHelp {
if (existing != null) {
return existing;
}
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (!disableLazyLoading) {
Object ref = target.contextRef(pc, id, ctx.unmodifiable(), ctx.isDisableLazyLoading());
if (!ctx.unmodifiable() && !ctx.isDisableLazyLoading()) {
ctx.register(path, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
@@ -81,7 +80,6 @@ abstract class AssocOneHelp {
Object val = read(ctx);
if (bean != null) {
property.setValue(bean, val);
ctx.propagateState(val);
}
return val;
}
@@ -32,7 +32,7 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
@Override
Object read(DataReader reader) throws SQLException {
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
EntityBean embeddedBean = property.targetDescriptor.createEntityBean2(reader.unmodifiable());
boolean notNull = false;
for (BeanProperty property : property.embeddedProps) {
Object value = property.readSet(reader, embeddedBean);
@@ -53,7 +53,6 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
if (bean != null) {
// set back to the parent bean
property.setValue(bean, dbVal);
ctx.propagateState(dbVal);
return dbVal;
} else {
return null;
@@ -62,7 +61,7 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
@Override
Object read(DbReadContext ctx) throws SQLException {
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
EntityBean embeddedBean = property.targetDescriptor.createEntityBean2(ctx.unmodifiable());
boolean notNull = false;
for (BeanProperty property : property.embeddedProps) {
Object value = property.readSet(ctx, embeddedBean);
@@ -70,12 +69,7 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
notNull = true;
}
}
if (notNull) {
ctx.propagateState(embeddedBean);
return embeddedBean;
} else {
return null;
}
return notNull ? embeddedBean : null;
}
@Override
@@ -53,9 +53,8 @@ final class AssocOneHelpRefInherit extends AssocOneHelp {
return existing;
}
// for inheritance hierarchy create the correct type for this row...
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (!disableLazyLoading) {
Object ref = desc.contextRef(pc, id, ctx.unmodifiable(), ctx.isDisableLazyLoading());
if (!ctx.unmodifiable() && !ctx.isDisableLazyLoading()) {
ctx.registerBeanInherit(property, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
@@ -23,6 +23,7 @@ import io.ebean.meta.QueryPlanInit;
import io.ebean.plugin.BeanDocType;
import io.ebean.plugin.BeanType;
import io.ebean.plugin.ExpressionPath;
import io.ebean.plugin.Lookups;
import io.ebean.plugin.Property;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.*;
@@ -414,8 +415,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
return null;
}
try {
return (EntityBean) beanType.getDeclaredConstructor().newInstance();
} catch (Exception e) {
return Lookups.newDefaultInstance(beanType);
} catch (Throwable e) {
throw new IllegalStateException("Error trying to create the prototypeEntityBean for " + beanType, e);
}
}
@@ -687,6 +688,22 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
}
}
@Override
public void freeze(EntityBean entityBean) {
if (entityBean._ebean_getIntercept().freeze()) {
// recursively freeze the graph for this entityBean
for (BeanProperty beanProperty : propertiesMutable) {
beanProperty.freeze(entityBean);
}
for (BeanPropertyAssocOne<?> one : propertiesOne) {
one.freeze(entityBean);
}
for (BeanPropertyAssocMany<?> many : propertiesMany) {
many.freeze(entityBean);
}
}
}
public void metricPersistBatch(PersistRequest.Type type, long startNanos, int size) {
iudMetrics.addBatch(type, startNanos, size);
}
@@ -1189,8 +1206,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
/**
* Try to load the beanCollection from cache return true if successful.
*/
public boolean cacheManyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, String parentKey, Boolean readOnly) {
return cacheHelp.manyPropLoad(many, bc, parentKey, readOnly);
public boolean cacheManyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, String parentKey) {
return cacheHelp.manyPropLoad(many, bc, parentKey);
}
/**
@@ -1239,8 +1256,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
/**
* Load the entity bean as the correct bean type.
*/
EntityBean cacheBeanLoadDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
return cacheHelp.loadBeanDirect(id, readOnly, data, context);
EntityBean cacheBeanLoadDirect(Object id, boolean unmodifiable, CachedBeanData data, PersistenceContext context) {
return cacheHelp.loadBeanDirect(id, unmodifiable, data, context);
}
/**
@@ -1278,8 +1295,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
/**
* Return a bean from the bean cache (or null).
*/
public T cacheBeanGet(Object id, Boolean readOnly, PersistenceContext context) {
return cacheHelp.beanCacheGet(cacheKey(id), readOnly, context);
public T cacheBeanGet(Object id, boolean unmodifiable, PersistenceContext context) {
return cacheHelp.beanCacheGet(cacheKey(id), unmodifiable, context);
}
/**
@@ -1308,15 +1325,15 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
return cacheHelp.beanCacheLoad(bean, ebi, cacheKey(id), context);
}
public BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
return cacheHelp.cacheIdLookup(context, ids);
public BeanCacheResult<T> cacheIdLookup(PersistenceContext context, boolean unmodifiable, Collection<?> ids) {
return cacheHelp.cacheIdLookup(context, unmodifiable, ids);
}
/**
* Use natural key lookup to hit the bean cache.
*/
public BeanCacheResult<T> naturalKeyLookup(PersistenceContext context, Set<Object> keys) {
return cacheHelp.naturalKeyLookup(context, keys);
public BeanCacheResult<T> naturalKeyLookup(PersistenceContext context, boolean unmodifiable, Set<Object> keys) {
return cacheHelp.naturalKeyLookup(context, unmodifiable, keys);
}
public void cacheNaturalKeyPut(String key, String newKey) {
@@ -1764,10 +1781,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
/**
* Create a reference with a check for the bean in the persistence context.
*/
public EntityBean createReference(Boolean readOnly, Object id, PersistenceContext pc) {
public EntityBean createReference(PersistenceContext pc, Object id) {
Object refBean = contextGet(pc, id);
if (refBean == null) {
refBean = createReference(readOnly, false, id, pc);
refBean = createReference(false, false, id, pc);
}
return (EntityBean) refBean;
}
@@ -1776,8 +1793,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
* Create a reference bean based on the id.
*/
@SuppressWarnings("unchecked")
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
public T createReference(boolean unmodifiable, boolean disableLazyLoad, Object id, PersistenceContext pc) {
if (cacheSharableBeans && unmodifiable) {
CachedBeanData d = cacheHelp.beanCacheGetData(cacheKey(id));
if (d != null) {
Object shareableBean = d.getSharableBean();
@@ -1793,18 +1810,15 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
if (inheritInfo != null && !inheritInfo.isConcrete()) {
return findReferenceBean(id, pc);
}
EntityBean eb = createEntityBean();
EntityBean eb = createEntityBean2(unmodifiable);
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
if (disableLazyLoad) {
ebi.setDisableLazyLoad(true);
} else {
} else if (!unmodifiable) {
ebi.setBeanLoader(refBeanLoader());
}
ebi.setReference(idPropertyIndex);
if (Boolean.TRUE == readOnly) {
ebi.setReadOnly(true);
}
if (pc != null) {
contextPut(pc, id, eb);
ebi.setPersistenceContext(pc);
@@ -2029,11 +2043,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
return pc.putIfAbsent(rootBeanType, id, localBean);
}
/**
* Create a reference bean and put it in the persistence context (and return it).
*/
public Object contextRef(PersistenceContext pc, Boolean readOnly, boolean disableLazyLoad, Object id) {
return createReference(readOnly, disableLazyLoad, id, pc);
public Object contextRef(PersistenceContext pc, Object id) {
return createReference(false, false, id, pc);
}
public Object contextRef(PersistenceContext pc, Object id, boolean unmodifiable, boolean disableLazyLoad) {
return createReference(unmodifiable, disableLazyLoad, id, pc);
}
/**
@@ -2148,7 +2163,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
* Set the Id value to the bean (without type conversion).
*/
public void setId(Object idValue, EntityBean bean) {
idProperty.setValueIntercept(bean, idValue);
idProperty.setValue(bean, idValue);
}
@Override
@@ -3186,8 +3201,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
* provides unique ordering of the rows (so that the paging is predicable).
*/
public void appendOrderById(SpiQuery<T> query) {
if (idProperty != null && !idProperty.isEmbedded() && !query.order().containsProperty(idProperty.name())) {
query.order().asc(idProperty.name());
if (idProperty != null && !idProperty.isEmbedded() && !query.orderBy().containsProperty(idProperty.name())) {
query.orderBy().asc(idProperty.name());
}
}
@@ -261,7 +261,7 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Try to load the bean collection from cache return true if successful.
*/
boolean manyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, String parentKey, Boolean readOnly) {
boolean manyPropLoad(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, String parentKey) {
if (many.isElementCollection()) {
// held as part of the bean cache so skip
return false;
@@ -280,7 +280,7 @@ final class BeanDescriptorCacheHelp<T> {
bc.checkEmptyLazyLoad();
int i = 0;
for (Object id : idList) {
final EntityBean ref = targetDescriptor.createReference(readOnly, id, persistenceContext);
final EntityBean ref = targetDescriptor.createReference(persistenceContext, id);
if (many.hasOrderColumn()) {
ref._ebean_getIntercept().setSortOrder(++i);
}
@@ -344,7 +344,7 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Hit the bean cache with the given ids returning the hits.
*/
BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
BeanCacheResult<T> cacheIdLookup(PersistenceContext context, boolean unmodifiable, Collection<?> ids) {
Set<Object> keys = new HashSet<>(ids.size());
for (Object id : ids) {
keys.add(desc.cacheKey(id));
@@ -359,7 +359,7 @@ final class BeanDescriptorCacheHelp<T> {
BeanCacheResult<T> result = new BeanCacheResult<>();
for (Map.Entry<Object, Object> entry : beanDataMap.entrySet()) {
CachedBeanData cachedBeanData = (CachedBeanData) entry.getValue();
T bean = convertToBean(entry.getKey(), false, context, cachedBeanData);
T bean = convertToBean(entry.getKey(), unmodifiable, context, cachedBeanData);
result.add(bean, desc.id(bean));
}
return result;
@@ -368,7 +368,7 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Use natural keys to hit the bean cache and return resulting hits.
*/
BeanCacheResult<T> naturalKeyLookup(PersistenceContext context, Set<Object> keys) {
BeanCacheResult<T> naturalKeyLookup(PersistenceContext context, boolean unmodifiable, Set<Object> keys) {
if (context == null) {
context = new DefaultPersistenceContext();
}
@@ -399,7 +399,7 @@ final class BeanDescriptorCacheHelp<T> {
for (Map.Entry<Object, Object> entry : beanDataMap.entrySet()) {
Object id = entry.getKey();
CachedBeanData cachedBeanData = (CachedBeanData) entry.getValue();
T bean = convertToBean(id, false, context, cachedBeanData);
T bean = convertToBean(id, unmodifiable, context, cachedBeanData);
Object naturalKey = reverseMap.get(id);
result.add(bean, naturalKey);
}
@@ -561,9 +561,9 @@ final class BeanDescriptorCacheHelp<T> {
return (CachedBeanData) getBeanCache().get(key);
}
T beanCacheGet(String key, Boolean readOnly, PersistenceContext context) {
T bean = beanCacheGetInternal(key, readOnly, context);
if (bean != null) {
T beanCacheGet(String key, boolean unmodifiable, PersistenceContext context) {
T bean = beanCacheGetInternal(key, unmodifiable, context);
if (bean != null && !unmodifiable) {
setupContext(bean, context);
}
return bean;
@@ -572,7 +572,7 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Return a bean from the bean cache.
*/
private T beanCacheGetInternal(String key, Boolean readOnly, PersistenceContext context) {
private T beanCacheGetInternal(String key, boolean unmodifiable, PersistenceContext context) {
CachedBeanData data = (CachedBeanData) getBeanCache().get(key);
if (data == null) {
if (beanLog.isLoggable(TRACE)) {
@@ -583,12 +583,12 @@ final class BeanDescriptorCacheHelp<T> {
if (beanLog.isLoggable(TRACE)) {
beanLog.log(TRACE, " GET {0}({1}) - hit", cacheName, key);
}
return convertToBean(key, readOnly, context, data);
return convertToBean(key, unmodifiable, context, data);
}
@SuppressWarnings("unchecked")
private T convertToBean(Object id, Boolean readOnly, PersistenceContext context, CachedBeanData data) {
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
private T convertToBean(Object id, boolean unmodifiable, PersistenceContext context, CachedBeanData data) {
if (cacheSharableBeans && unmodifiable) {
Object bean = data.getSharableBean();
if (bean != null) {
if (beanLog.isLoggable(TRACE)) {
@@ -600,18 +600,18 @@ final class BeanDescriptorCacheHelp<T> {
return (T) bean;
}
}
return (T) loadBean(id, readOnly, data, context);
return (T) loadBean(id, unmodifiable, data, context);
}
/**
* Load the entity bean taking into account inheritance.
*/
private EntityBean loadBean(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
private EntityBean loadBean(Object id, boolean unmodifiable, CachedBeanData data, PersistenceContext context) {
String discValue = data.getDiscValue();
if (discValue == null) {
return loadBeanDirect(id, readOnly, data, context);
return loadBeanDirect(id, unmodifiable, data, context);
} else {
return rootDescriptor(discValue).cacheBeanLoadDirect(id, readOnly, data, context);
return rootDescriptor(discValue).cacheBeanLoadDirect(id, unmodifiable, data, context);
}
}
@@ -625,26 +625,22 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Load the entity bean from cache data given this is the root bean type.
*/
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
EntityBean loadBeanDirect(Object id, boolean unmodifiable, CachedBeanData data, PersistenceContext context) {
id = desc.convertId(id);
EntityBean bean = null;
if (context == null) {
context = new DefaultPersistenceContext();
} else {
bean = (EntityBean) desc.contextGet(context, id);
}
EntityBean bean = context == null ? null : (EntityBean) desc.contextGet(context, id);;
if (bean == null) {
bean = desc.createEntityBean();
bean = desc.createEntityBean2(unmodifiable);
desc.setId(id, bean);
desc.contextPut(context, id, bean);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// Not using loadContext here so no batch lazy loading for these beans
ebi.setBeanLoader(desc.l2BeanLoader());
if (Boolean.TRUE.equals(readOnly)) {
ebi.setReadOnly(true);
if (!unmodifiable) {
if (context == null) {
context = new DefaultPersistenceContext();
}
desc.contextPut(context, id, bean);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
ebi.setPersistenceContext(context);
// Not using loadContext here so no batch lazy loading for these beans
ebi.setBeanLoader(desc.l2BeanLoader());
}
ebi.setPersistenceContext(context);
}
CachedBeanDataToBean.load(desc, bean, data, context);
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy;
import io.ebean.PersistenceIOException;
import io.ebean.SqlUpdate;
import io.ebean.bean.EntityBean;
import io.ebean.plugin.Lookups;
import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
@@ -22,8 +23,8 @@ class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
BeanDescriptorElementEmbedded(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
super(owner, deploy, elementHelp);
try {
this.prototype = (EntityBean) beanType.getDeclaredConstructor().newInstance();
} catch (Exception e) {
this.prototype = Lookups.newDefaultInstance(beanType);
} catch (Throwable e) {
throw new IllegalStateException("Unable to create entity bean prototype for "+beanType);
}
BeanPropertyAssocOne<?>[] embedded = propertiesEmbedded();
@@ -1002,11 +1002,8 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
if (!(mappedProp instanceof DeployBeanPropertyAssocOne<?>)) {
throw new PersistenceException("Error on " + prop + ". mappedBy property " + targetDesc + "." + mappedBy + " is not a OneToOne?");
}
DeployBeanPropertyAssocOne<?> mappedAssocOne = (DeployBeanPropertyAssocOne<?>) mappedProp;
if (!mappedAssocOne.isOneToOne()) {
throw new PersistenceException("Error on " + prop + ". mappedBy property " + targetDesc + "." + mappedBy + " is not a OneToOne?");
}
return mappedAssocOne;
// this is allowed to be a OneToOne or ManyToOne
return (DeployBeanPropertyAssocOne<?>) mappedProp;
}
private void checkUniDirectionalPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.DataIntegrityException;
import io.ebean.ModifyAwareType;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -1519,4 +1520,15 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
desc.registerColumn(dbColumn, path);
}
}
/**
* Freeze mutable types (like DbArray).
*/
public void freeze(EntityBean entityBean) {
Object value = getValue(entityBean);
if (value instanceof ModifyAwareType) {
ModifyAwareType bc = (ModifyAwareType) value;
setValue(entityBean, bc.freeze());
}
}
}
@@ -427,7 +427,9 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
BeanProperty[] others = target.propertiesBaseScalar();
if (descriptor.isRawSqlBased()) {
String dbColumn = owner.dbColumn();
return new ImportedIdSimple(owner, dbColumn, null, idProp, 0);
if (dbColumn != null) {
return new ImportedIdSimple(owner, dbColumn, null, idProp, 0);
}
}
if (idProp == null) {
return null;
@@ -1165,4 +1165,22 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
return false;
}
}
@Override
public void freeze(EntityBean entityBean) {
Object value = getValue(entityBean);
if (value instanceof BeanCollection) {
BeanCollection<?> beanCollection = (BeanCollection<?>) value;
Collection<?> entities = beanCollection.actualDetails();
if (entities != null) {
for (Object actualEntry : entities) {
targetDescriptor.freeze((EntityBean) actualEntry);
}
}
setValue(entityBean, beanCollection.freeze());
} else if (value == null) {
// make it an error to access the collection (no lazy loading allowed)
entityBean._ebean_getIntercept().setPropertyUnloaded(propertyIndex);
}
}
}
@@ -663,6 +663,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
setValue(entityBean, targetDescriptor.createRef(tenantId, null));
}
@Override
public void freeze(EntityBean entityBean) {
Object value = getValue(entityBean);
if (value instanceof EntityBean) {
targetDescriptor.freeze((EntityBean) value);
}
}
@Override
public void setValue(EntityBean bean, Object value) {
super.setValue(bean, value);
@@ -14,16 +14,6 @@ import java.util.Map;
*/
public interface DbReadContext {
/**
* Return the state of the object graph.
*/
Boolean isReadOnly();
/**
* Propagate the state to the bean.
*/
void propagateState(Object e);
/**
* Return the DataReader.
*/
@@ -102,4 +92,14 @@ public interface DbReadContext {
*/
void handleLoadError(String fullName, Exception e);
/**
* Return true if this many property should be included in unmodifiable
* query via a secondary query.
*/
boolean includeSecondary(BeanPropertyAssocMany<?> many);
/**
* Return true if we are loading unmodifiable beans.
*/
boolean unmodifiable();
}
@@ -228,8 +228,8 @@ public final class InheritInfo {
/**
* Create an EntityBean for this type.
*/
public EntityBean createEntityBean() {
return descriptor.createEntityBean();
public EntityBean createEntityBean(boolean unmodifiable) {
return descriptor.createEntityBean2(unmodifiable);
}
/**
@@ -20,6 +20,7 @@ public final class TableJoinColumn {
private final String foreignSqlFormula;
private final boolean insertable;
private final boolean updateable;
private final boolean nullable;
/**
* Hash for including in a query plan
*/
@@ -34,7 +35,8 @@ public final class TableJoinColumn {
this.localSqlFormula = InternString.intern(deploy.getLocalSqlFormula());
this.foreignSqlFormula = InternString.intern(deploy.getForeignSqlFormula());
this.insertable = deploy.isInsertable();
this.updateable = deploy.isUpdateable();
this.updateable = deploy.isUpdatable();
this.nullable = deploy.isNullable();
this.queryHash = hash();
}
@@ -45,6 +47,7 @@ public final class TableJoinColumn {
this.foreignSqlFormula = null;
this.insertable = source.isInsertable();
this.updateable = source.isUpdateable();
this.nullable = source.isNullable();
this.queryHash = hash();
}
@@ -115,6 +118,13 @@ public final class TableJoinColumn {
return updateable;
}
/**
* Return if this column is nullable.
*/
public boolean isNullable() {
return nullable;
}
public String getLocalSqlFormula() {
return localSqlFormula;
}

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