Compare commits

..
12 Commits
Author SHA1 Message Date
Rob Bygrave 3f6d565800 Version 16.9.0 2026-06-08 19:09:46 +12:00
Rob Bygrave 1408696912 Redesign of metric labels - secondary queries (lazy|query) now just u… (#3779)
* Redesign of metric labels - secondary queries (lazy|query) now just use parent + relativePath + type

# Query metric/plan label change — comparison

Improves the metric/plan name generated for **secondary** (`_lazy` / `_query`) loads so
they relate to their parent/root query, and unifies separators on `.`.

The secondary lazy name is now **always** `orm.<parent's full name>.<path>.<loadMode>`,
so it always prefixes the real parent metric.

## Root query name

| Root query source | Original | New |
|---|---|---|
| `setLabel("custMain")` | `orm.Customer_custMain` | `orm.Customer.custMain` |
| ProfileLocation `CustomerFinder.byName` | `orm.CustomerFinder.byName` | `orm.CustomerFinder.byName` *(same)* |
| ProfileLocation `DataLoader.loadAll` (Customer query) | `orm.Customer_DataLoader.loadAll` | `orm.Customer.DataLoader.loadAll` |
| Unlabeled, no location | `orm.Customer.findList` | `orm.Customer.findList` *(same)* |

## Secondary lazy (`contacts`) name

Original prefixes the **loaded** type (`Contact`) + the call-site location and uses `__`
between path and load mode. New prefixes the **parent's full name**.

| Root query source | Original lazy name | New lazy name |
|---|---|---|
| `setLabel("custMain")` *(profile location also present)* | `orm.Contact_CustomerFinder.findActive_contacts__lazy` — explicit label **lost** | `orm.Customer.custMain.contacts.lazy` |
| `setLabel("custMain")` *(no profile location)* | `orm.Contact_custMain_contacts__lazy` | `orm.Customer.custMain.contacts.lazy` |
| ProfileLocation `CustomerFinder.byName` | `orm.Contact_CustomerFinder.byName_contacts__lazy` | `orm.CustomerFinder.byName.contacts.lazy` |
| ProfileLocation `DataLoader.loadAll` | `orm.Contact_DataLoader.loadAll_contacts__lazy` | `orm.Customer.DataLoader.loadAll.contacts.lazy` |
| Unlabeled, no location | `orm.Contact.findList` *(own name; no parent path)* | `orm.Contact.findList` *(same)* |

## Problems fixed
- **(A) Secondary load didn't relate to its parent** — original prefixed the *loaded* type
  (`Contact`) + the *call-site* location, never the parent query's name. New name literally
  starts with the parent's full name.
- Explicit `setLabel` was **silently dropped** for secondary queries when a profile location
  existed.
- `__` path/loadMode separator and mixed `_`/`.` replaced by uniform `.`.

## Nested and `.query` secondary loads

Each secondary query name is `<immediate parent's full name>.<immediate path>.<loadMode>`,
so every hop literally prefixes its parent metric. Example with root `setLabel("custMain")`
on `Customer`, chain `Customer -> orders -> details`:

Nested lazy:
```
orm.Customer.custMain
orm.Customer.custMain.orders.lazy
orm.Customer.custMain.orders.lazy.details.lazy
```

Secondary eager `.query` fetch:
```
orm.Customer.custMain
orm.Customer.custMain.orders.query
orm.Customer.custMain.orders.query.details.query
```

The intermediate load mode (`.lazy.` / `.query.`) is retained so each name is an exact
extension of its immediate parent's name.

* Use ProfileLocation as leading metric name without <type> prefix

* docs: Add guide for ebean-query-metrics.md

* docs: Add guide for query plan capture
2026-06-08 19:03:43 +12:00
Rob Bygrave e0531a6315 Merge branch 'master' of github.com:ebean-orm/ebean 2026-06-08 19:01:17 +12:00
Rob Bygraveandrobin.bygrave 0023f0ce13 [open telemetry] Add query lable as an attribute to the query spans (#3778)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-06-08 10:42:12 +12:00
Rob Bygrave 032f4de857 Version 16.8.0 2026-06-06 20:06:30 +12:00
Rob Bygrave 59431814ce Merge branch 'master' of github.com:ebean-orm/ebean 2026-06-06 19:57:18 +12:00
Rob Bygrave e7194055be Merge pull request #3776 from ebean-orm/feature/PString_eqIfNotBlank
Add PString eqIfNotBlank() helper query expression
2026-06-06 18:37:59 +12:00
robin.bygrave 806c7cd752 Fix test with overlapping cases 2026-06-06 18:16:11 +12:00
Rob Bygrave dfc7f92160 Merge pull request #3777 from ebean-orm/fature/otel-query-hash
[open telemetry] Add query hash as an attribute to the query spans
2026-06-06 18:04:18 +12:00
robin.bygrave 26351325a8 Fix test with overlapping cases 2026-06-06 18:03:39 +12:00
robin.bygrave 2d37f9d01e [open telemetry] Add query hash as an attribute to the query spans 2026-06-06 17:55:59 +12:00
robin.bygrave d3fd03ce5b Add PString eqIfNotBlank() helper query expression
Just to make this relatively common case pretty nice and clean
2026-06-06 17:45:45 +12:00
77 changed files with 1027 additions and 274 deletions
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-net-postgis-types</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-pgvector-types</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -47,19 +47,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -42,13 +42,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -17,13 +17,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -41,7 +41,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -60,13 +60,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>composites</artifactId>
+2
View File
@@ -14,6 +14,8 @@ Key guides (fetch and follow when performing the relevant task):
- Migrate to `Database.builder()`: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/migrating-to-database-builder.md
- Write queries with query beans: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/writing-ebean-query-beans.md
- Persisting and transactions: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/persisting-and-transactions-with-ebean.md
- Query metrics and naming: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/ebean-query-metrics.md
- Query plan capture: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/ebean-query-plan-capture.md
- Test container setup: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-postgres-test-container.md
- DB migration generation: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-db-migration-generation.md
- Lombok with entity beans: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/lombok-with-ebean-entity-beans.md
+4
View File
@@ -29,6 +29,8 @@ existing Maven project. Complete the steps in order.
| Guide | Description |
|-------|-------------|
| [Ebean OpenTelemetry tracing](add-ebean-opentelemetry.md) | Add `ebean-opentelemetry`, register `GlobalOpenTelemetry` once before Ebean databases are built, and troubleshoot missing spans or double-registration errors |
| [Ebean query metrics and naming](ebean-query-metrics.md) | How Ebean query metric names are derived from `setLabel(..)` and profile locations; secondary (lazy/query) load naming; inline SQL comments; collecting metrics at runtime; mapping to avaje-metrics tags |
| [Ebean query plan capture](ebean-query-plan-capture.md) | Enable and configure database query plan (`EXPLAIN`) capture for slow queries; bind capture vs plan capture; periodic and on-demand collection; thresholds, load limits, EXPLAIN dialect, and listeners |
## Entity beans
@@ -149,6 +151,8 @@ Key guides (fetch and follow these when performing the relevant task):
- Write queries with query beans: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/writing-ebean-query-beans.md
- Immutable bean cache for read-only references: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/immutable-bean-cache.md
- Ebean OpenTelemetry tracing: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-opentelemetry.md
- Query metrics and naming: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/ebean-query-metrics.md
- Query plan capture: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/ebean-query-plan-capture.md
- Persisting and transactions: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/persisting-and-transactions-with-ebean.md
- Test container setup: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-postgres-test-container.md
- DB migration generation: https://raw.githubusercontent.com/ebean-orm/ebean/HEAD/docs/guides/add-ebean-db-migration-generation.md
+258
View File
@@ -0,0 +1,258 @@
# Guide: Ebean query metrics and naming
## Purpose
This guide explains the metrics Ebean captures, how the metric **name** for a query
is derived, and how you influence that name with `setLabel(..)` and **profile
locations**. It also covers secondary (lazy / query) load naming, the inline SQL
comment, collecting metrics at runtime, and how the names map to avaje-metrics tags.
Use this guide when you want to identify a query in metrics/telemetry, when a query
shows up under an unexpected metric name, or when wiring Ebean metrics into a reporter.
---
## Overview
Ebean records timing and counter metrics for the work it does. Every metric has a
**name** whose leading segment identifies the kind of work:
| Prefix | What it measures | Example name |
|---|---|---|
| `orm.` | Entity (ORM) query | `orm.Customer.findList`, `orm.CustomerFinder.byName` |
| `dto.` | DTO query | `dto.CustomerDto_byEmail` |
| `sql.query.` | Raw SQL query | `sql.query.<label>` |
| `sql.update.` / `sql.call.` | Raw SQL update / stored procedure call | `sql.update.<label>` |
| `orm.update.` | ORM update statement | `orm.update.<label>` |
| `iud.` | Bean insert / update / delete | `iud.Customer.insert` |
| `txn.main` / `txn.readonly` / `txn.named.` | Transactions | `txn.main`, `txn.named.processOrders` |
| `l2n.` | L2 cache region | `l2n.customer.hit` |
The rest of this guide focuses on **`orm.` query names**, which is where labels and
profile locations apply.
---
## How an ORM query name is derived
An entity query name has the form `orm.<identifier>`. The `<identifier>` comes from one
of three sources, in priority order:
1. **An explicit `setLabel(..)`** — prefixed with the bean type for disambiguation.
2. **A profile location** — used as-is (it is already a unique `Class.method` identifier).
3. **Neither** — the bean type plus the query type (e.g. `findList`).
| Root query source | Resulting name |
|---|---|
| `setLabel("custMain")` on `Customer` | `orm.Customer.custMain` |
| Profile location `CustomerFinder.byName` | `orm.CustomerFinder.byName` |
| Unlabelled `DB.find(Customer.class).findList()` | `orm.Customer.findList` |
The asymmetry is intentional: an explicit label is a short, ambiguous token (`custMain`
could be used for any bean), so the bean type is prefixed. A profile location is already
unique and type-independent, so it is used as-is.
### Step 1 - Label a query explicitly
```java
List<Customer> customers = DB.find(Customer.class)
.setLabel("custMain")
.findList();
// metric name: orm.Customer.custMain
```
DTO queries support `setLabel(..)` too (the label should be unique to the DTO type):
```java
DB.findDto(CustomerDto.class, sql)
.setLabel("byEmail")
.findList();
// metric name: dto.CustomerDto_byEmail
```
### Step 2 - Use a profile location (preferred for finders / query beans)
A profile location identifies a query by its **call site** (`Class.method`) instead of a
hand-written label.
**The common case is automatic.** With Ebean's byte-code enhancement enabled (the normal
setup when using query beans / finders), Ebean assigns each query a profile location
derived from its call site — no code is required:
```java
List<Customer> customers = new QCustomer()
.status.eq(Status.ACTIVE)
.findList();
// metric name: orm.<CallingClass>.<method> (often with a line number, see below)
```
The enhancer derives the location from the calling code (the method that runs the query),
and for many call sites it includes the **source line number** (e.g.
`CustomerService.find:42`), so distinct call sites — even in the same method — get distinct
names automatically.
**Setting one explicitly.** You can also set a profile location yourself, which is useful
without enhancement or to control the identity:
```java
ProfileLocation LOC = ProfileLocation.create();
List<Customer> customers = DB.find(Customer.class)
.setProfileLocation(LOC)
.where().eq("status", Status.ACTIVE)
.findList();
// metric name: orm.<DeclaringClass>.<method>
```
Factory choices:
- `ProfileLocation.create()` — call site as `Class.method`, **no line number**.
- `ProfileLocation.createWithLine()` — includes the source line number
(e.g. `CustomerService.find:42`), so two queries in the **same method** get
**distinct** names.
- `ProfileLocation.create("label")` — a named location (used for named transactions).
> Note: a location with no line number (`create()`, or a call site the enhancer emits
> without a line) means two different queries in the same method share one name. The
> queried entity is still distinguishable downstream via the avaje-metrics `type` tag
> (see "Mapping to avaje-metrics tags" below). Use `createWithLine()` to separate
> same-method call sites in the name itself.
---
## Secondary (lazy / query) load naming
When a query lazy-loads or `fetchQuery()`-loads an association, Ebean issues a
**secondary** query. Its name **extends the parent query's full name** with the relative
path and the load mode (`lazy` or `query`), joined with `.`:
```
orm.<parent name without the "orm." prefix>.<path>.<loadMode>
```
So a secondary load is always an exact extension of its parent metric name, which makes
the relationship obvious in dashboards.
Example — root labelled `custMain` on `Customer`, chain `Customer -> orders -> details`:
Lazy loading:
```
orm.Customer.custMain
orm.Customer.custMain.orders.lazy
orm.Customer.custMain.orders.lazy.details.lazy
```
Secondary eager `fetchQuery()` loading:
```
orm.Customer.custMain
orm.Customer.custMain.orders.query
orm.Customer.custMain.orders.query.details.query
```
The same applies with a **profile-location** root (no explicit `setLabel`):
```
orm.CustomerFinder.byName
orm.CustomerFinder.byName.contacts.lazy
```
Unlike the root query, the secondary name is **not** bean-type prefixed by the loaded
type — it inherits the parent's name so it relates back to where the load originated.
---
## Inline SQL comment
When `includeLabelInSql` is enabled (the default), Ebean prepends the query's label (or
profile-location label) as an inline SQL comment, which is useful for matching slow
queries in database logs back to application code:
```sql
select /* CustomerFinder.byName */ t0.id, t0.name from be_customer t0 where ...
```
The comment uses the explicit `setLabel(..)` if present, otherwise the profile-location
label. Secondary queries use their full extended name
(e.g. `/* Customer.custMain.contacts.query */`). `EXISTS` / subquery forms are not
commented.
Disable it via the builder:
```java
Database.builder()
.includeLabelInSql(false)
.build();
```
---
## Collecting metrics at runtime
Read collected metrics through `Database.metaInfo()`:
```java
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.ServerMetrics;
ServerMetrics metrics = database.metaInfo().collectMetrics(); // resets counters
for (MetaQueryMetric q : metrics.queryMetrics()) {
System.out.printf("%s type=%s count=%d total=%d mean=%d%n",
q.name(), // e.g. orm.Customer.custMain
q.type().getSimpleName(), // the queried bean/DTO type, e.g. Customer
q.count(), q.total(), q.mean());
}
```
Key API:
- `database.metaInfo()``MetaInfoManager`.
- `collectMetrics()` collects and **resets**; `collectMetrics(false)` collects without
reset; `visitMetrics(visitor)` for streaming.
- `ServerMetrics` exposes `queryMetrics()`, `timedMetrics()`, `countMetrics()`.
- `MetaQueryMetric` exposes `name()`, `label()`, `type()` (the queried `Class<?>`),
`sql()`, `hash()`, plus timing `count()` / `total()` / `max()` / `mean()`.
---
## Mapping to avaje-metrics tags
When integrating with **avaje-metrics** (`avaje-metrics-ebean`
`DatabaseMetricSupplier`), the flat `orm.`/`dto.`/`sql.` names are translated to a tagged
form, with the bean type carried as a `type` tag:
```
ebean.query{kind=orm|dto|sql, type=<BeanSimpleName>, label=<rest of the name>}
```
Because the entity is available as the `type` tag, two different-entity queries that
share a profile-location name remain distinct series on tag-aware backends (OpenTelemetry,
Prometheus, StatsD) without needing the bean type in the name.
For the integration setup, see the avaje-metrics guide
[`add-ebean-metrics.md`](https://github.com/avaje/avaje-metrics/blob/master/docs/guides/add-ebean-metrics.md).
To capture the database execution plan (`EXPLAIN`) for slow queries identified by these
metrics, see [Ebean query plan capture](ebean-query-plan-capture.md).
---
## Troubleshooting
### A query shows up as `orm.<Bean>.findList` (no useful identity)
It has neither a label nor a profile location. Add `setLabel(..)` or a
`ProfileLocation`, or apply a profile location on the finder / query bean.
### Two queries in one method share a metric name
This happens when the profile location for those call sites has no line number. With
enhancement, many call sites already include a line number; for those that don't, use
`ProfileLocation.createWithLine()` to separate them by line, or give each an explicit
`setLabel(..)`. On tag-aware backends the avaje-metrics `type` tag already separates
different entity types.
### A secondary (lazy / query) load isn't grouped under its parent
Secondary names extend the parent's full name. If the parent has no label or profile
location, its name falls back to `orm.<Bean>.<queryType>` and the secondary extends
that. Give the root query a label or profile location for a stable parent name.
+206
View File
@@ -0,0 +1,206 @@
# Guide: Ebean query plan capture
## Purpose
This guide explains how to enable and configure **query plan capture** in Ebean — the
mechanism that captures the database's actual execution plan (via `EXPLAIN`) for slow
queries, so you can diagnose missing indexes and poor plans in production.
Use this guide when you want Ebean to record real query plans, when tuning the capture
thresholds and load limits, or when wiring a listener to ship captured plans somewhere.
---
## Overview
Query plan capture is a **two-phase** mechanism:
1. **Bind capture** — when enabled, Ebean watches query executions and, for queries
slower than a threshold, captures the actual **bind values** that were used. This is
cheap: it just remembers the parameters of a slow execution.
2. **Plan capture** — using those captured bind values, Ebean runs `EXPLAIN <sql>`
against the database to obtain the execution plan, producing `MetaQueryPlan` results
that are handed to a `QueryPlanListener`.
Plan capture is split this way so the expensive `EXPLAIN` work (actual database load)
happens periodically or on demand, against representative bind values, rather than on
every slow query.
Two ways to trigger phase 2:
- **Automatic periodic capture** — a background timer collects plans on a schedule.
- **On demand** — call the `MetaInfoManager` API to arm and collect plans yourself
(this is what remote tooling such as ebean-insight uses).
Only `orm.` SELECT-style queries are plan capable — raw SQL and update/DML metrics are not.
---
## Step 1 - Enable bind capture
Bind capture is the master switch; nothing is captured until it is on.
```java
Database database = Database.builder()
.queryPlanEnable(true) // turn on bind capture
.queryPlanThresholdMicros(100_000) // capture binds for queries slower than 100ms
.build();
```
- `queryPlanEnable(boolean)` — enable bind capture. Default **false**.
- `queryPlanThresholdMicros(long)` — global execution-time threshold (microseconds) a
query must exceed before its bind values are captured. Default **`Long.MAX_VALUE`**
(effectively off), so you must either lower it or arm specific plans by hash (Step 3).
Equivalent `application.properties` (avaje-config / properties):
```properties
queryPlan.enable=true
queryPlan.thresholdMicros=100000
```
---
## Step 2 - Enable automatic periodic capture (optional)
To have Ebean periodically run `EXPLAIN` for armed queries and report the plans:
```java
Database database = Database.builder()
.queryPlanEnable(true)
.queryPlanThresholdMicros(100_000)
.queryPlanCapture(true) // turn on the periodic capture timer
.queryPlanCapturePeriodSecs(600) // every 10 minutes (default)
.queryPlanCaptureMaxTimeMillis(10_000) // stop after 10s of capturing per cycle
.queryPlanCaptureMaxCount(10) // at most 10 plans per cycle
.queryPlanListener(capture -> {
for (var plan : capture.plans()) {
System.out.println(plan.label() + "\n" + plan.plan());
}
})
.build();
```
- `queryPlanCapture(boolean)` — enable the background periodic capture. Default **false**.
- `queryPlanCapturePeriodSecs(long)` — capture frequency in seconds. Default **600** (10 min).
- `queryPlanCaptureMaxTimeMillis(long)` — per-cycle time budget; capture stops once
exceeded, bounding the database load. Default **10000** (10s).
- `queryPlanCaptureMaxCount(int)` — max plans captured per cycle. Default **10**.
- `queryPlanListener(QueryPlanListener)` — receives each `QueryPlanCapture`. If not set,
the default listener logs plans to the `io.ebean.QUERYPLAN` logger at `INFO`.
Properties form:
```properties
queryPlan.enable=true
queryPlan.thresholdMicros=100000
queryPlan.capture=true
queryPlan.capturePeriodSecs=600
queryPlan.captureMaxTimeMillis=10000
queryPlan.captureMaxCount=10
```
---
## Step 3 - Capture on demand (foreground)
Instead of (or in addition to) the periodic timer, drive capture through
`database.metaInfo()`. This is useful for targeted capture and is how remote tooling
arms specific slow queries by their plan hash.
```java
import io.ebean.meta.MetaInfoManager;
import io.ebean.meta.MetaQueryPlan;
import io.ebean.meta.QueryPlanInit;
import io.ebean.meta.QueryPlanRequest;
MetaInfoManager meta = database.metaInfo();
// Phase 1: arm bind capture - either all plans or specific hashes
QueryPlanInit init = new QueryPlanInit();
init.setAll(true); // or init.add("<planHash>", 50_000);
init.thresholdMicros(100_000);
List<MetaQueryPlan> armed = meta.queryPlanInit(init);
// ... let the application run so slow executions capture their bind values ...
// Phase 2: collect plans now (runs EXPLAIN)
QueryPlanRequest request = new QueryPlanRequest();
request.maxCount(10);
request.maxTimeMillis(10_000);
request.since(System.currentTimeMillis() - 300_000); // binds at least ~5 min old
List<MetaQueryPlan> plans = meta.queryPlanCollectNow(request);
```
- `QueryPlanInit` arms bind capture. `setAll(true)` arms every plan; `add(hash, micros)`
arms a specific plan (a hash of `"all"` is treated as all).
- `QueryPlanRequest.since(epochMillis)` ensures the captured bind values have existed for
a while, so they better represent the slowest executions. `maxCount` / `maxTimeMillis`
bound the work, mirroring the periodic settings.
`MetaQueryPlan` exposes `beanType()`, `label()`, `profileLocation()`, `sql()`, `hash()`,
`bind()`, `plan()` (the raw EXPLAIN output), `queryTimeMicros()`, `captureCount()`,
`captureMicros()`, and `whenCaptured()`.
---
## Step 4 - EXPLAIN dialect
Ebean chooses the `EXPLAIN` statement per database platform:
| Platform | EXPLAIN used |
|---|---|
| PostgreSQL | `explain (analyze, costs, verbose, buffers) <sql>` |
| YugabyteDB | `explain (analyze, buffers, dist) <sql>` |
| Oracle | `EXPLAIN PLAN FOR <sql>` |
| SQL Server | platform-specific logger |
| H2 / MySQL / other | `explain <sql>` |
Override the prefix with `queryPlanExplain(..)` (or `queryPlan.explain`):
```java
Database.builder()
.queryPlanExplain("explain (costs, verbose)") // omit ANALYZE on Postgres
.build();
```
> **Caution (PostgreSQL / Yugabyte):** the default includes `ANALYZE`, which **actually
> executes** the query to produce real timings. For non-idempotent or expensive queries,
> override with a non-ANALYZE `explain` to avoid side effects and extra load.
---
## Related setting: internal plan TTL
`queryPlanTTLSeconds(int)` (default **300**) is a **different** concept — it is the time to
live for Ebean's *internal* query plan (the object that knows how to execute a query, read
the result set and collect metrics). It is not part of EXPLAIN capture, but is set through
the same builder.
---
## Troubleshooting
### No plans are captured
1. `queryPlanEnable(true)` must be set — it is the master switch.
2. `queryPlanThresholdMicros` defaults to `Long.MAX_VALUE`. Lower it, or arm specific
plans via `QueryPlanInit`, otherwise no execution is ever "slow enough".
3. For periodic capture, also set `queryPlanCapture(true)`.
4. Queries must actually run slower than the threshold to have their binds captured.
### Plans appear but nothing is reported anywhere
No `queryPlanListener` is configured, so plans go to the default `io.ebean.QUERYPLAN`
logger. Set a listener, or enable `INFO` logging for `io.ebean.QUERYPLAN`.
### Capture adds noticeable database load
`EXPLAIN ANALYZE` executes the query. Reduce `queryPlanCaptureMaxCount`, increase
`queryPlanCapturePeriodSecs`, tighten `queryPlanCaptureMaxTimeMillis`, or override
`queryPlanExplain` to a non-ANALYZE form.
### An update/DML metric never offers plan capture
Only `orm.` SELECT-style queries are plan capable. `orm.update.*`, `iud.*`, `sql.update.*`
and similar write metrics are intentionally excluded.
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean api</name>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>ebean-bench</artifactId>
+28 -28
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean bom</name>
@@ -89,25 +89,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -125,13 +125,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -155,37 +155,37 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-spring-txn</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<!-- platforms -->
@@ -193,91 +193,91 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-clickhouse</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-db2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-hana</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mariadb</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-mysql</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-nuodb</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-oracle</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis-types</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-pgvector</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-pgvector-types</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlite</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-sqlserver</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>io.ebean</groupId>
<artifactId>ebean-parent</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>ebean-core-json</artifactId>
<name>ebean-core-json</name>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
+7 -7
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -22,13 +22,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-json</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -52,7 +52,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -165,21 +165,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
@@ -345,9 +345,18 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
void fetchProperties(String path, OrmQueryProperties other);
/**
* Set the on a secondary query given the label, relativePath and profile location of the parent query.
* Set the label on a secondary query by extending the parent query's full plan
* name with the relative path and load mode (joined with '.').
* <p>
* The {@code parentName} is the parent query's full plan name (without the
* leading "orm."), so for a root query labelled "custMain" on Customer the
* secondary lazy load of contacts becomes {@code Customer.custMain.contacts.lazy}.
* The profile location of the parent query is also propagated.
*
* @param parentName the parent query's full plan name (no "orm." prefix)
* @param relativePath the path to the loaded property plus the load mode, e.g. {@code contacts.lazy}
*/
void setProfilePath(String label, String relativePath, @Nullable ProfileLocation profileLocation);
void setProfilePath(String parentName, String relativePath, @Nullable ProfileLocation profileLocation);
/**
* Set the query mode.
@@ -21,6 +21,8 @@ abstract class DLoadBaseContext {
protected final String fullPath;
protected final String path;
protected final String serverName;
final OrmQueryProperties queryProps;
@@ -38,6 +40,7 @@ abstract class DLoadBaseContext {
this.serverName = parent.server().name();
this.desc = desc;
this.queryProps = queryProps;
this.path = path;
this.fullPath = parent.fullPath(path);
this.hitCache = parent.isBeanCacheGet() && desc.isBeanCaching();
this.objectGraphNode = parent.objectGraphNode(path);
@@ -50,15 +53,21 @@ abstract class DLoadBaseContext {
* set onto the secondary query.
*/
void setLabel(SpiQuery<?> query) {
String label = parent.planLabel();
if (label != null) {
query.setProfilePath(label, pathMode(query), parent.profileLocation());
String prefix = parent.namePrefix();
if (prefix != null) {
query.setProfilePath(prefix, pathMode(query), parent.profileLocation());
}
}
/**
* Extend the parent query's full name with this load's immediate path segment
* and load mode. The parent name already carries everything up to this point,
* so only the immediate path (not the root relative fullPath) is appended to
* avoid duplicating intermediate path segments on nested secondary loads.
*/
private String pathMode(SpiQuery<?> query) {
final var loadMode = query.loadMode();
return fullPath == null ? '_' + loadMode : fullPath + "__" + loadMode;
return path == null ? loadMode : path + '.' + loadMode;
}
PersistenceContext persistenceContext() {
@@ -11,6 +11,7 @@ import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.*;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.query.CQueryPlan;
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.sql.Timestamp;
@@ -53,7 +54,7 @@ public final class DLoadContext implements LoadContext {
*/
private final String relativePath;
private final ObjectGraphOrigin origin;
private final String planLabel;
private final String namePrefix;
private final ProfileLocation profileLocation;
private final ProfilingListener profilingListener;
private final Map<String, ObjectGraphNode> nodePathMap = new HashMap<>();
@@ -81,7 +82,7 @@ public final class DLoadContext implements LoadContext {
this.disableReadAudit = false;
this.includeSoftDeletes = false;
this.relativePath = null;
this.planLabel = null;
this.namePrefix = null;
this.profileLocation = null;
this.profilingListener = null;
this.immutableCaches = Collections.emptyMap();
@@ -111,7 +112,7 @@ public final class DLoadContext implements LoadContext {
this.disableLazyLoading = query.isDisableLazyLoading();
this.useBeanCache = query.beanCacheMode();
this.profilingListener = query.profilingListener();
this.planLabel = query.planLabel();
this.namePrefix = deriveNamePrefix(query);
this.profileLocation = query.profileLocation();
this.immutableCaches = query.immutableBeanCaches();
this.secondaryProperties = query.isUnmodifiable() ? new HashSet<>() : null;
@@ -130,10 +131,41 @@ public final class DLoadContext implements LoadContext {
}
/**
* Return the query plan label of the origin query.
* Build the name prefix that secondary (lazy/query) loads extend with their
* path and load mode. This is the parent query's full plan name (without the
* leading "orm.").
* <p>
* For a root query with an explicit {@code setLabel(..)} that is the bean type
* combined with the label (for disambiguation). For a profile location the
* label is already a unique, type-independent identifier so it is used as-is.
* For a parent that is itself a secondary query the plan label already carries
* the full name, so it is used as-is (avoiding a duplicate prefix on nested
* lazy loads).
*/
String planLabel() {
return planLabel;
@Nullable
private String deriveNamePrefix(SpiQuery<?> query) {
final String label = query.planLabel();
if (label == null) {
return null;
}
if (query.loadMode() != null) {
// parent is itself a secondary - label already carries the full name
return label;
}
if (query.label() != null) {
// explicit setLabel - prefix the bean type for disambiguation
return CQueryPlan.planLabelWithType(label, rootDescriptor.simpleName());
}
// profile location - already a unique, type-independent identifier
return label;
}
/**
* Return the parent name prefix that secondary queries extend, or null.
*/
@Nullable
String namePrefix() {
return namePrefix;
}
/**
@@ -569,7 +569,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
public void profile() {
transaction()
.profileStream()
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), loadedBeanCount, query.profileId(), query.getGeneratedSql());
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), loadedBeanCount, query.profileId(), queryPlan.hash(), query.getGeneratedSql());
}
QueryIterator<T> readIterate(int bufferSize, OrmQueryRequest<T> request) {
@@ -165,7 +165,7 @@ final class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent, Ca
public void profile() {
transaction()
.profileStream()
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), rowCount, query.profileId(), query.getGeneratedSql());
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), rowCount, query.profileId(), queryPlan.hash(), query.getGeneratedSql());
}
Set<String> dependentTables() {
@@ -88,7 +88,7 @@ public class CQueryPlan implements SpiQueryPlan {
this.profileLocation = query.profileLocation();
this.location = (profileLocation == null) ? null : profileLocation.location();
this.label = query.planLabel();
this.name = deriveName(label, query.type(), request.descriptor().simpleName());
this.name = deriveName(label, query, request.descriptor().simpleName());
this.asOfTableCount = query.getAsOfTableCount();
this.sql = sqlRes.getSql();
this.sqlTree = sqlTree;
@@ -112,7 +112,7 @@ public class CQueryPlan implements SpiQueryPlan {
this.profileLocation = query.profileLocation();
this.location = (profileLocation == null) ? null : profileLocation.location();
this.label = query.planLabel();
this.name = deriveName(label, query.type(), request.descriptor().simpleName());
this.name = deriveName(label, query, request.descriptor().simpleName());
this.planKey = buildPlanKey(sql, logWhereSql);
this.asOfTableCount = 0;
this.sql = sql;
@@ -126,19 +126,58 @@ public class CQueryPlan implements SpiQueryPlan {
this.hash = Md5.hash(sql, name, location);
}
private String deriveName(String label, SpiQuery.Type type, String simpleName) {
private String deriveName(String label, SpiQuery<?> query, String simpleName) {
return deriveName(label, query.loadMode() != null, query.label() != null, query.type().label(), simpleName);
}
/**
* Derive the query plan / metric name.
*
* @param label the query plan label (null for an unlabelled query)
* @param secondary true if this is a secondary query (lazy or query load) where the label
* already carries the parent profile path, so the bean type is not prefixed
* @param explicitLabel true if the label came from an explicit {@code setLabel(..)} (as opposed
* to a profile location). An explicit label is prefixed with the bean type for
* disambiguation; a profile location is already a unique, type-independent
* identifier and is used as-is.
* @param typeLabel the query type label (used only when there is no plan label)
* @param simpleName the bean descriptor simple name
*/
static String deriveName(String label, boolean secondary, boolean explicitLabel, String typeLabel, String simpleName) {
if (label == null) {
return Str.add("orm.", simpleName, ".", type.label());
return Str.add("orm.", simpleName, ".", typeLabel);
}
if (secondary) {
// secondary (lazy/query) load - the label already carries the full parent
// name plus the path and load mode, so use it verbatim
return Str.add("orm.", label);
}
if (explicitLabel) {
return Str.add("orm.", planLabelWithType(label, simpleName));
}
// profile location - already a unique, type-independent identifier
return Str.add("orm.", label);
}
/**
* Combine the bean type simple name with an explicit (non-null) {@code setLabel}.
* <p>
* Used for a root query name and the parent prefix that secondary (lazy/query)
* loads extend, when the name comes from an explicit label. The bean type is
* prefixed for disambiguation, except when the label already starts with the
* bean type (avoiding duplication). Profile locations are not routed through
* here - they are unique identifiers and used as-is.
*/
public static String planLabelWithType(String label, String simpleName) {
int pos = simpleName.indexOf('.');
if (pos > 1) {
// element collection and label
return Str.add("orm.", simpleName.substring(0, pos), "_", label);
return Str.add(simpleName.substring(0, pos), "_", label);
}
if (label.startsWith(simpleName)) {
return Str.add("orm.", label);
return label;
}
return Str.add("orm.", simpleName, "_", label);
return Str.add(simpleName, ".", label);
}
private SpiQueryBindCapture initBindCapture(SpiQuery<?> query) {
@@ -137,7 +137,7 @@ final class CQueryRowCount implements SpiProfileTransactionEvent, CancelableQuer
public void profile() {
transaction()
.profileStream()
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), rowCount, query.profileId(), query.getGeneratedSql());
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), rowCount, query.profileId(), queryPlan.hash(), query.getGeneratedSql());
}
Set<String> dependentTables() {
@@ -115,7 +115,7 @@ final class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery
public void profile() {
transaction()
.profileStream()
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), rowCount, query.profileId(), query.getGeneratedSql());
.addQueryEvent(query.profileEventId(), profileOffset, desc.name(), rowCount, query.profileId(), queryPlan.hash(), query.getGeneratedSql());
}
@Override
@@ -255,9 +255,11 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
}
@Override
public final void setProfilePath(String label, String relativePath, @Nullable ProfileLocation profileLocation) {
public final void setProfilePath(String parentName, String relativePath, @Nullable ProfileLocation profileLocation) {
this.profileLocation = profileLocation;
this.label = (profileLocation == null ? label : profileLocation.label()) + '_' + relativePath;
// 'parentName' is the parent query's full plan name (bean type + label, or
// the full name of a parent secondary query) so extend it directly.
this.label = parentName + '.' + relativePath;
}
@Override
@@ -13,7 +13,7 @@ public interface ProfileStream {
/**
* Add a query event.
*/
void addQueryEvent(String event, long offset, String beanName, int beanCount, String queryId, String sql);
void addQueryEvent(String event, long offset, String beanName, int beanCount, String queryId, String hash, String sql);
/**
* Add a persist event.
@@ -0,0 +1,71 @@
package io.ebeaninternal.server.query;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for the query plan / metric name derivation.
*/
class CQueryPlanNameTest {
@Test
void deriveName_root_unlabelled() {
assertThat(CQueryPlan.deriveName(null, false, false, "findList", "Customer"))
.isEqualTo("orm.Customer.findList");
}
@Test
void deriveName_root_explicitLabel() {
assertThat(CQueryPlan.deriveName("custMain", false, true, "findList", "Customer"))
.isEqualTo("orm.Customer.custMain");
}
@Test
void deriveName_root_profileLocation_startsWithBeanType() {
// profile location used as-is (no bean type prefix)
assertThat(CQueryPlan.deriveName("CustomerFinder.byName", false, false, "findList", "Customer"))
.isEqualTo("orm.CustomerFinder.byName");
}
@Test
void deriveName_root_profileLocation_differentClass() {
// profile location is a unique, type-independent identifier - not prefixed with the bean type
assertThat(CQueryPlan.deriveName("DataLoader.loadAll", false, false, "findList", "Customer"))
.isEqualTo("orm.DataLoader.loadAll");
}
@Test
void deriveName_secondary_usesLabelVerbatim() {
// secondary query label already carries the full parent name + path + load mode
assertThat(CQueryPlan.deriveName("Customer.custMain.contacts.lazy", true, true, "findList", "Contact"))
.isEqualTo("orm.Customer.custMain.contacts.lazy");
assertThat(CQueryPlan.deriveName("CustomerFinder.byName.contacts.lazy", true, true, "findList", "Contact"))
.isEqualTo("orm.CustomerFinder.byName.contacts.lazy");
}
@Test
void planLabelWithType_explicitLabel() {
assertThat(CQueryPlan.planLabelWithType("custMain", "Customer"))
.isEqualTo("Customer.custMain");
}
@Test
void planLabelWithType_startsWithBeanType() {
assertThat(CQueryPlan.planLabelWithType("CustomerFinder.byName", "Customer"))
.isEqualTo("CustomerFinder.byName");
}
@Test
void planLabelWithType_differentClass() {
assertThat(CQueryPlan.planLabelWithType("DataLoader.loadAll", "Customer"))
.isEqualTo("Customer.DataLoader.loadAll");
}
@Test
void planLabelWithType_elementCollection() {
// element collection descriptor simple name contains a dot - keeps underscore form
assertThat(CQueryPlan.planLabelWithType("custMain", "Customer.phones"))
.isEqualTo("Customer_custMain");
}
}
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean ddl generation</name>
@@ -28,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -65,7 +65,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
+2 -2
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -15,7 +15,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean net postgis types</name>
@@ -19,14 +19,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -54,7 +54,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>ebean-opentelemetry</artifactId>
@@ -28,7 +28,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -71,21 +71,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -26,6 +26,8 @@ final class OtelProfileStream implements ProfileStream {
static final AttributeKey<String> DB_QUERY_TEXT = AttributeKey.stringKey("db.query.text");
static final AttributeKey<Long> DB_QUERY_TIME = AttributeKey.longKey("db.query.time");
static final AttributeKey<String> EBEAN_BEAN_TYPE = AttributeKey.stringKey("ebean.bean_type");
static final AttributeKey<String> EBEAN_QUERY_LABEL = AttributeKey.stringKey("ebean.query_label");
static final AttributeKey<String> EBEAN_QUERY_HASH = AttributeKey.stringKey("ebean.query_hash");
static final AttributeKey<Long> EBEAN_ROW_COUNT = AttributeKey.longKey("ebean.row_count");
static final AttributeKey<Long> EBEAN_TOTAL_MICROS = AttributeKey.longKey("ebean.total_micros");
@@ -45,18 +47,24 @@ final class OtelProfileStream implements ProfileStream {
}
@Override
public void addQueryEvent(String event, long offset, String beanName, int beanCount, String queryId, String sql) {
public void addQueryEvent(String event, long offset, String beanName, int beanCount, String queryId, String hash, String sql) {
long exeMicros = offset() - offset;
var now = Instant.now();
String operation = operationName(event);
String name = queryId != null ? queryId : operation + " " + beanName;
Span child = childSpanBuilder(name, now.minus(exeMicros, ChronoUnit.MICROS))
SpanBuilder builder = childSpanBuilder(name, now.minus(exeMicros, ChronoUnit.MICROS))
.setAttribute(DB_OPERATION, operation)
.setAttribute(EBEAN_BEAN_TYPE, beanName)
.setAttribute(EBEAN_ROW_COUNT, (long) beanCount)
.setAttribute(DB_QUERY_TEXT, sql)
.setAttribute(DB_QUERY_TIME, exeMicros)
.startSpan();
.setAttribute(DB_QUERY_TIME, exeMicros);
if (hash != null) {
builder.setAttribute(EBEAN_QUERY_HASH, hash);
}
if (queryId != null) {
builder.setAttribute(EBEAN_QUERY_LABEL, queryId);
}
Span child = builder.startSpan();
child.end(now);
}
@@ -119,7 +119,7 @@ class OtelProfileHandlerTest {
OtelProfileStream stream = (OtelProfileStream) handler.createProfileStream(null, null);
assertNotNull(stream);
// First query event should update the transaction span name
stream.addQueryEvent("fm", stream.offset(), "Customer", 5, "qplan-1", "select ...");
stream.addQueryEvent("fm", stream.offset(), "Customer", 5, "qplan-1", "h1", "select ...");
stream.addEvent("c", 0);
stream.end(null);
} finally {
@@ -141,7 +141,7 @@ class OtelProfileHandlerTest {
assertNotNull(stream);
long offset = stream.offset();
// Simulate some time passing then record a find_many
stream.addQueryEvent("fm", offset, "Order", 42, "plan-abc", "select ...");
stream.addQueryEvent("fm", offset, "Order", 42, "plan-abc", "abc123hash", "select ...");
stream.addEvent("c", 0);
stream.end(null);
} finally {
@@ -155,6 +155,8 @@ class OtelProfileHandlerTest {
assertEquals("Order", querySpan.getAttributes().get(OtelProfileStream.EBEAN_BEAN_TYPE));
assertEquals(42L, querySpan.getAttributes().get(OtelProfileStream.EBEAN_ROW_COUNT));
assertEquals("select ...", querySpan.getAttributes().get(OtelProfileStream.DB_QUERY_TEXT));
assertEquals("abc123hash", querySpan.getAttributes().get(OtelProfileStream.EBEAN_QUERY_HASH));
assertEquals("plan-abc", querySpan.getAttributes().get(OtelProfileStream.EBEAN_QUERY_LABEL));
}
@Test
@@ -163,8 +165,8 @@ class OtelProfileHandlerTest {
try (Scope ignored = parent.makeCurrent()) {
OtelProfileStream stream = (OtelProfileStream) handler.createProfileStream(mockLocation("MyService.doAll"), null);
assertNotNull(stream);
stream.addQueryEvent("fo", stream.offset(), "User", 1, "p1", "select from user");
stream.addQueryEvent("fm", stream.offset(), "Order", 10, "p2", "select from order");
stream.addQueryEvent("fo", stream.offset(), "User", 1, "p1", "h1", "select from user");
stream.addQueryEvent("fm", stream.offset(), "Order", 10, "p2", "h2", "select from order");
stream.addEvent("c", 0);
stream.end(null);
} finally {
@@ -184,15 +186,18 @@ class OtelProfileHandlerTest {
try (Scope ignored = parent.makeCurrent()) {
OtelProfileStream stream = (OtelProfileStream) handler.createProfileStream(mockLocation("MyService.emptySql"), null);
assertNotNull(stream);
stream.addQueryEvent("fo", stream.offset(), "User", 1, "p1", "");
stream.addQueryEvent("fo", stream.offset(), "User", 1, null, null, "");
stream.addEvent("c", 0);
stream.end(null);
} finally {
parent.end();
}
SpanData querySpan = findSpan("p1");
SpanData querySpan = findSpan("find_one User");
assertNotNull(querySpan);
assertEquals("", querySpan.getAttributes().get(OtelProfileStream.DB_QUERY_TEXT));
// null hash / label should not set the attributes
assertNull(querySpan.getAttributes().get(OtelProfileStream.EBEAN_QUERY_HASH));
assertNull(querySpan.getAttributes().get(OtelProfileStream.EBEAN_QUERY_LABEL));
}
// ----------------------------------------------------------
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean pgvector types</name>
@@ -19,14 +19,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -54,7 +54,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean postgis types</name>
@@ -19,14 +19,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<!-- provided scope -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -62,7 +62,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean querybean</name>
@@ -17,7 +17,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -59,14 +59,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
@@ -80,7 +80,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -26,6 +26,35 @@ public final class PString<R> extends PBaseComparable<R, String> {
super(name, root, prefix);
}
/**
* Is equal to if the value is non-null and not blank, otherwise no expression is added to the query.
* <p>
* This is like {@code eqIfPresent()} but additionally treats an empty or whitespace-only value as
* "not present". When present, the value is trimmed before being used in the equal to expression.
* <p>
* This is a helper for building queries in fluid style where a String predicate is effectively
* optional, avoiding a separate {@code if} block and {@code trimToNull()} style helper.
*
* <pre>{@code
* List<Customer> customers = new QCustomer()
* .name.eqIfNotBlank(nameFilter)
* .status.eqIfPresent(statusFilter)
* .findList();
* }</pre>
*
* @param value the value which can be null or blank
* @return the root query bean instance
*/
public R eqIfNotBlank(@Nullable String value) {
if (value != null) {
String trimmed = value.trim();
if (!trimmed.isEmpty()) {
expr().eq(_name, trimmed);
}
}
return _root;
}
/**
* Case insensitive is equal to.
*
@@ -190,7 +190,7 @@ class QOrderTest {
final List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("select /* QOrderTest.fetchQueryWithBatch */ t0.id, t0.name, t0.phone_number from be_customer t0");
assertThat(sql.get(1)).contains("select /* QOrderTest.fetchQueryWithBatch_contacts__query */ t0.customer_id, t0.id, t0.first_name, t0.last_name, t0.email from be_contact t0 where");
assertThat(sql.get(1)).contains("select /* QOrderTest.fetchQueryWithBatch.contacts.query */ t0.customer_id, t0.id, t0.first_name, t0.last_name, t0.email from be_contact t0 where");
}
@Test
@@ -288,7 +288,7 @@ class QOrderTest {
// assert fetching customer via fetchQuery
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("select /* QOrderTest.viaFetchGraph_withNested_fetchQuery */ t0.id, t0.status, t0.customer_id from o_order t0 where t0.id = ?");
assertThat(sql.get(1)).contains("select /* QOrderTest.viaFetchGraph_withNested_fetchQuery_customer__query */ t0.id, t0.name, t0.phone_number from be_customer t0 where t0.id = ?");
assertThat(sql.get(1)).contains("select /* QOrderTest.viaFetchGraph_withNested_fetchQuery.customer.query */ t0.id, t0.name, t0.phone_number from be_customer t0 where t0.id = ?");
assertThat(found.getCustomer().getPhoneNumber().getMsisdn()).isEqualTo("Ph1");
}
@@ -0,0 +1,79 @@
package org.querytest;
import io.ebean.DB;
import io.ebean.Database;
import org.example.domain.Customer;
import org.example.domain.query.QCustomer;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.example.domain.query.QCustomer.Alias.name;
class QStringEqIfNotBlankTest {
@Test
void present_addsEqPredicate() {
var q = new QCustomer()
.select(name)
.name.eqIfNotBlank("rob")
.query();
q.findList();
assertThat(q.getGeneratedSql()).contains("where t0.name = ?");
}
@Test
void null_skipsPredicate() {
var q = new QCustomer()
.select(name)
.name.isNotNull()
.name.eqIfNotBlank(null)
.query();
q.findList();
assertThat(q.getGeneratedSql()).doesNotContain("t0.name = ?");
}
@Test
void blankWhitespace_skipsPredicate() {
var q = new QCustomer()
.select(name)
.name.isNotNull()
.name.eqIfNotBlank(" ")
.query();
q.findList();
assertThat(q.getGeneratedSql()).doesNotContain("t0.name = ?");
}
@Test
void empty_skipsPredicate() {
var q = new QCustomer()
.select(name)
.name.isNotNull()
.name.eqIfNotBlank("")
.query();
q.findList();
assertThat(q.getGeneratedSql()).doesNotContain("t0.name = ?");
}
@Test
void present_trimsBoundValue() {
Database db = DB.getDefault();
String unique = "eqIfNotBlank-" + System.nanoTime();
Customer customer = new Customer();
customer.name = unique;
db.save(customer);
Customer found = new QCustomer(db)
.name.eqIfNotBlank(" " + unique + " ")
.findOne();
assertThat(found).isNotNull();
assertThat(found.name).isEqualTo(unique);
db.delete(customer);
}
}
@@ -24,7 +24,7 @@ class QueryAlsoIfTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).isEqualTo("select /* QueryAlsoIfTest.also */ t0.id, t0.name from be_customer t0 where t0.name is not null and t0.email is not null");
assertThat(q.getGeneratedSql()).contains("from be_customer t0 where t0.name is not null and t0.email is not null");
}
@Test
@@ -61,7 +61,7 @@ class QueryAlsoIfTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).isEqualTo("select /* QueryAlsoIfTest.applyIfPresent */ t0.id, t0.name from be_customer t0 where t0.name is not null and t0.status = ?");
assertThat(q.getGeneratedSql()).contains("from be_customer t0 where t0.name is not null and t0.status = ?");
}
@Test
@@ -74,7 +74,7 @@ class QueryAlsoIfTest {
.query();
q.findList();
assertThat(q.getGeneratedSql()).isEqualTo("select /* QueryAlsoIfTest.notApplyIfPresent */ t0.id, t0.name from be_customer t0 where t0.name is not null");
assertThat(q.getGeneratedSql()).contains("from be_customer t0 where t0.name is not null");
}
@Test
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>ebean-redis</artifactId>
@@ -29,35 +29,35 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>ebean-spring-txn</artifactId>
@@ -28,7 +28,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
@@ -77,7 +77,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>ebean test</name>
@@ -33,20 +33,20 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -149,14 +149,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-all</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
+2 -2
View File
@@ -15,8 +15,8 @@ mvn -T 4 clean package
mvn -T 4 deploy -pl '!composites,!platforms' -Pcentral -DskipTests
## git commit, git tag, git push --tags
git commit -am 'Version 16.7.0'
git tag 16.7.0
git commit -am 'Version 16.9.0'
git tag 16.9.0
git push --tags
## convert to javax
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>kotlin querybean generator</name>
@@ -21,7 +21,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
@@ -35,7 +35,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
@@ -56,14 +56,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
+14 -14
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,67 +16,67 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-clickhouse</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-db2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hana</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-hsqldb</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mariadb</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-nuodb</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-oracle</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-postgres</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlanywhere</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlite</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-sqlserver</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<!-- Provided scope so that the H2HistoryTrigger can live in Ebean core
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,13 +16,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-platform-mysql</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>platforms</artifactId>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
<relativePath>../..</relativePath>
</parent>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean-parent</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<packaging>pom</packaging>
<name>ebean parent</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<name>querybean generator</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>tests</artifactId>
+4 -4
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>tests</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>test-java16</artifactId>
@@ -18,7 +18,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-h2</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -30,14 +30,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>provided</scope>
</dependency>
+3 -3
View File
@@ -6,7 +6,7 @@
<parent>
<artifactId>tests</artifactId>
<groupId>io.ebean</groupId>
<version>16.7.0</version>
<version>16.9.0</version>
</parent>
<artifactId>test-kotlin</artifactId>
@@ -40,7 +40,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
</dependency>
<dependency>
@@ -53,7 +53,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>16.7.0</version>
<version>16.9.0</version>
<scope>test</scope>
</dependency>