mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70eb557929 | ||
|
|
fdb3a3759b | ||
|
|
7a5e8d0e17 | ||
|
|
a0748f8139 | ||
|
|
9f65673f54 | ||
|
|
8f6a3c59d1 | ||
|
|
05210ec411 | ||
|
|
faffda8318 | ||
|
|
af3901ce01 | ||
|
|
acd538207f | ||
|
|
84f0ef9fce | ||
|
|
7ab447b21c | ||
|
|
6f8805a6b3 | ||
|
|
0171c1afef | ||
|
|
31c106f604 | ||
|
|
20d2838be4 | ||
|
|
4238a951ca | ||
|
|
b5d4bed615 | ||
|
|
3ef715ef64 | ||
|
|
aea39528ab | ||
|
|
dd498409e0 | ||
|
|
bae33101df | ||
|
|
e1972ac544 | ||
|
|
c7241ba3dc | ||
|
|
73416ed65a | ||
|
|
cd70c178f1 | ||
|
|
aa298235f3 | ||
|
|
8733fed14b | ||
|
|
169d170247 | ||
|
|
d0ee42e0de |
@@ -34,6 +34,8 @@ jobs:
|
||||
path:
|
||||
~/.m2
|
||||
key: build-${{ env.cache-name }}
|
||||
- name: Maven version
|
||||
run: mvn --version
|
||||
- name: Build with Maven
|
||||
run: mvn package
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-clickhouse</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-db2</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-hana</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mariadb</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mysql</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-nuodb</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-oracle</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -47,19 +47,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlite</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -41,7 +41,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -60,13 +60,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-all</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>composites</artifactId>
|
||||
|
||||
+2
-9
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
@@ -39,7 +39,7 @@
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>avaje-config</artifactId>
|
||||
<version>3.8</version>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -107,13 +107,6 @@
|
||||
</dependency>
|
||||
<!-- JAKARTA-DEPENDENCY-END -->
|
||||
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -155,4 +155,10 @@ public abstract class BeanFinder<I,T> {
|
||||
return db().findNative(type, nativeSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query using the ORM query language.
|
||||
*/
|
||||
protected Query<T> query(String ormQuery) {
|
||||
return db().createQuery(type, ormQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,6 +790,21 @@ public final class DB {
|
||||
return getDefault().createUpdate(beanType, ormUpdate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a named query.
|
||||
* <p>
|
||||
* For RawSql the named query is expected to be in ebean.xml.
|
||||
*
|
||||
* @param beanType The type of entity bean
|
||||
* @param namedQuery The name of the query
|
||||
* @param <T> The type of entity bean
|
||||
* @return The query
|
||||
*/
|
||||
public static <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
|
||||
return getDefault().createNamedQuery(beanType, namedQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query for a type of entity bean.
|
||||
* <p>
|
||||
@@ -809,6 +824,43 @@ public final class DB {
|
||||
return getDefault().createQuery(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the Ebean query language statement returning the query which can then
|
||||
* be modified (add expressions, change order by clause, change maxRows, change
|
||||
* fetch and select paths etc).
|
||||
* <p>
|
||||
* <h3>Example</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Find order additionally fetching the customer, details and details.product name.
|
||||
*
|
||||
* String eql = "fetch customer fetch details fetch details.product (name) where id = :orderId ";
|
||||
*
|
||||
* Query<Order> query = DB.createQuery(Order.class, eql);
|
||||
* query.setParameter("orderId", 2);
|
||||
*
|
||||
* Order order = query.findOne();
|
||||
*
|
||||
* // This is the same as:
|
||||
*
|
||||
* Order order = DB.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .fetch("details")
|
||||
* .fetch("detail.product", "name")
|
||||
* .setId(2)
|
||||
* .findOne();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType The type of bean to fetch
|
||||
* @param eql The Ebean query
|
||||
* @param <T> The type of the entity bean
|
||||
* @return The query with expressions defined as per the parsed query statement
|
||||
*/
|
||||
public static <T> Query<T> createQuery(Class<T> beanType, String eql) {
|
||||
return getDefault().createQuery(beanType, eql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query for a type of entity bean.
|
||||
* <p>
|
||||
|
||||
@@ -225,6 +225,18 @@ public interface Database {
|
||||
*/
|
||||
<T> UpdateQuery<T> update(Class<T> beanType);
|
||||
|
||||
/**
|
||||
* Create a named query.
|
||||
* <p>
|
||||
* For RawSql the named query is expected to be in ebean.xml.
|
||||
*
|
||||
* @param beanType The type of entity bean
|
||||
* @param namedQuery The name of the query
|
||||
* @param <T> The type of entity bean
|
||||
* @return The query
|
||||
*/
|
||||
<T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery);
|
||||
|
||||
/**
|
||||
* Create a query for an entity bean and synonym for {@link #find(Class)}.
|
||||
*
|
||||
@@ -232,6 +244,41 @@ public interface Database {
|
||||
*/
|
||||
<T> Query<T> createQuery(Class<T> beanType);
|
||||
|
||||
/**
|
||||
* Parse the Ebean query language statement returning the query which can then
|
||||
* be modified (add expressions, change order by clause, change maxRows, change
|
||||
* fetch and select paths etc).
|
||||
* <p>
|
||||
* <h3>Example</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Find order additionally fetching the customer, details and details.product name.
|
||||
*
|
||||
* String ormQuery = "fetch customer fetch details fetch details.product (name) where id = :orderId ";
|
||||
*
|
||||
* Query<Order> query = DB.createQuery(Order.class, ormQuery);
|
||||
* query.setParameter("orderId", 2);
|
||||
*
|
||||
* Order order = query.findOne();
|
||||
*
|
||||
* // This is the same as:
|
||||
*
|
||||
* Order order = DB.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .fetch("details")
|
||||
* .fetch("detail.product", "name")
|
||||
* .setId(2)
|
||||
* .findOne();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType The type of bean to fetch
|
||||
* @param ormQuery The Ebean ORM query
|
||||
* @param <T> The type of the entity bean
|
||||
* @return The query with expressions defined as per the parsed query statement
|
||||
*/
|
||||
<T> Query<T> createQuery(Class<T> beanType, String ormQuery);
|
||||
|
||||
/**
|
||||
* Create a query for a type of entity bean.
|
||||
* <p>
|
||||
@@ -395,6 +442,18 @@ public interface Database {
|
||||
*/
|
||||
<T> DtoQuery<T> findDto(Class<T> dtoType, String sql);
|
||||
|
||||
/**
|
||||
* Create a named Query for DTO beans.
|
||||
* <p>
|
||||
* DTO beans are just normal bean like classes with public constructor(s) and setters.
|
||||
* They do not need to be registered with DB before use.
|
||||
*
|
||||
* @param dtoType The type of the DTO bean the rows will be mapped into.
|
||||
* @param namedQuery The name of the query
|
||||
* @param <T> The type of the DTO bean.
|
||||
*/
|
||||
<T> DtoQuery<T> createNamedDtoQuery(Class<T> dtoType, String namedQuery);
|
||||
|
||||
/**
|
||||
* Look to execute a native sql query that does not return beans but instead
|
||||
* returns SqlRow or direct access to ResultSet.
|
||||
@@ -1321,6 +1380,109 @@ public interface Database {
|
||||
*/
|
||||
ScriptRunner script();
|
||||
|
||||
/**
|
||||
* Return the Document store.
|
||||
*/
|
||||
DocumentStore docStore();
|
||||
|
||||
/**
|
||||
* Publish a single bean given its type and id returning the resulting live bean.
|
||||
* <p>
|
||||
* The values are published from the draft to the live bean.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean
|
||||
* @param transaction the transaction the publish process should use (can be null)
|
||||
*/
|
||||
@Nullable
|
||||
<T> T publish(Class<T> beanType, Object id, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Publish a single bean given its type and id returning the resulting live bean.
|
||||
* This will use the current transaction or create one if required.
|
||||
* <p>
|
||||
* The values are published from the draft to the live bean.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean
|
||||
*/
|
||||
@Nullable
|
||||
<T> T publish(Class<T> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Publish the beans that match the query returning the resulting published beans.
|
||||
* <p>
|
||||
* The values are published from the draft beans to the live beans.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to publish
|
||||
* @param transaction the transaction the publish process should use (can be null)
|
||||
*/
|
||||
<T> List<T> publish(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Publish the beans that match the query returning the resulting published beans.
|
||||
* This will use the current transaction or create one if required.
|
||||
* <p>
|
||||
* The values are published from the draft beans to the live beans.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to publish
|
||||
*/
|
||||
<T> List<T> publish(Query<T> query);
|
||||
|
||||
/**
|
||||
* Restore the draft bean back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean to restore
|
||||
* @param transaction the transaction the restore process should use (can be null)
|
||||
*/
|
||||
@Nullable
|
||||
<T> T draftRestore(Class<T> beanType, Object id, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Restore the draft bean back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean to restore
|
||||
*/
|
||||
@Nullable
|
||||
<T> T draftRestore(Class<T> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Restore the draft beans matching the query back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to restore
|
||||
* @param transaction the transaction the restore process should use (can be null)
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Restore the draft beans matching the query back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to restore
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query);
|
||||
|
||||
/**
|
||||
* Returns the set of properties/paths that are unknown (do not map to known properties or paths).
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package io.ebean;
|
||||
|
||||
/**
|
||||
* Bean holding the details to update the document store.
|
||||
*/
|
||||
public final class DocStoreQueueEntry {
|
||||
|
||||
/**
|
||||
* Action to either update or delete a document from the index.
|
||||
*/
|
||||
public enum Action {
|
||||
|
||||
/**
|
||||
* Action is to update a document in the doc store.
|
||||
*/
|
||||
INDEX(1),
|
||||
|
||||
/**
|
||||
* Action is to delete a document from the doc store..
|
||||
*/
|
||||
DELETE(2),
|
||||
|
||||
/**
|
||||
* An update is required based on a change to a nested/embedded object at a given path.
|
||||
*/
|
||||
NESTED(3);
|
||||
|
||||
int value;
|
||||
|
||||
Action(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value associated with this action type.
|
||||
*/
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private final Action type;
|
||||
|
||||
private final String queueId;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final Object beanId;
|
||||
|
||||
/**
|
||||
* Construct for an INDEX or DELETE action.
|
||||
*/
|
||||
public DocStoreQueueEntry(Action type, String queueId, Object beanId) {
|
||||
this(type, queueId, null, beanId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for an NESTED/embedded path invalidation action.
|
||||
*/
|
||||
public DocStoreQueueEntry(Action type, String queueId, String path, Object beanId) {
|
||||
this.type = type;
|
||||
this.queueId = queueId;
|
||||
this.path = path;
|
||||
this.beanId = beanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event type.
|
||||
*/
|
||||
public Action getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associate queueId.
|
||||
*/
|
||||
public String getQueueId() {
|
||||
return queueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path if this is a nested update.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean id (which matches the document id).
|
||||
*/
|
||||
public Object getBeanId() {
|
||||
return beanId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.avaje.lang.NonNullApi;
|
||||
import io.avaje.lang.Nullable;
|
||||
import io.ebean.docstore.DocQueryContext;
|
||||
import io.ebean.docstore.RawDoc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Document storage operations.
|
||||
*/
|
||||
@NonNullApi
|
||||
public interface DocumentStore {
|
||||
|
||||
/**
|
||||
* Update the associated document store using the result of the query.
|
||||
* <p>
|
||||
* This will execute the query against the database creating a document for each
|
||||
* bean graph and sending this to the document store.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the select and fetch paths of the query is set for you to match the
|
||||
* document structure needed based on <code>@DocStore</code> and <code>@DocStoreEmbedded</code>
|
||||
* so what this query requires is the predicates only.
|
||||
* </p>
|
||||
* <p>
|
||||
* This query will be executed using findEach so it is safe to use a query
|
||||
* that will fetch a lot of beans. The default bulkBatchSize is used.
|
||||
* </p>
|
||||
*
|
||||
* @param query The query that selects object to send to the document store.
|
||||
*/
|
||||
<T> void indexByQuery(Query<T> query);
|
||||
|
||||
/**
|
||||
* Update the associated document store index using the result of the query additionally specifying a
|
||||
* bulkBatchSize to use for sending the messages to ElasticSearch.
|
||||
*
|
||||
* @param query The query that selects object to send to the document store.
|
||||
* @param bulkBatchSize The batch size to use when bulk sending to the document store.
|
||||
*/
|
||||
<T> void indexByQuery(Query<T> query, int bulkBatchSize);
|
||||
|
||||
/**
|
||||
* Update the document store for all beans of this type.
|
||||
* <p>
|
||||
* This is the same as indexByQuery where the query has no predicates and so fetches all rows.
|
||||
* </p>
|
||||
*/
|
||||
void indexAll(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Return the bean by fetching it's content from the document store.
|
||||
* If the document is not found null is returned.
|
||||
* <p>
|
||||
* Typically this is called indirectly by findOne() on the query.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Customer customer =
|
||||
* database.find(Customer.class)
|
||||
* .setUseDocStore(true)
|
||||
* .setId(42)
|
||||
* .findOne();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Nullable
|
||||
<T> T find(DocQueryContext<T> request);
|
||||
|
||||
/**
|
||||
* Execute the find list query. This request is prepared to execute secondary queries.
|
||||
* <p>
|
||||
* Typically this is called indirectly by findList() on the query that has setUseDocStore(true).
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> newCustomers =
|
||||
* database.find(Customer.class)
|
||||
* .setUseDocStore(true)
|
||||
* .where().eq("status, Customer.Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
<T> List<T> findList(DocQueryContext<T> request);
|
||||
|
||||
/**
|
||||
* Execute the query against the document store returning the paged list.
|
||||
* <p>
|
||||
* The query should have <code>firstRow</code> or <code>maxRows</code> set prior to calling this method.
|
||||
* </p>
|
||||
* <p>
|
||||
* Typically this is called indirectly by findPagedList() on the query that has setUseDocStore(true).
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Customer> newCustomers =
|
||||
* database.find(Customer.class)
|
||||
* .setUseDocStore(true)
|
||||
* .where().eq("status, Customer.Status.NEW)
|
||||
* .setMaxRows(50)
|
||||
* .findPagedList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
<T> PagedList<T> findPagedList(DocQueryContext<T> request);
|
||||
|
||||
/**
|
||||
* Execute the query against the document store with the expectation of a large set of results
|
||||
* that are processed in a scrolling resultSet fashion.
|
||||
* <p>
|
||||
* For example, with the ElasticSearch doc store this uses SCROLL.
|
||||
* </p>
|
||||
* <p>
|
||||
* Typically this is called indirectly by findEach() on the query that has setUseDocStore(true).
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* database.find(Order.class)
|
||||
* .setUseDocStore(true)
|
||||
* .where()... // perhaps add predicates
|
||||
* .findEach((Order order) -> {
|
||||
* // process the bean ...
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
<T> void findEach(DocQueryContext<T> query, Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query against the document store with the expectation of a large set of results
|
||||
* that are processed in a scrolling resultSet fashion.
|
||||
* <p>
|
||||
* Unlike findEach() this provides the opportunity to stop iterating through the large query.
|
||||
* </p>
|
||||
* <p>
|
||||
* For example, with the ElasticSearch doc store this uses SCROLL.
|
||||
* </p>
|
||||
* <p>
|
||||
* Typically this is called indirectly by findEachWhile() on the query that has setUseDocStore(true).
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* database.find(Order.class)
|
||||
* .setUseDocStore(true)
|
||||
* .where()... // perhaps add predicates
|
||||
* .findEachWhile(new Predicate<Order>() {
|
||||
* @Override
|
||||
* public void accept(Order bean) {
|
||||
* // process the bean
|
||||
*
|
||||
* // return true to continue, false to stop
|
||||
* // boolean shouldContinue = ...
|
||||
* return shouldContinue;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
<T> void findEachWhile(DocQueryContext<T> query, Predicate<T> consumer);
|
||||
|
||||
/**
|
||||
* Find each processing raw documents.
|
||||
*
|
||||
* @param indexNameType The full index name and type
|
||||
* @param rawQuery The query to execute
|
||||
* @param consumer Consumer to process each document
|
||||
*/
|
||||
void findEach(String indexNameType, String rawQuery, Consumer<RawDoc> consumer);
|
||||
|
||||
/**
|
||||
* Find each processing raw documents stopping when the predicate returns false.
|
||||
*
|
||||
* @param indexNameType The full index name and type
|
||||
* @param rawQuery The query to execute
|
||||
* @param consumer Consumer to process each document until false is returned
|
||||
*/
|
||||
void findEachWhile(String indexNameType, String rawQuery, Predicate<RawDoc> consumer);
|
||||
|
||||
/**
|
||||
* Process the queue entries sending updates to the document store or queuing them for later processing.
|
||||
*/
|
||||
long process(List<DocStoreQueueEntry> queueEntries) throws IOException;
|
||||
|
||||
/**
|
||||
* Drop the index from the document store (similar to DDL drop table).
|
||||
* <pre>{@code
|
||||
*
|
||||
* DocumentStore documentStore = database.docStore();
|
||||
*
|
||||
* documentStore.dropIndex("product_copy");
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
void dropIndex(String indexName);
|
||||
|
||||
/**
|
||||
* Create an index given a mapping file as a resource in the classPath (similar to DDL create table).
|
||||
* <pre>{@code
|
||||
*
|
||||
* DocumentStore documentStore = database.docStore();
|
||||
*
|
||||
* // uses product_copy.mapping.json resource
|
||||
* // ... to define mappings for the index
|
||||
*
|
||||
* documentStore.createIndex("product_copy", null);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param indexName the name of the new index
|
||||
* @param alias the alias of the index
|
||||
*/
|
||||
void createIndex(String indexName, String alias);
|
||||
|
||||
/**
|
||||
* Modify the settings on an index.
|
||||
* <p>
|
||||
* For example, this can be used be used to set elasticSearch refresh_interval
|
||||
* on an index before a bulk update.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // refresh_interval -1 ... disable refresh while bulk loading
|
||||
*
|
||||
* Map<String,Object> settings = new LinkedHashMap<>();
|
||||
* settings.put("refresh_interval", "-1");
|
||||
*
|
||||
* documentStore.indexSettings("product", settings);
|
||||
*
|
||||
* }</pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // refresh_interval 1s ... restore after bulk loading
|
||||
*
|
||||
* Map<String,Object> settings = new LinkedHashMap<>();
|
||||
* settings.put("refresh_interval", "1s");
|
||||
*
|
||||
* documentStore.indexSettings("product", settings);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param indexName the name of the index to update settings on
|
||||
* @param settings the settings to set on the index
|
||||
*/
|
||||
void indexSettings(String indexName, Map<String, Object> settings);
|
||||
|
||||
/**
|
||||
* Copy the index to a new index.
|
||||
* <p>
|
||||
* This copy process does not use the database but instead will copy from the source index to a destination index.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* long copyCount = documentStore.copyIndex(Product.class, "product_copy");
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType The bean type of the source index
|
||||
* @param newIndex The name of the index to copy to
|
||||
* @return the number of documents copied to the new index
|
||||
*/
|
||||
long copyIndex(Class<?> beanType, String newIndex);
|
||||
|
||||
/**
|
||||
* Copy entries from an index to a new index but limiting to documents that have been
|
||||
* modified since the sinceEpochMillis time.
|
||||
* <p>
|
||||
* To support this the document needs to have a <code>@WhenModified</code> property.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* long copyCount = documentStore.copyIndex(Product.class, "product_copy", sinceMillis);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType The bean type of the source index
|
||||
* @param newIndex The name of the index to copy to
|
||||
* @return the number of documents copied to the new index
|
||||
*/
|
||||
long copyIndex(Class<?> beanType, String newIndex, long sinceEpochMillis);
|
||||
|
||||
/**
|
||||
* Copy from a source index to a new index taking only the documents
|
||||
* matching the given query.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // predicates to select the source documents to copy
|
||||
* Query<Product> query = database.find(Product.class)
|
||||
* .where()
|
||||
* .ge("whenModified", new Timestamp(since))
|
||||
* .ge("name", "A")
|
||||
* .lt("name", "D")
|
||||
* .query();
|
||||
*
|
||||
* // copy from the source index to "product_copy" index
|
||||
* long copyCount = documentStore.copyIndex(query, "product_copy", 1000);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param query The query to select the source documents to copy
|
||||
* @param newIndex The target index to copy the documents to
|
||||
* @param bulkBatchSize The ElasticSearch bulk batch size, if 0 uses the default.
|
||||
* @return The number of documents copied to the new index.
|
||||
*/
|
||||
long copyIndex(Query<?> query, String newIndex, int bulkBatchSize);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.search.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -623,6 +625,31 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression raw(String raw);
|
||||
|
||||
/**
|
||||
* Create a Text Match expression (currently doc store/Elastic only).
|
||||
*/
|
||||
Expression textMatch(String propertyName, String search, Match options);
|
||||
|
||||
/**
|
||||
* Create a Text Multi match expression (currently doc store/Elastic only).
|
||||
*/
|
||||
Expression textMultiMatch(String query, MultiMatch options);
|
||||
|
||||
/**
|
||||
* Create a text simple query expression (currently doc store/Elastic only).
|
||||
*/
|
||||
Expression textSimple(String search, TextSimple options);
|
||||
|
||||
/**
|
||||
* Create a text query string expression (currently doc store/Elastic only).
|
||||
*/
|
||||
Expression textQueryString(String search, TextQueryString options);
|
||||
|
||||
/**
|
||||
* Create a text common terms expression (currently doc store/Elastic only).
|
||||
*/
|
||||
Expression textCommonTerms(String search, TextCommonTerms options);
|
||||
|
||||
/**
|
||||
* And - join two expressions with a logical and.
|
||||
*/
|
||||
@@ -666,4 +693,12 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
<T> Junction<T> junction(Junction.Type type, Query<T> query, ExpressionList<T> parent);
|
||||
|
||||
/**
|
||||
* Add the expressions to the given expression list.
|
||||
*
|
||||
* @param where The expression list to add the expressions to
|
||||
* @param expressions The expressions that are parsed
|
||||
* @param params Bind parameters to match ? or ?1 bind positions.
|
||||
*/
|
||||
<T> void where(ExpressionList<T> where, String expressions, Object[] params);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebean;
|
||||
|
||||
import io.avaje.lang.NonNullApi;
|
||||
import io.avaje.lang.Nullable;
|
||||
import io.ebean.search.*;
|
||||
|
||||
import jakarta.persistence.NonUniqueResultException;
|
||||
import java.sql.Connection;
|
||||
@@ -105,6 +106,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> asOf(Timestamp asOf);
|
||||
|
||||
/**
|
||||
* Execute the query against the draft set of tables.
|
||||
*/
|
||||
Query<T> asDraft();
|
||||
|
||||
/**
|
||||
* Convert the query to a DTO bean query.
|
||||
* <p>
|
||||
@@ -412,6 +418,42 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Optional<T> findOneOrEmpty();
|
||||
|
||||
/**
|
||||
* Execute find row count query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the row count query
|
||||
*/
|
||||
FutureRowCount<T> findFutureCount();
|
||||
|
||||
/**
|
||||
* Execute find Id's query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the list of Id's
|
||||
*/
|
||||
FutureIds<T> findFutureIds();
|
||||
|
||||
/**
|
||||
* Execute find list query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using firstRow and maxRows.
|
||||
* <p>
|
||||
@@ -465,6 +507,28 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> filterMany(String manyProperty);
|
||||
|
||||
/**
|
||||
* Deprecated for removal - migrate to filterManyRaw()
|
||||
* <p>
|
||||
* Add filter expressions to the many property.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* DB.find(Customer.class)
|
||||
* .where()
|
||||
* .eq("name", "Rob")
|
||||
* .filterMany("orders", "status = ?", Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param manyProperty The many property
|
||||
* @param expressions Filter expressions with and, or and ? or ?1 type bind parameters
|
||||
* @param params Bind parameters used in the expressions
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
ExpressionList<T> filterMany(String manyProperty, String expressions, Object... params);
|
||||
|
||||
/**
|
||||
* Add filter expressions for the many path. The expressions can include SQL functions if
|
||||
* desired and the property names are translated to column names.
|
||||
@@ -516,6 +580,19 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> setDistinct(boolean distinct);
|
||||
|
||||
/**
|
||||
* Set the index(es) to search for a document store which uses partitions.
|
||||
* <p>
|
||||
* For example, when executing a query against ElasticSearch with daily indexes we can
|
||||
* explicitly specify the indexes to search against.
|
||||
* </p>
|
||||
*
|
||||
* @param indexName The index or indexes to search against
|
||||
* @return This query
|
||||
* @see Query#setDocIndexName(String)
|
||||
*/
|
||||
Query<T> setDocIndexName(String indexName);
|
||||
|
||||
/**
|
||||
* Set the first row to fetch.
|
||||
*
|
||||
@@ -599,6 +676,14 @@ public interface ExpressionList<T> {
|
||||
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if this query should execute against the doc store.
|
||||
* <p>
|
||||
* When setting this you may also consider disabling lazy loading.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setUseDocStore(boolean useDocsStore);
|
||||
|
||||
/**
|
||||
* Set true if you want to disable lazy loading.
|
||||
* <p>
|
||||
@@ -607,6 +692,16 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> setDisableLazyLoading(boolean disableLazyLoading);
|
||||
|
||||
/**
|
||||
* Disable read auditing for this query.
|
||||
* <p>
|
||||
* This is intended to be used when the query is not a user initiated query and instead
|
||||
* part of the internal processing in an application to load a cache or document store etc.
|
||||
* In these cases we don't want the query to be part of read auditing.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setDisableReadAuditing();
|
||||
|
||||
/**
|
||||
* Set a label on the query (to help identify query execution statistics).
|
||||
*/
|
||||
@@ -626,6 +721,14 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> where();
|
||||
|
||||
/**
|
||||
* Add the expressions to this expression list.
|
||||
*
|
||||
* @param expressions The expressions that are parsed and added to this expression list
|
||||
* @param params Bind parameters to match ? or ?1 bind positions.
|
||||
*/
|
||||
ExpressionList<T> where(String expressions, Object... params);
|
||||
|
||||
/**
|
||||
* Path exists - for the given path in a JSON document.
|
||||
* <pre>{@code
|
||||
@@ -1520,6 +1623,47 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> rawOrEmpty(String raw, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Add a match expression.
|
||||
*
|
||||
* @param propertyName The property name for the match
|
||||
* @param search The search value
|
||||
*/
|
||||
ExpressionList<T> match(String propertyName, String search);
|
||||
|
||||
/**
|
||||
* Add a match expression with options.
|
||||
*
|
||||
* @param propertyName The property name for the match
|
||||
* @param search The search value
|
||||
*/
|
||||
ExpressionList<T> match(String propertyName, String search, Match options);
|
||||
|
||||
/**
|
||||
* Add a multi-match expression.
|
||||
*/
|
||||
ExpressionList<T> multiMatch(String search, String... properties);
|
||||
|
||||
/**
|
||||
* Add a multi-match expression using options.
|
||||
*/
|
||||
ExpressionList<T> multiMatch(String search, MultiMatch options);
|
||||
|
||||
/**
|
||||
* Add a simple query string expression.
|
||||
*/
|
||||
ExpressionList<T> textSimple(String search, TextSimple options);
|
||||
|
||||
/**
|
||||
* Add a query string expression.
|
||||
*/
|
||||
ExpressionList<T> textQueryString(String search, TextQueryString options);
|
||||
|
||||
/**
|
||||
* Add common terms expression.
|
||||
*/
|
||||
ExpressionList<T> textCommonTerms(String search, TextCommonTerms options);
|
||||
|
||||
/**
|
||||
* And - join two expressions with a logical and.
|
||||
*/
|
||||
@@ -1662,6 +1806,42 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Junction<T> disjunction();
|
||||
|
||||
/**
|
||||
* Start a list of expressions that will be joined by MUST.
|
||||
* <p>
|
||||
* This automatically makes the query a useDocStore(true) query that
|
||||
* will execute against the document store (ElasticSearch etc).
|
||||
* </p>
|
||||
* <p>
|
||||
* This is logically similar to and().
|
||||
* </p>
|
||||
*/
|
||||
Junction<T> must();
|
||||
|
||||
/**
|
||||
* Start a list of expressions that will be joined by SHOULD.
|
||||
* <p>
|
||||
* This automatically makes the query a useDocStore(true) query that
|
||||
* will execute against the document store (ElasticSearch etc).
|
||||
* </p>
|
||||
* <p>
|
||||
* This is logically similar to or().
|
||||
* </p>
|
||||
*/
|
||||
Junction<T> should();
|
||||
|
||||
/**
|
||||
* Start a list of expressions that will be joined by MUST NOT.
|
||||
* <p>
|
||||
* This automatically makes the query a useDocStore(true) query that
|
||||
* will execute against the document store (ElasticSearch etc).
|
||||
* </p>
|
||||
* <p>
|
||||
* This is logically similar to not().
|
||||
* </p>
|
||||
*/
|
||||
Junction<T> mustNot();
|
||||
|
||||
/**
|
||||
* End a junction returning the parent expression list.
|
||||
* <p>
|
||||
|
||||
@@ -213,4 +213,11 @@ public class Finder<I, T> {
|
||||
return db().findNative(type, nativeSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query using the ORM query language.
|
||||
*/
|
||||
public Query<T> query(String ormQuery) {
|
||||
return db().createQuery(type, ormQuery);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* FutureIds represents the result of a background query execution for the Id's.
|
||||
* <p>
|
||||
* It extends the java.util.concurrent.Future with the ability to get the Id's
|
||||
* while the query is still executing in the background.
|
||||
* </p>
|
||||
*/
|
||||
public interface FutureIds<T> extends Future<List<Object>> {
|
||||
|
||||
/**
|
||||
* Returns the original query used to fetch the Id's.
|
||||
*/
|
||||
Query<T> getQuery();
|
||||
|
||||
}
|
||||
@@ -250,6 +250,11 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
Query<T> asOf(Timestamp asOf);
|
||||
|
||||
/**
|
||||
* Execute the query against the draft set of tables.
|
||||
*/
|
||||
Query<T> asDraft();
|
||||
|
||||
/**
|
||||
* Convert the query to a DTO bean query.
|
||||
* <p>
|
||||
@@ -316,6 +321,43 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
Query<T> setPersistenceContextScope(PersistenceContextScope scope);
|
||||
|
||||
/**
|
||||
* Set the index(es) to search for a document store which uses partitions.
|
||||
* <p>
|
||||
* For example, when executing a query against ElasticSearch with daily indexes we can
|
||||
* explicitly specify the indexes to search against.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // explicitly specify the indexes to search
|
||||
* query.setDocIndexName("logstash-2016.11.5,logstash-2016.11.6")
|
||||
*
|
||||
* // search today's index
|
||||
* query.setDocIndexName("$today")
|
||||
*
|
||||
* // search the last 3 days
|
||||
* query.setDocIndexName("$last-3")
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* If the indexName is specified with ${daily} e.g. "logstash-${daily}" ... then we can use
|
||||
* $today and $last-x as the search docIndexName like the examples below.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // search today's index
|
||||
* query.setDocIndexName("$today")
|
||||
*
|
||||
* // search the last 3 days
|
||||
* query.setDocIndexName("$last-3")
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param indexName The index or indexes to search against
|
||||
* @return This query
|
||||
*/
|
||||
Query<T> setDocIndexName(String indexName);
|
||||
|
||||
/**
|
||||
* Return the ExpressionFactory used by this query.
|
||||
*/
|
||||
@@ -382,6 +424,16 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
Query<T> setIncludeSoftDeletes();
|
||||
|
||||
/**
|
||||
* Disable read auditing for this query.
|
||||
* <p>
|
||||
* This is intended to be used when the query is not a user initiated query and instead
|
||||
* part of the internal processing in an application to load a cache or document store etc.
|
||||
* In these cases we don't want the query to be part of read auditing.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setDisableReadAuditing();
|
||||
|
||||
/**
|
||||
* Specify the properties to fetch on the root level entity bean in comma delimited format.
|
||||
* <p>
|
||||
@@ -1068,6 +1120,41 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
int findCount();
|
||||
|
||||
/**
|
||||
* Execute find row count query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the row count query
|
||||
*/
|
||||
FutureRowCount<T> findFutureCount();
|
||||
|
||||
/**
|
||||
* Execute find Id's query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the list of Id's
|
||||
*/
|
||||
FutureIds<T> findFutureIds();
|
||||
|
||||
/**
|
||||
* Execute find list query in a background thread.
|
||||
* <p>
|
||||
* This query will execute in it's own PersistenceContext and using its own transaction.
|
||||
* What that means is that it will not share any bean instances with other queries.
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using firstRow and maxRows.
|
||||
* <p>
|
||||
@@ -1216,6 +1303,27 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
ExpressionList<T> where();
|
||||
|
||||
/**
|
||||
* Add Full text search expressions for Document store queries.
|
||||
* <p>
|
||||
* This is currently ElasticSearch only and provides the full text
|
||||
* expressions such as Match and Multi-Match.
|
||||
* </p>
|
||||
* <p>
|
||||
* This automatically makes this query a "Doc Store" query and will execute
|
||||
* against the document store (ElasticSearch).
|
||||
* </p>
|
||||
* <p>
|
||||
* Expressions added here are added to the "query" section of an ElasticSearch
|
||||
* query rather than the "filter" section.
|
||||
* </p>
|
||||
* <p>
|
||||
* Expressions added to the where() are added to the "filter" section of an
|
||||
* ElasticSearch query.
|
||||
* </p>
|
||||
*/
|
||||
ExpressionList<T> text();
|
||||
|
||||
/**
|
||||
* This applies a filter on the 'many' property list rather than the root
|
||||
* level objects.
|
||||
@@ -1474,6 +1582,14 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
Query<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Set to true if this query should execute against the doc store.
|
||||
* <p>
|
||||
* When setting this you may also consider disabling lazy loading.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setUseDocStore(boolean useDocStore);
|
||||
|
||||
/**
|
||||
* When set to true when you want the returned beans to be read only.
|
||||
*/
|
||||
@@ -1608,6 +1724,28 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
* Restrict the query to only return subtypes of the given inherit type.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Animal> animals =
|
||||
* new QAnimal()
|
||||
* .name.startsWith("Fluffy")
|
||||
* .setInheritType(Cat.class)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param type An inheritance subtype of the
|
||||
*/
|
||||
Query<T> setInheritType(Class<? extends T> type);
|
||||
|
||||
/**
|
||||
* Returns the inherit type. This is normally the same as getBeanType() returns as long as no other type is set.
|
||||
*/
|
||||
Class<? extends T> getInheritType();
|
||||
|
||||
/**
|
||||
* Return the type of query being executed.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.DocStoreConfig;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
@@ -209,6 +211,29 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
boolean isActive();
|
||||
|
||||
/**
|
||||
* Set the behavior for document store updates on this transaction.
|
||||
* <p>
|
||||
* For example, set the mode to DocStoreEvent.IGNORE for this transaction and
|
||||
* then any changes via this transaction are not sent to the doc store. This
|
||||
* would be used when doing large bulk inserts into the database and we want
|
||||
* to control how that is sent to the document store.
|
||||
* </p>
|
||||
*/
|
||||
void setDocStoreMode(DocStoreMode mode);
|
||||
|
||||
/**
|
||||
* Set the batch size to use for sending messages to the document store.
|
||||
* <p>
|
||||
* You might set this if you know the changes in this transaction result in especially large or
|
||||
* especially small payloads and want to adjust the batch size to match.
|
||||
* </p>
|
||||
* <p>
|
||||
* Setting this overrides the default of {@link DocStoreConfig#getBulkBatchSize()}
|
||||
* </p>
|
||||
*/
|
||||
void setDocStoreBatchSize(int batchSize);
|
||||
|
||||
/**
|
||||
* Explicitly turn off or on the cascading nature of save() and delete(). This
|
||||
* gives the developer exact control over what beans are saved and deleted
|
||||
|
||||
@@ -14,6 +14,8 @@ import io.ebean.event.*;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetricNamingMatch;
|
||||
import io.ebean.util.StringHelper;
|
||||
|
||||
@@ -132,6 +134,16 @@ public class DatabaseConfig {
|
||||
*/
|
||||
private List<String> packages = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Configuration for the ElasticSearch integration.
|
||||
*/
|
||||
private DocStoreConfig docStoreConfig = new DocStoreConfig();
|
||||
|
||||
/**
|
||||
* Set to true when the Database only uses Document store.
|
||||
*/
|
||||
private boolean docStoreOnly;
|
||||
|
||||
/**
|
||||
* This is used to populate @WhoCreated, @WhoModified and
|
||||
* support other audit features (who executed a query etc).
|
||||
@@ -404,6 +416,8 @@ public class DatabaseConfig {
|
||||
private ChangeLogListener changeLogListener;
|
||||
private ChangeLogRegister changeLogRegister;
|
||||
private boolean changeLogAsync = true;
|
||||
private ReadAuditLogger readAuditLogger;
|
||||
private ReadAuditPrepare readAuditPrepare;
|
||||
private EncryptKeyManager encryptKeyManager;
|
||||
private EncryptDeployManager encryptDeployManager;
|
||||
private Encryptor encryptor;
|
||||
@@ -1183,6 +1197,40 @@ public class DatabaseConfig {
|
||||
this.changeLogAsync = changeLogAsync;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger to use.
|
||||
*/
|
||||
public ReadAuditLogger getReadAuditLogger() {
|
||||
return readAuditLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ReadAuditLogger to use. If not set the default implementation is used
|
||||
* which logs the read events in JSON format to a standard named SLF4J logger
|
||||
* (which can be configured in say logback to log to a separate log file).
|
||||
*/
|
||||
public void setReadAuditLogger(ReadAuditLogger readAuditLogger) {
|
||||
this.readAuditLogger = readAuditLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditPrepare to use.
|
||||
*/
|
||||
public ReadAuditPrepare getReadAuditPrepare() {
|
||||
return readAuditPrepare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ReadAuditPrepare to use.
|
||||
* <p>
|
||||
* It is expected that an implementation is used that read user context information
|
||||
* (user id, user ip address etc) and sets it on the ReadEvent bean before it is sent
|
||||
* to the ReadAuditLogger.
|
||||
*/
|
||||
public void setReadAuditPrepare(ReadAuditPrepare readAuditPrepare) {
|
||||
this.readAuditPrepare = readAuditPrepare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for profiling.
|
||||
*/
|
||||
@@ -1551,6 +1599,34 @@ public class DatabaseConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this Database is a Document store only instance (has no JDBC DB).
|
||||
*/
|
||||
public boolean isDocStoreOnly() {
|
||||
return docStoreOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if this Database is Document store only instance (has no JDBC DB).
|
||||
*/
|
||||
public void setDocStoreOnly(boolean docStoreOnly) {
|
||||
this.docStoreOnly = docStoreOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the ElasticSearch integration.
|
||||
*/
|
||||
public DocStoreConfig getDocStoreConfig() {
|
||||
return docStoreConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration for the ElasticSearch integration.
|
||||
*/
|
||||
public void setDocStoreConfig(DocStoreConfig docStoreConfig) {
|
||||
this.docStoreConfig = docStoreConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the constraint naming convention used in DDL generation.
|
||||
*/
|
||||
@@ -2733,6 +2809,13 @@ public class DatabaseConfig {
|
||||
readOnlyDataSourceConfig.loadSettings(p.properties, name + "-ro");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is broken out to allow overridden behaviour.
|
||||
*/
|
||||
protected void loadDocStoreSettings(PropertiesWrapper p) {
|
||||
docStoreConfig.loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is broken out to allow overridden behaviour.
|
||||
*/
|
||||
@@ -2764,6 +2847,11 @@ public class DatabaseConfig {
|
||||
}
|
||||
loadDataSourceSettings(p);
|
||||
|
||||
if (docStoreConfig == null) {
|
||||
docStoreConfig = new DocStoreConfig();
|
||||
}
|
||||
loadDocStoreSettings(p);
|
||||
|
||||
defaultServer = p.getBoolean("defaultServer", defaultServer);
|
||||
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
|
||||
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
|
||||
@@ -2778,6 +2866,7 @@ public class DatabaseConfig {
|
||||
queryPlanCapturePeriodSecs = p.getLong("queryPlan.capturePeriodSecs", queryPlanCapturePeriodSecs);
|
||||
queryPlanCaptureMaxTimeMillis = p.getLong("queryPlan.captureMaxTimeMillis", queryPlanCaptureMaxTimeMillis);
|
||||
queryPlanCaptureMaxCount = p.getInt("queryPlan.captureMaxCount", queryPlanCaptureMaxCount);
|
||||
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
|
||||
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
|
||||
localOnlyL2Cache = p.getBoolean("localOnlyL2Cache", localOnlyL2Cache);
|
||||
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
|
||||
/**
|
||||
* Configuration for the Document store integration (e.g. ElasticSearch).
|
||||
*/
|
||||
public class DocStoreConfig {
|
||||
|
||||
/**
|
||||
* True when the Document store integration is active/on.
|
||||
*/
|
||||
protected boolean active;
|
||||
|
||||
/**
|
||||
* Set to true means Ebean will generate mapping files on startup.
|
||||
*/
|
||||
protected boolean generateMapping;
|
||||
|
||||
/**
|
||||
* When true the Document store should drop and re-create document indexes.
|
||||
*/
|
||||
protected boolean dropCreate;
|
||||
|
||||
/**
|
||||
* When true the Document store should create any document indexes that don't already exist.
|
||||
*/
|
||||
protected boolean create;
|
||||
|
||||
/**
|
||||
* The URL of the Document store. For example: http://localhost:9200.
|
||||
*/
|
||||
protected String url;
|
||||
|
||||
/**
|
||||
* Credential that be used for authentication to document store.
|
||||
*/
|
||||
protected String username;
|
||||
|
||||
/**
|
||||
* Password credential that be used for authentication to document store.
|
||||
*/
|
||||
protected String password;
|
||||
|
||||
/**
|
||||
* Set to true such that the client allows connections to invalid/self signed SSL certificates.
|
||||
*/
|
||||
protected boolean allowAllCertificates;
|
||||
|
||||
/**
|
||||
* The default mode used by indexes.
|
||||
*/
|
||||
protected DocStoreMode persist = DocStoreMode.UPDATE;
|
||||
|
||||
/**
|
||||
* The default batch size to use for the Bulk API calls.
|
||||
*/
|
||||
protected int bulkBatchSize = 1000;
|
||||
|
||||
/**
|
||||
* Resource path for the Document store mapping files.
|
||||
*/
|
||||
protected String mappingPath;
|
||||
|
||||
/**
|
||||
* Suffix used for mapping files.
|
||||
*/
|
||||
protected String mappingSuffix;
|
||||
|
||||
/**
|
||||
* Location of resources that mapping files are generated into.
|
||||
*/
|
||||
protected String pathToResources = "src/main/resources";
|
||||
|
||||
/**
|
||||
* Return true if the Document store (ElasticSearch) integration is active.
|
||||
*/
|
||||
public boolean isActive() {
|
||||
String systemValue = System.getProperty("ebean.docstore.active");
|
||||
if (systemValue != null) {
|
||||
return Boolean.parseBoolean(systemValue);
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to make the Document store (ElasticSearch) integration active.
|
||||
*/
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL to the Document store.
|
||||
*/
|
||||
public String getUrl() {
|
||||
String systemValue = System.getProperty("ebean.docstore.url");
|
||||
if (systemValue != null) {
|
||||
return systemValue;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user credential for connecting to the document store.
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user credential for connecting to the document store.
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the password credential for connecting to the document store.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password credential for connecting to the document store.
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to the Document store.
|
||||
* <p>
|
||||
* For a local ElasticSearch server this would be: http://localhost:9200
|
||||
*/
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Ebean should generate mapping files on server startup.
|
||||
*/
|
||||
public boolean isGenerateMapping() {
|
||||
String systemValue = System.getProperty("ebean.docstore.generateMapping");
|
||||
if (systemValue != null) {
|
||||
return Boolean.parseBoolean(systemValue);
|
||||
}
|
||||
return generateMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if Ebean should generate mapping files on server startup.
|
||||
*/
|
||||
public void setGenerateMapping(boolean generateMapping) {
|
||||
this.generateMapping = generateMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the document store should recreate mapped indexes.
|
||||
*/
|
||||
public boolean isDropCreate() {
|
||||
String systemValue = System.getProperty("ebean.docstore.dropCreate");
|
||||
if (systemValue != null) {
|
||||
return Boolean.parseBoolean(systemValue);
|
||||
}
|
||||
return dropCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the document store should recreate mapped indexes.
|
||||
*/
|
||||
public void setDropCreate(boolean dropCreate) {
|
||||
this.dropCreate = dropCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create true if the document store should create mapped indexes that don't yet exist.
|
||||
* This is only used if dropCreate is false.
|
||||
*/
|
||||
public boolean isCreate() {
|
||||
String systemValue = System.getProperty("ebean.docstore.create");
|
||||
if (systemValue != null) {
|
||||
return Boolean.parseBoolean(systemValue);
|
||||
}
|
||||
return create;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the document store should create mapped indexes that don't yet exist.
|
||||
* This is only used if dropCreate is false.
|
||||
*/
|
||||
public void setCreate(boolean create) {
|
||||
this.create = create;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the client allows connections to invalid/self signed SSL certificates.
|
||||
*/
|
||||
public boolean isAllowAllCertificates() {
|
||||
return allowAllCertificates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true such that the client allows connections to invalid/self signed SSL certificates.
|
||||
*/
|
||||
public void setAllowAllCertificates(boolean allowAllCertificates) {
|
||||
this.allowAllCertificates = allowAllCertificates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default batch size to use for calls to the Bulk API.
|
||||
*/
|
||||
public int getBulkBatchSize() {
|
||||
return bulkBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default batch size to use for calls to the Bulk API.
|
||||
* <p>
|
||||
* The batch size can be set on a transaction via {@link Transaction#setDocStoreBatchSize(int)}.
|
||||
* </p>
|
||||
*/
|
||||
public void setBulkBatchSize(int bulkBatchSize) {
|
||||
this.bulkBatchSize = bulkBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapping path.
|
||||
*/
|
||||
public String getMappingPath() {
|
||||
return mappingPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mapping path.
|
||||
*/
|
||||
public void setMappingPath(String mappingPath) {
|
||||
this.mappingPath = mappingPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapping suffix.
|
||||
*/
|
||||
public String getMappingSuffix() {
|
||||
return mappingSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mapping suffix.
|
||||
*/
|
||||
public void setMappingSuffix(String mappingSuffix) {
|
||||
this.mappingSuffix = mappingSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative file system path to resources when generating mapping files.
|
||||
*/
|
||||
public String getPathToResources() {
|
||||
return pathToResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the relative file system path to resources when generating mapping files.
|
||||
*/
|
||||
public void setPathToResources(String pathToResources) {
|
||||
this.pathToResources = pathToResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default behavior for when Insert, Update and Delete events occur on beans that have an associated
|
||||
* Document store.
|
||||
*/
|
||||
public DocStoreMode getPersist() {
|
||||
return persist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default behavior for when Insert, Update and Delete events occur on beans that have an associated
|
||||
* Document store.
|
||||
* <ul>
|
||||
* <li>DocStoreEvent.UPDATE - build and send message to Bulk API</li>
|
||||
* <li>DocStoreEvent.QUEUE - add an entry with the index type and id only into a queue for later processing</li>
|
||||
* <li>DocStoreEvent.IGNORE - ignore. Most likely used when some scheduled batch job handles updating the index</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* You might choose to use QUEUE if that particular index data is updating very frequently or the cost of indexing
|
||||
* is expensive. Setting it to QUEUE can mean many changes can be batched together potentially coalescing multiple
|
||||
* updates for an index entry into a single update.
|
||||
* </p>
|
||||
* <p>
|
||||
* You might choose to use IGNORE when you have your own external process for updating the indexes. In this case
|
||||
* you don't want Ebean to do anything when the data changes.
|
||||
* </p>
|
||||
*/
|
||||
public void setPersist(DocStoreMode persist) {
|
||||
this.persist = persist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings specified in properties files.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper properties) {
|
||||
|
||||
active = properties.getBoolean("docstore.active", active);
|
||||
url = properties.get("docstore.url", url);
|
||||
username = properties.get("docstore.username", url);
|
||||
password = properties.get("docstore.password", url);
|
||||
persist = properties.getEnum(DocStoreMode.class, "docstore.persist", persist);
|
||||
bulkBatchSize = properties.getInt("docstore.bulkBatchSize", bulkBatchSize);
|
||||
generateMapping = properties.getBoolean("docstore.generateMapping", generateMapping);
|
||||
dropCreate = properties.getBoolean("docstore.dropCreate", dropCreate);
|
||||
create = properties.getBoolean("docstore.create", create);
|
||||
allowAllCertificates = properties.getBoolean("docstore.allowAllCertificates", allowAllCertificates);
|
||||
mappingPath = properties.get("docstore.mappingPath", mappingPath);
|
||||
mappingSuffix = properties.get("docstore.mappingSuffix", mappingSuffix);
|
||||
pathToResources = properties.get("docstore.pathToResources", pathToResources);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.ebean.docstore;
|
||||
|
||||
/**
|
||||
* Document Mapping for a bean marker interface.
|
||||
*/
|
||||
public interface DocMapping {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.ebean.docstore;
|
||||
|
||||
/**
|
||||
* Document query request context marker interface.
|
||||
*/
|
||||
public interface DocQueryContext<T> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.ebean.docstore;
|
||||
|
||||
/**
|
||||
* Document update context marker interface.
|
||||
*/
|
||||
public interface DocUpdateContext {
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package io.ebean.docstore;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Raw document.
|
||||
*/
|
||||
public class RawDoc {
|
||||
|
||||
private Map<String, Object> source;
|
||||
private String id;
|
||||
private double score;
|
||||
private String index;
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* Construct the document with all the meta data.
|
||||
*/
|
||||
public RawDoc(Map<String, Object> source, String id, double score, String index, String type) {
|
||||
this.source = source;
|
||||
this.id = id;
|
||||
this.score = score;
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct empty (typically for JSON marshalling).
|
||||
*/
|
||||
public RawDoc() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the source document as a Map.
|
||||
*/
|
||||
public Map<String, Object> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Id value.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the score.
|
||||
*/
|
||||
public double getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index name.
|
||||
*/
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index type.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source document.
|
||||
*/
|
||||
public void setSource(Map<String, Object> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the id value.
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the score.
|
||||
*/
|
||||
public void setScore(double score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index name.
|
||||
*/
|
||||
public void setIndex(String index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index type.
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebean.event.readaudit;
|
||||
|
||||
/**
|
||||
* Log that the query was executed
|
||||
*/
|
||||
public interface ReadAuditLogger {
|
||||
|
||||
/**
|
||||
* Called when a new query plan is created.
|
||||
* <p>
|
||||
* The query plan has the full sql and logging the query plan separately means that each of
|
||||
* the bean and many read events can log the query plan key and not the full sql (reducing the
|
||||
* bulk size of the read audit logs).
|
||||
* </p>
|
||||
*/
|
||||
void queryPlan(ReadAuditQueryPlan queryPlan);
|
||||
|
||||
/**
|
||||
* Audit a find bean query that returned a bean.
|
||||
* <p>
|
||||
* Finds that did not return a bean are excluded.
|
||||
* </p>
|
||||
*/
|
||||
void auditBean(ReadEvent readBean);
|
||||
|
||||
/**
|
||||
* Audit a find many query that returned some beans.
|
||||
* <p>
|
||||
* Finds that did not return any beans are excluded.
|
||||
* </p>
|
||||
* <p>
|
||||
* For large queries executed via findEach() etc the ids are collected in batches
|
||||
* and logged. Hence the ids list has a maximum size of the batch size.
|
||||
* </p>
|
||||
*/
|
||||
void auditMany(ReadEvent readMany);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ebean.event.readaudit;
|
||||
|
||||
/**
|
||||
* Set user context information into the read event prior to it being logged.
|
||||
*/
|
||||
public interface ReadAuditPrepare {
|
||||
|
||||
/**
|
||||
* Prepare the read event by setting any user context information into the read event such as the
|
||||
* application user id and ip address.
|
||||
* <p>
|
||||
* This method is called prior to the read event being sent to the ReadAuditLogger.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that for findFutureList() queries prepare() is called early in the foreground thread
|
||||
* prior to the query executing and at that point the ReadEvent bean only has the bean type
|
||||
* and no other details (which are populated later when the query is executed in the background
|
||||
* thread).
|
||||
* </p>
|
||||
*/
|
||||
void prepare(ReadEvent readEvent);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package io.ebean.event.readaudit;
|
||||
|
||||
/**
|
||||
* A SQL query and associated keys.
|
||||
* <p>
|
||||
* This is logged as a separate event so that the
|
||||
* </p>
|
||||
*/
|
||||
public class ReadAuditQueryPlan {
|
||||
|
||||
String beanType;
|
||||
|
||||
String queryKey;
|
||||
|
||||
String sql;
|
||||
|
||||
/**
|
||||
* Construct given the beanType, queryKey and sql.
|
||||
*/
|
||||
public ReadAuditQueryPlan(String beanType, String queryKey, String sql) {
|
||||
this.beanType = beanType;
|
||||
this.queryKey = queryKey;
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for JSON tools.
|
||||
*/
|
||||
public ReadAuditQueryPlan() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "beanType:" + beanType + " queryKey:" + queryKey + " sql:" + sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean type.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean type.
|
||||
*/
|
||||
public void setBeanType(String beanType) {
|
||||
this.beanType = beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query key (relative to the bean type).
|
||||
*/
|
||||
public String getQueryKey() {
|
||||
return queryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query key.
|
||||
*/
|
||||
public void setQueryKey(String queryKey) {
|
||||
this.queryKey = queryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql statement.
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sql statement.
|
||||
*/
|
||||
public void setSql(String sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ReadAuditQueryPlan that = (ReadAuditQueryPlan) o;
|
||||
if (!beanType.equals(that.beanType)) return false;
|
||||
if (!queryKey.equals(that.queryKey)) return false;
|
||||
return sql.equals(that.sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = beanType.hashCode();
|
||||
result = 92821 * result + queryKey.hashCode();
|
||||
result = 92821 * result + sql.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package io.ebean.event.readaudit;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Read event sent to the ReadEventLogger.
|
||||
* <p>
|
||||
* This is a flattened in that it contains either a read bean or list of beans. It is flattened
|
||||
* in this way to simplify logging and processing and simply means that it either contains an
|
||||
* id or a list of ids.
|
||||
* </p>
|
||||
*/
|
||||
public class ReadEvent {
|
||||
|
||||
/**
|
||||
* User defined 'source' such as the application name.
|
||||
*/
|
||||
protected String source;
|
||||
|
||||
/**
|
||||
* Application user id expected to be optionally populated by ChangeLogPrepare.
|
||||
*/
|
||||
protected String userId;
|
||||
|
||||
/**
|
||||
* Application user ip address expected to be optionally populated by ChangeLogPrepare.
|
||||
*/
|
||||
protected String userIpAddress;
|
||||
|
||||
/**
|
||||
* Arbitrary user context information expected to be optionally populated by ChangeLogPrepare.
|
||||
*/
|
||||
protected Map<String, String> userContext;
|
||||
|
||||
/**
|
||||
* The time the bean change was created.
|
||||
*/
|
||||
protected long eventTime;
|
||||
|
||||
/**
|
||||
* The type of the bean(s) read.
|
||||
*/
|
||||
protected String beanType;
|
||||
|
||||
/**
|
||||
* The query key (relative to the bean type).
|
||||
*/
|
||||
protected String queryKey;
|
||||
|
||||
/**
|
||||
* The bind log when the query was executed.
|
||||
*/
|
||||
protected String bindLog;
|
||||
|
||||
/**
|
||||
* The id of the bean read.
|
||||
*/
|
||||
protected Object id;
|
||||
|
||||
/**
|
||||
* The ids of the beans read.
|
||||
*/
|
||||
protected List<Object> ids;
|
||||
|
||||
/**
|
||||
* Common constructor for single bean and multi-bean read events.
|
||||
*/
|
||||
protected ReadEvent(String beanType, String queryKey, String bindLog) {
|
||||
this.beanType = beanType;
|
||||
this.queryKey = queryKey;
|
||||
this.bindLog = bindLog;
|
||||
this.eventTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for a single bean read.
|
||||
*/
|
||||
public ReadEvent(String beanType, String queryKey, String bindLog, Object id) {
|
||||
this(beanType, queryKey, bindLog);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for many beans read.
|
||||
*/
|
||||
public ReadEvent(String beanType, String queryKey, String bindLog, List<Object> ids) {
|
||||
this(beanType, queryKey, bindLog);
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for many future list query.
|
||||
*/
|
||||
public ReadEvent(String beanType) {
|
||||
this.beanType = beanType;
|
||||
this.eventTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for JSON tools.
|
||||
*/
|
||||
public ReadEvent() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a code that identifies the source of the change (like the name of the application).
|
||||
*/
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source of the change (like the name of the application).
|
||||
*/
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the application user Id.
|
||||
*/
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application user Id.
|
||||
* <p>
|
||||
* This can be set by the ChangeLogListener in the prepare() method which is called
|
||||
* in the foreground thread.
|
||||
* </p>
|
||||
*/
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the application users ip address.
|
||||
*/
|
||||
public String getUserIpAddress() {
|
||||
return userIpAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application users ip address.
|
||||
* <p>
|
||||
* This can be set by the ChangeLogListener in the prepare() method which is called
|
||||
* in the foreground thread.
|
||||
* </p>
|
||||
*/
|
||||
public void setUserIpAddress(String userIpAddress) {
|
||||
this.userIpAddress = userIpAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a user context value - anything you set yourself in ChangeLogListener prepare().
|
||||
*/
|
||||
public Map<String, String> getUserContext() {
|
||||
if (userContext == null) {
|
||||
userContext = new LinkedHashMap<>();
|
||||
}
|
||||
return userContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user context value (anything you like).
|
||||
* <p>
|
||||
* This can be set by the ChangeLogListener in the prepare() method which is called
|
||||
* in the foreground thread.
|
||||
* </p>
|
||||
*/
|
||||
public void setUserContext(Map<String, String> userContext) {
|
||||
this.userContext = userContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of bean read.
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of bean read.
|
||||
*/
|
||||
public void setBeanType(String beanType) {
|
||||
this.beanType = beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query key (relative to the bean type).
|
||||
*/
|
||||
public String getQueryKey() {
|
||||
return queryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query key (relative to the bean type).
|
||||
*/
|
||||
public void setQueryKey(String queryKey) {
|
||||
this.queryKey = queryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind log used when executing the query.
|
||||
*/
|
||||
public String getBindLog() {
|
||||
return bindLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bind log used when executing the query.
|
||||
*/
|
||||
public void setBindLog(String bindLog) {
|
||||
this.bindLog = bindLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event date time.
|
||||
*/
|
||||
public long getEventTime() {
|
||||
return eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the event date time.
|
||||
*/
|
||||
public void setEventTime(long eventTime) {
|
||||
this.eventTime = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id of the bean read.
|
||||
*/
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the id of the bean read.
|
||||
*/
|
||||
public void setId(Object id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ids of the beans read.
|
||||
*/
|
||||
public List<Object> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ids of the beans read.
|
||||
*/
|
||||
public void setIds(List<Object> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Provides Auditing of read events including queries and L2 cache.
|
||||
* <p>
|
||||
* Provides a built support for supplied an audit of all the 'read events' for beans annotated
|
||||
* with <code>@ReadAudit</code>
|
||||
* </p>
|
||||
*/
|
||||
package io.ebean.event.readaudit;
|
||||
@@ -0,0 +1,72 @@
|
||||
package io.ebean.plugin;
|
||||
|
||||
import io.ebean.FetchPath;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.docstore.DocUpdateContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Doc store functions for a specific entity bean type.
|
||||
*
|
||||
* @param <T> The type of entity bean
|
||||
*/
|
||||
public interface BeanDocType<T> {
|
||||
|
||||
/**
|
||||
* Return the doc store index type for this bean type.
|
||||
*/
|
||||
String indexType();
|
||||
|
||||
/**
|
||||
* Return the doc store index name for this bean type.
|
||||
*/
|
||||
String indexName();
|
||||
|
||||
/**
|
||||
* Apply the appropriate fetch path to the query such that the query returns beans matching
|
||||
* the document store structure with the expected embedded properties.
|
||||
*/
|
||||
void applyPath(Query<T> spiQuery);
|
||||
|
||||
/**
|
||||
* Return the FetchPath for the embedded document.
|
||||
*/
|
||||
FetchPath embedded(String path);
|
||||
|
||||
/**
|
||||
* For embedded 'many' properties we need a FetchPath relative to the root which is used to
|
||||
* build and replace the embedded list.
|
||||
*/
|
||||
FetchPath embeddedManyRoot(String path);
|
||||
|
||||
/**
|
||||
* Return a 'raw' property mapped for the given property.
|
||||
* If none exists the given property is returned.
|
||||
*/
|
||||
String rawProperty(String property);
|
||||
|
||||
/**
|
||||
* Store the bean in the doc store index.
|
||||
* <p>
|
||||
* This somewhat assumes the bean is fetched with appropriate path properties
|
||||
* to match the expected document structure.
|
||||
*/
|
||||
void index(Object idValue, T bean, DocUpdateContext txn) throws IOException;
|
||||
|
||||
/**
|
||||
* Add a delete by Id to the doc store.
|
||||
*/
|
||||
void deleteById(Object idValue, DocUpdateContext txn) throws IOException;
|
||||
|
||||
/**
|
||||
* Add a embedded document update to the doc store.
|
||||
*
|
||||
* @param idValue the Id value of the bean holding the embedded document
|
||||
* @param embeddedProperty the embedded property
|
||||
* @param embeddedRawContent the content of the embedded document in JSON form
|
||||
* @param txn the doc store transaction to add the update to
|
||||
*/
|
||||
void updateEmbedded(Object idValue, String embeddedProperty, String embeddedRawContent, DocUpdateContext txn) throws IOException;
|
||||
|
||||
}
|
||||
@@ -2,12 +2,15 @@ package io.ebean.plugin;
|
||||
|
||||
import io.ebean.Query;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.docstore.DocMapping;
|
||||
import io.ebean.event.BeanFindController;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.BeanPersistListener;
|
||||
import io.ebean.event.BeanQueryAdapter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Information and methods on BeanDescriptors made available to plugins.
|
||||
@@ -142,4 +145,73 @@ public interface BeanType<T> {
|
||||
*/
|
||||
IdType idType();
|
||||
|
||||
/**
|
||||
* Return true if this bean type has doc store backing.
|
||||
*/
|
||||
boolean isDocStoreMapped();
|
||||
|
||||
/**
|
||||
* Return the DocumentMapping for this bean type.
|
||||
* <p>
|
||||
* This is the document structure and mapping options for how this bean type is mapped
|
||||
* for the document store.
|
||||
* </p>
|
||||
*/
|
||||
DocMapping docMapping();
|
||||
|
||||
/**
|
||||
* Return the doc store queueId for this bean type.
|
||||
*/
|
||||
String docStoreQueueId();
|
||||
|
||||
/**
|
||||
* Return the doc store support for this bean type.\
|
||||
*/
|
||||
BeanDocType<T> docStore();
|
||||
|
||||
/**
|
||||
* Add the discriminator value to the query if needed.
|
||||
*/
|
||||
void addInheritanceWhere(Query<?> query);
|
||||
|
||||
/**
|
||||
* Return the root bean type for an inheritance hierarchy.
|
||||
*/
|
||||
BeanType<?> root();
|
||||
|
||||
/**
|
||||
* Return true if this bean type has an inheritance hierarchy.
|
||||
*/
|
||||
boolean hasInheritance();
|
||||
|
||||
/**
|
||||
* Return true if this object is the root level object in its entity
|
||||
* inheritance.
|
||||
*/
|
||||
boolean isInheritanceRoot();
|
||||
|
||||
/**
|
||||
* Returns all direct children of this beantype
|
||||
*/
|
||||
List<BeanType<?>> inheritanceChildren();
|
||||
|
||||
/**
|
||||
* Returns the parent in inheritance hierarchy
|
||||
*/
|
||||
BeanType<?> inheritanceParent();
|
||||
|
||||
/**
|
||||
* Visit all children recursively
|
||||
*/
|
||||
void visitAllInheritanceChildren(Consumer<BeanType<?>> visitor);
|
||||
|
||||
/**
|
||||
* Return the discriminator column.
|
||||
*/
|
||||
String discColumn();
|
||||
|
||||
/**
|
||||
* Create a bean given the discriminator value.
|
||||
*/
|
||||
T createBeanUsingDisc(Object discValue);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ public interface SpiServer extends Database {
|
||||
*/
|
||||
List<? extends BeanType<?>> beanTypes(String baseTableName);
|
||||
|
||||
/**
|
||||
* Return the bean type for a given doc store queueId.
|
||||
*/
|
||||
BeanType<?> beanTypeForQueueId(String queueId);
|
||||
|
||||
/**
|
||||
* Return a BeanLoader.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package io.ebean.search;
|
||||
|
||||
/**
|
||||
* Options for the text match and multi match expressions.
|
||||
*/
|
||||
public abstract class AbstractMatch {
|
||||
|
||||
protected boolean operatorAnd;
|
||||
|
||||
protected String analyzer;
|
||||
|
||||
protected double boost;
|
||||
|
||||
protected String minShouldMatch;
|
||||
|
||||
protected int maxExpansions;
|
||||
|
||||
protected String zeroTerms;
|
||||
|
||||
protected double cutoffFrequency;
|
||||
|
||||
protected String fuzziness;
|
||||
|
||||
protected int prefixLength;
|
||||
|
||||
protected String rewrite;
|
||||
|
||||
/**
|
||||
* Return true if using the AND operator otherwise using the OR operator.
|
||||
*/
|
||||
public boolean isOperatorAnd() {
|
||||
return operatorAnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the boost.
|
||||
*/
|
||||
public double getBoost() {
|
||||
return boost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum should match.
|
||||
*/
|
||||
public String getMinShouldMatch() {
|
||||
return minShouldMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the zero terms option.
|
||||
*/
|
||||
public String getZeroTerms() {
|
||||
return zeroTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cutoff frequency.
|
||||
*/
|
||||
public double getCutoffFrequency() {
|
||||
return cutoffFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max expansions.
|
||||
*/
|
||||
public int getMaxExpansions() {
|
||||
return maxExpansions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the analyzer.
|
||||
*/
|
||||
public String getAnalyzer() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fuzziness.
|
||||
*/
|
||||
public String getFuzziness() {
|
||||
return fuzziness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prefix length.
|
||||
*/
|
||||
public int getPrefixLength() {
|
||||
return prefixLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rewrite option.
|
||||
*/
|
||||
public String getRewrite() {
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package io.ebean.search;
|
||||
|
||||
/**
|
||||
* Options for the text match expression.
|
||||
*/
|
||||
public class Match extends AbstractMatch {
|
||||
|
||||
protected boolean phrase;
|
||||
|
||||
protected boolean phrasePrefix;
|
||||
|
||||
public Match() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to be a "Phrase" type expression.
|
||||
*/
|
||||
public Match phrase() {
|
||||
phrase = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to be a "Phrase Prefix" type expression.
|
||||
*/
|
||||
public Match phrasePrefix() {
|
||||
phrasePrefix = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the AND operator (rather than OR).
|
||||
*/
|
||||
public Match opAnd() {
|
||||
operatorAnd = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the OR operator (rather than AND).
|
||||
*/
|
||||
public Match opOr() {
|
||||
operatorAnd = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the zero terms.
|
||||
*/
|
||||
public Match zeroTerms(String zeroTerms) {
|
||||
this.zeroTerms = zeroTerms;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cutoff frequency.
|
||||
*/
|
||||
public Match cutoffFrequency(double cutoffFrequency) {
|
||||
this.cutoffFrequency = cutoffFrequency;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max expansions (for phrase prefix only).
|
||||
*/
|
||||
public Match maxExpansions(int maxExpansions) {
|
||||
this.maxExpansions = maxExpansions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Analyzer to use for this expression.
|
||||
*/
|
||||
public Match analyzer(String analyzer) {
|
||||
this.analyzer = analyzer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boost.
|
||||
*/
|
||||
public Match boost(double boost) {
|
||||
this.boost = boost;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rewrite to use.
|
||||
*/
|
||||
public Match minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rewrite to use.
|
||||
*/
|
||||
public Match rewrite(String rewrite) {
|
||||
this.rewrite = rewrite;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a phrase query.
|
||||
*/
|
||||
public boolean isPhrase() {
|
||||
return phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a phrase prefix query.
|
||||
*/
|
||||
public boolean isPhrasePrefix() {
|
||||
return phrasePrefix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package io.ebean.search;
|
||||
|
||||
/**
|
||||
* Options for the text match expression.
|
||||
*/
|
||||
public class MultiMatch extends AbstractMatch {
|
||||
|
||||
/**
|
||||
* The MultiMatch type.
|
||||
*/
|
||||
public enum Type {
|
||||
BEST_FIELDS,
|
||||
MOST_FIELDS,
|
||||
CROSS_FIELDS,
|
||||
PHRASE,
|
||||
PHRASE_PREFIX
|
||||
}
|
||||
|
||||
protected final String[] fields;
|
||||
|
||||
protected Type type = Type.BEST_FIELDS;
|
||||
|
||||
protected double tieBreaker;
|
||||
|
||||
/**
|
||||
* Create with the given fields.
|
||||
*/
|
||||
public static MultiMatch fields(String... fields) {
|
||||
return new MultiMatch(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with a set of fields.
|
||||
*/
|
||||
public MultiMatch(String... fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of query.
|
||||
*/
|
||||
public MultiMatch type(Type type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tieBreaker to use.
|
||||
*/
|
||||
public MultiMatch tieBreaker(double tieBreaker) {
|
||||
this.tieBreaker = tieBreaker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the AND operator (rather than OR).
|
||||
*/
|
||||
public MultiMatch opAnd() {
|
||||
operatorAnd = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the OR operator (rather than AND).
|
||||
*/
|
||||
public MultiMatch opOr() {
|
||||
operatorAnd = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match value.
|
||||
*/
|
||||
public MultiMatch minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boost.
|
||||
*/
|
||||
public MultiMatch boost(double boost) {
|
||||
this.boost = boost;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the zero terms.
|
||||
*/
|
||||
public MultiMatch zeroTerms(String zeroTerms) {
|
||||
this.zeroTerms = zeroTerms;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cutoff frequency.
|
||||
*/
|
||||
public MultiMatch cutoffFrequency(double cutoffFrequency) {
|
||||
this.cutoffFrequency = cutoffFrequency;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max expansions (for phrase prefix only).
|
||||
*/
|
||||
public MultiMatch maxExpansions(int maxExpansions) {
|
||||
this.maxExpansions = maxExpansions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Analyzer to use for this expression.
|
||||
*/
|
||||
public MultiMatch analyzer(String analyzer) {
|
||||
this.analyzer = analyzer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rewrite to use.
|
||||
*/
|
||||
public MultiMatch rewrite(String rewrite) {
|
||||
this.rewrite = rewrite;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type.
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fields to search.
|
||||
*/
|
||||
public String[] getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tie breaker.
|
||||
*/
|
||||
public double getTieBreaker() {
|
||||
return tieBreaker;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package io.ebean.search;
|
||||
|
||||
/**
|
||||
* Text common terms query.
|
||||
* <p>
|
||||
* This maps to an ElasticSearch "common terms query".
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* TextCommonTerms options = new TextCommonTerms()
|
||||
* .cutoffFrequency(0.001)
|
||||
* .minShouldMatch("50%")
|
||||
* .lowFreqOperatorAnd(true)
|
||||
* .highFreqOperatorAnd(true);
|
||||
*
|
||||
* List<Customer> customers = database.find(Customer.class)
|
||||
* .text()
|
||||
* .textCommonTerms("the brown", options)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // ElasticSearch expression
|
||||
*
|
||||
* "common": {
|
||||
* "body": {
|
||||
* "query": "the brown",
|
||||
* "cutoff_frequency": 0.001,
|
||||
* "low_freq_operator": "and",
|
||||
* "high_freq_operator": "and",
|
||||
* "minimum_should_match": "50%"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public class TextCommonTerms {
|
||||
|
||||
protected double cutoffFrequency;
|
||||
|
||||
protected boolean lowFreqOperatorAnd;
|
||||
protected boolean highFreqOperatorAnd;
|
||||
|
||||
protected String minShouldMatch;
|
||||
protected String minShouldMatchLowFreq;
|
||||
protected String minShouldMatchHighFreq;
|
||||
|
||||
/**
|
||||
* Set the cutoff frequency.
|
||||
*/
|
||||
public TextCommonTerms cutoffFrequency(double cutoffFrequency) {
|
||||
this.cutoffFrequency = cutoffFrequency;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if low frequency terms should use AND operator.
|
||||
*/
|
||||
public TextCommonTerms lowFreqOperatorAnd(boolean opAnd) {
|
||||
this.lowFreqOperatorAnd = opAnd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if high frequency terms should use AND operator.
|
||||
*/
|
||||
public TextCommonTerms highFreqOperatorAnd(boolean opAnd) {
|
||||
this.highFreqOperatorAnd = opAnd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match.
|
||||
*/
|
||||
public TextCommonTerms minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match for low frequency terms.
|
||||
*/
|
||||
public TextCommonTerms minShouldMatchLowFreq(String minShouldMatchLowFreq) {
|
||||
this.minShouldMatchLowFreq = minShouldMatchLowFreq;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match for high frequency terms.
|
||||
*/
|
||||
public TextCommonTerms minShouldMatchHighFreq(String minShouldMatchHighFreq) {
|
||||
this.minShouldMatchHighFreq = minShouldMatchHighFreq;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if low freq should use the AND operator.
|
||||
*/
|
||||
public boolean isLowFreqOperatorAnd() {
|
||||
return lowFreqOperatorAnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if high freq should use the AND operator.
|
||||
*/
|
||||
public boolean isHighFreqOperatorAnd() {
|
||||
return highFreqOperatorAnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cutoff frequency.
|
||||
*/
|
||||
public double getCutoffFrequency() {
|
||||
return cutoffFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum to match.
|
||||
*/
|
||||
public String getMinShouldMatch() {
|
||||
return minShouldMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum to match for high frequency.
|
||||
*/
|
||||
public String getMinShouldMatchHighFreq() {
|
||||
return minShouldMatchHighFreq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum to match for low frequency.
|
||||
*/
|
||||
public String getMinShouldMatchLowFreq() {
|
||||
return minShouldMatchLowFreq;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
package io.ebean.search;
|
||||
|
||||
/**
|
||||
* Text query string options.
|
||||
* <p>
|
||||
* This maps to an ElasticSearch "query string query".
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* TextQueryString options = new TextQueryString()
|
||||
* .analyzeWildcard(true)
|
||||
* .fields("name")
|
||||
* .lenient(true)
|
||||
* .opAnd();
|
||||
*
|
||||
* List<Customer> customers = database.find(Customer.class)
|
||||
* .text()
|
||||
* .textSimple("quick brown", options)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // just use default options
|
||||
* TextQueryString options = new TextQueryString();
|
||||
*
|
||||
* List<Customer> customers = database.find(Customer.class)
|
||||
* .text()
|
||||
* .textSimple("quick brown", options)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public class TextQueryString {
|
||||
|
||||
public static final int DEFAULT_FUZZY_MAX_EXPANSIONS = 50;
|
||||
|
||||
protected final String[] fields;
|
||||
|
||||
/**
|
||||
* Only used when multiple fields set.
|
||||
*/
|
||||
protected boolean useDisMax = true;
|
||||
|
||||
/**
|
||||
* Only used when multiple fields set.
|
||||
*/
|
||||
protected double tieBreaker;
|
||||
|
||||
protected String defaultField;
|
||||
|
||||
protected boolean operatorAnd;
|
||||
|
||||
protected String analyzer;
|
||||
|
||||
protected boolean allowLeadingWildcard = true;
|
||||
|
||||
protected boolean lowercaseExpandedTerms = true;
|
||||
|
||||
protected int fuzzyMaxExpansions = DEFAULT_FUZZY_MAX_EXPANSIONS;
|
||||
|
||||
protected String fuzziness;
|
||||
|
||||
protected int fuzzyPrefixLength;
|
||||
|
||||
protected double phraseSlop;
|
||||
|
||||
protected double boost;
|
||||
|
||||
protected boolean analyzeWildcard;
|
||||
|
||||
protected boolean autoGeneratePhraseQueries;
|
||||
|
||||
protected String minShouldMatch;
|
||||
|
||||
protected boolean lenient;
|
||||
|
||||
protected String locale;
|
||||
|
||||
protected String timeZone;
|
||||
|
||||
protected String rewrite;
|
||||
|
||||
/**
|
||||
* Create with given fields.
|
||||
*/
|
||||
public static TextQueryString fields(String... fields) {
|
||||
return new TextQueryString(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with the fields to use.
|
||||
*/
|
||||
public TextQueryString(String... fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the AND operator (rather than OR).
|
||||
*/
|
||||
public TextQueryString opAnd() {
|
||||
this.operatorAnd = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the OR operator (rather than AND).
|
||||
*/
|
||||
public TextQueryString opOr() {
|
||||
this.operatorAnd = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale.
|
||||
*/
|
||||
public TextQueryString locale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lenient mode.
|
||||
*/
|
||||
public TextQueryString lenient(boolean lenient) {
|
||||
this.lenient = lenient;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match.
|
||||
*/
|
||||
public TextQueryString minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the analyzer.
|
||||
*/
|
||||
public TextQueryString analyzer(String analyzer) {
|
||||
this.analyzer = analyzer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set useDisMax option (when multiple fields only).
|
||||
*/
|
||||
public TextQueryString useDisMax(boolean useDisMax) {
|
||||
this.useDisMax = useDisMax;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tieBreaker option (when multiple fields only).
|
||||
*/
|
||||
public TextQueryString tieBreaker(double tieBreaker) {
|
||||
this.tieBreaker = tieBreaker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default field.
|
||||
*/
|
||||
public TextQueryString defaultField(String defaultField) {
|
||||
this.defaultField = defaultField;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set allow leading wildcard mode.
|
||||
*/
|
||||
public TextQueryString allowLeadingWildcard(boolean allowLeadingWildcard) {
|
||||
this.allowLeadingWildcard = allowLeadingWildcard;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lowercase expanded terms mode.
|
||||
*/
|
||||
public TextQueryString lowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
|
||||
this.lowercaseExpandedTerms = lowercaseExpandedTerms;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fuzzy max expansions.
|
||||
*/
|
||||
public TextQueryString fuzzyMaxExpansions(int fuzzyMaxExpansions) {
|
||||
this.fuzzyMaxExpansions = fuzzyMaxExpansions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fuzziness.
|
||||
*/
|
||||
public TextQueryString fuzziness(String fuzziness) {
|
||||
this.fuzziness = fuzziness;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fuzzy prefix length.
|
||||
*/
|
||||
public TextQueryString fuzzyPrefixLength(int fuzzyPrefixLength) {
|
||||
this.fuzzyPrefixLength = fuzzyPrefixLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the phrase slop.
|
||||
*/
|
||||
public TextQueryString phraseSlop(double phraseSlop) {
|
||||
this.phraseSlop = phraseSlop;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boost.
|
||||
*/
|
||||
public TextQueryString boost(double boost) {
|
||||
this.boost = boost;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the analyze wildcard mode.
|
||||
*/
|
||||
public TextQueryString analyzeWildcard(boolean analyzeWildcard) {
|
||||
this.analyzeWildcard = analyzeWildcard;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auto generate phrase queries mode.
|
||||
*/
|
||||
public TextQueryString autoGeneratePhraseQueries(boolean autoGeneratePhraseQueries) {
|
||||
this.autoGeneratePhraseQueries = autoGeneratePhraseQueries;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time zone.
|
||||
*/
|
||||
public TextQueryString timeZone(String timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rewrite option.
|
||||
*/
|
||||
public TextQueryString rewrite(String rewrite) {
|
||||
this.rewrite = rewrite;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rewrite option.
|
||||
*/
|
||||
public String getRewrite() {
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fields.
|
||||
*/
|
||||
public String[] getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if AND is the default operator.
|
||||
*/
|
||||
public boolean isOperatorAnd() {
|
||||
return operatorAnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the analyzer.
|
||||
*/
|
||||
public String getAnalyzer() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the locale.
|
||||
*/
|
||||
public String getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return lenient mode.
|
||||
*/
|
||||
public boolean isLenient() {
|
||||
return lenient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum should match.
|
||||
*/
|
||||
public String getMinShouldMatch() {
|
||||
return minShouldMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the useDixMax mode.
|
||||
*/
|
||||
public boolean isUseDisMax() {
|
||||
return useDisMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tie breaker.
|
||||
*/
|
||||
public double getTieBreaker() {
|
||||
return tieBreaker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default field.
|
||||
*/
|
||||
public String getDefaultField() {
|
||||
return defaultField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the allow leading wildcard mode.
|
||||
*/
|
||||
public boolean isAllowLeadingWildcard() {
|
||||
return allowLeadingWildcard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the lowercase expanded terms mode.
|
||||
*/
|
||||
public boolean isLowercaseExpandedTerms() {
|
||||
return lowercaseExpandedTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fuzzy max expansions.
|
||||
*/
|
||||
public int getFuzzyMaxExpansions() {
|
||||
return fuzzyMaxExpansions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fuzziness.
|
||||
*/
|
||||
public String getFuzziness() {
|
||||
return fuzziness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fuzzy prefix length.
|
||||
*/
|
||||
public int getFuzzyPrefixLength() {
|
||||
return fuzzyPrefixLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the phrase slop.
|
||||
*/
|
||||
public double getPhraseSlop() {
|
||||
return phraseSlop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the analyze wildcard mode.
|
||||
*/
|
||||
public boolean isAnalyzeWildcard() {
|
||||
return analyzeWildcard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the boost.
|
||||
*/
|
||||
public double getBoost() {
|
||||
return boost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the auto generate phase queries mode.
|
||||
*/
|
||||
public boolean isAutoGeneratePhraseQueries() {
|
||||
return autoGeneratePhraseQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time zone.
|
||||
*/
|
||||
public String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package io.ebean.search;
|
||||
|
||||
/**
|
||||
* Simple text query options.
|
||||
* <p>
|
||||
* This maps to an ElasticSearch "simple text query".
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* TextSimple options = new TextSimple()
|
||||
* .analyzeWildcard(true)
|
||||
* .fields("name")
|
||||
* .lenient(true)
|
||||
* .opAnd();
|
||||
*
|
||||
* List<Customer> customers = database.find(Customer.class)
|
||||
* .text()
|
||||
* .textSimple("quick brown", options)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public class TextSimple {
|
||||
|
||||
protected String[] fields;
|
||||
|
||||
protected boolean operatorAnd;
|
||||
|
||||
protected String analyzer;
|
||||
|
||||
protected String flags;
|
||||
|
||||
protected boolean lowercaseExpandedTerms = true;
|
||||
|
||||
protected boolean analyzeWildcard;
|
||||
|
||||
protected String locale;
|
||||
|
||||
protected boolean lenient;
|
||||
|
||||
protected String minShouldMatch;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
public TextSimple() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fields.
|
||||
*/
|
||||
public TextSimple fields(String... fields) {
|
||||
this.fields = fields;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use AND as the default operator.
|
||||
*/
|
||||
public TextSimple opAnd() {
|
||||
this.operatorAnd = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use OR as the default operator.
|
||||
*/
|
||||
public TextSimple opOr() {
|
||||
this.operatorAnd = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the analyzer
|
||||
*/
|
||||
public TextSimple analyzer(String analyzer) {
|
||||
this.analyzer = analyzer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flags.
|
||||
*/
|
||||
public TextSimple flags(String flags) {
|
||||
this.flags = flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the false to not use lowercase expanded terms.
|
||||
*/
|
||||
public TextSimple lowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
|
||||
this.lowercaseExpandedTerms = lowercaseExpandedTerms;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to use analyze wildcard.
|
||||
*/
|
||||
public TextSimple analyzeWildcard(boolean analyzeWildcard) {
|
||||
this.analyzeWildcard = analyzeWildcard;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale.
|
||||
*/
|
||||
public TextSimple locale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lenient mode.
|
||||
*/
|
||||
public TextSimple lenient(boolean lenient) {
|
||||
this.lenient = lenient;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match.
|
||||
*/
|
||||
public TextSimple minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return lenient mode.
|
||||
*/
|
||||
public boolean isLenient() {
|
||||
return lenient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to analyse wildcard.
|
||||
*/
|
||||
public boolean isAnalyzeWildcard() {
|
||||
return analyzeWildcard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return lowercase expanded terms mode.
|
||||
*/
|
||||
public boolean isLowercaseExpandedTerms() {
|
||||
return lowercaseExpandedTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the default operator should be AND.
|
||||
*/
|
||||
public boolean isOperatorAnd() {
|
||||
return operatorAnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the analyzer to use.
|
||||
*/
|
||||
public String getAnalyzer() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fields.
|
||||
*/
|
||||
public String[] getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the locale.
|
||||
*/
|
||||
public String getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the flags.
|
||||
*/
|
||||
public String getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum should match.
|
||||
*/
|
||||
public String getMinShouldMatch() {
|
||||
return minShouldMatch;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides text search expressions like Match, TextQueryString etc.
|
||||
*/
|
||||
package io.ebean.search;
|
||||
@@ -30,10 +30,13 @@ module io.ebean.api {
|
||||
exports io.ebean.config;
|
||||
exports io.ebean.config.dbplatform;
|
||||
exports io.ebean.event;
|
||||
exports io.ebean.event.readaudit;
|
||||
exports io.ebean.event.changelog;
|
||||
exports io.ebean.common;
|
||||
exports io.ebean.docstore;
|
||||
exports io.ebean.plugin;
|
||||
exports io.ebean.metric;
|
||||
exports io.ebean.search;
|
||||
exports io.ebean.service;
|
||||
exports io.ebean.text;
|
||||
exports io.ebean.text.json;
|
||||
|
||||
+26
-26
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -89,25 +89,25 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -125,13 +125,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -155,37 +155,37 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-spring-txn</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<!-- platforms -->
|
||||
@@ -193,79 +193,79 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-clickhouse</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-db2</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-h2</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-hana</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mariadb</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mysql</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-nuodb</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-oracle</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgres</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlite</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlserver</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -34,13 +34,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+36
-13
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -22,7 +22,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -46,9 +46,20 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>13.18.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.13.1</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Class retention Nonnull and Nullable annotations
|
||||
@@ -148,21 +159,21 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>13.23.0-jakarta</version>
|
||||
<version>13.23.2-jakarta</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -173,13 +184,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -230,6 +234,25 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<doctitle>Ebean 12</doctitle>
|
||||
<overview>src/main/java/io/ebean/overview.html</overview>
|
||||
<excludePackageNames>io.ebeaninternal.*:io.ebeanservice:io.ebean.common:io.ebean.bean:io.ebean.service:io.ebean.metric:io.ebean.util:io.ebean.config.properties:io.ebean.config.dbplatform</excludePackageNames>
|
||||
<linksource>true</linksource>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
|
||||
@@ -51,4 +51,8 @@ public interface LoadContext {
|
||||
*/
|
||||
void register(String path, BeanPropertyAssocMany<?> many, BeanCollection<?> bc);
|
||||
|
||||
/**
|
||||
* Use soft-references for streaming queries, so unreachable entries can be garbage collected.
|
||||
*/
|
||||
void useReferences(boolean useReferences);
|
||||
}
|
||||
|
||||
@@ -43,4 +43,5 @@ public interface LoadManyBuffer {
|
||||
|
||||
void configureQuery(SpiQuery<?> query);
|
||||
|
||||
boolean isUseDocStore();
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public final class LoadManyRequest extends LoadRequest {
|
||||
query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
|
||||
}
|
||||
query.setLazyLoadForParents(many);
|
||||
many.addWhereParentIdIn(query, parentIdList(server));
|
||||
many.addWhereParentIdIn(query, parentIdList(server), loadContext.isUseDocStore());
|
||||
query.setPersistenceContext(loadContext.persistenceContext());
|
||||
query.setLoadDescription(lazy ? "+lazy" : "+query", description());
|
||||
if (lazy) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import io.avaje.lang.Nullable;
|
||||
import io.ebean.*;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.CallOrigin;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.SpiQuery.Type;
|
||||
@@ -99,6 +101,11 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio
|
||||
*/
|
||||
BeanDescriptor<?> descriptorById(String className);
|
||||
|
||||
/**
|
||||
* Return BeanDescriptor using it's unique doc store queueId.
|
||||
*/
|
||||
BeanDescriptor<?> descriptorByQueueId(String queueId);
|
||||
|
||||
/**
|
||||
* Return BeanDescriptors mapped to this table.
|
||||
*/
|
||||
@@ -183,6 +190,17 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio
|
||||
*/
|
||||
boolean isSupportedType(java.lang.reflect.Type genericType);
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger to use for logging all read audit events.
|
||||
*/
|
||||
ReadAuditLogger readAuditLogger();
|
||||
|
||||
/**
|
||||
* Return the ReadAuditPrepare used to populate the read audit events with
|
||||
* user context information (user id, user ip address etc).
|
||||
*/
|
||||
ReadAuditPrepare readAuditPrepare();
|
||||
|
||||
/**
|
||||
* Return the DataTimeZone to use when reading/writing timestamps via JDBC.
|
||||
*/
|
||||
@@ -331,6 +349,10 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio
|
||||
|
||||
<T> FutureRowCount<T> findFutureCount(SpiQuery<T> query);
|
||||
|
||||
<T> FutureIds<T> findFutureIds(SpiQuery<T> query);
|
||||
|
||||
<T> FutureList<T> findFutureList(SpiQuery<T> query);
|
||||
|
||||
<T> PagedList<T> findPagedList(SpiQuery<T> query);
|
||||
|
||||
<T> Set<T> findSet(SpiQuery<T> query);
|
||||
|
||||
@@ -3,6 +3,9 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.expression.DocQueryContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,6 +21,16 @@ public interface SpiExpression extends Expression {
|
||||
*/
|
||||
void simplify();
|
||||
|
||||
/**
|
||||
* Write the expression as an elastic search expression.
|
||||
*/
|
||||
void writeDocQuery(DocQueryContext context) throws IOException;
|
||||
|
||||
/**
|
||||
* Return the nested path for this expression.
|
||||
*/
|
||||
String nestedPath(BeanDescriptor<?> desc);
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.Junction;
|
||||
import io.ebeaninternal.server.expression.DocQueryContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -31,6 +32,11 @@ public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Write the top level where expressions taking into account possible extra idEquals expression.
|
||||
*/
|
||||
void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException;
|
||||
|
||||
/**
|
||||
* Apply firstRow maxRows limits on the filterMany query.
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.Junction;
|
||||
import io.ebeaninternal.server.expression.DocQueryContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* SPI methods for Junction.
|
||||
*/
|
||||
public interface SpiJunction<T> extends Junction<T> {
|
||||
|
||||
/**
|
||||
* Write the Junction taking into account it is implied.
|
||||
*/
|
||||
void writeDocQueryJunction(DocQueryContext context) throws IOException;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ebean.Query;
|
||||
import io.ebean.bean.CallOrigin;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
@@ -168,6 +169,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
SOFT_DELETED(false),
|
||||
|
||||
/**
|
||||
* Query runs against draft tables.
|
||||
*/
|
||||
DRAFT(false),
|
||||
|
||||
/**
|
||||
* Query runs against current data (normal).
|
||||
*/
|
||||
@@ -285,6 +291,17 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
SpiRawSql rawSql();
|
||||
|
||||
/**
|
||||
* Return true if this query should be executed against the doc store.
|
||||
*/
|
||||
boolean isUseDocStore();
|
||||
|
||||
/**
|
||||
* For doc store query return the document index name to search against.
|
||||
* This is for partitioned indexes (like daily logstash indexes etc).
|
||||
*/
|
||||
String getDocIndexName();
|
||||
|
||||
/**
|
||||
* Return the PersistenceContextScope that this query should use.
|
||||
* <p>
|
||||
@@ -359,6 +376,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
boolean isAsOfQuery();
|
||||
|
||||
/**
|
||||
* Return true if this is a 'As Draft' query.
|
||||
*/
|
||||
boolean isAsDraft();
|
||||
|
||||
/**
|
||||
* Return true if this query includes soft deleted rows.
|
||||
*/
|
||||
@@ -487,6 +509,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
NaturalKeyBindParam naturalKeyBindParam();
|
||||
|
||||
/**
|
||||
* Prepare the query for docstore execution with nested paths.
|
||||
*/
|
||||
void prepareDocNested();
|
||||
|
||||
/**
|
||||
* Set the query to be a delete query.
|
||||
*/
|
||||
@@ -692,6 +719,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
SpiExpressionList<T> havingExpressions();
|
||||
|
||||
/**
|
||||
* Return the text expressions.
|
||||
*/
|
||||
SpiExpressionList<T> textExpression();
|
||||
|
||||
/**
|
||||
* Returns true if either firstRow or maxRows has been set.
|
||||
*/
|
||||
@@ -758,6 +790,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
boolean tuneFetchProperties(OrmQueryDetail detail);
|
||||
|
||||
/**
|
||||
* If this is a RawSql based entity set the default RawSql if not set.
|
||||
*/
|
||||
void setDefaultRawSqlIfRequired();
|
||||
|
||||
/**
|
||||
* Set to true if this query has been tuned by autoTune.
|
||||
*/
|
||||
@@ -835,6 +872,21 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
int bufferFetchSizeHint();
|
||||
|
||||
/**
|
||||
* Return true if read auditing is disabled on this query.
|
||||
*/
|
||||
boolean isDisableReadAudit();
|
||||
|
||||
/**
|
||||
* Set the readEvent for future queries (as prepared in foreground thread).
|
||||
*/
|
||||
void setFutureFetchAudit(ReadEvent event);
|
||||
|
||||
/**
|
||||
* Read the readEvent for future queries (null otherwise).
|
||||
*/
|
||||
ReadEvent futureFetchAudit();
|
||||
|
||||
/**
|
||||
* Return the base table to use if user defined on the query.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
@@ -102,6 +103,19 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
Boolean isUpdateAllLoadedProperties();
|
||||
|
||||
/**
|
||||
* Return the batchSize specifically set for this transaction or 0.
|
||||
* <p>
|
||||
* Returning 0 implies to use the system wide default batch size.
|
||||
*/
|
||||
DocStoreMode docStoreMode();
|
||||
|
||||
/**
|
||||
* Return the batch size to us for ElasticSearch Bulk API calls
|
||||
* as a result of this transaction.
|
||||
*/
|
||||
int getDocStoreBatchSize();
|
||||
|
||||
/**
|
||||
* Return the batchSize specifically set for this transaction or 0.
|
||||
* <p>
|
||||
@@ -273,6 +287,11 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
void sendChangeLog(ChangeSet changeSet);
|
||||
|
||||
/**
|
||||
* Return a document store transaction.
|
||||
*/
|
||||
DocStoreTransaction docStoreTransaction();
|
||||
|
||||
/**
|
||||
* Set the current Tenant Id.
|
||||
*/
|
||||
|
||||
@@ -2,12 +2,14 @@ package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TransactionCallback;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistDeferredRelationship;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.persist.BatchControl;
|
||||
import io.ebeaninternal.server.transaction.ProfileStream;
|
||||
import io.ebeanservice.docstore.api.DocStoreTransaction;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
@@ -110,6 +112,32 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.tenantId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocStoreTransaction docStoreTransaction() {
|
||||
return transaction.docStoreTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocStoreMode docStoreMode() {
|
||||
return transaction.docStoreMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocStoreMode(DocStoreMode mode) {
|
||||
transaction.setDocStoreMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDocStoreBatchSize() {
|
||||
return transaction.getDocStoreBatchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocStoreBatchSize(int batchSize) {
|
||||
transaction.setDocStoreBatchSize(batchSize);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isLogSql() {
|
||||
return transaction.isLogSql();
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.transaction.DeleteByIdMap;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
@@ -124,6 +125,18 @@ public final class TransactionEvent implements Serializable {
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any relevant PersistRequestBean's to DocStoreUpdates for later processing.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
|
||||
List<PersistRequestBean<?>> requests = listenerNotify();
|
||||
if (requests != null) {
|
||||
for (PersistRequestBean<?> persistRequestBean : requests) {
|
||||
persistRequestBean.addDocStoreUpdates(docStoreUpdates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CacheChangeSet that we add cache notification messages to.
|
||||
* <p>
|
||||
|
||||
+19
-2
@@ -15,6 +15,7 @@ public final class CachedBeanData implements Externalizable {
|
||||
|
||||
private long whenCreated;
|
||||
private long version;
|
||||
private String discValue;
|
||||
private Map<String, Object> data;
|
||||
/**
|
||||
* The sharable bean is effectively transient (near cache only).
|
||||
@@ -24,9 +25,10 @@ public final class CachedBeanData implements Externalizable {
|
||||
/**
|
||||
* Construct from a loaded bean.
|
||||
*/
|
||||
public CachedBeanData(Object sharableBean, Map<String, Object> data, long version) {
|
||||
public CachedBeanData(Object sharableBean, String discValue, Map<String, Object> data, long version) {
|
||||
this.whenCreated = System.currentTimeMillis();
|
||||
this.sharableBean = sharableBean;
|
||||
this.discValue = discValue;
|
||||
this.data = data;
|
||||
this.version = version;
|
||||
}
|
||||
@@ -41,6 +43,11 @@ public final class CachedBeanData implements Externalizable {
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeLong(version);
|
||||
out.writeLong(whenCreated);
|
||||
boolean hasDisc = discValue != null;
|
||||
out.writeBoolean(hasDisc);
|
||||
if (hasDisc) {
|
||||
out.writeUTF(discValue);
|
||||
}
|
||||
out.writeInt(data.size());
|
||||
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
||||
out.writeUTF(entry.getKey());
|
||||
@@ -52,6 +59,9 @@ public final class CachedBeanData implements Externalizable {
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
version = in.readLong();
|
||||
whenCreated = in.readLong();
|
||||
if (in.readBoolean()) {
|
||||
discValue = in.readUTF();
|
||||
}
|
||||
data = new LinkedHashMap<>();
|
||||
int count = in.readInt();
|
||||
for (int i = 0; i < count; i++) {
|
||||
@@ -75,7 +85,7 @@ public final class CachedBeanData implements Externalizable {
|
||||
Map<String, Object> copy = new HashMap<>();
|
||||
copy.putAll(data);
|
||||
copy.putAll(changes);
|
||||
return new CachedBeanData(null, copy, version);
|
||||
return new CachedBeanData(null, discValue, copy, version);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +102,13 @@ public final class CachedBeanData implements Externalizable {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the raw discriminator value.
|
||||
*/
|
||||
public String getDiscValue() {
|
||||
return discValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sharable (immutable read only) bean. Near cache only use.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public final class CachedBeanDataFromBean {
|
||||
|
||||
long version = desc.getVersion(bean);
|
||||
EntityBean sharableBean = createSharableBean(desc, bean, ebi);
|
||||
return new CachedBeanData(sharableBean, data, version);
|
||||
return new CachedBeanData(sharableBean, desc.discValue(), data, version);
|
||||
}
|
||||
|
||||
private static EntityBean createSharableBean(BeanDescriptor<?> desc, EntityBean bean, EntityBeanIntercept beanEbi) {
|
||||
|
||||
+3
@@ -14,6 +14,9 @@ public final class CachedBeanDataToBean {
|
||||
// any future lazy loading skips L2 bean cache
|
||||
ebi.setLoadedFromCache(true);
|
||||
BeanProperty idProperty = desc.idProperty();
|
||||
if (desc.inheritInfo() != null) {
|
||||
desc = desc.inheritInfo().readType(bean.getClass()).desc();
|
||||
}
|
||||
if (idProperty != null) {
|
||||
// load the id property
|
||||
loadProperty(bean, cacheBeanData, ebi, idProperty, context);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
/**
|
||||
* Bean Id value plus discriminator type.
|
||||
* <p>
|
||||
* Put into L2 cache such that we know the type of a bean with inheritance.
|
||||
*/
|
||||
public final class CachedBeanId implements Externalizable {
|
||||
|
||||
private String discValue;
|
||||
private Object id;
|
||||
|
||||
public CachedBeanId(String discValue, Object id) {
|
||||
this.discValue = discValue;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from serialisation.
|
||||
*/
|
||||
public CachedBeanId() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeUTF(discValue);
|
||||
out.writeObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
discValue = in.readUTF();
|
||||
id = in.readObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return discValue + ":" + id;
|
||||
}
|
||||
|
||||
public String getDiscValue() {
|
||||
return discValue;
|
||||
}
|
||||
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -178,11 +178,12 @@ final class DefaultBeanLoader {
|
||||
desc.contextPut(pc, id, bean);
|
||||
ebi.setPersistenceContext(pc);
|
||||
}
|
||||
boolean draft = desc.isDraftInstance(bean);
|
||||
if (embeddedOwnerIndex == -1) {
|
||||
if (desc.lazyLoadMany(ebi)) {
|
||||
return;
|
||||
}
|
||||
if (Mode.LAZYLOAD_BEAN == mode && desc.isBeanCaching()) {
|
||||
if (!draft && Mode.LAZYLOAD_BEAN == mode && desc.isBeanCaching()) {
|
||||
// lazy loading and the bean cache is active
|
||||
if (desc.cacheBeanLoad(bean, ebi, id, pc)) {
|
||||
return;
|
||||
@@ -191,7 +192,9 @@ final class DefaultBeanLoader {
|
||||
}
|
||||
SpiQuery<?> query = server.createQuery(desc.type());
|
||||
query.setLazyLoadProperty(ebi.lazyLoadProperty());
|
||||
if (mode == SpiQuery.Mode.LAZYLOAD_BEAN && desc.isSoftDelete()) {
|
||||
if (draft) {
|
||||
query.asDraft();
|
||||
} else if (mode == SpiQuery.Mode.LAZYLOAD_BEAN && desc.isSoftDelete()) {
|
||||
query.setIncludeSoftDeletes();
|
||||
}
|
||||
if (embeddedOwnerIndex > -1) {
|
||||
|
||||
@@ -78,12 +78,16 @@ public final class DefaultContainer implements SpiContainer {
|
||||
BootupClasses bootupClasses = bootupClasses(config);
|
||||
|
||||
boolean online = true;
|
||||
TenantMode tenantMode = config.getTenantMode();
|
||||
if (TenantMode.DB != tenantMode) {
|
||||
setDataSource(config);
|
||||
if (!tenantMode.isDynamicDataSource()) {
|
||||
// check the autoCommit and Transaction Isolation
|
||||
online = checkDataSource(config);
|
||||
if (config.isDocStoreOnly()) {
|
||||
config.setDatabasePlatform(new DatabasePlatform());
|
||||
} else {
|
||||
TenantMode tenantMode = config.getTenantMode();
|
||||
if (TenantMode.DB != tenantMode) {
|
||||
setDataSource(config);
|
||||
if (!tenantMode.isDynamicDataSource()) {
|
||||
// check the autoCommit and Transaction Isolation
|
||||
online = checkDataSource(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import io.ebean.config.*;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.ShutdownManager;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.*;
|
||||
import io.ebean.migration.auto.AutoMigrationRunner;
|
||||
import io.ebean.plugin.BeanType;
|
||||
@@ -28,9 +30,11 @@ import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.dto.DtoBeanDescriptor;
|
||||
import io.ebeaninternal.server.dto.DtoBeanManager;
|
||||
import io.ebeaninternal.server.el.ElFilter;
|
||||
import io.ebeaninternal.server.grammer.EqlParser;
|
||||
import io.ebeaninternal.server.query.*;
|
||||
import io.ebeaninternal.server.querydefn.*;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
@@ -39,6 +43,7 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeaninternal.util.ParamTypeHelper;
|
||||
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
import io.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.NonUniqueResultException;
|
||||
@@ -88,6 +93,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private final DtoBeanManager dtoBeanManager;
|
||||
private final BeanDescriptorManager descriptorManager;
|
||||
private final AutoTuneService autoTuneService;
|
||||
private final ReadAuditPrepare readAuditPrepare;
|
||||
private final ReadAuditLogger readAuditLogger;
|
||||
private final CQueryEngine cqueryEngine;
|
||||
private final List<Plugin> serverPlugins;
|
||||
private final SpiDdlGenerator ddlGenerator;
|
||||
@@ -97,6 +104,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private final DefaultBeanLoader beanLoader;
|
||||
private final EncryptKeyManager encryptKeyManager;
|
||||
private final SpiJsonContext jsonContext;
|
||||
private final DocumentStore documentStore;
|
||||
private final MetaInfoManager metaInfoManager;
|
||||
private final CurrentTenantProvider currentTenantProvider;
|
||||
private final SpiLogManager logManager;
|
||||
@@ -138,12 +146,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.relationalQueryEngine = config.createRelationalQueryEngine();
|
||||
this.dtoQueryEngine = config.createDtoQueryEngine();
|
||||
this.autoTuneService = config.createAutoTuneService(this);
|
||||
this.readAuditPrepare = config.getReadAuditPrepare();
|
||||
this.readAuditLogger = config.getReadAuditLogger();
|
||||
this.beanLoader = new DefaultBeanLoader(this);
|
||||
this.jsonContext = config.createJsonContext(this);
|
||||
this.dataTimeZone = config.getDataTimeZone();
|
||||
this.clockService = config.getClockService();
|
||||
|
||||
this.transactionManager = config.createTransactionManager(this);
|
||||
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
|
||||
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
|
||||
this.documentStore = docStoreComponents.documentStore();
|
||||
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this, this.config.getMetricNaming());
|
||||
this.serverPlugins = config.getPlugins();
|
||||
@@ -177,7 +189,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Execute all the plugins with an online flag indicating the DB is up or not.
|
||||
*/
|
||||
public void executePlugins(boolean online) {
|
||||
ddlGenerator.execute(online);
|
||||
if (!config.isDocStoreOnly()) {
|
||||
ddlGenerator.execute(online);
|
||||
}
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
plugin.online(online);
|
||||
}
|
||||
@@ -269,6 +283,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return transactionManager.readOnlyDataSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadAuditPrepare readAuditPrepare() {
|
||||
return readAuditPrepare;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadAuditLogger readAuditLogger() {
|
||||
return readAuditLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run any initialisation required before registering with the ClusterManager.
|
||||
*/
|
||||
@@ -594,7 +618,25 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return (T) existing;
|
||||
}
|
||||
}
|
||||
return (T) desc.contextRef(pc, null, false, id);
|
||||
InheritInfo inheritInfo = desc.inheritInfo();
|
||||
if (inheritInfo == null || inheritInfo.isConcrete()) {
|
||||
return (T) desc.contextRef(pc, null, false, id);
|
||||
}
|
||||
return referenceFindOne(type, id, desc);
|
||||
}
|
||||
|
||||
private <T> T referenceFindOne(Class<T> type, Object id, BeanDescriptor<?> desc) {
|
||||
BeanProperty idProp = desc.idProperty();
|
||||
if (idProp == null) {
|
||||
throw new PersistenceException("No ID properties for this type? " + desc);
|
||||
}
|
||||
// we actually need to do a query because we don't know the type without the discriminator
|
||||
// value, just select the id property and discriminator column (auto added)
|
||||
T bean = find(type).select(idProp.name()).setId(id).findOne();
|
||||
if (bean == null) {
|
||||
throw new EntityNotFoundException("Could not find reference bean " + id + " for " + desc);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -806,6 +848,29 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
|
||||
BeanDescriptor<T> desc = desc(beanType);
|
||||
String named = desc.namedQuery(namedQuery);
|
||||
if (named != null) {
|
||||
return createQuery(beanType, named);
|
||||
}
|
||||
SpiRawSql rawSql = desc.namedRawSql(namedQuery);
|
||||
if (rawSql != null) {
|
||||
DefaultOrmQuery<T> query = createQuery(beanType);
|
||||
query.setRawSql(rawSql);
|
||||
return query;
|
||||
}
|
||||
throw new PersistenceException("No named query called " + namedQuery + " for bean:" + beanType.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType, String eql) {
|
||||
DefaultOrmQuery<T> query = createQuery(beanType);
|
||||
EqlParser.parse(eql, query);
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType) {
|
||||
return new DefaultOrmQuery<>(desc(beanType), this, expressionFactory);
|
||||
@@ -822,6 +887,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new DefaultDtoQuery<>(this, descriptor, sql.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DtoQuery<T> createNamedDtoQuery(Class<T> dtoType, String namedQuery) {
|
||||
DtoBeanDescriptor<T> descriptor = dtoBeanManager.descriptor(dtoType);
|
||||
String sql = descriptor.namedRawSql(namedQuery);
|
||||
if (sql == null) {
|
||||
throw new PersistenceException("No named query called " + namedQuery + " for bean:" + dtoType.getName());
|
||||
}
|
||||
return new DefaultDtoQuery<>(this, descriptor, sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DtoQuery<T> findDto(Class<T> dtoType, SpiQuery<?> ormQuery) {
|
||||
DtoBeanDescriptor<T> descriptor = dtoBeanManager.descriptor(dtoType);
|
||||
@@ -885,6 +960,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
transaction = currentServerTransaction();
|
||||
}
|
||||
if (!query.isRawSql()) {
|
||||
query.setDefaultRawSqlIfRequired();
|
||||
if (query.isAutoTunable() && !autoTuneService.tuneQuery(query)) {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
// to define the 'default' select clause
|
||||
@@ -967,6 +1043,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(query);
|
||||
request.prepareQuery();
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().find(request);
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (T) request.findId();
|
||||
@@ -1209,12 +1288,58 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> FutureIds<T> findFutureIds(SpiQuery<T> query) {
|
||||
SpiQuery<T> copy = query.copy();
|
||||
boolean createdTransaction = false;
|
||||
SpiTransaction transaction = query.transaction();
|
||||
if (transaction == null) {
|
||||
transaction = currentServerTransaction();
|
||||
if (transaction == null) {
|
||||
transaction = (SpiTransaction) createTransaction();
|
||||
createdTransaction = true;
|
||||
}
|
||||
copy.usingTransaction(transaction);
|
||||
}
|
||||
QueryFutureIds<T> queryFuture = new QueryFutureIds<>(new CallableQueryIds<>(this, copy, createdTransaction));
|
||||
backgroundExecutor.execute(queryFuture.futureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> FutureList<T> findFutureList(SpiQuery<T> query) {
|
||||
SpiQuery<T> spiQuery = query.copy();
|
||||
// FutureList query always run in it's own persistence content
|
||||
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
|
||||
if (!spiQuery.isDisableReadAudit()) {
|
||||
BeanDescriptor<T> desc = descriptorManager.descriptor(spiQuery.getBeanType());
|
||||
desc.readAuditFutureList(spiQuery);
|
||||
}
|
||||
// Create a new transaction solely to execute the findList() at some future time
|
||||
boolean createdTransaction = false;
|
||||
SpiTransaction transaction = query.transaction();
|
||||
if (transaction == null) {
|
||||
transaction = currentServerTransaction();
|
||||
if (transaction == null) {
|
||||
transaction = (SpiTransaction) createTransaction();
|
||||
createdTransaction = true;
|
||||
}
|
||||
spiQuery.usingTransaction(transaction);
|
||||
}
|
||||
QueryFutureList<T> queryFuture = new QueryFutureList<>(new CallableQueryList<>(this, spiQuery, createdTransaction));
|
||||
backgroundExecutor.execute(queryFuture.futureTask());
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(SpiQuery<T> query) {
|
||||
int maxRows = query.getMaxRows();
|
||||
if (maxRows == 0) {
|
||||
throw new PersistenceException("maxRows must be specified for findPagedList() query");
|
||||
}
|
||||
if (query.isUseDocStore()) {
|
||||
return docStore().findPagedList(createQueryRequest(Type.LIST, query));
|
||||
}
|
||||
return new LimitOffsetPagedList<>(this, query);
|
||||
}
|
||||
|
||||
@@ -1242,6 +1367,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public <T> void findEach(SpiQuery<T> query, Consumer<T> consumer) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query);
|
||||
if (request.isUseDocStore()) {
|
||||
docStore().findEach(request, consumer);
|
||||
return;
|
||||
}
|
||||
request.initTransIfRequired();
|
||||
request.findEach(consumer);
|
||||
// no try finally - findEach guarantee's cleanup of the transaction if required
|
||||
@@ -1250,6 +1379,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public <T> void findEach(SpiQuery<T> query, int batch, Consumer<List<T>> consumer) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query);
|
||||
// if (request.isUseDocStore()) {
|
||||
// docStore().findEach(request, consumer);
|
||||
// return;
|
||||
// }
|
||||
request.initTransIfRequired();
|
||||
request.findEach(batch, consumer);
|
||||
// no try finally - findEach guarantee's cleanup of the transaction if required
|
||||
@@ -1258,6 +1391,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public <T> void findEachWhile(SpiQuery<T> query, Predicate<T> consumer) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query);
|
||||
if (request.isUseDocStore()) {
|
||||
docStore().findEachWhile(request, consumer);
|
||||
return;
|
||||
}
|
||||
request.initTransIfRequired();
|
||||
request.findEachWhile(consumer);
|
||||
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
|
||||
@@ -1292,6 +1429,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (result != null) {
|
||||
return (List<T>) result;
|
||||
}
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().findList(request);
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findList();
|
||||
@@ -1551,6 +1691,54 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> publish(Query<T> query, @Nullable Transaction transaction) {
|
||||
return executeInTrans((txn) -> persister.publish(query, txn), transaction);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T publish(Class<T> beanType, Object id) {
|
||||
return publish(beanType, id, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> publish(Query<T> query) {
|
||||
return publish(query, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T publish(Class<T> beanType, Object id, @Nullable Transaction transaction) {
|
||||
Query<T> query = find(beanType).setId(id);
|
||||
List<T> liveBeans = publish(query, transaction);
|
||||
return (liveBeans.size() == 1) ? liveBeans.get(0) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> draftRestore(Query<T> query, @Nullable Transaction transaction) {
|
||||
return executeInTrans((txn) -> persister.draftRestore(query, txn), transaction);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T draftRestore(Class<T> beanType, Object id, @Nullable Transaction transaction) {
|
||||
Query<T> query = find(beanType).setId(id);
|
||||
List<T> beans = draftRestore(query, transaction);
|
||||
return (beans.size() == 1) ? beans.get(0) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T draftRestore(Class<T> beanType, Object id) {
|
||||
return draftRestore(beanType, id, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> draftRestore(Query<T> query) {
|
||||
return draftRestore(query, null);
|
||||
}
|
||||
|
||||
private EntityBean checkEntityBean(Object bean) {
|
||||
return (EntityBean) Objects.requireNonNull(bean);
|
||||
}
|
||||
@@ -1859,6 +2047,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return descriptorManager.beanTypes(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanType<?> beanTypeForQueueId(String queueId) {
|
||||
return descriptorByQueueId(queueId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDescriptor<?> descriptorByQueueId(String queueId) {
|
||||
return descriptorManager.descriptorByQueueId(queueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SPI bean types for the given bean class.
|
||||
*/
|
||||
@@ -1965,6 +2163,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return callStackFactory.createCallOrigin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentStore docStore() {
|
||||
return documentStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonContext json() {
|
||||
// immutable thread safe so return shared instance
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.api.CoreLog;
|
||||
import io.ebeaninternal.server.dto.DtoNamedQueries;
|
||||
import io.ebeaninternal.xmapping.api.XmapDto;
|
||||
import io.ebeaninternal.xmapping.api.XmapEbean;
|
||||
import io.ebeaninternal.xmapping.api.XmapRawSql;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.System.Logger.Level.ERROR;
|
||||
|
||||
/**
|
||||
* Reads the Xml deployment information.
|
||||
*/
|
||||
final class InternalConfigXmlMap {
|
||||
|
||||
private final List<XmapEbean> xmlEbeanList;
|
||||
private final ClassLoader classLoader;
|
||||
private final Map<Class<?>, DtoNamedQueries> dtoNamedQueries = new HashMap<>();
|
||||
|
||||
InternalConfigXmlMap(List<XmapEbean> xmlEbeanList, ClassLoader classLoader) {
|
||||
this.xmlEbeanList = xmlEbeanList;
|
||||
this.classLoader = classLoader;
|
||||
initDtoMapping();
|
||||
}
|
||||
|
||||
void initDtoMapping() {
|
||||
if (xmlEbeanList != null) {
|
||||
for (XmapEbean mapping : xmlEbeanList) {
|
||||
List<XmapDto> dtoList = mapping.getDto();
|
||||
for (XmapDto dto : dtoList) {
|
||||
readDtoMapping(dto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the XML deployment information for entity beans.
|
||||
*/
|
||||
List<XmapEbean> xmlDeployment() {
|
||||
return xmlEbeanList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named queries for Dto beans.
|
||||
*/
|
||||
Map<Class<?>, DtoNamedQueries> readDtoMapping() {
|
||||
return dtoNamedQueries;
|
||||
}
|
||||
|
||||
private void readDtoMapping(XmapDto dto) {
|
||||
Class<?> dtoClass;
|
||||
try {
|
||||
dtoClass = Class.forName(dto.getClazz(), false, classLoader);
|
||||
} catch (Exception e) {
|
||||
CoreLog.internal.log(ERROR, "Could not load dto bean class " + dto.getClazz() + " for ebean xml entry");
|
||||
return;
|
||||
}
|
||||
DtoNamedQueries namedQueries = dtoNamedQueries.computeIfAbsent(dtoClass, aClass -> new DtoNamedQueries());
|
||||
for (XmapRawSql sql : dto.getRawSql()) {
|
||||
namedQueries.addRawSql(sql.getName(), sql.getQuery());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import io.ebean.config.dbplatform.DbHistorySupport;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.*;
|
||||
@@ -29,6 +31,7 @@ import io.ebeaninternal.server.core.timezone.*;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import io.ebeaninternal.server.dto.DtoBeanManager;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
@@ -41,10 +44,18 @@ import io.ebeaninternal.server.persist.DefaultPersister;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
import io.ebeaninternal.server.persist.platform.PostgresMultiValueBind;
|
||||
import io.ebeaninternal.server.query.*;
|
||||
import io.ebeaninternal.server.readaudit.DefaultReadAuditLogger;
|
||||
import io.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
|
||||
import io.ebeaninternal.server.json.DJsonContext;
|
||||
import io.ebeaninternal.server.transaction.*;
|
||||
import io.ebeaninternal.server.type.DefaultTypeManager;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
import io.ebeaninternal.xmapping.api.XmapEbean;
|
||||
import io.ebeaninternal.xmapping.api.XmapService;
|
||||
import io.ebeanservice.docstore.api.DocStoreFactory;
|
||||
import io.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import io.ebeanservice.docstore.none.NoneDocStoreFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -63,6 +74,7 @@ public final class InternalConfiguration {
|
||||
private final DatabaseConfig config;
|
||||
private final BootupClasses bootupClasses;
|
||||
private final DatabasePlatform databasePlatform;
|
||||
private final DeployInherit deployInherit;
|
||||
private final TypeManager typeManager;
|
||||
private final DtoBeanManager dtoBeanManager;
|
||||
private final ClockService clockService;
|
||||
@@ -79,6 +91,7 @@ public final class InternalConfiguration {
|
||||
private final ExpressionFactory expressionFactory;
|
||||
private final SpiBackgroundExecutor backgroundExecutor;
|
||||
private final JsonFactory jsonFactory;
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
private final List<Plugin> plugins = new ArrayList<>();
|
||||
private final MultiValueBind multiValueBind;
|
||||
private final SpiLogManager logManager;
|
||||
@@ -95,6 +108,7 @@ public final class InternalConfiguration {
|
||||
this.clockService = new ClockService(config.getClock());
|
||||
this.tableModState = new TableModState();
|
||||
this.logManager = initLogManager();
|
||||
this.docStoreFactory = initDocStoreFactory(service(DocStoreFactory.class));
|
||||
this.jsonFactory = config.getJsonFactory();
|
||||
this.clusterManager = clusterManager;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
@@ -103,24 +117,32 @@ public final class InternalConfiguration {
|
||||
this.expressionFactory = initExpressionFactory(config);
|
||||
this.typeManager = new DefaultTypeManager(config, bootupClasses);
|
||||
this.multiValueBind = createMultiValueBind(databasePlatform.platform());
|
||||
this.deployInherit = new DeployInherit(bootupClasses);
|
||||
this.deployCreateProperties = new DeployCreateProperties(typeManager);
|
||||
this.deployUtil = new DeployUtil(typeManager, config);
|
||||
this.serverCachePlugin = initServerCachePlugin();
|
||||
this.cacheManager = initCacheManager();
|
||||
|
||||
this.dtoBeanManager = new DtoBeanManager(typeManager);
|
||||
final InternalConfigXmlMap xmlMap = initExternalMapping();
|
||||
this.dtoBeanManager = new DtoBeanManager(typeManager, xmlMap.readDtoMapping());
|
||||
this.beanDescriptorManager = new BeanDescriptorManager(this);
|
||||
Map<String, String> asOfTableMapping = beanDescriptorManager.deploy();
|
||||
Map<String, String> asOfTableMapping = beanDescriptorManager.deploy(xmlMap.xmlDeployment());
|
||||
Map<String, String> draftTableMap = beanDescriptorManager.draftTableMap();
|
||||
beanDescriptorManager.scheduleBackgroundTrim();
|
||||
this.dataTimeZone = initDataTimeZone();
|
||||
this.binder = getBinder(typeManager, databasePlatform, dataTimeZone);
|
||||
this.cQueryEngine = new CQueryEngine(config, databasePlatform, binder, asOfTableMapping);
|
||||
this.cQueryEngine = new CQueryEngine(config, databasePlatform, binder, asOfTableMapping, draftTableMap);
|
||||
}
|
||||
|
||||
public boolean isJacksonCorePresent() {
|
||||
return jacksonCorePresent;
|
||||
}
|
||||
|
||||
private InternalConfigXmlMap initExternalMapping() {
|
||||
final List<XmapEbean> xmEbeans = readExternalMapping();
|
||||
return new InternalConfigXmlMap(xmEbeans, config.getClassLoadConfig().getClassLoader());
|
||||
}
|
||||
|
||||
private <S> S service(Class<S> cls) {
|
||||
S service = config.getServiceObject(cls);
|
||||
if (service != null) {
|
||||
@@ -130,6 +152,14 @@ public final class InternalConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private List<XmapEbean> readExternalMapping() {
|
||||
final XmapService xmapService = service(XmapService.class);
|
||||
if (xmapService == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return xmapService.read(config.getClassLoadConfig().getClassLoader(), config.getMappingLocations());
|
||||
}
|
||||
|
||||
private SpiLogManager initLogManager() {
|
||||
// allow plugin - i.e. capture executed SQL for testing/asserts
|
||||
SpiLoggerFactory loggerFactory = service(SpiLoggerFactory.class);
|
||||
@@ -150,6 +180,17 @@ public final class InternalConfiguration {
|
||||
return new DefaultExpressionFactory(config.isExpressionEqualsWithNullAsNoop(), nativeIlike);
|
||||
}
|
||||
|
||||
private DocStoreFactory initDocStoreFactory(DocStoreFactory service) {
|
||||
return service == null ? new NoneDocStoreFactory() : service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the doc store factory.
|
||||
*/
|
||||
public DocStoreFactory getDocStoreFactory() {
|
||||
return docStoreFactory;
|
||||
}
|
||||
|
||||
ClockService getClockService() {
|
||||
return clockService;
|
||||
}
|
||||
@@ -206,6 +247,22 @@ public final class InternalConfiguration {
|
||||
return plugin((listener != null) ? listener : jacksonCorePresent ? new DefaultChangeLogListener() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger implementation to use.
|
||||
*/
|
||||
ReadAuditLogger getReadAuditLogger() {
|
||||
ReadAuditLogger found = bootupClasses.getReadAuditLogger();
|
||||
return plugin(found != null ? found : jacksonCorePresent ? new DefaultReadAuditLogger() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditPrepare implementation to use.
|
||||
*/
|
||||
ReadAuditPrepare getReadAuditPrepare() {
|
||||
ReadAuditPrepare found = bootupClasses.getReadAuditPrepare();
|
||||
return plugin(found != null ? found : new DefaultReadAuditPrepare());
|
||||
}
|
||||
|
||||
/**
|
||||
* For 'As Of' queries return the number of bind variables per predicate.
|
||||
*/
|
||||
@@ -292,6 +349,10 @@ public final class InternalConfiguration {
|
||||
return beanDescriptorManager;
|
||||
}
|
||||
|
||||
public DeployInherit getDeployInherit() {
|
||||
return deployInherit;
|
||||
}
|
||||
|
||||
public DeployCreateProperties getDeployCreateProperties() {
|
||||
return deployCreateProperties;
|
||||
}
|
||||
@@ -313,19 +374,29 @@ public final class InternalConfiguration {
|
||||
return new GeneratedPropertyFactory(offlineMode, config, bootupClasses.getIdGenerators());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DocStoreIntegration components for the given server.
|
||||
*/
|
||||
DocStoreIntegration createDocStoreIntegration(SpiServer server) {
|
||||
return plugin(docStoreFactory.create(server));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
TransactionManager createTransactionManager(SpiServer server) {
|
||||
TransactionManager createTransactionManager(SpiServer server, DocStoreUpdateProcessor indexUpdateProcessor) {
|
||||
|
||||
TransactionScopeManager scopeManager = createTransactionScopeManager();
|
||||
boolean notifyL2CacheInForeground = cacheManager.isLocalL2Caching() || config.isNotifyL2CacheInForeground();
|
||||
|
||||
TransactionManagerOptions options =
|
||||
new TransactionManagerOptions(server, notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
|
||||
beanDescriptorManager, dataSource(), profileHandler(), logManager,
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
|
||||
tableModState, cacheNotify, clockService);
|
||||
|
||||
if (config.isDocStoreOnly()) {
|
||||
return new DocStoreTransactionManager(options);
|
||||
}
|
||||
return new TransactionManager(options);
|
||||
}
|
||||
|
||||
@@ -519,14 +590,16 @@ public final class InternalConfiguration {
|
||||
*/
|
||||
QueryPlanLogger queryPlanLogger(Platform platform) {
|
||||
switch (platform.base()) {
|
||||
case POSTGRES:
|
||||
return new QueryPlanLoggerPostgres();
|
||||
case SQLSERVER:
|
||||
return new QueryPlanLoggerSqlServer();
|
||||
case ORACLE:
|
||||
return new QueryPlanLoggerOracle();
|
||||
case POSTGRES:
|
||||
return new QueryPlanLoggerExplain("explain (analyze, buffers) ");
|
||||
case YUGABYTE:
|
||||
return new QueryPlanLoggerExplain("explain (analyze, buffers, dist) ");
|
||||
default:
|
||||
return new QueryPlanLoggerExplain();
|
||||
return new QueryPlanLoggerExplain("explain ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return server.databasePlatform().escapeLikeString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeSecondaryQueries(boolean forEach) {
|
||||
// disable lazy loading leaves loadContext null
|
||||
if (loadContext != null) {
|
||||
@@ -156,6 +157,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseDocStore() {
|
||||
return query.isUseDocStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run BeanQueryAdapter preQuery() if needed.
|
||||
*/
|
||||
@@ -228,6 +234,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
persistenceContext.beginIterate();
|
||||
}
|
||||
loadContext = new DLoadContext(this, secondaryQueries);
|
||||
loadContext.useReferences(Type.ITERATE == query.type());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,6 +257,23 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JsonReadOptions taking into account lazy loading and persistence context.
|
||||
*/
|
||||
@Override
|
||||
public JsonReadOptions createJsonReadOptions() {
|
||||
persistenceContext = persistenceContext(query, transaction);
|
||||
if (query.persistenceContext() == null) {
|
||||
query.setPersistenceContext(persistenceContext);
|
||||
}
|
||||
JsonReadOptions jsonRead = new JsonReadOptions();
|
||||
jsonRead.setPersistenceContext(persistenceContext);
|
||||
if (!query.isDisableLazyLoading()) {
|
||||
loadContext = new DLoadContext(this, secondaryQueries);
|
||||
jsonRead.setLoadContext(loadContext);
|
||||
}
|
||||
return jsonRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the TransactionContext either explicitly set on the query or
|
||||
@@ -624,6 +648,17 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -639,6 +674,24 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return cached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query type contains bean data (not just ids etc) and hence we want to include
|
||||
* it in read auditing. Return false for row count and find ids queries.
|
||||
*/
|
||||
private boolean readAuditQueryType() {
|
||||
Type type = query.type();
|
||||
switch (type) {
|
||||
case BEAN:
|
||||
case ITERATE:
|
||||
case LIST:
|
||||
case SET:
|
||||
case MAP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void putToQueryCache(Object result) {
|
||||
beanDescriptor.queryCachePut(cacheKey, new QueryCacheEntry(result, dependentTables, transaction.startNanoTime()));
|
||||
}
|
||||
@@ -665,6 +718,15 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return (batchSize > 0) ? batchSize : server.lazyLoadBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if read auditing is on for this query request.
|
||||
* <p>
|
||||
* This means that read audit is on for this bean type and that query has not explicitly disabled it.
|
||||
*/
|
||||
public boolean isAuditReads() {
|
||||
return beanDescriptor.isReadAuditing() && !query.isDisableReadAudit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table alias for this query.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,9 @@ import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.persist.*;
|
||||
import io.ebeaninternal.server.transaction.BeanPersistIdMap;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdate;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.OptimisticLockException;
|
||||
@@ -27,7 +30,7 @@ import java.util.*;
|
||||
/**
|
||||
* PersistRequest for insert update or delete of a bean.
|
||||
*/
|
||||
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T>, PreGetterCallback, SpiProfileTransactionEvent {
|
||||
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T>, DocStoreUpdate, PreGetterCallback, SpiProfileTransactionEvent {
|
||||
|
||||
private final BeanManager<T> beanManager;
|
||||
private final BeanDescriptor<T> beanDescriptor;
|
||||
@@ -41,8 +44,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private final Object parentBean;
|
||||
private final boolean dirty;
|
||||
private final boolean publish;
|
||||
private int flags;
|
||||
private boolean saveRecurse;
|
||||
private DocStoreMode docStoreMode;
|
||||
private final ConcurrencyMode concurrencyMode;
|
||||
/**
|
||||
* The unique id used for logging summary.
|
||||
@@ -132,6 +137,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
this.parentBean = parentBean;
|
||||
this.controller = beanDescriptor.persistController();
|
||||
this.type = type;
|
||||
this.docStoreMode = calcDocStoreMode(transaction, type);
|
||||
this.flags = flags;
|
||||
if (Flags.isRecurse(flags)) {
|
||||
this.persistCascade = t.isPersistCascade();
|
||||
@@ -147,6 +153,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
this.concurrencyMode = beanDescriptor.concurrencyMode(intercept);
|
||||
this.publish = Flags.isPublish(flags);
|
||||
if (isMarkDraftDirty(publish)) {
|
||||
beanDescriptor.setDraftDirty(entityBean, true);
|
||||
}
|
||||
this.dirty = intercept.isDirty();
|
||||
}
|
||||
|
||||
@@ -175,11 +185,29 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
profileBase(type.profileEventId, offset, beanDescriptor.name(), flushCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the document store event that should be used for this request.
|
||||
* <p>
|
||||
* Used to check if the Transaction has set the mode to IGNORE when doing large batch inserts that we
|
||||
* don't want to send to the doc store.
|
||||
*/
|
||||
private DocStoreMode calcDocStoreMode(SpiTransaction txn, Type type) {
|
||||
DocStoreMode txnMode = (txn == null) ? null : txn.docStoreMode();
|
||||
return beanDescriptor.docStoreMode(type, txnMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCascade() {
|
||||
return Flags.isRecurse(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the draftDirty property should be set to true for this request.
|
||||
*/
|
||||
private boolean isMarkDraftDirty(boolean publish) {
|
||||
return !publish && type != Type.DELETE && beanDescriptor.isDraftable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transaction from prior persist request.
|
||||
* Only used when hard deleting draft & associated live beans.
|
||||
@@ -194,7 +222,9 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Init the transaction and also check for batch on cascade escalation.
|
||||
*/
|
||||
public void initTransIfRequiredWithBatchCascade() {
|
||||
createImplicitTransIfRequired();
|
||||
if (createImplicitTransIfRequired()) {
|
||||
docStoreMode = calcDocStoreMode(transaction, type);
|
||||
}
|
||||
checkBatchEscalationOnCascade();
|
||||
}
|
||||
|
||||
@@ -387,16 +417,22 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Set the cache notify status.
|
||||
*/
|
||||
private void setNotifyCache() {
|
||||
this.notifyCache = beanDescriptor.isCacheNotify(type);
|
||||
this.notifyCache = beanDescriptor.isCacheNotify(type, publish);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this change should notify persist listener or doc store (and keep the request).
|
||||
*/
|
||||
private boolean isNotifyListeners() {
|
||||
return isNotifyPersistListener();
|
||||
return isNotifyPersistListener() || isDocStoreNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this request should update the document store.
|
||||
*/
|
||||
private boolean isDocStoreNotify() {
|
||||
return docStoreMode != DocStoreMode.IGNORE;
|
||||
}
|
||||
|
||||
private boolean isNotifyPersistListener() {
|
||||
return beanPersistListener != null;
|
||||
@@ -424,6 +460,46 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the persist request updating the document store.
|
||||
*/
|
||||
@Override
|
||||
public void docStoreUpdate(DocStoreUpdateContext txn) throws IOException {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
beanDescriptor.docStoreInsert(idValue, this, txn);
|
||||
break;
|
||||
case UPDATE:
|
||||
case DELETE_SOFT:
|
||||
beanDescriptor.docStoreUpdate(idValue, this, txn);
|
||||
break;
|
||||
case DELETE:
|
||||
beanDescriptor.docStoreDeleteById(idValue, txn);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this event to the queue entries in IndexUpdates.
|
||||
*/
|
||||
@Override
|
||||
public void addToQueue(DocStoreUpdates docStoreUpdates) {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
case UPDATE:
|
||||
case DELETE_SOFT:
|
||||
docStoreUpdates.queueIndex(beanDescriptor.docStoreQueueId(), idValue);
|
||||
break;
|
||||
case DELETE:
|
||||
docStoreUpdates.queueDelete(beanDescriptor.docStoreQueueId(), idValue);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
|
||||
beanPersistMap.add(beanDescriptor, type, idValue);
|
||||
}
|
||||
@@ -566,6 +642,28 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return beanDescriptor.createRef(beanId(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean type is a Draftable.
|
||||
*/
|
||||
public boolean isDraftable() {
|
||||
return beanDescriptor.isDraftable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this request is a hard delete of a draftable bean.
|
||||
* If this is true Ebean is expected to auto-publish and delete the associated live bean.
|
||||
*/
|
||||
public boolean isHardDeleteDraft() {
|
||||
if (type == Type.DELETE && beanDescriptor.isDraftable() && !beanDescriptor.isDraftableElement()) {
|
||||
// deleting a top level draftable bean
|
||||
if (beanDescriptor.isLiveInstance(entityBean)) {
|
||||
throw new PersistenceException("Explicit Delete is not allowed on a 'live' bean - only draft beans");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this was a hard/permanent delete request (and should cascade as such).
|
||||
*/
|
||||
@@ -573,6 +671,16 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return (type == Type.DELETE && beanDescriptor.isSoftDelete());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for @Draftable entity beans with @Draft property that the bean is a 'draft'.
|
||||
* Save or Update is not allowed to execute using 'live' beans - must use publish().
|
||||
*/
|
||||
public void checkDraft() {
|
||||
if (beanDescriptor.isDraftable() && beanDescriptor.isLiveInstance(entityBean)) {
|
||||
throw new PersistenceException("Save or update is not allowed on a 'live' bean - only draft beans");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent bean for cascading save with unidirectional relationship.
|
||||
*/
|
||||
@@ -757,7 +865,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
setNotifyCache();
|
||||
boolean isChangeLog = beanDescriptor.isChangeLog();
|
||||
if (type == Type.UPDATE && (isChangeLog || notifyCache)) {
|
||||
if (type == Type.UPDATE && (isChangeLog || notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
|
||||
// get the dirty properties for update notification to the doc store
|
||||
dirtyProperties = intercept.dirtyProperties();
|
||||
}
|
||||
@@ -820,19 +928,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
private void logSummaryMessage() {
|
||||
String draft = (beanDescriptor.isDraftable() && !publish) ? " draft[true]" : "";
|
||||
String name = beanDescriptor.name();
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
transaction.logSummary("Inserted [{0}] [{1}]", name, (idValue == null ? "" : idValue));
|
||||
transaction.logSummary("Inserted [{0}] [{1}]{2}", name, (idValue == null ? "" : idValue), draft);
|
||||
break;
|
||||
case UPDATE:
|
||||
transaction.logSummary("Updated [{0}] [{1}]", name, idValue);
|
||||
transaction.logSummary("Updated [{0}] [{1}]{2}", name, idValue , draft);
|
||||
break;
|
||||
case DELETE:
|
||||
transaction.logSummary("Deleted [{0}] [{1}]", name, idValue);
|
||||
transaction.logSummary("Deleted [{0}] [{1}]{2}", name, idValue , draft);
|
||||
break;
|
||||
case DELETE_SOFT:
|
||||
transaction.logSummary("SoftDelete [{0}] [{1}]", name, idValue);
|
||||
transaction.logSummary("SoftDelete [{0}] [{1}]{2}", name, idValue , draft);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -870,6 +979,9 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
private void postInsert() {
|
||||
// mark all properties as loaded after an insert to support immediate update
|
||||
beanDescriptor.setAllLoaded(entityBean);
|
||||
if (!publish) {
|
||||
beanDescriptor.setDraft(entityBean);
|
||||
}
|
||||
if (transaction.isAutoPersistUpdates() && idValue != null) {
|
||||
// with getGeneratedKeys off we will not have a idValue
|
||||
beanDescriptor.contextPut(transaction.persistenceContext(), idValue, entityBean);
|
||||
@@ -966,6 +1078,32 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For requests that update document store add this event to either the list
|
||||
* of queue events or list of update events.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
|
||||
if (type == Type.UPDATE) {
|
||||
beanDescriptor.docStoreUpdateEmbedded(this, docStoreUpdates);
|
||||
}
|
||||
switch (docStoreMode) {
|
||||
case UPDATE: {
|
||||
docStoreUpdates.addPersist(this);
|
||||
return;
|
||||
}
|
||||
case QUEUE: {
|
||||
if (type == Type.DELETE) {
|
||||
docStoreUpdates.queueDelete(beanDescriptor.docStoreQueueId(), idValue);
|
||||
} else {
|
||||
docStoreUpdates.queueIndex(beanDescriptor.docStoreQueueId(), idValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if all loaded properties should be used for an update.
|
||||
* <p>
|
||||
@@ -991,6 +1129,13 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this request is a 'publish' action.
|
||||
*/
|
||||
public boolean isPublish() {
|
||||
return publish;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key for an update persist request.
|
||||
*/
|
||||
@@ -1007,14 +1152,17 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
key.append('v');
|
||||
}
|
||||
}
|
||||
if (publish) {
|
||||
key.append('p');
|
||||
}
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table to update.
|
||||
* Return the table to update depending if the request is a 'publish' one or normal.
|
||||
*/
|
||||
public String updateTable() {
|
||||
return beanDescriptor.baseTable();
|
||||
return publish ? beanDescriptor.baseTable() : beanDescriptor.draftTable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1083,6 +1231,30 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist to the document store now (via buffer, not post commit).
|
||||
*/
|
||||
public void docStorePersist() {
|
||||
idValue = beanDescriptor.getId(entityBean);
|
||||
if (type == Type.UPDATE) {
|
||||
dirtyProperties = intercept.dirtyProperties();
|
||||
}
|
||||
// processing now so set IGNORE (unlike DB + DocStore processing with post-commit)
|
||||
docStoreMode = DocStoreMode.IGNORE;
|
||||
try {
|
||||
docStoreUpdate(transaction.docStoreTransaction().obtain());
|
||||
postExecute();
|
||||
if (type == Type.UPDATE
|
||||
&& beanDescriptor.isDocStoreEmbeddedInvalidation()
|
||||
&& transaction.isPersistCascade()) {
|
||||
// queue embedded/nested updates for later processing
|
||||
beanDescriptor.docStoreUpdateEmbedded(this, transaction.docStoreTransaction().queue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Error persisting doc store bean", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a common 'now' value across both when created and when updated etc.
|
||||
*/
|
||||
@@ -1202,7 +1374,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Return the SQL used to fetch the last inserted id value.
|
||||
*/
|
||||
public String selectLastInsertedId() {
|
||||
return beanDescriptor.selectLastInsertedId();
|
||||
return beanDescriptor.selectLastInsertedId(publish);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,6 +83,16 @@ public interface Persister {
|
||||
*/
|
||||
int executeCallable(CallableSql callable, Transaction t);
|
||||
|
||||
/**
|
||||
* Publish the draft beans matching the given query.
|
||||
*/
|
||||
<T> List<T> publish(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Restore the draft beans back to the matching live beans.
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Visit the metrics.
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebean.Version;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeanservice.docstore.api.DocQueryRequest;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -16,7 +17,7 @@ import java.util.function.Predicate;
|
||||
/**
|
||||
* Defines the ORM query request api.
|
||||
*/
|
||||
public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T> {
|
||||
public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequest<T> {
|
||||
|
||||
/**
|
||||
* Return the query.
|
||||
@@ -177,6 +178,11 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T> {
|
||||
*/
|
||||
void markNotQueryOnly();
|
||||
|
||||
/**
|
||||
* Return true if this query is expected to use the doc store.
|
||||
*/
|
||||
boolean isUseDocStore();
|
||||
|
||||
/**
|
||||
* Return true if delete by statement is allowed for this type given cascade rules etc.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,8 @@ import io.ebean.event.*;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.api.CoreLog;
|
||||
|
||||
@@ -66,10 +68,14 @@ public class BootupClasses implements Predicate<Class<?>> {
|
||||
private Class<? extends ChangeLogPrepare> changeLogPrepareClass;
|
||||
private Class<? extends ChangeLogListener> changeLogListenerClass;
|
||||
private Class<? extends ChangeLogRegister> changeLogRegisterClass;
|
||||
private Class<? extends ReadAuditPrepare> readAuditPrepareClass;
|
||||
private Class<? extends ReadAuditLogger> readAuditLoggerClass;
|
||||
|
||||
private ChangeLogPrepare changeLogPrepare;
|
||||
private ChangeLogListener changeLogListener;
|
||||
private ChangeLogRegister changeLogRegister;
|
||||
private ReadAuditPrepare readAuditPrepare;
|
||||
private ReadAuditLogger readAuditLogger;
|
||||
|
||||
public BootupClasses() {
|
||||
}
|
||||
@@ -167,11 +173,19 @@ public class BootupClasses implements Predicate<Class<?>> {
|
||||
}
|
||||
|
||||
public void addChangeLogInstances(DatabaseConfig config) {
|
||||
readAuditPrepare = config.getReadAuditPrepare();
|
||||
readAuditLogger = config.getReadAuditLogger();
|
||||
changeLogPrepare = config.getChangeLogPrepare();
|
||||
changeLogListener = config.getChangeLogListener();
|
||||
changeLogRegister = config.getChangeLogRegister();
|
||||
// if not already set create the implementations found
|
||||
// via classpath scanning
|
||||
if (readAuditPrepare == null && readAuditPrepareClass != null) {
|
||||
readAuditPrepare = create(readAuditPrepareClass, false);
|
||||
}
|
||||
if (readAuditLogger == null && readAuditLoggerClass != null) {
|
||||
readAuditLogger = create(readAuditLoggerClass, false);
|
||||
}
|
||||
if (changeLogPrepare == null && changeLogPrepareClass != null) {
|
||||
changeLogPrepare = create(changeLogPrepareClass, false);
|
||||
}
|
||||
@@ -238,6 +252,14 @@ public class BootupClasses implements Predicate<Class<?>> {
|
||||
return changeLogRegister;
|
||||
}
|
||||
|
||||
public ReadAuditPrepare getReadAuditPrepare() {
|
||||
return readAuditPrepare;
|
||||
}
|
||||
|
||||
public ReadAuditLogger getReadAuditLogger() {
|
||||
return readAuditLogger;
|
||||
}
|
||||
|
||||
public List<IdGenerator> getIdGenerators() {
|
||||
return createAdd(idGeneratorInstances, idGeneratorCandidates);
|
||||
}
|
||||
@@ -402,6 +424,15 @@ public class BootupClasses implements Predicate<Class<?>> {
|
||||
interesting = true;
|
||||
}
|
||||
|
||||
if (ReadAuditPrepare.class.isAssignableFrom(cls)) {
|
||||
readAuditPrepareClass = (Class<? extends ReadAuditPrepare>) cls;
|
||||
interesting = true;
|
||||
}
|
||||
if (ReadAuditLogger.class.isAssignableFrom(cls)) {
|
||||
readAuditLoggerClass = (Class<? extends ReadAuditLogger>) cls;
|
||||
interesting = true;
|
||||
}
|
||||
|
||||
return interesting;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.query.SqlTreeJoin;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Helper for BeanPropertyAssocOne imported reference but with inheritance.
|
||||
*/
|
||||
final class AssocOneHelpRefInherit extends AssocOneHelp {
|
||||
|
||||
private final InheritInfo inherit;
|
||||
|
||||
AssocOneHelpRefInherit(BeanPropertyAssocOne<?> property) {
|
||||
super(property);
|
||||
this.inherit = property.targetInheritInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
void loadIgnore(DbReadContext ctx) {
|
||||
property.targetIdBinder.loadIgnore(ctx);
|
||||
ctx.dataReader().incrementPos(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and set a Reference bean.
|
||||
*/
|
||||
@Override
|
||||
Object read(DbReadContext ctx) throws SQLException {
|
||||
// read discriminator to determine the type
|
||||
InheritInfo rowInheritInfo = inherit.readType(ctx);
|
||||
BeanDescriptor<?> desc;
|
||||
if (rowInheritInfo != null) {
|
||||
desc = rowInheritInfo.desc();
|
||||
} else if (!inherit.hasChildren()) {
|
||||
desc = inherit.desc();
|
||||
} else {
|
||||
// ignore the id property
|
||||
property.targetIdBinder.loadIgnore(ctx);
|
||||
return null;
|
||||
}
|
||||
Object id = property.targetIdBinder.read(ctx);
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
// check transaction context to see if it already exists
|
||||
PersistenceContext pc = ctx.persistenceContext();
|
||||
Object existing = desc.contextGet(pc, id);
|
||||
if (existing != null) {
|
||||
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) {
|
||||
ctx.registerBeanInherit(property, ((EntityBean) ref)._ebean_getIntercept());
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
// add join to support the discriminator column
|
||||
String relativePrefix = ctx.relativePrefix(property.name);
|
||||
ctx.addExtraJoin(new Extra(relativePrefix, joinType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra join to support the discriminator column.
|
||||
*/
|
||||
final class Extra implements SqlTreeJoin {
|
||||
final String relativePrefix;
|
||||
final SqlJoinType joinType;
|
||||
Extra(String relativePrefix, SqlJoinType joinType) {
|
||||
this.relativePrefix = relativePrefix;
|
||||
this.joinType = joinType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJoin(DbSqlContext ctx) {
|
||||
// add join to support the discriminator column *IF* join is not already present
|
||||
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append columns for foreign key columns.
|
||||
*/
|
||||
@Override
|
||||
void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
if (!subQuery) {
|
||||
// add discriminator column
|
||||
String relativePrefix = ctx.relativePrefix(property.name());
|
||||
String tableAlias = ctx.tableAlias(relativePrefix);
|
||||
ctx.appendColumn(tableAlias, property.targetInheritInfo.getDiscriminatorColumn());
|
||||
}
|
||||
property.importedId.sqlAppend(ctx);
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,13 @@ import io.ebean.event.*;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeLogFilter;
|
||||
import io.ebean.event.changelog.ChangeType;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.plugin.BeanDocType;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
import io.ebean.plugin.Property;
|
||||
@@ -41,8 +45,15 @@ import io.ebeaninternal.server.query.*;
|
||||
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.util.SortByClause;
|
||||
import io.ebeaninternal.util.SortByClauseParser;
|
||||
import io.ebeanservice.docstore.api.DocStoreBeanAdapter;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import io.ebeanservice.docstore.api.mapping.DocumentMapping;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
@@ -78,6 +89,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
private final ConcurrentHashMap<String, STreeProperty> dynamicProperty = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Map<String, String>> pathMaps = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, SpiRawSql> namedRawSql;
|
||||
private final Map<String, String> namedQuery;
|
||||
private final boolean multiValueSupported;
|
||||
private boolean batchEscalateOnCascadeInsert;
|
||||
private boolean batchEscalateOnCascadeDelete;
|
||||
@@ -95,6 +108,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
* getGeneratedKeys is not supported.
|
||||
*/
|
||||
private final String selectLastInsertedId;
|
||||
private final String selectLastInsertedIdDraft;
|
||||
private final boolean autoTunable;
|
||||
private final ConcurrencyMode concurrencyMode;
|
||||
private final IndexDefinition[] indexDefinitions;
|
||||
@@ -106,12 +120,18 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
private final TableJoin primaryKeyJoin;
|
||||
private final BeanProperty softDeleteProperty;
|
||||
private final boolean softDelete;
|
||||
private final String draftTable;
|
||||
private final PartitionMeta partitionMeta;
|
||||
private final TablespaceMeta tablespaceMeta;
|
||||
private final String storageEngine;
|
||||
private final String dbComment;
|
||||
private final boolean readAuditing;
|
||||
private final boolean draftable;
|
||||
private final boolean draftableElement;
|
||||
private final BeanProperty unmappedJson;
|
||||
private final BeanProperty tenant;
|
||||
private final BeanProperty draft;
|
||||
private final BeanProperty draftDirty;
|
||||
private final LinkedHashMap<String, BeanProperty> propMap;
|
||||
/**
|
||||
* Map of DB column to property path (for nativeSql mapping).
|
||||
@@ -134,6 +154,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
private final BeanQueryAdapter queryAdapter;
|
||||
private final BeanFindController beanFinder;
|
||||
private final ChangeLogFilter changeLogFilter;
|
||||
final InheritInfo inheritInfo;
|
||||
private final boolean abstractType;
|
||||
private final BeanProperty idProperty;
|
||||
private final int idPropertyIndex;
|
||||
@@ -145,6 +166,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
* Properties that are initialised in the constructor need to be 'unloaded' to support partial object queries.
|
||||
*/
|
||||
private final int[] unloadProperties;
|
||||
/**
|
||||
* Properties local to this type (not from a super type).
|
||||
*/
|
||||
private final BeanProperty[] propertiesLocal;
|
||||
/**
|
||||
* Scalar mutable properties (need to dirty check on update).
|
||||
*/
|
||||
@@ -198,8 +223,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
private final String name;
|
||||
private final String baseTableAlias;
|
||||
private final boolean cacheSharableBeans;
|
||||
private final String docStoreQueueId;
|
||||
private final BeanDescriptorDraftHelp<T> draftHelp;
|
||||
private final BeanDescriptorCacheHelp<T> cacheHelp;
|
||||
private final BeanDescriptorJsonHelp<T> jsonHelp;
|
||||
private DocStoreBeanAdapter<T> docStoreAdapter;
|
||||
private DocumentMapping docMapping;
|
||||
private boolean docStoreEmbeddedInvalidation;
|
||||
private final String defaultSelectClause;
|
||||
private SpiEbeanServer ebeanServer;
|
||||
|
||||
@@ -207,7 +237,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
this.owner = owner;
|
||||
this.multiValueSupported = owner.isMultiValueSupported();
|
||||
this.entityType = deploy.getEntityType();
|
||||
this.properties = deploy.getProperties();
|
||||
this.properties = deploy.propertyNames();
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.baseTableAlias = "t0";
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
@@ -215,6 +245,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
this.prototypeEntityBean = createPrototypeEntityBean(beanType);
|
||||
this.iudMetrics = new BeanIudMetrics(name);
|
||||
this.namedQuery = deploy.getNamedQuery();
|
||||
this.namedRawSql = deploy.getNamedRawSql();
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
this.beanFinder = deploy.getBeanFinder();
|
||||
this.persistController = deploy.getPersistController();
|
||||
this.persistListener = deploy.getPersistListener();
|
||||
@@ -228,9 +261,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
this.idGeneratedValue = deploy.isIdGeneratedValue();
|
||||
this.idGenerator = deploy.getIdGenerator();
|
||||
this.selectLastInsertedId = deploy.getSelectLastInsertedId();
|
||||
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
|
||||
this.concurrencyMode = deploy.getConcurrencyMode();
|
||||
this.indexDefinitions = deploy.getIndexDefinitions();
|
||||
this.readAuditing = deploy.isReadAuditing();
|
||||
this.draftable = deploy.isDraftable();
|
||||
this.draftableElement = deploy.isDraftableElement();
|
||||
this.historySupport = deploy.isHistorySupport();
|
||||
this.draftTable = deploy.getDraftTable();
|
||||
this.baseTable = InternString.intern(deploy.getBaseTable());
|
||||
this.baseTableAsOf = deploy.getBaseTableAsOf();
|
||||
this.primaryKeyJoin = deploy.getPrimaryKeyJoin();
|
||||
@@ -252,11 +290,14 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
this.versionProperty = listHelper.getVersionProperty();
|
||||
this.unmappedJson = listHelper.getUnmappedJson();
|
||||
this.tenant = listHelper.getTenant();
|
||||
this.draft = listHelper.getDraft();
|
||||
this.draftDirty = listHelper.getDraftDirty();
|
||||
this.propMap = listHelper.getPropertyMap();
|
||||
this.propertiesTransient = listHelper.getTransients();
|
||||
this.propertiesNonTransient = listHelper.getNonTransients();
|
||||
this.propertiesBaseScalar = listHelper.getBaseScalar();
|
||||
this.propertiesEmbedded = listHelper.getEmbedded();
|
||||
this.propertiesLocal = listHelper.getLocal();
|
||||
this.propertiesMutable = listHelper.getMutable();
|
||||
this.unidirectional = listHelper.getUnidirectional();
|
||||
this.orderColumn = listHelper.getOrderColumn();
|
||||
@@ -279,6 +320,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly();
|
||||
this.cacheHelp = new BeanDescriptorCacheHelp<>(this, owner.cacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
|
||||
this.jsonHelp = initJsonHelp();
|
||||
this.draftHelp = new BeanDescriptorDraftHelp<>(this);
|
||||
this.docStoreAdapter = owner.createDocStoreBeanAdapter(this, deploy);
|
||||
this.docStoreQueueId = docStoreAdapter.queueId();
|
||||
// Check if there are no cascade save associated beans ( subject to change
|
||||
// in initialiseOther()). Note that if we are in an inheritance hierarchy
|
||||
// then we also need to check every BeanDescriptors in the InheritInfo as
|
||||
@@ -438,10 +482,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
* as they are used to get the imported and exported properties.
|
||||
*/
|
||||
void initialiseId(BeanDescriptorInitContext initContext) {
|
||||
if (draftable) {
|
||||
initContext.addDraft(baseTable, draftTable);
|
||||
}
|
||||
if (historySupport) {
|
||||
// add mapping (used to swap out baseTable for asOf queries)
|
||||
initContext.addHistory(baseTable, baseTableAsOf);
|
||||
}
|
||||
if (inheritInfo != null) {
|
||||
inheritInfo.setDescriptor(this);
|
||||
}
|
||||
|
||||
// initialise just the Id property only
|
||||
if (idProperty != null) {
|
||||
idProperty.initialise(initContext);
|
||||
@@ -452,6 +503,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
* Initialise the exported and imported parts for associated properties.
|
||||
*/
|
||||
public void initialiseOther(BeanDescriptorInitContext initContext) {
|
||||
for (BeanPropertyAssocMany<?> many : propertiesManyToMany) {
|
||||
// register associated draft table for M2M intersection
|
||||
many.registerDraftIntersectionTable(initContext);
|
||||
}
|
||||
if (historySupport) {
|
||||
// history support on this bean so check all associated intersection tables
|
||||
// and if they are not excluded register the associated 'with history' table
|
||||
@@ -552,6 +607,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
}
|
||||
}
|
||||
}
|
||||
docStoreEmbeddedInvalidation = docStoreAdapter.hasEmbeddedInvalidation();
|
||||
}
|
||||
|
||||
private void addUniqueColumns(IndexDefinition indexDef) {
|
||||
@@ -587,6 +643,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
for (BeanPropertyAssocOne<?> one : propertiesOne) {
|
||||
one.initialisePostTarget();
|
||||
}
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
docStoreAdapter = (DocStoreBeanAdapter<T>) inheritInfo.getRoot().desc().docStoreAdapter();
|
||||
}
|
||||
docMapping = docStoreAdapter.createDocMapping();
|
||||
docStoreAdapter.registerPaths();
|
||||
cacheHelp.deriveNotifyFlags();
|
||||
}
|
||||
|
||||
@@ -614,6 +675,18 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return type == PersistRequest.Type.INSERT ? batchEscalateOnCascadeInsert : batchEscalateOnCascadeDelete;
|
||||
}
|
||||
|
||||
void initInheritInfo() {
|
||||
if (inheritInfo != null) {
|
||||
// need to check every BeanDescriptor in the inheritance hierarchy
|
||||
if (saveRecurseSkippable) {
|
||||
saveRecurseSkippable = inheritInfo.isSaveRecurseSkippable();
|
||||
}
|
||||
if (deleteRecurseSkippable) {
|
||||
deleteRecurseSkippable = inheritInfo.isDeleteRecurseSkippable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void metricPersistBatch(PersistRequest.Type type, long startNanos, int size) {
|
||||
iudMetrics.addBatch(type, startNanos, size);
|
||||
}
|
||||
@@ -650,6 +723,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger for logging read audit events.
|
||||
*/
|
||||
public ReadAuditLogger readAuditLogger() {
|
||||
return ebeanServer.readAuditLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditPrepare for preparing read audit events prior to logging.
|
||||
*/
|
||||
private ReadAuditPrepare readAuditPrepare() {
|
||||
return ebeanServer.readAuditPrepare();
|
||||
}
|
||||
|
||||
public boolean isChangeLog() {
|
||||
return changeLogFilter != null;
|
||||
}
|
||||
@@ -852,6 +939,166 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return defaultSelectClause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this object is the root level object in its entity
|
||||
* inheritance.
|
||||
*/
|
||||
@Override
|
||||
public boolean isInheritanceRoot() {
|
||||
return inheritInfo == null || inheritInfo.isRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type maps to a root type of a doc store document (not embedded or ignored).
|
||||
*/
|
||||
@Override
|
||||
public boolean isDocStoreMapped() {
|
||||
return docStoreAdapter.mapped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this bean type has embedded doc store invalidation.
|
||||
*/
|
||||
public boolean isDocStoreEmbeddedInvalidation() {
|
||||
return docStoreEmbeddedInvalidation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the queueId used to uniquely identify this type when queuing an index updateAdd.
|
||||
*/
|
||||
@Override
|
||||
public String docStoreQueueId() {
|
||||
return docStoreQueueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentMapping docMapping() {
|
||||
return docMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the doc store helper for this bean type.
|
||||
*/
|
||||
@Override
|
||||
public BeanDocType<T> docStore() {
|
||||
return docStoreAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return doc store adapter for internal use for processing persist requests.
|
||||
*/
|
||||
public DocStoreBeanAdapter<T> docStoreAdapter() {
|
||||
return docStoreAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Document mapping recursively with the given prefix relative to the root of the document.
|
||||
*/
|
||||
public void docStoreMapping(final DocMappingBuilder mapping, final String prefix) {
|
||||
if (prefix != null && idProperty != null) {
|
||||
// id property not included in the
|
||||
idProperty.docStoreMapping(mapping, prefix);
|
||||
}
|
||||
if (inheritInfo != null) {
|
||||
String discCol = inheritInfo.getDiscriminatorColumn();
|
||||
if (Types.VARCHAR == inheritInfo.getDiscriminatorType()) {
|
||||
mapping.add(new DocPropertyMapping(discCol, DocPropertyType.ENUM));
|
||||
} else {
|
||||
mapping.add(new DocPropertyMapping(discCol, DocPropertyType.INTEGER));
|
||||
}
|
||||
}
|
||||
for (BeanProperty prop : propertiesNonTransient) {
|
||||
prop.docStoreMapping(mapping, prefix);
|
||||
}
|
||||
if (inheritInfo != null) {
|
||||
inheritInfo.visitChildren(inheritInfo1 -> {
|
||||
for (BeanProperty localProperty : inheritInfo1.localProperties()) {
|
||||
localProperty.docStoreMapping(mapping, prefix);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root bean type if part of inheritance hierarchy.
|
||||
*/
|
||||
@Override
|
||||
public BeanType<?> root() {
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
return inheritInfo.getRoot().desc();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full name taking into account inheritance.
|
||||
*/
|
||||
public String rootName() {
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
return inheritInfo.getRoot().desc().name();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named ORM query.
|
||||
*/
|
||||
public String namedQuery(String name) {
|
||||
return namedQuery.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named RawSql query.
|
||||
*/
|
||||
public SpiRawSql namedRawSql(String named) {
|
||||
return namedRawSql.get(named);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of DocStoreMode that should occur for this type of persist request
|
||||
* given the transactions requested mode.
|
||||
*/
|
||||
public DocStoreMode docStoreMode(PersistRequest.Type persistType, DocStoreMode txnMode) {
|
||||
return docStoreAdapter.mode(persistType, txnMode);
|
||||
}
|
||||
|
||||
public void docStoreInsert(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext bulkUpdate) throws IOException {
|
||||
docStoreAdapter.insert(idValue, persistRequest, bulkUpdate);
|
||||
}
|
||||
|
||||
public void docStoreUpdate(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext bulkUpdate) throws IOException {
|
||||
docStoreAdapter.update(idValue, persistRequest, bulkUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this update invalidates an embedded part of a doc store document.
|
||||
*/
|
||||
public void docStoreUpdateEmbedded(PersistRequestBean<T> request, DocStoreUpdates docStoreUpdates) {
|
||||
docStoreAdapter.updateEmbedded(request, docStoreUpdates);
|
||||
}
|
||||
|
||||
public void docStoreDeleteById(Object idValue, DocStoreUpdateContext txn) throws IOException {
|
||||
docStoreAdapter.deleteById(idValue, txn);
|
||||
}
|
||||
|
||||
public T publish(T draftBean, T liveBean) {
|
||||
return draftHelp.publish(draftBean, liveBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset properties on the draft bean based on @DraftDirty and @DraftReset.
|
||||
*/
|
||||
public boolean draftReset(T draftBean) {
|
||||
return draftHelp.draftReset(draftBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the draft dirty boolean property or null if there is not one assigned to this bean type.
|
||||
*/
|
||||
BeanProperty draftDirty() {
|
||||
return draftDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the query for multi-tenancy check for document store only use.
|
||||
*/
|
||||
@@ -862,6 +1109,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
tenant.addTenant(query, tenantId);
|
||||
}
|
||||
}
|
||||
if (isDocStoreOnly()) {
|
||||
query.setUseDocStore(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -901,7 +1151,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
/**
|
||||
* Return true if the persist request needs to notify the cache.
|
||||
*/
|
||||
public boolean isCacheNotify(PersistRequest.Type type) {
|
||||
public boolean isCacheNotify(PersistRequest.Type type, boolean publish) {
|
||||
if (draftable && !publish) {
|
||||
// no caching when editing draft beans
|
||||
return false;
|
||||
}
|
||||
return cacheHelp.isCacheNotify(type);
|
||||
}
|
||||
|
||||
@@ -1118,6 +1372,52 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
cacheHelp.cacheBeanUpdate(key, changes, updateNaturalKey, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the read audit of a findFutureList() query.
|
||||
*/
|
||||
public void readAuditFutureList(SpiQuery<T> spiQuery) {
|
||||
if (isReadAuditing()) {
|
||||
ReadEvent event = new ReadEvent(fullName);
|
||||
// prepare in the foreground thread while we have the user context
|
||||
// information (query is processed/executed later in bg thread)
|
||||
readAuditPrepare(event);
|
||||
spiQuery.setFutureFetchAudit(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a bean read to the read audit log.
|
||||
*/
|
||||
public void readAuditBean(String queryKey, String bindLog, Object bean) {
|
||||
ReadEvent event = new ReadEvent(fullName, queryKey, bindLog, idForJson(bean));
|
||||
readAuditPrepare(event);
|
||||
readAuditLogger().auditBean(event);
|
||||
}
|
||||
|
||||
private void readAuditPrepare(ReadEvent event) {
|
||||
ReadAuditPrepare prepare = readAuditPrepare();
|
||||
if (prepare != null) {
|
||||
prepare.prepare(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a many bean read to the read audit log.
|
||||
*/
|
||||
public void readAuditMany(String queryKey, String bindLog, List<Object> ids) {
|
||||
ReadEvent event = new ReadEvent(fullName, queryKey, bindLog, ids);
|
||||
readAuditPrepare(event);
|
||||
readAuditLogger().auditMany(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a futureList many read to the read audit log.
|
||||
*/
|
||||
public void readAuditFutureMany(ReadEvent event) {
|
||||
// this has already been prepared (in foreground thread)
|
||||
readAuditLogger().auditMany(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table alias. This is always the first letter of the bean name.
|
||||
*/
|
||||
@@ -1495,11 +1795,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
if (d != null) {
|
||||
Object shareableBean = d.getSharableBean();
|
||||
if (shareableBean != null) {
|
||||
if (isReadAuditing()) {
|
||||
readAuditBean("ref", "", shareableBean);
|
||||
}
|
||||
return (T) shareableBean;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
EntityBean eb = createEntityBean();
|
||||
id = convertSetId(id, eb);
|
||||
EntityBeanIntercept ebi = eb._ebean_getIntercept();
|
||||
@@ -1534,6 +1840,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
* Create a non read only reference bean without checking cacheSharableBeans.
|
||||
*/
|
||||
public T createReference(Object id, PersistenceContext pc) {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
return createRef(id, pc);
|
||||
}
|
||||
|
||||
@@ -1632,7 +1941,17 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
* visible.
|
||||
*/
|
||||
public BeanPropertyAssocOne<?> unidirectional() {
|
||||
return unidirectional;
|
||||
BeanDescriptor<?> other = this;
|
||||
while (true) {
|
||||
if (other.unidirectional != null) {
|
||||
return other.unidirectional;
|
||||
}
|
||||
if (other.inheritInfo != null && !other.inheritInfo.isRoot()) {
|
||||
other = other.inheritInfo.getParent().desc();
|
||||
continue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1889,6 +2208,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
if (lazyLoadProperty == -1) {
|
||||
return false;
|
||||
}
|
||||
if (inheritInfo != null) {
|
||||
return descOf(ebi.owner().getClass()).lazyLoadMany(ebi, lazyLoadProperty, parent);
|
||||
}
|
||||
return lazyLoadMany(ebi, lazyLoadProperty, parent);
|
||||
}
|
||||
|
||||
@@ -1916,6 +2238,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the correct BeanDescriptor based on the bean class type.
|
||||
*/
|
||||
BeanDescriptor<?> descOf(Class<?> type) {
|
||||
return inheritInfo.readType(type).desc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Comparator for local sorting of lists.
|
||||
*
|
||||
@@ -2108,8 +2437,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
|
||||
BeanProperty _findBeanProperty(String propName) {
|
||||
BeanProperty prop = propMap.get(propName);
|
||||
if (prop == null && "_$IdClass$".equals(propName)) {
|
||||
return idProperty;
|
||||
if (prop == null) {
|
||||
if ("_$IdClass$".equals(propName)) {
|
||||
return idProperty;
|
||||
} else if (inheritInfo != null) {
|
||||
// search in sub types...
|
||||
return inheritInfo.findSubTypeProperty(propName);
|
||||
}
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
@@ -2143,6 +2477,45 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return autoTunable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Inheritance mapping information. This will be null if this type
|
||||
* of bean is not involved in any ORM inheritance mapping.
|
||||
*/
|
||||
@Override
|
||||
public InheritInfo inheritInfo() {
|
||||
return inheritInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInheritance() {
|
||||
return inheritInfo != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String discColumn() {
|
||||
return inheritInfo.getDiscriminatorColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the discriminator value for this bean type (or null when there is no inheritance).
|
||||
*/
|
||||
public String discValue() {
|
||||
return inheritInfo == null ? null : inheritInfo.getDiscriminatorStringValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T createBeanUsingDisc(Object discValue) {
|
||||
return (T) inheritInfo.getType(discValue.toString()).desc().createBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInheritanceWhere(Query<?> query) {
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
query.where().eq(inheritInfo.getDiscriminatorColumn(), inheritInfo.getDiscriminatorValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is an embedded bean.
|
||||
*/
|
||||
@@ -2335,6 +2708,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
@Override
|
||||
public String baseTable(SpiQuery.TemporalMode mode) {
|
||||
switch (mode) {
|
||||
case DRAFT:
|
||||
return draftTable;
|
||||
case VERSIONS:
|
||||
return baseTableVersionsBetween;
|
||||
case AS_OF:
|
||||
@@ -2344,6 +2719,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated draft table.
|
||||
*/
|
||||
public String draftTable() {
|
||||
return draftTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if read auditing is on this entity bean.
|
||||
*/
|
||||
public boolean isReadAuditing() {
|
||||
return readAuditing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSoftDelete() {
|
||||
return softDelete;
|
||||
@@ -2375,6 +2764,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this entity type is draftable.
|
||||
*/
|
||||
public boolean isDraftable() {
|
||||
return draftable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this entity type is a draftable element (child).
|
||||
*/
|
||||
public boolean isDraftableElement() {
|
||||
return draftableElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> pathMap(String prefix) {
|
||||
return pathMaps.computeIfAbsent(prefix, s -> {
|
||||
@@ -2440,6 +2843,28 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the draft to true for this entity bean instance.
|
||||
* This bean is being loaded via asDraft() query.
|
||||
*/
|
||||
@Override
|
||||
public void setDraft(EntityBean entityBean) {
|
||||
if (draft != null) {
|
||||
draft.setValue(entityBean, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean is considered a 'draft' instance (not 'live').
|
||||
*/
|
||||
public boolean isDraftInstance(EntityBean entityBean) {
|
||||
if (draft != null) {
|
||||
return Boolean.TRUE == draft.getValue(entityBean);
|
||||
}
|
||||
// no draft property - so return false
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToManyDirty(EntityBean bean) {
|
||||
final EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
@@ -2454,6 +2879,39 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean is draftable and considered a 'live' instance.
|
||||
*/
|
||||
public boolean isLiveInstance(EntityBean entityBean) {
|
||||
if (draft != null) {
|
||||
return Boolean.FALSE == draft.getValue(entityBean);
|
||||
}
|
||||
// no draft property - so return false
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a @DraftDirty property set it's value on the bean.
|
||||
*/
|
||||
public void setDraftDirty(EntityBean entityBean, boolean value) {
|
||||
if (draftDirty != null) {
|
||||
// check to see if the dirty property has already
|
||||
// been set and if so do not set the value
|
||||
if (!entityBean._ebean_getIntercept().isChangedProperty(draftDirty.propertyIndex())) {
|
||||
draftDirty.setValueIntercept(entityBean, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimise the draft query fetching any draftable element relationships.
|
||||
*/
|
||||
public void draftQueryOptimise(Query<T> query) {
|
||||
// use per query PersistenceContext to ensure fresh beans loaded
|
||||
query.setPersistenceContextScope(PersistenceContextScope.QUERY);
|
||||
draftHelp.draftQueryOptimise(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this entity bean has history support.
|
||||
*/
|
||||
@@ -2499,8 +2957,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
* This is only used with Identity columns and getGeneratedKeys is not
|
||||
* supported.
|
||||
*/
|
||||
public String selectLastInsertedId() {
|
||||
return selectLastInsertedId;
|
||||
public String selectLastInsertedId(boolean publish) {
|
||||
return publish ? selectLastInsertedId : selectLastInsertedIdDraft;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2870,6 +3328,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return propertiesBaseScalar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties local to this type for inheritance.
|
||||
*/
|
||||
public BeanProperty[] propertiesLocal() {
|
||||
return propertiesLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties set as generated values on insert.
|
||||
*/
|
||||
@@ -2932,4 +3397,27 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return propertiesUnique;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BeanType<?>> inheritanceChildren() {
|
||||
if (hasInheritance()) {
|
||||
return inheritInfo().getChildren()
|
||||
.stream()
|
||||
.map(InheritInfo::desc)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanType<?> inheritanceParent() {
|
||||
return inheritInfo() == null ? null : inheritInfo().getParent().desc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAllInheritanceChildren(Consumer<BeanType<?>> visitor) {
|
||||
if (hasInheritance()) {
|
||||
inheritInfo().visitChildren(info -> visitor.accept(info.desc()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-10
@@ -449,7 +449,44 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
void beanPutAll(Collection<EntityBean> beans) {
|
||||
beanCachePutAllDirect(beans);
|
||||
if (desc.inheritInfo != null) {
|
||||
Class<?> aClass = theClassOf(beans);
|
||||
// check if all beans have the same class
|
||||
for (EntityBean bean : beans) {
|
||||
if (!bean.getClass().equals(aClass)) {
|
||||
aClass = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (aClass == null) {
|
||||
// there are different bean types in the collection, so we add one by one to the cache
|
||||
for (EntityBean bean : beans) {
|
||||
desc.descOf(bean.getClass()).cacheBeanPutDirect(bean);
|
||||
}
|
||||
} else {
|
||||
desc.descOf(aClass).cacheBeanPutAllDirect(beans);
|
||||
}
|
||||
} else {
|
||||
beanCachePutAllDirect(beans);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> theClassOf(Collection<EntityBean> beans) {
|
||||
if (beans instanceof List) {
|
||||
return ((List<?>) beans).get(0).getClass();
|
||||
}
|
||||
return beans.iterator().next().getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a bean into the bean cache.
|
||||
*/
|
||||
void beanCachePut(EntityBean bean) {
|
||||
if (desc.inheritInfo != null) {
|
||||
desc.descOf(bean.getClass()).cacheBeanPutDirect(bean);
|
||||
} else {
|
||||
beanCachePutDirect(bean);
|
||||
}
|
||||
}
|
||||
|
||||
void beanCachePutAllDirect(Collection<EntityBean> beans) {
|
||||
@@ -483,13 +520,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a bean into the bean cache.
|
||||
*/
|
||||
void beanCachePut(EntityBean bean) {
|
||||
beanCachePutDirect(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the bean into the bean cache.
|
||||
*/
|
||||
@@ -564,6 +594,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
if (beanLog.isLoggable(TRACE)) {
|
||||
beanLog.log(TRACE, " GET {0}({1}) - hit shared bean", cacheName, id);
|
||||
}
|
||||
if (desc.isReadAuditing()) {
|
||||
desc.readAuditBean("l2", "", bean);
|
||||
}
|
||||
return (T) bean;
|
||||
}
|
||||
}
|
||||
@@ -574,7 +607,19 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the entity bean taking into account inheritance.
|
||||
*/
|
||||
private EntityBean loadBean(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
return loadBeanDirect(id, readOnly, data, context);
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return loadBeanDirect(id, readOnly, data, context);
|
||||
} else {
|
||||
return rootDescriptor(discValue).cacheBeanLoadDirect(id, readOnly, data, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root BeanDescriptor for inheritance.
|
||||
*/
|
||||
private BeanDescriptor<?> rootDescriptor(String discValue) {
|
||||
return desc.inheritInfo.readType(discValue).desc();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,6 +648,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, data, context);
|
||||
if (desc.isReadAuditing()) {
|
||||
desc.readAuditBean("l2", "", bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@@ -610,7 +658,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Load the embedded bean checking for inheritance.
|
||||
*/
|
||||
EntityBean embeddedBeanLoad(CachedBeanData data, PersistenceContext context) {
|
||||
return embeddedBeanLoadDirect(data, context);
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return embeddedBeanLoadDirect(data, context);
|
||||
} else {
|
||||
return rootDescriptor(discValue).cacheEmbeddedBeanLoadDirect(data, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.Query;
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper for BeanDescriptor that manages draft entity beans.
|
||||
*
|
||||
* @param <T> The entity bean type
|
||||
*/
|
||||
final class BeanDescriptorDraftHelp<T> {
|
||||
|
||||
private final BeanDescriptor<T> desc;
|
||||
private final BeanProperty draftDirty;
|
||||
private final BeanProperty[] resetProperties;
|
||||
|
||||
BeanDescriptorDraftHelp(BeanDescriptor<T> desc) {
|
||||
this.desc = desc;
|
||||
this.draftDirty = desc.draftDirty();
|
||||
this.resetProperties = resetProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties that are reset on draft beans after publish.
|
||||
*/
|
||||
private BeanProperty[] resetProperties() {
|
||||
List<BeanProperty> list = new ArrayList<>();
|
||||
for (BeanProperty prop : desc.propertiesNonMany()) {
|
||||
if (prop.isDraftReset()) {
|
||||
list.add(prop);
|
||||
}
|
||||
}
|
||||
return list.toArray(new BeanProperty[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of all the 'reset properties' to null on the draft bean.
|
||||
*/
|
||||
boolean draftReset(T draftBean) {
|
||||
EntityBean draftEntityBean = (EntityBean) draftBean;
|
||||
if (draftDirty != null) {
|
||||
// set @DraftDirty property to false
|
||||
draftDirty.setValueIntercept(draftEntityBean, false);
|
||||
}
|
||||
// set to null on all @DraftReset properties
|
||||
for (BeanProperty resetProperty : resetProperties) {
|
||||
resetProperty.setValueIntercept(draftEntityBean, null);
|
||||
}
|
||||
// return true if the bean is dirty (and should be persisted)
|
||||
return draftEntityBean._ebean_getIntercept().isDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer the values from the draftBean to the liveBean.
|
||||
* <p>
|
||||
* This will recursive transfer values to all @DraftableElement properties.
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T publish(T draftBean, T liveBean) {
|
||||
if (liveBean == null) {
|
||||
liveBean = (T) desc.createEntityBean();
|
||||
}
|
||||
EntityBean draft = (EntityBean) draftBean;
|
||||
EntityBean live = (EntityBean) liveBean;
|
||||
BeanProperty idProperty = desc.idProperty();
|
||||
if (idProperty != null) {
|
||||
idProperty.publish(draft, live);
|
||||
}
|
||||
for (BeanProperty prop : desc.propertiesNonMany()) {
|
||||
prop.publish(draft, live);
|
||||
}
|
||||
for (BeanPropertyAssocMany<?> many : desc.propertiesMany()) {
|
||||
if (many.targetDescriptor().isDraftable()) {
|
||||
many.publishMany(draft, live);
|
||||
}
|
||||
}
|
||||
return liveBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch draftable element relationships.
|
||||
*/
|
||||
void draftQueryOptimise(Query<T> query) {
|
||||
for (BeanPropertyAssocOne<?> anOne : desc.propertiesOne()) {
|
||||
if (anOne.targetDescriptor().isDraftableElement()) {
|
||||
query.fetch(anOne.name());
|
||||
}
|
||||
}
|
||||
for (BeanPropertyAssocMany<?> aMany : desc.propertiesMany()) {
|
||||
if (aMany.targetDescriptor().isDraftableElement()) {
|
||||
query.fetch(aMany.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -5,14 +5,20 @@ import java.util.Map;
|
||||
class BeanDescriptorInitContext {
|
||||
|
||||
private final Map<String, String> withHistoryTables;
|
||||
private final Map<String, String> draftTables;
|
||||
private final String asOfViewSuffix;
|
||||
private String embeddedPrefix;
|
||||
|
||||
BeanDescriptorInitContext(Map<String, String> withHistoryTables, String asOfViewSuffix) {
|
||||
BeanDescriptorInitContext(Map<String, String> withHistoryTables, Map<String, String> draftTables, String asOfViewSuffix) {
|
||||
this.withHistoryTables = withHistoryTables;
|
||||
this.draftTables = draftTables;
|
||||
this.asOfViewSuffix = asOfViewSuffix;
|
||||
}
|
||||
|
||||
void addDraft(String baseTable, String draftTable) {
|
||||
draftTables.put(baseTable, draftTable);
|
||||
}
|
||||
|
||||
void addHistory(String baseTable, String baseTableAsOf) {
|
||||
withHistoryTables.put(baseTable, baseTableAsOf);
|
||||
}
|
||||
@@ -21,6 +27,10 @@ class BeanDescriptorInitContext {
|
||||
withHistoryTables.put(intersectionTableName, intersectionTableName + asOfViewSuffix);
|
||||
}
|
||||
|
||||
void addDraftIntersection(String intersectionPublishTable, String intersectionDraftTable) {
|
||||
draftTables.put(intersectionPublishTable, intersectionDraftTable);
|
||||
}
|
||||
|
||||
public void setEmbeddedPrefix(String embeddedPrefix) {
|
||||
this.embeddedPrefix = embeddedPrefix;
|
||||
}
|
||||
|
||||
+41
-3
@@ -19,14 +19,24 @@ import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
|
||||
final class BeanDescriptorJsonHelp<T> {
|
||||
|
||||
private final BeanDescriptor<T> desc;
|
||||
private final InheritInfo inheritInfo;
|
||||
|
||||
BeanDescriptorJsonHelp(BeanDescriptor<T> desc) {
|
||||
this.desc = desc;
|
||||
this.inheritInfo = desc.inheritInfo;
|
||||
}
|
||||
|
||||
void jsonWrite(SpiJsonWriter writeJson, EntityBean bean, String key) throws IOException {
|
||||
writeJson.writeStartObject(key);
|
||||
jsonWriteProperties(writeJson, bean);
|
||||
if (inheritInfo == null) {
|
||||
jsonWriteProperties(writeJson, bean);
|
||||
} else {
|
||||
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
|
||||
String discValue = localInheritInfo.getDiscriminatorStringValue();
|
||||
String discColumn = localInheritInfo.getDiscriminatorColumn();
|
||||
writeJson.gen().writeStringField(discColumn, discValue);
|
||||
localInheritInfo.desc().jsonWriteProperties(writeJson, bean);
|
||||
}
|
||||
writeJson.writeEndObject();
|
||||
}
|
||||
|
||||
@@ -35,7 +45,11 @@ final class BeanDescriptorJsonHelp<T> {
|
||||
}
|
||||
|
||||
void jsonWriteDirty(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
if (inheritInfo == null) {
|
||||
jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
} else {
|
||||
desc.descOf(bean.getClass()).jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
}
|
||||
}
|
||||
|
||||
void jsonWriteDirtyProperties(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
@@ -66,7 +80,31 @@ final class BeanDescriptorJsonHelp<T> {
|
||||
throw new JsonParseException(parser, "Unexpected token " + token + " - expecting start_object", parser.getCurrentLocation());
|
||||
}
|
||||
}
|
||||
return jsonReadObject(jsonRead, path, target);
|
||||
|
||||
if (desc.inheritInfo == null || !withInheritance) {
|
||||
return jsonReadObject(jsonRead, path, target);
|
||||
}
|
||||
|
||||
ObjectNode node = jsonRead.mapper().readTree(parser);
|
||||
if (node.isNull()) {
|
||||
return null;
|
||||
}
|
||||
JsonParser newParser = node.traverse();
|
||||
SpiJsonReader newReader = jsonRead.forJson(newParser);
|
||||
|
||||
// check for the discriminator value to determine the correct sub type
|
||||
String discColumn = inheritInfo.getRoot().getDiscriminatorColumn();
|
||||
JsonNode discNode = node.get(discColumn);
|
||||
if (discNode == null || discNode.isNull()) {
|
||||
if (!desc.isAbstractType()) {
|
||||
return desc.jsonReadObject(newReader, path, target);
|
||||
}
|
||||
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
|
||||
throw new JsonParseException(newParser, msg, parser.getCurrentLocation());
|
||||
}
|
||||
|
||||
BeanDescriptor<T> inheritDesc = (BeanDescriptor<T>) inheritInfo.readType(discNode.asText()).desc();
|
||||
return inheritDesc.jsonReadObject(newReader, path, target);
|
||||
}
|
||||
|
||||
private T jsonReadObject(SpiJsonReader readJson, String path, T target) throws IOException {
|
||||
|
||||
+171
-18
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.Model;
|
||||
import io.ebean.RawSqlBuilder;
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
@@ -34,6 +35,12 @@ import io.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import io.ebeaninternal.server.properties.BeanPropertyAccess;
|
||||
import io.ebeaninternal.server.properties.EnhanceBeanPropertyAccess;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
import io.ebeaninternal.xmapping.api.XmapEbean;
|
||||
import io.ebeaninternal.xmapping.api.XmapEntity;
|
||||
import io.ebeaninternal.xmapping.api.XmapNamedQuery;
|
||||
import io.ebeaninternal.xmapping.api.XmapRawSql;
|
||||
import io.ebeanservice.docstore.api.DocStoreBeanAdapter;
|
||||
import io.ebeanservice.docstore.api.DocStoreFactory;
|
||||
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
@@ -59,6 +66,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
|
||||
private final ReadAnnotations readAnnotations;
|
||||
private final TransientProperties transientProperties;
|
||||
private final DeployInherit deplyInherit;
|
||||
private final BeanPropertyAccess beanPropertyAccess = new EnhanceBeanPropertyAccess();
|
||||
private final DeployUtil deployUtil;
|
||||
private final PersistControllerManager persistControllerManager;
|
||||
@@ -74,6 +82,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
private final ChangeLogListener changeLogListener;
|
||||
private final ChangeLogRegister changeLogRegister;
|
||||
private final ChangeLogPrepare changeLogPrepare;
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
private final MultiValueBind multiValueBind;
|
||||
private final TypeManager typeManager;
|
||||
private final BootupClasses bootupClasses;
|
||||
@@ -81,6 +90,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
private final List<BeanDescriptor<?>> elementDescriptors = new ArrayList<>();
|
||||
private final Map<Class<?>, BeanTable> beanTableMap = new HashMap<>();
|
||||
private final Map<String, BeanDescriptor<?>> descMap = new HashMap<>();
|
||||
private final Map<String, BeanDescriptor<?>> descQueueMap = new HashMap<>();
|
||||
private final Map<String, BeanManager<?>> beanManagerMap = new HashMap<>();
|
||||
private final Map<String, List<BeanDescriptor<?>>> tableToDescMap = new HashMap<>();
|
||||
private final Map<String, List<BeanDescriptor<?>>> tableToViewDescMap = new HashMap<>();
|
||||
@@ -101,6 +111,10 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
* Map of base tables to 'with history views' used to support 'as of' queries.
|
||||
*/
|
||||
private final Map<String, String> asOfTableMap = new HashMap<>();
|
||||
/**
|
||||
* Map of base tables to 'draft' tables.
|
||||
*/
|
||||
private final Map<String, String> draftTableMap = new HashMap<>();
|
||||
|
||||
// temporary collections used during startup and then cleared
|
||||
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<>();
|
||||
@@ -114,6 +128,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
this.config = config.getConfig();
|
||||
this.serverName = InternString.intern(this.config.getName());
|
||||
this.cacheManager = config.getCacheManager();
|
||||
this.docStoreFactory = config.getDocStoreFactory();
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
this.dataSource = this.config.getDataSource();
|
||||
this.encryptKeyManager = this.config.getEncryptKeyManager();
|
||||
@@ -128,6 +143,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
this.createProperties = config.getDeployCreateProperties();
|
||||
this.namingConvention = this.config.getNamingConvention();
|
||||
this.dbIdentity = config.getDatabasePlatform().dbIdentity();
|
||||
this.deplyInherit = config.getDeployInherit();
|
||||
this.deployUtil = config.getDeployUtil();
|
||||
this.typeManager = deployUtil.typeManager();
|
||||
this.beanManagerFactory = new BeanManagerFactory(config.getDatabasePlatform());
|
||||
@@ -204,6 +220,15 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor<T> descriptor, DeployBeanDescriptor<T> deploy) {
|
||||
return docStoreFactory.createAdapter(descriptor, deploy);
|
||||
}
|
||||
|
||||
public BeanDescriptor<?> descriptorByQueueId(String queueId) {
|
||||
return descQueueMap.get(queueId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiBeanType beanType(Class<?> entityType) {
|
||||
return descriptor(entityType);
|
||||
@@ -249,15 +274,24 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
return idBinderFactory.createIdBinder(idProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the map of base tables to draft tables.
|
||||
*/
|
||||
public Map<String, String> draftTableMap() {
|
||||
return draftTableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy returning the asOfTableMap (which is required by the SQL builders).
|
||||
*/
|
||||
public Map<String, String> deploy() {
|
||||
public Map<String, String> deploy(List<XmapEbean> mappings) {
|
||||
try {
|
||||
createListeners();
|
||||
readEntityDeploymentInitial();
|
||||
readXmlMapping(mappings);
|
||||
readEntityBeanTable();
|
||||
readEntityDeploymentAssociations();
|
||||
readInheritedIdGenerators();
|
||||
// creates the BeanDescriptors
|
||||
readEntityRelationships();
|
||||
List<BeanDescriptor<?>> list = new ArrayList<>(descMap.values());
|
||||
@@ -281,6 +315,56 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
}
|
||||
}
|
||||
|
||||
private void readXmlMapping(List<XmapEbean> mappings) {
|
||||
if (mappings != null) {
|
||||
ClassLoader classLoader = config.getClassLoadConfig().getClassLoader();
|
||||
for (XmapEbean mapping : mappings) {
|
||||
List<XmapEntity> entityDeploy = mapping.getEntity();
|
||||
for (XmapEntity deploy : entityDeploy) {
|
||||
readEntityMapping(classLoader, deploy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readEntityMapping(ClassLoader classLoader, XmapEntity entityDeploy) {
|
||||
String entityClassName = entityDeploy.getClazz();
|
||||
Class<?> entityClass;
|
||||
try {
|
||||
entityClass = Class.forName(entityClassName, false, classLoader);
|
||||
} catch (Exception e) {
|
||||
log.log(ERROR, "Could not load entity bean class " + entityClassName + " for ebean.xml entry");
|
||||
return;
|
||||
}
|
||||
|
||||
DeployBeanInfo<?> info = deployInfoMap.get(entityClass);
|
||||
if (info == null) {
|
||||
log.log(ERROR, "No entity bean for ebean.xml entry " + entityClassName);
|
||||
|
||||
} else {
|
||||
for (XmapRawSql sql : entityDeploy.getRawSql()) {
|
||||
RawSqlBuilder builder;
|
||||
try {
|
||||
builder = RawSqlBuilder.parse(sql.getQuery());
|
||||
} catch (RuntimeException e) {
|
||||
builder = RawSqlBuilder.unparsed(sql.getQuery());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> columnMapping : sql.getColumnMapping().entrySet()) {
|
||||
builder.columnMapping(columnMapping.getKey(), columnMapping.getValue());
|
||||
}
|
||||
for (Map.Entry<String, String> aliasMapping : sql.getAliasMapping().entrySet()) {
|
||||
builder.tableAliasMapping(aliasMapping.getKey(), aliasMapping.getValue());
|
||||
}
|
||||
info.addRawSql(sql.getName(), builder.create());
|
||||
}
|
||||
|
||||
for (XmapNamedQuery namedQuery : entityDeploy.getNamedQuery()) {
|
||||
info.addNamedQuery(namedQuery.getName(), namedQuery.getQuery());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Encrypt key given the table and column name.
|
||||
*/
|
||||
@@ -359,11 +443,10 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
// build map of tables to view entities dependent on those tables
|
||||
// for the purpose of invalidating appropriate query caches
|
||||
String[] dependentTables = desc.dependentTables();
|
||||
if (dependentTables != null && dependentTables.length > 0) {
|
||||
if (dependentTables != null) {
|
||||
for (String depTable : dependentTables) {
|
||||
depTable = depTable.toLowerCase();
|
||||
List<BeanDescriptor<?>> list = tableToViewDescMap.computeIfAbsent(depTable, k -> new ArrayList<>(1));
|
||||
list.add(desc);
|
||||
tableToViewDescMap.computeIfAbsent(depTable, k -> new ArrayList<>(1)).add(desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,7 +472,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
// now that all the BeanDescriptors are in their map
|
||||
// we can initialise them which sorts out circular
|
||||
// dependencies for OneToMany and ManyToOne etc
|
||||
BeanDescriptorInitContext initContext = new BeanDescriptorInitContext(asOfTableMap, asOfViewSuffix);
|
||||
BeanDescriptorInitContext initContext = new BeanDescriptorInitContext(asOfTableMap, draftTableMap, asOfViewSuffix);
|
||||
|
||||
// PASS 1:
|
||||
// initialise the ID properties of all the beans
|
||||
@@ -399,7 +482,12 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
d.initialiseId(initContext);
|
||||
}
|
||||
|
||||
// PASS 2: Was to initialise all the inherit info
|
||||
// PASS 2:
|
||||
// now initialise all the inherit info
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
d.initInheritInfo();
|
||||
}
|
||||
|
||||
// PASS 3:
|
||||
// now initialise all the associated properties
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
@@ -489,7 +577,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
}
|
||||
}
|
||||
|
||||
private <T> String errNothingRegistered() {
|
||||
private String errNothingRegistered() {
|
||||
return "There are no registered entities. If using query beans, that generates EbeanEntityRegister.java into " +
|
||||
"generated sources and is service loaded. If using module-info.java, then probably missing 'provides io.ebean.config.EntityClassRegister with EbeanEntityRegister' clause.";
|
||||
}
|
||||
@@ -531,6 +619,9 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
private void registerDescriptor(DeployBeanInfo<?> info) {
|
||||
BeanDescriptor<?> desc = new BeanDescriptor<>(this, info.getDescriptor());
|
||||
descMap.put(desc.type().getName(), desc);
|
||||
if (desc.isDocStoreMapped()) {
|
||||
descQueueMap.put(desc.docStoreQueueId(), desc);
|
||||
}
|
||||
for (BeanPropertyAssocMany<?> many : desc.propertiesMany()) {
|
||||
if (many.isElementCollection()) {
|
||||
elementDescriptors.add(many.elementDescriptor());
|
||||
@@ -600,6 +691,20 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
}
|
||||
}
|
||||
|
||||
private void readInheritedIdGenerators() {
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
|
||||
InheritInfo inheritInfo = descriptor.getInheritInfo();
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
DeployBeanInfo<?> rootBeanInfo = deployInfoMap.get(inheritInfo.getRoot().getType());
|
||||
PlatformIdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator();
|
||||
if (rootIdGen != null) {
|
||||
descriptor.setIdGenerator(rootIdGen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the BeanTable from the deployment information gathered so far.
|
||||
*/
|
||||
@@ -622,6 +727,9 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
secondaryPropsJoins(info);
|
||||
}
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
setInheritanceInfo(info);
|
||||
}
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
if (!info.isEmbedded()) {
|
||||
registerDescriptor(info);
|
||||
@@ -629,6 +737,28 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the inheritance info.
|
||||
*/
|
||||
private void setInheritanceInfo(DeployBeanInfo<?> info) {
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deployInfoMap.get(oneProp.getTargetType());
|
||||
if (assoc != null) {
|
||||
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deployInfoMap.get(manyProp.getTargetType());
|
||||
if (assoc != null) {
|
||||
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void secondaryPropsJoins(DeployBeanInfo<?> info) {
|
||||
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
|
||||
for (DeployBeanProperty prop : descriptor.propertiesBase()) {
|
||||
@@ -762,6 +892,13 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
orderProperty.setDbUpdateable(orderColumn.isUpdatable());
|
||||
orderProperty.setDbRead(true);
|
||||
orderProperty.setOwningType(targetDesc.getBeanType());
|
||||
final InheritInfo targetInheritInfo = targetDesc.getInheritInfo();
|
||||
if (targetInheritInfo != null) {
|
||||
for (InheritInfo child : targetInheritInfo.getChildren()) {
|
||||
final DeployBeanDescriptor<?> childDescriptor = deployInfoMap.get(child.getType()).getDescriptor();
|
||||
childDescriptor.setOrderColumn(orderProperty);
|
||||
}
|
||||
}
|
||||
targetDesc.setOrderColumn(orderProperty);
|
||||
}
|
||||
|
||||
@@ -877,6 +1014,13 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
// skip mapping check
|
||||
return;
|
||||
}
|
||||
DeployBeanDescriptor<?> targetDesc = targetDescriptor(prop);
|
||||
if (targetDesc.isDraftableElement()) {
|
||||
// automatically turning on orphan removal and CascadeType.ALL
|
||||
prop.setModifyListenMode(BeanCollection.ModifyListenMode.REMOVALS);
|
||||
prop.getCascadeInfo().setSaveDelete(true, true);
|
||||
}
|
||||
|
||||
if (prop.hasOrderColumn()) {
|
||||
makeOrderColumn(prop);
|
||||
}
|
||||
@@ -902,7 +1046,6 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
String mappedBy = prop.getMappedBy();
|
||||
|
||||
// get the mappedBy property
|
||||
DeployBeanDescriptor<?> targetDesc = targetDescriptor(prop);
|
||||
DeployBeanPropertyAssocOne<?> mappedAssocOne = mappedManyToOne(prop, targetDesc, mappedBy);
|
||||
DeployTableJoin tableJoin = prop.getTableJoin();
|
||||
if (!tableJoin.hasJoinColumns()) {
|
||||
@@ -944,6 +1087,9 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
// get the bean descriptor that holds the mappedBy property
|
||||
String mappedBy = prop.getMappedBy();
|
||||
if (mappedBy == null) {
|
||||
if (targetDescriptor(prop).isDraftable()) {
|
||||
prop.setIntersectionDraftTable();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -968,6 +1114,10 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
DeployTableJoin inverseJoin = new DeployTableJoin();
|
||||
mappedIntJoin.copyTo(inverseJoin, false, intTableName);
|
||||
prop.setInverseJoin(inverseJoin);
|
||||
|
||||
if (targetDesc.isDraftable()) {
|
||||
prop.setIntersectionDraftTable();
|
||||
}
|
||||
}
|
||||
|
||||
private DeployBeanPropertyAssocMany<?> mappedManyToMany(DeployBeanPropertyAssocMany<?> prop, String mappedBy, DeployBeanDescriptor<?> targetDesc) {
|
||||
@@ -1009,6 +1159,9 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
|
||||
// set bean controller, finder and listener
|
||||
setBeanControllerFinderListener(desc);
|
||||
deplyInherit.process(desc);
|
||||
desc.checkInheritanceMapping();
|
||||
|
||||
createProperties.createProperties(desc);
|
||||
DeployBeanInfo<T> info = new DeployBeanInfo<>(deployUtil, desc);
|
||||
readAnnotations.readInitial(info);
|
||||
@@ -1032,7 +1185,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
setConcurrencyMode(desc);
|
||||
}
|
||||
// generate the byte code
|
||||
createByteCode(desc);
|
||||
setAccessors(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1081,7 +1234,8 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
if (identityMode.isIdentity()) {
|
||||
// used when getGeneratedKeys is not supported (SQL Server 2000, SAP Hana)
|
||||
String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable());
|
||||
desc.setSelectLastInsertedId(selectLastInsertedId);
|
||||
String selectLastInsertedIdDraft = (!desc.isDraftable()) ? selectLastInsertedId : dbIdentity.getSelectLastInsertedId(desc.getDraftTable());
|
||||
desc.setSelectLastInsertedId(selectLastInsertedId, selectLastInsertedIdDraft);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1100,13 +1254,13 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, stepSize, seqName);
|
||||
}
|
||||
|
||||
private void createByteCode(DeployBeanDescriptor<?> deploy) {
|
||||
private void setAccessors(DeployBeanDescriptor<?> deploy) {
|
||||
// check to see if the bean supports EntityBean interface
|
||||
// generate a subclass if required
|
||||
setEntityBeanClass(deploy);
|
||||
confirmEnhanced(deploy);
|
||||
// use Code generation or Standard reflection to support
|
||||
// getter and setter methods
|
||||
setBeanReflect(deploy);
|
||||
setPropertyAccessors(deploy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1133,15 +1287,14 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
* and getting of properties. It is generally faster to use code generation
|
||||
* rather than reflection to do this.
|
||||
*/
|
||||
private void setBeanReflect(DeployBeanDescriptor<?> desc) {
|
||||
private void setPropertyAccessors(DeployBeanDescriptor<?> desc) {
|
||||
// Set the BeanReflectGetter and BeanReflectSetter that typically
|
||||
// use generated code. NB: Due to Bug 166 so now doing this for
|
||||
// abstract classes as well.
|
||||
BeanPropertiesReader reflectProps = new BeanPropertiesReader(desc.getBeanType());
|
||||
desc.setProperties(reflectProps.getProperties());
|
||||
BeanPropertiesReader reflectProps = new BeanPropertiesReader(desc.propertyNames());
|
||||
for (DeployBeanProperty prop : desc.propertiesAll()) {
|
||||
String propName = prop.getName();
|
||||
Integer pos = reflectProps.getPropertyIndex(propName);
|
||||
Integer pos = reflectProps.propertyIndex(propName);
|
||||
if (pos == null) {
|
||||
if (isPersistentField(prop)) {
|
||||
throw new IllegalStateException(
|
||||
@@ -1213,7 +1366,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
/**
|
||||
* Test the bean type to see if it implements EntityBean interface already.
|
||||
*/
|
||||
private void setEntityBeanClass(DeployBeanDescriptor<?> desc) {
|
||||
private void confirmEnhanced(DeployBeanDescriptor<?> desc) {
|
||||
Class<?> beanClass = desc.getBeanType();
|
||||
if (!hasEntityBeanInterface(beanClass)) {
|
||||
String msg = "Bean " + beanClass + " is not enhanced? Check packages specified in ebean.mf. If you are running in IDEA or " +
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebean.core.type.ScalarType;
|
||||
import io.ebeaninternal.server.cache.SpiCacheManager;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeanservice.docstore.api.DocStoreBeanAdapter;
|
||||
|
||||
/**
|
||||
* Provides a method to find a BeanDescriptor.
|
||||
@@ -56,6 +57,11 @@ public interface BeanDescriptorMap {
|
||||
*/
|
||||
IdBinder createIdBinder(BeanProperty id);
|
||||
|
||||
/**
|
||||
* Create a doc store specific adapter for this bean type.
|
||||
*/
|
||||
<T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor<T> descriptor, DeployBeanDescriptor<T> deploy);
|
||||
|
||||
/**
|
||||
* Return the scalarType for the given JDBC type.
|
||||
*/
|
||||
|
||||
@@ -36,6 +36,10 @@ import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.*;
|
||||
import io.ebeaninternal.util.ValueUtil;
|
||||
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyOptions;
|
||||
import io.ebeanservice.docstore.api.support.DocStructure;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.io.DataInput;
|
||||
@@ -139,6 +143,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
@SuppressWarnings("rawtypes")
|
||||
final ScalarType scalarType;
|
||||
|
||||
private final DocPropertyOptions docOptions;
|
||||
/**
|
||||
* The length or precision for DB column.
|
||||
*/
|
||||
@@ -166,6 +171,10 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
final boolean jsonDeserialize;
|
||||
private final boolean unmappedJson;
|
||||
private final boolean tenantId;
|
||||
private final boolean draft;
|
||||
private final boolean draftOnly;
|
||||
private final boolean draftDirty;
|
||||
private final boolean draftReset;
|
||||
private final boolean softDelete;
|
||||
private final String softDeleteDbSet;
|
||||
private final String softDeleteDbPredicate;
|
||||
@@ -192,6 +201,10 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
this.excludedFromHistory = deploy.isExcludedFromHistory();
|
||||
this.unmappedJson = deploy.isUnmappedJson();
|
||||
this.tenantId = deploy.isTenantId();
|
||||
this.draft = deploy.isDraft();
|
||||
this.draftDirty = deploy.isDraftDirty();
|
||||
this.draftOnly = deploy.isDraftOnly();
|
||||
this.draftReset = deploy.isDraftReset();
|
||||
this.secondaryTable = deploy.isSecondaryTable();
|
||||
if (secondaryTable) {
|
||||
this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin());
|
||||
@@ -230,6 +243,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
this.lob = isLobType(dbType);
|
||||
this.propertyType = deploy.getPropertyType();
|
||||
this.field = deploy.getField();
|
||||
this.docOptions = deploy.getDocPropertyOptions();
|
||||
this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), false, null);
|
||||
this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), dbEncrypted, dbColumn);
|
||||
this.elPrefix = deploy.getElPrefix();
|
||||
@@ -282,6 +296,10 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
this.aggregation = null;
|
||||
this.excludedFromHistory = source.excludedFromHistory;
|
||||
this.tenantId = source.tenantId;
|
||||
this.draft = source.draft;
|
||||
this.draftDirty = source.draftDirty;
|
||||
this.draftOnly = source.draftOnly;
|
||||
this.draftReset = source.draftReset;
|
||||
this.softDelete = source.softDelete;
|
||||
this.softDeleteDbSet = source.softDeleteDbSet;
|
||||
this.softDeleteDbPredicate = source.softDeleteDbPredicate;
|
||||
@@ -320,6 +338,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
this.lob = isLobType(dbType);
|
||||
this.propertyType = source.type();
|
||||
this.field = source.field();
|
||||
this.docOptions = source.docOptions;
|
||||
this.unmappedJson = source.unmappedJson;
|
||||
this.elPrefix = override.replace(source.elPrefix, source.dbColumn);
|
||||
this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn);
|
||||
@@ -464,7 +483,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
ctx.appendFormulaSelect(aggregation);
|
||||
} else if (formula) {
|
||||
ctx.appendFormulaSelect(sqlFormulaSelect);
|
||||
} else if (!isTransient) {
|
||||
} else if (!isTransient && !ignoreDraftOnlyProperty(ctx.isDraftQuery())) {
|
||||
if (secondaryTableJoin != null) {
|
||||
ctx.pushTableAlias(ctx.relativePrefix(secondaryTableJoinPrefix));
|
||||
}
|
||||
@@ -571,6 +590,17 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return local;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy/set the property value from the draft bean to the live bean.
|
||||
*/
|
||||
public void publish(EntityBean draftBean, EntityBean liveBean) {
|
||||
if (!version && !draftOnly) {
|
||||
// set property value from draft to live
|
||||
Object value = getValueIntercept(draftBean);
|
||||
setValueIntercept(liveBean, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB literal expression to set the deleted state to true.
|
||||
*/
|
||||
@@ -1060,8 +1090,16 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
/**
|
||||
* Return true if this property is loadable from a resultSet.
|
||||
*/
|
||||
public boolean isLoadProperty() {
|
||||
return !isTransient || formula;
|
||||
public boolean isLoadProperty(boolean draftQuery) {
|
||||
return !ignoreDraftOnlyProperty(draftQuery) && (!isTransient || formula);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a draftOnly property on a non-asDraft query and as such this
|
||||
* property should not be included in a sql query.
|
||||
*/
|
||||
private boolean ignoreDraftOnlyProperty(boolean draftQuery) {
|
||||
return draftOnly && !draftQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1210,6 +1248,36 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this property only exists on the draft table.
|
||||
*/
|
||||
public boolean isDraftOnly() {
|
||||
return draftOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this property is a boolean flag on a draftable bean
|
||||
* indicating if the instance is a draft or live bean.
|
||||
*/
|
||||
public boolean isDraft() {
|
||||
return draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this property is a boolean flag only on the draft table
|
||||
* indicating that when the draft is different from the published row.
|
||||
*/
|
||||
public boolean isDraftDirty() {
|
||||
return draftDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this property is reset/cleared on publish (on the draft bean).
|
||||
*/
|
||||
boolean isDraftReset() {
|
||||
return draftReset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this property is the soft delete property.
|
||||
*/
|
||||
@@ -1276,6 +1344,15 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append this property to the document store based on includeByDefault setting.
|
||||
*/
|
||||
public void docStoreInclude(boolean includeByDefault, DocStructure docStructure) {
|
||||
if (includeByDefault) {
|
||||
docStructure.addProperty(name);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isJsonSerialize() {
|
||||
return jsonSerialize;
|
||||
}
|
||||
@@ -1385,6 +1462,24 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the document mapping if this property is included for this index.
|
||||
*/
|
||||
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
|
||||
if (mapping.includesProperty(prefix, name)) {
|
||||
DocPropertyType type = scalarType.docType();
|
||||
DocPropertyOptions options = docOptions.copy();
|
||||
if (isKeywordType(type, options)) {
|
||||
type = DocPropertyType.KEYWORD;
|
||||
}
|
||||
mapping.add(new DocPropertyMapping(name, type, options));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isKeywordType(DocPropertyType type, DocPropertyOptions docOptions) {
|
||||
return type == DocPropertyType.TEXT && (docOptions.isCode() || id || discriminator);
|
||||
}
|
||||
|
||||
public void merge(EntityBean bean, EntityBean existing) {
|
||||
// do nothing unless Many property
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import io.ebean.Query;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.CoreLog;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
@@ -21,6 +24,9 @@ import io.ebeaninternal.server.query.STreePropertyAssoc;
|
||||
import io.ebeaninternal.server.query.STreeType;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import io.ebeanservice.docstore.api.support.DocStructure;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.util.ArrayList;
|
||||
@@ -39,6 +45,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
*/
|
||||
BeanDescriptor<T> targetDescriptor;
|
||||
IdBinder targetIdBinder;
|
||||
InheritInfo targetInheritInfo;
|
||||
String targetIdProperty;
|
||||
/**
|
||||
* Derived list of exported property and matching foreignKey
|
||||
@@ -62,6 +69,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
*/
|
||||
final BeanTable beanTable;
|
||||
final String mappedBy;
|
||||
private final String docStoreDoc;
|
||||
private final String extraWhere;
|
||||
private final int fetchPreference;
|
||||
private boolean saveRecurseSkippable;
|
||||
@@ -75,6 +83,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
this.extraWhere = InternString.intern(deploy.getExtraWhere());
|
||||
this.beanTable = deploy.getBeanTable();
|
||||
this.mappedBy = InternString.intern(deploy.getMappedBy());
|
||||
this.docStoreDoc = deploy.getDocStoreDoc();
|
||||
this.tableJoin = new TableJoin(deploy.getTableJoin());
|
||||
this.targetType = deploy.getTargetType();
|
||||
this.cascadeInfo = deploy.getCascadeInfo();
|
||||
@@ -91,6 +100,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
extraWhere = source.extraWhere;
|
||||
beanTable = source.beanTable;
|
||||
mappedBy = source.mappedBy;
|
||||
docStoreDoc = source.docStoreDoc;
|
||||
targetType = source.targetType;
|
||||
cascadeInfo = source.cascadeInfo;
|
||||
fetchPreference = source.fetchPreference;
|
||||
@@ -111,6 +121,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
targetDescriptor = descriptor.descriptor(targetType);
|
||||
if (!isTransient) {
|
||||
targetIdBinder = targetDescriptor.idBinder();
|
||||
targetInheritInfo = targetDescriptor.inheritInfo();
|
||||
saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable();
|
||||
if (!targetIdBinder.isComplexId()) {
|
||||
targetIdProperty = targetIdBinder.idSelect();
|
||||
@@ -299,6 +310,59 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
return extraWhere;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the elastic search doc for this embedded property.
|
||||
*/
|
||||
private String docStoreDoc() {
|
||||
return docStoreDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if and how the associated bean is included in the doc store document.
|
||||
*/
|
||||
@Override
|
||||
public void docStoreInclude(boolean includeByDefault, DocStructure docStructure) {
|
||||
String embeddedDoc = docStoreDoc();
|
||||
if (embeddedDoc == null) {
|
||||
// not annotated so use include by default
|
||||
// which is *ToOne included and *ToMany excluded
|
||||
if (includeByDefault) {
|
||||
docStoreIncludeByDefault(docStructure.doc());
|
||||
}
|
||||
} else {
|
||||
// explicitly annotated to be included
|
||||
if (embeddedDoc.isEmpty()) {
|
||||
embeddedDoc = "*";
|
||||
}
|
||||
// add in a nested way
|
||||
PathProperties embDoc = PathProperties.parse(embeddedDoc);
|
||||
docStructure.addNested(name, embDoc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the property in the document store by default.
|
||||
*/
|
||||
void docStoreIncludeByDefault(PathProperties pathProps) {
|
||||
pathProps.addToPath(null, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
|
||||
if (mapping.includesPath(prefix, name)) {
|
||||
String fullName = SplitName.add(prefix, name);
|
||||
|
||||
DocPropertyType type = isMany() ? DocPropertyType.LIST : DocPropertyType.OBJECT;
|
||||
DocPropertyMapping nested = new DocPropertyMapping(name, type);
|
||||
mapping.push(nested);
|
||||
targetDescriptor.docStoreMapping(mapping, fullName);
|
||||
mapping.pop();
|
||||
if (!nested.children().isEmpty()) {
|
||||
mapping.add(nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this association is updateable.
|
||||
*/
|
||||
|
||||
@@ -41,6 +41,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
* Join for manyToMany intersection table.
|
||||
*/
|
||||
private final TableJoin intersectionJoin;
|
||||
private final String intersectionPublishTable;
|
||||
private final String intersectionDraftTable;
|
||||
private final boolean orphanRemoval;
|
||||
private IntersectionTable intersectionTable;
|
||||
/**
|
||||
@@ -100,6 +102,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
this.mapKey = deploy.getMapKey();
|
||||
this.fetchOrderBy = deploy.getFetchOrderBy();
|
||||
this.intersectionJoin = deploy.createIntersectionTableJoin();
|
||||
if (intersectionJoin != null) {
|
||||
this.intersectionPublishTable = intersectionJoin.getTable();
|
||||
this.intersectionDraftTable = deploy.getIntersectionDraftTable();
|
||||
} else {
|
||||
this.intersectionPublishTable = null;
|
||||
this.intersectionDraftTable = null;
|
||||
}
|
||||
this.inverseJoin = deploy.createInverseTableJoin();
|
||||
this.modifyListenMode = deploy.getModifyListenMode();
|
||||
this.jsonHelp = descriptor.isJacksonCorePresent() ? new BeanPropertyAssocManyJsonHelp(this) : null;
|
||||
@@ -177,6 +186,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return manyToMany && !excludedFromHistory && descriptor.isHistorySupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void docStoreIncludeByDefault(PathProperties pathProps) {
|
||||
// by default not including "Many" properties in document store
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerColumn(BeanDescriptor<?> desc, String prefix) {
|
||||
if (targetDescriptor != null) {
|
||||
@@ -338,8 +352,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
}
|
||||
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
sqlHelp.addWhereParentIdIn(query, parentIds);
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds, boolean useDocStore) {
|
||||
if (useDocStore) {
|
||||
// assumes the ManyToOne property is included
|
||||
query.where().in(childMasterIdProperty, parentIds);
|
||||
} else {
|
||||
sqlHelp.addWhereParentIdIn(query, parentIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +375,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
|
||||
private IntersectionTable initIntersectionTable() {
|
||||
IntersectionBuilder row = new IntersectionBuilder(intersectionJoin.getTable());
|
||||
IntersectionBuilder row = new IntersectionBuilder(intersectionPublishTable, intersectionDraftTable);
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
row.addColumn(exportedProperty.getForeignDbColumn());
|
||||
}
|
||||
@@ -415,7 +434,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
StringBuilder sb = new StringBuilder(50).append("from "); // use from to stop parsing on table name
|
||||
SpiQuery<?> query = request.queryRequest().query();
|
||||
if (hasJoinTable()) {
|
||||
sb.append(intersectionJoin.getTable());
|
||||
sb.append(query.isAsDraft() ? intersectionDraftTable : intersectionPublishTable);
|
||||
} else {
|
||||
sb.append(targetDescriptor.baseTable(query.temporalMode()));
|
||||
}
|
||||
@@ -745,21 +764,37 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return row;
|
||||
}
|
||||
|
||||
public IntersectionRow buildManyToManyDeleteChildren(EntityBean parentBean) {
|
||||
String tableName = intersectionJoin.getTable();
|
||||
public IntersectionRow buildManyToManyDeleteChildren(EntityBean parentBean, boolean publish) {
|
||||
String tableName = publish ? intersectionPublishTable : intersectionDraftTable;
|
||||
IntersectionRow row = new IntersectionRow(tableName);
|
||||
buildExport(row, parentBean);
|
||||
return row;
|
||||
}
|
||||
|
||||
public IntersectionRow buildManyToManyMapBean(EntityBean parent, EntityBean other) {
|
||||
String tableName = intersectionJoin.getTable();
|
||||
public IntersectionRow buildManyToManyMapBean(EntityBean parent, EntityBean other, boolean publish) {
|
||||
String tableName = publish ? intersectionPublishTable : intersectionDraftTable;
|
||||
IntersectionRow row = new IntersectionRow(tableName);
|
||||
buildExport(row, parent);
|
||||
buildImport(row, other);
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the mapping of intersection table to associated draft table.
|
||||
*/
|
||||
void registerDraftIntersectionTable(BeanDescriptorInitContext initContext) {
|
||||
if (hasDraftIntersection()) {
|
||||
initContext.addDraftIntersection(intersectionPublishTable, intersectionDraftTable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the relationship is a ManyToMany with the intersection having an associated draft table.
|
||||
*/
|
||||
private boolean hasDraftIntersection() {
|
||||
return intersectionDraftTable != null && !intersectionDraftTable.equals(intersectionPublishTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the two side of a many to many to the given SqlUpdate.
|
||||
*/
|
||||
@@ -844,6 +879,55 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
jsonHelp.jsonRead(readJson, parentBean);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void publishMany(EntityBean draft, EntityBean live) {
|
||||
// collections will not be null due to enhancement
|
||||
BeanCollection<T> draftVal = (BeanCollection<T>) getValueIntercept(draft);
|
||||
BeanCollection<T> liveVal = (BeanCollection<T>) getValueIntercept(live);
|
||||
|
||||
// Organise the existing live beans into map keyed by id
|
||||
Map<Object, T> liveBeansAsMap = liveBeansAsMap(liveVal);
|
||||
|
||||
// publish from each draft to live bean creating new live beans as required
|
||||
draftVal.size();
|
||||
Collection<T> actualDetails = draftVal.actualDetails();
|
||||
for (T bean : actualDetails) {
|
||||
Object id = targetDescriptor.id(bean);
|
||||
T liveBean = liveBeansAsMap.remove(id);
|
||||
|
||||
if (isManyToMany()) {
|
||||
if (liveBean == null) {
|
||||
// add new relationship (Map not allowed here)
|
||||
liveVal.addBean(targetDescriptor.createReference(id, null));
|
||||
}
|
||||
} else {
|
||||
// recursively publish the OneToMany child bean
|
||||
T newLive = targetDescriptor.publish(bean, liveBean);
|
||||
if (liveBean == null) {
|
||||
// Map not allowed here
|
||||
liveVal.addBean(newLive);
|
||||
}
|
||||
}
|
||||
}
|
||||
// anything remaining should be deleted (so remove from modify aware collection)
|
||||
Collection<T> values = liveBeansAsMap.values();
|
||||
for (T value : values) {
|
||||
liveVal.removeBean(value);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Object, T> liveBeansAsMap(BeanCollection<?> liveVal) {
|
||||
liveVal.size();
|
||||
Collection<?> liveBeans = liveVal.actualDetails();
|
||||
Map<Object, T> liveMap = new LinkedHashMap<>();
|
||||
for (Object liveBean : liveBeans) {
|
||||
Object id = targetDescriptor.id(liveBean);
|
||||
liveMap.put(id, (T) liveBean);
|
||||
}
|
||||
return liveMap;
|
||||
}
|
||||
|
||||
public boolean isIncludeCascadeSave() {
|
||||
// Note ManyToMany always included as we always 'save'
|
||||
// the relationship via insert/delete of intersection table
|
||||
@@ -863,6 +947,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return sqlHelp.insertElementCollection();
|
||||
}
|
||||
|
||||
public boolean isTargetDocStoreMapped() {
|
||||
return targetDescriptor.isDocStoreMapped();
|
||||
}
|
||||
|
||||
void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
if (elementDescriptor != null) {
|
||||
elementDescriptor.jsonWriteMapEntry(ctx, entry);
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.cache.CachedBeanData;
|
||||
import io.ebeaninternal.server.cache.CachedBeanId;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
@@ -124,9 +125,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
// no imported or exported information
|
||||
} else if (!oneToOneExported) {
|
||||
importedId = createImportedId(this, targetDescriptor, tableJoin);
|
||||
if (importedId == null) {
|
||||
throw new IllegalStateException("Missing @Id property on " + targetDescriptor);
|
||||
}
|
||||
if (importedId.isScalar()) {
|
||||
// limit JoinColumn mapping to the @Id / primary key
|
||||
TableJoinColumn[] columns = tableJoin.columns();
|
||||
@@ -321,6 +319,15 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
|
||||
prefix = SplitName.add(prefix, name);
|
||||
if (!embedded) {
|
||||
InheritInfo inheritInfo = targetDescriptor.inheritInfo();
|
||||
if (inheritInfo != null) {
|
||||
// expect the discriminator column to be included in order
|
||||
// to determine the inheritance type so we add it to the
|
||||
// selectChain (so that it takes a position in the resultSet)
|
||||
String discriminatorColumn = inheritInfo.getDiscriminatorColumn();
|
||||
String discProperty = prefix + "." + discriminatorColumn;
|
||||
selectChain.add(discProperty);
|
||||
}
|
||||
if (targetIdBinder == null) {
|
||||
throw new IllegalStateException("No Id binding property for " + fullName()
|
||||
+ ". Probably a missing @OneToOne mapping annotation on this relationship?");
|
||||
@@ -413,11 +420,19 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
if (embedded) {
|
||||
return targetDescriptor.cacheEmbeddedBeanExtract((EntityBean) ap);
|
||||
} else if (targetInheritInfo != null) {
|
||||
return createCacheBeanId(ap);
|
||||
} else {
|
||||
return targetDescriptor.idProperty().getCacheDataValue((EntityBean) ap);
|
||||
}
|
||||
}
|
||||
|
||||
private Object createCacheBeanId(Object bean) {
|
||||
final BeanDescriptor<?> desc = targetDescriptor.descOf(bean.getClass());
|
||||
final Object id = desc.idProperty().getCacheDataValue((EntityBean) bean);
|
||||
return new CachedBeanId(desc.discValue(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(Object value) {
|
||||
return targetDescriptor.idBinder().cacheKey(value);
|
||||
@@ -431,11 +446,20 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
if (embedded) {
|
||||
setValue(bean, targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData, context));
|
||||
} else {
|
||||
setValue(bean, refBean(targetDescriptor, cacheData, context));
|
||||
if (cacheData instanceof CachedBeanId) {
|
||||
setValue(bean, refInheritBean((CachedBeanId) cacheData, context));
|
||||
} else {
|
||||
setValue(bean, refBean(targetDescriptor, cacheData, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object refInheritBean(CachedBeanId cacheId, PersistenceContext context) {
|
||||
final InheritInfo rowInheritInfo = targetInheritInfo.readType(cacheId.getDiscValue());
|
||||
return refBean(rowInheritInfo.desc(), cacheId.getId(), context);
|
||||
}
|
||||
|
||||
private Object refBean(BeanDescriptor<?> desc, Object id, PersistenceContext context) {
|
||||
if (id instanceof String) {
|
||||
id = desc.idProperty().scalarType.parse((String) id);
|
||||
@@ -694,7 +718,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
} else if (oneToOneExported) {
|
||||
return new AssocOneHelpRefExported(this);
|
||||
} else {
|
||||
return new AssocOneHelpRefSimple(this, embeddedPrefix);
|
||||
if (targetInheritInfo != null) {
|
||||
return new AssocOneHelpRefInherit(this);
|
||||
} else {
|
||||
return new AssocOneHelpRefSimple(this, embeddedPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,11 @@ public interface DbReadContext {
|
||||
*/
|
||||
SpiQuery.Mode queryMode();
|
||||
|
||||
/**
|
||||
* Return true if the underlying query is a 'asDraft' query.
|
||||
*/
|
||||
boolean isDraftQuery();
|
||||
|
||||
/**
|
||||
* Return true if this request disables lazy loading.
|
||||
*/
|
||||
|
||||
@@ -124,6 +124,11 @@ public interface DbSqlContext {
|
||||
*/
|
||||
int asOfTableCount();
|
||||
|
||||
/**
|
||||
* Return true if the query is a 'asDraft' query.
|
||||
*/
|
||||
boolean isDraftQuery();
|
||||
|
||||
/**
|
||||
* Start group by clause.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.annotation.DocCode;
|
||||
import io.ebean.annotation.DocProperty;
|
||||
import io.ebean.annotation.DocSortable;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyOptions;
|
||||
|
||||
/**
|
||||
* The options for document property collected when reading deployment mapping.
|
||||
*/
|
||||
public final class DeployDocPropertyOptions {
|
||||
|
||||
private static final DocPropertyOptions EMPTY = new DocPropertyOptions();
|
||||
|
||||
private DocPropertyOptions mapping;
|
||||
|
||||
private void createOptions() {
|
||||
if (mapping == null) {
|
||||
mapping = new DocPropertyOptions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the DocProperty deployment options.
|
||||
*/
|
||||
public void setDocProperty(DocProperty doc) {
|
||||
createOptions();
|
||||
mapping.apply(doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the DocSortable deployment options.
|
||||
*/
|
||||
public void setDocSortable(DocSortable doc) {
|
||||
createOptions();
|
||||
mapping.sortable(true);
|
||||
setStore(doc.store());
|
||||
setBoost(doc.boost());
|
||||
setNullValue(doc.nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the DocCode deployment options.
|
||||
*/
|
||||
public void setDocCode(DocCode doc) {
|
||||
createOptions();
|
||||
mapping.code(true);
|
||||
setStore(doc.store());
|
||||
setBoost(doc.boost());
|
||||
setNullValue(doc.nullValue());
|
||||
}
|
||||
|
||||
private void setNullValue(String value) {
|
||||
if (!value.isEmpty()) {
|
||||
mapping.nullValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBoost(float boost) {
|
||||
if (Float.compare(boost, 1.0F) != 0) {
|
||||
mapping.boost(boost);
|
||||
}
|
||||
}
|
||||
|
||||
private void setStore(boolean store) {
|
||||
if (store) {
|
||||
mapping.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocPropertyOptions with the collected options.
|
||||
*/
|
||||
public DocPropertyOptions create() {
|
||||
return (mapping == null) ? EMPTY : mapping;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployInheritInfo;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperties;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a node in the Inheritance tree. Holds information regarding Super Subclass support.
|
||||
*/
|
||||
public final class InheritInfo {
|
||||
|
||||
private final String discriminatorStringValue;
|
||||
private final Object discriminatorValue;
|
||||
private final String discriminatorColumn;
|
||||
private final int discriminatorType;
|
||||
private final int discriminatorLength;
|
||||
private final String columnDefn;
|
||||
private final String where;
|
||||
private final Class<?> type;
|
||||
private final List<InheritInfo> children = new ArrayList<>();
|
||||
/**
|
||||
* Map of discriminator values to InheritInfo.
|
||||
*/
|
||||
private final HashMap<String, InheritInfo> discMap;
|
||||
/**
|
||||
* Map of class types to InheritInfo (taking into account subclass proxy classes).
|
||||
*/
|
||||
private final HashMap<String, InheritInfo> typeMap;
|
||||
private final InheritInfo parent;
|
||||
private final InheritInfo root;
|
||||
private BeanDescriptor<?> descriptor;
|
||||
|
||||
public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) {
|
||||
this.parent = parent;
|
||||
this.type = deploy.getType();
|
||||
this.discriminatorColumn = InternString.intern(deploy.getColumnName(parent));
|
||||
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
|
||||
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
|
||||
this.discriminatorType = deploy.getDiscriminatorType(parent);
|
||||
this.discriminatorLength = deploy.getColumnLength(parent);
|
||||
this.columnDefn = deploy.getColumnDefn();
|
||||
this.where = InternString.intern(deploy.getWhere());
|
||||
if (r == null) {
|
||||
// this is a root node
|
||||
root = this;
|
||||
discMap = new HashMap<>();
|
||||
typeMap = new HashMap<>();
|
||||
registerWithRoot(this);
|
||||
} else {
|
||||
this.root = r;
|
||||
// register with the root node...
|
||||
discMap = null;
|
||||
typeMap = null;
|
||||
root.registerWithRoot(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit all the children in the inheritance tree.
|
||||
*/
|
||||
public void visitChildren(InheritInfoVisitor visitor) {
|
||||
for (InheritInfo child : children) {
|
||||
visitor.visit(child);
|
||||
child.visitChildren(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append check constraint values for the entire inheritance hierarchy.
|
||||
*/
|
||||
public void appendCheckConstraintValues(final String propertyName, final Set<String> checkConstraintValues) {
|
||||
visitChildren(inheritInfo -> {
|
||||
BeanProperty prop = inheritInfo.desc().beanProperty(propertyName);
|
||||
if (prop != null) {
|
||||
Set<String> values = prop.dbCheckConstraintValues();
|
||||
if (values != null) {
|
||||
checkConstraintValues.addAll(values);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if anything in the inheritance hierarchy has a relationship with a save cascade on
|
||||
* it.
|
||||
*/
|
||||
boolean isSaveRecurseSkippable() {
|
||||
return root.isNodeSaveRecurseSkippable();
|
||||
}
|
||||
|
||||
private boolean isNodeSaveRecurseSkippable() {
|
||||
if (!descriptor.isSaveRecurseSkippable()) {
|
||||
return false;
|
||||
}
|
||||
for (InheritInfo child : children) {
|
||||
if (!child.isNodeSaveRecurseSkippable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if anything in the inheritance hierarchy has a relationship with a delete cascade
|
||||
* on it.
|
||||
*/
|
||||
boolean isDeleteRecurseSkippable() {
|
||||
return root.isNodeDeleteRecurseSkippable();
|
||||
}
|
||||
|
||||
private boolean isNodeDeleteRecurseSkippable() {
|
||||
if (!descriptor.isDeleteRecurseSkippable()) {
|
||||
return false;
|
||||
}
|
||||
for (InheritInfo child : children) {
|
||||
if (!child.isNodeDeleteRecurseSkippable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the descriptor for this node.
|
||||
*/
|
||||
public void setDescriptor(BeanDescriptor<?> descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated BeanDescriptor for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> desc() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the local properties for this node in the hierarchy.
|
||||
*/
|
||||
public BeanProperty[] localProperties() {
|
||||
return descriptor.propertiesLocal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the children.
|
||||
*/
|
||||
public List<InheritInfo> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this node has children.
|
||||
* <p>
|
||||
* When an inheritance node has no children then we don't need
|
||||
* the discriminator column as the type is effectively known.
|
||||
*/
|
||||
public boolean hasChildren() {
|
||||
return !children.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bean property additionally looking in the sub types.
|
||||
*/
|
||||
BeanProperty findSubTypeProperty(String propertyName) {
|
||||
BeanProperty prop;
|
||||
for (InheritInfo childInfo : children) {
|
||||
// recursively search this child bean descriptor
|
||||
prop = childInfo.desc().findProperty(propertyName);
|
||||
if (prop != null) {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the local properties for each sub class below this one.
|
||||
*/
|
||||
public void addChildrenProperties(SqlTreeProperties selectProps) {
|
||||
for (InheritInfo childInfo : children) {
|
||||
selectProps.add(childInfo.descriptor.propertiesLocal());
|
||||
childInfo.addChildrenProperties(selectProps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated InheritInfo for this DB row read.
|
||||
*/
|
||||
public InheritInfo readType(DbReadContext ctx) throws SQLException {
|
||||
return readType(ctx.dataReader().getString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated InheritInfo for this discriminator value.
|
||||
*/
|
||||
InheritInfo readType(String discValue) {
|
||||
if (discValue == null) {
|
||||
return null;
|
||||
}
|
||||
InheritInfo typeInfo = root.getType(discValue);
|
||||
if (typeInfo == null) {
|
||||
throw new PersistenceException("Inheritance type for discriminator value [" + discValue + "] was not found?");
|
||||
}
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated InheritInfo for this bean type.
|
||||
*/
|
||||
public InheritInfo readType(Class<?> beanType) {
|
||||
InheritInfo typeInfo = root.getTypeByClass(beanType);
|
||||
if (typeInfo == null) {
|
||||
throw new PersistenceException("Inheritance type for bean type [" + beanType.getName() + "] was not found?");
|
||||
}
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EntityBean for this type.
|
||||
*/
|
||||
public EntityBean createEntityBean() {
|
||||
return descriptor.createEntityBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IdBinder for this type.
|
||||
*/
|
||||
public IdBinder getIdBinder() {
|
||||
return descriptor.idBinder();
|
||||
}
|
||||
|
||||
/**
|
||||
* return the type.
|
||||
*/
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root node of the tree.
|
||||
* <p>
|
||||
* The root has a map of discriminator values to types.
|
||||
* </p>
|
||||
*/
|
||||
public InheritInfo getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent node.
|
||||
*/
|
||||
public InheritInfo getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is the root node.
|
||||
*/
|
||||
public boolean isRoot() {
|
||||
return parent == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is considered a concrete type in the inheritance hierarchy.
|
||||
*/
|
||||
public boolean isConcrete() {
|
||||
return !Modifier.isAbstract(type.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* For a discriminator get the inheritance information for this tree.
|
||||
*/
|
||||
public InheritInfo getType(String discValue) {
|
||||
return discMap.get(discValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the InheritInfo for the given bean type.
|
||||
*/
|
||||
private InheritInfo getTypeByClass(Class<?> beanType) {
|
||||
return typeMap.get(beanType.getName());
|
||||
}
|
||||
|
||||
private void registerWithRoot(InheritInfo info) {
|
||||
if (info.getDiscriminatorStringValue() != null) {
|
||||
String stringDiscValue = info.getDiscriminatorStringValue();
|
||||
discMap.put(stringDiscValue, info);
|
||||
}
|
||||
typeMap.put(info.getType().getName(), info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child node.
|
||||
*/
|
||||
public void addChild(InheritInfo childInfo) {
|
||||
children.add(childInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the derived where for the discriminator.
|
||||
*/
|
||||
public String getWhere() {
|
||||
return where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column name of the discriminator.
|
||||
*/
|
||||
public String getDiscriminatorColumn() {
|
||||
return discriminatorColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sql type of the discriminator value.
|
||||
*/
|
||||
public int getDiscriminatorType() {
|
||||
return discriminatorType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the discriminator column.
|
||||
*/
|
||||
public int getColumnLength() {
|
||||
return discriminatorLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the explicit column definition.
|
||||
*/
|
||||
public String getColumnDefn() {
|
||||
return columnDefn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the discriminator value for this node.
|
||||
*/
|
||||
String getDiscriminatorStringValue() {
|
||||
return discriminatorStringValue;
|
||||
}
|
||||
|
||||
public Object getDiscriminatorValue() {
|
||||
return discriminatorValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InheritInfo " + type.getName() + " disc:" + discriminatorStringValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
/**
|
||||
* Used to visit all the InheritInfo in a single inheritance hierarchy.
|
||||
*/
|
||||
public interface InheritInfoVisitor {
|
||||
|
||||
/**
|
||||
* visit the InheritInfo for this node.
|
||||
*/
|
||||
void visit(InheritInfo inheritInfo);
|
||||
|
||||
}
|
||||
@@ -8,11 +8,13 @@ import java.util.List;
|
||||
*/
|
||||
public final class IntersectionBuilder {
|
||||
|
||||
private final String table;
|
||||
private final String publishTable;
|
||||
private final String draftTable;
|
||||
private final List<String> columns = new ArrayList<>();
|
||||
|
||||
IntersectionBuilder(String table) {
|
||||
this.table = table;
|
||||
IntersectionBuilder(String publishTable, String draftTable) {
|
||||
this.publishTable = publishTable;
|
||||
this.draftTable = draftTable;
|
||||
}
|
||||
|
||||
public void addColumn(String column) {
|
||||
@@ -20,9 +22,18 @@ public final class IntersectionBuilder {
|
||||
}
|
||||
|
||||
public IntersectionTable build() {
|
||||
String insertSql = insertSql(table);
|
||||
String deleteSql = deleteSql(table);
|
||||
return new IntersectionTable(insertSql, deleteSql);
|
||||
String insertSql = insertSql(publishTable);
|
||||
String deleteSql = deleteSql(publishTable);
|
||||
String draftInsertSql;
|
||||
String draftDeleteSql;
|
||||
if (publishTable.equals(draftTable)) {
|
||||
draftInsertSql = insertSql;
|
||||
draftDeleteSql = deleteSql;
|
||||
} else {
|
||||
draftInsertSql = insertSql(draftTable);
|
||||
draftDeleteSql = deleteSql(draftTable);
|
||||
}
|
||||
return new IntersectionTable(insertSql, deleteSql, draftInsertSql, draftDeleteSql);
|
||||
}
|
||||
|
||||
private String insertSql(String tableName) {
|
||||
|
||||
@@ -7,24 +7,28 @@ public final class IntersectionTable {
|
||||
|
||||
private final String insertSql;
|
||||
private final String deleteSql;
|
||||
private final String draftInsertSql;
|
||||
private final String draftDeleteSql;
|
||||
|
||||
IntersectionTable(String insertSql, String deleteSql) {
|
||||
IntersectionTable(String insertSql, String deleteSql, String draftInsertSql, String draftDeleteSql) {
|
||||
this.insertSql = insertSql;
|
||||
this.deleteSql = deleteSql;
|
||||
this.draftInsertSql = draftInsertSql;
|
||||
this.draftDeleteSql = draftDeleteSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SqlUpdate for inserting into the intersection table.
|
||||
*/
|
||||
public SqlUpdate insert(Database server) {
|
||||
return server.sqlUpdate(insertSql);
|
||||
public SqlUpdate insert(Database server, boolean draft) {
|
||||
return server.sqlUpdate(draft ? draftInsertSql : insertSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SqlUpdate for deleting from the intersection table.
|
||||
*/
|
||||
public SqlUpdate delete(Database server) {
|
||||
return server.sqlUpdate(deleteSql);
|
||||
public SqlUpdate delete(Database server, boolean draft) {
|
||||
return server.sqlUpdate(draft ? draftDeleteSql : deleteSql);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ public final class TableJoin {
|
||||
*/
|
||||
private final SqlJoinType type;
|
||||
|
||||
private final InheritInfo inheritInfo;
|
||||
|
||||
/**
|
||||
* Columns as an array.
|
||||
*/
|
||||
@@ -47,6 +49,7 @@ public final class TableJoin {
|
||||
this.extraWhere = deploy.getExtraWhere();
|
||||
this.table = InternString.intern(deploy.getTable());
|
||||
this.type = deploy.getType();
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
DeployTableJoinColumn[] deployCols = deploy.columns();
|
||||
this.columns = new TableJoinColumn[deployCols.length];
|
||||
for (int i = 0; i < deployCols.length; i++) {
|
||||
@@ -60,6 +63,7 @@ public final class TableJoin {
|
||||
this.extraWhere = source.extraWhere;
|
||||
this.table = source.table;
|
||||
this.type = source.type;
|
||||
this.inheritInfo = source.inheritInfo;
|
||||
this.columns = new TableJoinColumn[1];
|
||||
this.columns[0] = source.columns[0].withOverrideColumn(overrideColumn);
|
||||
this.queryHash = calcQueryHash();
|
||||
|
||||
+247
-22
@@ -1,6 +1,8 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
import io.ebean.annotation.DocStore;
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.Identity;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.TableName;
|
||||
@@ -8,6 +10,7 @@ import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import io.ebean.event.*;
|
||||
import io.ebean.event.changelog.ChangeLogFilter;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
import io.ebeaninternal.server.core.CacheOptions;
|
||||
@@ -17,7 +20,12 @@ import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
|
||||
import io.ebeaninternal.server.idgen.UuidV1IdGenerator;
|
||||
import io.ebeaninternal.server.idgen.UuidV1RndIdGenerator;
|
||||
import io.ebeaninternal.server.idgen.UuidV4IdGenerator;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -25,6 +33,10 @@ import java.util.*;
|
||||
*/
|
||||
public class DeployBeanDescriptor<T> {
|
||||
|
||||
private static final Map<String, String> EMPTY_NAMED_QUERY = new HashMap<>();
|
||||
|
||||
private static final Map<String, SpiRawSql> EMPTY_RAW_MAP = new HashMap<>();
|
||||
|
||||
private static class PropOrder implements Comparator<DeployBeanProperty> {
|
||||
|
||||
@Override
|
||||
@@ -35,14 +47,14 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private static final PropOrder PROP_ORDER = new PropOrder();
|
||||
|
||||
private static final String I_SCALAOBJECT = "scala.ScalaObject";
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final BeanDescriptorManager manager;
|
||||
/**
|
||||
* Map of BeanProperty Linked so as to preserve order.
|
||||
*/
|
||||
private LinkedHashMap<String, DeployBeanProperty> propMap = new LinkedHashMap<>();
|
||||
private Map<String, SpiRawSql> namedRawSql;
|
||||
private Map<String, String> namedQuery;
|
||||
private EntityType entityType;
|
||||
private DeployBeanPropertyAssocOne<?> unidirectional;
|
||||
private DeployBeanProperty orderColumn;
|
||||
@@ -58,6 +70,7 @@ public class DeployBeanDescriptor<T> {
|
||||
* Used with Identity columns but no getGeneratedKeys support.
|
||||
*/
|
||||
private String selectLastInsertedId;
|
||||
private String selectLastInsertedIdDraft;
|
||||
/**
|
||||
* The concurrency mode for beans of this type.
|
||||
*/
|
||||
@@ -70,8 +83,12 @@ public class DeployBeanDescriptor<T> {
|
||||
private String baseTable;
|
||||
private String baseTableAsOf;
|
||||
private String baseTableVersionsBetween;
|
||||
private String draftTable;
|
||||
private String[] dependentTables;
|
||||
private boolean historySupport;
|
||||
private boolean readAuditing;
|
||||
private boolean draftable;
|
||||
private boolean draftableElement;
|
||||
private TableName baseTableFull;
|
||||
private String[] properties;
|
||||
/**
|
||||
@@ -91,11 +108,25 @@ public class DeployBeanDescriptor<T> {
|
||||
/**
|
||||
* Inheritance information. Server side only.
|
||||
*/
|
||||
private InheritInfo inheritInfo;
|
||||
private String name;
|
||||
private ChangeLogFilter changeLogFilter;
|
||||
private String dbComment;
|
||||
private PartitionMeta partitionMeta;
|
||||
private TablespaceMeta tablespaceMeta;
|
||||
/**
|
||||
* One of NONE, INDEX or EMBEDDED.
|
||||
*/
|
||||
private boolean docStoreMapped;
|
||||
private DocStore docStore;
|
||||
private PathProperties docStorePathProperties;
|
||||
private String docStoreQueueId;
|
||||
private String docStoreIndexName;
|
||||
private String docStoreIndexType;
|
||||
private DocStoreMode docStorePersist;
|
||||
private DocStoreMode docStoreInsert;
|
||||
private DocStoreMode docStoreUpdate;
|
||||
private DocStoreMode docStoreDelete;
|
||||
private DeployBeanProperty idProperty;
|
||||
private TableJoin primaryKeyJoin;
|
||||
|
||||
@@ -108,10 +139,30 @@ public class DeployBeanDescriptor<T> {
|
||||
this.beanType = beanType;
|
||||
}
|
||||
|
||||
private String[] readPropertyNames() {
|
||||
try {
|
||||
Field field = beanType.getField("_ebean_props");
|
||||
return (String[]) field.get(null);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Error getting _ebean_props field on type " + beanType, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPropertyNames(String[] properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public String[] propertyNames() {
|
||||
if (properties == null) {
|
||||
properties = readPropertyNames();
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IdClass to use.
|
||||
*/
|
||||
public void setIdClass(Class idClass) {
|
||||
public void setIdClass(Class<?> idClass) {
|
||||
this.idClass = idClass;
|
||||
}
|
||||
|
||||
@@ -164,6 +215,20 @@ public class DeployBeanDescriptor<T> {
|
||||
return historySupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read auditing on for this entity bean.
|
||||
*/
|
||||
public void setReadAuditing() {
|
||||
readAuditing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if read auditing is on for this entity bean.
|
||||
*/
|
||||
public boolean isReadAuditing() {
|
||||
return readAuditing;
|
||||
}
|
||||
|
||||
public void setDbComment(String dbComment) {
|
||||
this.dbComment = dbComment;
|
||||
}
|
||||
@@ -194,23 +259,46 @@ public class DeployBeanDescriptor<T> {
|
||||
return tablespaceMeta;
|
||||
}
|
||||
|
||||
public boolean isScalaObject() {
|
||||
Class<?>[] interfaces = beanType.getInterfaces();
|
||||
for (Class<?> anInterface : interfaces) {
|
||||
String iname = anInterface.getName();
|
||||
if (I_SCALAOBJECT.equals(iname)) {
|
||||
return true;
|
||||
}
|
||||
public void setDraftable() {
|
||||
draftable = true;
|
||||
}
|
||||
|
||||
public boolean isDraftable() {
|
||||
return draftable;
|
||||
}
|
||||
|
||||
public void setDraftableElement() {
|
||||
draftable = true;
|
||||
draftableElement = true;
|
||||
}
|
||||
|
||||
public boolean isDraftableElement() {
|
||||
return draftableElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the top level doc store deployment information.
|
||||
*/
|
||||
public void readDocStore(DocStore docStore) {
|
||||
this.docStore = docStore;
|
||||
docStoreMapped = true;
|
||||
docStoreQueueId = docStore.queueId();
|
||||
docStoreIndexName = docStore.indexName();
|
||||
docStoreIndexType = docStore.indexType();
|
||||
docStorePersist = docStore.persist();
|
||||
docStoreInsert = docStore.insert();
|
||||
docStoreUpdate = docStore.update();
|
||||
docStoreDelete = docStore.delete();
|
||||
String doc = docStore.doc();
|
||||
if (!doc.isEmpty()) {
|
||||
docStorePathProperties = PathProperties.parse(doc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public DeployBeanTable createDeployBeanTable() {
|
||||
|
||||
DeployBeanTable beanTable = new DeployBeanTable(getBeanType());
|
||||
beanTable.setBaseTable(baseTable);
|
||||
beanTable.setIdProperty(idProperty());
|
||||
|
||||
return beanTable;
|
||||
}
|
||||
|
||||
@@ -278,14 +366,6 @@ public class DeployBeanDescriptor<T> {
|
||||
this.identityMode.setIdType(type);
|
||||
}
|
||||
|
||||
public String[] getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(String[] props) {
|
||||
this.properties = props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class type this BeanDescriptor describes.
|
||||
*/
|
||||
@@ -301,6 +381,21 @@ public class DeployBeanDescriptor<T> {
|
||||
return changeLogFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Inheritance mapping information. This will be null if this type
|
||||
* of bean is not involved in any ORM inheritance mapping.
|
||||
*/
|
||||
public InheritInfo getInheritInfo() {
|
||||
return inheritInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ORM inheritance mapping information.
|
||||
*/
|
||||
public void setInheritInfo(InheritInfo inheritInfo) {
|
||||
this.inheritInfo = inheritInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that this type invalidates query caches.
|
||||
*/
|
||||
@@ -485,6 +580,10 @@ public class DeployBeanDescriptor<T> {
|
||||
postConstructListeners.add(postConstructListener);
|
||||
}
|
||||
|
||||
public String getDraftTable() {
|
||||
return draftTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* For view based entity return the dependant tables.
|
||||
*/
|
||||
@@ -538,6 +637,7 @@ public class DeployBeanDescriptor<T> {
|
||||
this.baseTable = baseTableFull == null ? null : baseTableFull.getQualifiedName();
|
||||
this.baseTableAsOf = baseTable + asOfSuffix;
|
||||
this.baseTableVersionsBetween = baseTable + versionsBetweenSuffix;
|
||||
this.draftTable = (draftable) ? baseTable + "_draft" : baseTable;
|
||||
}
|
||||
|
||||
public void sortProperties() {
|
||||
@@ -611,11 +711,16 @@ public class DeployBeanDescriptor<T> {
|
||||
return selectLastInsertedId;
|
||||
}
|
||||
|
||||
public String getSelectLastInsertedIdDraft() {
|
||||
return selectLastInsertedIdDraft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SQL used to return the last inserted Id.
|
||||
*/
|
||||
public void setSelectLastInsertedId(String selectLastInsertedId) {
|
||||
public void setSelectLastInsertedId(String selectLastInsertedId, String selectLastInsertedIdDraft) {
|
||||
this.selectLastInsertedId = selectLastInsertedId;
|
||||
this.selectLastInsertedIdDraft = selectLastInsertedIdDraft;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -832,6 +937,126 @@ public class DeployBeanDescriptor<T> {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the mapping for class inheritance
|
||||
*/
|
||||
public void checkInheritanceMapping() {
|
||||
if (inheritInfo == null) {
|
||||
checkInheritance(getBeanType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check valid mapping annotations on the class hierarchy.
|
||||
*/
|
||||
private void checkInheritance(Class<?> beanType) {
|
||||
|
||||
Class<?> parent = beanType.getSuperclass();
|
||||
if (parent == null || Object.class.equals(parent)) {
|
||||
// all good
|
||||
return;
|
||||
}
|
||||
if (parent.isAnnotationPresent(Entity.class)) {
|
||||
String msg = "Checking " + getBeanType() + " and found " + parent + " that has @Entity annotation rather than MappedSuperclass?";
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
if (parent.isAnnotationPresent(MappedSuperclass.class)) {
|
||||
// continue checking
|
||||
checkInheritance(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public PathProperties getDocStorePathProperties() {
|
||||
return docStorePathProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type is mapped for a doc store.
|
||||
*/
|
||||
public boolean isDocStoreMapped() {
|
||||
return docStoreMapped;
|
||||
}
|
||||
|
||||
public String getDocStoreQueueId() {
|
||||
return docStoreQueueId;
|
||||
}
|
||||
|
||||
public String getDocStoreIndexName() {
|
||||
return docStoreIndexName;
|
||||
}
|
||||
|
||||
public String getDocStoreIndexType() {
|
||||
return docStoreIndexType;
|
||||
}
|
||||
|
||||
public DocStore getDocStore() {
|
||||
return docStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocStore index behavior for bean inserts.
|
||||
*/
|
||||
public DocStoreMode getDocStoreInsertEvent() {
|
||||
return getDocStoreIndexEvent(docStoreInsert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocStore index behavior for bean updates.
|
||||
*/
|
||||
public DocStoreMode getDocStoreUpdateEvent() {
|
||||
return getDocStoreIndexEvent(docStoreUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocStore index behavior for bean deletes.
|
||||
*/
|
||||
public DocStoreMode getDocStoreDeleteEvent() {
|
||||
return getDocStoreIndexEvent(docStoreDelete);
|
||||
}
|
||||
|
||||
private DocStoreMode getDocStoreIndexEvent(DocStoreMode mostSpecific) {
|
||||
if (!docStoreMapped) {
|
||||
return DocStoreMode.IGNORE;
|
||||
}
|
||||
if (mostSpecific != DocStoreMode.DEFAULT) return mostSpecific;
|
||||
if (docStorePersist != DocStoreMode.DEFAULT) return docStorePersist;
|
||||
return config.getDocStoreConfig().getPersist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named ORM queries.
|
||||
*/
|
||||
public Map<String, String> getNamedQuery() {
|
||||
return (namedQuery != null) ? namedQuery : EMPTY_NAMED_QUERY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a named query.
|
||||
*/
|
||||
public void addNamedQuery(String name, String query) {
|
||||
if (namedQuery == null) {
|
||||
namedQuery = new LinkedHashMap<>();
|
||||
}
|
||||
namedQuery.put(name, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named RawSql queries.
|
||||
*/
|
||||
public Map<String, SpiRawSql> getNamedRawSql() {
|
||||
return (namedRawSql != null) ? namedRawSql : EMPTY_RAW_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a named RawSql from ebean.xml file.
|
||||
*/
|
||||
public void addRawSql(String name, SpiRawSql rawSql) {
|
||||
if (namedRawSql == null) {
|
||||
namedRawSql = new HashMap<>();
|
||||
}
|
||||
namedRawSql.put(name, rawSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the aggregation formula into expressions with table alias placeholders.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user