mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-09-20 11:17:36 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9bf33383b | ||
|
|
be28314e31 | ||
|
|
b5fe26d034 | ||
|
|
053d82b54a | ||
|
|
0141e2effa | ||
|
|
a33bb4d4c8 | ||
|
|
d0348210be | ||
|
|
753cf66cf0 | ||
|
|
4a8b4dd5fb | ||
|
|
2faf7008db | ||
|
|
76c25cbe83 | ||
|
|
5c36a725f4 | ||
|
|
b99d4f5a51 | ||
|
|
7d18942357 | ||
|
|
e51d9ba2ab | ||
|
|
1b1dce90ed | ||
|
|
405834fa45 | ||
|
|
7720229af9 | ||
|
|
89a294414e | ||
|
|
7bbfc00b86 | ||
|
|
d6c76de0a2 | ||
|
|
36f3986689 | ||
|
|
f0f4f13ec6 | ||
|
|
08466d42e9 | ||
|
|
1f841982a5 | ||
|
|
b34704a1fa | ||
|
|
7853bf0960 | ||
|
|
0128d64841 | ||
|
|
68b313f778 | ||
|
|
fad32c58d8 | ||
|
|
8f1ebdb89a | ||
|
|
f7703efde2 | ||
|
|
c5ffccb649 | ||
|
|
3e65705bf9 | ||
|
|
7469a38bbc | ||
|
|
7ea4311662 | ||
|
|
f556e04f58 | ||
|
|
d9c75a60a5 | ||
|
|
225bec7b28 | ||
|
|
da44f7e604 | ||
|
|
e0eda4f8be | ||
|
|
e4e780588c | ||
|
|
7c8f235550 | ||
|
|
9a77ec1567 | ||
|
|
ff426319f5 | ||
|
|
7f56aff9dd | ||
|
|
cdbdd98d36 | ||
|
|
7bf030f59c | ||
|
|
16dca28831 | ||
|
|
678b43693d | ||
|
|
7ab93a0cdf | ||
|
|
951f942c73 | ||
|
|
cf2ec4b81d | ||
|
|
7d1745ba53 | ||
|
|
7adbdbf532 | ||
|
|
40d9c88e34 |
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-clickhouse</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-db2</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-hana</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mariadb</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-mysql</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-nuodb</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-oracle</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -47,19 +47,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlite</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -41,7 +41,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -60,13 +60,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-all</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>composites</artifactId>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
|
||||
@@ -216,6 +216,8 @@ public interface ExpressionList<T> {
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* @deprecated migrate to {@link #usingTransaction(Transaction)} then delete().
|
||||
* <p>
|
||||
* Execute as a delete query deleting the 'root level' beans that match the predicates
|
||||
* in the query.
|
||||
* <p>
|
||||
@@ -225,6 +227,7 @@ public interface ExpressionList<T> {
|
||||
*
|
||||
* @return the number of rows that were deleted.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "13.1.0")
|
||||
int delete(Transaction transaction);
|
||||
|
||||
/**
|
||||
@@ -236,11 +239,14 @@ public interface ExpressionList<T> {
|
||||
int update();
|
||||
|
||||
/**
|
||||
* @deprecated migrate to {@link #usingTransaction(Transaction)} then update().
|
||||
* <p>
|
||||
* Execute as a update query with the given transaction.
|
||||
*
|
||||
* @return the number of rows that were updated.
|
||||
* @see UpdateQuery
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "13.1.0")
|
||||
int update(Transaction transaction);
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,903 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.avaje.lang.Nullable;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Build and execute an ORM query.
|
||||
*
|
||||
* @param <SELF> The type of the builder
|
||||
* @param <T> The entity bean type
|
||||
*/
|
||||
public interface QueryBuilder<SELF, T> extends QueryBuilderProjection<SELF, T> {
|
||||
|
||||
/**
|
||||
* Set root table alias.
|
||||
*/
|
||||
SELF alias(String alias);
|
||||
|
||||
/**
|
||||
* Apply changes to the query conditional on the supplied predicate.
|
||||
* <p>
|
||||
* Typically, the changes are extra predicates etc.
|
||||
*
|
||||
* @param predicate The predicate which when true the changes are applied
|
||||
* @param apply The changes to apply to the query
|
||||
*/
|
||||
SELF alsoIf(BooleanSupplier predicate, Consumer<SELF> apply);
|
||||
|
||||
/**
|
||||
* Perform an 'As of' query using history tables to return the object graph
|
||||
* as of a time in the past.
|
||||
* <p>
|
||||
* To perform this query the DB must have underlying history tables.
|
||||
*
|
||||
* @param asOf the date time in the past at which you want to view the data
|
||||
*/
|
||||
SELF asOf(Timestamp asOf);
|
||||
|
||||
/**
|
||||
* Convert the query to a DTO bean query.
|
||||
* <p>
|
||||
* We effectively use the underlying ORM query to build the SQL and then execute
|
||||
* and map it into DTO beans.
|
||||
*/
|
||||
<D> DtoQuery<D> asDto(Class<D> dtoClass);
|
||||
|
||||
/**
|
||||
* Convert the query to a UpdateQuery.
|
||||
* <p>
|
||||
* Typically this is used with query beans to covert a query bean
|
||||
* query into an UpdateQuery like the examples below.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rowsUpdated = new QCustomer()
|
||||
* .name.startsWith("Rob")
|
||||
* .asUpdate()
|
||||
* .set("active", false)
|
||||
* .update();;
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rowsUpdated = new QContact()
|
||||
* .notes.note.startsWith("Make Inactive")
|
||||
* .email.endsWith("@foo.com")
|
||||
* .customer.id.equalTo(42)
|
||||
* .asUpdate()
|
||||
* .set("inactive", true)
|
||||
* .setRaw("email = lower(email)")
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
UpdateQuery<T> asUpdate();
|
||||
|
||||
/**
|
||||
* Return a copy of the query.
|
||||
* <p>
|
||||
* This is so that you can use a Query as a "prototype" for creating other
|
||||
* query instances. You could create a Query with various where expressions
|
||||
* and use that as a "prototype" - using this copy() method to create a new
|
||||
* instance that you can then add other expressions then execute.
|
||||
* </p>
|
||||
*/
|
||||
SELF copy();
|
||||
|
||||
/**
|
||||
* Execute the query using the given transaction.
|
||||
*/
|
||||
SELF usingTransaction(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query using the given connection.
|
||||
*/
|
||||
SELF usingConnection(Connection connection);
|
||||
|
||||
/**
|
||||
* Execute the query using the given database.
|
||||
*/
|
||||
SELF usingDatabase(Database database);
|
||||
|
||||
/**
|
||||
* Ensure that the master DataSource is used if there is a read only data source
|
||||
* being used (that is using a read replica database potentially with replication lag).
|
||||
* <p>
|
||||
* When the database is configured with a read-only DataSource via
|
||||
* say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then
|
||||
* by default when a query is run without an active transaction, it uses the read-only data
|
||||
* source. We we use {@code usingMaster()} to instead ensure that the query is executed
|
||||
* against the master data source.
|
||||
*/
|
||||
SELF usingMaster();
|
||||
|
||||
/**
|
||||
* Set the base table to use for this query.
|
||||
* <p>
|
||||
* Typically this is used when a table has partitioning and we wish to specify a specific
|
||||
* partition/table to query against.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* QOrder()
|
||||
* .setBaseTable("order_2019_05")
|
||||
* .status.equalTo(Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
SELF setBaseTable(String baseTable);
|
||||
|
||||
/**
|
||||
* Specify the PersistenceContextScope to use for this query.
|
||||
* <p>
|
||||
* When this is not set the 'default' configured on {@link io.ebean.config.DatabaseConfig#setPersistenceContextScope(PersistenceContextScope)}
|
||||
* is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}.
|
||||
* <p>
|
||||
* Note that the same persistence Context is used for subsequent lazy loading and query join queries.
|
||||
* <p>
|
||||
* Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for
|
||||
* queries executed as #findIterate, #findEach, #findEachWhile.
|
||||
*
|
||||
* @param scope The scope to use for this query and subsequent lazy loading.
|
||||
*/
|
||||
SELF setPersistenceContextScope(PersistenceContextScope scope);
|
||||
|
||||
/**
|
||||
* Explicitly specify whether to use AutoTune for this query.
|
||||
* <p>
|
||||
* If you do not call this method on a query the "Implicit AutoTune mode" is
|
||||
* used to determine if AutoTune should be used for a given query.
|
||||
* <p>
|
||||
* AutoTune can add additional fetch paths to the query and specify which
|
||||
* properties are included for each path. If you have explicitly defined some
|
||||
* fetch paths AutoTune will not remove them.
|
||||
*/
|
||||
SELF setAutoTune(boolean autoTune);
|
||||
|
||||
/**
|
||||
* Execute the query allowing properties with invalid JSON to be collected and not fail the query.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch a bean with JSON content
|
||||
* EBasicJsonList bean= DB.find(EBasicJsonList.class)
|
||||
* .setId(42)
|
||||
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
|
||||
* .findOne();
|
||||
*
|
||||
*
|
||||
* // get the invalid JSON errors from the bean state
|
||||
* Map<String, Exception> errors = server().getBeanState(bean).getLoadErrors();
|
||||
*
|
||||
* // If this map is not empty tell we have invalid JSON
|
||||
* // and should try and fix the JSON content or inform the user
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
SELF setAllowLoadErrors();
|
||||
|
||||
/**
|
||||
* Set the default lazy loading batch size to use.
|
||||
* <p>
|
||||
* When lazy loading is invoked on beans loaded by this query then this sets the
|
||||
* batch size used to load those beans.
|
||||
*
|
||||
* @param lazyLoadBatchSize the number of beans to lazy load in a single batch
|
||||
*/
|
||||
SELF setLazyLoadBatchSize(int lazyLoadBatchSize);
|
||||
|
||||
/**
|
||||
* Set a label on the query.
|
||||
* <p>
|
||||
* This label can be used to help identify query performance metrics but we can also use
|
||||
* profile location enhancement on Finders so for some that would be a better option.
|
||||
*/
|
||||
SELF setLabel(String label);
|
||||
|
||||
/**
|
||||
* Set a SQL query hint.
|
||||
* <p>
|
||||
* This results in an inline comment that immediately follows
|
||||
* after the select keyword in the form: {@code /*+ hint *\/ }
|
||||
*/
|
||||
SELF setHint(String hint);
|
||||
|
||||
/**
|
||||
* Execute the query including soft deleted rows.
|
||||
* <p>
|
||||
* This means that Ebean will not add any predicates to the query for filtering out
|
||||
* soft deleted rows. You can still add your own predicates for the deleted properties
|
||||
* and effectively you have full control over the query to include or exclude soft deleted
|
||||
* rows as needed for a given use case.
|
||||
*/
|
||||
SELF setIncludeSoftDeletes();
|
||||
|
||||
/**
|
||||
* Set true if you want to disable lazy loading.
|
||||
* <p>
|
||||
* That is, once the object graph is returned further lazy loading is disabled.
|
||||
*/
|
||||
SELF setDisableLazyLoading(boolean disableLazyLoading);
|
||||
|
||||
/**
|
||||
* Set whether this query uses DISTINCT.
|
||||
*/
|
||||
SELF setDistinct(boolean distinct);
|
||||
|
||||
/**
|
||||
* Set the first row to return for this query.
|
||||
*
|
||||
* @param firstRow the first row to include in the query result.
|
||||
*/
|
||||
SELF setFirstRow(int firstRow);
|
||||
|
||||
/**
|
||||
* Set the maximum number of rows to return in the query.
|
||||
*
|
||||
* @param maxRows the maximum number of rows to return in the query.
|
||||
*/
|
||||
SELF setMaxRows(int maxRows);
|
||||
|
||||
/**
|
||||
* Set RawSql to use for this query.
|
||||
*/
|
||||
SELF setRawSql(RawSql rawSql);
|
||||
|
||||
/**
|
||||
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<CountedValue<Order.Status>> orderStatusCount =
|
||||
*
|
||||
* DB.find(Order.class)
|
||||
* .select("status")
|
||||
* .where()
|
||||
* .gt("orderDate", LocalDate.now().minusMonths(3))
|
||||
*
|
||||
* // fetch as single attribute with a COUNT
|
||||
* .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* for (CountedValue<Order.Status> entry : orderStatusCount) {
|
||||
* System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
|
||||
* }
|
||||
*
|
||||
* // produces
|
||||
*
|
||||
* count:3 orderStatus:NEW
|
||||
* count:1 orderStatus:SHIPPED
|
||||
* count:1 orderStatus:COMPLETE
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
SELF setCountDistinct(CountDistinctOrder orderBy);
|
||||
|
||||
/**
|
||||
* Set the property to use as keys for a map.
|
||||
* <p>
|
||||
* If no property is set then the id property is used.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Assuming sku is unique for products...
|
||||
*
|
||||
* Map<String,Product> productMap = DB.find(Product.class)
|
||||
* .setMapKey("sku") // sku map keys...
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapKey the property to use as keys for a map.
|
||||
*/
|
||||
SELF setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* When set to true when you want the returned beans to be read only.
|
||||
*/
|
||||
SELF setReadOnly(boolean readOnly);
|
||||
|
||||
/**
|
||||
* Set a timeout on this query.
|
||||
* <p>
|
||||
* This will typically result in a call to setQueryTimeout() on a
|
||||
* preparedStatement. If the timeout occurs an exception will be thrown - this
|
||||
* will be a SQLException wrapped up in a PersistenceException.
|
||||
* </p>
|
||||
*
|
||||
* @param secs the query timeout limit in seconds. Zero means there is no limit.
|
||||
*/
|
||||
SELF setTimeout(int secs);
|
||||
|
||||
/**
|
||||
* A hint which for JDBC translates to the Statement.fetchSize().
|
||||
* <p>
|
||||
* Gives the JDBC driver a hint as to the number of rows that should be
|
||||
* fetched from the database when more rows are needed for ResultSet.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that internally findEach and findEachWhile will set the fetch size
|
||||
* if it has not already as these queries expect to process a lot of rows.
|
||||
* If we didn't then Postgres and MySql for example would eagerly pull back
|
||||
* all the row data and potentially consume a lot of memory in the process.
|
||||
* </p>
|
||||
* <p>
|
||||
* As findEach and findEachWhile automatically set the fetch size we don't have
|
||||
* to do so generally but we might still wish to for tuning a specific use case.
|
||||
* </p>
|
||||
*/
|
||||
SELF setBufferFetchSizeHint(int fetchSize);
|
||||
|
||||
/**
|
||||
* Set the mode to use the bean cache when executing this query.
|
||||
* <p>
|
||||
* By default, "find by id" and "find by natural key" will use the bean cache
|
||||
* when bean caching is enabled. Setting this to false means that the query
|
||||
* will not use the bean cache and instead hit the database.
|
||||
* <p>
|
||||
* By default, findList() with natural keys will not use the bean cache. In that
|
||||
* case we need to explicitly use the bean cache.
|
||||
*/
|
||||
SELF setBeanCacheMode(CacheMode beanCacheMode);
|
||||
|
||||
/**
|
||||
* Set the {@link CacheMode} to use the query for executing this query.
|
||||
*/
|
||||
SELF setUseQueryCache(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Set this to false to not use the bean cache.
|
||||
* <p>
|
||||
* This method is now superseded by {@link #setBeanCacheMode(CacheMode)}
|
||||
* which provides more explicit options controlled bean cache use.
|
||||
* <p>
|
||||
* This method is likely to be deprecated in the future with migration
|
||||
* over to setUseBeanCache().
|
||||
*/
|
||||
default SELF setUseCache(boolean useCache) {
|
||||
return setBeanCacheMode(useCache ? CacheMode.ON : CacheMode.OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #setUseQueryCache(CacheMode)} with <code>ON</code> or <code>OFF</code>.
|
||||
*/
|
||||
default SELF setUseQueryCache(boolean enabled) {
|
||||
return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order by clause replacing the existing order by clause if there is
|
||||
* one.
|
||||
* <p>
|
||||
* This follows SQL syntax using commas between each property with the
|
||||
* optional asc and desc keywords representing ascending and descending order
|
||||
* respectively.
|
||||
*/
|
||||
SELF orderBy(String orderByClause);
|
||||
|
||||
/**
|
||||
* Set an OrderBy object to replace any existing OrderBy clause.
|
||||
*/
|
||||
SELF setOrderBy(OrderBy<T> orderBy);
|
||||
|
||||
/**
|
||||
* Controls, if paginated queries should always append an 'order by id' statement at the end to
|
||||
* guarantee a deterministic sort result. This may affect performance.
|
||||
* If this is not enabled, and an orderBy is set on the query, it's up to the programmer that
|
||||
* this query provides a deterministic result.
|
||||
*/
|
||||
SELF orderById(boolean orderById);
|
||||
|
||||
/**
|
||||
* Execute the query with the given lock type and WAIT.
|
||||
* <p>
|
||||
* Note that <code>forUpdate()</code> is the same as
|
||||
* <code>withLock(LockType.UPDATE)</code>.
|
||||
* <p>
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
SELF withLock(Query.LockType lockType);
|
||||
|
||||
/**
|
||||
* Execute the query with the given lock type and lock wait.
|
||||
* <p>
|
||||
* Note that <code>forUpdateNoWait()</code> is the same as
|
||||
* <code>withLock(LockType.UPDATE, LockWait.NOWAIT)</code>.
|
||||
* <p>
|
||||
* Provides us with the ability to explicitly use Postgres
|
||||
* SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks.
|
||||
*/
|
||||
SELF withLock(Query.LockType lockType, Query.LockWait lockWait);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
* <p>
|
||||
* The same as <code>withLock(LockType.UPDATE, LockWait.WAIT)</code>.
|
||||
*/
|
||||
SELF forUpdate();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "no wait" option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* <p>
|
||||
* The same as <code>withLock(LockType.UPDATE, LockWait.NOWAIT)</code>.
|
||||
*/
|
||||
SELF forUpdateNoWait();
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause with "skip locked" option.
|
||||
* <p>
|
||||
* This is typically a Postgres and Oracle only option at this stage.
|
||||
* <p>
|
||||
* The same as <code>withLock(LockType.UPDATE, LockWait.SKIPLOCKED)</code>.
|
||||
*/
|
||||
SELF forUpdateSkipLocked();
|
||||
|
||||
/**
|
||||
* Returns the set of properties or paths that are unknown (do not map to known properties or paths).
|
||||
* <p>
|
||||
* Validate the query checking the where and orderBy expression paths to confirm if
|
||||
* they represent valid properties or paths for the given bean type.
|
||||
*/
|
||||
Set<String> validate();
|
||||
|
||||
/**
|
||||
* Return the sql that was generated for executing this query.
|
||||
* <p>
|
||||
* This is only available after the query has been executed and provided only
|
||||
* for informational purposes.
|
||||
*/
|
||||
String getGeneratedSql();
|
||||
|
||||
/**
|
||||
* Return the type of beans being queried.
|
||||
*/
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
* Execute as a delete query deleting the 'root level' beans that match the predicates
|
||||
* in the query.
|
||||
* <p>
|
||||
* Note that if the query includes joins then the generated delete statement may not be
|
||||
* optimal depending on the database platform.
|
||||
*
|
||||
* @return the number of beans/rows that were deleted.
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists =
|
||||
* new QContact()
|
||||
* .email.equalTo("rob@foo.com")
|
||||
* .exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query()
|
||||
* .where().eq("email", "rob@foo.com")
|
||||
* .exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
boolean exists();
|
||||
|
||||
/**
|
||||
* Execute the query returning either a single bean or null (if no matching
|
||||
* bean is found).
|
||||
* <p>
|
||||
* If more than 1 row is found for this query then a PersistenceException is
|
||||
* thrown.
|
||||
* <p>
|
||||
* This is useful when your predicates dictate that your query should only
|
||||
* return 0 or 1 results.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // assuming the sku of products is unique...
|
||||
* Product product =
|
||||
* new QProduct()
|
||||
* .sku.equalTo("aa113")
|
||||
* .findOne();
|
||||
* ...
|
||||
* }</pre>
|
||||
* <p>
|
||||
* It is also useful with finding objects by their id when you want to specify
|
||||
* further join information to optimise the query.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Fetch order 42 and additionally fetch join its order details...
|
||||
* Order order =
|
||||
* new QOrder()
|
||||
* .fetch("details") // eagerly load the order details
|
||||
* .id.equalTo(42)
|
||||
* .findOne();
|
||||
*
|
||||
* // the order details were eagerly loaded
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
* ...
|
||||
* }</pre>
|
||||
*/
|
||||
@Nullable
|
||||
T findOne();
|
||||
|
||||
/**
|
||||
* Execute the query returning an optional bean.
|
||||
*/
|
||||
Optional<T> findOneOrEmpty();
|
||||
|
||||
/**
|
||||
* Execute the query returning the list of objects.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* new QCustomer()
|
||||
* .name.ilike("rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#findList()
|
||||
*/
|
||||
List<T> findList();
|
||||
|
||||
/**
|
||||
* Execute the query returning the result as a Stream.
|
||||
* <p>
|
||||
* Note that this can support very large queries iterating
|
||||
* any number of results. To do so internally it can use
|
||||
* multiple persistence contexts.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // use try with resources to ensure Stream is closed
|
||||
*
|
||||
* try (Stream<Customer> stream = query.findStream()) {
|
||||
* stream
|
||||
* .map(...)
|
||||
* .collect(...);
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Stream<T> findStream();
|
||||
|
||||
/**
|
||||
* Execute the query returning the set of objects.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
* new QCustomer()
|
||||
* .name.ilike("rob%")
|
||||
* .findSet();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#findSet()
|
||||
*/
|
||||
Set<T> findSet();
|
||||
|
||||
/**
|
||||
* Execute the query returning the list of Id's.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
*
|
||||
* @see Query#findIds()
|
||||
*/
|
||||
<A> List<A> findIds();
|
||||
|
||||
/**
|
||||
* Execute the query returning a map of the objects.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* <p>
|
||||
* You can use setMapKey() or asMapKey() to specify the property to be used as keys
|
||||
* on the map. If one is not specified then the id property is used.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Map<String, Product> map =
|
||||
* new QProduct()
|
||||
* .sku.asMapKey()
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#findMap()
|
||||
*/
|
||||
<K> Map<K, T> findMap();
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
* <p>
|
||||
* Note that findIterate (and findEach and findEachWhile) uses a "per graph"
|
||||
* persistence context scope and adjusts jdbc fetch buffer size for large
|
||||
* queries. As such it is better to use findList for small queries.
|
||||
* <p>
|
||||
* Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()}
|
||||
* when you have finished iterating the results (typically in a finally block).
|
||||
* <p>
|
||||
* findEach() and findEachWhile() are preferred to findIterate() as they ensure
|
||||
* the jdbc statement and resultSet are closed at the end of the iteration.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query =
|
||||
* new QCustomer()
|
||||
* .status.equalTo(Customer.Status.NEW)
|
||||
* .orderBy()
|
||||
* id.asc()
|
||||
* .query();
|
||||
*
|
||||
* try (QueryIterator<Customer> it = query.findIterate()) {
|
||||
* while (it.hasNext()) {
|
||||
* Customer customer = it.next();
|
||||
* // do something with customer ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
QueryIterator<T> findIterate();
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
* <p>
|
||||
* <h3>Example</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* new QCustomer()
|
||||
* .setDistinct(true)
|
||||
* .select(name)
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return the list of values for the selected property
|
||||
*/
|
||||
<A> List<A> findSingleAttributeList();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single value or null for a single property.
|
||||
* <p>
|
||||
* <h3>Example</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* LocalDate maxDate =
|
||||
* new QCustomer()
|
||||
* .select("max(startDate)")
|
||||
* .findSingleAttribute();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return a single value or null for the selected property
|
||||
*/
|
||||
@Nullable
|
||||
<A> A findSingleAttribute();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single optional attribute value.
|
||||
* <p>
|
||||
* <h3>Example</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Optional<String> maybeName =
|
||||
* new QCustomer()
|
||||
* .select(name)
|
||||
* .id.eq(42)
|
||||
* .status.eq(NEW)
|
||||
* .findSingleAttributeOrEmpty();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return an optional value for the selected property
|
||||
*/
|
||||
<A> Optional<A> findSingleAttributeOrEmpty();
|
||||
|
||||
/**
|
||||
* Execute the query returning a hashset of values for a single property.
|
||||
*/
|
||||
<A> Set<A> findSingleAttributeSet();
|
||||
|
||||
/**
|
||||
* Execute the query processing the beans one at a time.
|
||||
* <p>
|
||||
* This method is appropriate to process very large query results as the
|
||||
* beans are consumed one at a time and do not need to be held in memory
|
||||
* (unlike #findList #findSet etc)
|
||||
* <p>
|
||||
* Note that internally Ebean can inform the JDBC driver that it is expecting larger
|
||||
* resultSet and specifically for MySQL this hint is required to stop it's JDBC driver
|
||||
* from buffering the entire resultSet. As such, for smaller resultSets findList() is
|
||||
* generally preferable.
|
||||
* <p>
|
||||
* Compared with #findEachWhile this will always process all the beans where as
|
||||
* #findEachWhile provides a way to stop processing the query result early before
|
||||
* all the beans have been read.
|
||||
* <p>
|
||||
* This method is functionally equivalent to findIterate() but instead of using an
|
||||
* iterator uses the Consumer interface which is better suited to use with closures.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* new QCustomer()
|
||||
* .status.equalTo(Status.NEW)
|
||||
* .orderBy().id.asc()
|
||||
* .findEach((Customer customer) -> {
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer the consumer used to process the queried beans.
|
||||
*/
|
||||
void findEach(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute findEach streaming query batching the results for consuming.
|
||||
* <p>
|
||||
* This query execution will stream the results and is suited to consuming
|
||||
* large numbers of results from the database.
|
||||
* <p>
|
||||
* Typically, we use this batch consumer when we want to do further processing on
|
||||
* the beans and want to do that processing in batch form, for example - 100 at
|
||||
* a time.
|
||||
*
|
||||
* @param batch The number of beans processed in the batch
|
||||
* @param consumer Process the batch of beans
|
||||
*/
|
||||
void findEach(int batch, Consumer<List<T>> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query using callbacks to a visitor to process the resulting
|
||||
* beans one at a time.
|
||||
* <p>
|
||||
* This method is functionally equivalent to findIterate() but instead of using an
|
||||
* iterator uses the Predicate interface which is better suited to use with closures.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* new QCustomer()
|
||||
* .status.equalTo(Status.NEW)
|
||||
* .orderBy().id.asc()
|
||||
* .findEachWhile((Customer customer) -> {
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
*
|
||||
* // return true to continue processing or false to stop
|
||||
* return (customer.getId() < 40);
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer the consumer used to process the queried beans.
|
||||
*/
|
||||
void findEachWhile(Predicate<T> consumer);
|
||||
|
||||
/**
|
||||
* Return versions of a @History entity bean.
|
||||
* <p>
|
||||
* Generally this query is expected to be a find by id or unique predicates query.
|
||||
* It will execute the query against the history returning the versions of the bean.
|
||||
*/
|
||||
List<Version<T>> findVersions();
|
||||
|
||||
/**
|
||||
* Return versions of a @History entity bean between a start and end timestamp.
|
||||
* <p>
|
||||
* Generally this query is expected to be a find by id or unique predicates query.
|
||||
* It will execute the query against the history returning the versions of the bean.
|
||||
*/
|
||||
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
|
||||
|
||||
/**
|
||||
* Return the count of entities this query should return.
|
||||
* <p>
|
||||
* This is the number of 'top level' or 'root level' entities.
|
||||
*/
|
||||
int findCount();
|
||||
|
||||
/**
|
||||
* Execute find row count query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
*
|
||||
* @return a Future object for the row count query
|
||||
*/
|
||||
FutureRowCount<T> findFutureCount();
|
||||
|
||||
/**
|
||||
* Execute find Id's query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
*
|
||||
* @return a Future object for the list of Id's
|
||||
*/
|
||||
FutureIds<T> findFutureIds();
|
||||
|
||||
/**
|
||||
* Execute find list query in a background thread.
|
||||
* <p>
|
||||
* This query will execute in it's own PersistenceContext and using its own transaction.
|
||||
* What that means is that it will not share any bean instances with other queries.
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using firstRow and maxRows.
|
||||
* <p>
|
||||
* The benefit of using this over findList() is that it provides functionality to get the
|
||||
* total row count etc.
|
||||
* <p>
|
||||
* If maxRows is not set on the query prior to calling findPagedList() then a
|
||||
* PersistenceException is thrown.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList =
|
||||
* new QOrder()
|
||||
* .setFirstRow(50)
|
||||
* .setMaxRows(20)
|
||||
* .findPagedList();
|
||||
*
|
||||
* // fetch the total row count in the background
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* List<Order> orders = pagedList.getList();
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return The PagedList
|
||||
*/
|
||||
PagedList<T> findPagedList();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package io.ebean;
|
||||
|
||||
/**
|
||||
* Builder for ORM Query projection (the select and fetch part).
|
||||
*
|
||||
* @param <SELF> The builder type
|
||||
* @param <T> The entity bean type
|
||||
*/
|
||||
public interface QueryBuilderProjection<SELF, T> {
|
||||
|
||||
/**
|
||||
* Apply the path properties replacing the select and fetch clauses.
|
||||
* <p>
|
||||
* This is typically used when the FetchPath is applied to both the query and the JSON output.
|
||||
*/
|
||||
SELF apply(FetchPath fetchPath);
|
||||
|
||||
/**
|
||||
* Specify the properties to fetch on the root level entity bean in comma delimited format.
|
||||
* <p>
|
||||
* The Id property is automatically included in the properties to fetch unless setDistinct(true)
|
||||
* is set on the query.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use {@link #fetch(String, String)} to specify specific properties to fetch
|
||||
* on other non-root level paths of the object graph.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* // Only fetch the customer id, name and status.
|
||||
* // This is described as a "Partial Object"
|
||||
* .select("name, status")
|
||||
* .where.ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param fetchProperties the properties to fetch for this bean (* = all properties).
|
||||
*/
|
||||
SELF select(String fetchProperties);
|
||||
|
||||
/**
|
||||
* Set DISTINCT ON clause. This is a Postgres only SQL feature.
|
||||
*
|
||||
* @param distinctOn The properties to include in the DISTINCT ON clause.
|
||||
*/
|
||||
SELF distinctOn(String distinctOn);
|
||||
|
||||
/**
|
||||
* Apply the fetchGroup which defines what part of the object graph to load.
|
||||
*/
|
||||
SELF select(FetchGroup<T> fetchGroup);
|
||||
|
||||
/**
|
||||
* Specify a path to fetch eagerly including specific properties.
|
||||
* <p>
|
||||
* Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can
|
||||
* not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean
|
||||
* will automatically convert this fetch query into a "query join" - i.e. use fetchQuery().
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // query orders...
|
||||
* List<Order> orders = DB.find(Order.class)
|
||||
* // fetch the customer...
|
||||
* // ... getting the customers name and phone number
|
||||
* .fetch("customer", "name, phoneNumber")
|
||||
*
|
||||
* // ... also fetch the customers billing address (* = all properties)
|
||||
* .fetch("customer.billingAddress", "*")
|
||||
* .findList();
|
||||
* }</pre>
|
||||
* <p>
|
||||
* If columns is null or "*" then all columns/properties for that path are fetched.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param path the property path we wish to fetch eagerly.
|
||||
* @param fetchProperties properties of the associated bean that you want to include in the
|
||||
* fetch (* means all properties, null also means all properties).
|
||||
*/
|
||||
SELF fetch(String path, String fetchProperties);
|
||||
|
||||
/**
|
||||
* Fetch the path and properties using a "query join" (separate SQL query).
|
||||
* <p>
|
||||
* This is the same as:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, fetchProperties, FetchConfig.ofQuery())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* This would be used instead of a fetch() when we use a separate SQL query to fetch this
|
||||
* part of the object graph rather than a SQL join.
|
||||
* <p>
|
||||
* We might typically get a performance benefit when the path to fetch is a OneToMany
|
||||
* or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many
|
||||
* is high.
|
||||
*
|
||||
* @param path the property path we wish to fetch eagerly.
|
||||
* @param fetchProperties properties of the associated bean that you want to include in the
|
||||
* fetch (* means all properties, null also means all properties).
|
||||
*/
|
||||
SELF fetchQuery(String path, String fetchProperties);
|
||||
|
||||
/**
|
||||
* Fetch the path and properties using L2 bean cache.
|
||||
*
|
||||
* @param path The path of the beans we are fetching from L2 cache.
|
||||
* @param fetchProperties The properties that should be loaded.
|
||||
*/
|
||||
SELF fetchCache(String path, String fetchProperties);
|
||||
|
||||
/**
|
||||
* Fetch the path and properties lazily (via batch lazy loading).
|
||||
* <p>
|
||||
* This is the same as:
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, fetchProperties, FetchConfig.ofLazy())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* The reason for using fetchLazy() is to either:
|
||||
* <ul>
|
||||
* <li>Control/tune what is fetched as part of lazy loading</li>
|
||||
* <li>Make use of the L2 cache, build this part of the graph from L2 cache</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param path the property path we wish to fetch lazily.
|
||||
* @param fetchProperties properties of the associated bean that you want to include in the
|
||||
* fetch (* means all properties, null also means all properties).
|
||||
*/
|
||||
SELF fetchLazy(String path, String fetchProperties);
|
||||
|
||||
/**
|
||||
* Additionally specify a FetchConfig to use a separate query or lazy loading
|
||||
* to load this path.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email", FetchConfig.ofLazy(10))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param path the property path we wish to fetch eagerly.
|
||||
*/
|
||||
SELF fetch(String path, String fetchProperties, FetchConfig fetchConfig);
|
||||
|
||||
/**
|
||||
* Specify a path to fetch eagerly including all its properties.
|
||||
* <p>
|
||||
* Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can
|
||||
* not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean
|
||||
* will automatically convert this fetch query into a "query join" - i.e. use fetchQuery().
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* // eager fetch the contacts
|
||||
* .fetch("contacts")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param path the property path we wish to fetch eagerly.
|
||||
*/
|
||||
SELF fetch(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path eagerly using a "query join" (separate SQL query).
|
||||
* <p>
|
||||
* This is the same as:
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, FetchConfig.ofQuery())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* This would be used instead of a fetch() when we use a separate SQL query to fetch this
|
||||
* part of the object graph rather than a SQL join.
|
||||
* <p>
|
||||
* We might typically get a performance benefit when the path to fetch is a OneToMany
|
||||
* or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many
|
||||
* is high.
|
||||
*
|
||||
* @param path the property path we wish to fetch eagerly
|
||||
*/
|
||||
SELF fetchQuery(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path eagerly using L2 cache.
|
||||
*/
|
||||
SELF fetchCache(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path lazily (via batch lazy loading).
|
||||
* <p>
|
||||
* This is the same as:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* fetch(path, FetchConfig.ofLazy())
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* The reason for using fetchLazy() is to either:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Control/tune what is fetched as part of lazy loading</li>
|
||||
* <li>Make use of the L2 cache, build this part of the graph from L2 cache</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param path the property path we wish to fetch lazily.
|
||||
*/
|
||||
SELF fetchLazy(String path);
|
||||
|
||||
/**
|
||||
* Additionally specify a JoinConfig to specify a "query join" and or define
|
||||
* the lazy loading query.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* // lazy fetch contacts with a batch size of 100
|
||||
* .fetch("contacts", FetchConfig.ofLazy(100))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
SELF fetch(String path, FetchConfig fetchConfig);
|
||||
|
||||
}
|
||||
@@ -1,50 +1,29 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Slow query event.
|
||||
* The data for the slow query.
|
||||
*/
|
||||
public class SlowQueryEvent {
|
||||
|
||||
private final String sql;
|
||||
|
||||
private final long timeMillis;
|
||||
|
||||
private final int rowCount;
|
||||
|
||||
private final ObjectGraphNode originNode;
|
||||
|
||||
/**
|
||||
* Construct with the SQL and execution time in millis.
|
||||
*/
|
||||
public SlowQueryEvent(String sql, long timeMillis, int rowCount, ObjectGraphNode originNode) {
|
||||
this.sql = sql;
|
||||
this.timeMillis = timeMillis;
|
||||
this.rowCount = rowCount;
|
||||
this.originNode = originNode;
|
||||
}
|
||||
public interface SlowQueryEvent {
|
||||
|
||||
/**
|
||||
* Return the SQL for the slow query.
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Return the execution time in millis.
|
||||
*/
|
||||
public long getTimeMillis() {
|
||||
return timeMillis;
|
||||
}
|
||||
long getTimeMillis();
|
||||
|
||||
/**
|
||||
* Return the total row count associated with the query.
|
||||
*/
|
||||
public int getRowCount() {
|
||||
return rowCount;
|
||||
}
|
||||
int getRowCount();
|
||||
|
||||
/**
|
||||
* Return the origin point for the root query.
|
||||
@@ -53,7 +32,20 @@ public class SlowQueryEvent {
|
||||
* shows the code that invoked the query.
|
||||
* </p>
|
||||
*/
|
||||
public ObjectGraphNode getOriginNode() {
|
||||
return originNode;
|
||||
}
|
||||
ObjectGraphNode getOriginNode();
|
||||
|
||||
/**
|
||||
* Return the bind parameters.
|
||||
*/
|
||||
List<Object> getBindParams();
|
||||
|
||||
/**
|
||||
* Return the label.
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
}
|
||||
|
||||
@@ -4,22 +4,8 @@ public final class AnsiSqlRowsLimiter implements SqlLimiter {
|
||||
|
||||
@Override
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
String dbSql = request.getDbSql();
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
sb.append("select ");
|
||||
if (request.isDistinct()) {
|
||||
sb.append("distinct ");
|
||||
}
|
||||
sb.append(dbSql);
|
||||
int firstRow = request.getFirstRow();
|
||||
if (firstRow > 0) {
|
||||
sb.append(" offset ").append(firstRow).append(" rows");
|
||||
}
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
sb.append(" fetch next ").append(maxRows).append(" rows only");
|
||||
}
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
final var ansiSql = request.ansiOffsetRows();
|
||||
final var sql = request.getDbPlatform().completeSql(ansiSql, request.getOrmQuery());
|
||||
return new SqlLimitResponse(sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,16 @@ public final class LimitOffsetSqlLimiter implements SqlLimiter {
|
||||
|
||||
@Override
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
String dbSql = request.getDbSql();
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
sb.append("select ");
|
||||
if (request.isDistinct()) {
|
||||
sb.append("distinct ");
|
||||
}
|
||||
sb.append(dbSql);
|
||||
final var buffer = request.selectDistinctOnSql();
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
sb.append(" limit ").append(maxRows);
|
||||
buffer.append(" limit ").append(maxRows);
|
||||
}
|
||||
int firstRow = request.getFirstRow();
|
||||
if (firstRow > 0) {
|
||||
sb.append(" offset ").append(firstRow);
|
||||
buffer.append(" offset ").append(firstRow);
|
||||
}
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
return new SqlLimitResponse(sql);
|
||||
return new SqlLimitResponse(request.getDbPlatform().completeSql(buffer.toString(), request.getOrmQuery()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,21 @@ import io.ebean.Query;
|
||||
*/
|
||||
public interface SqlLimitRequest {
|
||||
|
||||
/**
|
||||
* Return ANSI SQL Offset and next rows.
|
||||
*/
|
||||
String ansiOffsetRows();
|
||||
|
||||
/**
|
||||
* Create and return buffer including the select distinct clause.
|
||||
*/
|
||||
StringBuilder selectDistinct();
|
||||
|
||||
/**
|
||||
* Create and return buffer including the select distinct on clause.
|
||||
*/
|
||||
StringBuilder selectDistinctOnSql();
|
||||
|
||||
/**
|
||||
* Return true if the query uses distinct.
|
||||
*/
|
||||
|
||||
+26
-26
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -89,25 +89,25 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -125,13 +125,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-jackson-mapper</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -155,37 +155,37 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-spring-txn</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- platforms -->
|
||||
@@ -193,79 +193,79 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-clickhouse</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-db2</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-h2</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-hana</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mariadb</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-mysql</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-nuodb</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-oracle</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgres</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis-types</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlite</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-sqlserver</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -22,7 +22,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -46,7 +46,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -147,21 +147,21 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-h2</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-sqlserver</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.api;
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.core.BindPadding;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -57,17 +58,22 @@ public final class LoadManyRequest extends LoadRequest {
|
||||
return loadContext.fullPath();
|
||||
}
|
||||
|
||||
private List<Object> parentIdList(SpiEbeanServer server) {
|
||||
List<Object> idList = new ArrayList<>();
|
||||
BeanPropertyAssocMany<?> many = many();
|
||||
private List<Object> parentIdList(SpiEbeanServer server, BeanPropertyAssocMany<?> many, PersistenceContext pc) {
|
||||
final var idList = new ArrayList<>(loadContext.size());
|
||||
final var descriptor = many.descriptor();
|
||||
for (int i = 0; i < loadContext.size(); i++) {
|
||||
BeanCollection<?> bc = loadContext.get(i);
|
||||
final BeanCollection<?> bc = loadContext.get(i);
|
||||
if (bc != null) {
|
||||
if (lazy && !originIncluded && bc == originCollection) {
|
||||
originIncluded = true;
|
||||
}
|
||||
idList.add(many.parentId(bc.owner()));
|
||||
final var parent = bc.owner();
|
||||
final var parentId = descriptor.getId(parent);
|
||||
idList.add(parentId);
|
||||
bc.setLoader(server); // don't use the load buffer again
|
||||
if (lazy) {
|
||||
descriptor.contextPutIfAbsent(pc, parentId, parent);
|
||||
if (!originIncluded && bc == originCollection) {
|
||||
originIncluded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (originCollection != null && !originIncluded) {
|
||||
@@ -75,7 +81,7 @@ public final class LoadManyRequest extends LoadRequest {
|
||||
idList.add(many.parentId(originCollection.owner()));
|
||||
originCollection.setLoader(server); // don't use the load buffer again
|
||||
}
|
||||
if (many.targetDescriptor().isPadInExpression()) {
|
||||
if (descriptor.isPadInExpression()) {
|
||||
BindPadding.padIds(idList);
|
||||
}
|
||||
return idList;
|
||||
@@ -99,8 +105,9 @@ public final class LoadManyRequest extends LoadRequest {
|
||||
query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
|
||||
}
|
||||
query.setLazyLoadForParents(many);
|
||||
many.addWhereParentIdIn(query, parentIdList(server));
|
||||
query.setPersistenceContext(loadContext.persistenceContext());
|
||||
final var pc = loadContext.persistenceContext();
|
||||
many.addWhereParentIdIn(query, parentIdList(server, many, pc));
|
||||
query.setPersistenceContext(pc);
|
||||
query.setLoadDescription(lazy ? "lazy" : "query", description());
|
||||
if (lazy) {
|
||||
query.setLazyLoadBatchSize(loadContext.batchSize());
|
||||
|
||||
@@ -71,7 +71,7 @@ public interface SpiExpression extends Expression {
|
||||
*
|
||||
* @param request the associated request.
|
||||
*/
|
||||
void addBindValues(SpiExpressionRequest request);
|
||||
void addBindValues(SpiExpressionBind request);
|
||||
|
||||
/**
|
||||
* Validate all the properties/paths associated with this expression.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* Expression bind values capture.
|
||||
*/
|
||||
public interface SpiExpressionBind {
|
||||
|
||||
/**
|
||||
* Return the bean descriptor for the root type.
|
||||
*/
|
||||
BeanDescriptor<?> descriptor();
|
||||
|
||||
/**
|
||||
* Add an encryption key to bind to this request.
|
||||
*/
|
||||
void addBindEncryptKey(Object encryptKey);
|
||||
|
||||
/**
|
||||
* Add a bind value to this request.
|
||||
*/
|
||||
void addBindValue(Object bindValue);
|
||||
|
||||
/**
|
||||
* Escapes a string to use it as exact match in Like clause.
|
||||
*/
|
||||
String escapeLikeString(String value);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionRequest;
|
||||
|
||||
@@ -10,7 +9,7 @@ import java.util.List;
|
||||
/**
|
||||
* Request object used for gathering expression sql and bind values.
|
||||
*/
|
||||
public interface SpiExpressionRequest extends DbExpressionRequest {
|
||||
public interface SpiExpressionRequest extends SpiExpressionBind, DbExpressionRequest {
|
||||
|
||||
/**
|
||||
* Return the DB specific handler for JSON and ARRAY expressions.
|
||||
@@ -22,11 +21,6 @@ public interface SpiExpressionRequest extends DbExpressionRequest {
|
||||
*/
|
||||
String parseDeploy(String logicalProp);
|
||||
|
||||
/**
|
||||
* Return the bean descriptor for the root type.
|
||||
*/
|
||||
BeanDescriptor<?> descriptor();
|
||||
|
||||
/**
|
||||
* Return the associated QueryRequest.
|
||||
*/
|
||||
@@ -58,16 +52,6 @@ public interface SpiExpressionRequest extends DbExpressionRequest {
|
||||
*/
|
||||
SpiExpressionRequest parse(String expression);
|
||||
|
||||
/**
|
||||
* Add an encryption key to bind to this request.
|
||||
*/
|
||||
void addBindEncryptKey(Object encryptKey);
|
||||
|
||||
/**
|
||||
* Add a bind value to this request.
|
||||
*/
|
||||
void addBindValue(Object bindValue);
|
||||
|
||||
/**
|
||||
* Return the accumulated expression sql for all expressions in this request.
|
||||
*/
|
||||
@@ -88,11 +72,6 @@ public interface SpiExpressionRequest extends DbExpressionRequest {
|
||||
*/
|
||||
void appendLike(boolean rawLikeExpression);
|
||||
|
||||
/**
|
||||
* Escapes a string to use it as exact match in Like clause.
|
||||
*/
|
||||
String escapeLikeString(String value);
|
||||
|
||||
/**
|
||||
* Append IN expression taking into account platform and type support for Multi-value.
|
||||
*/
|
||||
|
||||
@@ -417,6 +417,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
@Override
|
||||
SpiQuery<T> copy();
|
||||
|
||||
/**
|
||||
* Return the distinct on clause.
|
||||
*/
|
||||
String distinctOn();
|
||||
|
||||
/**
|
||||
* Return a copy of the query attaching to a different EbeanServer.
|
||||
*/
|
||||
@@ -522,7 +527,6 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
* Return true if the query should include the Id property.
|
||||
* <p>
|
||||
* distinct and single attribute queries exclude the Id property.
|
||||
* </p>
|
||||
*/
|
||||
boolean isWithId();
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.config.SlowQueryEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Slow query event.
|
||||
*/
|
||||
final class DSlowQueryEvent implements SlowQueryEvent {
|
||||
|
||||
private final String sql;
|
||||
private final long timeMillis;
|
||||
private final int rowCount;
|
||||
private final ObjectGraphNode originNode;
|
||||
private final List<Object> bindParams;
|
||||
private final String label;
|
||||
private final ProfileLocation profileLocation;
|
||||
|
||||
/**
|
||||
* Construct with the SQL and execution time in millis.
|
||||
*/
|
||||
DSlowQueryEvent(String sql, long timeMillis, int rowCount, ObjectGraphNode originNode,
|
||||
List<Object> bindParams, String label, ProfileLocation profileLocation) {
|
||||
this.sql = sql;
|
||||
this.timeMillis = timeMillis;
|
||||
this.rowCount = rowCount;
|
||||
this.originNode = originNode;
|
||||
this.bindParams = bindParams;
|
||||
this.profileLocation = profileLocation;
|
||||
this.label = label != null ? label : profileLocation == null ? null : profileLocation.label();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeMillis() {
|
||||
return timeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectGraphNode getOriginNode() {
|
||||
return originNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getBindParams() {
|
||||
return bindParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return profileLocation;
|
||||
}
|
||||
}
|
||||
@@ -63,8 +63,8 @@ final class DefaultBeanLoader {
|
||||
Object parentId = parentDesc.getId(parentBean);
|
||||
if (pc == null) {
|
||||
pc = new DefaultPersistenceContext();
|
||||
parentDesc.contextPut(pc, parentId, parentBean);
|
||||
}
|
||||
parentDesc.contextPutIfAbsent(pc, parentId, parentBean);
|
||||
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching() && many.isUseCache();
|
||||
if (useManyIdCache) {
|
||||
Boolean readOnly = null;
|
||||
|
||||
@@ -2020,7 +2020,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public void slowQueryCheck(long timeMicros, int rowCount, SpiQuery<?> query) {
|
||||
if (timeMicros > slowQueryMicros && slowQueryListener != null) {
|
||||
slowQueryListener.process(new SlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount, query.parentNode()));
|
||||
List<Object> bindParams = new SlowQueryBindCapture(query).capture();
|
||||
slowQueryListener.process(new DSlowQueryEvent(query.getGeneratedSql(), timeMicros / 1000L, rowCount,
|
||||
query.parentNode(), bindParams, query.label(), query.profileLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.avaje.applog.AppLog;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.config.SlowQueryEvent;
|
||||
import io.ebean.config.SlowQueryListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.System.Logger.Level.WARNING;
|
||||
|
||||
/**
|
||||
@@ -16,11 +18,10 @@ final class DefaultSlowQueryListener implements SlowQueryListener {
|
||||
|
||||
@Override
|
||||
public void process(SlowQueryEvent event) {
|
||||
String firstStack = "";
|
||||
ObjectGraphNode node = event.getOriginNode();
|
||||
if (node != null) {
|
||||
firstStack = node.origin().top();
|
||||
}
|
||||
log.log(WARNING, "Slow query warning - millis:{0} rows:{1} caller[{2}] sql[{3}]", event.getTimeMillis(), event.getRowCount(), firstStack, event.getSql());
|
||||
ProfileLocation profileLocation = event.getProfileLocation();
|
||||
String loc = profileLocation == null ? "" : profileLocation.fullLocation();
|
||||
List<Object> bindParams = event.getBindParams();
|
||||
log.log(WARNING, "Slow query warning - millis:{0} rows:{1} location:{2} sql[{3}] params{4}",
|
||||
event.getTimeMillis(), event.getRowCount(), loc, event.getSql(), bindParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +378,9 @@ public final class InternalConfiguration {
|
||||
*/
|
||||
private DataTimeZone initDataTimeZone() {
|
||||
String tz = config.getDataTimeZone();
|
||||
if ("NoTimeZone".equals(tz)) {
|
||||
return new NoDataTimeZone();
|
||||
}
|
||||
if (tz == null) {
|
||||
if (isMySql(getPlatform())) {
|
||||
return new MySqlDataTimeZone();
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionList;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.persist.MultiValueWrapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class SlowQueryBindCapture implements SpiExpressionBind {
|
||||
|
||||
private final SpiQuery<?> query;
|
||||
private final List<Object> bindParams = new ArrayList<>();
|
||||
|
||||
SlowQueryBindCapture(SpiQuery<?> query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
List<Object> capture() {
|
||||
var params = query.bindParams();
|
||||
if (params != null) {
|
||||
var positionedParameters = params.positionedParameters();
|
||||
for (BindParams.Param param : positionedParameters) {
|
||||
if (param.isInParam()) {
|
||||
add(param.inValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
Object id = query.getId();
|
||||
if (id != null) {
|
||||
add(id);
|
||||
}
|
||||
SpiExpressionList<?> spiExpressionList = query.whereExpressions();
|
||||
if (spiExpressionList != null) {
|
||||
spiExpressionList.addBindValues(this);
|
||||
}
|
||||
return bindParams;
|
||||
}
|
||||
|
||||
private void add(Object value) {
|
||||
if (value instanceof MultiValueWrapper) {
|
||||
var mvw = (MultiValueWrapper) value;
|
||||
bindParams.add(mvw.getValues());
|
||||
} else {
|
||||
bindParams.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDescriptor<?> descriptor() {
|
||||
return query.descriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValue(Object bindValue) {
|
||||
add(bindValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindEncryptKey(Object encryptKey) {
|
||||
bindParams.add("*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String escapeLikeString(String value) {
|
||||
return query.descriptor().ebeanServer().databasePlatform().escapeLikeString(value);
|
||||
}
|
||||
}
|
||||
@@ -713,9 +713,9 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a mutableHash for the given JSON value.
|
||||
* creates a mutableHash for the given Object value.
|
||||
*/
|
||||
public MutableValueInfo createMutableInfo(String json) {
|
||||
public MutableValueInfo createMutableInfo(Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
+22
-22
@@ -39,22 +39,22 @@ public final class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableValueInfo createMutableInfo(String json) {
|
||||
public MutableValueInfo createMutableInfo(Object value) {
|
||||
if (sourceDetection) {
|
||||
return new SourceMutableValue(scalarType, json);
|
||||
return new SourceMutableValue(scalarType, value);
|
||||
} else {
|
||||
return new ChecksumMutableValue(scalarType, json);
|
||||
return new ChecksumMutableValue(scalarType, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Next when no prior MutableValueInfo.
|
||||
*/
|
||||
private MutableValueNext next(String json) {
|
||||
private MutableValueNext next(String json, Object value) {
|
||||
if (sourceDetection) {
|
||||
return new SourceMutableValue(scalarType, json);
|
||||
return new SourceMutableValue(scalarType, value);
|
||||
} else {
|
||||
return new NextPair(json, new ChecksumMutableValue(scalarType, json));
|
||||
return new NextPair(json, new ChecksumMutableValue(scalarType, value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,12 +66,13 @@ public final class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
|
||||
// mutation detection based on json content or checksum of json content
|
||||
// only perform serialisation to json once
|
||||
final String json = scalarType.format(value);
|
||||
|
||||
final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex);
|
||||
if (oldHash == null) {
|
||||
if (value == null) {
|
||||
return false; // no change, still null
|
||||
}
|
||||
ebi.mutableNext(propertyIndex, next(json));
|
||||
ebi.mutableNext(propertyIndex, next(json, value));
|
||||
return true;
|
||||
}
|
||||
// only perform compute of checksum/hash once (if checksum based)
|
||||
@@ -92,9 +93,8 @@ public final class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
|
||||
}
|
||||
if (bean != null) {
|
||||
setValue(bean, value);
|
||||
String json = reader.popJson();
|
||||
if (json != null) {
|
||||
final MutableValueInfo hash = createMutableInfo(json);
|
||||
if (value != null) {
|
||||
final MutableValueInfo hash = createMutableInfo(value);
|
||||
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
|
||||
}
|
||||
}
|
||||
@@ -110,10 +110,10 @@ public final class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) {
|
||||
if (cacheData instanceof String) {
|
||||
// parse back from string to support optimisation of java object serialisation
|
||||
final String jsonContent = (String) cacheData;
|
||||
final MutableValueInfo hash = createMutableInfo(jsonContent);
|
||||
cacheData = scalarType.parse((String) cacheData);
|
||||
final MutableValueInfo hash = createMutableInfo(cacheData);
|
||||
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
|
||||
cacheData = scalarType.parse(jsonContent);
|
||||
|
||||
}
|
||||
setValue(bean, cacheData);
|
||||
}
|
||||
@@ -150,9 +150,9 @@ public final class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
|
||||
private final ScalarType<?> parent;
|
||||
private final long checksum;
|
||||
|
||||
ChecksumMutableValue(ScalarType<?> parent, String json) {
|
||||
ChecksumMutableValue(ScalarType<?> parent, Object value) {
|
||||
this.parent = parent;
|
||||
this.checksum = Checksum.checksum(json);
|
||||
this.checksum = Checksum.checksum(parent.format(value));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,33 +185,33 @@ public final class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
|
||||
*/
|
||||
private static final class SourceMutableValue implements MutableValueInfo, MutableValueNext {
|
||||
|
||||
private final String originalJson;
|
||||
private final String formattedJson;
|
||||
private final ScalarType<?> parent;
|
||||
|
||||
SourceMutableValue(ScalarType<?> parent, String json) {
|
||||
SourceMutableValue(ScalarType<?> parent, Object value) {
|
||||
this.parent = parent;
|
||||
this.originalJson = json;
|
||||
this.formattedJson = parent.format(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableValueNext nextDirty(String json) {
|
||||
return Objects.equals(originalJson, json) ? null : new SourceMutableValue(parent, json);
|
||||
return Objects.equals(formattedJson, json) ? null : new SourceMutableValue(parent, parent.parse(json));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqualToObject(Object obj) {
|
||||
return Objects.equals(originalJson, parent.format(obj));
|
||||
return Objects.equals(formattedJson, parent.format(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get() {
|
||||
// rebuild the 'oldValue' for change log etc
|
||||
return parent.parse(originalJson);
|
||||
return parent.parse(formattedJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String content() {
|
||||
return originalJson;
|
||||
return formattedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy.id;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -143,7 +144,7 @@ public interface IdBinder {
|
||||
/**
|
||||
* Binds multiple id value to a request.
|
||||
*/
|
||||
void addBindValues(SpiExpressionRequest request, Collection<?> ids);
|
||||
void addBindValues(SpiExpressionBind request, Collection<?> ids);
|
||||
|
||||
/**
|
||||
* Return the sql for binding the id using an IN clause.
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.id;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -269,7 +270,7 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request, Collection<?> values) {
|
||||
public void addBindValues(SpiExpressionBind request, Collection<?> values) {
|
||||
for (Object value : values) {
|
||||
final EntityBean bean = (EntityBean) value;
|
||||
for (BeanProperty prop : props) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy.id;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.bind.DataBind;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
@@ -130,7 +131,7 @@ final class IdBinderEmpty implements IdBinder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request, Collection<?> ids) {
|
||||
public void addBindValues(SpiExpressionBind request, Collection<?> ids) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.id;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
@@ -143,7 +144,7 @@ public final class IdBinderSimple implements IdBinder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request, Collection<?> values) {
|
||||
public void addBindValues(SpiExpressionBind request, Collection<?> values) {
|
||||
List<Object> copy = new ArrayList<>(values);
|
||||
copy.replaceAll(idValue -> convertSetId(idValue, null));
|
||||
request.addBindValue(new MultiValueWrapper(copy));
|
||||
|
||||
@@ -81,7 +81,7 @@ abstract class AbstractExpression implements SpiExpression {
|
||||
validation.validate(propName);
|
||||
}
|
||||
|
||||
protected final ElPropertyValue getElProp(SpiExpressionRequest request) {
|
||||
protected final ElPropertyValue getElProp(SpiExpressionBind request) {
|
||||
return request.descriptor().elGetValue(propName);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,7 @@ abstract class AbstractTextExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
// do nothing, only execute against document store
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ final class AllEqualsExpression extends NonPrepareExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
if (propMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -55,7 +56,7 @@ final class ArrayContainsExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (Object value : values) {
|
||||
request.addBindValue(value);
|
||||
}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
/**
|
||||
@@ -42,7 +43,7 @@ final class ArrayIsEmptyExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
// nothing to bind
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -27,7 +28,7 @@ final class BetweenExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
// bind the key as well as the value
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ final class BetweenPropertyExpression extends NonPrepareExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
request.addBindValue(val());
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
/**
|
||||
@@ -47,7 +48,7 @@ final class BitwiseExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
request.addBindValue(flags);
|
||||
request.addBindValue(match);
|
||||
}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -22,7 +23,7 @@ final class CaseInsensitiveEqualExpression extends AbstractValueExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
// bind the key as well as the value
|
||||
|
||||
+2
-8
@@ -5,12 +5,7 @@ import io.ebean.LikeType;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
@@ -177,8 +172,7 @@ final class DefaultExampleExpression implements SpiExpression, ExampleExpression
|
||||
* Adds bind values to the request.
|
||||
*/
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (SpiExpression item : list) {
|
||||
item.addBindValues(request);
|
||||
}
|
||||
|
||||
+1
-1
@@ -446,7 +446,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (SpiExpression expr : list) {
|
||||
expr.addBindValues(request);
|
||||
}
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ final class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreE
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (Object bindParam : bindParams) {
|
||||
request.addBindValue(bindParam);
|
||||
}
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ final class ExistsSqlQueryExpression implements SpiExpression, UnsupportedDocSto
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (Object bindParam : bindParams) {
|
||||
request.addBindValue(bindParam);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ final class IdExpression extends NonPrepareExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
// 'flatten' EmbeddedId and multiple ID cases
|
||||
// into an array of the underlying scalar field values
|
||||
for (Object bindIdValue : request.descriptor().bindIdValues(value)) {
|
||||
|
||||
@@ -50,7 +50,7 @@ public final class IdInExpression extends NonPrepareExpression implements IdInCo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
if (idCollection.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.MultiValueWrapper;
|
||||
|
||||
@@ -95,7 +92,7 @@ public final class InExpression extends AbstractExpression implements IdInCommon
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
if (empty) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@ package io.ebeaninternal.server.expression;
|
||||
import io.ebean.Pairs;
|
||||
import io.ebean.Pairs.Entry;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.persist.MultiValueWrapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -53,8 +50,7 @@ final class InPairsExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
// Note at this point entries may have been removed when used with l2 caching
|
||||
// ... for each l2 cache hit an entry was removed
|
||||
this.concatBindValues = new ArrayList<>(entries.size());
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -25,7 +26,7 @@ final class InRangeExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
// bind the key twice, for both values
|
||||
|
||||
@@ -2,10 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.InTuples;
|
||||
import io.ebean.service.SpiInTuples;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -32,7 +29,7 @@ final class InTuplesExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (Object[] entry : entries) {
|
||||
for (Object value : entry) {
|
||||
requireNonNull(value);
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -25,7 +22,7 @@ final class IsEmptyExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
// no bind values
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -91,7 +92,7 @@ final class JsonPathExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
if (value != null) {
|
||||
// value is null for EXISTS/NOT EXISTS
|
||||
request.addBindValue(value);
|
||||
|
||||
@@ -122,7 +122,7 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (SpiExpression expr : exprList.internalList()) {
|
||||
expr.addBindValues(request);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
|
||||
import io.ebean.LikeType;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -18,7 +19,7 @@ final class LikeExpression extends AbstractValueExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
// bind the key as well as the value
|
||||
@@ -71,7 +72,7 @@ final class LikeExpression extends AbstractValueExpression {
|
||||
return strValue().equals(that.strValue());
|
||||
}
|
||||
|
||||
private static String getValue(String value, boolean caseInsensitive, LikeType type, SpiExpressionRequest request) {
|
||||
private static String getValue(String value, boolean caseInsensitive, LikeType type, SpiExpressionBind request) {
|
||||
if (caseInsensitive) {
|
||||
value = value.toLowerCase();
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
@@ -101,7 +96,7 @@ abstract class LogicExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
expOne.addBindValues(request);
|
||||
expTwo.addBindValues(request);
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
|
||||
import io.ebean.LikeType;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -16,8 +17,7 @@ final class NativeILikeExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
// bind the key as well as the value
|
||||
|
||||
@@ -69,7 +69,7 @@ final class NoopExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.Expression;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
final class NotExpression implements SpiExpression {
|
||||
@@ -59,7 +54,7 @@ final class NotExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
exp.addBindValues(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -43,7 +40,7 @@ final class NullExpression extends AbstractExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ final class RawExpression extends NonPrepareExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
if (values != null) {
|
||||
for (Object value : values) {
|
||||
if (value instanceof Collection<?>) {
|
||||
|
||||
@@ -2,10 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
public final class SimpleExpression extends AbstractValueExpression {
|
||||
@@ -47,8 +44,7 @@ public final class SimpleExpression extends AbstractValueExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null) {
|
||||
if (prop.isAssocId()) {
|
||||
|
||||
@@ -70,11 +70,11 @@ final class SubQueryExpression extends AbstractExpression implements Unsupported
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
request.property(propName).append(op.expression).append('(').parse(sql).append(')');
|
||||
request.property(propName).append(op.expression).append('(').append(sql).append(')');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (Object bindParam : bindParams) {
|
||||
request.addBindValue(bindParam);
|
||||
}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
|
||||
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiExpression;
|
||||
import io.ebeaninternal.api.SpiExpressionBind;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -42,7 +43,7 @@ final class SubQueryRawExpression extends AbstractExpression implements Unsuppor
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
public void addBindValues(SpiExpressionBind request) {
|
||||
for (Object bindParam : bindParams) {
|
||||
request.addBindValue(bindParam);
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ final class BindablePropertyJsonInsert extends BindableProperty {
|
||||
} else {
|
||||
// on insert store hash and push json
|
||||
final String json = prop.format(value);
|
||||
final MutableValueInfo hash = prop.createMutableInfo(json);
|
||||
final MutableValueInfo hash = prop.createMutableInfo(value);
|
||||
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
|
||||
request.pushJson(json);
|
||||
request.bind(value, prop);
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.query;
|
||||
import io.ebean.*;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.SqlLimitRequest;
|
||||
import io.ebean.config.dbplatform.SqlLimitResponse;
|
||||
import io.ebean.config.dbplatform.SqlLimiter;
|
||||
import io.ebean.text.PathProperties;
|
||||
@@ -518,7 +517,7 @@ final class CQueryBuilder {
|
||||
private final boolean distinct;
|
||||
private final boolean countSingleAttribute;
|
||||
private final String dbOrderBy;
|
||||
private boolean useSqlLimiter;
|
||||
private final boolean useSqlLimiter;
|
||||
private boolean hasWhere;
|
||||
|
||||
private BuildReq(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
|
||||
@@ -535,13 +534,15 @@ final class CQueryBuilder {
|
||||
this.distinct = query.isDistinct() || select.isSqlDistinct();
|
||||
this.dbOrderBy = predicates.dbOrderBy();
|
||||
this.countSingleAttribute = query.isCountDistinct() && query.isSingleAttribute();
|
||||
this.useSqlLimiter = selectClause == null
|
||||
&& query.hasMaxRowsOrFirstRow()
|
||||
&& (select.distinctOn() != null || select.manyProperty() == null || query.isSingleAttribute());
|
||||
}
|
||||
|
||||
private void appendSelect() {
|
||||
if (selectClause != null) {
|
||||
sb.append(selectClause);
|
||||
} else {
|
||||
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && (select.manyProperty() == null || query.isSingleAttribute()));
|
||||
if (!useSqlLimiter) {
|
||||
appendSelectDistinct();
|
||||
}
|
||||
@@ -566,7 +567,7 @@ final class CQueryBuilder {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append(')');
|
||||
}
|
||||
if (distinct && dbOrderBy != null) {
|
||||
if (distinct && dbOrderBy != null && query.distinctOn() == null) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
String[] tokens = DbOrderByTrim.trim(dbOrderBy).split(",");
|
||||
for (String token : tokens) {
|
||||
@@ -718,7 +719,7 @@ final class CQueryBuilder {
|
||||
}
|
||||
if (useSqlLimiter) {
|
||||
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
|
||||
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform, distinct);
|
||||
var r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform, distinct, select.distinctOn(), hint(), inlineSqlComment());
|
||||
return sqlLimiter.limit(r);
|
||||
} else {
|
||||
if (updateStatement) {
|
||||
|
||||
@@ -62,6 +62,7 @@ public final class CQueryPredicates {
|
||||
*/
|
||||
private String dbFilterMany;
|
||||
private String dbOrderBy;
|
||||
private String dbDistinctOn;
|
||||
private String dbUpdateClause;
|
||||
/**
|
||||
* Includes from where and order by clauses.
|
||||
@@ -209,6 +210,10 @@ public final class CQueryPredicates {
|
||||
}
|
||||
}
|
||||
if (buildSql) {
|
||||
final String distinctOn = query.distinctOn();
|
||||
if (distinctOn != null) {
|
||||
dbDistinctOn = deployParser.parse(distinctOn);
|
||||
}
|
||||
predicateIncludes = deployParser.includes();
|
||||
}
|
||||
}
|
||||
@@ -231,6 +236,9 @@ public final class CQueryPredicates {
|
||||
if (dbOrderBy != null) {
|
||||
dbOrderBy = alias.parse(dbOrderBy);
|
||||
}
|
||||
if (dbDistinctOn != null) {
|
||||
dbDistinctOn = alias.parse(dbDistinctOn);
|
||||
}
|
||||
}
|
||||
|
||||
private String parseOrderBy() {
|
||||
@@ -351,6 +359,13 @@ public final class CQueryPredicates {
|
||||
return dbOrderBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the db distinct on clause.
|
||||
*/
|
||||
String dbDistinctOn() {
|
||||
return dbDistinctOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the includes required for the where and order by clause.
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,11 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
|
||||
return new DFetchGroup<>(detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> distinctOn(String distinctOn) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> select(String columns) {
|
||||
detail.select(columns);
|
||||
|
||||
@@ -3,8 +3,9 @@ package io.ebeaninternal.server.query;
|
||||
import io.ebean.PagedList;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -20,7 +21,7 @@ public final class LimitOffsetPagedList<T> implements PagedList<T> {
|
||||
private final int firstRow;
|
||||
private final int maxRows;
|
||||
|
||||
private int foregroundTotalRowCount = -1;
|
||||
private int totalRowCount = -1;
|
||||
private Future<Integer> futureRowCount;
|
||||
private List<T> list;
|
||||
|
||||
@@ -57,7 +58,12 @@ public final class LimitOffsetPagedList<T> implements PagedList<T> {
|
||||
lock.lock();
|
||||
try {
|
||||
if (list == null) {
|
||||
list = server.findList(query);
|
||||
if (totalRowCount == 0) {
|
||||
// already count and no rows
|
||||
list = Collections.emptyList();
|
||||
} else {
|
||||
list = server.findList(query);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
} finally {
|
||||
@@ -87,20 +93,21 @@ public final class LimitOffsetPagedList<T> implements PagedList<T> {
|
||||
public int getTotalCount() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (totalRowCount > -1) {
|
||||
return totalRowCount;
|
||||
}
|
||||
if (futureRowCount != null) {
|
||||
try {
|
||||
// background query already initiated so get it with a wait
|
||||
return futureRowCount.get();
|
||||
totalRowCount = futureRowCount.get();
|
||||
return totalRowCount;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
// already fetched?
|
||||
if (foregroundTotalRowCount > -1) return foregroundTotalRowCount;
|
||||
|
||||
// just using foreground thread
|
||||
foregroundTotalRowCount = server.findCount(query);
|
||||
return foregroundTotalRowCount;
|
||||
totalRowCount = server.findCount(query);
|
||||
return totalRowCount;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
@@ -154,6 +154,10 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
|
||||
private String buildDistinctOn() {
|
||||
String distinctOn = predicates.dbDistinctOn();
|
||||
if (distinctOn != null) {
|
||||
return distinctOn;
|
||||
}
|
||||
if (rawSql || !distinctOnPlatform || !sqlDistinct || Type.COUNT == query.type()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
* Lazy loading batch size (can override server wide default).
|
||||
*/
|
||||
private int lazyLoadBatchSize;
|
||||
private String distinctOn;
|
||||
private OrderBy<T> orderBy;
|
||||
private String loadMode;
|
||||
private String loadDescription;
|
||||
@@ -474,7 +475,10 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
* Limit the number of fetch joins to Many properties, mark as query joins as needed.
|
||||
*/
|
||||
private void markQueryJoins() {
|
||||
detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type.defaultSelect());
|
||||
if (distinctOn == null) {
|
||||
// no automatic join to query join conversion when distinctOn is used
|
||||
detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch(), type.defaultSelect());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAllowOneManyFetch() {
|
||||
@@ -588,12 +592,11 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
return countDistinctOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Id should be included in the query.
|
||||
*/
|
||||
@Override
|
||||
public final boolean isWithId() {
|
||||
return !manualId && !distinct && !singleAttribute;
|
||||
// distinctOn orm query will auto include the id property
|
||||
// distinctOn dto query does NOT (via setting manualId to true)
|
||||
return !manualId && !singleAttribute && (!distinct || distinctOn != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -689,6 +692,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
copy.baseTable = baseTable;
|
||||
copy.rootTableAlias = rootTableAlias;
|
||||
copy.distinct = distinct;
|
||||
copy.distinctOn = distinctOn;
|
||||
copy.allowLoadErrors = allowLoadErrors;
|
||||
copy.timeout = timeout;
|
||||
copy.mapKey = mapKey;
|
||||
@@ -748,6 +752,11 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String distinctOn() {
|
||||
return distinctOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String loadDescription() {
|
||||
return loadDescription;
|
||||
@@ -1018,6 +1027,9 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
}
|
||||
if (distinct) {
|
||||
sb.append("/dt");
|
||||
if (distinctOn != null) {
|
||||
sb.append("/o:").append(distinctOn);
|
||||
}
|
||||
}
|
||||
if (allowLoadErrors) {
|
||||
sb.append("/ae");
|
||||
@@ -1271,6 +1283,13 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
detail.addNested(name, nestedDetail, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Query<T> distinctOn(String distinctOn) {
|
||||
this.distinctOn = distinctOn;
|
||||
this.distinct = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Query<T> select(String columns) {
|
||||
detail.select(columns);
|
||||
|
||||
@@ -11,13 +11,67 @@ public final class OrmQueryLimitRequest implements SqlLimitRequest {
|
||||
private final String sql;
|
||||
private final String sqlOrderBy;
|
||||
private final boolean distinct;
|
||||
private final String distinctOn;
|
||||
private final String hint;
|
||||
private final String label;
|
||||
|
||||
public OrmQueryLimitRequest(String sql, String sqlOrderBy, SpiQuery<?> ormQuery, DatabasePlatform dbPlatform, boolean distinct) {
|
||||
this(sql, sqlOrderBy, ormQuery, dbPlatform, distinct, null, "", "");
|
||||
}
|
||||
|
||||
public OrmQueryLimitRequest(String sql, String sqlOrderBy, SpiQuery<?> ormQuery, DatabasePlatform dbPlatform,
|
||||
boolean distinct, String distinctOn, String hint, String label) {
|
||||
this.sql = sql;
|
||||
this.sqlOrderBy = sqlOrderBy;
|
||||
this.ormQuery = ormQuery;
|
||||
this.dbPlatform = dbPlatform;
|
||||
this.distinct = distinct;
|
||||
this.distinctOn = distinctOn;
|
||||
this.hint = hint;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
private StringBuilder newBuffer() {
|
||||
return new StringBuilder(50 + sql.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String ansiOffsetRows() {
|
||||
final var buffer = selectDistinct();
|
||||
buffer.append(sql);
|
||||
int firstRow = getFirstRow();
|
||||
if (firstRow > 0) {
|
||||
buffer.append(" offset ").append(firstRow).append(" rows");
|
||||
}
|
||||
int maxRows = getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
buffer.append(" fetch next ").append(maxRows).append(" rows only");
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder selectDistinct() {
|
||||
final var buffer = newBuffer();
|
||||
buffer.append("select ").append(hint).append(label);
|
||||
if (distinct) {
|
||||
buffer.append("distinct ");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder selectDistinctOnSql() {
|
||||
var buffer = newBuffer();
|
||||
buffer.append("select ").append(hint).append(label);
|
||||
if (distinct) {
|
||||
buffer.append("distinct ");
|
||||
if (distinctOn != null) {
|
||||
buffer.append("on (").append(distinctOn).append(") ");
|
||||
}
|
||||
}
|
||||
buffer.append(sql);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -190,6 +190,9 @@ final class ScalarTypeJsonSet {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set convertList(List list) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
return new LinkedHashSet(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Database;
|
||||
import io.ebean.Query;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SlowQueryBindCaptureTest {
|
||||
|
||||
static Database db;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
db = DB.getDefault();
|
||||
db.script().run("/h2-init.sql");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindParams_capture() {
|
||||
String sql = "select * from o_customer p where p.status = ?";
|
||||
Query<Customer> query = db.findNative(Customer.class, sql)
|
||||
.setParameter("N");
|
||||
query.findList();
|
||||
|
||||
var sbc = new SlowQueryBindCapture((SpiQuery<?>) query);
|
||||
List<Object> bindParams = sbc.capture();
|
||||
assertThat(bindParams).hasSize(1);
|
||||
assertThat(bindParams.get(0)).isEqualTo("N");
|
||||
}
|
||||
|
||||
@Test
|
||||
void expressionsCapture() {
|
||||
Query<Customer> query = db.find(Customer.class)
|
||||
.where()
|
||||
.startsWith("name", "rob")
|
||||
.eq("status", "N")
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
var sbc = new SlowQueryBindCapture((SpiQuery<?>) query);
|
||||
List<Object> bindParams = sbc.capture();
|
||||
assertThat(bindParams).hasSize(2);
|
||||
assertThat(bindParams.get(0)).isEqualTo("rob%");
|
||||
assertThat(bindParams.get(1)).isEqualTo("N");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void multiValueCapture() {
|
||||
Query<Customer> query = db.find(Customer.class)
|
||||
.where()
|
||||
.in("id", List.of(1, 2, 3, 4))
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
var sbc = new SlowQueryBindCapture((SpiQuery<?>) query);
|
||||
List<Object> bindParams = sbc.capture();
|
||||
assertThat(bindParams).hasSize(1);
|
||||
Object bindParam0 = bindParams.get(0);
|
||||
assertThat(bindParam0).isInstanceOf(ArrayList.class);
|
||||
var values = (ArrayList<Object>) bindParam0;
|
||||
assertThat(values).contains(1, 2, 3, 4);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.*;
|
||||
import io.ebeaninternal.api.BindValuesKey;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequestTestHelper;
|
||||
import io.ebeaninternal.server.expression.BaseExpressionTest;
|
||||
@@ -54,6 +55,32 @@ public class DefaultOrmQueryTest extends BaseExpressionTest {
|
||||
assertThat(q1.getId()).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test
|
||||
void when_distinctOn_then_planChanges() {
|
||||
DefaultOrmQuery<Order> q1 = (DefaultOrmQuery<Order>) DB.find(Order.class).distinctOn("name");
|
||||
DefaultOrmQuery<Order> q2 = (DefaultOrmQuery<Order>) DB.find(Order.class);
|
||||
|
||||
prepare(q1, q2);
|
||||
assertThat(q1.createQueryPlanKey()).isNotEqualTo(q2.createQueryPlanKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void when_distinctOn_match() {
|
||||
DefaultOrmQuery<Order> q1 = (DefaultOrmQuery<Order>) DB.find(Order.class).distinctOn("name");
|
||||
DefaultOrmQuery<Order> q2 = (DefaultOrmQuery<Order>) DB.find(Order.class).distinctOn("name");
|
||||
|
||||
prepare(q1, q2);
|
||||
assertThat(q1.createQueryPlanKey()).isEqualTo(q2.createQueryPlanKey());
|
||||
assertThat(bindKey(q1)).isEqualTo(bindKey(q2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void when_distinctOn_copy() {
|
||||
DefaultOrmQuery<Order> q1 = (DefaultOrmQuery<Order>) DB.find(Order.class).distinctOn("name");
|
||||
SpiQuery<Order> copy = q1.copy();
|
||||
assertThat(copy.distinctOn()).isEqualTo("name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_addWhere_then_planChanges() {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
create table animal (
|
||||
create table if not exists animal (
|
||||
species varchar(255) not null,
|
||||
id bigint generated by default as identity not null,
|
||||
shelter_id bigint,
|
||||
@@ -11,14 +11,14 @@ create table animal (
|
||||
constraint pk_animal primary key (id)
|
||||
);
|
||||
|
||||
create table animal_shelter (
|
||||
create table if not exists animal_shelter (
|
||||
id bigint generated by default as identity not null,
|
||||
name varchar(255),
|
||||
version bigint not null,
|
||||
constraint pk_animal_shelter primary key (id)
|
||||
);
|
||||
|
||||
create table o_customer (
|
||||
create table if not exists o_customer (
|
||||
id integer generated by default as identity not null,
|
||||
status varchar(1),
|
||||
name varchar(40) not null,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean ddl generation</name>
|
||||
@@ -28,14 +28,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-all</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean postgis types</name>
|
||||
@@ -19,14 +19,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-platform-postgres</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope -->
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -82,11 +82,11 @@
|
||||
<plugin>
|
||||
<groupId>io.repaint.maven</groupId>
|
||||
<artifactId>tiles-maven-plugin</artifactId>
|
||||
<version>2.34</version>
|
||||
<version>2.40</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<tiles>
|
||||
<tile>io.ebean.tile:enhancement:13.22.0</tile>
|
||||
<tile>io.ebean.tile:enhancement:14.3.0</tile>
|
||||
</tiles>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean querybean</name>
|
||||
@@ -17,7 +17,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -63,14 +63,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>14.0.2</version>
|
||||
<version>14.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -96,11 +96,11 @@
|
||||
<plugin>
|
||||
<groupId>io.repaint.maven</groupId>
|
||||
<artifactId>tiles-maven-plugin</artifactId>
|
||||
<version>2.34</version>
|
||||
<version>2.40</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<tiles>
|
||||
<tile>io.ebean.tile:enhancement:13.22.0</tile>
|
||||
<tile>io.ebean.tile:enhancement:14.3.0</tile>
|
||||
</tiles>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
package io.ebean.typequery;
|
||||
|
||||
import io.ebean.*;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Query bean for strongly typed query construction and execution.
|
||||
* <p>
|
||||
* For each entity bean querybean-generator generates a query bean that implements QueryBean.
|
||||
*
|
||||
* <h2>Example - usage of QCustomer</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Date fiveDaysAgo = ...
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* new QCustomer()
|
||||
* .name.ilike("rob")
|
||||
* .status.equalTo(Customer.Status.GOOD)
|
||||
* .registered.after(fiveDaysAgo)
|
||||
* .contacts.email.endsWith("@foo.com")
|
||||
* .orderBy()
|
||||
* .name.asc()
|
||||
* .registered.desc()
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <h2>Resulting SQL where</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where lower(t0.name) like ? and t0.status = ? and t0.registered > ? and u1.email like ?
|
||||
* order by t0.name, t0.registered desc;
|
||||
*
|
||||
* --bind(rob,GOOD,Mon Jul 27 12:05:37 NZST 2015,%@foo.com)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> the entity bean type (normal entity bean type e.g. Customer)
|
||||
* @param <R> the specific query bean type (e.g. QCustomer)
|
||||
*/
|
||||
public interface IQueryBean<T, R> extends QueryBuilder<R, T> {
|
||||
|
||||
/**
|
||||
* Return the underlying query.
|
||||
* <p>
|
||||
* Generally it is not expected that you will need to do this but typically use
|
||||
* the find methods available on this 'root query bean' instance like findList().
|
||||
*/
|
||||
Query<T> query();
|
||||
|
||||
/**
|
||||
* Return the fetch group.
|
||||
*/
|
||||
FetchGroup<T> buildFetchGroup();
|
||||
|
||||
/**
|
||||
* Set DISTINCT ON properties. This is a Postgres only SQL feature.
|
||||
*
|
||||
* @param properties The properties to include in the DISTINCT ON clause.
|
||||
*/
|
||||
R distinctOn(TQProperty<R, ?>... properties);
|
||||
|
||||
/**
|
||||
* Specify the properties to be loaded on the 'main' root level entity bean.
|
||||
* <p>
|
||||
* The resulting entities with be "partially loaded" aka partial objects.
|
||||
* <p>
|
||||
* Alternatively we can use a {@link #select(FetchGroup)} to specify all properties
|
||||
* to load on all parts of the graph.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // alias for the customer properties in select()
|
||||
* QCustomer cust = QCustomer.alias();
|
||||
*
|
||||
* // alias for the contact properties in contacts.fetch()
|
||||
* QContact contact = QContact.alias();
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* new QCustomer()
|
||||
* // specify the parts of the graph we want to load
|
||||
* .select(cust.id, cust.name)
|
||||
* .contacts.fetch(contact.firstName, contact.lastName, contact.email)
|
||||
*
|
||||
* // predicates
|
||||
* .id.gt(1)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param properties the list of properties to fetch
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
R select(TQProperty<R, ?>... properties);
|
||||
|
||||
/**
|
||||
* Specify the properties to be loaded on the 'main' root level entity bean
|
||||
* also allowing for functions to be used like {@link StdOperators#max(Query.Property)}.
|
||||
*
|
||||
* @param properties the list of properties to fetch
|
||||
*/
|
||||
R select(Query.Property<?>... properties);
|
||||
|
||||
/**
|
||||
* Add an expression to the WHERE or HAVING clause.
|
||||
*/
|
||||
R add(Expression expression);
|
||||
|
||||
/**
|
||||
* Add EXISTS sub-query predicate.
|
||||
*/
|
||||
R exists(Query<?> subQuery);
|
||||
|
||||
/**
|
||||
* Add NOT EXISTS sub-query predicate.
|
||||
*/
|
||||
R notExists(Query<?> subQuery);
|
||||
|
||||
/**
|
||||
* EXISTS using a SQL SubQuery.
|
||||
*
|
||||
* @param sqlSubQuery The SQL SubQuery
|
||||
* @param bindValues Optional bind values if the SubQuery uses {@code ? } bind values.
|
||||
*/
|
||||
R exists(String sqlSubQuery, Object... bindValues);
|
||||
|
||||
/**
|
||||
* Not EXISTS using a SQL SubQuery.
|
||||
*
|
||||
* @param sqlSubQuery The SQL SubQuery
|
||||
* @param bindValues Optional bind values if the SubQuery uses {@code ? } bind values.
|
||||
*/
|
||||
R notExists(String sqlSubQuery, Object... bindValues);
|
||||
|
||||
/**
|
||||
* Set the Id value to query. This is used with findOne().
|
||||
* <p>
|
||||
* You can use this to have further control over the query. For example adding fetch joins.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Order order =
|
||||
* new QOrder()
|
||||
* .setId(1)
|
||||
* .fetch("details")
|
||||
* .findOne();
|
||||
*
|
||||
* // the order details were eagerly fetched
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R setId(Object id);
|
||||
|
||||
/**
|
||||
* Set a list of Id values to match.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> orders =
|
||||
* new QOrder()
|
||||
* .setIdIn(42, 43, 44)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R setIdIn(Object... ids);
|
||||
|
||||
/**
|
||||
* Set a collection of Id values to match.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Collection<?> ids = ...
|
||||
*
|
||||
* List<Order> orders =
|
||||
* new QOrder()
|
||||
* .setIdIn(ids)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R setIdIn(Collection<?> ids);
|
||||
|
||||
/**
|
||||
* Add raw expression with no parameters.
|
||||
* <p>
|
||||
* When properties in the clause are fully qualified as table-column names
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* raw("orderQty < shipQty")
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Subquery example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R raw(String rawExpression);
|
||||
|
||||
/**
|
||||
* Add raw expression with an array of parameters.
|
||||
* <p>
|
||||
* The raw expression should contain the same number of ? as there are
|
||||
* parameters.
|
||||
* <p>
|
||||
* When properties in the clause are fully qualified as table-column names
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
*/
|
||||
R raw(String rawExpression, Object... bindValues);
|
||||
|
||||
/**
|
||||
* Only add the raw expression if the values is not null or empty.
|
||||
* <p>
|
||||
* This is a pure convenience expression to make it nicer to deal with the pattern where we use
|
||||
* raw() expression with a subquery and only want to add the subquery predicate when the collection
|
||||
* of values is not empty.
|
||||
*
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* QCustomer query = new QCustomer() // add some predicates
|
||||
* .status.equalTo(Status.NEW);
|
||||
*
|
||||
* // common pattern - we can use rawOrEmpty() instead
|
||||
* if (orderIds != null && !orderIds.isEmpty()) {
|
||||
* query.raw("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using rawOrEmpty()</h3>
|
||||
* Note that in the example below we use the <code>?1</code> bind parameter to get "parameter expansion"
|
||||
* for each element in the collection.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* new QCustomer()
|
||||
* .status.equalTo(Status.NEW)
|
||||
* // only add the expression if orderIds is not empty
|
||||
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Postgres ANY</h3>
|
||||
* With Postgres we would often use the SQL <code>ANY</code> expression and array parameter binding
|
||||
* rather than <code>IN</code>.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* new QCustomer()
|
||||
* .status.equalTo(Status.NEW)
|
||||
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id = any(?))", orderIds);
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Note that we need to cast the Postgres array for UUID types like:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* " ... = any(?::uuid[])"
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param raw The raw expression that is typically a subquery
|
||||
* @param values The values which is typically a list or set of id values.
|
||||
*/
|
||||
R rawOrEmpty(String raw, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Add raw expression with a single parameter.
|
||||
* <p>
|
||||
* The raw expression should contain a single ? at the location of the
|
||||
* parameter.
|
||||
* <p>
|
||||
* When properties in the clause are fully qualified as table-column names
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* <p>
|
||||
* <h4>Example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // use a database function
|
||||
* raw("add_days(orderDate, 10) < ?", someDate)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Subquery example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R raw(String rawExpression, Object bindValue);
|
||||
|
||||
/**
|
||||
* In expression using multiple columns.
|
||||
*/
|
||||
R inTuples(InTuples inTuples);
|
||||
|
||||
/**
|
||||
* Marker that can be used to indicate that the order by clause is defined after this.
|
||||
* <p>
|
||||
* <h2>Example: order by customer name, order date</h2>
|
||||
* <pre>{@code
|
||||
* List<Order> orders =
|
||||
* new QOrder()
|
||||
* .customer.name.ilike("rob")
|
||||
* .orderBy()
|
||||
* .customer.name.asc()
|
||||
* .orderDate.asc()
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R orderBy();
|
||||
|
||||
/**
|
||||
* @deprecated migrate to {@link #orderBy()}.
|
||||
*/
|
||||
@Deprecated(since = "13.19", forRemoval = true)
|
||||
R order();
|
||||
|
||||
/**
|
||||
* @deprecated migrate to {@link #orderBy(String)}
|
||||
*/
|
||||
@Deprecated(since = "13.19", forRemoval = true)
|
||||
R order(String orderByClause);
|
||||
|
||||
/**
|
||||
* Begin a list of expressions added by 'OR'.
|
||||
* <p>
|
||||
* Use endOr() or endJunction() to stop added to OR and 'pop' to the parent expression list.
|
||||
* <p>
|
||||
* <h2>Example</h2>
|
||||
* <p>
|
||||
* This example uses an 'OR' expression list with an inner 'AND' expression list.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* new QCustomer()
|
||||
* .status.equalTo(Customer.Status.GOOD)
|
||||
* .or()
|
||||
* .id.greaterThan(1000)
|
||||
* .and()
|
||||
* .name.startsWith("super")
|
||||
* .registered.after(fiveDaysAgo)
|
||||
* .endAnd()
|
||||
* .endOr()
|
||||
* .orderBy().id.desc()
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <h2>Resulting SQL where clause</h2>
|
||||
* <pre>{@code sql
|
||||
*
|
||||
* where t0.status = ? and (t0.id > ? or (t0.name like ? and t0.registered > ? ) )
|
||||
* order by t0.id desc;
|
||||
*
|
||||
* --bind(GOOD,1000,super%,Wed Jul 22 00:00:00 NZST 2015)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R or();
|
||||
|
||||
/**
|
||||
* Begin a list of expressions added by 'AND'.
|
||||
* <p>
|
||||
* Use endAnd() or endJunction() to stop added to AND and 'pop' to the parent expression list.
|
||||
* <p>
|
||||
* Note that typically the AND expression is only used inside an outer 'OR' expression.
|
||||
* This is because the top level expression list defaults to an 'AND' expression list.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <p>
|
||||
* This example uses an 'OR' expression list with an inner 'AND' expression list.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* new QCustomer()
|
||||
* .status.equalTo(Customer.Status.GOOD)
|
||||
* .or() // OUTER 'OR'
|
||||
* .id.greaterThan(1000)
|
||||
* .and() // NESTED 'AND' expression list
|
||||
* .name.startsWith("super")
|
||||
* .registered.after(fiveDaysAgo)
|
||||
* .endAnd()
|
||||
* .endOr()
|
||||
* .orderBy().id.desc()
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <h2>Resulting SQL where clause</h2>
|
||||
* <pre>{@code sql
|
||||
*
|
||||
* where t0.status = ? and (t0.id > ? or (t0.name like ? and t0.registered > ? ) )
|
||||
* order by t0.id desc;
|
||||
*
|
||||
* --bind(GOOD,1000,super%,Wed Jul 22 00:00:00 NZST 2015)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R and();
|
||||
|
||||
/**
|
||||
* Begin a list of expressions added by NOT.
|
||||
* <p>
|
||||
* Use endNot() or endJunction() to stop added to NOT and 'pop' to the parent expression list.
|
||||
*/
|
||||
R not();
|
||||
|
||||
/**
|
||||
* End a list of expressions added by 'OR'.
|
||||
*/
|
||||
R endJunction();
|
||||
|
||||
/**
|
||||
* End OR junction - synonym for endJunction().
|
||||
*/
|
||||
R endOr();
|
||||
|
||||
/**
|
||||
* End AND junction - synonym for endJunction().
|
||||
*/
|
||||
R endAnd();
|
||||
|
||||
/**
|
||||
* End NOT junction - synonym for endJunction().
|
||||
*/
|
||||
R endNot();
|
||||
|
||||
/**
|
||||
* Add expression after this to the WHERE expression list.
|
||||
* <p>
|
||||
* For queries against the normal database (not the doc store) this has no effect.
|
||||
* <p>
|
||||
* This is intended for use with Document Store / ElasticSearch where expressions can be put into either
|
||||
* the "query" section or the "filter" section of the query. Full text expressions like MATCH are in the
|
||||
* "query" section but many expression can be in either - expressions after the where() are put into the
|
||||
* "filter" section which means that they don't add to the relevance and are also cache-able.
|
||||
*/
|
||||
R where();
|
||||
|
||||
/**
|
||||
* Return the expression list that has been built for this query.
|
||||
*/
|
||||
ExpressionList<T> getExpressionList();
|
||||
|
||||
/**
|
||||
* Start adding expressions to the having clause when using @Aggregation properties.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* new QMachineUse()
|
||||
* // where ...
|
||||
* .date.inRange(fromDate, toDate)
|
||||
*
|
||||
* .having()
|
||||
* .sumHours.greaterThan(1)
|
||||
* .findList()
|
||||
*
|
||||
* // The sumHours property uses @Aggregation
|
||||
* // e.g. @Aggregation("sum(hours)")
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
R having();
|
||||
|
||||
/**
|
||||
* Return the underlying having clause to typically when using dynamic aggregation formula.
|
||||
* <p>
|
||||
* Note that after this we no longer have the query bean so typically we use this right
|
||||
* at the end of the query.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // sum(distanceKms) ... is a "dynamic formula"
|
||||
* // so we use havingClause() for it like:
|
||||
*
|
||||
* List<MachineUse> machineUse =
|
||||
*
|
||||
* new QMachineUse()
|
||||
* .select("machine, sum(fuelUsed), sum(distanceKms)")
|
||||
*
|
||||
* // where ...
|
||||
* .date.greaterThan(LocalDate.now().minusDays(7))
|
||||
*
|
||||
* .havingClause()
|
||||
* .gt("sum(distanceKms)", 2)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> havingClause();
|
||||
|
||||
/**
|
||||
* Set the profile location of this query. This is used to relate query execution metrics
|
||||
* back to a location like a specific line of code.
|
||||
*/
|
||||
R setProfileLocation(ProfileLocation profileLocation);
|
||||
}
|
||||
@@ -0,0 +1,810 @@
|
||||
package io.ebean.typequery;
|
||||
|
||||
import io.avaje.lang.NonNullApi;
|
||||
import io.avaje.lang.Nullable;
|
||||
import io.ebean.*;
|
||||
import io.ebean.service.SpiFetchGroupQuery;
|
||||
import io.ebeaninternal.api.SpiQueryFetch;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Base root query bean providing common features for all root query beans.
|
||||
* <p>
|
||||
* For each entity bean querybean-generator generates a query bean that extends TQRootBean.
|
||||
*
|
||||
* <h2>Example - usage of QCustomer</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Date fiveDaysAgo = ...
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* new QCustomer()
|
||||
* .name.ilike("rob")
|
||||
* .status.equalTo(Customer.Status.GOOD)
|
||||
* .registered.after(fiveDaysAgo)
|
||||
* .contacts.email.endsWith("@foo.com")
|
||||
* .orderBy()
|
||||
* .name.asc()
|
||||
* .registered.desc()
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <h2>Resulting SQL where</h2>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where lower(t0.name) like ? and t0.status = ? and t0.registered > ? and u1.email like ?
|
||||
* order by t0.name, t0.registered desc;
|
||||
*
|
||||
* --bind(rob,GOOD,Mon Jul 27 12:05:37 NZST 2015,%@foo.com)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> the entity bean type (normal entity bean type e.g. Customer)
|
||||
* @param <R> the specific root query bean type (e.g. QCustomer)
|
||||
*/
|
||||
@NonNullApi
|
||||
public abstract class QueryBean<T, R> implements IQueryBean<T, R> {
|
||||
|
||||
/**
|
||||
* The underlying query.
|
||||
*/
|
||||
private final Query<T> query;
|
||||
|
||||
/**
|
||||
* The root query bean instance. Used to provide fluid query construction.
|
||||
*/
|
||||
private final R root;
|
||||
|
||||
/**
|
||||
* The underlying expression lists held as a stack. Pushed and popped based on and/or (conjunction/disjunction).
|
||||
*/
|
||||
private ArrayStack<ExpressionList<T>> whereStack;
|
||||
|
||||
/**
|
||||
* Construct using the type of bean to query on and the default database.
|
||||
*/
|
||||
public QueryBean(Class<T> beanType) {
|
||||
this(beanType, DB.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct using the type of bean to query on and a given database.
|
||||
*/
|
||||
public QueryBean(Class<T> beanType, Database database) {
|
||||
this(database.find(beanType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with a transaction.
|
||||
*/
|
||||
protected QueryBean(Class<T> beanType, Transaction transaction) {
|
||||
this(beanType);
|
||||
query.usingTransaction(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with a database and transaction.
|
||||
*/
|
||||
protected QueryBean(Class<T> beanType, Database database, Transaction transaction) {
|
||||
this(beanType, database);
|
||||
query.usingTransaction(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct using a query.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public QueryBean(Query<T> query) {
|
||||
this.query = query;
|
||||
this.root = (R) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for using as an 'Alias' to use the properties as known string
|
||||
* values for select() and fetch().
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public QueryBean(boolean aliasDummy) {
|
||||
this.query = null;
|
||||
this.root = (R) this;
|
||||
}
|
||||
|
||||
/** Construct for FilterMany */
|
||||
protected QueryBean(ExpressionList<T> filter) {
|
||||
this.query = null;
|
||||
this.root = null;
|
||||
this.whereStack = new ArrayStack<>();
|
||||
whereStack.push(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* The enhancement will no longer use this method. Will be removed once the new IntelliJ plugin is released.
|
||||
*/
|
||||
protected void setRoot(R root) {
|
||||
// do nothing, remove this method shortly.
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroup<T> buildFetchGroup() {
|
||||
return ((SpiFetchGroupQuery<T>) query()).buildFetchGroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> query() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R distinctOn(String distinctOn) {
|
||||
query.distinctOn(distinctOn);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R select(String properties) {
|
||||
query.select(properties);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R select(FetchGroup<T> fetchGroup) {
|
||||
query.select(fetchGroup);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final R distinctOn(TQProperty<R, ?>... properties) {
|
||||
final var joiner = new StringJoiner(", ");
|
||||
for (Query.Property<?> property : properties) {
|
||||
joiner.add(property.toString());
|
||||
}
|
||||
distinctOn(joiner.toString());
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SafeVarargs
|
||||
public final R select(TQProperty<R, ?>... properties) {
|
||||
((SpiQueryFetch) query).selectProperties(properties(properties));
|
||||
return root;
|
||||
}
|
||||
|
||||
private Set<String> properties(Query.Property<?>[] properties) {
|
||||
Set<String> props = new LinkedHashSet<>();
|
||||
for (Query.Property<?> property : properties) {
|
||||
props.add(property.toString());
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R select(Query.Property<?>... properties) {
|
||||
((SpiQueryFetch) query).selectProperties(properties(properties));
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetch(String path) {
|
||||
query.fetch(path);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetchQuery(String path) {
|
||||
query.fetchQuery(path);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetchCache(String path) {
|
||||
query.fetchCache(path);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetchQuery(String path, String properties) {
|
||||
query.fetchQuery(path, properties);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetchCache(String path, String properties) {
|
||||
query.fetchCache(path, properties);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetch(String path, String properties) {
|
||||
query.fetch(path, properties);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetch(String path, String properties, FetchConfig fetchConfig) {
|
||||
query.fetch(path, properties, fetchConfig);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R fetch(String path, FetchConfig fetchConfig) {
|
||||
query.fetch(path, fetchConfig);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R fetchLazy(String path, String fetchProperties) {
|
||||
query.fetchLazy(path, fetchProperties);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R fetchLazy(String path) {
|
||||
query.fetchLazy(path);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R apply(FetchPath pathProperties) {
|
||||
query.apply(pathProperties);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R alsoIf(BooleanSupplier predicate, Consumer<R> apply) {
|
||||
if (predicate.getAsBoolean()) {
|
||||
apply.accept(root);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R asOf(Timestamp asOf) {
|
||||
query.asOf(asOf);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setIncludeSoftDeletes() {
|
||||
query.setIncludeSoftDeletes();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R add(Expression expression) {
|
||||
peekExprList().add(expression);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R alias(String alias) {
|
||||
query.alias(alias);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setMaxRows(int maxRows) {
|
||||
query.setMaxRows(maxRows);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setFirstRow(int firstRow) {
|
||||
query.setFirstRow(firstRow);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setAllowLoadErrors() {
|
||||
query.setAllowLoadErrors();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setAutoTune(boolean autoTune) {
|
||||
query.setAutoTune(autoTune);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setBufferFetchSizeHint(int fetchSize) {
|
||||
query.setBufferFetchSizeHint(fetchSize);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setDistinct(boolean distinct) {
|
||||
query.setDistinct(distinct);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setBaseTable(String baseTable) {
|
||||
query.setBaseTable(baseTable);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R withLock(Query.LockType lockType) {
|
||||
query.withLock(lockType);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R withLock(Query.LockType lockType, Query.LockWait lockWait) {
|
||||
query.withLock(lockType, lockWait);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R exists(Query<?> subQuery) {
|
||||
peekExprList().exists(subQuery);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R notExists(Query<?> subQuery) {
|
||||
peekExprList().notExists(subQuery);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R exists(String sqlSubQuery, Object... bindValues) {
|
||||
peekExprList().exists(sqlSubQuery, bindValues);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R notExists(String sqlSubQuery, Object... bindValues) {
|
||||
peekExprList().notExists(sqlSubQuery, bindValues);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R forUpdate() {
|
||||
query.forUpdate();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R forUpdateNoWait() {
|
||||
query.forUpdateNoWait();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R forUpdateSkipLocked() {
|
||||
query.forUpdateSkipLocked();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final UpdateQuery<T> asUpdate() {
|
||||
return query.asUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <D> DtoQuery<D> asDto(Class<D> dtoClass) {
|
||||
return query.asDto(dtoClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setId(Object id) {
|
||||
query.setId(id);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setIdIn(Object... ids) {
|
||||
query.where().idIn(ids);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setIdIn(Collection<?> ids) {
|
||||
query.where().idIn(ids);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setLabel(String label) {
|
||||
query.setLabel(label);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setHint(String hint) {
|
||||
query.setHint(hint);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setProfileLocation(ProfileLocation profileLocation) {
|
||||
query.setProfileLocation(profileLocation);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setLazyLoadBatchSize(int lazyLoadBatchSize) {
|
||||
query.setLazyLoadBatchSize(lazyLoadBatchSize);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setMapKey(String mapKey) {
|
||||
query.setMapKey(mapKey);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setPersistenceContextScope(PersistenceContextScope scope) {
|
||||
query.setPersistenceContextScope(scope);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R setCountDistinct(CountDistinctOrder orderBy) {
|
||||
query.setCountDistinct(orderBy);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setRawSql(RawSql rawSql) {
|
||||
query.setRawSql(rawSql);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setReadOnly(boolean readOnly) {
|
||||
query.setReadOnly(readOnly);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setBeanCacheMode(CacheMode beanCacheMode) {
|
||||
query.setBeanCacheMode(beanCacheMode);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setDisableLazyLoading(boolean disableLazyLoading) {
|
||||
query.setDisableLazyLoading(disableLazyLoading);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setUseQueryCache(CacheMode cacheMode) {
|
||||
query.setUseQueryCache(cacheMode);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R setTimeout(int secs) {
|
||||
query.setTimeout(secs);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<String> validate() {
|
||||
return query.validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R raw(String rawExpression) {
|
||||
peekExprList().raw(rawExpression);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R raw(String rawExpression, Object... bindValues) {
|
||||
peekExprList().raw(rawExpression, bindValues);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R rawOrEmpty(String raw, Collection<?> values) {
|
||||
peekExprList().rawOrEmpty(raw, values);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R raw(String rawExpression, Object bindValue) {
|
||||
peekExprList().raw(rawExpression, bindValue);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R inTuples(InTuples inTuples) {
|
||||
peekExprList().inTuples(inTuples);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R orderBy() {
|
||||
// Yes this does not actually do anything! We include it because style wise it makes
|
||||
// the query nicer to read and suggests that order by definitions are added after this
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated(since = "13.19", forRemoval = true)
|
||||
public final R order() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R orderBy(String orderByClause) {
|
||||
query.orderBy(orderByClause);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R setOrderBy(OrderBy<T> orderBy) {
|
||||
query.setOrderBy(orderBy);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R orderById(boolean orderById) {
|
||||
query.orderById(orderById);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated(since = "13.19", forRemoval = true)
|
||||
public final R order(String orderByClause) {
|
||||
return orderBy(orderByClause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R or() {
|
||||
pushExprList(peekExprList().or());
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R and() {
|
||||
pushExprList(peekExprList().and());
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R not() {
|
||||
pushExprList(peekExprList().not());
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final R endJunction() {
|
||||
whereStack.pop();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R endOr() {
|
||||
return endJunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R endAnd() {
|
||||
return endJunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R endNot() {
|
||||
return endJunction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the expression list onto the appropriate stack.
|
||||
*/
|
||||
private R pushExprList(ExpressionList<T> list) {
|
||||
whereStack.push(list);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R where() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R usingTransaction(Transaction transaction) {
|
||||
query.usingTransaction(transaction);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R usingConnection(Connection connection) {
|
||||
query.usingConnection(connection);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R usingDatabase(Database database) {
|
||||
query.usingDatabase(database);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R usingMaster() {
|
||||
query.usingMaster();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean exists() {
|
||||
return query.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final T findOne() {
|
||||
return query.findOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Optional<T> findOneOrEmpty() {
|
||||
return query.findOneOrEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<T> findList() {
|
||||
return query.findList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Stream<T> findStream() {
|
||||
return query.findStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<T> findSet() {
|
||||
return query.findSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <A> List<A> findIds() {
|
||||
return query.findIds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <K> Map<K, T> findMap() {
|
||||
return query.findMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final QueryIterator<T> findIterate() {
|
||||
return query.findIterate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <A> List<A> findSingleAttributeList() {
|
||||
return query.findSingleAttributeList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <A> Set<A> findSingleAttributeSet() {
|
||||
return query.findSingleAttributeSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final <A> A findSingleAttribute() {
|
||||
return query.findSingleAttribute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <A> Optional<A> findSingleAttributeOrEmpty() {
|
||||
return query.findSingleAttributeOrEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void findEach(Consumer<T> consumer) {
|
||||
query.findEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void findEach(int batch, Consumer<List<T>> consumer) {
|
||||
query.findEach(batch, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void findEachWhile(Predicate<T> consumer) {
|
||||
query.findEachWhile(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<Version<T>> findVersions() {
|
||||
return query.findVersions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end) {
|
||||
return query.findVersionsBetween(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int findCount() {
|
||||
return query.findCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FutureRowCount<T> findFutureCount() {
|
||||
return query.findFutureCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FutureIds<T> findFutureIds() {
|
||||
return query.findFutureIds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FutureList<T> findFutureList() {
|
||||
return query.findFutureList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PagedList<T> findPagedList() {
|
||||
return query.findPagedList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int delete() {
|
||||
return query.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getGeneratedSql() {
|
||||
return query.getGeneratedSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Class<T> getBeanType() {
|
||||
return query.getBeanType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExpressionList<T> getExpressionList() {
|
||||
return query.where();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final R having() {
|
||||
if (whereStack == null) {
|
||||
whereStack = new ArrayStack<>();
|
||||
}
|
||||
// effectively putting having expression list onto stack
|
||||
// such that expression now add to the having clause
|
||||
whereStack.push(query.having());
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ExpressionList<T> havingClause() {
|
||||
return query.having();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current expression list that expressions should be added to.
|
||||
*/
|
||||
protected final ExpressionList<T> peekExprList() {
|
||||
if (whereStack == null) {
|
||||
whereStack = new ArrayStack<>();
|
||||
whereStack.push(query.where());
|
||||
}
|
||||
// return the current expression list
|
||||
return whereStack.peek();
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
* Eagerly fetch this association fetching all the properties.
|
||||
*/
|
||||
public final R fetch() {
|
||||
((TQRootBean) _root).query().fetch(_name);
|
||||
((QueryBean) _root).query().fetch(_name);
|
||||
return _root;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
* Eagerly fetch this association using a "query join".
|
||||
*/
|
||||
public final R fetchQuery() {
|
||||
((TQRootBean) _root).query().fetchQuery(_name);
|
||||
((QueryBean) _root).query().fetchQuery(_name);
|
||||
return _root;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
* Cache misses are populated via fetchQuery().
|
||||
*/
|
||||
public final R fetchCache() {
|
||||
((TQRootBean) _root).query().fetchCache(_name);
|
||||
((QueryBean) _root).query().fetchCache(_name);
|
||||
return _root;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
* Use lazy loading for fetching this association.
|
||||
*/
|
||||
public final R fetchLazy() {
|
||||
((TQRootBean) _root).query().fetchLazy(_name);
|
||||
((QueryBean) _root).query().fetchLazy(_name);
|
||||
return _root;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
* Eagerly fetch this association with the properties specified.
|
||||
*/
|
||||
public final R fetch(String properties) {
|
||||
((TQRootBean) _root).query().fetch(_name, properties);
|
||||
((QueryBean) _root).query().fetch(_name, properties);
|
||||
return _root;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
* Eagerly fetch this association using a "query join" with the properties specified.
|
||||
*/
|
||||
public final R fetchQuery(String properties) {
|
||||
((TQRootBean) _root).query().fetchQuery(_name, properties);
|
||||
((QueryBean) _root).query().fetchQuery(_name, properties);
|
||||
return _root;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
* Cache misses are populated via fetchQuery().
|
||||
*/
|
||||
public final R fetchCache(String properties) {
|
||||
((TQRootBean) _root).query().fetchCache(_name, properties);
|
||||
((QueryBean) _root).query().fetchCache(_name, properties);
|
||||
return _root;
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
|
||||
}
|
||||
|
||||
private SpiQueryFetch spiQuery() {
|
||||
return (SpiQueryFetch) ((TQRootBean) _root).query();
|
||||
return (SpiQueryFetch) ((QueryBean) _root).query();
|
||||
}
|
||||
|
||||
private Set<String> properties(TQProperty<?, ?>... props) {
|
||||
|
||||
@@ -41,7 +41,7 @@ public class TQProperty<R, T> implements Query.Property<T> {
|
||||
* Internal method to return the underlying expression list.
|
||||
*/
|
||||
protected final ExpressionList<?> expr() {
|
||||
return ((TQRootBean<?, ?>) _root).peekExprList();
|
||||
return ((QueryBean<?, ?>) _root).peekExprList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ import io.ebean.*;
|
||||
import io.ebean.annotation.Transactional;
|
||||
import io.ebean.test.LoggedSql;
|
||||
import io.ebean.types.Inet;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import org.example.domain.Address;
|
||||
import org.example.domain.Country;
|
||||
import org.example.domain.Customer;
|
||||
@@ -226,6 +227,22 @@ public class QCustomerTest {
|
||||
.findList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distinctOn() {
|
||||
var c = QContact.alias();
|
||||
var q = new QContact()
|
||||
.distinctOn(c.customer)
|
||||
.select(c.lastName, c.whenCreated)
|
||||
.orderBy()
|
||||
.customer.id.asc()
|
||||
.whenCreated.desc()
|
||||
.query();
|
||||
|
||||
SpiQuery<?> spiQuery = (SpiQuery<?>) q;
|
||||
assertThat(spiQuery.distinctOn()).isEqualTo("customer");
|
||||
assertThat(spiQuery.isDistinct()).isTrue();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Test
|
||||
public void forUpdate() {
|
||||
@@ -314,7 +331,7 @@ public class QCustomerTest {
|
||||
.query();
|
||||
|
||||
q.findList();
|
||||
assertThat(q.getGeneratedSql()).isEqualTo("select t0.id, t0.name from be_customer t0 limit 10");
|
||||
assertThat(q.getGeneratedSql()).isEqualTo("select /* QCustomerTest.filterManySeparateQuery */ t0.id, t0.name from be_customer t0 limit 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user