mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-09-23 03:25:01 +00:00
Compare commits
52
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292b6d2c72 | ||
|
|
a99ef3ebf2 | ||
|
|
19afa845d1 | ||
|
|
239900cd3b | ||
|
|
73ac39971c | ||
|
|
3a876252ec | ||
|
|
231b52ba88 | ||
|
|
808019cf3d | ||
|
|
364520455f | ||
|
|
e92621a489 | ||
|
|
ccd1b7b8ec | ||
|
|
d144273307 | ||
|
|
41c5ebdcd7 | ||
|
|
9e711efecb | ||
|
|
ef7fd76f14 | ||
|
|
b7e3ddbedd | ||
|
|
69c932816a | ||
|
|
a1facf527b | ||
|
|
ec42ebf66d | ||
|
|
5dede70801 | ||
|
|
b2f02ecc1f | ||
|
|
15fa7cd1c2 | ||
|
|
8dced38cd5 | ||
|
|
7addba7c93 | ||
|
|
00b45c1642 | ||
|
|
a3d9711fe6 | ||
|
|
1239c50723 | ||
|
|
d85d8449e2 | ||
|
|
baf71cbab3 | ||
|
|
ff0ca35e54 | ||
|
|
0fc9a1ac2f | ||
|
|
f7b4edb193 | ||
|
|
284c8d4d21 | ||
|
|
59a5c5f9bd | ||
|
|
5b04d6eca3 | ||
|
|
d5547808a2 | ||
|
|
5606c95de5 | ||
|
|
4c2bd408b0 | ||
|
|
db76d5c27c | ||
|
|
f92c2f8c14 | ||
|
|
51c3065419 | ||
|
|
e46dd66292 | ||
|
|
3e1451bee7 | ||
|
|
4ac3c4ddb1 | ||
|
|
ee8e48902b | ||
|
|
c37e752ace | ||
|
|
c9a6f822ac | ||
|
|
2231ef9fd1 | ||
|
|
3ff1905318 | ||
|
|
9b4fecff96 | ||
|
|
6f356fd55e | ||
|
|
591f260f5b |
@@ -3,6 +3,7 @@
|
||||
[](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean)
|
||||
[](https://github.com/ebean-orm/ebean/blob/master/LICENSE)
|
||||
[](https://github.com/ebean-orm/ebean/actions/workflows/multi-jdk-build.yml)
|
||||
[](https://www.graalvm.org/)
|
||||
|
||||
##### Build with database platforms
|
||||
[](https://github.com/ebean-orm/ebean/actions/workflows/h2database.yml)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-clickhouse</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-db2</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-hana</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mariadb</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mysql</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -47,19 +47,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-net-postgis-types</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-nuodb</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-oracle</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>ebean-pgvector</name>
|
||||
<description>ebean-pgvector composite</description>
|
||||
<artifactId>ebean-pgvector</artifactId>
|
||||
|
||||
<properties>
|
||||
<pgvector.version>0.1.6</pgvector.version>
|
||||
<postgres.jdbc.version>42.7.2</postgres.jdbc.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-datasource</artifactId>
|
||||
<version>${ebean-datasource.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>${ebean-migration.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Technically optional but most expected to use query beans -->
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-pgvector-types</artifactId>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgres.jdbc.version}</version>
|
||||
<exclusions>
|
||||
<!-- exclude unnecessary checker framework -->
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.pgvector</groupId>
|
||||
<artifactId>pgvector</artifactId>
|
||||
<version>${pgvector.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.ebean.pgvector.assembly;
|
||||
|
||||
/**
|
||||
* Nothing interesting here - required placeholder for javadoc.
|
||||
*/
|
||||
public class Assembly {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
module io.ebean.pgvector {
|
||||
|
||||
requires transitive io.ebean.api;
|
||||
requires transitive io.ebean.core;
|
||||
requires transitive io.ebean.datasource;
|
||||
requires transitive io.ebean.querybean;
|
||||
requires transitive io.ebean.platform.postgres;
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -47,19 +47,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlite</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -41,7 +41,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -60,13 +60,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-all</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
+2
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>composites</artifactId>
|
||||
@@ -25,6 +25,7 @@
|
||||
<module>ebean-postgres</module>
|
||||
<module>ebean-postgis</module>
|
||||
<module>ebean-net-postgis</module>
|
||||
<module>ebean-pgvector</module>
|
||||
<!-- <module>sqlanywhere</module>-->
|
||||
<module>ebean-sqlite</module>
|
||||
<module>ebean-sqlserver</module>
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>io.avaje</groupId>
|
||||
<artifactId>avaje-config</artifactId>
|
||||
<version>4.1</version>
|
||||
<version>4.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -989,6 +989,14 @@ public interface DatabaseBuilder {
|
||||
@Deprecated
|
||||
DatabaseBuilder setNamingConvention(NamingConvention namingConvention);
|
||||
|
||||
/**
|
||||
* Set the AggregateFormulaContext which is used to determine if a database function
|
||||
* is an aggregate function (like sum, min, max, avg etc).
|
||||
* <p>
|
||||
* Use this to override the default known aggregation functions.
|
||||
*/
|
||||
DatabaseConfig aggregateFormulaContext(AggregateFormulaContext aggregateFormulaContext);
|
||||
|
||||
/**
|
||||
* Set to true if all DB column and table names should use quoted identifiers.
|
||||
* <p>
|
||||
@@ -2610,6 +2618,11 @@ public interface DatabaseBuilder {
|
||||
*/
|
||||
NamingConvention getNamingConvention();
|
||||
|
||||
/**
|
||||
* Return the AggregateFormulaContext.
|
||||
*/
|
||||
AggregateFormulaContext aggregateFormulaContext();
|
||||
|
||||
/**
|
||||
* Return true if all DB column and table names should use quoted identifiers.
|
||||
*/
|
||||
|
||||
@@ -234,5 +234,16 @@ public interface DtoQuery<T> extends CancelableQuery {
|
||||
* source. We use {@code usingMaster()} to instead ensure that the query is executed
|
||||
* against the master data source.
|
||||
*/
|
||||
DtoQuery<T> usingMaster();
|
||||
default DtoQuery<T> usingMaster() {
|
||||
return usingMaster(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the master DataSource is used when useMaster is true. Otherwise, the read only
|
||||
* data source can be used if defined.
|
||||
*
|
||||
* @see #usingMaster()
|
||||
*/
|
||||
DtoQuery<T> usingMaster(boolean useMaster);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebean;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Object relational query for finding a List, Set, Map or single entity bean.
|
||||
@@ -310,6 +311,7 @@ public interface Query<T> extends CancelableQuery, QueryBuilder<Query<T>, T> {
|
||||
/**
|
||||
* Return the Id value.
|
||||
*/
|
||||
@Nullable
|
||||
Object getId();
|
||||
|
||||
/**
|
||||
@@ -446,11 +448,13 @@ public interface Query<T> extends CancelableQuery, QueryBuilder<Query<T>, T> {
|
||||
/**
|
||||
* Return the "for update" wait mode to use.
|
||||
*/
|
||||
@Nullable
|
||||
LockWait getForUpdateLockWait();
|
||||
|
||||
/**
|
||||
* Return the lock type (strength) to use with "for update".
|
||||
*/
|
||||
@Nullable
|
||||
LockType getForUpdateLockType();
|
||||
|
||||
/**
|
||||
|
||||
@@ -135,7 +135,17 @@ public interface QueryBuilder<SELF extends QueryBuilder<SELF, T>, T> extends Que
|
||||
* source. We we use {@code usingMaster()} to instead ensure that the query is executed
|
||||
* against the master data source.
|
||||
*/
|
||||
SELF usingMaster();
|
||||
default SELF usingMaster() {
|
||||
return usingMaster(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the master DataSource is used when useMaster is true. Otherwise, the read only
|
||||
* data source can be used if defined.
|
||||
*
|
||||
* @see #usingMaster()
|
||||
*/
|
||||
SELF usingMaster(boolean useMaster);
|
||||
|
||||
/**
|
||||
* Set the base table to use for this query.
|
||||
@@ -393,6 +403,12 @@ public interface QueryBuilder<SELF extends QueryBuilder<SELF, T>, T> extends Que
|
||||
* This means that the returning graph can't be mutated via setters, all the collections
|
||||
* are unmodifiable collections, lazy loading is disabled and that the query uses
|
||||
* {@link PersistenceContextScope#QUERY}.
|
||||
* <p>
|
||||
* Attempting to mutate an unmodifiable bean will throw a <code>UnmodifiableEntityException</code>.
|
||||
* Attempting to load an unloaded property will throw a <code>LazyInitialisationException</code>
|
||||
*
|
||||
* @see LazyInitialisationException
|
||||
* @see UnmodifiableEntityException
|
||||
*/
|
||||
SELF setUnmodifiable(boolean unmodifiable);
|
||||
|
||||
@@ -441,6 +457,16 @@ public interface QueryBuilder<SELF extends QueryBuilder<SELF, T>, T> extends Que
|
||||
|
||||
/**
|
||||
* Set the {@link CacheMode} to use the query for executing this query.
|
||||
* <p>
|
||||
* Since version 16.x using the query bean will set the query to use unmodifiable (see
|
||||
* {@link #setUnmodifiable(boolean)}) so the returned object graph is unmodifiable
|
||||
* and safe to cache by the application.
|
||||
* <p>
|
||||
* Attempting to mutate an unmodifiable bean will throw a <code>UnmodifiableEntityException</code>.
|
||||
* Attempting to load an unloaded property will throw a <code>LazyInitialisationException</code>
|
||||
*
|
||||
* @see LazyInitialisationException
|
||||
* @see UnmodifiableEntityException
|
||||
*/
|
||||
SELF setUseQueryCache(CacheMode cacheMode);
|
||||
|
||||
|
||||
@@ -63,7 +63,17 @@ public interface SqlQuery extends Serializable, CancelableQuery {
|
||||
* source. We use {@code usingMaster()} to instead ensure that the query is executed
|
||||
* against the master data source.
|
||||
*/
|
||||
SqlQuery usingMaster();
|
||||
default SqlQuery usingMaster() {
|
||||
return usingMaster(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the master DataSource is used when useMaster is true. Otherwise, the read only
|
||||
* data source can be used if defined.
|
||||
*
|
||||
* @see #usingMaster()
|
||||
*/
|
||||
SqlQuery usingMaster(boolean useMaster);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list.
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used when parsing formulas to determine if they are aggregation formulas like
|
||||
* sum, min, max, avg, count etc.
|
||||
* <p>
|
||||
* Ebean needs to determine if they are aggregation formulas to determine which
|
||||
* properties should be included in a GROUP BY clause etc.
|
||||
*/
|
||||
public interface AggregateFormulaContext {
|
||||
|
||||
/**
|
||||
* Return true if the outer function is an aggregate function (like sum, count, min, max, avg etc).
|
||||
*/
|
||||
boolean isAggregate(String outerFunction);
|
||||
|
||||
/**
|
||||
* Return true if the aggregate function returns a BIGINT type.
|
||||
* This is true for functions like count that return a numeric value regardless of the
|
||||
* type of the property or expression inside the outer function.
|
||||
*/
|
||||
boolean isCount(String outerFunction);
|
||||
|
||||
/**
|
||||
* Return true if the aggregate function returns a VARCHAR type.
|
||||
* This is true for functions that return a string concatenation like group_concat etc
|
||||
* regardless of the type of the property used inside the outer function.
|
||||
*/
|
||||
boolean isConcat(String outerFunction);
|
||||
|
||||
/**
|
||||
* Return a builder for the AggregateFormulaContext.
|
||||
*/
|
||||
static Builder builder() {
|
||||
return new AggregateFormulaContextBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for the AggregateFormulaContext.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Override the default set of aggregation functions.
|
||||
*/
|
||||
Builder aggregateFunctions(Set<String> count);
|
||||
|
||||
/**
|
||||
* Override the default set of concat functions.
|
||||
*/
|
||||
Builder concatFunctions(Set<String> concat);
|
||||
|
||||
/**
|
||||
* Override the default set of count functions.
|
||||
*/
|
||||
Builder countFunctions(Set<String> count);
|
||||
|
||||
/**
|
||||
* Build the AggregateFormulaContext.
|
||||
*/
|
||||
AggregateFormulaContext build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
final class AggregateFormulaContextBuilder implements AggregateFormulaContext.Builder {
|
||||
|
||||
private Set<String> aggFunctions = Set.of("count", "max", "min", "avg", "sum", "group_concat", "string_agg", "listagg");
|
||||
private Set<String> concat = Set.of("concat", "group_concat", "string_agg", "listagg");
|
||||
private Set<String> count = Set.of("count");
|
||||
|
||||
@Override
|
||||
public AggregateFormulaContext.Builder aggregateFunctions(Set<String> agg) {
|
||||
this.aggFunctions = agg;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregateFormulaContext.Builder concatFunctions(Set<String> concat) {
|
||||
this.concat = concat;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregateFormulaContext.Builder countFunctions(Set<String> count) {
|
||||
this.count = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregateFormulaContext build() {
|
||||
return new FormulaContext(aggFunctions, concat, count);
|
||||
}
|
||||
|
||||
private static final class FormulaContext implements AggregateFormulaContext {
|
||||
|
||||
private final Set<String> aggFunctions;
|
||||
private final Set<String> concat;
|
||||
private final Set<String> count;
|
||||
|
||||
private FormulaContext(Set<String> aggFunctions, Set<String> concat, Set<String> count) {
|
||||
this.aggFunctions = aggFunctions;
|
||||
this.concat = concat;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAggregate(String outerFunction) {
|
||||
return aggFunctions.contains(outerFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCount(String outerFunction) {
|
||||
return count.contains(outerFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConcat(String outerFunction) {
|
||||
return concat.contains(outerFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,6 +356,8 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
|
||||
*/
|
||||
private NamingConvention namingConvention = new UnderscoreNamingConvention();
|
||||
|
||||
private AggregateFormulaContext aggregateFormulaContext = AggregateFormulaContext.builder().build();
|
||||
|
||||
/**
|
||||
* Behaviour of updates in JDBC batch to by default include all properties.
|
||||
*/
|
||||
@@ -1279,6 +1281,17 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregateFormulaContext aggregateFormulaContext() {
|
||||
return aggregateFormulaContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseConfig aggregateFormulaContext(AggregateFormulaContext aggregateFormulaContext) {
|
||||
this.aggregateFormulaContext = aggregateFormulaContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllQuotedIdentifiers() {
|
||||
return platformConfig.isAllQuotedIdentifiers();
|
||||
|
||||
@@ -46,6 +46,11 @@ public class DbPlatformTypeMapping {
|
||||
private static final DbPlatformType MULTILINESTRING = new DbPlatformType("multilinestring");
|
||||
private static final DbPlatformType MULTIPOLYGON = new DbPlatformType("multipolygon");
|
||||
|
||||
private static final DbPlatformType VECTOR = new DbPlatformType("vector", 2000, null);
|
||||
private static final DbPlatformType VECTOR_HALF = new DbPlatformType("halfvec", 4000, null);
|
||||
private static final DbPlatformType VECTOR_BIT = new DbPlatformType("bit", 64000, null);
|
||||
private static final DbPlatformType VECTOR_SPARSE = new DbPlatformType("sparsevec", 1000, null);
|
||||
|
||||
private final Map<DbType, DbPlatformType> typeMap = new EnumMap<>(DbType.class);
|
||||
|
||||
/**
|
||||
@@ -93,6 +98,10 @@ public class DbPlatformTypeMapping {
|
||||
put(DbType.MULTIPOINT, MULTIPOINT);
|
||||
put(DbType.MULTILINESTRING, MULTILINESTRING);
|
||||
put(DbType.MULTIPOLYGON, MULTIPOLYGON);
|
||||
put(DbType.VECTOR, VECTOR);
|
||||
put(DbType.VECTOR_HALF, VECTOR_HALF);
|
||||
put(DbType.VECTOR_BIT, VECTOR_BIT);
|
||||
put(DbType.VECTOR_SPARSE, VECTOR_SPARSE);
|
||||
|
||||
if (logicalTypes) {
|
||||
// keep it logical for 2 layer DDL generation
|
||||
|
||||
@@ -51,7 +51,12 @@ public enum DbType {
|
||||
JSONB(ExtraDbTypes.JSONB),
|
||||
JSONCLOB(ExtraDbTypes.JSONClob),
|
||||
JSONBLOB(ExtraDbTypes.JSONBlob),
|
||||
JSONVARCHAR(ExtraDbTypes.JSONVarchar);
|
||||
JSONVARCHAR(ExtraDbTypes.JSONVarchar),
|
||||
|
||||
VECTOR(ExtraDbTypes.VECTOR),
|
||||
VECTOR_HALF(ExtraDbTypes.VECTOR_HALF),
|
||||
VECTOR_BIT(ExtraDbTypes.VECTOR_BIT),
|
||||
VECTOR_SPARSE(ExtraDbTypes.VECTOR_SPARSE);
|
||||
|
||||
private final int id;
|
||||
|
||||
|
||||
@@ -74,4 +74,24 @@ public interface ExtraDbTypes {
|
||||
*/
|
||||
int MULTILINESTRING = 6007;
|
||||
|
||||
/**
|
||||
* PGVector base type
|
||||
*/
|
||||
int VECTOR = 7000;
|
||||
|
||||
/**
|
||||
* PGVector half precision float type
|
||||
*/
|
||||
int VECTOR_HALF = 7001;
|
||||
|
||||
/**
|
||||
* PGVector binary type (bit)
|
||||
*/
|
||||
int VECTOR_BIT = 7002;
|
||||
|
||||
/**
|
||||
* PGVector sparse type
|
||||
*/
|
||||
int VECTOR_SPARSE = 7003;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AggregateFormulaContextTest {
|
||||
|
||||
@Test
|
||||
void defaultContext() {
|
||||
var defaultContext = AggregateFormulaContext.builder().build();
|
||||
for (String aggFunction : List.of("count", "max", "min", "avg", "sum", "group_concat", "string_agg", "listagg")) {
|
||||
assertThat(defaultContext.isAggregate(aggFunction)).isTrue();
|
||||
}
|
||||
for (String c : List.of("concat", "group_concat", "string_agg", "listagg")) {
|
||||
assertThat(defaultContext.isConcat(c)).isTrue();
|
||||
}
|
||||
for (String c : List.of("count")) {
|
||||
assertThat(defaultContext.isCount(c)).isTrue();
|
||||
}
|
||||
|
||||
assertThat(defaultContext.isConcat("junk")).isFalse();
|
||||
assertThat(defaultContext.isCount("junk")).isFalse();
|
||||
assertThat(defaultContext.isAggregate("junk")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideAggregateFunctions() {
|
||||
AggregateFormulaContext mySum = AggregateFormulaContext.builder()
|
||||
.aggregateFunctions(Set.of("my_sum"))
|
||||
.build();
|
||||
|
||||
assertThat(mySum.isAggregate("my_sum")).isTrue();
|
||||
assertThat(mySum.isAggregate("avg")).isFalse();
|
||||
assertThat(mySum.isCount("count")).isTrue();
|
||||
assertThat(mySum.isConcat("group_concat")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideConcatFunctions() {
|
||||
AggregateFormulaContext myConcat = AggregateFormulaContext.builder()
|
||||
.concatFunctions(Set.of("my_concat"))
|
||||
.build();
|
||||
|
||||
assertThat(myConcat.isAggregate("avg")).isTrue();
|
||||
assertThat(myConcat.isCount("count")).isTrue();
|
||||
assertThat(myConcat.isConcat("group_concat")).isFalse();
|
||||
assertThat(myConcat.isConcat("my_concat")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideCountFunctions() {
|
||||
AggregateFormulaContext myCount = AggregateFormulaContext.builder()
|
||||
.countFunctions(Set.of("my_count"))
|
||||
.build();
|
||||
|
||||
assertThat(myCount.isAggregate("avg")).isTrue();
|
||||
assertThat(myCount.isCount("count")).isFalse();
|
||||
assertThat(myCount.isCount("my_count")).isTrue();
|
||||
assertThat(myCount.isConcat("group_concat")).isTrue();
|
||||
}
|
||||
}
|
||||
+38
-26
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -89,25 +89,25 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -125,13 +125,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -155,37 +155,37 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-spring-txn</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- platforms -->
|
||||
@@ -193,79 +193,91 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-clickhouse</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-db2</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-h2</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-hana</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mariadb</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mysql</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-nuodb</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-oracle</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgres</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-pgvector</artifactId>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-pgvector-types</artifactId>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlite</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlserver</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</parent>
|
||||
<artifactId>ebean-core-json</artifactId>
|
||||
<name>ebean-core-json</name>
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
+7
-7
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-json</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -52,7 +52,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -165,21 +165,21 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.config.AggregateFormulaContext;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
|
||||
public interface FormulaBuilder {
|
||||
|
||||
STreeProperty create(AggregateFormulaContext context, String formula, String path);
|
||||
|
||||
}
|
||||
@@ -41,7 +41,7 @@ final class NaturalKeyEntryBasic implements NaturalKeyEntry {
|
||||
* Create when query uses an IN PAIRS clause.
|
||||
*/
|
||||
NaturalKeyEntryBasic(BeanNaturalKey naturalKey, List<NaturalKeyEq> eqList,
|
||||
String inMapProperty0, String inMapProperty1, Pairs.Entry pair) {
|
||||
String inMapProperty0, String inMapProperty1, Pairs.Entry pair) {
|
||||
load(eqList);
|
||||
map.put(inMapProperty0, pair.getA());
|
||||
map.put(inMapProperty1, pair.getB());
|
||||
@@ -49,6 +49,14 @@ final class NaturalKeyEntryBasic implements NaturalKeyEntry {
|
||||
this.key = calculateKey(naturalKey);
|
||||
}
|
||||
|
||||
NaturalKeyEntryBasic(BeanNaturalKey naturalKey, List<NaturalKeyEq> eqList,
|
||||
Map<String, Object> properties, Object[] naturalKeyValue) {
|
||||
load(eqList);
|
||||
map.putAll(properties);
|
||||
this.inValue = naturalKeyValue;
|
||||
this.key = calculateKey(naturalKey);
|
||||
}
|
||||
|
||||
private void load(List<NaturalKeyEq> eqList) {
|
||||
if (eqList != null) {
|
||||
for (NaturalKeyEq eq : eqList) {
|
||||
|
||||
@@ -3,10 +3,7 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.Pairs;
|
||||
import io.ebeaninternal.server.deploy.BeanNaturalKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Collects the data for processing the natural key cache processing.
|
||||
@@ -19,8 +16,9 @@ public final class NaturalKeyQueryData<T> {
|
||||
*/
|
||||
private boolean hasIn;
|
||||
// IN Pairs clause - only one allowed
|
||||
private String inProperty0, inProperty1;
|
||||
private List<Pairs.Entry> inPairs;
|
||||
private String[] properties;
|
||||
private List<Object[]> inTuples;
|
||||
// IN clause - only one allowed
|
||||
private List<Object> inValues;
|
||||
private String inProperty;
|
||||
@@ -47,14 +45,39 @@ public final class NaturalKeyQueryData<T> {
|
||||
}
|
||||
if (matchProperty(property0) && matchProperty(property1)) {
|
||||
this.hasIn = true;
|
||||
this.inProperty0 = property0;
|
||||
this.inProperty1 = property1;
|
||||
this.properties = new String[]{property0, property1};
|
||||
this.inPairs = new ArrayList<>(inPairs); // will be modified
|
||||
return this.inPairs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match for In Tuples expression. We only allow one IN clause.
|
||||
*/
|
||||
public List<Object[]> matchInTuples(String[] properties, List<Object[]> inTuples) {
|
||||
if (hasIn) {
|
||||
// only 1 IN allowed (to project naturalIds)
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean matchAll = true;
|
||||
|
||||
for (String property : properties) {
|
||||
if (!matchProperty(property)) {
|
||||
matchAll = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchAll) {
|
||||
this.hasIn = true;
|
||||
this.properties = Arrays.copyOf(properties, properties.length);
|
||||
this.inTuples = new ArrayList<>(inTuples);
|
||||
return this.inTuples;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match for IN expression. We only allow one IN clause.
|
||||
*/
|
||||
@@ -100,6 +123,8 @@ public final class NaturalKeyQueryData<T> {
|
||||
addInValues();
|
||||
} else if (inPairs != null) {
|
||||
addInPairs();
|
||||
} else if (inTuples != null) {
|
||||
addInTuples();
|
||||
} else {
|
||||
addEqualsKey();
|
||||
}
|
||||
@@ -110,7 +135,17 @@ public final class NaturalKeyQueryData<T> {
|
||||
// a findList() with an IN Map clause so we project
|
||||
// for every IN value a natural key combination
|
||||
for (Pairs.Entry entry : inPairs) {
|
||||
set.add(new NaturalKeyEntryBasic(naturalKey, eqList, inProperty0, inProperty1, entry));
|
||||
set.add(new NaturalKeyEntryBasic(naturalKey, eqList, properties[0], properties[1], entry));
|
||||
}
|
||||
}
|
||||
|
||||
private void addInTuples() {
|
||||
for (Object[] inTuple : inTuples) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
for (int i = 0; i < inTuple.length; i++) {
|
||||
map.put(properties[i], inTuple[i]);
|
||||
}
|
||||
set.add(new NaturalKeyEntryBasic(naturalKey, eqList, map, inTuple));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,11 +187,8 @@ public final class NaturalKeyQueryData<T> {
|
||||
if (inProperty != null) {
|
||||
exprProps.add(inProperty);
|
||||
}
|
||||
if (inProperty0 != null) {
|
||||
exprProps.add(inProperty0);
|
||||
}
|
||||
if (inProperty1 != null) {
|
||||
exprProps.add(inProperty1);
|
||||
if (properties != null) {
|
||||
exprProps.addAll(Arrays.asList(properties));
|
||||
}
|
||||
if (eqList != null) {
|
||||
for (NaturalKeyEq eq : eqList) {
|
||||
@@ -173,6 +205,7 @@ public final class NaturalKeyQueryData<T> {
|
||||
int defined = (inValues == null) ? 0 : 1;
|
||||
defined += (inPairs == null) ? 0 : 2;
|
||||
defined += (eqList == null) ? 0 : eqList.size();
|
||||
defined += (inTuples == null) ? 0 : properties.length;
|
||||
return defined == naturalKey.length();
|
||||
}
|
||||
|
||||
@@ -206,6 +239,9 @@ public final class NaturalKeyQueryData<T> {
|
||||
} else if (inPairs != null) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
inPairs.remove(inValue);
|
||||
} else if (inTuples != null) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
inTuples.remove(inValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,9 @@ public interface SpiBeanType {
|
||||
* or removals from the collection.
|
||||
*/
|
||||
boolean isToManyDirty(EntityBean bean);
|
||||
|
||||
/**
|
||||
* Return the FormulaBuilder for this type.
|
||||
*/
|
||||
FormulaBuilder formulaBuilder();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import io.ebean.*;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
@@ -377,4 +378,6 @@ public interface SpiEbeanServer extends SpiServer, BeanCollectionLoader {
|
||||
|
||||
@Nullable
|
||||
SqlRow findOne(SpiSqlQuery query);
|
||||
|
||||
<T> STreeProperty createFormulaProperty(SpiBeanType desc, String formula, String path);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,4 @@ public interface SpiQueryManyJoin {
|
||||
*/
|
||||
String fetchOrderBy();
|
||||
|
||||
/**
|
||||
* Wrap the filter many expression with a condition allowing lEFT JOIN null matching row.
|
||||
*/
|
||||
String idNullOr(String filterManyExpression);
|
||||
}
|
||||
|
||||
@@ -338,11 +338,6 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
boolean isNestedUseSavepoint();
|
||||
|
||||
/**
|
||||
* Return true if explicitly set to skip cache (ignores skipOnWrite).
|
||||
*/
|
||||
boolean isSkipCacheExplicit();
|
||||
|
||||
/**
|
||||
* Fire pre commit processing/listeners.
|
||||
*/
|
||||
|
||||
@@ -169,11 +169,6 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
transaction.setSkipCache(skipCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return transaction.isSkipCacheExplicit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCache() {
|
||||
return transaction.isSkipCache();
|
||||
|
||||
@@ -113,6 +113,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
private final long slowQueryMicros;
|
||||
private final SlowQueryListener slowQueryListener;
|
||||
private final boolean disableL2Cache;
|
||||
private final AggregateFormulaContext formulaContext;
|
||||
private boolean shutdown;
|
||||
|
||||
/**
|
||||
@@ -128,6 +129,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
this.extraMetrics = config.getExtraMetrics();
|
||||
this.serverName = this.config.getName();
|
||||
this.formulaContext = config.getConfig().aggregateFormulaContext();
|
||||
this.lazyLoadBatchSize = this.config.getLazyLoadBatchSize();
|
||||
this.cqueryEngine = config.getCQueryEngine();
|
||||
this.expressionFactory = config.getExpressionFactory();
|
||||
@@ -928,6 +930,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return findId(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> STreeProperty createFormulaProperty(SpiBeanType desc, String formula, String path) {
|
||||
return desc.formulaBuilder().create(formulaContext, formula, path);
|
||||
}
|
||||
|
||||
<T> SpiOrmQueryRequest<T> createQueryRequest(Type type, SpiQuery<T> query) {
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(type, query);
|
||||
request.prepareQuery();
|
||||
|
||||
@@ -497,10 +497,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return cacheKey != null && query.queryCacheMode().isPut();
|
||||
}
|
||||
|
||||
public boolean isBeanCachePutMany() {
|
||||
return !transaction.isSkipCacheExplicit() && query.isBeanCachePut();
|
||||
}
|
||||
|
||||
public boolean isBeanCachePut() {
|
||||
return !transaction.isSkipCache() && query.isBeanCachePut();
|
||||
}
|
||||
|
||||
@@ -2410,7 +2410,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
*/
|
||||
private STreeProperty findSqlTreeFormula(String formula, String path) {
|
||||
String key = formula + "-" + path;
|
||||
return dynamicProperty.computeIfAbsent(key, (fullKey) -> FormulaPropertyPath.create(this, formula, path));
|
||||
return dynamicProperty.computeIfAbsent(key, (fullKey) -> ebeanServer.createFormulaProperty(this, formula, path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormulaBuilder formulaBuilder() {
|
||||
return new DFormulaBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-1
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy;
|
||||
import io.ebean.config.BeanNotRegisteredException;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
|
||||
import io.ebeaninternal.server.deploy.parse.AnnotationParser;
|
||||
import jakarta.persistence.Column;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -12,6 +13,8 @@ import java.util.Map;
|
||||
*/
|
||||
final class BeanEmbeddedMetaFactory {
|
||||
|
||||
private static final int COLUMN_LENGTH_UNSET = AnnotationParser.COLUMN_LENGTH_UNSET;
|
||||
|
||||
/**
|
||||
* Create BeanProperties for embedded beans using the deployment specific DB column name and table alias.
|
||||
*/
|
||||
@@ -58,7 +61,7 @@ final class BeanEmbeddedMetaFactory {
|
||||
}
|
||||
|
||||
private static int dbLength(Column override, BeanProperty source) {
|
||||
return (override != null && (override.length() != 0)) ? override.length() : source.dbLength();
|
||||
return (override != null && (override.length() != COLUMN_LENGTH_UNSET)) ? override.length() : source.dbLength();
|
||||
}
|
||||
|
||||
private static int dbScale(Column override, BeanProperty source) {
|
||||
|
||||
@@ -36,11 +36,6 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
return "prefix:" + prefix + " name:" + name + " dbColumn:" + dbColumn + " ph:" + placeHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idNullOr(String filterManyExpression) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAggregation() {
|
||||
return false;
|
||||
|
||||
@@ -513,11 +513,6 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return owningType.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idNullOr(String filterManyExpression) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadIgnore(DbReadContext ctx) {
|
||||
ctx.dataReader().incrementPos(1);
|
||||
|
||||
@@ -634,11 +634,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return fetchOrderBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idNullOr(String filterManyExpression) {
|
||||
return targetIdBinder.idNullOr(name, filterManyExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order by for use when lazy loading the associated collection.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.config.AggregateFormulaContext;
|
||||
import io.ebeaninternal.api.FormulaBuilder;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
|
||||
final class DFormulaBuilder implements FormulaBuilder {
|
||||
|
||||
private final BeanDescriptor<?> descriptor;
|
||||
|
||||
DFormulaBuilder(BeanDescriptor<?> descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreeProperty create(AggregateFormulaContext context, String formula, String path) {
|
||||
return FormulaPropertyPath.create(descriptor, context, formula, path);
|
||||
}
|
||||
}
|
||||
@@ -154,4 +154,9 @@ public interface DbSqlContext {
|
||||
* as it was already added to the query.
|
||||
*/
|
||||
boolean joinAdded();
|
||||
|
||||
/**
|
||||
* Include the filter many predicates if specified into the JOIN clause.
|
||||
*/
|
||||
void includeFilterMany();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.config.AggregateFormulaContext;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
|
||||
@@ -9,11 +10,10 @@ import java.util.Set;
|
||||
|
||||
final class FormulaPropertyPath {
|
||||
|
||||
private static final String[] AGG_FUNCTIONS = {"count", "max", "min", "avg", "sum"};
|
||||
|
||||
private static final String DISTINCT_ = "distinct ";
|
||||
|
||||
private final BeanDescriptor<?> descriptor;
|
||||
private final AggregateFormulaContext context;
|
||||
private final String formula;
|
||||
private final String outerFunction;
|
||||
private final String internalExpression;
|
||||
@@ -24,12 +24,13 @@ final class FormulaPropertyPath {
|
||||
private String cast;
|
||||
private String alias;
|
||||
|
||||
static STreeProperty create(BeanDescriptor<?> descriptor, String formula, String path) {
|
||||
return new FormulaPropertyPath(descriptor, formula, path).build();
|
||||
static STreeProperty create(BeanDescriptor<?> descriptor, AggregateFormulaContext context, String formula, String path) {
|
||||
return new FormulaPropertyPath(descriptor, context, formula, path).build();
|
||||
}
|
||||
|
||||
FormulaPropertyPath(BeanDescriptor<?> descriptor, String formula, String path) {
|
||||
FormulaPropertyPath(BeanDescriptor<?> descriptor, AggregateFormulaContext context, String formula, String path) {
|
||||
this.descriptor = descriptor;
|
||||
this.context = context;
|
||||
this.formula = formula;
|
||||
int openBracket = formula.indexOf('(');
|
||||
int closeBracket = formula.lastIndexOf(')');
|
||||
@@ -106,10 +107,10 @@ final class FormulaPropertyPath {
|
||||
}
|
||||
return create(scalarType);
|
||||
}
|
||||
if (isCount()) {
|
||||
if (context.isCount(outerFunction)) {
|
||||
return create(descriptor.scalarType(Types.BIGINT));
|
||||
}
|
||||
if (isConcat()) {
|
||||
if (context.isConcat(outerFunction)) {
|
||||
return create(descriptor.scalarType(Types.VARCHAR));
|
||||
}
|
||||
if (firstProp == null) {
|
||||
@@ -144,12 +145,7 @@ final class FormulaPropertyPath {
|
||||
}
|
||||
|
||||
private boolean isAggregate() {
|
||||
for (String aggFunction : AGG_FUNCTIONS) {
|
||||
if (aggFunction.equals(outerFunction)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return context.isAggregate(outerFunction);
|
||||
}
|
||||
|
||||
private String buildFormula(String parsed) {
|
||||
|
||||
@@ -27,11 +27,6 @@ public interface IdBinder {
|
||||
*/
|
||||
void initialise();
|
||||
|
||||
/**
|
||||
* Wrap the filter many expression with a condition allowing lEFT JOIN null matching row.
|
||||
*/
|
||||
String idNullOr(String name, String filterManyExpression);
|
||||
|
||||
String idSelect();
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,20 +45,6 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
this.idInValueSql = idInExpandedForm ? idInExpanded() : idInCompressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idNullOr(String prefix, String filterManyExpression) {
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append("((");
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append("${").append(prefix).append('}').append(props[i].dbColumn()).append(" is null");
|
||||
}
|
||||
sb.append(") or (").append(filterManyExpression).append("))");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idSelect() {
|
||||
return embIdProperty.name();
|
||||
|
||||
@@ -28,11 +28,6 @@ final class IdBinderEmpty implements IdBinder {
|
||||
public void initialise() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idNullOr(String name, String filterManyExpression) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idSelect() {
|
||||
return "";
|
||||
|
||||
@@ -261,9 +261,4 @@ public final class IdBinderSimple implements IdBinder {
|
||||
final Object value = idProperty.getValue(bean);
|
||||
return scalarType.format(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idNullOr(String prefix, String filterManyExpression) {
|
||||
return "(${" + prefix + "}" + idProperty.dbColumn() + " is null or (" + filterManyExpression + "))";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -804,11 +804,9 @@ public class DeployBeanDescriptor<T> {
|
||||
* Return the defaultSelectClause using FetchType.LAZY and FetchType.EAGER.
|
||||
*/
|
||||
public String getDefaultSelectClause() {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
boolean hasLazyFetch = false;
|
||||
|
||||
for (DeployBeanProperty prop : propMap.values()) {
|
||||
if (!prop.isTransient() && !(prop instanceof DeployBeanPropertyAssocMany<?>)) {
|
||||
if (prop.isFetchEager()) {
|
||||
|
||||
@@ -617,6 +617,8 @@ public class DeployBeanProperty {
|
||||
this.dbRead = true;
|
||||
this.dbInsertable = false;
|
||||
this.dbUpdateable = false;
|
||||
// aggregation by default not fetchEager
|
||||
this.fetchEager = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+28
-2
@@ -5,10 +5,14 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -19,6 +23,11 @@ import java.util.UUID;
|
||||
*/
|
||||
public abstract class AnnotationParser extends AnnotationBase {
|
||||
|
||||
/**
|
||||
* The default/unset value for {@code @Column.length()}. Ebean API uses 0 and JPA uses 255.
|
||||
*/
|
||||
public static final int COLUMN_LENGTH_UNSET = _ColumnProbeHolder.class.getAnnotation(_ColumnLengthProbe.class).value().length();
|
||||
|
||||
final DeployBeanInfo<?> info;
|
||||
final DeployBeanDescriptor<?> descriptor;
|
||||
final Class<?> beanType;
|
||||
@@ -98,7 +107,7 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
prop.setUnique(columnAnn.unique());
|
||||
if (columnAnn.precision() > 0) {
|
||||
prop.setDbLength(columnAnn.precision());
|
||||
} else if (columnAnn.length() != 0) {
|
||||
} else if (columnAnn.length() != COLUMN_LENGTH_UNSET) {
|
||||
// set default 255 on DbTypeMap
|
||||
prop.setDbLength(columnAnn.length());
|
||||
}
|
||||
@@ -132,4 +141,21 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
protected String processFormula(String source) {
|
||||
return source == null ? null : source.replace("${dbTableName}", descriptor.getBaseTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe annotation used solely to read the default value of {@code @Column.length()}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@interface _ColumnLengthProbe {
|
||||
Column value() default @Column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder so we can read a bare {@code @Column} instance.
|
||||
*/
|
||||
@_ColumnLengthProbe
|
||||
private static final class _ColumnProbeHolder {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -286,8 +286,4 @@ public final class ElPropertyChain implements ElPropertyValue {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idNullOr(String filterManyExpression) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -12,7 +12,7 @@ final class InTuplesExpression extends AbstractExpression {
|
||||
|
||||
private final boolean not;
|
||||
private final String[] properties;
|
||||
private final List<Object[]> entries;
|
||||
private List<Object[]> entries;
|
||||
|
||||
InTuplesExpression(InTuples pairs, boolean not) {
|
||||
super("");
|
||||
@@ -25,7 +25,15 @@ final class InTuplesExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
return false;
|
||||
if (not) {
|
||||
return false;
|
||||
}
|
||||
List<Object[]> copy = data.matchInTuples(properties, entries);
|
||||
if (copy == null) {
|
||||
return false;
|
||||
}
|
||||
entries = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -329,6 +329,13 @@ public final class Binder {
|
||||
geoTypeBinder.bind(b, dataType, data);
|
||||
break;
|
||||
|
||||
case DbPlatformType.VECTOR:
|
||||
case DbPlatformType.VECTOR_HALF:
|
||||
case DbPlatformType.VECTOR_BIT:
|
||||
case DbPlatformType.VECTOR_SPARSE:
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
case java.sql.Types.OTHER:
|
||||
b.setObject(data, dataType);
|
||||
break;
|
||||
|
||||
@@ -280,7 +280,7 @@ final class CQueryBuilder {
|
||||
sql = wrapSelectCount(sql);
|
||||
} else if (wrap || query.isRawSql()) {
|
||||
// remove order by - mssql does not accept order by in subqueries
|
||||
int pos = sql.lastIndexOf(" order by ");
|
||||
int pos = lastTopLevelOrderBy(sql);
|
||||
if (pos != -1) {
|
||||
sql = sql.substring(0, pos);
|
||||
}
|
||||
@@ -300,6 +300,27 @@ final class CQueryBuilder {
|
||||
return request.descriptor().includesAggregation(query.detail());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the last " order by " that is not inside parentheses (i.e. not inside a subquery).
|
||||
* Returns the position or -1 if not found.
|
||||
*/
|
||||
static int lastTopLevelOrderBy(String sql) {
|
||||
String target = " order by ";
|
||||
int depth = 0;
|
||||
int lastFound = -1;
|
||||
for (int i = 0; i < sql.length(); i++) {
|
||||
char c = sql.charAt(i);
|
||||
if (c == '(') {
|
||||
depth++;
|
||||
} else if (c == ')') {
|
||||
depth--;
|
||||
} else if (depth == 0 && c == ' ' && sql.regionMatches(true, i, target, 0, target.length())) {
|
||||
lastFound = i;
|
||||
}
|
||||
}
|
||||
return lastFound;
|
||||
}
|
||||
|
||||
private String wrapSelectCount(String sql) {
|
||||
sql = "select count(*) from ( " + sql + ")";
|
||||
if (selectCountWithAlias) {
|
||||
@@ -715,7 +736,7 @@ final class CQueryBuilder {
|
||||
appendHistoryAsOfPredicate();
|
||||
appendFindId();
|
||||
appendToWhere(predicates.dbWhere());
|
||||
appendToWhere(predicates.dbFilterMany());
|
||||
appendToWhere(predicates.dbFilterManyWhere());
|
||||
if (!query.isIncludeSoftDeletes()) {
|
||||
appendSoftDelete();
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ public final class CQueryPredicates {
|
||||
private final Object idValue;
|
||||
private final BindParams bindParams;
|
||||
private DefaultExpressionRequest filterMany;
|
||||
private boolean filterManyJoin;
|
||||
/**
|
||||
* Bind values from the where expressions.
|
||||
*/
|
||||
@@ -106,6 +107,9 @@ public final class CQueryPredicates {
|
||||
dataBind.append(", ");
|
||||
}
|
||||
}
|
||||
if (filterManyJoin) {
|
||||
filterMany.bind(dataBind);
|
||||
}
|
||||
if (idValue != null) {
|
||||
// this is a find by id type query...
|
||||
request.descriptor().bindId(dataBind, idValue);
|
||||
@@ -119,7 +123,7 @@ public final class CQueryPredicates {
|
||||
if (where != null) {
|
||||
where.bind(dataBind);
|
||||
}
|
||||
if (filterMany != null) {
|
||||
if (!filterManyJoin && filterMany != null) {
|
||||
filterMany.bind(dataBind);
|
||||
}
|
||||
if (having != null) {
|
||||
@@ -189,9 +193,10 @@ public final class CQueryPredicates {
|
||||
if (chunk != null) {
|
||||
SpiExpressionList<?> filterManyExpr = chunk.getFilterMany();
|
||||
if (filterManyExpr != null) {
|
||||
this.filterMany = new DefaultExpressionRequest(request, deployParser, binder, filterManyExpr);
|
||||
filterManyJoin = chunk.isFilterManyJoin();
|
||||
filterMany = new DefaultExpressionRequest(request, deployParser, binder, filterManyExpr);
|
||||
if (buildSql) {
|
||||
dbFilterMany = manyProperty.idNullOr(filterMany.buildSql());
|
||||
dbFilterMany = filterMany.buildSql();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,10 +344,17 @@ public final class CQueryPredicates {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a db filter for filtering many fetch joins.
|
||||
* Return a db filter to be included in the WHERE.
|
||||
*/
|
||||
String dbFilterMany() {
|
||||
return dbFilterMany;
|
||||
String dbFilterManyWhere() {
|
||||
return filterManyJoin ? null : dbFilterMany;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a db filter to be included in the JOIN.
|
||||
*/
|
||||
String dbFilterManyJoin() {
|
||||
return filterManyJoin ? dbFilterMany : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,8 @@ final class DFetchGroup<T> implements SpiFetchGroup<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrmQueryDetail detail() {
|
||||
return detail.copy();
|
||||
public OrmQueryDetail detail(OrmQueryDetail existing) {
|
||||
return detail.copy(existing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,6 +22,7 @@ final class DefaultDbSqlContext implements DbSqlContext {
|
||||
private final ArrayStack<String> joinStack = new ArrayStack<>();
|
||||
private final ArrayStack<String> prefixStack = new ArrayStack<>();
|
||||
private final String fromForUpdate;
|
||||
private final String dbFilterManyJoin;
|
||||
private boolean useColumnAlias;
|
||||
private int columnIndex;
|
||||
private int asOfTableCount;
|
||||
@@ -41,7 +42,7 @@ final class DefaultDbSqlContext implements DbSqlContext {
|
||||
private boolean joinSuppressed;
|
||||
|
||||
DefaultDbSqlContext(SqlTreeAlias alias, String columnAliasPrefix, CQueryHistorySupport historySupport,
|
||||
CQueryDraftSupport draftSupport, String fromForUpdate) {
|
||||
CQueryDraftSupport draftSupport, String fromForUpdate, String dbFilterManyJoin) {
|
||||
this.alias = alias;
|
||||
this.columnAliasPrefix = columnAliasPrefix;
|
||||
this.useColumnAlias = columnAliasPrefix != null;
|
||||
@@ -49,6 +50,14 @@ final class DefaultDbSqlContext implements DbSqlContext {
|
||||
this.historySupport = historySupport;
|
||||
this.historyQuery = (historySupport != null);
|
||||
this.fromForUpdate = fromForUpdate;
|
||||
this.dbFilterManyJoin = dbFilterManyJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void includeFilterMany() {
|
||||
if (dbFilterManyJoin != null) {
|
||||
sb.append(" and ").append(dbFilterManyJoin);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,7 +52,7 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Query<T> select(FetchGroup fetchGroup) {
|
||||
this.detail = ((SpiFetchGroup) fetchGroup).detail();
|
||||
this.detail = ((SpiFetchGroup) fetchGroup).detail(detail);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> usingMaster() {
|
||||
public Query<T> usingMaster(boolean useMaster) {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ public final class DefaultOrmQueryEngine implements OrmQueryEngine {
|
||||
result = finder.postProcessMany(request, result);
|
||||
}
|
||||
|
||||
if (result != null && request.isBeanCachePutMany()) {
|
||||
if (result != null && request.isBeanCachePut()) {
|
||||
// load the individual beans into the bean cache
|
||||
request.descriptor().cacheBeanPutAll(result.actualDetails());
|
||||
}
|
||||
|
||||
+11
-1
@@ -14,6 +14,7 @@ import io.ebeaninternal.server.core.RelationalQueryRequest;
|
||||
import io.ebeaninternal.server.core.RowReader;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
|
||||
import jakarta.persistence.NonUniqueResultException;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
import java.sql.SQLException;
|
||||
@@ -115,9 +116,14 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.BEAN);
|
||||
T value = request.mapOne(mapper);
|
||||
if (request.next()) {
|
||||
throw new NonUniqueResultException("Got more than 1 result for findOne");
|
||||
}
|
||||
request.logSummary();
|
||||
return value;
|
||||
|
||||
} catch (NonUniqueResultException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
@@ -160,13 +166,17 @@ public final class DefaultRelationalQueryEngine implements RelationalQueryEngine
|
||||
T value = null;
|
||||
if (dataReader.next()) {
|
||||
value = scalarType.read(dataReader);
|
||||
if (dataReader.next()) {
|
||||
throw new NonUniqueResultException("Got more than 1 result for findSingleAttribute");
|
||||
}
|
||||
}
|
||||
request.logSummary();
|
||||
return value;
|
||||
|
||||
} catch (NonUniqueResultException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(errMsg(e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public final class SqlTreeBuilder {
|
||||
CQueryHistorySupport historySupport = builder.historySupport(query);
|
||||
CQueryDraftSupport draftSupport = builder.draftSupport(query);
|
||||
String colAlias = subQuery || rootNode.isSingleProperty() ? null : columnAliasPrefix;
|
||||
this.ctx = new DefaultDbSqlContext(alias, colAlias, historySupport, draftSupport, fromForUpdate);
|
||||
this.ctx = new DefaultDbSqlContext(alias, colAlias, historySupport, draftSupport, fromForUpdate, predicates.dbFilterManyJoin());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,10 +371,11 @@ public final class SqlTreeBuilder {
|
||||
IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes, manyWhereJoins, temporalMode);
|
||||
Collection<SqlTreeNodeExtraJoin> extraJoins = extraJoinDistill.getExtraJoinRootNodes();
|
||||
if (!extraJoins.isEmpty()) {
|
||||
// add extra joins required to support predicates
|
||||
// and/or order by clause
|
||||
// add extra joins required to support predicates and/or order by clause
|
||||
for (SqlTreeNodeExtraJoin extraJoin : extraJoins) {
|
||||
myList.add(extraJoin);
|
||||
if (!addToParent(extraJoin, myList)) {
|
||||
myList.add(extraJoin);
|
||||
}
|
||||
if (extraJoin.isManyJoin()) {
|
||||
// as we are now going to join to the many then we need
|
||||
// to add the distinct to the sql query to stop duplicate
|
||||
@@ -385,6 +386,20 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the extra join was added as a child to one of the nodes.
|
||||
*/
|
||||
private boolean addToParent(SqlTreeNodeExtraJoin extraJoin, List<SqlTreeNode> myList) {
|
||||
String parentPath = SplitName.split(extraJoin.prefix())[0];
|
||||
for (SqlTreeNode maybeParent : myList) {
|
||||
if (maybeParent.prefix().equals(parentPath)) {
|
||||
maybeParent.addChild(extraJoin);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A subQuery has slightly different rules in that it just generates SQL (into
|
||||
* the where clause) and its properties are not required to read the resultSet
|
||||
@@ -641,7 +656,7 @@ public final class SqlTreeBuilder {
|
||||
SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin);
|
||||
// register the root because these are the only ones we
|
||||
// return back.
|
||||
rootRegister.put(root.name(), root);
|
||||
rootRegister.put(root.prefix(), root);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,4 +84,12 @@ interface SqlTreeNode {
|
||||
*/
|
||||
default void unselectLobsForPlatform() {
|
||||
}
|
||||
|
||||
default String prefix() {
|
||||
return ""; // not matched
|
||||
}
|
||||
|
||||
default void addChild(SqlTreeNode extraJoin) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,16 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
this.pathMap = createPathMap(prefix, desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChild(SqlTreeNode extraJoin) {
|
||||
children.add(extraJoin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlTreeLoad createLoad() {
|
||||
return new SqlTreeLoadBean(this);
|
||||
|
||||
@@ -87,7 +87,8 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
return manyJoin;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
@Override
|
||||
public String prefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,5 +45,6 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
@Override
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
super.appendFrom(ctx, joinType.autoToOuter());
|
||||
ctx.includeFilterMany();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ public final class DefaultDtoQuery<T> extends AbstractQuery implements SpiDtoQue
|
||||
}
|
||||
|
||||
@Override
|
||||
public DtoQuery<T> usingMaster() {
|
||||
this.useMaster = true;
|
||||
public DtoQuery<T> usingMaster(boolean useMaster) {
|
||||
this.useMaster = useMaster;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -769,7 +769,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
copy.useQueryCache = useQueryCache;
|
||||
copy.unmodifiable = unmodifiable;
|
||||
if (detail != null) {
|
||||
copy.detail = detail.copy();
|
||||
copy.detail = detail.copy(null);
|
||||
}
|
||||
copy.temporalMode = temporalMode;
|
||||
copy.firstRow = firstRow;
|
||||
@@ -1387,7 +1387,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
@Override
|
||||
public final Query<T> select(FetchGroup<T> fetchGroup) {
|
||||
if (fetchGroup != null) {
|
||||
this.detail = ((SpiFetchGroup<T>) fetchGroup).detail();
|
||||
this.detail = ((SpiFetchGroup<T>) fetchGroup).detail(detail);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -1475,8 +1475,8 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> usingMaster() {
|
||||
this.useMaster = true;
|
||||
public Query<T> usingMaster(boolean useMaster) {
|
||||
this.useMaster = useMaster;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -60,8 +60,8 @@ public final class DefaultRelationalQuery extends AbstractQuery implements SpiSq
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlQuery usingMaster() {
|
||||
this.useMaster = true;
|
||||
public SqlQuery usingMaster(boolean useMaster) {
|
||||
this.useMaster = useMaster;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,12 +39,21 @@ public final class OrmQueryDetail implements Serializable {
|
||||
/**
|
||||
* Return a deep copy of the OrmQueryDetail.
|
||||
*/
|
||||
public OrmQueryDetail copy() {
|
||||
public OrmQueryDetail copy(OrmQueryDetail existing) {
|
||||
OrmQueryDetail copy = new OrmQueryDetail();
|
||||
copy.baseProps = baseProps.copy();
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
|
||||
copy.fetchPaths.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
if (existing != null) {
|
||||
// transfer any existing filterMany expressions
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : existing.fetchPaths.entrySet()) {
|
||||
var filterMany = entry.getValue().getFilterMany();
|
||||
if (filterMany != null) {
|
||||
copy.getChunk(entry.getKey(), true).setFilterMany(filterMany);
|
||||
}
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,8 +171,9 @@ public final class OrmQueryProperties implements Serializable {
|
||||
if (filterMany == null) {
|
||||
FilterExprPath exprPath = new FilterExprPath(path);
|
||||
SpiExpressionFactory queryEf = (SpiExpressionFactory) rootQuery.getExpressionFactory();
|
||||
ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath);
|
||||
ExpressionFactory filterEf = queryEf.createExpressionFactory();
|
||||
filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
|
||||
// assuming conditions supported in JOIN, not setting markForQueryJoin = true
|
||||
}
|
||||
return filterMany;
|
||||
}
|
||||
@@ -187,6 +188,13 @@ public final class OrmQueryProperties implements Serializable {
|
||||
return filterMany.trimPath(trimPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is a filterMany and it should be included in the JOIN.
|
||||
*/
|
||||
public boolean isFilterManyJoin() {
|
||||
return filterMany != null && !markForQueryJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust filterMany expressions for inclusion in main query.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ public interface SpiFetchGroup<T> extends FetchGroup<T> {
|
||||
/**
|
||||
* Return the detail to use for query execution.
|
||||
*/
|
||||
OrmQueryDetail detail();
|
||||
OrmQueryDetail detail(OrmQueryDetail existing);
|
||||
|
||||
/**
|
||||
* Return the underlying detail for copy purposes.
|
||||
|
||||
+1
-5
@@ -20,7 +20,7 @@ public final class DocStoreTransactionManager extends TransactionManager {
|
||||
|
||||
@Override
|
||||
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
|
||||
return createTransaction(explicit, null);
|
||||
return new DocStoreOnlyTransaction(explicit, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,8 +28,4 @@ public final class DocStoreTransactionManager extends TransactionManager {
|
||||
return new DocStoreOnlyTransaction(false, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpiTransaction createTransaction(boolean explicit, Connection c) {
|
||||
return new DocStoreOnlyTransaction(explicit, this);
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -150,11 +150,6 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkipCache(boolean skipCache) {
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* Ebean transaction used for AutoCommit true connections.
|
||||
*/
|
||||
final class JdbcAutoCommitTransaction extends JdbcTransaction {
|
||||
|
||||
JdbcAutoCommitTransaction(boolean explicit, Connection connection, @Nullable TransactionManager manager) {
|
||||
super(true, explicit, connection, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
void performRollback() {
|
||||
long offset = profileOffset();
|
||||
if (profileStream != null) {
|
||||
profileStream.addEvent(EVT_ROLLBACK, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void performCommit() {
|
||||
long offset = profileOffset();
|
||||
if (profileStream != null) {
|
||||
profileStream.addEvent(EVT_COMMIT, offset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -90,13 +90,17 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
*/
|
||||
private final boolean skipCacheAfterWrite;
|
||||
DocStoreTransaction docStoreTxn;
|
||||
private ProfileStream profileStream;
|
||||
protected ProfileStream profileStream;
|
||||
private ProfileLocation profileLocation;
|
||||
private final Instant startTime = Instant.now();
|
||||
private final long startNanos;
|
||||
private boolean autoPersistUpdates;
|
||||
|
||||
JdbcTransaction(boolean explicit, Connection connection, TransactionManager manager) {
|
||||
this(false, explicit, connection, manager);
|
||||
}
|
||||
|
||||
JdbcTransaction(boolean autoCommit, boolean explicit, Connection connection, TransactionManager manager) {
|
||||
try {
|
||||
this.active = true;
|
||||
this.explicit = explicit;
|
||||
@@ -125,7 +129,9 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.onQueryOnlyCommit = true;
|
||||
}
|
||||
|
||||
checkAutoCommit(connection);
|
||||
if (!autoCommit) {
|
||||
checkAutoCommit(connection);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
@@ -203,11 +209,6 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
return autoPersistUpdates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isSkipCacheExplicit() {
|
||||
return (skipCache != null && !skipCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isSkipCache() {
|
||||
if (skipCache != null) return skipCache;
|
||||
|
||||
@@ -235,11 +235,6 @@ final class NoTransaction implements SpiTransaction {
|
||||
public void setSkipCache(boolean skipCache) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCacheExplicit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipCache() {
|
||||
return false;
|
||||
|
||||
@@ -13,9 +13,18 @@ import java.sql.SQLException;
|
||||
abstract class TransactionFactory {
|
||||
|
||||
final TransactionManager manager;
|
||||
private final boolean autoCommitMode;
|
||||
|
||||
TransactionFactory(TransactionManager manager) {
|
||||
this.manager = manager;
|
||||
this.autoCommitMode = manager.isAutoCommitMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new transaction.
|
||||
*/
|
||||
SpiTransaction createTransaction(boolean explicit, Connection connection) {
|
||||
return autoCommitMode ? new JdbcAutoCommitTransaction(explicit, connection, manager) : new JdbcTransaction(explicit, connection, manager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ class TransactionFactoryBasic extends TransactionFactory {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = dataSource.getConnection();
|
||||
SpiTransaction t = manager.createTransaction(explicit, connection);
|
||||
SpiTransaction t = createTransaction(explicit, connection);
|
||||
return setIsolationLevel(t, isolationLevel);
|
||||
} catch (PersistenceException ex) {
|
||||
JdbcClose.close(connection);
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class TransactionFactoryTenant extends TransactionFactory {
|
||||
try {
|
||||
Object tenantId = tenantProvider.currentId();
|
||||
connection = dataSourceSupplier.connection(tenantId);
|
||||
SpiTransaction transaction = manager.createTransaction(explicit, connection);
|
||||
SpiTransaction transaction = createTransaction(explicit, connection);
|
||||
transaction.setTenantId(tenantId);
|
||||
return setIsolationLevel(transaction, isolationLevel);
|
||||
|
||||
|
||||
+7
-8
@@ -5,12 +5,12 @@ import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.annotation.TxType;
|
||||
import io.ebean.cache.ServerCacheNotification;
|
||||
import io.ebean.cache.ServerCacheNotify;
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.datasource.DataSourcePool;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
@@ -106,6 +106,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
private final ServerCacheNotify cacheNotify;
|
||||
private final boolean supportsSavepointId;
|
||||
private final ConcurrentHashMap<String, ProfileLocation> profileLocations = new ConcurrentHashMap<>();
|
||||
private final boolean autoCommitMode;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
@@ -115,6 +116,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
this.logManager = options.logManager;
|
||||
this.databasePlatform = options.config.getDatabasePlatform();
|
||||
this.supportsSavepointId = databasePlatform.supportsSavepointId();
|
||||
this.autoCommitMode = databasePlatform.platform() == Platform.CLICKHOUSE;
|
||||
this.skipCacheAfterWrite = options.config.isSkipCacheAfterWrite();
|
||||
this.notifyL2CacheInForeground = options.notifyL2CacheInForeground;
|
||||
this.autoPersistUpdates = options.config.isAutoPersistUpdates();
|
||||
@@ -206,6 +208,10 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isAutoCommitMode() {
|
||||
return autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the DB platform supports SavepointId().
|
||||
*/
|
||||
@@ -298,13 +304,6 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
return transactionFactory.createReadOnlyTransaction(tenantId, useMaster);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new transaction.
|
||||
*/
|
||||
SpiTransaction createTransaction(boolean explicit, Connection c) {
|
||||
return new JdbcTransaction(explicit, c, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a local rolled back transaction.
|
||||
*/
|
||||
|
||||
@@ -216,6 +216,9 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
found = checkInheritedTypes(type);
|
||||
}
|
||||
if (found instanceof ScalarTypeClass) {
|
||||
log.log(WARNING, "@Column mapping for type Class is deprecated. Please refer to https://ebean.io/docs/deprecated#class-mapping");
|
||||
}
|
||||
return found != ScalarTypeNotFound.INSTANCE ? found : null; // Do not return ScalarTypeNotFound, otherwise checks will fail
|
||||
}
|
||||
|
||||
@@ -371,7 +374,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private ScalarType<?> createJsonObjectMapperType(DeployBeanProperty prop, int dbType, DocPropertyType docType) {
|
||||
if (jsonMapper == null) {
|
||||
throw new IllegalArgumentException("Unsupported @DbJson mapping - Jackson ObjectMapper not present for " + prop);
|
||||
throw new IllegalArgumentException("Unsupported @DbJson mapping - Missing dependency ebean-jackson-mapper? Jackson ObjectMapper not present for " + prop);
|
||||
}
|
||||
if (MutationDetection.DEFAULT == prop.getMutationDetection()) {
|
||||
prop.setMutationDetection(jsonManager.mutationDetection());
|
||||
|
||||
@@ -10,7 +10,7 @@ import jakarta.persistence.PersistenceException;
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
final class ScalarTypeClass extends ScalarTypeBaseVarchar<Class> {
|
||||
|
||||
public ScalarTypeClass() {
|
||||
ScalarTypeClass() {
|
||||
super(Class.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ module io.ebean.core {
|
||||
exports io.ebeaninternal.server.querydefn to io.ebean.autotune, io.ebean.querybean, io.ebean.test, io.ebean.elastic;
|
||||
exports io.ebeaninternal.server.rawsql to io.ebean.test;
|
||||
exports io.ebeaninternal.server.json to io.ebean.test, io.ebean.elastic;
|
||||
exports io.ebeaninternal.server.type to io.ebean.postgis, io.ebean.test, io.ebean.postgis.types;
|
||||
exports io.ebeaninternal.server.type to io.ebean.postgis, io.ebean.test, io.ebean.postgis.types, io.ebean.pgvector;
|
||||
exports io.ebeaninternal.server.transaction to io.ebean.test, io.ebean.elastic, io.ebean.spring.txn, io.ebean.k8scache;
|
||||
exports io.ebeaninternal.server.util to io.ebean.querybean;
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.config.AggregateFormulaContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
|
||||
@@ -7,11 +8,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class FormulaPropertyPathTest extends BaseTest {
|
||||
|
||||
private BeanDescriptor<Customer> customerDesc = getBeanDescriptor(Customer.class);
|
||||
private final BeanDescriptor<Customer> customerDesc = getBeanDescriptor(Customer.class);
|
||||
|
||||
@Test
|
||||
public void isFormula() {
|
||||
|
||||
assertFormula("max(version)", "max", "version");
|
||||
assertFormula("min(name)", "min", "name");
|
||||
assertFormula("avg(id)", "avg", "id");
|
||||
@@ -58,8 +58,8 @@ public class FormulaPropertyPathTest extends BaseTest {
|
||||
}
|
||||
|
||||
private void assertFormula(String input, String funcName, String expression, String cast, String alias) {
|
||||
|
||||
FormulaPropertyPath propertyPath = new FormulaPropertyPath(customerDesc, input, null);
|
||||
var context = AggregateFormulaContext.builder().build();
|
||||
FormulaPropertyPath propertyPath = new FormulaPropertyPath(customerDesc, context, input, null);
|
||||
|
||||
assertThat(propertyPath.internalExpression()).isEqualTo(expression);
|
||||
assertThat(propertyPath.outerFunction()).isEqualTo(funcName);
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ class BasicProfileLocationTest {
|
||||
DProfileLocation loc = new DTimedProfileLocation("foo", MetricFactory.get().createTimedMetric("junk"));
|
||||
|
||||
assertThat(loc.obtain()).isTrue();
|
||||
assertThat(loc.fullLocation()).endsWith("org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:728)");
|
||||
assertThat(loc.fullLocation()).endsWith("org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:786)");
|
||||
assertThat(loc.location()).isEqualTo("org.junit.platform.commons.util.ReflectionUtils.invokeMethod");
|
||||
assertThat(loc.label()).isEqualTo("ReflectionUtils.invokeMethod");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class CQueryBuilderTest {
|
||||
|
||||
/**
|
||||
* Simulates what buildRowCountQuery does: strip the top-level order by, then wrap with count.
|
||||
* This is the logic that must use lastTopLevelOrderBy instead of lastIndexOf.
|
||||
*/
|
||||
private static String simulateCountWrap(String sql) {
|
||||
int pos = CQueryBuilder.lastTopLevelOrderBy(sql);
|
||||
if (pos != -1) {
|
||||
sql = sql.substring(0, pos);
|
||||
}
|
||||
return "select count(*) from ( " + sql + ") as c";
|
||||
}
|
||||
|
||||
private static int countChar(String s, char c) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) == c) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Test
|
||||
void lastTopLevelOrderBy_simple() {
|
||||
String sql = "select t0.id from ad t0 order by t0.id";
|
||||
int pos = CQueryBuilder.lastTopLevelOrderBy(sql);
|
||||
assertThat(pos).isEqualTo(sql.indexOf(" order by "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lastTopLevelOrderBy_noOrderBy() {
|
||||
String sql = "select t0.id from ad t0";
|
||||
int pos = CQueryBuilder.lastTopLevelOrderBy(sql);
|
||||
assertThat(pos).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lastTopLevelOrderBy_insideSubquery() {
|
||||
// order by is only inside a subquery - should not be found at top level
|
||||
String sql = "select t0.id from ad t0 where t0.id in (select t0.id from ad t0 order by t0.rebate)";
|
||||
int pos = CQueryBuilder.lastTopLevelOrderBy(sql);
|
||||
assertThat(pos).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lastTopLevelOrderBy_bothLevels() {
|
||||
// order by inside subquery AND at top level - should find only the top-level one
|
||||
String sql = "select t0.id from ad t0 where t0.id in (select t0.id from ad t0 order by t0.rebate) order by t0.id";
|
||||
int pos = CQueryBuilder.lastTopLevelOrderBy(sql);
|
||||
assertThat(sql.substring(pos)).isEqualTo(" order by t0.id");
|
||||
}
|
||||
|
||||
@Test
|
||||
void lastTopLevelOrderBy_nestedSubqueries() {
|
||||
// deeply nested order by should not be found
|
||||
String sql = "select t0.id from ad t0 where t0.id in (select t0.id from ad t0 where t0.x in (select id from foo order by bar))";
|
||||
int pos = CQueryBuilder.lastTopLevelOrderBy(sql);
|
||||
assertThat(pos).isEqualTo(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reproduces the exact scenario from https://github.com/ebean-orm/ebean/issues/3686
|
||||
*
|
||||
* With lastIndexOf(" order by "), the inner subquery's order by is matched,
|
||||
* stripping its closing parenthesis and producing unbalanced SQL.
|
||||
*/
|
||||
@Test
|
||||
void countWrap_formulaJoinWithSubqueryOrderBy_issue3686() {
|
||||
// This is the SQL that buildRowCountQuery would produce before wrapping,
|
||||
// matching the bug report: @Formula with JOIN + IN subquery with distinctOn + orderBy
|
||||
String innerSql = "select t0.id from ad t0"
|
||||
+ " LEFT JOIN price_range ON price_range.ad_id = t0.id"
|
||||
+ " where t0.id in (select distinct on (t0.rebate) t0.id from ad t0 order by t0.rebate)";
|
||||
|
||||
String countSql = simulateCountWrap(innerSql);
|
||||
|
||||
// The subquery's closing ) must be preserved
|
||||
assertThat(countSql).contains("order by t0.rebate)");
|
||||
// Parentheses must be balanced
|
||||
assertThat(countChar(countSql, '(')).isEqualTo(countChar(countSql, ')'));
|
||||
// Should end with ") as c" - the outer count wrapper's closing paren
|
||||
assertThat(countSql).endsWith(") as c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void countWrap_topLevelOrderByIsStripped() {
|
||||
// When there IS a top-level order by, it should be stripped
|
||||
String innerSql = "select t0.id from ad t0"
|
||||
+ " LEFT JOIN price_range ON price_range.ad_id = t0.id"
|
||||
+ " where t0.id in (select distinct on (t0.rebate) t0.id from ad t0 order by t0.rebate)"
|
||||
+ " order by price_range.discounted_price";
|
||||
|
||||
String countSql = simulateCountWrap(innerSql);
|
||||
|
||||
// Top-level order by should be removed
|
||||
assertThat(countSql).doesNotContain("discounted_price");
|
||||
// But inner subquery order by must remain intact
|
||||
assertThat(countSql).contains("order by t0.rebate)");
|
||||
// Parentheses must be balanced
|
||||
assertThat(countChar(countSql, '(')).isEqualTo(countChar(countSql, ')'));
|
||||
}
|
||||
|
||||
@Test
|
||||
void countWrap_simpleOrderByIsStripped() {
|
||||
// Simple case: top-level order by with no subquery
|
||||
String innerSql = "select t0.id from ad t0 order by t0.id";
|
||||
|
||||
String countSql = simulateCountWrap(innerSql);
|
||||
|
||||
assertThat(countSql).isEqualTo("select count(*) from ( select t0.id from ad t0) as c");
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean ddl generation</name>
|
||||
@@ -28,14 +28,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-all</artifactId>
|
||||
<version>16.0.1</version>
|
||||
<version>16.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user