Compare commits

..
53 Commits
Author SHA1 Message Date
Rob Bygrave b50ce5fc3f Version 13.25.0 2023-11-06 20:08:00 +13:00
Rob Bygrave 624e92840c Adjust "for testing purposes" log messages 2023-11-06 12:55:22 +13:00
Rob Bygrave b03adc412c Adjust "for testing purposes" log messages 2023-11-06 12:52:35 +13:00
Rob BygraveandGitHub e794f5e916 Merge pull request #3264 from ebean-orm/feature/DatabaseBuilder_Settings
Fix to return DataSourceBuilder.Settings for getDataSourceConfig() an…
2023-11-06 12:50:37 +13:00
Rob Bygrave a2551ce718 Bump ebean-datasource to 8.9 2023-11-06 12:50:14 +13:00
Rob Bygrave 5b0a3607f6 Fix to return DataSourceBuilder.Settings for getDataSourceConfig() and getReadOnlyDataSourceConfig()
That is, existing code using DataSourceConfig has access to the getter
methods of DataSourceConfig. The DataSourceBuilder interface only has
the setter methods and DataSourceBuilder.Settings has both getters and
setter methods.

That is, the refactor to extract the DataSourceBuilder interface also
split off the getter methods to the DataSourceBuilder.Settings interface
(because most of the time when using the builder we only need the
setter methods and effectively hiding the getter methods behind the settings()
is useful to simplify the API for users).
2023-11-06 12:14:27 +13:00
Rob Bygrave bcf7bd17be Version 13.14.0-jakarta 2023-11-05 22:21:20 +13:00
Rob BygraveandGitHub 102d8d5979 Merge pull request #3263 from ebean-orm/feature/DatabaseBuilder-take-2
DatabaseBuilder take 2
2023-11-05 22:11:26 +13:00
Rob Bygrave 3edc232003 Update test BeanDescriptor_registerTest with DatabaseBuilder use 2023-11-05 22:03:25 +13:00
Rob Bygrave 043dc5c51f Add setters without the set prefix to DatabaseBuilder with deprecation
So add `name(String name)` as preferred to `setName(String name)` etc
2023-11-05 22:01:02 +13:00
Rob Bygrave e2d666df35 Use fluid setters for DatabaseConfig and DatabaseBuilder 2023-11-04 13:05:22 +13:00
Rob Bygrave 314e0a14b8 Move the internal serviceObjectKey() methods back into DatabaseConfig
They were incorrectly extracted to the interface as default methods
2023-11-03 23:00:29 +13:00
Rob Bygrave 1f9cf1e4b7 Split DatabaseBuilder interface separating the setters n getters adding DatabaseBuilder.Settings
So DatabaseBuilder.Settings has all the getters for code looking to read
the configuration that has been set. So DatabaseBuilder now just has the
setter methods.

Use DatabaseBuilder.settings() to access the settings and read the config
that has been set.
2023-11-03 22:58:11 +13:00
Rob Bygrave 12029ed06e Refactor extract interface DatabaseBuilder from DatabaseConfig
First step in supporting the builder pattern for creating Database
2023-11-03 21:17:55 +13:00
Rob BygraveandGitHub 89741b9312 Merge pull request #3262 from ebean-orm/feature/improve-javadoc-deprecated
Improve the javadoc for deprecated methods (to assist IDE quick-fix actions)
2023-11-03 20:05:41 +13:00
Rob Bygrave ae962c2e08 Improve the javadoc for deprecated methods (to assist IDE quick-fix actions) 2023-11-03 20:04:25 +13:00
Rob BygraveandGitHub 4e3501972a Merge pull request #3260 from ebean-orm/feature/bump-migration2
Bump ebean-migration to 13.11.1 (with fastMode option and index file support)
2023-11-03 19:52:00 +13:00
Rob Bygrave a65ba41850 Bump ebean-migration to 13.11.1 (with fastMode option and index file support) 2023-11-03 18:33:02 +13:00
Rob Bygrave 3b11c1cb6b Merge branch 'master' of github.com:ebean-orm/ebean 2023-11-03 18:31:31 +13:00
Rob Bygrave 41185eeb5c Bump ebean-datasource to 8.8 with DataSourceBuilder
First step in migrating the ebean-datasource API to use builder pattern.
DataSourceConfig (concrete type) migrating to DataSourceBuilder (interface).

A followup step will deprecate the use of DataSourceConfig.
2023-11-03 18:31:11 +13:00
Rob BygraveandGitHub e97ff54ab2 Merge pull request #3259 from ebean-orm/feature/loadMode_safer
Change loadMode to be 'safer' (for Grafana etc). e.g. change "(+lazy) " to "__lazy"
2023-11-02 09:00:38 +13:00
robin.bygrave 97fae09c52 Tidy SpiQuery.setProfilePath() internal implementation 2023-11-01 15:48:27 +13:00
robin.bygrave 8b0f58b3c2 Change loadMode to be 'safer' (for Grafana etc). e.g. change "(+lazy)" to "__lazy"
The loadMode ends up as a suffix to some query metrics.

For example "foo.findIt_baz(+lazy)", and the (+lazy) part isn't safe/friendly to tools
like Grafana. So change to instead of (+lazy) use __lazy.
2023-11-01 15:20:18 +13:00
Rob Bygrave 70eb557929 Version 13.23.2-jakarta 2023-10-27 22:13:05 +13:00
Rob Bygrave fdb3a3759b Bump ebean-agent to 13.23.2 2023-10-27 22:10:40 +13:00
Rob BygraveandGitHub 7a5e8d0e17 Merge pull request #3256 from ebean-orm/feature/upgrade-antlr4-4_13_1
Upgrade ANTLR4 from version 4.8-1 to 4.13.1
2023-10-27 21:45:37 +13:00
robin.bygrave a0748f8139 Upgrade ANTLR4 from version 4.8-1 to 4.13.1 2023-10-26 13:52:44 +13:00
Rob BygraveandGitHub 9f65673f54 Merge pull request #3255 from ebean-orm/feature/bump-junit-bytebuddy
Bump io.avaje:junit and bytebuddy version to support JDK 22+
2023-10-25 22:12:49 +13:00
Rob Bygrave 8f6a3c59d1 Bump io.avaje:junit and bytebuddy version to support JDK 22+ 2023-10-25 21:58:19 +13:00
Rob BygraveandGitHub 05210ec411 Merge pull request #3254 from ebean-orm/feature/query-plan-capture-yuga
Use DIST with query plan capture for Yugabyte plus simplify
2023-10-25 08:23:01 +13:00
Rob Bygrave faffda8318 Use DIST with query plan capture for Yugabyte plus simplify
Change QueryPlanLoggerExplain to take the options for PG, Yugabyte and others
2023-10-25 08:22:25 +13:00
Rob BygraveandGitHub af3901ce01 Merge pull request #3253 from ebean-orm/feature/3251
#3251 - QueryBeans: Entities can not have same name as some Ebean API…
2023-10-25 08:11:40 +13:00
Rob Bygrave acd538207f #3251 - QueryBeans: Entities can not have same name as some Ebean API classes 2023-10-24 23:08:30 +13:00
Rob Bygrave 84f0ef9fce Use querybean-generator via provided scope rather than maven compiler plugin
This is because when releasing both jakarta and javax versions these
need to have their versions adjusted
2023-10-24 23:07:14 +13:00
Rob BygraveandGitHub 7ab447b21c Merge pull request #3252 from ebean-orm/feature/querybean-generator-provided
Use querybean-generator via provided scope rather than maven compiler plugin
2023-10-24 22:23:56 +13:00
Rob Bygrave 6f8805a6b3 Use querybean-generator via provided scope rather than maven compiler plugin
This is because when releasing both jakarta and javax versions these
need to have their versions adjusted
2023-10-24 22:17:28 +13:00
Rob Bygrave 0171c1afef Version 13.23.1 2023-10-18 15:47:04 +13:00
Rob Bygrave 31c106f604 Bump ebean-agent to 13.23.1 2023-10-18 15:16:20 +13:00
Rob BygraveandGitHub 20d2838be4 Merge pull request #3250 from ebean-orm/feature/bump-parent
Make Jackson a transitive dependency of ebean-jackson-mapper
2023-10-17 23:25:43 +13:00
Rob Bygrave 4238a951ca Make Jackson a transitive dependency of ebean-jackson-mapper
So jackson-core and jackson-databind are both transitive dependencies
of ebean-jackson-mapper.
2023-10-17 23:25:09 +13:00
Rob BygraveandGitHub b5d4bed615 Merge pull request #3249 from ebean-orm/feature/bump-parent
Support native-image via querybean-generator
2023-10-17 23:18:17 +13:00
Rob Bygrave 3ef715ef64 Support native-image via querybean-generator 2023-10-17 23:09:18 +13:00
Rob BygraveandGitHub aea39528ab Merge pull request #3248 from ebean-orm/feature/bump-parent
Bump dependency for avaje-config to 3.9
2023-10-17 22:39:04 +13:00
Rob Bygrave dd498409e0 Bump dependency for avaje-config to 3.9 2023-10-17 22:38:33 +13:00
Rob BygraveandGitHub bae33101df Merge pull request #3247 from ebean-orm/feature/bump-parent
Bump parent pom
2023-10-17 22:37:53 +13:00
Rob Bygrave e1972ac544 Bump parent pom 2023-10-17 22:37:20 +13:00
Rob Bygrave c7241ba3dc Fix test for java 16+ annotation processor version 2023-10-16 23:30:45 +13:00
Rob Bygrave 73416ed65a For testing java 16+ include JDK 17 and 21 only 2023-10-16 23:19:48 +13:00
Rob Bygrave cd70c178f1 For testing java 16+ include JDK 17 and 21 only 2023-10-16 23:17:25 +13:00
Rob Bygrave aa298235f3 For testing java 16+ include JDK 21 and 22 2023-10-16 23:15:32 +13:00
Rob BygraveandGitHub 8733fed14b Merge pull request #3246 from ebean-orm/feature/annotation-processing-jdk-22
Explicit annotation processors required with JDK 22
2023-10-16 22:06:20 +13:00
Rob Bygrave 169d170247 Explicit annotation processors required with JDK 22
JDK 22 changes such that annotation processors are not found by default on
the classpath. As such they either need to be explicitly registered with
the compiler (e.g. maven-compiler-plugin) or use -proc:full
2023-10-16 21:54:32 +13:00
Rob Bygrave d0ee42e0de Include <compilerArgument>-proc:full</compilerArgument> for JDK 22 EA build 2023-10-16 20:32:41 +13:00
590 changed files with 30815 additions and 3018 deletions
+2
View File
@@ -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
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+8 -8
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,31 +17,31 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-joda-time</artifactId>
<version>13.18.0</version>
<version>13.24.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-jsonnode</artifactId>
<version>13.18.0</version>
<version>13.24.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</parent>
<artifactId>composites</artifactId>
+2 -9
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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);
}
}
+52
View File
@@ -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>
+180 -1
View File
@@ -42,7 +42,7 @@ import java.util.concurrent.Callable;
* <h5>Constructing a Database</h5>
* <p>
* Databases are constructed by the DatabaseFactory. They can be created
* programmatically via {@link DatabaseFactory#create(DatabaseConfig)} or they
* programmatically via {@link DatabaseFactory#create(DatabaseBuilder)} or they
* can be automatically constructed on demand using configuration information in
* the application.properties file.
*
@@ -86,6 +86,23 @@ import java.util.concurrent.Callable;
@NonNullApi
public interface Database {
/**
* Return a new database builder.
* <pre>{@code
*
* // build the 'default' database using configuration
* // from application.properties / application.yaml
*
* Database db = Database.builder()
* .loadFromProperties()
* .build();
*
* }</pre>
*/
static DatabaseBuilder builder() {
return new DatabaseConfig();
}
/**
* Shutdown the Database instance.
*/
@@ -225,6 +242,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 +261,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 +459,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 +1397,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>
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,6 @@
package io.ebean;
import io.ebean.config.ContainerConfig;
import io.ebean.config.DatabaseConfig;
import io.ebean.service.SpiContainer;
import io.ebean.service.SpiContainerFactory;
@@ -76,9 +75,10 @@ public final class DatabaseFactory {
*
* }</pre>
*/
public static Database create(DatabaseConfig config) {
public static Database create(DatabaseBuilder builder) {
lock.lock();
try {
var config = builder.settings();
if (config.getName() == null) {
throw new PersistenceException("The name is null (it is required)");
}
@@ -102,7 +102,7 @@ public final class DatabaseFactory {
/**
* Create using the DatabaseConfig additionally specifying a classLoader to use as the context class loader.
*/
public static Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) {
public static Database createWithContextClassLoader(DatabaseBuilder config, ClassLoader classLoader) {
lock.lock();
try {
ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
@@ -132,7 +132,7 @@ public final class DatabaseFactory {
}
}
private static Database createInternal(DatabaseConfig config) {
private static Database createInternal(DatabaseBuilder.Settings config) {
return container(config.getContainerConfig()).createServer(config);
}
@@ -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;
@@ -53,7 +54,7 @@ public interface ExpressionList<T> {
Query<T> orderById(boolean orderById);
/**
* Deprecated migrate to {@link #orderBy(String)}
* @deprecated migrate to {@link #orderBy(String)}
*/
@Deprecated(since = "13.19", forRemoval = true)
default ExpressionList<T> order(String orderByClause) {
@@ -71,7 +72,7 @@ public interface ExpressionList<T> {
ExpressionList<T> orderBy(String orderBy);
/**
* Deprecated migrate to orderBy().
* @deprecated migrate to {@link #orderBy()}.
*/
@Deprecated(forRemoval = true)
default OrderBy<T> order() {
@@ -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 {@link #filterManyRaw(String, String, Object...)}.
* <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();
}
+141 -3
View File
@@ -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.
@@ -1288,7 +1396,7 @@ public interface Query<T> extends CancelableQuery {
Query<T> orderBy(String orderByClause);
/**
* Deprecated migrate to orderBy().
* @deprecated migrate to {@link #orderBy()}.
*/
@Deprecated(since = "13.19", forRemoval = true)
default Query<T> order(String orderByClause) {
@@ -1307,7 +1415,7 @@ public interface Query<T> extends CancelableQuery {
OrderBy<T> orderBy();
/**
* Deprecated migrate to orderBy().
* @deprecated migrate to {@link #orderBy()}.
*/
@Deprecated(since = "13.19", forRemoval = true)
default OrderBy<T> order() {
@@ -1320,7 +1428,7 @@ public interface Query<T> extends CancelableQuery {
Query<T> setOrderBy(OrderBy<T> orderBy);
/**
* Deprecated migrate to setOrderBy().
* @deprecated migrate to {@link #setOrderBy(OrderBy)}.
*/
@Deprecated(since = "13.19", forRemoval = true)
default Query<T> setOrder(OrderBy<T> orderBy) {
@@ -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
@@ -1,6 +1,6 @@
package io.ebean.cache;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
/**
* Plugin that provides a ServerCacheNotify implementation.
@@ -12,5 +12,5 @@ public interface ServerCacheNotifyPlugin {
/**
* Create a ServerCacheNotify implementation given the server configuration.
*/
ServerCacheNotify create(DatabaseConfig config);
ServerCacheNotify create(DatabaseBuilder config);
}
@@ -1,7 +1,7 @@
package io.ebean.cache;
import io.ebean.BackgroundExecutor;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
/**
* The plugin interface that creates a ServerCacheFactory.
@@ -11,5 +11,5 @@ public interface ServerCachePlugin {
/**
* Create the ServerCacheFactory given the server config and background executor service.
*/
ServerCacheFactory create(DatabaseConfig config, BackgroundExecutor executor);
ServerCacheFactory create(DatabaseBuilder config, BackgroundExecutor executor);
}
@@ -1,5 +1,7 @@
package io.ebean.config;
import io.ebean.DatabaseBuilder;
/**
* Used to provide some automatic configuration early in the creation of a Database.
*/
@@ -8,11 +10,11 @@ public interface AutoConfigure {
/**
* Perform configuration for the DatabaseConfig prior to properties load.
*/
void preConfigure(DatabaseConfig config);
void preConfigure(DatabaseBuilder config);
/**
* Provide some configuration the DatabaseConfig prior to server creation but after properties have been applied.
*/
void postConfigure(DatabaseConfig config);
void postConfigure(DatabaseBuilder config);
}
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,7 @@
package io.ebean.config;
import io.ebean.DatabaseBuilder;
/**
* Provides a ServiceLoader based mechanism to configure a DatabaseConfig.
* <p>
@@ -35,5 +37,5 @@ public interface DatabaseConfigProvider {
* Typically we explicitly register entity bean classes and thus avoid classpath scanning.
* </p>
*/
void apply(DatabaseConfig config);
void apply(DatabaseBuilder config);
}
@@ -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;
}
}
@@ -1,6 +1,6 @@
package io.ebean.event;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
/**
* Used to configure the server on startup.
@@ -13,6 +13,6 @@ public interface ServerConfigStartup {
/**
* On starting configure the DatabaseConfig.
*/
void onStart(DatabaseConfig config);
void onStart(DatabaseBuilder config);
}
@@ -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);
}
@@ -3,7 +3,7 @@ package io.ebean.plugin;
import io.ebean.Database;
import io.ebean.bean.BeanLoader;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import io.ebean.config.dbplatform.DatabasePlatform;
import java.util.List;
@@ -16,7 +16,7 @@ public interface SpiServer extends Database {
/**
* Return the DatabaseConfig.
*/
DatabaseConfig config();
DatabaseBuilder.Settings config();
/**
* Return the DatabasePlatform for this database.
@@ -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;
@@ -1,7 +1,7 @@
package io.ebean.service;
import io.ebean.Database;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
/**
* Creates the Database implementations. This is used internally by the EbeanServerFactory and is not currently
@@ -14,7 +14,7 @@ public interface SpiContainer {
*
* @param configuration The configuration information for this database.
*/
Database createServer(DatabaseConfig configuration);
Database createServer(DatabaseBuilder configuration);
/**
* Create an EbeanServer just using the name.
+3
View File
@@ -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;
@@ -1,6 +1,7 @@
package io.ebean.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.DatabaseBuilder;
import io.ebean.annotation.MutationDetection;
import io.ebean.annotation.PersistBatch;
import io.ebean.config.dbplatform.IdType;
@@ -17,7 +18,7 @@ class DatabaseConfigTest {
@Test
void testLoadFromEbeanProperties() {
DatabaseConfig config = new DatabaseConfig();
var config = new DatabaseConfig().settings();
config.loadFromProperties();
assertEquals(PersistBatch.NONE, config.getPersistBatch());
@@ -32,7 +33,7 @@ class DatabaseConfigTest {
Properties props = new Properties();
props.setProperty("ddl.initSql", "${user.home}" + fileSeparator + "initSql");
DatabaseConfig config = new DatabaseConfig();
var config = new DatabaseConfig().settings();
config.loadFromProperties(props);
String ddlInitSql = config.getDdlInitSql();
@@ -41,7 +42,7 @@ class DatabaseConfigTest {
@Test
void testLoadWithProperties() {
DatabaseConfig config = new DatabaseConfig();
DatabaseBuilder config = new DatabaseConfig();
config.setPersistBatch(PersistBatch.NONE);
config.setPersistBatchOnCascade(PersistBatch.NONE);
config.setAutoReadOnlyDataSource(false);
@@ -83,46 +84,47 @@ class DatabaseConfigTest {
config.loadFromProperties(props);
assertFalse(config.isDefaultServer());
assertTrue(config.isDisableL2Cache());
assertTrue(config.isNotifyL2CacheInForeground());
assertTrue(config.isDbOffline());
assertTrue(config.isAutoReadOnlyDataSource());
assertTrue(config.isAutoLoadModuleInfo());
assertTrue(config.isLoadModuleInfo());
assertTrue(config.skipDataSourceCheck());
var settings = config.settings();
assertFalse(settings.isDefaultServer());
assertTrue(settings.isDisableL2Cache());
assertTrue(settings.isNotifyL2CacheInForeground());
assertTrue(settings.isDbOffline());
assertTrue(settings.isAutoReadOnlyDataSource());
assertTrue(settings.isAutoLoadModuleInfo());
assertTrue(settings.isLoadModuleInfo());
assertTrue(settings.skipDataSourceCheck());
assertTrue(config.isIdGeneratorAutomatic());
assertFalse(config.getPlatformConfig().isCaseSensitiveCollation());
assertTrue(config.getPlatformConfig().isForUpdateNoKey());
assertTrue(settings.isIdGeneratorAutomatic());
assertFalse(settings.getPlatformConfig().isCaseSensitiveCollation());
assertTrue(settings.getPlatformConfig().isForUpdateNoKey());
assertThat(config.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
assertThat(settings.getNamingConvention()).isInstanceOf(MatchingNamingConvention.class);
assertEquals(MutationDetection.NONE, config.getJsonMutationDetection());
assertEquals(MutationDetection.NONE, settings.getJsonMutationDetection());
config.setJsonMutationDetection(MutationDetection.SOURCE);
assertEquals(MutationDetection.SOURCE, config.getJsonMutationDetection());
assertEquals(IdType.SEQUENCE, config.getIdType());
assertEquals(PersistBatch.ALL, config.getPersistBatch());
assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade());
Assertions.assertEquals(PlatformConfig.DbUuid.BINARY, config.getPlatformConfig().getDbUuid());
Assertions.assertEquals(JsonConfig.DateTime.MILLIS, config.getJsonDateTime());
assertEquals(JsonConfig.Date.MILLIS, config.getJsonDate());
assertEquals(MutationDetection.SOURCE, settings.getJsonMutationDetection());
assertEquals(IdType.SEQUENCE, settings.getIdType());
assertEquals(PersistBatch.ALL, settings.getPersistBatch());
assertEquals(PersistBatch.ALL, settings.getPersistBatchOnCascade());
Assertions.assertEquals(PlatformConfig.DbUuid.BINARY, settings.getPlatformConfig().getDbUuid());
Assertions.assertEquals(JsonConfig.DateTime.MILLIS, settings.getJsonDateTime());
assertEquals(JsonConfig.Date.MILLIS, settings.getJsonDate());
assertEquals("r0,users,orgs", config.getEnabledL2Regions());
assertEquals("r0,users,orgs", settings.getEnabledL2Regions());
assertEquals(42, config.getJdbcFetchSizeFindEach());
assertEquals(43, config.getJdbcFetchSizeFindList());
assertEquals(4, config.getBackgroundExecutorSchedulePoolSize());
assertEquals(98, config.getBackgroundExecutorShutdownSecs());
assertEquals(42, settings.getJdbcFetchSizeFindEach());
assertEquals(43, settings.getJdbcFetchSizeFindList());
assertEquals(4, settings.getBackgroundExecutorSchedulePoolSize());
assertEquals(98, settings.getBackgroundExecutorShutdownSecs());
assertTrue(config.isQueryPlanEnable());
assertEquals(10000, config.getQueryPlanThresholdMicros());
assertTrue(config.isQueryPlanCapture());
assertEquals(42, config.getQueryPlanCapturePeriodSecs());
assertEquals(560, config.getQueryPlanCaptureMaxTimeMillis());
assertEquals(7, config.getQueryPlanCaptureMaxCount());
assertTrue(settings.isQueryPlanEnable());
assertEquals(10000, settings.getQueryPlanThresholdMicros());
assertTrue(settings.isQueryPlanCapture());
assertEquals(42, settings.getQueryPlanCapturePeriodSecs());
assertEquals(560, settings.getQueryPlanCaptureMaxTimeMillis());
assertEquals(7, settings.getQueryPlanCaptureMaxCount());
assertThat(config.getMappingLocations()).containsExactly("classpath:/foo","bar");
assertThat(settings.getMappingLocations()).containsExactly("classpath:/foo","bar");
config.setPersistBatch(PersistBatch.NONE);
config.setPersistBatchOnCascade(PersistBatch.NONE);
@@ -137,19 +139,19 @@ class DatabaseConfigTest {
props1.setProperty("ebean.notifyL2CacheInForeground", "false");
config.loadFromProperties(props1);
assertFalse(config.isDisableL2Cache());
assertFalse(config.isNotifyL2CacheInForeground());
assertFalse(settings.isDisableL2Cache());
assertFalse(settings.isNotifyL2CacheInForeground());
assertEquals(PersistBatch.ALL, config.getPersistBatch());
assertEquals(PersistBatch.ALL, config.getPersistBatchOnCascade());
assertEquals(PersistBatch.ALL, settings.getPersistBatch());
assertEquals(PersistBatch.ALL, settings.getPersistBatchOnCascade());
config.setEnabledL2Regions("r0,orgs");
assertEquals("r0,orgs", config.getEnabledL2Regions());
assertEquals("r0,orgs", settings.getEnabledL2Regions());
}
@Test
void test_defaults() {
DatabaseConfig config = new DatabaseConfig();
DatabaseBuilder.Settings config = new DatabaseConfig().settings();
assertTrue(config.isIdGeneratorAutomatic());
assertTrue(config.isDefaultServer());
assertFalse(config.isAutoPersistUpdates());
@@ -184,7 +186,7 @@ class DatabaseConfigTest {
void test_putServiceObject() {
ObjectMapper objectMapper = new ObjectMapper();
DatabaseConfig config = new DatabaseConfig();
var config = new DatabaseConfig().settings();
config.putServiceObject(objectMapper);
ObjectMapper mapper0 = config.getServiceObject(ObjectMapper.class);
+28 -28
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</parent>
<name>ebean bom</name>
@@ -89,49 +89,49 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-joda-time</artifactId>
<version>13.18.0</version>
<version>13.24.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-jsonnode</artifactId>
<version>13.18.0</version>
<version>13.24.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
</dependency>
</dependencies>
+2 -9
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-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>
@@ -1,6 +1,6 @@
package io.ebean.core.type;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import java.util.List;
@@ -12,5 +12,5 @@ public interface ExtraTypeFactory {
/**
* Provide extra types to Ebean.
*/
List<? extends ScalarType<?>> createTypes(DatabaseConfig config, Object objectMapper);
List<? extends ScalarType<?>> createTypes(DatabaseBuilder.Settings config, Object objectMapper);
}
@@ -1,7 +1,7 @@
package io.ebean.core.type;
import io.avaje.lang.Nullable;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
/**
* Factory to create ScalarTypeSet.
@@ -12,6 +12,6 @@ public interface ScalarTypeSetFactory {
* Create the ScalarTypeSet given the config and optional objectMapper.
*/
@Nullable
ScalarTypeSet<?> createTypeSet(DatabaseConfig config, @Nullable Object objectMapper);
ScalarTypeSet<?> createTypeSet(DatabaseBuilder.Settings config, @Nullable Object objectMapper);
}
+36 -13
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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.25.0-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.25.0-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.25.0-jakarta</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-jakarta</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>13.23.0-jakarta</version>
<version>13.25.0-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>
@@ -1,6 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import io.ebeaninternal.server.type.GeoTypeBinder;
/**
@@ -11,5 +11,5 @@ public interface GeoTypeProvider {
/**
* Create a binder for binding geometry types.
*/
GeoTypeBinder createBinder(DatabaseConfig config);
GeoTypeBinder createBinder(DatabaseBuilder.Settings config);
}
@@ -111,7 +111,7 @@ public final class LoadBeanRequest extends LoadRequest {
}
private String mode() {
return lazy ? "+lazy" : loadBuffer.isCache() ? "+cache" : "+query";
return lazy ? "lazy" : loadBuffer.isCache() ? "cache" : "query";
}
/**
@@ -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,9 +99,9 @@ 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());
query.setLoadDescription(lazy ? "lazy" : "query", description());
if (lazy) {
query.setLazyLoadBatchSize(loadContext.batchSize());
} else {
@@ -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;
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.api;
import io.avaje.lang.Nullable;
import io.ebean.CacheMode;
import io.ebean.CountDistinctOrder;
import io.ebean.ExpressionList;
@@ -10,6 +11,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 +170,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 +292,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>
@@ -322,7 +340,7 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
/**
* Set the on a secondary query given the label, relativePath and profile location of the parent query.
*/
void setProfilePath(String label, String relativePath, ProfileLocation profileLocation);
void setProfilePath(String label, String relativePath, @Nullable ProfileLocation profileLocation);
/**
* Set the query mode.
@@ -359,6 +377,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 +510,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 +720,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 +791,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 +873,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>
@@ -1,9 +1,9 @@
package io.ebeaninternal.server.autotune;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import io.ebeaninternal.api.SpiEbeanServer;
public interface AutoTuneServiceProvider {
AutoTuneService create(SpiEbeanServer server, DatabaseConfig config);
AutoTuneService create(SpiEbeanServer server, DatabaseBuilder config);
}
@@ -4,7 +4,7 @@ import io.ebean.cache.QueryCacheEntryValidate;
import io.ebean.cache.ServerCacheFactory;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import io.ebeaninternal.server.cluster.ClusterManager;
/**
@@ -13,7 +13,7 @@ import io.ebeaninternal.server.cluster.ClusterManager;
public final class CacheManagerOptions {
private final ClusterManager clusterManager;
private final DatabaseConfig databaseConfig;
private final DatabaseBuilder.Settings databaseBuilder;
private final boolean localL2Caching;
private CurrentTenantProvider currentTenantProvider;
private QueryCacheEntryValidate queryCacheEntryValidate;
@@ -24,15 +24,15 @@ public final class CacheManagerOptions {
CacheManagerOptions() {
this.localL2Caching = true;
this.clusterManager = null;
this.databaseConfig = null;
this.databaseBuilder = null;
this.cacheFactory = new DefaultServerCacheFactory();
this.beanDefault = new ServerCacheOptions();
this.queryDefault = new ServerCacheOptions();
}
public CacheManagerOptions(ClusterManager clusterManager, DatabaseConfig config, boolean localL2Caching) {
public CacheManagerOptions(ClusterManager clusterManager, DatabaseBuilder.Settings config, boolean localL2Caching) {
this.clusterManager = clusterManager;
this.databaseConfig = config;
this.databaseBuilder = config;
this.localL2Caching = localL2Caching;
this.currentTenantProvider = config.getCurrentTenantProvider();
}
@@ -55,7 +55,7 @@ public final class CacheManagerOptions {
}
public String getServerName() {
return (databaseConfig == null) ? "db" : databaseConfig.getName();
return (databaseBuilder == null) ? "db" : databaseBuilder.getName();
}
public boolean isLocalL2Caching() {
@@ -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.
*/
@@ -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) {
@@ -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;
}
}
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.cache;
import io.ebean.BackgroundExecutor;
import io.ebean.cache.ServerCacheFactory;
import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
/**
* Default implementation of ServerCachePlugin.
@@ -14,7 +14,7 @@ public final class DefaultServerCachePlugin implements ServerCachePlugin {
* Creates the default ServerCacheFactory.
*/
@Override
public ServerCacheFactory create(DatabaseConfig config, BackgroundExecutor executor) {
public ServerCacheFactory create(DatabaseBuilder config, BackgroundExecutor executor) {
return new DefaultServerCacheFactory(executor);
}
}
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.core;
import io.avaje.classpath.scanner.ClassPathScanner;
import io.avaje.classpath.scanner.ClassPathScannerFactory;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import java.util.ArrayList;
import java.util.List;
@@ -16,7 +16,7 @@ public class ClassPathScanners {
/**
* Return the list of ClassPathScanner services using DatabaseConfig service loader.
*/
public static List<ClassPathScanner> find(DatabaseConfig config) {
public static List<ClassPathScanner> find(DatabaseBuilder.Settings config) {
List<ClassPathScanner> scanners = new ArrayList<>();
for (ClassPathScannerFactory factory : ServiceLoader.load(ClassPathScannerFactory.class)) {
scanners.add(factory.createScanner(config.getClassLoadConfig().getClassLoader()));
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DatabasePlatformProvider;
import io.ebeaninternal.api.CoreLog;
@@ -37,7 +37,7 @@ public class DatabasePlatformFactory {
/**
* Create the appropriate database specific platform.
*/
public DatabasePlatform create(DatabaseConfig config) {
public DatabasePlatform create(DatabaseBuilder.Settings config) {
try {
String offlinePlatform = DbOffline.getPlatform();
if (offlinePlatform != null) {
@@ -82,9 +82,9 @@ final class DefaultBeanLoader {
// populate a new collection
BeanCollection<?> emptyCollection = many.createEmpty(parentBean);
many.setValue(parentBean, emptyCollection);
query.setLoadDescription("+refresh", null);
query.setLoadDescription("refresh", null);
} else {
query.setLoadDescription("+lazy", null);
query.setLoadDescription("lazy", null);
}
query.select(parentDesc.idBinder().idSelect());
@@ -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) {
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.DatabaseBuilder;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.ShutdownManager;
@@ -51,13 +52,14 @@ public final class DefaultContainer implements SpiContainer {
*/
@Override
public SpiEbeanServer createServer(String name) {
DatabaseConfig config = new DatabaseConfig();
DatabaseBuilder config = new DatabaseConfig();
config.setName(name);
config.loadFromProperties();
return createServer(config);
}
private SpiBackgroundExecutor createBackgroundExecutor(DatabaseConfig config) {
private SpiBackgroundExecutor createBackgroundExecutor(DatabaseBuilder builder) {
var config = builder.settings();
String namePrefix = "ebean-" + config.getName();
int schedulePoolSize = config.getBackgroundExecutorSchedulePoolSize();
int shutdownSecs = config.getBackgroundExecutorShutdownSecs();
@@ -69,21 +71,26 @@ public final class DefaultContainer implements SpiContainer {
* Create the implementation from the configuration.
*/
@Override
public SpiEbeanServer createServer(DatabaseConfig config) {
public SpiEbeanServer createServer(DatabaseBuilder builder) {
lock.lock();
try {
var config = builder.settings();
long start = System.currentTimeMillis();
applyConfigServices(config);
setNamingConvention(config);
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);
}
}
}
@@ -111,7 +118,7 @@ public final class DefaultContainer implements SpiContainer {
}
}
private void applyConfigServices(DatabaseConfig config) {
private void applyConfigServices(DatabaseBuilder.Settings config) {
if (config.isDefaultServer()) {
for (DatabaseConfigProvider configProvider : ServiceLoader.load(DatabaseConfigProvider.class)) {
configProvider.apply(config);
@@ -154,7 +161,7 @@ public final class DefaultContainer implements SpiContainer {
* Get the entities, scalarTypes, Listeners etc combining the class registered
* ones with the already created instances.
*/
private BootupClasses bootupClasses(DatabaseConfig config) {
private BootupClasses bootupClasses(DatabaseBuilder.Settings config) {
BootupClasses bootup = bootupClasses1(config);
bootup.addServerConfigStartup(config.getServerConfigStartupListeners());
bootup.runServerConfigStartup(config);
@@ -172,7 +179,7 @@ public final class DefaultContainer implements SpiContainer {
/**
* Get the class based entities, scalarTypes, Listeners etc.
*/
private BootupClasses bootupClasses1(DatabaseConfig config) {
private BootupClasses bootupClasses1(DatabaseBuilder.Settings config) {
Set<Class<?>> classes = config.classes();
if (config.isDisableClasspathSearch() || (classes != null && !classes.isEmpty())) {
// use classes we explicitly added via configuration
@@ -184,7 +191,7 @@ public final class DefaultContainer implements SpiContainer {
/**
* Set the naming convention to underscore if it has not already been set.
*/
private void setNamingConvention(DatabaseConfig config) {
private void setNamingConvention(DatabaseBuilder.Settings config) {
if (config.getNamingConvention() == null) {
config.setNamingConvention(new UnderscoreNamingConvention());
}
@@ -193,7 +200,7 @@ public final class DefaultContainer implements SpiContainer {
/**
* Set the DatabasePlatform if it has not already been set.
*/
private void setDatabasePlatform(DatabaseConfig config) {
private void setDatabasePlatform(DatabaseBuilder.Settings config) {
DatabasePlatform platform = config.getDatabasePlatform();
if (platform == null) {
if (config.getTenantMode().isDynamicDataSource()) {
@@ -209,7 +216,7 @@ public final class DefaultContainer implements SpiContainer {
/**
* Set the DataSource if it has not already been set.
*/
private void setDataSource(DatabaseConfig config) {
private void setDataSource(DatabaseBuilder.Settings config) {
if (isOfflineMode(config)) {
log.log(DEBUG, "... DbOffline using platform [{0}]", DbOffline.getPlatform());
} else {
@@ -217,7 +224,7 @@ public final class DefaultContainer implements SpiContainer {
}
}
private boolean isOfflineMode(DatabaseConfig config) {
private boolean isOfflineMode(DatabaseBuilder.Settings config) {
return config.isDbOffline() || DbOffline.isSet();
}
@@ -231,7 +238,7 @@ public final class DefaultContainer implements SpiContainer {
* checking may not work as expected.
* </p>
*/
private boolean checkDataSource(DatabaseConfig config) {
private boolean checkDataSource(DatabaseBuilder.Settings config) {
if (isOfflineMode(config)) {
return false;
}
@@ -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;
@@ -71,7 +76,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private static final System.Logger log = CoreLog.internal;
private final ReentrantLock lock = new ReentrantLock();
private final DatabaseConfig config;
private final DatabaseBuilder.Settings config;
private final String serverName;
private final DatabasePlatform databasePlatform;
private final TransactionManager transactionManager;
@@ -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();
@@ -158,7 +170,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
/**
* Create the CallStackFactory depending if AutoTune is being used.
*/
private CallOriginFactory initCallStackFactory(DatabaseConfig config) {
private CallOriginFactory initCallStackFactory(DatabaseBuilder.Settings config) {
if (!config.getAutoTuneConfig().isActive()) {
// use a common CallStack for performance as we don't care with no AutoTune
return new NoopCallOriginFactory();
@@ -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);
}
@@ -210,7 +224,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public DatabaseConfig config() {
public DatabaseBuilder.Settings config() {
return config;
}
@@ -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
@@ -1,11 +1,8 @@
package io.ebeaninternal.server.core;
import io.ebean.annotation.Platform;
import io.ebean.config.DatabaseConfig;
import io.ebean.datasource.DataSourceAlertFactory;
import io.ebean.datasource.DataSourceConfig;
import io.ebean.datasource.DataSourceFactory;
import io.ebean.datasource.DataSourcePoolListener;
import io.ebean.DatabaseBuilder;
import io.ebean.datasource.*;
import jakarta.persistence.PersistenceException;
import javax.sql.DataSource;
@@ -15,16 +12,16 @@ import javax.sql.DataSource;
*/
final class InitDataSource {
private final DatabaseConfig config;
private final DatabaseBuilder.Settings config;
/**
* Create and set the main DataSource and read-only DataSource.
*/
static void init(DatabaseConfig config) {
static void init(DatabaseBuilder.Settings config) {
new InitDataSource(config).initialise();
}
InitDataSource(DatabaseConfig config) {
InitDataSource(DatabaseBuilder.Settings config) {
this.config = config;
}
@@ -48,12 +45,12 @@ final class InitDataSource {
* Initialise the "read only" DataSource from configuration.
*/
private DataSource initReadOnlyDataSource() {
DataSourceConfig roConfig = readOnlyConfig();
var roConfig = readOnlyConfig();
return roConfig == null ? null : createFromConfig(roConfig, true);
}
DataSourceConfig readOnlyConfig() {
DataSourceConfig roConfig = config.getReadOnlyDataSourceConfig();
DataSourceBuilder.Settings readOnlyConfig() {
var roConfig = config.getReadOnlyDataSourceConfig();
if (roConfig == null) {
// it has explicitly been set to null, not expected but ok
return null;
@@ -64,11 +61,11 @@ final class InitDataSource {
// convenient alternate place to set the read-only url
final String readOnlyUrl = config.getDataSourceConfig().getReadOnlyUrl();
if (urlSet(readOnlyUrl)) {
roConfig.setUrl(readOnlyUrl);
roConfig.url(readOnlyUrl);
return roConfig;
}
if (config.isAutoReadOnlyDataSource()) {
roConfig.setUrl(null); // blank out in case it is "none"
roConfig.url(null); // blank out in case it is "none"
return roConfig;
} else {
return null;
@@ -79,9 +76,9 @@ final class InitDataSource {
return url != null && !"none".equalsIgnoreCase(url) && !url.trim().isEmpty();
}
private DataSource createFromConfig(DataSourceConfig dsConfig, boolean readOnly) {
private DataSource createFromConfig(DataSourceBuilder.Settings dsConfig, boolean readOnly) {
if (dsConfig == null) {
throw new PersistenceException("No DataSourceConfig defined for " + config.getName());
throw new PersistenceException("No DataSourceBuilder defined for " + config.getName());
}
if (dsConfig.isOffline()) {
if (config.getDatabasePlatformName() == null) {
@@ -94,10 +91,11 @@ final class InitDataSource {
if (readOnly) {
// setup to use AutoCommit such that we skip explicit commit
dsConfig.setAutoCommit(true);
dsConfig.setReadOnly(true);
dsConfig.setDefaults(config.getDataSourceConfig());
dsConfig.setIsolationLevel(config.getDataSourceConfig().getIsolationLevel());
var mainSettings = config.getDataSourceConfig();
dsConfig.autoCommit(true);
dsConfig.readOnly(true);
dsConfig.setDefaults(mainSettings);
dsConfig.isolationLevel(mainSettings.getIsolationLevel());
} else if (isPostgresAllQuotedIdentifiers()) {
dsConfig.addProperty("quoteReturningIdentifiers", false);
}
@@ -108,32 +106,32 @@ final class InitDataSource {
return config.isAllQuotedIdentifiers() && Platform.POSTGRES == config.getDatabasePlatform().platform().base();
}
private DataSource create(DataSourceConfig dsConfig, boolean readOnly) {
private DataSource create(DataSourceBuilder dsConfig, boolean readOnly) {
String poolName = config.getName() + (readOnly ? "-ro" : "");
return DataSourceFactory.create(poolName, dsConfig);
return dsConfig.name(poolName).build();
}
/**
* Attach DataSourceAlert via service loader if present.
*/
private void attachAlert(DataSourceConfig dsConfig) {
private void attachAlert(DataSourceBuilder.Settings dsConfig) {
DataSourceAlertFactory alertFactory = config.getServiceObject(DataSourceAlertFactory.class);
if (alertFactory == null) {
alertFactory = ServiceUtil.service(DataSourceAlertFactory.class);
}
if (alertFactory != null) {
dsConfig.setAlert(alertFactory.createAlert());
dsConfig.alert(alertFactory.createAlert());
}
}
/**
* Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached.
*/
private void attachListener(DataSourceConfig dsConfig) {
private void attachListener(DataSourceBuilder.Settings dsConfig) {
if (dsConfig.getListener() == null) {
String poolListener = dsConfig.getPoolListener();
if (poolListener != null) {
dsConfig.setListener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener));
dsConfig.listener((DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener));
}
}
}
@@ -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());
}
}
}
@@ -1,18 +1,18 @@
package io.ebeaninternal.server.core;
import com.fasterxml.jackson.core.JsonFactory;
import io.ebean.DatabaseBuilder;
import io.ebean.ExpressionFactory;
import io.ebean.annotation.Platform;
import io.ebean.cache.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.ExternalTransactionManager;
import io.ebean.config.ProfilingConfig;
import io.ebean.config.SlowQueryListener;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
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 +29,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 +42,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.*;
@@ -60,9 +69,10 @@ public final class InternalConfiguration {
private final TableModState tableModState;
private final boolean online;
private final DatabaseConfig config;
private final DatabaseBuilder.Settings 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 +89,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;
@@ -87,14 +98,15 @@ public final class InternalConfiguration {
private boolean localL2Caching;
InternalConfiguration(boolean online, ClusterManager clusterManager, SpiBackgroundExecutor backgroundExecutor,
DatabaseConfig config, BootupClasses bootupClasses) {
DatabaseBuilder.Settings config, BootupClasses bootupClasses) {
this.online = online;
this.config = config;
this.jacksonCorePresent = config.getClassLoadConfig().isJacksonCorePresent();
this.clockService = new ClockService(config.getClock());
this.clockService = new ClockService(config.settings().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 +115,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 +150,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);
@@ -145,11 +173,22 @@ public final class InternalConfiguration {
/**
* Create and return the ExpressionFactory based on configuration and database platform.
*/
private ExpressionFactory initExpressionFactory(DatabaseConfig config) {
private ExpressionFactory initExpressionFactory(DatabaseBuilder.Settings config) {
boolean nativeIlike = config.isExpressionNativeIlike() && databasePlatform.supportsNativeIlike();
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 +245,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.
*/
@@ -276,7 +331,7 @@ public final class InternalConfiguration {
return config.getDatabasePlatform();
}
public DatabaseConfig getConfig() {
public DatabaseBuilder.Settings getConfig() {
return config;
}
@@ -292,6 +347,10 @@ public final class InternalConfiguration {
return beanDescriptorManager;
}
public DeployInherit getDeployInherit() {
return deployInherit;
}
public DeployCreateProperties getDeployCreateProperties() {
return deployCreateProperties;
}
@@ -313,19 +372,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 +588,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.
*/
@@ -1,7 +1,7 @@
package io.ebeaninternal.server.core.bootup;
import io.avaje.classpath.scanner.ClassPathScanner;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.server.core.ClassPathScanners;
@@ -24,11 +24,11 @@ public class BootupClassPathSearch {
* Search the classPath for the classes we are interested in returning
* them as BootupClasses.
*/
public static BootupClasses search(DatabaseConfig config) {
public static BootupClasses search(DatabaseBuilder.Settings config) {
return new BootupClassPathSearch(config).getBootupClasses();
}
private BootupClassPathSearch(DatabaseConfig config) {
private BootupClassPathSearch(DatabaseBuilder.Settings config) {
// find packages defined in ebean.mf resources
Set<String> mfPackages = ManifestReader.create(config.getClassLoadConfig().getClassLoader())
.read("META-INF/ebean.mf")
@@ -1,7 +1,7 @@
package io.ebeaninternal.server.core.bootup;
import io.ebean.annotation.DocStore;
import io.ebean.config.DatabaseConfig;
import io.ebean.DatabaseBuilder;
import io.ebean.config.IdGenerator;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.core.type.ScalarType;
@@ -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() {
}
@@ -85,7 +91,7 @@ public class BootupClasses implements Predicate<Class<?>> {
/**
* Run any ServerConfigStartup listeners.
*/
public void runServerConfigStartup(DatabaseConfig config) {
public void runServerConfigStartup(DatabaseBuilder config) {
for (Class<?> cls : serverConfigStartupCandidates) {
try {
ServerConfigStartup newInstance = (ServerConfigStartup) cls.getDeclaredConstructor().newInstance();
@@ -166,12 +172,20 @@ public class BootupClasses implements Predicate<Class<?>> {
add(startupInstances, serverConfigStartupInstances, serverConfigStartupCandidates);
}
public void addChangeLogInstances(DatabaseConfig config) {
public void addChangeLogInstances(DatabaseBuilder.Settings 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);
}
}

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