* #2540 Add DTO mapping from Entity beans via generated mapper code
A large feature, refer to the docs / guides etc. This is a bit like MapStruct in that it generates mapping code via annotation processing but it ALSO creates an optimised FetchGroup for the destination DTO graph and it also implements a generic interface such that it can be fluently used via query.mapTo().
The value is that it automates the mapping code from entity -> dto plus optimises the query used to support building the DTO graph.
Expected to be better to use than MapStruct for ebean orm users.
* Bump ebean-annotation to 8.6 with the dto annotations
* Adjust the build order to via test dependency on ebean-api
---------
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
@Formula2 is a logical-path alternative to @Formula and a full replacement for it.
Where @Formula requires physical SQL (${ta} placeholders and hand-written joins),
@Formula2 takes a property-path expression and resolves the required joins
automatically:
@Formula2("coalesce(familyName, parent.familyName)")
String derivedFamilyName;
The path prefixes (parent, parent.parent, ...) define the joins needed to
satisfy the formula. Supported everywhere @Formula is:
- select() — root and nested-fetch paths, auto-joined
- where() — predicate paths trigger the extra joins
- orderBy() — order-by paths trigger the extra joins
- having()
- DDL — excluded from generated columns (read-only, like @Formula)
Default behaviour matches @Formula: included in the default select unless marked
@Transient, in which case it is opt-in but still auto-joins when selected.
Implementation:
- Parse @Formula2 after associations are wired, into a logical select fragment
plus the set of join paths (BeanDescriptor/BeanProperty).
- DeployPropertyParser registers formula2 join paths into query includes so the
existing predicate-include -> extra-join machinery builds the LEFT JOINs.
- ElPropertyChain prefixes all ${}/${path} placeholders for nested-path use.
- SqlTreeBuilder accumulates formula2 joins for select paths.
* 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
* Deprecate DatabaseConfig and DatabaseFactory, prefer Database.builder()
Deprecate the DatabaseConfig way of creating Database instance.
Migrate to use Database.builder().
* Deprecate DatabaseConfig and DatabaseFactory, migrate test code
* Use @Deprecated(forRemoval = true) on DatabaseConfig and DatabaseFactory
* Tidy up Database javadoc for deprecation