Compare commits

..
1 Commits
118 changed files with 427 additions and 1101 deletions
-1
View File
@@ -3,7 +3,6 @@
[![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/ebean-orm/ebean/blob/master/LICENSE)
[![Multi-JDK Build](https://github.com/ebean-orm/ebean/actions/workflows/multi-jdk-build.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/multi-jdk-build.yml)
[![GraalVM Native Image](https://img.shields.io/badge/GraalVM-Native%20Image%20Ready-darkgreen?logo=graalvm)](https://www.graalvm.org/)
##### Build with database platforms
[![H2Database](https://github.com/ebean-orm/ebean/actions/workflows/h2database.yml/badge.svg)](https://github.com/ebean-orm/ebean/actions/workflows/h2database.yml)
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-net-postgis-types</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-pgvector-types</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -41,7 +41,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -60,13 +60,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<artifactId>composites</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean api</name>
+1 -12
View File
@@ -234,16 +234,5 @@ public interface DtoQuery<T> extends CancelableQuery {
* source. We use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
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);
DtoQuery<T> usingMaster();
}
@@ -135,17 +135,7 @@ 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.
*/
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);
SELF usingMaster();
/**
* Set the base table to use for this query.
+1 -11
View File
@@ -63,17 +63,7 @@ public interface SqlQuery extends Serializable, CancelableQuery {
* source. We use {@code usingMaster()} to instead ensure that the query is executed
* against the master data source.
*/
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);
SqlQuery usingMaster();
/**
* Execute the query returning a list.
@@ -51,13 +51,6 @@ public class DbPlatformTypeMapping {
private static final DbPlatformType VECTOR_BIT = new DbPlatformType("bit", 64000, null);
private static final DbPlatformType VECTOR_SPARSE = new DbPlatformType("sparsevec", 1000, null);
/**
* Timestamp with max precision of 15, and fallback to plain timestamp without precision defined.
*/
private static final DbPlatformType TIMESTAMP =
new DbPlatformType("timestamp", 0, 15,
new DbPlatformType("timestamp", false));
private final Map<DbType, DbPlatformType> typeMap = new EnumMap<>(DbType.class);
/**
@@ -94,8 +87,7 @@ public class DbPlatformTypeMapping {
put(DbType.ARRAY);
put(DbType.DATE);
put(DbType.TIME);
put(DbType.TIMESTAMP, TIMESTAMP);
put(DbType.TIMESTAMP);
put(DbType.LONGVARBINARY);
put(DbType.LONGVARCHAR);
// most commonly real maps to db float
+28 -28
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean bom</name>
@@ -89,25 +89,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -125,13 +125,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -155,37 +155,37 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<!-- platforms -->
@@ -193,91 +193,91 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-pgvector</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-pgvector-types</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>io.ebean</groupId>
<artifactId>ebean-parent</artifactId>
<version>16.4.0</version>
<version>16.1.1</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.4.0</version>
<version>16.1.1</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
+7 -7
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-json</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -52,7 +52,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -165,21 +165,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
@@ -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,14 +49,6 @@ 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,7 +3,10 @@ package io.ebeaninternal.api;
import io.ebean.Pairs;
import io.ebeaninternal.server.deploy.BeanNaturalKey;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Collects the data for processing the natural key cache processing.
@@ -16,9 +19,8 @@ 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;
@@ -45,39 +47,14 @@ public final class NaturalKeyQueryData<T> {
}
if (matchProperty(property0) && matchProperty(property1)) {
this.hasIn = true;
this.properties = new String[]{property0, property1};
this.inProperty0 = property0;
this.inProperty1 = 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.
*/
@@ -123,8 +100,6 @@ public final class NaturalKeyQueryData<T> {
addInValues();
} else if (inPairs != null) {
addInPairs();
} else if (inTuples != null) {
addInTuples();
} else {
addEqualsKey();
}
@@ -135,17 +110,7 @@ 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, 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));
set.add(new NaturalKeyEntryBasic(naturalKey, eqList, inProperty0, inProperty1, entry));
}
}
@@ -187,8 +152,11 @@ public final class NaturalKeyQueryData<T> {
if (inProperty != null) {
exprProps.add(inProperty);
}
if (properties != null) {
exprProps.addAll(Arrays.asList(properties));
if (inProperty0 != null) {
exprProps.add(inProperty0);
}
if (inProperty1 != null) {
exprProps.add(inProperty1);
}
if (eqList != null) {
for (NaturalKeyEq eq : eqList) {
@@ -205,7 +173,6 @@ 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();
}
@@ -239,9 +206,6 @@ public final class NaturalKeyQueryData<T> {
} else if (inPairs != null) {
//noinspection SuspiciousMethodCalls
inPairs.remove(inValue);
} else if (inTuples != null) {
//noinspection SuspiciousMethodCalls
inTuples.remove(inValue);
}
}
}
@@ -15,4 +15,8 @@ public interface SpiQueryManyJoin {
*/
String fetchOrderBy();
/**
* Wrap the filter many expression with a condition allowing lEFT JOIN null matching row.
*/
String idNullOr(String filterManyExpression);
}
@@ -338,6 +338,11 @@ 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,6 +169,11 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
transaction.setSkipCache(skipCache);
}
@Override
public boolean isSkipCacheExplicit() {
return transaction.isSkipCacheExplicit();
}
@Override
public boolean isSkipCache() {
return transaction.isSkipCache();
@@ -497,6 +497,10 @@ 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();
}
@@ -36,6 +36,11 @@ 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,6 +513,11 @@ 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,6 +634,11 @@ 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.
*/
@@ -154,9 +154,4 @@ 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();
}
@@ -27,6 +27,11 @@ 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,6 +45,20 @@ 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,6 +28,11 @@ 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,4 +261,9 @@ 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 + "))";
}
}
@@ -286,4 +286,8 @@ public final class ElPropertyChain implements ElPropertyValue {
}
}
@Override
public String idNullOr(String filterManyExpression) {
throw new UnsupportedOperationException();
}
}
@@ -12,7 +12,7 @@ final class InTuplesExpression extends AbstractExpression {
private final boolean not;
private final String[] properties;
private List<Object[]> entries;
private final List<Object[]> entries;
InTuplesExpression(InTuples pairs, boolean not) {
super("");
@@ -25,15 +25,7 @@ final class InTuplesExpression extends AbstractExpression {
@Override
public boolean naturalKey(NaturalKeyQueryData<?> data) {
if (not) {
return false;
}
List<Object[]> copy = data.matchInTuples(properties, entries);
if (copy == null) {
return false;
}
entries = copy;
return true;
return false;
}
@Override
@@ -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 = lastTopLevelOrderBy(sql);
int pos = sql.lastIndexOf(" order by ");
if (pos != -1) {
sql = sql.substring(0, pos);
}
@@ -300,27 +300,6 @@ 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) {
@@ -736,7 +715,7 @@ final class CQueryBuilder {
appendHistoryAsOfPredicate();
appendFindId();
appendToWhere(predicates.dbWhere());
appendToWhere(predicates.dbFilterManyWhere());
appendToWhere(predicates.dbFilterMany());
if (!query.isIncludeSoftDeletes()) {
appendSoftDelete();
}
@@ -39,7 +39,6 @@ public final class CQueryPredicates {
private final Object idValue;
private final BindParams bindParams;
private DefaultExpressionRequest filterMany;
private boolean filterManyJoin;
/**
* Bind values from the where expressions.
*/
@@ -107,9 +106,6 @@ 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);
@@ -123,7 +119,7 @@ public final class CQueryPredicates {
if (where != null) {
where.bind(dataBind);
}
if (!filterManyJoin && filterMany != null) {
if (filterMany != null) {
filterMany.bind(dataBind);
}
if (having != null) {
@@ -193,10 +189,9 @@ public final class CQueryPredicates {
if (chunk != null) {
SpiExpressionList<?> filterManyExpr = chunk.getFilterMany();
if (filterManyExpr != null) {
filterManyJoin = chunk.isFilterManyJoin();
filterMany = new DefaultExpressionRequest(request, deployParser, binder, filterManyExpr);
this.filterMany = new DefaultExpressionRequest(request, deployParser, binder, filterManyExpr);
if (buildSql) {
dbFilterMany = filterMany.buildSql();
dbFilterMany = manyProperty.idNullOr(filterMany.buildSql());
}
}
}
@@ -344,17 +339,10 @@ public final class CQueryPredicates {
}
/**
* Return a db filter to be included in the WHERE.
* Return a db filter for filtering many fetch joins.
*/
String dbFilterManyWhere() {
return filterManyJoin ? null : dbFilterMany;
}
/**
* Return a db filter to be included in the JOIN.
*/
String dbFilterManyJoin() {
return filterManyJoin ? dbFilterMany : null;
String dbFilterMany() {
return dbFilterMany;
}
/**
@@ -22,7 +22,6 @@ 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;
@@ -42,7 +41,7 @@ final class DefaultDbSqlContext implements DbSqlContext {
private boolean joinSuppressed;
DefaultDbSqlContext(SqlTreeAlias alias, String columnAliasPrefix, CQueryHistorySupport historySupport,
CQueryDraftSupport draftSupport, String fromForUpdate, String dbFilterManyJoin) {
CQueryDraftSupport draftSupport, String fromForUpdate) {
this.alias = alias;
this.columnAliasPrefix = columnAliasPrefix;
this.useColumnAlias = columnAliasPrefix != null;
@@ -50,14 +49,6 @@ 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
@@ -235,7 +235,7 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
}
@Override
public Query<T> usingMaster(boolean useMaster) {
public Query<T> usingMaster() {
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.isBeanCachePut()) {
if (result != null && request.isBeanCachePutMany()) {
// load the individual beans into the bean cache
request.descriptor().cacheBeanPutAll(result.actualDetails());
}
@@ -14,7 +14,6 @@ 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;
@@ -116,14 +115,9 @@ 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);
@@ -166,17 +160,13 @@ 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, predicates.dbFilterManyJoin());
this.ctx = new DefaultDbSqlContext(alias, colAlias, historySupport, draftSupport, fromForUpdate);
}
/**
@@ -371,11 +371,10 @@ 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) {
if (!addToParent(extraJoin, myList)) {
myList.add(extraJoin);
}
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
@@ -386,20 +385,6 @@ 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
@@ -656,7 +641,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.prefix(), root);
rootRegister.put(root.name(), root);
}
}
@@ -84,12 +84,4 @@ interface SqlTreeNode {
*/
default void unselectLobsForPlatform() {
}
default String prefix() {
return ""; // not matched
}
default void addChild(SqlTreeNode extraJoin) {
throw new UnsupportedOperationException();
}
}
@@ -99,16 +99,6 @@ 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,8 +87,7 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
return manyJoin;
}
@Override
public String prefix() {
public String name() {
return prefix;
}
@@ -45,6 +45,5 @@ 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(boolean useMaster) {
this.useMaster = useMaster;
public DtoQuery<T> usingMaster() {
this.useMaster = true;
return this;
}
@@ -1475,8 +1475,8 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
}
@Override
public Query<T> usingMaster(boolean useMaster) {
this.useMaster = useMaster;
public Query<T> usingMaster() {
this.useMaster = true;
return this;
}
@@ -60,8 +60,8 @@ public final class DefaultRelationalQuery extends AbstractQuery implements SpiSq
}
@Override
public SqlQuery usingMaster(boolean useMaster) {
this.useMaster = useMaster;
public SqlQuery usingMaster() {
this.useMaster = true;
return this;
}
@@ -171,9 +171,8 @@ public final class OrmQueryProperties implements Serializable {
if (filterMany == null) {
FilterExprPath exprPath = new FilterExprPath(path);
SpiExpressionFactory queryEf = (SpiExpressionFactory) rootQuery.getExpressionFactory();
ExpressionFactory filterEf = queryEf.createExpressionFactory();
ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath);
filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
// assuming conditions supported in JOIN, not setting markForQueryJoin = true
}
return filterMany;
}
@@ -188,13 +187,6 @@ 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.
*/
@@ -150,6 +150,11 @@ final class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEve
return false;
}
@Override
public boolean isSkipCacheExplicit() {
return false;
}
@Override
public void setSkipCache(boolean skipCache) {
}
@@ -209,6 +209,11 @@ 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,6 +235,11 @@ final class NoTransaction implements SpiTransaction {
public void setSkipCache(boolean skipCache) {
}
@Override
public boolean isSkipCacheExplicit() {
return false;
}
@Override
public boolean isSkipCache() {
return false;
@@ -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:786)");
assertThat(loc.fullLocation()).endsWith("org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:728)");
assertThat(loc.location()).isEqualTo("org.junit.platform.commons.util.ReflectionUtils.invokeMethod");
assertThat(loc.label()).isEqualTo("ReflectionUtils.invokeMethod");
}
@@ -1,118 +0,0 @@
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 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean ddl generation</name>
@@ -28,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -65,7 +65,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -15,7 +15,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean net postgis types</name>
@@ -19,14 +19,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -54,7 +54,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean pgvector types</name>
@@ -19,14 +19,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -54,7 +54,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean postgis types</name>
@@ -19,14 +19,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -62,7 +62,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean querybean</name>
@@ -17,7 +17,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -59,14 +59,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
@@ -80,7 +80,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -739,8 +739,8 @@ public abstract class QueryBean<T, R extends QueryBean<T, R>> implements IQueryB
}
@Override
public R usingMaster(boolean useMaster) {
query.usingMaster(useMaster);
public final R usingMaster() {
query.usingMaster();
return root;
}
@@ -328,7 +328,7 @@ public class QCustomerTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).isEqualTo("select /* QCustomerTest.filterMany */ t0.id, t0.name, t1.id, t1.first_name, t1.last_name from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id and t1.first_name like ? escape'|' and t1.email is not null order by t0.id");
assertThat(q.getGeneratedSql()).isEqualTo("select /* QCustomerTest.filterMany */ t0.id, t0.name, t1.id, t1.first_name, t1.last_name from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where (t1.id is null or (t1.first_name like ? escape'|' and t1.email is not null)) order by t0.id");
}
@Test
@@ -340,7 +340,7 @@ public class QCustomerTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).isEqualTo("select /* QCustomerTest.filterManySingle */ t0.id, t0.name, t1.id, t1.first_name, t1.last_name from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id and t1.first_name like ? escape'|' order by t0.id");
assertThat(q.getGeneratedSql()).isEqualTo("select /* QCustomerTest.filterManySingle */ t0.id, t0.name, t1.id, t1.first_name, t1.last_name from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where (t1.id is null or (t1.first_name like ? escape'|')) order by t0.id");
}
@Test
@@ -375,7 +375,7 @@ public class QCustomerTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).contains(" from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id and t1.first_name like ? and t1.first_name like ? escape'|' order by t0.id");
assertThat(q.getGeneratedSql()).contains(" from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where (t1.id is null or (t1.first_name like ? and t1.first_name like ? escape'|')) order by t0.id");
}
@Test
@@ -389,7 +389,7 @@ public class QCustomerTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).contains(" from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id and (t1.first_name like ? escape'|' or t1.last_name like ? escape'|') order by t0.id");
assertThat(q.getGeneratedSql()).contains(" from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where (t1.id is null or ((t1.first_name like ? escape'|' or t1.last_name like ? escape'|'))) order by t0.id");
}
@Test
@@ -406,7 +406,7 @@ public class QCustomerTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).contains(" t0.id, t0.name, t1.id, t1.email from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id and t1.first_name like ? escape'|' order by t0.id");
assertThat(q.getGeneratedSql()).contains(" t0.id, t0.name, t1.id, t1.email from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where (t1.id is null or (t1.first_name like ? escape'|')) order by t0.id");
}
@Test
@@ -446,22 +446,6 @@ public class QCustomerTest {
.findList();
}
@Test
public void usingMaster_true() {
new QCustomer()
.registered.isNull()
.usingMaster(true)
.findList();
}
@Test
public void usingMaster_false() {
new QCustomer()
.registered.isNull()
.usingMaster(false)
.findList();
}
@Test
public void usingTransaction() {
try (Transaction transaction = DB.getDefault().createTransaction()) {
@@ -22,7 +22,7 @@ class QDataWithFormulaMainTest {
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains(" from data_with_formula_main t0 left join data_with_formula t1 on t1.main_id = t0.id and t1.meta_key = ? order by t0.id");
assertThat(sql.get(0)).contains(" where ((t1.main_id is null and t1.meta_key is null and t1.value_index is null) or (t1.meta_key = ?)) order by t0.id");
}
@Test
@@ -36,6 +36,6 @@ class QDataWithFormulaMainTest {
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains(" left join data_with_formula t1 on t1.main_id = t0.id and t1.meta_key = ? order by t0.id");
assertThat(sql.get(0)).contains(" where ((t1.main_id is null and t1.meta_key is null and t1.value_index is null) or (t1.meta_key = ?)) order by t0.id");
}
}
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<artifactId>ebean-redis</artifactId>
@@ -29,35 +29,35 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -1,8 +1,6 @@
package org.integration;
import io.ebean.DB;
import io.ebean.InTuples;
import io.ebean.Pairs;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheStatistics;
import org.domain.*;
@@ -179,76 +177,6 @@ class IntegrationTest {
.findOne();
}
@Test
void naturalKey_inPairs() throws InterruptedException {
DB.save(new OtherOne("ip_A", "ip_1", "ip_A1"));
DB.save(new OtherOne("ip_A", "ip_2", "ip_A2"));
DB.save(new OtherOne("ip_B", "ip_1", "ip_B1"));
ServerCache nkeyCache = DB.cacheManager().naturalKeyCache(OtherOne.class);
nkeyCache.clear();
Pairs pairs = new Pairs("one", "two")
.add("ip_A", "ip_1")
.add("ip_A", "ip_2")
.add("ip_B", "ip_1");
// first fetch — miss, populates natural key + bean cache
List<OtherOne> list0 = DB.find(OtherOne.class)
.where()
.inPairs(pairs)
.setUseCache(true)
.findList();
assertThat(list0).hasSize(3);
nkeyCache.statistics(true); // reset stats
Thread.sleep(5);
// second fetch — all three should hit the natural key cache
List<OtherOne> list1 = DB.find(OtherOne.class)
.where()
.inPairs(pairs)
.setUseCache(true)
.findList();
assertThat(list1).hasSize(3);
assertThat(nkeyCache.statistics(true).getHitCount()).isEqualTo(3);
}
@Test
void naturalKey_inTuples() throws InterruptedException {
DB.save(new OtherOne("it_A", "it_1", "it_A1"));
DB.save(new OtherOne("it_A", "it_2", "it_A2"));
DB.save(new OtherOne("it_B", "it_1", "it_B1"));
ServerCache nkeyCache = DB.cacheManager().naturalKeyCache(OtherOne.class);
nkeyCache.clear();
InTuples tuples = InTuples.of("one", "two")
.add("it_A", "it_1")
.add("it_A", "it_2")
.add("it_B", "it_1");
// first fetch — miss, populates natural key + bean cache
List<OtherOne> list0 = DB.find(OtherOne.class)
.where()
.inTuples(tuples)
.setUseCache(true)
.findList();
assertThat(list0).hasSize(3);
nkeyCache.statistics(true); // reset stats
Thread.sleep(5);
// second fetch — all three should hit the natural key cache
List<OtherOne> list1 = DB.find(OtherOne.class)
.where()
.inTuples(tuples)
.setUseCache(true)
.findList();
assertThat(list1).hasSize(3);
assertThat(nkeyCache.statistics(true).getHitCount()).isEqualTo(3);
}
@Test
void test() throws InterruptedException {
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<artifactId>ebean-spring-txn</artifactId>
@@ -28,7 +28,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
@@ -77,7 +77,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
+8 -8
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>ebean test</name>
@@ -13,7 +13,7 @@
<properties>
<bytebuddy.version>1.18.1</bytebuddy.version>
<assertj.version>3.27.7</assertj.version>
<assertj.version>3.27.6</assertj.version>
</properties>
<dependencies>
@@ -33,20 +33,20 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
@@ -149,14 +149,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
@@ -226,7 +226,7 @@
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>11.1.0.jre8-preview</version>
<version>10.2.0.jre8</version>
<scope>test</scope>
</dependency>
@@ -155,34 +155,6 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
}
}
@Test
public void asDto_usingMaster_true() {
asDtoUsingMaster(true);
}
@Test
public void asDto_usingMaster_false() {
asDtoUsingMaster(false);
}
private void asDtoUsingMaster(boolean useMaster) {
ResetBasicData.reset();
LoggedSql.start();
DtoQuery<ContactDto> query = DB.find(Contact.class)
.select("id, email")
.where().isNotNull("email")
.asDto(ContactDto.class)
.usingMaster(useMaster);
List<ContactDto> dtos = query.findList();
assertThat(dtos).isNotEmpty();
for (ContactDto dto : dtos) {
assertThat(dto.getEmail()).isNotNull();
}
}
@Test
public void asDto_withExplicitId() {
@@ -2,17 +2,16 @@ package io.ebean.xtest.config.dbplatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbPlatformTypeMapping;
import io.ebean.config.dbplatform.DbType;
import org.junit.jupiter.api.Test;
import java.sql.Types;
import static org.assertj.core.api.Assertions.assertThat;
class DbPlatformTypeMappingTest {
public class DbPlatformTypeMappingTest {
@Test
void logicalBoolean_renderType_expect_noLength() {
public void logicalBoolean_renderType_expect_noLength() {
DbPlatformTypeMapping logicalMapping = DbPlatformTypeMapping.logicalTypes();
@@ -20,11 +19,4 @@ class DbPlatformTypeMappingTest {
String colDefinition = type.renderType(1, 1, false);
assertThat(colDefinition).isEqualTo("boolean");
}
@Test
void timestamp_with_255_expectNoPrecision() {
DbPlatformType timestampType = new DbPlatformTypeMapping().get(DbType.TIMESTAMP);
String colDefinition = timestampType.renderType(255, 0, true);
assertThat(colDefinition).isEqualTo("timestamp");
}
}
@@ -30,13 +30,13 @@ public class BeanFindControllerTest extends BaseTestCase {
var config = new DatabaseConfig();
config.setName("h2otherfind");
config.setRegister(false);
config.setDefaultServer(false);
config.loadFromProperties();
config.setDdlGenerate(true);
config.setDdlRun(true);
config.setDdlExtra(false);
config.setRegister(false);
config.setDefaultServer(false);
config.add(new ModUuidGenerator());
config.addClass(EBasic.class);
config.addClass(ECustomId.class);
@@ -173,13 +173,13 @@ public class BeanFindControllerTest extends BaseTestCase {
DatabaseConfig config = new DatabaseConfig();
config.setName("h2otherfind");
config.setRegister(false);
config.setDefaultServer(false);
config.loadFromProperties();
config.setDdlGenerate(true);
config.setDdlRun(true);
config.setDdlExtra(false);
config.setRegister(false);
config.setDefaultServer(false);
config.add(new ModUuidGenerator());
config.addClass(FindControllerMain.class);
config.addClass(SoftRefA.class);
@@ -10,8 +10,6 @@ import io.ebean.event.BeanDeleteIdRequest;
import io.ebean.event.BeanPersistAdapter;
import io.ebean.event.BeanPersistRequest;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tests.model.basic.EBasicVer;
import org.tests.model.basic.UTDetail;
import org.tests.model.basic.UTMaster;
@@ -24,15 +22,13 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BeanPersistControllerTest {
private static final Logger log = LoggerFactory.getLogger(BeanPersistControllerTest.class);
private final PersistAdapter continuePersistingAdapter = new PersistAdapter(true);
private final PersistAdapter stopPersistingAdapter = new PersistAdapter(false);
@Test
public void issued1() {
Database db = createDatabase(continuePersistingAdapter);
Database db = getDatabase(continuePersistingAdapter);
UTMaster bean0 = new UTMaster("m0");
bean0.setJournal(new UTMaster.Journal());
@@ -51,12 +47,11 @@ public class BeanPersistControllerTest {
assertThat(journal.getEntries()).hasSize(2);
db.shutdown();
log.info("done issued1");
}
@Test
public void issue_1341() {
Database db = createDatabase(continuePersistingAdapter);
Database db = getDatabase(continuePersistingAdapter);
UTMaster bean0 = new UTMaster("one0");
UTDetail detail0 = new UTDetail("detail0", 12, 23D);
@@ -82,13 +77,12 @@ public class BeanPersistControllerTest {
}
db.shutdown();
log.info("done issue_1341");
}
@Test
public void testInsertUpdateDelete_given_continuePersistingAdapter() {
Database db = createDatabase(continuePersistingAdapter);
Database db = getDatabase(continuePersistingAdapter);
EBasicVer bean = new EBasicVer("testController");
@@ -108,13 +102,12 @@ public class BeanPersistControllerTest {
assertThat(continuePersistingAdapter.methodsCalled).containsExactly("preDelete", "postDelete");
db.shutdown();
log.info("done testInsertUpdateDelete_given_continuePersistingAdapter");
}
@Test
public void testInsertUpdateDelete_given_stopPersistingAdapter() {
Database db = createDatabase(stopPersistingAdapter);
Database db = getDatabase(stopPersistingAdapter);
EBasicVer bean = new EBasicVer("testController");
@@ -145,19 +138,18 @@ public class BeanPersistControllerTest {
stopPersistingAdapter.methodsCalled.clear();
db.shutdown();
log.info("done testInsertUpdateDelete_given_stopPersistingAdapter");
}
private Database createDatabase(PersistAdapter persistAdapter) {
private Database getDatabase(PersistAdapter persistAdapter) {
DatabaseBuilder config = new DatabaseConfig();
config.setName("h2ebasicver");
config.setRegister(false);
config.setDefaultServer(false);
config.loadFromProperties();
config.setDdlGenerate(true);
config.setDdlRun(true);
config.setDdlExtra(false);
config.setRegister(false);
config.setDefaultServer(false);
config.addClass(EBasicVer.class);
config.addClass(UTMaster.class);
config.addClass(UTDetail.class);
@@ -52,13 +52,13 @@ public class BeanPostLoadTest extends BaseTestCase {
DatabaseBuilder config = new DatabaseConfig();
config.setName("h2ebasicver");
config.setRegister(false);
config.setDefaultServer(false);
config.loadFromProperties();
config.setDdlGenerate(true);
config.setDdlRun(true);
config.setDdlExtra(false);
config.setRegister(false);
config.setDefaultServer(false);
config.addClass(EBasicVer.class);
config.add(postLoad);
@@ -7,20 +7,23 @@ import org.tests.model.basic.Order;
import jakarta.persistence.PersistenceException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
class TestErrorBindLog extends BaseTestCase {
public class TestErrorBindLog extends BaseTestCase {
@Test
void test() {
public void test() {
try {
DB.find(Order.class).where().gt("id", "JUNK").findList();
} catch (PersistenceException e) {
String msg = e.getMessage();
if (isHana() || isDb2()) {
assertThat(msg).contains("Error with property");
} else {
assertThat(msg).contains("Bind values:");
if (isHana()) {
assertTrue(msg.contains("Error with property[1] dt[12]data[JUNK]"));
}
else {
assertTrue(msg.contains("Bind values:"));
}
}
}
@@ -68,15 +68,11 @@ public class TestQueryUsingConnection extends BaseTestCase {
final int otherCount = DB.find(Country.class).findCount();
final int masterCount = DB.find(Country.class).usingMaster().findCount();
final int masterCount2 = DB.find(Country.class).usingMaster(true).findCount();
final int masterCount3 = DB.find(Country.class).usingMaster(false).findCount();
transaction.rollback();
assertThat(count).isEqualTo(otherCount + 1);
assertThat(otherCount).isEqualTo(masterCount);
assertThat(otherCount).isEqualTo(masterCount2);
assertThat(otherCount).isEqualTo(masterCount3);
}
}
@@ -124,7 +124,7 @@ public class TestBeanCache extends BaseTestCase {
ServerCacheStatistics statistics = beanCache.statistics(true);
assertThat(statistics.getHitCount()).isEqualTo(0);
assertThat(statistics.getMissCount()).isEqualTo(0);
assertThat(statistics.getPutCount()).isEqualTo(0);
assertThat(statistics.getPutCount()).isEqualTo(2);
}
@Test
@@ -1,27 +0,0 @@
package org.tests.model.basic;
import io.ebean.Model;
import jakarta.persistence.*;
@Entity
public class Building extends Model {
public static final String CAFE = "cafe";
public static final String HOUSE = "house";
public static final String STORE = "store";
@Id
public int id;
@Column(nullable = false)
public String type;
@Column(name = "lvl")
public int level;
public final String name;
@ManyToOne(optional = false)
public final Clan clan;
public Building(Clan clan, String type, String name) {
this.clan = clan;
this.type = type;
this.name = name;
}
}
@@ -1,17 +0,0 @@
package org.tests.model.basic;
import io.ebean.Model;
import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Clan extends Model {
@Id
public int id;
@OneToMany(cascade = CascadeType.ALL)
public List<Building> buildings = new ArrayList<>();
}
@@ -1,18 +0,0 @@
package org.tests.model.basic;
import io.ebean.Model;
import jakarta.persistence.*;
@Entity
public class ClanQuest extends Model {
@Id
public int id;
@ManyToOne(optional = false)
public final Clan clan;
public ClanQuest(Clan clan) {
this.clan = clan;
}
}
@@ -6,12 +6,13 @@ import io.ebean.ExpressionList;
import io.ebean.Query;
import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.*;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -174,8 +175,7 @@ public class TestQueryFilterMany extends BaseTestCase {
assertThat(customers).isNotEmpty();
List<String> sqlList = LoggedSql.stop();
assertEquals(1, sqlList.size());
assertThat(sqlList.get(0)).contains(" left join o_customer t2 on t2.id = t1.kcustomer_id and t1.status = ? where ");
assertThat(sqlList.get(0)).contains(" where lower(t0.name) = ? order by t0.id");
assertThat(sqlList.get(0)).contains(" where lower(t0.name) = ? and (t1.id is null or (t1.status = ?)) order by t0.id");
}
@Test
@@ -211,7 +211,8 @@ public class TestQueryFilterMany extends BaseTestCase {
assertThat(result).isNotEmpty();
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id and t1.order_date is not null order by t0.id");
assertThat(sql.get(0)).contains("from o_customer t0 left join o_order t1");
assertThat(sql.get(0)).contains("where (t1.id is null or (t1.order_date is not null)) order by t0.id");
}
@Test
@@ -230,78 +231,25 @@ public class TestQueryFilterMany extends BaseTestCase {
}
@Test
public void test_filterMany_excludedExplicitly() {
Clan clan = new Clan();
ClanQuest quest = new ClanQuest(clan);
DB.saveAll(clan, quest);
public void test_filterMany_with_isNotEmpty() {
LoggedSql.start();
// fetch when there are no buildings at all
Optional<ClanQuest> result = DB.find(ClanQuest.class)
.setId(quest.id)
.fetch("clan", "buildings")
.filterMany("clan.buildings").eq("type", Building.CAFE)
.findOneOrEmpty();
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains(" left join building t2 on t2.clan_id = t1.id and t2.type = ? where t0.id = ? order by t0.id");
assertThat(result).isPresent();
List<Building> emptyBuildings = result.map(r -> r.clan.buildings).orElseThrow();
assertThat(emptyBuildings).isEmpty();
// add some buildings
var b0 = new Building(clan, Building.CAFE, "b0");
var b1 = new Building(clan, Building.CAFE, "b1");
var b2 = new Building(clan, Building.HOUSE, "b2");
DB.saveAll(b0, b1, b2);
// fetch with some buildings to match the filter many predicate
Optional<ClanQuest> resultWithBuildings = DB.find(ClanQuest.class)
.setId(quest.id)
.fetch("clan", "buildings")
.filterMany("clan.buildings").eq("type", Building.CAFE)
.findOneOrEmpty();
List<Building> someBuildings = resultWithBuildings.map(r -> r.clan.buildings).orElseThrow();
assertThat(someBuildings).hasSize(2);
assertThat(someBuildings.stream().map(b -> b.name).collect(Collectors.toList())).contains("b0", "b1");
// fetch with no matching buildings
Optional<ClanQuest> resultWithNoMatchingBuildings = DB.find(ClanQuest.class)
.setId(quest.id)
.fetch("clan", "buildings")
.filterMany("clan.buildings").eq("type", Building.STORE)
.findOneOrEmpty();
List<Building> noMatchingBuildings = resultWithNoMatchingBuildings.map(r -> r.clan.buildings).orElseThrow();
assertThat(noMatchingBuildings).isEmpty();
}
@Test
void test_filterMany_with_isNotEmpty() {
ResetBasicData.reset();
LoggedSql.start();
Query<Customer> query = DB.find(Customer.class)
.setUnmodifiable(true)
.select("id, status, name")
.fetch("orders", "status, orderDate, shipDate")
.filterMany("orders").raw("1 = 0")
.filterMany("orders").raw("1=0")
.where().isNotEmpty("orders")
.query();
List<Customer> list = query.findList();
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains(" from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null and 1 = 0 where exists ");
assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.name, t1.id, t1.status, t1.order_date, t1.ship_date from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null and 1 = 0 where exists (select 1 from o_order x where x.kcustomer_id = t0.id and x.order_date is not null) order by t0.id; --bind()");
for (Customer customer : list) {
assertThat(customer.getOrders()).isEmpty();
}
List<String> sqlList = LoggedSql.stop();
assertEquals(1, sqlList.size());
assertThat(sqlList.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where exists (select 1 from o_order x where x.kcustomer_id = t0.id and x.order_date is not null) and (t1.id is null or (1=0)) order by t0.id");
}
@Test
@@ -339,10 +287,11 @@ public class TestQueryFilterMany extends BaseTestCase {
List<String> sqlList = LoggedSql.stop();
assertEquals(1, sqlList.size());
assertThat(sqlList.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where (t1.id is null or (t1.status ");
if (isPostgresCompatible()) {
assertThat(sqlList.get(0)).contains("left join o_customer t2 on t2.id = t1.kcustomer_id and t1.status = any(?) order by t0.id");
assertThat(sqlList.get(0)).contains("where (t1.id is null or (t1.status = any(?))) order by t0.id");
} else {
assertThat(sqlList.get(0)).contains("left join o_customer t2 on t2.id = t1.kcustomer_id and t1.status in (?) order by t0.id");
assertThat(sqlList.get(0)).contains("where (t1.id is null or (t1.status in (?))) order by t0.id");
}
}
@@ -386,7 +335,7 @@ public class TestQueryFilterMany extends BaseTestCase {
List<String> sql = LoggedSql.stop();
assertEquals(1, sql.size());
assertSql(sql.get(0)).contains(" left join o_customer t2 on t2.id = t1.kcustomer_id and (t1.status = ? or t1.order_date = ?) order by t0.id");
assertSql(sql.get(0)).contains(" from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where (t1.id is null or ((t1.status = ? or t1.order_date = ?))) order by t0.id");
}
@Test
@@ -402,7 +351,7 @@ public class TestQueryFilterMany extends BaseTestCase {
List<String> sql = LoggedSql.stop();
assertThat(sql.size()).isGreaterThan(1);
assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id and t1.first_name is not null order by t0.id; --bind()");
assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id where (t1.id is null or (t1.first_name is not null)) order by t0.id; --bind()");
platformAssertIn(sql.get(1), " from contact_note t0 where (t0.contact_id)");
assertSql(sql.get(1)).contains(" and lower(t0.title) like");
}
@@ -440,26 +389,9 @@ public class TestQueryFilterMany extends BaseTestCase {
assertThat(sql).hasSize(1);
if (isSqlServer()) {
assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id and (t1.first_name is not null and lower(t1.email) like ?) order by t0.id; --bind(rob%)");
assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id where (t1.id is null or ((t1.first_name is not null and lower(t1.email) like ?))) order by t0.id; --bind(rob%)");
} else {
assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id and (t1.first_name is not null and lower(t1.email) like ? escape'|') order by t0.id; --bind(rob%)");
assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id where (t1.id is null or ((t1.first_name is not null and lower(t1.email) like ? escape'|'))) order by t0.id; --bind(rob%)");
}
}
@Test
void testFilterManyWithNestedPathExpression() {
ResetBasicData.reset();
LoggedSql.start();
DB.find(Customer.class)
.select("id, name")
.fetch("contacts", "firstName, email")
.filterMany("contacts").eq("group.name", "DoesNotExist").isNotNull("cretime")
.findList();
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertSql(sql.get(0)).contains(" from o_customer t0 left join contact t1 on t1.customer_id = t0.id left join contact_group t2 on t2.id = t1.group_id and t2.name = ? and t1.cretime is not null order by t0.id");
}
}
@@ -20,7 +20,7 @@ class TestQueryFilterManyOnM2M extends BaseTestCase {
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains(" left join mrole t1 on t1.role_id = t1z_.mrole_role_id and lower(t1.role_name) like ");
assertThat(sql.get(0)).contains("select t0.userid, t0.user_name, t0.user_type_id, t1.role_id, t1.role_name from muser t0 left join mrole_muser t1z_ on t1z_.muser_userid = t0.userid left join mrole t1 on t1.role_id = t1z_.mrole_role_id where (t1.role_id is null or (lower(t1.role_name) like ");
assertThat(sql.get(0)).contains("order by t0.userid;");
}
@@ -35,7 +35,7 @@ public class TestQueryFilterManySimple extends BaseTestCase {
list.get(0).getOrders().size();
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id and t1.status = ? and t1.order_date > ?");
assertThat(sql.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where");
assertThat(sql.get(0)).contains("order by t0.id");
if (isPostgresCompatible()) {
assertThat(sql.get(1)).contains("from contact t0 where (t0.customer_id) = any(?) and t0.first_name is not null;");
@@ -43,19 +43,4 @@ public class TestQueryFilterManySimple extends BaseTestCase {
assertThat(sql.get(1)).contains("from contact t0 where (t0.customer_id) in (?) and t0.first_name is not null;");
}
}
@Test
void testNestedFilterMany() {
ResetBasicData.reset();
LoggedSql.start();
DB.find(Customer.class)
.fetch("orders")
.filterMany("orders").eq("customer.status", Customer.Status.NEW)
.findList();
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id and t2.status = ? order by t0.id");
}
}
@@ -4,7 +4,6 @@ import io.ebean.DB;
import io.ebean.Query;
import io.ebean.test.LoggedSql;
import io.ebean.xtest.BaseTestCase;
import io.ebean.xtest.ForPlatform;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Order;
@@ -17,8 +16,6 @@ import org.tests.model.family.ParentPerson;
import java.sql.Date;
import java.util.List;
import static io.ebean.annotation.Platform.H2;
import static io.ebean.annotation.Platform.POSTGRES;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -336,52 +333,6 @@ public class TestQueryJoinOnFormula extends BaseTestCase {
assertThat(loggedSql.get(0)).contains("where coalesce(f2.child_age, 0) = ?");
}
/**
* Test for https://github.com/ebean-orm/ebean/issues/3686
* findCount with Formula join + subquery containing ORDER BY
* should produce valid SQL with properly balanced parentheses.
*
* Before the fix, lastIndexOf(" order by ") matched the ORDER BY inside the
* IN subquery, stripping its closing parenthesis and producing invalid SQL.
*/
@ForPlatform({H2, POSTGRES})
@Test
public void test_findCount_formulaJoin_subqueryWithOrderBy_issue3686() {
LoggedSql.start();
// Subquery with orderBy — the order by inside the subquery triggers the bug.
Query<Order> subQuery = DB.find(Order.class)
.select("id")
.orderBy("id");
// Outer query: formula join (totalItems) via where clause + findCount
int count = DB.find(Order.class)
.where()
.in("id", subQuery)
.eq("totalItems", 3)
.findCount();
assertThat(count).isEqualTo(2);
List<String> sql = LoggedSql.stop();
assertEquals(1, sql.size());
String countSql = sql.get(0);
// The count query must wrap with select count(*) from ( ... )
assertThat(countSql).contains("select count(*) from (");
// The subquery's ORDER BY and closing ) must be preserved
assertThat(countSql).contains("order by t0.id)");
// Parentheses must be balanced in the generated SQL
int open = 0, close = 0;
for (char c : countSql.toCharArray()) {
if (c == '(') open++;
if (c == ')') close++;
}
assertThat(open).as("parentheses must be balanced in: " + countSql).isEqualTo(close);
}
@Test
public void test_softRef() {
@@ -75,18 +75,16 @@ public class TestOrderByWithDistinct extends BaseTestCase {
assertSql(query).startsWith("select distinct on (t0.id, t2.name, t1.id) t0.id, t0.parent_id, t0.ref_id, t1.id, t1.parent_id, t1.ref_id, t2.name "
+ "from e_basic_tree t0 "
+ "left join e_basic_tree t1 on t1.parent_id = t0.id "
+ "left join e_basic t2 on t2.id = t1.ref_id "
+ "join e_basic_tree u1 on u1.parent_id = t0.id "
+ "join e_basic u2 on u2.id = u1.ref_id "
+ "where u2.status = ? order by t0.id, t2.name");
+ "join e_basic u2 on u2.id = u1.ref_id left "
+ "join e_basic t2 on t2.id = t1.ref_id where u2.status = ? order by t0.id, t2.name");
} else {
assertSql(query).startsWith("select distinct t0.id, t0.parent_id, t0.ref_id, t1.id, t1.parent_id, t1.ref_id, t2.name "
+ "from e_basic_tree t0 "
+ "left join e_basic_tree t1 on t1.parent_id = t0.id "
+ "left join e_basic t2 on t2.id = t1.ref_id "
+ "join e_basic_tree u1 on u1.parent_id = t0.id "
+ "join e_basic u2 on u2.id = u1.ref_id "
+ "where u2.status = ? order by t0.id, t2.name");
+ "join e_basic u2 on u2.id = u1.ref_id left "
+ "join e_basic t2 on t2.id = t1.ref_id where u2.status = ? order by t0.id, t2.name");
}
}
@@ -785,10 +785,9 @@ class TestQuerySingleAttribute extends BaseTestCase {
.query();
List<Order.Status> statusList = query.findSingleAttributeList();
String sql = sqlOf(query);
assertThat(sql)
assertSql(query)
.contains("select distinct t1.status from o_customer t0 "
+ "left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null and t1.status is not null")
+ "left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null where (t1.id is null or (t1.status is not null))")
.doesNotContain("order by");
assertThat(statusList).hasSize(4);
@@ -6,7 +6,6 @@ import io.ebean.xtest.ForPlatform;
import io.ebean.annotation.Platform;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.test.LoggedSql;
import jakarta.persistence.NonUniqueResultException;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
@@ -22,7 +21,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
class SqlQueryTests extends BaseTestCase {
@@ -87,19 +85,6 @@ class SqlQueryTests extends BaseTestCase {
assertThat(val2).isEqualTo(11);
}
@Test
void findSingleAttribute_whenMultipleRows_expect_NonUniqueResultException() {
ResetBasicData.reset();
String sql = "select id from o_order order by id desc";
assertThatThrownBy(() -> {
DB.sqlQuery(sql)
.mapToScalar(Long.class)
.findOne();
}).isInstanceOf(NonUniqueResultException.class)
.hasMessageContaining("Got more than 1 result for findSingleAttribute");
}
@Test
void findSingleAttributeList_decimal() {
ResetBasicData.reset();
@@ -280,28 +265,6 @@ class SqlQueryTests extends BaseTestCase {
assertThat(custDtos).isNotEmpty();
}
@Test
void queryUsingMaster_true() {
queryUsingMasterAsParameter(true);
}
@Test
void queryUsingMaster_false() {
queryUsingMasterAsParameter(false);
}
void queryUsingMasterAsParameter(boolean useMaster) {
ResetBasicData.reset();
String sql = "select id, name, status from o_customer where name is not null";
List<CustDto> custDtos = DB.sqlQuery(sql)
.usingMaster(useMaster)
.mapTo(CUST_MAPPER)
.findList();
assertThat(custDtos).isNotEmpty();
}
@Test
void queryUsingConnection() throws SQLException {
ResetBasicData.reset();
@@ -410,18 +373,6 @@ class SqlQueryTests extends BaseTestCase {
assertThat(rob.name).isEqualTo("Rob");
}
@Test
void findOne_mapper_when_notUnique() {
ResetBasicData.reset();
String sql = "select id, name, status from o_customer order by name desc";
assertThatThrownBy(() -> DB.sqlQuery(sql)
.mapTo(CUST_MAPPER)
.findOne())
.isInstanceOf(NonUniqueResultException.class)
.hasMessageContaining("Got more than 1 result for findOne");
}
@Test
void findList_mapper() {
ResetBasicData.reset();
-66
View File
@@ -1,66 +0,0 @@
# GraalVM Native Image
Ebean ORM supports GraalVM native image compilation. No additional dependencies or
native-image configuration files are required beyond what Ebean already provides.
## How it works
Ebean uses **bytecode enhancement at compile time** (via the ebean-maven-plugin or
ebean-gradle-plugin). The enhanced entity classes are compiled directly into the native
image. Because the enhancement happens ahead-of-time, Ebean does not rely on runtime
reflection for normal ORM operations — queries, inserts, updates, deletes, and
associations all work without additional configuration.
Entity classes do require reflection registration for native image, but this is handled
**automatically by the querybean-generator** annotation processor at compile time.
Projects using the querybean-generator (which is the standard and expected setup) have
nothing extra to do for `@Entity` classes.
## Setup
Ensure the ebean enhancement plugin runs during your build, as it normally would for
a standard JVM application. No extra steps are required for native image.
**Maven:**
```xml
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>${ebean.version}</version>
<extensions>true</extensions>
</plugin>
```
**Gradle:**
```kotlin
plugins {
id("io.ebean") version "${ebeanVersion}"
}
```
## DtoQuery and reflection
[`DtoQuery`](https://ebean.io/docs/query/dto) maps SQL results onto plain classes using
their public constructors and setter methods. This mapping uses reflection at runtime, so
DTO classes must be explicitly registered for reflection in your native-image configuration.
Create `src/main/resources/META-INF/native-image/<group-id>/<artifact-id>/reflect-config.json`
and add an entry for each DTO class:
```json
[
{
"name": "com.example.CustomerDto",
"allDeclaredConstructors": true,
"allPublicMethods": true
},
{
"name": "com.example.OrderSummaryDto",
"allDeclaredConstructors": true,
"allPublicMethods": true
}
]
```
> **Note:** `@Entity` classes are registered automatically by the querybean-generator —
> no manual entries are needed for them.
+2 -2
View File
@@ -15,8 +15,8 @@ mvn -T 4 clean package
mvn -T 4 deploy -pl '!composites,!platforms' -Pcentral -DskipTests
## git commit, git tag, git push --tags
git commit -am 'Version 16.4.0'
git tag 16.4.0
git commit -am 'Version 16.1.1'
git tag 16.1.1
git push --tags
## convert to javax
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
</parent>
<name>kotlin querybean generator</name>
@@ -21,7 +21,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
@@ -35,7 +35,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
@@ -56,14 +56,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
<scope>test</scope>
</dependency>
@@ -83,7 +83,7 @@
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>2.3.10</version>
<version>2.2.10</version>
<executions>
<execution>
<id>test-compile</id>
+14 -14
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,67 +16,67 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hsqldb</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlanywhere</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.4.0</version>
<version>16.1.1</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.4.0</version>
<version>16.1.1</version>
</dependency>
</dependencies>

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