Commit Graph
58 Commits
Author SHA1 Message Date
robin.bygrave 3c4e87f3f7 Docs: Update docs on OneToOne mapping with mappedBy 2026-08-06 21:53:14 +12:00
Rob Bygraveandrobin.bygrave 49f6bef2fb Add ebean-avajejsonb-mapper module - Mapping DbJson content using avaje jsonb library (no reflection, graalvm support) (#3871)
* Add ebean-avajejsonb-mapper module - Mapping DbJson content using avaje jsonb library (no reflection, graalvm support)

* Fix flakey test TestInheritance

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-24 10:01:03 +12:00
robin.bygrave 56f86e0263 dto mapping: add DtoConverters 2026-07-17 10:01:54 +12:00
robin.bygrave 19b9e82afd dto mapping: add some negative tests 2026-07-16 22:02:44 +12:00
robin.bygrave bd0b274973 dto mapping: various fixes
Fix #2 — @DtoRef never checked hasField(): Added requires() to @DtoRef
 (same explicit-empty semantics as @DtoPath). DtoMappingReader now
 detects a computed association getter and requires an explicit
 requires(); DtoMapperWriter's REF case routes computed segments
 through extraFetchPaths instead of a broken select(assoc).

Fix #3 — requires() values never validated: Added DtoMappingReader.validateRequiresPath(...), walking every requires() segment against the real property graph (with List unwrapping via a new getterReturnTypeMirror helper). A typo now fails at compile time instead of resurfacing as a runtime PersistenceException. New negative

Fix #4 — bare requires() fetch vs narrowed sibling @DtoPath fetch collision: Traced into Ebean's OrmQueryDetail.fetch(...) and confirmed same-path fetch calls replace, not merge (Map.put). The old dedup logic had priority backwards, silently letting a narrow selection win and drop a computed getter's real dependency. Fixed by prioritizing the full fetch. Proved the regression test was meaningful by reverting the fix and confirming it fails with LazyInitialisationException: Property not loaded: line1, then restored the fix and confirmed it passes.
2026-07-16 21:36:17 +12:00
robin.bygrave 7b7a86201d dto mapping: support calculated getter methods with and without requires fetch properties 2026-07-16 20:38:03 +12:00
robin.bygrave 3431eee81a dto mapping: Add @DtoPath( failOnNull=true) option for null -> primitive handling 2026-07-16 18:26:40 +12:00
Rob Bygraveandrobin.bygrave 6c939d72f8 dto extensions - usingMaster, findStream etc (#3854)
* DtoQuery - add usingMaster, usingTransaction, usingConnection options.

* DtoQuery - Support DtoPath renaming a ToOne or ToMany

* MappedQuery - Add findStream() support

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-16 17:04:22 +12:00
Rob Bygraveandrobin.bygrave 30314f163d DtoQuery - add usingMaster, usingTransaction, usingConnection options. (#3853)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-16 15:50:07 +12:00
Rob Bygraveandrobin.bygrave 636bb28df3 #2540 Add DTO mapping from Entity beans via generated mapper code (#3852)
* #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>
2026-07-16 08:40:41 +12:00
robin.bygrave 007409d21c Bump postgresql to 42.7.11 2026-07-07 09:21:14 +12:00
Rob Bygraveandrobin.bygrave fc78b2af80 Add RawSqlBuilder.withPlaceholders() for CTEs and other complex SQL (#3649) (#3827)
* Add RawSqlBuilder.withPlaceholders() for CTEs and other complex SQL (#3649)

RawSqlBuilder.parse() uses keyword-based scanning to locate SELECT
columns and the WHERE/HAVING injection points. This fails for CTEs,
window functions and other complex SQL where "select"/"from" keywords
appear in places the scanner doesn't expect.

withPlaceholders(sql) skips SELECT/FROM column parsing entirely and
only locates the ${where}, ${andWhere}, ${having} and ${andHaving}
placeholder positions, requiring explicit columnMapping() calls (as
with unparsed()). This lets dynamic where()/having() expressions be
injected into otherwise unparseable SQL.

Also fixes two bugs in the underlying placeholder-position splitting:
- static SQL following a ${having}/${andHaving} placeholder (e.g. a
  trailing ORDER BY) was silently dropped when both a where and a
  having placeholder were present
- using only ${having}/${andHaving} (no where placeholder) caused a
  dynamically added HAVING clause to be appended after trailing
  static SQL, producing invalid SQL

Changes:
- RawSqlBuilder.withPlaceholders(sql) + SpiRawSqlService.withPlaceholders()
- DRawSqlParser.parseAsTemplate() / parseTemplate() - placeholder-position
  only parsing, correctly splitting preWhere/preHaving/trailing SQL
- CQueryBuilderRawSql - skip column-list and "select" prefix handling
  in template mode (signalled by an empty preFrom)
- Unit tests in DRawSqlServiceTest covering where/andWhere/having/andHaving
  placeholder combinations, including the two fixed edge cases
- Integration tests in TestRawSqlWithPlaceholders (ebean-test) covering
  CTE queries with dynamic where/having and verifying generated SQL

* Add examples test using query beans

* Add docs guides for RawSql

* Add ${orderBy} ${andOrderBy}

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-03 18:22:42 +12:00
robin.bygrave f50d56faee Add support for @Formula2 with logical paths and automatic joins
@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.
2026-06-23 19:02:37 +12:00
robin.bygrave 56d33b1f1b Update documentation for the DbJson mapping support 2026-06-22 10:06:34 +12:00
robin.bygrave e9b00cbbd1 Add documentation for the DbJson mapping support 2026-06-19 22:10:54 +12:00
robin.bygrave 313fdda857 Refactor from Jackson Core to avaje-json-core 2026-06-18 22:00:33 +12:00
robin.bygrave 7966d8bc1b docs: Improve around findList().stream() vs findStream() use 2026-06-13 10:23:17 +12:00
robin.bygrave a1ee75ffec docs: update docs / guides for query bean optional predicates 2026-06-10 16:59:24 +12:00
robin.bygrave da3dd8b215 docs: findStream() preferred over findList().stream() 2026-06-10 16:31:36 +12:00
robin.bygrave 6d30e6ff82 docs: Improve docs / guides / writing query beans, section on Optional predicates 2026-06-10 16:29:17 +12:00
robin.bygrave ec49824430 SqlQuery - update docs wrt plan capture 2026-06-09 00:28:13 +12:00
robin.bygrave abacda2c8f Update docs 2026-06-08 23:34:19 +12:00
robin.bygrave 8c37b53bad DtoQuery modified to support query plan capture 2026-06-08 23:06:09 +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
robin.bygrave 7035ff20eb Docs: Add a doc guide for adding open telemetry 2026-05-29 17:11:04 +12:00
robin.bygrave 0652167101 docs: add next steps to test container guide
- Suggest TestEntityBuilder for test data creation
- Link to testing-with-testentitybuilder.md
2026-05-13 13:57:40 +12:00
robin.bygrave 6633293151 docs: update guide versions and fix defaults
- ebean.version 17.2.0 -> 17.5.0 (minimum for TestEntityBuilder)
- avaje-inject 11.5 -> 12.5
- Add io.avaje:junit:1.8 bundle to test dependencies
- Remove .skipDataSourceCheck(true) from database config example
- Add note about Configuration DI wiring in Step 3
2026-05-13 13:57:39 +12:00
Rob Bygrave 9e603f2848 Deprecate DatabaseConfig and DatabaseFactory, prefer Database.builder() (#3769)
* 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
2026-05-08 00:06:43 +12:00
Rob Bygrave 2d199737aa Docs: Add docs/notes on potential future L2 immutable bean cache 2026-05-07 21:36:45 +12:00
Rob Bygrave 4dde7064ba Add ImmutableBeanCache - some parts of the query result graph populated via ImmutableBeanCache (#3753)
* Change [implicit] Lazy loading to be by TYPE rather than by PATH

We have some graph models where a common type (in the tests it is Label)
is used in many different *paths* of the graph. When we are loading via
*path* then all the loading of the Labels isn't in a single batch / load context
but instead split into different load contexts PER PATH.

This change, means that lazy loading operates by TYPE instead of by PATH.

In the tests, all the Labels are read via a single lazy loading query rather
that one lazy loading query per PATH, and this is more efficient.

* ImmutableBeanCache part 1

* ImmutableBeanCache part 2

* ImmutableBeanCache part 3 - add ImmutableBeanCaches

* ImmutableBeanCache - add propagation of caches to secondary queries

* ImmutableBeanCache - use and improve BeanDescriptor.merge() to positional

Using positional avoids the lookup by property name.

* ImmutableBeanCache - add unit testing with BeanDescriptorMergeTest

* ImmutableBeanCache - add testing for the further lazy loading on mutable beans

Also add doc / guides

* Add ImmutableCacheBuilder with underlying DefaultServerCache implementation

This provides a cache with maxSize, maxIdleSeconds, maxSecondsToLive

* Improve javadoc and doc / guides for ImmutableBeanCache

* Add cache invalidation for ImmutableBeanCache

* Use immutable cache direct hits for unmodifiable assoc-one refs

 - add ImmutableBeanCache.getIfPresent() as a non-loading probe
 - use immutable cache hits in AssocOneHelp / AssocOneHelpRefInherit
 - avoid creating ref beans and merge/copy for unmodifiable cache hits
 - keep existing mutable-query and miss/backfill behavior unchanged
2026-05-06 22:35:30 +12:00
Rob Bygrave 9954ad1ea7 Docs: Update add ebean postgres guide with gate on DI question 2026-05-04 23:50:19 +12:00
robin.bygrave 2928247ba7 Docs: Add the mkdir 2026-05-01 16:45:43 +12:00
robin.bygrave 3066bb4f74 Docs: Setup add a Step 4b - add di dependencies bit 2026-05-01 16:07:58 +12:00
robin.bygrave a96144cdaa Docs: Adjust setup guide with initial DI / no DI question 2026-05-01 16:03:11 +12:00
robin.bygrave 50e20c3aa0 Docs: update docs / skills location to separate git repo 2026-05-01 15:38:37 +12:00
robin.bygrave f07ca68eb3 Docs: Add docs/skill/ebean-orm 2026-05-01 15:09:54 +12:00
robin.bygrave 0ec7ddb82d Docs: Change the ordering so that test container setup proceeds the main database configuration 2026-05-01 14:59:36 +12:00
robin.bygrave 8074a24d03 Docs: Improve docs with DB -> database and wording around getters/setters/accessors 2026-05-01 14:52:43 +12:00
robin.bygrave f44bf59cb6 Docs: Improve testing-with-testentitybuilder.md - better wiring example 2026-04-24 17:52:44 +12:00
robin.bygrave afadaa7208 Docs: Add a template AGENTS.md to assist agent onboarding 2026-04-24 09:32:37 +12:00
Rob Bygraveandrobin.bygrave 46b7510e6c TestEntityBuilder - add saveAll() helper method (#3751)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-04-22 15:10:04 +12:00
robin.bygrave 6eda7044a0 Docs: Improve testing-with-testentitybuilder.md 2026-04-21 13:01:18 +12:00
robin.bygrave e4adf25022 Docs: Improve testing-with-testentitybuilder.md 2026-04-20 15:49:44 +12:00
robin.bygrave ad5483100e Improve README.md with reference to llms.txt 2026-04-20 14:55:01 +12:00
robin.bygrave 6d9ed670ef Docs: Improve doc guides wording 2026-04-16 16:48:27 +12:00
robin.bygrave 9e2d821475 Docs: Add LIBRARY.md 2026-04-16 13:53:22 +12:00
Rob Bygrave 80ce7a41a2 Docs: guides - improve writing query beans guide 2026-04-14 00:46:37 +12:00
Rob Bygrave 6458bb0f1c Docs: Add docs / guides for writing queries and using transactions 2026-04-14 00:34:21 +12:00
Rob Bygrave 7ba550cc0d Docs: Add docs / guides for ebean entity bean recommendations 2026-04-12 23:49:41 +12:00
Rob Bygrave fff8dfb8b1 Docs: Add docs / guides for ebean entity bean recommendations 2026-04-12 23:43:54 +12:00