Compare commits

..
49 Commits
Author SHA1 Message Date
Rob Bygrave fc124e8318 Tidy pom only 2025-04-30 00:02:30 +12:00
Rob Bygrave f4fd4f4b09 Version 16.0.0-RC2 2025-04-30 00:01:52 +12:00
Rob Bygrave 16f5311885 Bump parent pom to 5.1 for new maven central deploy 2025-04-29 23:41:48 +12:00
Rob Bygrave 75a0675977 Merge pull request #3630 from ebean-orm/feature/fix-ea-build-kotlin
Fix EA build, Kotlin not supporting 25-EA release
2025-04-29 23:37:10 +12:00
Rob Bygrave 80ebf4a1ee Fix EA build, Kotlin not supporting 25-EA release
It barfs trying to parse the 25-EA version, means the kotlin-querybean-generator fails the build for the Java Early Release versions.

With this, it will skip the kotlin modules for the Java Early Access release builds.
2025-04-29 23:36:22 +12:00
Rob Bygrave 4185beb5aa Merge pull request #3629 from ebean-orm/feature/bump-datasource-10
Bump ebean-datasource to 10.0 (Virtual Threads support via MR Jar)
2025-04-29 23:17:54 +12:00
Rob Bygrave 79908c0164 Bump ebean-datasource to 10.0 (Virtual Threads support via MR Jar)
Note that this still supports Java 11.

The 10.0 version of ebean-datasource uses Multi-Version jar such that if running using Java 21+ then it will use Virtual Threads for the background Heartbeat validation and also background closing of connections.
2025-04-29 23:09:49 +12:00
Rob Bygrave b5a16f42ca #3626 Add tests for bean property name instead of db column 2025-04-23 22:33:18 +12:00
Gonçalo Andrade a320a132b3 Fix incorrect usage of bean property name instead of corresponding column 2025-04-22 14:51:38 +01:00
Rob Bygrave 9e84e93511 Merge pull request #3621 from FOCONIS/new-db-versions
New db versions + testcontainers 7.8
2025-04-17 23:18:05 +12:00
Noemi Praml 6fb40c71ef update StartDb2 2025-04-16 15:22:08 +02:00
Noemi Praml 202b9ae94a update to testcontainers 7.8 2025-04-16 14:52:21 +02:00
Noemi Praml d79b452378 new db2 version 2025-04-16 14:19:11 +02:00
Noemi Praml 78a408dd10 new sqlserver version 2025-04-16 14:19:11 +02:00
Noemi Praml 6af974e432 new mariadb version 2025-04-16 14:19:11 +02:00
Rob Bygrave 04a8475291 Merge pull request #3623 from ebean-orm/feature/readOnly-DataSource-mark-nullable
Explicitly mark Database.readOnlyDataSource() as @Nullable
2025-04-15 22:25:06 +12:00
Rob Bygrave c56345f92e Explicitly mark Database.readOnlyDataSource() as @Nullable
Using JSpecify @Nullable on the return for Database.readOnlyDataSource()
2025-04-15 22:24:14 +12:00
Rob Bygrave ed0a46955e Explicitly mark Database.readOnlyDataSource() as @Nullable
Using JSpecify @Nullable on the return for Database.readOnlyDataSource()
2025-04-15 22:22:08 +12:00
Rob Bygrave 3c4b04a893 Version 16.0.0-rc1 2025-04-09 23:18:27 +12:00
Rob Bygrave ee403bdb83 Merge pull request #3620 from ebean-orm/feature/bump-ebean-agent
Bump the ebean-agent to 14.12.0, support for Lombok SuperBuilder
2025-04-09 23:02:46 +12:00
Rob Bygrave b18abe2221 Bump the ebean-agent to 14.12.0, support for Lombok SuperBuilder 2025-04-09 22:52:39 +12:00
Rob Bygrave d68f372d99 Merge pull request #3619 from ebean-orm/feature/column-length-255
For Postgres @Column(length=255) means varchar(255)
2025-04-09 20:22:56 +12:00
Rob Bygrave c8f769aa2a For Postgres @Column(length=255) means varchar(255)
The `@Column` length defaults to 0 rather than 255, this allows us to detect when it has been explicitly set and thus for Postgres we can now have:

@Column(length=255) mapping to -> varchar(255)
@Column mapping to -> varchar
2025-04-09 20:07:56 +12:00
Rob Bygrave bde626fd45 Merge pull request #3618 from ebean-orm/feature/bump-datasource-95
Bump ebean-datasource to 9.5
2025-04-09 20:04:23 +12:00
Rob Bygrave b828b73b2c Bump ebean-datasource to 9.5
When using DataSourceBuilder for both main and readOnly datasource, the maxConnections for the readOnly will default to the main pool maxConnections.
2025-04-09 20:02:14 +12:00
Rob Bygrave 2795a8578f Merge pull request #3616 from ebean-orm/feature/lazyLoadBatch-100
Increase default lazyLoadBatchSize from 10 to 100
2025-04-04 21:15:23 +13:00
Rob Bygrave bd0a3c2303 Increase default lazyLoadBatchSize from 10 to 100 2025-04-04 21:14:47 +13:00
Rob Bygrave 21e50e9d00 Merge pull request #3615 from ebean-orm/feature/3612-redis-exception
#3612 Catch all RedisException (rather than only IOException)
2025-04-04 20:47:44 +13:00
Rob Bygrave 88a47bf0a9 #3612 Catch all RedisException (rather than only IOException) 2025-04-04 20:45:59 +13:00
Rob Bygrave d9a45245f4 Version 16.0.0-beta1 2025-04-03 19:35:14 +13:00
Rob Bygrave 7be72df788 Merge pull request #3570 from ebean-orm/feature/improved-readOnly-immutable
Unmodifiable - Improved read only immutable entity beans / graphs
2025-04-02 22:24:19 +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 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 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 e6f92eaceb ReadOnly Immutable query WIP 2024-10-24 16:02:50 +13:00
181 changed files with 1492 additions and 1421 deletions
+1 -1
View File
@@ -34,5 +34,5 @@ jobs:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: mariadb 10.6
- name: mariadb 10.11
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-mariadb.properties
+2 -2
View File
@@ -34,5 +34,5 @@ jobs:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: sqlserver 2017
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-sqlserver17.properties
- name: sqlserver 2022
run: mvn -T 8 clean test -Dprops.file=testconfig/ebean-sqlserver.properties
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-net-postgis-types</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -41,7 +41,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -60,13 +60,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</parent>
<artifactId>composites</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</parent>
<name>ebean api</name>
@@ -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
@@ -137,6 +137,7 @@ public interface Database {
/**
* Return the associated read only DataSource for this Database instance (can be null).
*/
@Nullable
DataSource readOnlyDataSource();
/**
@@ -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();
}
}
@@ -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.
@@ -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).
*/
@@ -552,4 +540,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
@@ -401,7 +400,7 @@ public class InterceptReadOnly implements EntityBeanIntercept {
@Override
public void initialisedMany(int propertyIndex) {
loaded[propertyIndex] = true;
}
@Override
@@ -416,12 +415,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 +437,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
@@ -22,7 +22,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,10 +56,6 @@ 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;
/**
@@ -67,7 +63,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
*/
private int state;
private boolean forceUpdate;
private boolean readOnly;
private boolean dirty;
/**
* Flag set to disable lazy loading.
@@ -77,7 +72,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 +95,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 +103,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 +129,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
'}';
}
@Override
public EntityBean owner() {
return owner;
}
@Override
public PersistenceContext persistenceContext() {
return persistenceContext;
@@ -200,16 +193,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 +281,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 +384,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 +622,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];
@@ -708,7 +650,7 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
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;
@@ -845,18 +787,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();
}
}
}
}
@@ -251,7 +251,7 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
/**
* The default batch size for lazy loading
*/
private int lazyLoadBatchSize = 10;
private int lazyLoadBatchSize = 100;
/**
* The default batch size for 'query joins'.
@@ -77,6 +77,8 @@ class DatabaseConfigTest {
props.setProperty("readOnlyDatabase", "true");
props.setProperty("lengthCheck", "ON");
props.setProperty("includeLabelInSql", "false");
props.setProperty("lazyLoadBatchSize", "50");
props.setProperty("queryBatchSize", "60");
props.setProperty("queryPlan.enable", "true");
props.setProperty("queryPlan.thresholdMicros", "10000");
@@ -100,6 +102,8 @@ class DatabaseConfigTest {
assertTrue(settings.readOnlyDatabase());
assertFalse(settings.isIncludeLabelInSql());
assertThat(settings.getLengthCheck()).isEqualTo(LengthCheck.ON);
assertThat(settings.getLazyLoadBatchSize()).isEqualTo(50);
assertThat(settings.getQueryBatchSize()).isEqualTo(60);
assertTrue(settings.isIdGeneratorAutomatic());
assertFalse(settings.getPlatformConfig().isCaseSensitiveCollation());
@@ -179,6 +183,8 @@ class DatabaseConfigTest {
assertTrue(config.getPlatformConfig().isCaseSensitiveCollation());
assertTrue(config.isAutoLoadModuleInfo());
assertTrue(config.isLoadModuleInfo());
assertThat(config.getLazyLoadBatchSize()).isEqualTo(100);
assertThat(config.getQueryBatchSize()).isEqualTo(100);
assertFalse(config.isQueryPlanEnable());
assertEquals(Long.MAX_VALUE, config.getQueryPlanThresholdMicros());
@@ -197,6 +203,10 @@ class DatabaseConfigTest {
assertTrue(config.isAutoPersistUpdates());
config.setSkipDataSourceCheck(true);
assertTrue(config.skipDataSourceCheck());
config.lazyLoadBatchSize(20);
assertThat(config.getLazyLoadBatchSize()).isEqualTo(20);
config.queryBatchSize(30);
assertThat(config.getQueryBatchSize()).isEqualTo(30);
}
@Test
+26 -26
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</parent>
<name>ebean bom</name>
@@ -89,25 +89,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -125,13 +125,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -155,37 +155,37 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<!-- platforms -->
@@ -193,79 +193,79 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</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.11.0</version>
<version>16.0.0-RC2</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,7 +22,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -46,7 +46,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</dependency>
<dependency>
@@ -159,21 +159,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<scope>test</scope>
</dependency>
@@ -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);
}
@@ -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);
}
}
@@ -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
@@ -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;
@@ -279,6 +278,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
@Nullable
public DataSource readOnlyDataSource() {
return transactionManager.readOnlyDataSource();
}
@@ -610,7 +610,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);
}
@@ -994,7 +994,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);
}
/**
@@ -1205,11 +1205,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);
}
@@ -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);
}
}
}
@@ -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;
@@ -688,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);
}
@@ -1190,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);
}
/**
@@ -1240,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);
}
/**
@@ -1279,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);
}
/**
@@ -1309,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) {
@@ -1765,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;
}
@@ -1777,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();
@@ -1794,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);
@@ -2030,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);
}
/**
@@ -2149,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
@@ -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);
@@ -58,7 +58,7 @@ final class BeanEmbeddedMetaFactory {
}
private static int dbLength(Column override, BeanProperty source) {
return (override != null && (override.length() != 255)) ? override.length() : source.dbLength();
return (override != null && (override.length() != 0)) ? override.length() : source.dbLength();
}
private static int dbScale(Column override, BeanProperty source) {
@@ -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());
}
}
}
@@ -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);
}
}
}
@@ -666,6 +666,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);
}
/**
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.id;
import io.ebean.bean.EntityBean;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiExpressionBind;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
@@ -324,7 +323,7 @@ public final class IdBinderEmbedded implements IdBinder {
@Override
public Object read(DbReadContext ctx) throws SQLException {
final EntityBean embId = idDesc.createEntityBean();
final EntityBean embId = idDesc.createEntityBean2(ctx.unmodifiable());
boolean nullValue = true;
for (BeanProperty prop : props) {
final Object value = prop.read(ctx);
@@ -245,7 +245,8 @@ public final class IdBinderSimple implements IdBinder {
idValue = scalarType.toBeanType(idValue);
}
if (bean != null) {
idProperty.setValueIntercept(bean, idValue);
// not using interception to support unmodifiable entities
idProperty.setValue(bean, idValue);
}
return idValue;
}
@@ -263,6 +264,6 @@ public final class IdBinderSimple implements IdBinder {
@Override
public String idNullOr(String prefix, String filterManyExpression) {
return "(${" + prefix + "}" + idProperty.name() + " is null or (" + filterManyExpression + "))";
return "(${" + prefix + "}" + idProperty.dbColumn() + " is null or (" + filterManyExpression + "))";
}
}
@@ -98,7 +98,7 @@ public abstract class AnnotationParser extends AnnotationBase {
prop.setUnique(columnAnn.unique());
if (columnAnn.precision() > 0) {
prop.setDbLength(columnAnn.precision());
} else if (columnAnn.length() != 255) {
} else if (columnAnn.length() != 0) {
// set default 255 on DbTypeMap
prop.setDbLength(columnAnn.length());
}
@@ -11,9 +11,7 @@ import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Default implementation of LoadContext.
@@ -27,7 +25,7 @@ public final class DLoadContext implements LoadContext {
private final DLoadBeanContext rootBeanContext;
private final boolean asDraft;
private final Timestamp asOf;
private final Boolean readOnly;
private final boolean unmodifiable;
private final CacheMode useBeanCache;
private final int defaultBatchSize;
private final boolean disableLazyLoading;
@@ -48,6 +46,7 @@ public final class DLoadContext implements LoadContext {
boolean useReferences;
private List<OrmQueryProperties> secQuery;
private Object tenantId;
private final Set<BeanProperty> secondaryProperties;
/**
* Construct for use with JSON marshalling (doc store).
@@ -62,7 +61,7 @@ public final class DLoadContext implements LoadContext {
this.useBeanCache = CacheMode.OFF;
this.asDraft = false;
this.asOf = null;
this.readOnly = false;
this.unmodifiable = false;
this.disableLazyLoading = false;
this.disableReadAudit = false;
this.includeSoftDeletes = false;
@@ -71,6 +70,7 @@ public final class DLoadContext implements LoadContext {
this.profileLocation = null;
this.profilingListener = null;
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, null);
this.secondaryProperties = null;
}
private ObjectGraphOrigin initOrigin() {
@@ -90,13 +90,14 @@ public final class DLoadContext implements LoadContext {
this.asOf = query.getAsOf();
this.asDraft = query.isAsDraft();
this.includeSoftDeletes = query.isIncludeSoftDeletes() && query.mode() == SpiQuery.Mode.NORMAL;
this.readOnly = query.isReadOnly();
this.unmodifiable = query.isUnmodifiable();
this.disableReadAudit = query.isDisableReadAudit();
this.disableLazyLoading = query.isDisableLazyLoading();
this.useBeanCache = query.beanCacheMode();
this.profilingListener = query.profilingListener();
this.planLabel = query.planLabel();
this.profileLocation = query.profileLocation();
this.secondaryProperties = query.isUnmodifiable() ? new HashSet<>() : null;
ObjectGraphNode parentNode = query.parentNode();
if (parentNode != null) {
@@ -156,6 +157,14 @@ public final class DLoadContext implements LoadContext {
ElPropertyValue elGetValue = rootDescriptor.elGetValue(props.getPath());
boolean many = elGetValue.beanProperty().containsMany();
registerSecondaryNode(many, props);
if (many && secondaryProperties != null) {
secondaryProperties.add(elGetValue.beanProperty());
}
}
@Override
public boolean includeSecondary(BeanPropertyAssocMany<?> many) {
return secondaryProperties != null && secondaryProperties.contains(many);
}
boolean isBeanCacheGet() {
@@ -230,14 +239,6 @@ public final class DLoadContext implements LoadContext {
return ebeanServer;
}
/**
* Return the parent state which defines the sharedInstance and readOnly status
* which needs to be propagated to other beans and collections.
*/
Boolean isReadOnly() {
return readOnly;
}
@Override
public PersistenceContext persistenceContext() {
return persistenceContext;
@@ -320,9 +321,7 @@ public final class DLoadContext implements LoadContext {
if (useDocStore && docStoreMapped) {
query.setUseDocStore(true);
}
if (readOnly != null) {
query.setReadOnly(readOnly);
}
query.setUnmodifiable(unmodifiable);
query.setDisableLazyLoading(disableLazyLoading);
query.asOf(asOf);
if (asDraft) {
@@ -198,7 +198,7 @@ final class DLoadManyContext extends DLoadBaseContext implements LoadManyContext
BeanDescriptor<?> parentDesc = context.desc.descriptor(ownerBean.getClass());
Object parentId = parentDesc.getId(ownerBean);
final String parentKey = parentDesc.cacheKey(parentId);
if (parentDesc.cacheManyPropLoad(context.property, bc, parentKey, context.parent.isReadOnly())) {
if (parentDesc.cacheManyPropLoad(context.property, bc, parentKey)) {
// we loaded the bean collection from cache so remove it from the buffer
if (removeFromBuffer(bc)) {
bc.setLoader(context.parent.server());
@@ -401,7 +401,7 @@ public final class Binder {
return new DataBind(dataTimeZone, stmt, connection);
}
public DataReader createDataReader(ResultSet resultSet) {
return new RsetDataReader(dataTimeZone, resultSet);
public DataReader createDataReader(boolean unmodifiable, ResultSet resultSet) {
return new RsetDataReader(unmodifiable, dataTimeZone, resultSet);
}
}
@@ -44,6 +44,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
private final ReentrantLock lock = new ReentrantLock();
private final boolean loadContextBean;
private final boolean unmodifiable;
/**
* The resultSet rows read.
@@ -152,8 +153,6 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
private final ProfilingListener profilingListener;
private final Boolean readOnly;
private long profileOffset;
private long startNano;
@@ -186,7 +185,6 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
this.queryMode = query.mode();
this.loadContextBean = queryMode.isLoadContextBean() || query.getForUpdateLockType() != null;
this.lazyLoadManyProperty = query.lazyLoadMany();
this.readOnly = query.isReadOnly();
this.disableLazyLoading = query.isDisableLazyLoading();
this.objectGraphNode = query.parentNode();
this.profilingListener = query.profilingListener();
@@ -207,6 +205,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
} else {
this.help = createHelp(request);
}
this.unmodifiable = request.query().isUnmodifiable();
this.collection = (help != null ? help.createEmptyNoParent() : null);
}
@@ -239,17 +238,8 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
}
@Override
public Boolean isReadOnly() {
return readOnly;
}
@Override
public void propagateState(Object e) {
if (Boolean.TRUE.equals(readOnly)) {
if (e instanceof EntityBean) {
((EntityBean) e)._ebean_getIntercept().setReadOnly(true);
}
}
public boolean unmodifiable() {
return unmodifiable;
}
@Override
@@ -299,7 +289,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
if (resultSet == null) {
return false;
}
dataReader = queryPlan.createDataReader(resultSet);
dataReader = queryPlan.createDataReader(query.isUnmodifiable(), resultSet);
return true;
}
@@ -474,6 +464,14 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
return result;
}
EntityBean nextBean() {
EntityBean bean = next();
if (unmodifiable) {
request.unmodifiableFreeze(bean);
}
return bean;
}
EntityBean next() {
if (audit) {
auditNextBean();
@@ -603,6 +601,11 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
request.loadContext().register(path(many.name()), many, bc);
}
@Override
public boolean includeSecondary(BeanPropertyAssocMany<?> many) {
return request.loadContext().includeSecondary(many);
}
/**
* Return true if this is a raw sql query as opposed to Ebean generated sql.
*/
@@ -107,15 +107,9 @@ public final class CQueryEngine {
if (collection instanceof List) {
collection = (A) Collections.unmodifiableList((List<?>) collection);
request.putToQueryCache(collection);
if (Boolean.FALSE.equals(request.query().isReadOnly())) {
collection = (A) new ArrayList<>(collection);
}
} else if (collection instanceof Set) {
collection = (A) Collections.unmodifiableSet((Set<?>) collection);
request.putToQueryCache(collection);
if (Boolean.FALSE.equals(request.query().isReadOnly())) {
collection = (A) new LinkedHashSet<>(collection);
}
}
}
return collection;
@@ -358,6 +352,7 @@ public final class CQueryEngine {
if (request.isQueryCachePut()) {
request.addDependentTables(cquery.dependentTables());
}
request.unmodifiableFreeze(beanCollection);
return beanCollection;
} catch (SQLException e) {
@@ -390,6 +385,7 @@ public final class CQueryEngine {
cquery.auditFind(bean);
}
request.executeSecondaryQueries(false);
request.unmodifiableFreeze(bean);
return (T) bean;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
@@ -137,7 +137,7 @@ final class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Ca
} finally {
lock.unlock();
}
dataReader = new RsetDataReader(request.dataTimeZone(), pstmt.executeQuery());
dataReader = new RsetDataReader(query.isUnmodifiable(), request.dataTimeZone(), pstmt.executeQuery());
query.checkCancelled();
}
@@ -37,7 +37,7 @@ final class CQueryIteratorSimple<T> implements QueryIterator<T> {
@Override
@SuppressWarnings("unchecked")
public T next() {
return (T) cquery.next();
return (T) cquery.nextBean();
}
@Override
@@ -44,6 +44,7 @@ final class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
}
request.executeSecondaryQueries(true);
}
request.unmodifiableFreeze(buffer);
ret = !buffer.isEmpty();
return ret;
} catch (SQLException e) {
@@ -205,8 +205,8 @@ public class CQueryPlan implements SpiQueryPlan {
return new DQueryPlanOutput(beanType(), name, hash, sql, profileLocation, bind, planString);
}
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReader(dataTimeZone, rset);
public DataReader createDataReader(boolean unmodifiable, ResultSet rset) {
return new RsetDataReader(unmodifiable, dataTimeZone, rset);
}
/**
@@ -22,8 +22,8 @@ final class CQueryPlanRawSql extends CQueryPlan {
}
@Override
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReaderIndexed(dataTimeZone, rset, rsetIndexPositions);
public DataReader createDataReader(boolean unmodifiable, ResultSet rset) {
return new RsetDataReaderIndexed(unmodifiable, dataTimeZone, rset, rsetIndexPositions);
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
@@ -509,7 +509,7 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
}
@Override
public Query<T> setReadOnly(boolean readOnly) {
public Query<T> setUnmodifiable(boolean unmodifiable) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@@ -114,7 +114,7 @@ public final class DefaultOrmQueryEngine implements OrmQueryEngine {
}
@Override
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
public <T> Object findMany(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
BeanFindController finder = request.finder();
@@ -129,22 +129,21 @@ public final class DefaultOrmQueryEngine implements OrmQueryEngine {
result = finder.postProcessMany(request, result);
}
SpiQuery<T> query = request.query();
if (result != null && request.isBeanCachePutMany()) {
// load the individual beans into the bean cache
request.descriptor().cacheBeanPutAll(result.actualDetails());
}
request.mergeCacheHits(result);
if (request.isQueryCachePut()) {
// load the query result into the query cache
result.setReadOnly(true);
request.putToQueryCache(result);
if (Boolean.FALSE.equals(query.isReadOnly())) {
result = result.shallowCopy();
Object finalResult = result;
if (request.query().isUnmodifiable()) {
finalResult = result == null ? null : result.freeze();
if (request.isQueryCachePut()) {
// load the query result into the query cache
request.putToQueryCache(finalResult);
}
}
return result;
return finalResult;
}
/**
@@ -147,7 +147,7 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
request.setDefaultFetchBuffer(defaultFetchSizeFindList);
}
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
final DataReader dataReader = binder.createDataReader(request.resultSet());
final DataReader dataReader = binder.createDataReader(false, request.resultSet());
T value = null;
if (dataReader.next()) {
value = scalarType.read(dataReader);
@@ -172,7 +172,7 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
request.setDefaultFetchBuffer(defaultFetchSizeFindList);
}
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
final DataReader dataReader = binder.createDataReader(request.resultSet());
final DataReader dataReader = binder.createDataReader(false, request.resultSet());
List<T> rows = new ArrayList<>();
while (dataReader.next()) {
rows.add(scalarType.read(dataReader));
@@ -197,7 +197,7 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
final DataReader dataReader = binder.createDataReader(request.resultSet());
final DataReader dataReader = binder.createDataReader(false, request.resultSet());
while (dataReader.next()) {
consumer.accept(scalarType.read(dataReader));
}
@@ -34,7 +34,7 @@ public interface STreePropertyAssocMany extends STreePropertyAssoc {
BeanCollection<?> createReference(EntityBean localBean, boolean forceNewReference);
/**
* Populate the collection for read-only disabled lazy loading (aka Java Collections non mutable empty collection).
* Populate the collection for read-only disabled lazy loading (aka Java Collections non-mutable empty collection).
*/
void createEmptyReference(EntityBean localBean);
@@ -99,6 +99,11 @@ public interface STreeType {
*/
void postLoad(Object localBean);
/**
* Freeze the properties of the entity.
*/
void freeze(EntityBean entityBean);
/**
* Return the base table to use given the temporalMode.
*/
@@ -45,12 +45,12 @@ public final class SqlTreeBuilder {
*/
private final boolean rawNoId;
private final boolean disableLazyLoad;
private final boolean readOnly;
private final SpiQuery.TemporalMode temporalMode;
private final SqlTreeNode rootNode;
private boolean sqlDistinct;
private final boolean platformDistinctNoLobs;
private final SqlTreeCommon common;
private final boolean unmodifiable;
/**
* Construct for RawSql query.
@@ -60,7 +60,7 @@ public final class SqlTreeBuilder {
this.desc = request.descriptor();
this.rawNoId = rawNoId;
this.disableLazyLoad = request.query().isDisableLazyLoading();
this.readOnly = Boolean.TRUE.equals(request.query().isReadOnly());
this.unmodifiable = request.query().isUnmodifiable();
this.query = null;
this.subQuery = false;
this.distinctOnPlatform = false;
@@ -72,7 +72,7 @@ public final class SqlTreeBuilder {
this.manyWhereJoins = null;
this.alias = null;
this.ctx = null;
this.common = new SqlTreeCommon(temporalMode, disableLazyLoad, readOnly, null);
this.common = new SqlTreeCommon(temporalMode, disableLazyLoad, unmodifiable, null);
this.rootNode = buildRootNode(desc);
}
@@ -88,7 +88,7 @@ public final class SqlTreeBuilder {
this.query = request.query();
this.temporalMode = SpiQuery.TemporalMode.of(query);
this.disableLazyLoad = query.isDisableLazyLoading();
this.readOnly = Boolean.TRUE.equals(query.isReadOnly());
this.unmodifiable = query.isUnmodifiable();
this.subQuery = Type.SQ_EXISTS == query.type()
|| Type.SQ_EX == query.type()
|| Type.ID_LIST == query.type()
@@ -101,7 +101,7 @@ public final class SqlTreeBuilder {
this.alias = new SqlTreeAlias(request.baseTableAlias(), temporalMode);
this.distinctOnPlatform = builder.isPlatformDistinctOn();
this.platformDistinctNoLobs = builder.isPlatformDistinctNoLobs();
this.common = new SqlTreeCommon(temporalMode, disableLazyLoad, readOnly, includeJoin);
this.common = new SqlTreeCommon(temporalMode, disableLazyLoad, unmodifiable, includeJoin);
this.rootNode = buildRootNode(desc);
String fromForUpdate = builder.fromForUpdate(query);
CQueryHistorySupport historySupport = builder.historySupport(query);
@@ -7,13 +7,13 @@ final class SqlTreeCommon {
private final SpiQuery.TemporalMode temporalMode;
private final boolean disableLazyLoad;
private final boolean readOnly;
private final boolean unmodifiable;
private final TableJoin includeJoin;
SqlTreeCommon(SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad, boolean readOnly, TableJoin includeJoin) {
SqlTreeCommon(SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad, boolean unmodifiable, TableJoin includeJoin) {
this.temporalMode = temporalMode;
this.disableLazyLoad = disableLazyLoad;
this.readOnly = readOnly;
this.unmodifiable = unmodifiable;
this.includeJoin = includeJoin;
}
@@ -25,8 +25,8 @@ final class SqlTreeCommon {
return disableLazyLoad;
}
boolean readOnly() {
return readOnly;
boolean unmodifiable() {
return unmodifiable;
}
TableJoin includeJoin() {
@@ -31,7 +31,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
final boolean readId;
private final boolean readIdNormal;
private final boolean disableLazyLoad;
private final boolean readOnlyNoIntercept;
private final boolean unmodifiable;
private final InheritInfo inheritInfo;
final String prefix;
private final Map<String, String> pathMap;
@@ -54,7 +54,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
this.readId = node.readId;
this.readIdNormal = readId && !temporalVersions;
this.disableLazyLoad = node.disableLazyLoad;
this.readOnlyNoIntercept = disableLazyLoad && node.readOnly;
this.unmodifiable = node.unmodifiable;
this.partialObject = node.partialObject;
this.properties = node.properties;
this.pathMap = node.pathMap;
@@ -111,7 +111,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
localIdBinder = idBinder;
localDesc = desc;
} else {
localBean = localInfo.createEntityBean();
localBean = localInfo.createEntityBean(unmodifiable);
localType = localInfo.getType();
localIdBinder = localInfo.getIdBinder();
localDesc = localInfo.desc();
@@ -162,7 +162,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
void initBeanType() throws SQLException {
localDesc = desc;
localBean = desc.createEntityBean2(readOnlyNoIntercept);
localBean = desc.createEntityBean2(unmodifiable);
localIdBinder = idBinder;
}
@@ -224,7 +224,6 @@ class SqlTreeLoadBean implements SqlTreeLoad {
private void initSqlLoadBean() {
ctx.setCurrentPrefix(prefix, pathMap);
ctx.propagateState(localBean);
sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
}
@@ -302,9 +301,7 @@ class SqlTreeLoadBean implements SqlTreeLoad {
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
for (STreePropertyAssocMany many : localDesc.propsMany()) {
if (many != loadingChildProperty) {
if (readOnlyNoIntercept) {
many.createEmptyReference(localBean);
} else {
if (!unmodifiable || ctx.includeSecondary(many.asMany())) {
// create a proxy for the many (deferred fetching)
BeanCollection<?> ref = many.createReference(localBean, forceNewReference);
if (ref != null) {
@@ -38,7 +38,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
final boolean readId;
final boolean readIdNormal;
final boolean disableLazyLoad;
final boolean readOnly;
final boolean unmodifiable;
final InheritInfo inheritInfo;
final String prefix;
final Map<String, String> pathMap;
@@ -92,7 +92,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
this.readId = !aggregationRoot && withId && desc.hasId();
this.readIdNormal = readId && !temporalVersions;
this.disableLazyLoad = common.disableLazyLoad() || !readIdNormal || desc.isRawSqlBased();
this.readOnly = common.readOnly();
this.unmodifiable = common.unmodifiable();
this.partialObject = props.isPartialObject();
this.properties = props.props();
this.children = myChildren == null ? Collections.emptyList() : myChildren;
@@ -132,7 +132,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
private boolean usageProfiling = true;
private CacheMode useBeanCache = CacheMode.AUTO;
private CacheMode useQueryCache = CacheMode.OFF;
private Boolean readOnly;
private boolean unmodifiable;
private PersistenceContextScope persistenceContextScope;
/**
@@ -514,6 +514,10 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
if (!useDocStore) {
createExtraJoinsToSupportManyWhereClause();
}
if (unmodifiable) {
disableLazyLoading = true;
persistenceContextScope = PersistenceContextScope.QUERY;
}
return markQueryJoins();
}
@@ -763,7 +767,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
copy.nativeSql = nativeSql;
copy.useBeanCache = useBeanCache;
copy.useQueryCache = useQueryCache;
copy.readOnly = readOnly;
copy.unmodifiable = unmodifiable;
if (detail != null) {
copy.detail = detail.copy();
}
@@ -1113,7 +1117,9 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
if (allowLoadErrors) {
sb.append("/ae");
}
if (disableLazyLoading) {
if (unmodifiable) {
sb.append("/um");
} else if (disableLazyLoading) {
sb.append("/dl");
}
if (baseTable != null) {
@@ -1277,14 +1283,14 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
}
@Override
public final Boolean isReadOnly() {
return readOnly;
public Query<T> setUnmodifiable(boolean unmodifiable) {
this.unmodifiable = unmodifiable;
return this;
}
@Override
public final Query<T> setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
return this;
public boolean isUnmodifiable() {
return unmodifiable;
}
@Override
@@ -1328,6 +1334,9 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
@Override
public final Query<T> setUseQueryCache(CacheMode useQueryCache) {
this.useQueryCache = useQueryCache;
if (CacheMode.OFF != useQueryCache) {
unmodifiable = true;
}
return this;
}
@@ -1,5 +1,7 @@
package io.ebeaninternal.server.transaction;
import org.jspecify.annotations.Nullable;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@@ -25,6 +27,7 @@ public interface DataSourceSupplier {
* This can return null meaning that no read only DataSource (with autoCommit)
* is available for use so normal transactions with explicit commit should be used.
*/
@Nullable
DataSource readOnlyDataSource();
/**
@@ -10,6 +10,7 @@ import io.ebean.cache.ServerCacheNotification;
import io.ebean.cache.ServerCacheNotify;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.datasource.DataSourcePool;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeSet;
@@ -30,6 +31,8 @@ import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import io.ebeanservice.docstore.api.DocStoreUpdates;
import jakarta.persistence.PersistenceException;
import org.jspecify.annotations.Nullable;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@@ -257,6 +260,7 @@ public class TransactionManager implements SpiTransactionManager {
}
@Override
@Nullable
public final DataSource readOnlyDataSource() {
return dataSourceSupplier.readOnlyDataSource();
}
@@ -23,16 +23,23 @@ public class RsetDataReader implements DataReader {
static final int clobBufferSize = 512;
static final int stringInitialSize = 512;
private final boolean unmodifiable;
private final DataTimeZone dataTimeZone;
private final ResultSet rset;
protected int pos;
private String json;
public RsetDataReader(DataTimeZone dataTimeZone, ResultSet rset) {
public RsetDataReader(boolean unmodifiable, DataTimeZone dataTimeZone, ResultSet rset) {
this.unmodifiable = unmodifiable;
this.dataTimeZone = dataTimeZone;
this.rset = rset;
}
@Override
public boolean unmodifiable() {
return unmodifiable;
}
@Override
public final void pushJson(String json) {
this.json = json;
@@ -11,8 +11,8 @@ public final class RsetDataReaderIndexed extends RsetDataReader {
private final int[] rsetIndexPositions;
public RsetDataReaderIndexed(DataTimeZone dataTimeZone, ResultSet rset, int[] rsetIndexPositions) {
super(dataTimeZone, rset);
public RsetDataReaderIndexed(boolean unmodifiable, DataTimeZone dataTimeZone, ResultSet rset, int[] rsetIndexPositions) {
super(unmodifiable, dataTimeZone, rset);
this.rsetIndexPositions = rsetIndexPositions;
}
@@ -28,33 +28,33 @@ public class BeanDescriptorTest extends BaseTest {
@Test
public void createReference() {
Customer bean = customerDesc.createReference(null, false, 42, null);
Customer bean = customerDesc.createReference(false, false, 42, null);
assertThat(bean.getId()).isEqualTo(42);
Assertions.assertThat(server().beanState(bean).isReadOnly()).isFalse();
assertThat(server().beanState(bean).isUnmodifiable()).isFalse();
}
@Test
public void createReference_whenReadOnly() {
Customer bean = customerDesc.createReference(Boolean.TRUE, false, 42, null);
Assertions.assertThat(server().beanState(bean).isReadOnly()).isTrue();
Customer bean = customerDesc.createReference(true, false, 42, null);
assertThat(server().beanState(bean).isUnmodifiable()).isTrue();
}
@Test
public void createReference_whenNotReadOnly() {
Customer bean = customerDesc.createReference(Boolean.FALSE, false, 42, null);
Assertions.assertThat(server().beanState(bean).isReadOnly()).isFalse();
Customer bean = customerDesc.createReference(false, false, 42, null);
assertThat(server().beanState(bean).isUnmodifiable()).isFalse();
bean = customerDesc.createReference(42, null);
Assertions.assertThat(server().beanState(bean).isReadOnly()).isFalse();
assertThat(server().beanState(bean).isUnmodifiable()).isFalse();
}
@Test
public void createReference_when_disabledLazyLoad() {
Customer bean = customerDesc.createReference(Boolean.FALSE, true, 42, null);
Assertions.assertThat(server().beanState(bean).isDisableLazyLoad()).isTrue();
Customer bean = customerDesc.createReference(false, true, 42, null);
assertThat(server().beanState(bean).isDisableLazyLoad()).isTrue();
}
@Test
@@ -78,7 +78,7 @@ public class BeanDescriptorTest extends BaseTest {
BeanDescriptor<Animal> animalDesc = spiEbeanServer().descriptor(Animal.class);
Animal bean = animalDesc.createReference(Boolean.FALSE, false, dog.getId(), null);
Animal bean = animalDesc.createReference(false, false, dog.getId(), null);
assertThat(bean.getId()).isEqualTo(dog.getId());
}
@@ -8,6 +8,7 @@ import java.io.*;
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.*;
@@ -241,6 +242,36 @@ public class ModifyAwareListTest {
assertTrue(set.isMarkedDirty());
}
@Test
void freeze() {
ModifyAwareList<String> orig = createList();
List<String> frozen = orig.freeze();
assertThatThrownBy(() -> frozen.add("junk"))
.isInstanceOf(UnsupportedOperationException.class);
}
@Test
public void freezeAndSerialise() throws IOException, ClassNotFoundException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
List<String> orig = createList().freeze();
oos.writeObject(orig);
oos.flush();
oos.close();
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
@SuppressWarnings("unchecked")
List<String> read = (List<String>)ois.readObject();
assertThat(read).contains("A", "B", "C", "D", "E");
assertThatThrownBy(() -> read.add("junk"))
.isInstanceOf(UnsupportedOperationException.class);
}
@Test
public void serialise() throws IOException, ClassNotFoundException {
@@ -5,8 +5,11 @@ import org.junit.jupiter.api.Test;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class ModifyAwareMapTest {
@@ -25,6 +28,13 @@ public class ModifyAwareMapTest {
return new ModifyAwareMap<>(set);
}
@Test
void freeze() {
Map<String, Integer> frozen = createMap().freeze();
assertThatThrownBy(() -> frozen.put("junk", 1))
.isInstanceOf(UnsupportedOperationException.class);
}
@Test
public void serialise() throws IOException, ClassNotFoundException {
@@ -6,8 +6,10 @@ import org.junit.jupiter.api.Test;
import java.io.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class ModifyAwareSetTest {
@@ -22,6 +24,13 @@ public class ModifyAwareSetTest {
return new ModifyAwareSet<>(set);
}
@Test
void freeze() {
Set<String> frozen = createSet().freeze();
assertThatThrownBy(() -> frozen.add("junk"))
.isInstanceOf(UnsupportedOperationException.class);
}
@Test
public void serialise() throws IOException, ClassNotFoundException {
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</parent>
<name>ebean ddl generation</name>
@@ -28,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<scope>provided</scope>
</dependency>
@@ -65,7 +65,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<scope>test</scope>
</dependency>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,7 +14,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>14.11.0</version>
<version>16.0.0-RC2</version>
<scope>provided</scope>
</dependency>

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