Commit Graph
935 Commits
Author SHA1 Message Date
Rob Bygrave e7da3346a9 Merge branch 'master' into ebean-15x 2026-09-21 19:53:06 +12:00
Rob Bygrave 2b92e417df Version 18.6.0 2026-09-21 19:52:06 +12:00
Rob Bygrave 29e7ee1c68 Merge branch 'master' into ebean-15x 2026-08-17 21:58:27 +12:00
Rob Bygrave fdcbb91981 Version 18.5.0 2026-08-17 21:57:31 +12:00
robin.bygrave 581074e679 Merge branch 'master' into ebean-15x 2026-08-17 13:15:03 +12:00
robin.bygrave 80f5cab086 Version 18.5.0-RC1 2026-08-17 13:13:49 +12:00
Rob Bygraveandrobin.bygrave cc9e67c326 Add dbName() to MetaQueryPlan - easier to support multi-db query plan capture handling (#3879)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-08-14 18:32:53 +12:00
Rob Bygraveandrobin.bygrave 7a7dfb96d7 Metrics - support both CUMULATIVE and DELTA metrics collection concur… (#3878)
* Metrics - support both CUMULATIVE and DELTA metrics collection concurrently

ebean insight [and StatsD] work off DELTA metrics, where as OTEL
and Prometheus want CUMULATIVE. With this change we can have a metrics
collection for ebean insight using DELTA mode and have a second collection
use CUMULATIVE for reporting to OTEL - for the case of fan-out metrics
going to 2 places.

This isn't strictly needed when only one collection mode is used.

* Metrics - Add Mode with RESET, DELTA and CUMULATIVE

Previously we were overloading RESET and DELTA but we really need
these to be 2 separate modes for the existing tests and the
get(reset) api

* Fix test TestNatKeyCacheWithForeignKey with cache stats reset

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-08-14 18:32:23 +12:00
Rob Bygrave 003eff09ca Merge branch 'master' into ebean-15x 2026-07-28 22:30:37 +12:00
Rob Bygrave 46a73f6250 Version 18.4.0 2026-07-28 22:29:48 +12:00
Rob Bygraveandrobin.bygrave d6ba4967b6 ORM Update - add usingTransaction() to support using explicit transaction (#3868)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-22 09:45:54 +12:00
Rob Bygraveandrobin.bygrave 0d0ec79f1e SqlUpdate - add usingTransaction() to support using explicit transaction (#3867)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-21 09:59:23 +12:00
Rob Bygraveandrobin.bygrave 1737ae13db Modify SqlQuery.TypedQuery to implement FindableQuery (common interface) (#3866)
Add usingConnection() and canel() to support common FindableQuery interface

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-20 23:27:51 +12:00
Rob Bygraveandrobin.bygrave 17b641b163 MappedQuery - add findEach() findEachWhile() to MappedQuery (and promote to StreamableQuery) (#3865)
* MappedQuery - add findEach() findEachWhile() to MappedQuery (and promote to StreamableQuery)

* MappedQuery - add findEach() findEachWhile() to MappedQuery (and promote to StreamableQuery)

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-20 23:12:37 +12:00
Rob Bygraveandrobin.bygrave 4f6168dfed Add FinableQuery, StreamableQuery common interfaces for Query, DtoQuery, MappedQuery, SqlQuery (#3864)
* Add FinableQuery, StreamableQuery common interfaces for Query, DtoQuery, MappedQuery, SqlQuery

* QueryBean.cancel() added to support the new common FindableQuery interface

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-20 22:44:42 +12:00
robin.bygrave 37567e8fe8 Change MappedQuery to extend CancelableQuery - make it cancelable 2026-07-20 21:50:59 +12:00
robin.bygrave 298d244e48 Merge branch 'master' into ebean-15x 2026-07-17 10:56:52 +12:00
robin.bygrave 857e92583d enh: Add findOneOrThrow() with decent derived message for EntityNotFoundException
Adding this as I am seeing a LOT of cases where ebean can derive a good enough
error message (id case, plus single unique key case) that it is worth adding
this syntax sugar.

Yes we have discussed this a well it's not so useful for the multi-lingual /
 non-english case and also marginal when the error message needs to be built
 case.
2026-07-16 23:15:15 +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 b6225b6ae4 InsertOnConflict - change to exclude non-updatable and generated on insert only (e.g. @WhenCreated) (#3855)
* Version 18.3.0

* InsertOnConflict - change to exclude non-updatable and generated on insert only (e.g. @WhenCreated)

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-16 17:14: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
robin.bygrave d083b27e91 Version 18.3.0 2026-07-16 16:57:52 +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
Rob Bygraveandrobin.bygrave d54a24b27d #3848 Fix regression for Sql Server and Oracle with exists() introduced in 18.2.0 (#3849)
* #3848 Fix regression for Sql Server and Oracle with exists() introduced in 18.2.0

Fix #3848: exists() generates invalid scalar SQL on SQL Server and Oracle

Query.exists() generated `select exists(<subquery>)`, which is valid on
Postgres/H2/MySQL but rejected by SQL Server and Oracle since both only
support EXISTS as a predicate, not as a directly selectable scalar boolean
expression.                                                                                                                                                                              ┃
                                                                                                                                                                                            ┃
Add DatabasePlatform.existsWithCaseWhen/existsFromClause capability flags
and use them in CQueryBuilder.wrapSelectExists() to generate
`select case when exists(<subquery>) then 1 else 0 end` on platforms that                                                                                                                ┃
need it, with an additional ` from dual` suffix for Oracle (which requires                                                                                                               ┃
a FROM clause on every select). CQueryExists reads the boolean result via                                                                                                                ┃
ResultSet.getBoolean(1), which correctly interprets the resulting 0/1 int.

* Fix tests TestInsertCheckUnique for Oracle and SQL Server

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-14 10:13:16 +12:00
Rob Bygraveandrobin.bygrave 73c9ff8d80 Throw exception on duplicate Database registration (#3838) (#3847)
* Throw exception on duplicate Database registration (#3838)

DatabaseFactory.create() with register(true) previously returned the
existing Database instance when another instance was already registered
under the same name (added in #3759). This silently caused two
independently created Database instances (e.g. with different
DataSourceConfig.url values) to collide/share state, since the
second "instance" was actually the first one in disguise.

Change this to throw an IllegalStateException instead, forcing
callers to either use a unique DatabaseConfig name or setRegister(false)
when the Database is not intended to be registered/looked up by name.

* Also handle deregistration on shutdown

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-14 00:34:49 +12:00
Rob Bygraveandrobin.bygrave 6d1d58b8c2 #1989: Add findPagedList() support to DtoQuery (when based on orm query) (#3843)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-13 20:34:25 +12:00
Rob BygraveandRoland Praml a82dcb2509 Fix #3041 check uniqueness cache skipclean (#3834)
* checkUniqueness supports queryCache and skipClean

* Keep the DB.checkUniqueness() as per original (not overload there)

---------

Co-authored-by: Roland Praml <roland.praml@foconis.de>
2026-07-06 22:03:52 +12:00
Rob Bygrave 1596144896 Merge branch 'master' into ebean-15x 2026-07-03 19:47:19 +12:00
Rob Bygrave e525d4e159 Version 18.2.0 2026-07-03 19:38:05 +12:00
Rob Bygraveandrobin.bygrave 4e639cb88a Add deletePermanent() - hard delete for soft delete capable beans (#3828)
Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-03 18:23:18 +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
Andrey Glushkov 942bab5efb Fix: DuplicateKeyException when cascade-saving through a @ManyToOne to an unmodifiable bean (address #3813) (#3814)
* Fix DuplicateKeyException with unmodifiable cascaded bean

* Fix broken test TestDeleteByQuery.maxDelete_when_beanCaching_expect_selectThenDelete
2026-07-01 22:43:33 +12:00
Rob Bygrave e372579b15 Merge branch 'master' into tenant-support-for-query-plans 2026-07-01 20:33:31 +12:00
Rob Bygraveandrobin.bygrave 66bcfd107d Add tenant-partitioned L2 caches (#2956) (#3809)
* Add tenant-partitioned L2 caches (#2956)

 When `tenantPartitionedCache` is enabled, each tenant gets its own
 cache namespace (keys include the tenant id), improving cache-hit
 ratio by preventing cross-tenant key collisions.

 Refactor BeanDescriptorCacheHelp to abstract base class with two
 concrete subclasses:
 - BeanDescriptorCacheHelpFixed - static cache refs (original behaviour)
 - BeanDescriptorCacheHelpPartitioned - per-request Supplier<> lookup
   so the correct tenant-scoped cache is resolved on each access

 Fix background-thread invalidation: clear(name) in partitioned mode
 now scans all matching cache entries by key prefix rather than calling
 tenantProvider.currentId() which is null/wrong on background executor
 threads.

 Add SpiCacheManager.clearTenant(tenantId) to allow removal of all
 cache entries for a deactivated tenant, preventing unbounded memory
 growth in long-running multi-tenant deployments.

 Also load cache settings (cacheMaxSize, cacheMaxIdleTime, etc.) from
 application properties - these were previously missing from loadSettings().

* Fix versions in test-java16

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-07-01 20:04:08 +12:00
robin.bygrave 3d2cc2d4af Version 18.1.0 2026-07-01 13:28:23 +12:00
robin.bygrave ad05ed051b Modify SequenceIdGenerator to internally use ArrayDeque 2026-06-30 22:49:19 +12:00
Rob Bygraveandrobin.bygrave 01b8c3dbcb Multi-tenant aware DB sequence id generation (replaces #2305) (#3805)
## Problem

SequenceIdGenerator captured a single DataSource at deploy time and held one shared pre-fetch buffer. Under multi-tenancy this is wrong:

 - TenantMode.DB/DB_WITH_MASTER — there is no bootstrap DataSource, so BeanDescriptorManager passed null; sequence allocation couldn't resolve the current tenant's database.
 - TenantMode.SCHEMA/CATALOG — a single shared buffer let one tenant's pre-fetched ids be handed to another (cross-tenant bleed), and pre-fetch used a connection that wasn't scoped to the requesting tenant.

(Supersedes the per-datasource delegator approach in #2305, which leaked via a WeakHashMap whose values strongly referenced the keys, only handled DB mode, and re-resolved the tenant on the background thread.)

## Fix:

Make SequenceIdGenerator itself tenant aware, keeping all platform modules untouched.

 - New TenantConnectionSource (ebean-api, additive): optional interface a DataSource may implement — currentTenantId() + connectionForTenant(tenantId).
 - SequenceIdGenerator: the shared idList/lock/loading flag become a per-tenant TenantBuffer keyed by tenantId in a ConcurrentHashMap. Connections are obtained per tenant. Background pre-fetch captures the tenant at submit time (the executor thread has no tenant in scope) and fetches by explicit tenantId — fixing a latent ThreadLocal-propagation bug.
 - SequenceDataSource (ebean-core): adapts DataSourceSupplier to TenantConnectionSource; routes to the tenant DB (DB mode) or sets schema/catalog (SCHEMA/CATALOG).
 - Wiring: InternalConfiguration exposes the DataSourceSupplier; BeanDescriptorManager wraps it only for dynamic-datasource tenant modes.

## Performance (single-tenant unaffected)

 - A cached single buffer field short-circuits the ConcurrentHashMap for the non-tenant key.
 - NONE/PARTITION pass the plain DataSource (not wrapped), so tenantSource == null and the hot path is just a couple of cheap branches — equivalent to the original.

## Compatibility

 - Platform constructor signature (be, ds, seqName, allocationSize) unchanged — no changes to the 9 platform modules.
 - One protected-method signature changed: getMoreIds(int) → getMoreIds(Object tenantKey, int). Rarely overridden (subclasses override getSql/readIds), but a source-incompat for any external custom platform that did.

## Tests

 - TenantSequenceTest — DB-per-tenant: tenant 1 → 1,2,3; tenant 2 independently → 1.
 - SequenceBatchIdGeneratorTest adapted to the per-tenant buffer.
 - Existing sequence + multitenancy suites pass.

## Potential Follow-ups (not in this PR)

 - Add a SCHEMA-mode test.
 - Optional removeTenant(tenantId) hook if unbounded tenant churn is a concern (buffers hold only Longs + a lock, no DataSource, so no real leak).
 - SimpleSequenceIdGenerator (non-batching) left as-is — already uses the txn connection.

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-06-30 22:28:28 +12:00
Rob Bygraveandrobin.bygrave 97bd0e1bb8 Add Transaction.setGeneratedPropertiesEnabled(boolean) (#3799)
Adds transaction-level control over whether Ebean auto-generates values for @WhenCreated, @WhenModified, @WhoCreated and @WhoModified properties.

Motivation

Backup/restore scenarios need to preserve the original audit timestamps and user values when re-inserting exported data. Without this, every save overwrites those fields with the current time/user.

Usage

 try (Transaction txn = DB.beginTransaction()) {
   txn.setGeneratedPropertiesEnabled(false);
   bean.setWhenCreated(originalTimestamp);
   bean.setWhenModified(originalTimestamp);
   DB.save(bean);
   txn.commit();
 }

Behaviour

 - Disabled (false): generated property values are only written if the property currently has a null value. Any non-null value set on the bean is preserved.
 - @Version is unaffected: the version property always auto-increments regardless of this setting, preserving optimistic locking integrity.
 - Default is true: all existing behaviour is unchanged.

Files changed

 - Transaction — new setGeneratedPropertiesEnabled(boolean) with javadoc
 - SpiTransaction — new isGeneratedPropertiesEnabled()
 - SpiTransactionProxy — delegates both methods
 - JdbcTransaction — field + implementation (default true)
 - NoTransaction, ImplicitReadOnlyTransaction — no-op setter, true getter
 - PersistRequestBean — onInsertGeneratedProperties, onUpdateGeneratedProperties, onFailedUpdateUndoGeneratedProperties all gate on isGeneratedPropertiesEnabled()
 - TestGeneratedProperties — 3 new tests covering insert-preserves, insert-null-still-filled, and update-preserves

Supersedes

PR #2943 — same feature, renamed from setOverwriteGeneratedProperties to setGeneratedPropertiesEnabled for a clearer, positive-sense API.

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-06-26 18:53:29 +12:00
Rob Bygrave 7bf8f7fbce With Database register(true) check for existing registered Database by name (#3759)
And return the existing Database instance if present.

In theory this should not be needed. There are some test setups that hit this situation.
2026-06-24 18:02:45 +12:00
Rob Bygraveandrobin.bygrave 66d599faa2 Add support for Java 21 SequencedSet and SequencedMap (#3302)
* Add support for Java 21 SequencedSet and SequencedMap

Such that these can be used in place of Set and Map if desired.

* Refactor BeanSet, BeanList, BeanMap replacing setActualSet|List|Map

Replace with collectionAdd() and refresh() methods.

* Refactor rename method getBeanCollectionAdd() -> collectionAdd()

* Build needs to use Java 21 to support the multi-release jar

* Update SequencedSet etc from recent changes

* Update build, needs package to use MR-JAR

---------

Co-authored-by: robin.bygrave <robin.bygrave@eroad.com>
2026-06-23 21:43:19 +12:00
Rob Bygrave fbdc5d753e Merge branch 'master' into ebean-15x 2026-06-22 21:27:26 +12:00
Rob Bygrave a7fddf1981 Version 18.0.0 2026-06-22 21:11:45 +12:00
Rob Bygrave 852f199ffb Merge pull request #3789 from ebean-orm/feature/avaje-json-core
Refactor from Jackson Core to avaje-json-core
2026-06-22 21:02:52 +12:00
Rob Bygrave 564bc131cc Merge branch 'master' into ebean-15x 2026-06-22 20:40:07 +12:00
Rob Bygrave 0e6cd9db8c Version 16.11.1 2026-06-22 20:33:00 +12:00
robin.bygrave 313fdda857 Refactor from Jackson Core to avaje-json-core 2026-06-18 22:00:33 +12:00
Rob Bygrave af20eef1df Merge branch 'master' into ebean-15x 2026-06-14 21:10:18 +12:00
Rob Bygrave 7c5ee5b555 Version 16.11.0 2026-06-14 21:06:57 +12:00
robin.bygrave 6295faa351 Version 16.11.0-RC1 2026-06-13 10:46:40 +12:00