Skip to content

Commit eb6dc68

Browse files
committed
Merge: JEHibernate 4.0 — multi-module enterprise refactor
Breaking major release. Core coordinate -> de.jexcellence.hibernate:jehibernate-core. - Multi-module: jehibernate-{core,spring-boot,plugin,testing} - TODO-1 HikariCP pool (owned DataSource, getPoolHealth, clean shutdown) - TODO-2 Flyway migrations (Liquibase opt-in); ddl-auto default -> validate - TODO-3 EntityGraph fetch helpers (single-query N+1 avoidance) - TODO-5 multi-tenancy (DISCRIMINATOR/SCHEMA/DATABASE, strict leak guard) - TODO-6 jehibernate-testing (@JEHibernateTest, Testcontainers, fixtures) - TODO-7 plugin-bias decoupling (PluginPropertyLoader, slf4j-api bundled) - Per-database CRUD ITs (H2 runs; PG/MySQL/MariaDB via Testcontainers) Skipped: TODO-4 (Envers audit). jehibernate-spring-boot is a dependency skeleton (auto-configuration not yet implemented).
2 parents 1059336 + 763b262 commit eb6dc68

108 files changed

Lines changed: 3779 additions & 174 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Changelog
2+
3+
All notable changes to JEHibernate are documented here. The format is based on
4+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [4.0.0] — 2026-06-02
8+
9+
Major release. The library is now a **multi-module build** and the connection-pool and
10+
schema-management defaults changed. See the migration notes below.
11+
12+
### Added
13+
14+
- **Multi-module build** (ADR-0001): `jehibernate-core` (mandatory), `jehibernate-spring-boot`,
15+
`jehibernate-plugin`, `jehibernate-testing`. Gradle version catalog at
16+
`gradle/libs.versions.toml`. Shared build config in the root `subprojects {}` block.
17+
- **HikariCP connection pool** as the default (TODO-1, ADR-0003):
18+
- `PoolConfig` record with `maximumPoolSize`, `minimumIdle`, `idleTimeout`, `connectionTimeout`,
19+
`maxLifetime`, `leakDetectionThreshold`, `validationTimeout` (+ sensible defaults).
20+
- `ConfigurationBuilder.pool(PoolConfig)` and `ConfigurationBuilder.dataSource(DataSource)` for
21+
reusing an external (e.g. Spring) `DataSource`.
22+
- `JEHibernate.getPoolHealth()` → `PoolHealth(active, idle, total, threadsAwaiting, available)`.
23+
- `JEHibernate.getDataSource()`.
24+
- Pool properties readable from `hibernate.properties` via `jehibernate.pool.*`.
25+
- Deterministic pool shutdown in `JEHibernate.close()` (only when JEHibernate owns the pool).
26+
- **Schema migration** (TODO-2, ADR-0002):
27+
- Flyway by default (`V001__init.sql` under `classpath:db/migration`); Liquibase opt-in.
28+
- `MigrationConfig`, `MigrationTool`, `MigrationRunner`, `MigrationSupport`,
29+
`FlywayMigrationRunner`, `LiquibaseMigrationRunner`.
30+
- `ConfigurationBuilder.migration(MigrationConfig)`; properties `jehibernate.migration.{enabled,
31+
tool,location}`.
32+
- Migrations run before `SessionFactory.build()`. Absent tool → silent no-op.
33+
- **Lazy-loading / EntityGraph helpers** (TODO-3):
34+
- `AbstractCrudRepository.findByIdWithGraph(id, paths...)`, `findAllWithGraph(paths...)`,
35+
`findByIdWithNamedGraph(id, name)` — fetch named associations in a single query (no N+1),
36+
applied as a JPA `loadgraph` hint. Dot-separated paths supported for nested graphs.
37+
- `docs/lazy-loading-guide.md` with a when-to-use decision table (scoping vs read-only vs
38+
EntityGraph vs OSIV). OSIV is documented as a copy-ready last-resort pattern, deliberately
39+
not shipped as a bean.
40+
- **Multi-tenancy** (TODO-5, ADR-0004), off by default:
41+
- `MultiTenancyStrategy` (NONE/DATABASE/SCHEMA/DISCRIMINATOR), `MultiTenancyConfig`,
42+
`ConfigurationBuilder.multiTenancy(...)`.
43+
- `TenantContext` (thread-local, nesting `AutoCloseable` scope), `TenantResolver` SPI,
44+
`TenantContextResolver` (Hibernate `CurrentTenantIdentifierResolver` adapter with strict
45+
leak-guard that throws when no tenant is bound).
46+
- `SchemaMultiTenantConnectionProvider` (one shared pool, per-tenant `setSchema`, reset on
47+
release), `DatabaseMultiTenantConnectionProvider` (tenant → DataSource).
48+
- `docs/multi-tenancy-guide.md`.
49+
- Note: TODO-4 (Envers audit) is skipped, so the planned `tenant_id` column on the audit
50+
revision entity is deferred with it.
51+
- Integration tests: `PoolIntegrationTest` (50 parallel queries, health, defaults),
52+
`MigrationIntegrationTest` (apply-on-empty, no-op-on-restart, disabled, Liquibase config),
53+
`EntityGraphIntegrationTest` (single-query collection fetch verified via Hibernate Statistics),
54+
`DiscriminatorMultiTenancyTest` (tenant isolation, leak-guard throws, thread switch, shared pool),
55+
`MultiTenantConnectionProviderTest` (SCHEMA/DATABASE provider unit coverage).
56+
- **Testing module** `jehibernate-testing` (TODO-6):
57+
- `@JEHibernateTest` + `JEHibernateExtension` (JUnit 5): boots JEHibernate against H2 or a
58+
Testcontainers container (PostgreSQL/MySQL/MariaDB/MSSQL), injects `JEHibernate`/
59+
`EntityManagerFactory` as test parameters, resets the DB after each test.
60+
- `TestDatabase`, `DatabaseReset` (NONE/TRUNCATE_ALL/DROP_CREATE/ROLLBACK_TX, FK-aware via
61+
Hibernate `SchemaManager`), `Fixtures` + `Fixtures.Builder` fixture pattern.
62+
- Container-backed tests skip (not fail) when Docker is unavailable.
63+
- `docs/testing-guide.md`. The Spring `@JEHibernateRepositoryTest` slice is deferred until the
64+
Spring Boot auto-configuration exists.
65+
- **Plugin-bias decoupling** (TODO-7):
66+
- `PropertyLoader.fromClasspath(String)` and `fromFile(Path)` named entry points in core.
67+
- `PluginPropertyLoader.fromPluginDataFolder(...)` in `jehibernate-plugin` (the File/data-folder
68+
convenience now lives in the plugin module).
69+
- `slf4j-api` is now an `implementation` dependency of core (bundled transitively) so
70+
standalone/Spring consumers get the logging facade without manual setup.
71+
- `examples/spring-boot-demo/` (bootstrap < 50 lines) and `examples/spigot-plugin-demo/`
72+
(unchanged plugin API) added; `jehibernate-plugin` targets Java 21 (Paper runtime).
73+
- Integration/extension tests: `H2JEHibernateExtensionTest` (boot + reset between tests);
74+
a shared `AbstractCrudAcrossDatabasesIT` CRUD+query scenario run per database via
75+
`H2CrudIT` (always runs) and `PostgresCrudIT`/`MySqlCrudIT`/`MariaDbCrudIT`
76+
(Testcontainers, skipped without Docker).
77+
- ADRs under `docs/adr/`.
78+
79+
### Changed
80+
81+
- **BREAKING — published coordinate:** `de.jexcellence.hibernate:JEHibernate` →
82+
`de.jexcellence.hibernate:jehibernate-core`.
83+
- **BREAKING — `ddl-auto` default:** `update` → `validate`. Migrations now own the schema. Callers
84+
relying on the implicit `update` default must set `ddlAuto("update")` explicitly, provide
85+
migrations, or set `jehibernate.migration.enabled=false`.
86+
- **BREAKING — connection pool:** `connectionPool(min, max)` now configures HikariCP via
87+
`PoolConfig` instead of writing `hibernate.agroal.*`. Method signature unchanged. Agroal
88+
`compileOnly` dependencies removed.
89+
- `BukkitPluginExample` and other samples moved to `jehibernate-plugin/examples/`.
90+
91+
### Fixed
92+
93+
- `EntityScanner` / `RepositoryScanner` now also enumerate the package via the classloader in
94+
addition to JEHibernate's code-source URL, so entities/repositories located in a different
95+
output root than the JEHibernate jar (e.g. test sources, non-shaded consumers) are discovered.
96+
Previously scanning only the code-source URL returned zero results in those layouts.
97+
- `Specifications.equal(...)` reference in `IntegrationTest` updated to the renamed `equalTo(...)`
98+
(the suite no longer compiled after the 3.x rename).
99+
100+
### Migration from 3.x
101+
102+
```kotlin
103+
// before
104+
implementation("de.jexcellence.hibernate:JEHibernate:3.0.4")
105+
// after
106+
implementation("de.jexcellence.hibernate:jehibernate-core:4.0.0")
107+
implementation("org.flywaydb:flyway-core:11.1.1") // optional: enables migrations
108+
```
109+
110+
If you relied on Hibernate auto-DDL, keep the old behaviour explicitly:
111+
112+
```java
113+
.configuration(c -> c.database(...).url(...).ddlAuto("update"))
114+
```

‎README.md‎

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<p align="center">
88
<img src="https://img.shields.io/badge/Java-17%2B-orange" alt="Java 17+">
99
<img src="https://img.shields.io/badge/Hibernate-7.x-59666C" alt="Hibernate 7.x">
10-
<img src="https://img.shields.io/badge/Tests-78%20passing-brightgreen" alt="Tests">
10+
<img src="https://img.shields.io/badge/Tests-95%20passing-brightgreen" alt="Tests">
1111
<img src="https://img.shields.io/badge/License-Apache%202.0-blue" alt="License">
1212
</p>
1313
</p>
@@ -19,6 +19,95 @@ JEHibernate wraps Hibernate ORM with a clean, fluent API that eliminates 65%+ of
1919
**Runs on:** Spigot, Paper, Folia, Spring Boot, standalone Java applications.
2020
**Requires:** Java 17+ (virtual threads auto-enabled on 21+). Hibernate 7.x, Jakarta Persistence 3.1+.
2121

22+
---
23+
24+
## 4.0 Highlights (breaking)
25+
26+
4.0 is a **multi-module** release. Pick the modules you need:
27+
28+
| Module | Use it for |
29+
|---|---|
30+
| `jehibernate-core` | **Required.** The library — repositories, sessions, pooling, migration. |
31+
| `jehibernate-spring-boot` | Spring Boot auto-configuration that reuses your `DataSource` bean. |
32+
| `jehibernate-plugin` | Spigot/Paper `PropertyLoader` data-folder convenience. |
33+
| `jehibernate-testing` | Testcontainers + fixtures (test scope only). |
34+
35+
What changed (see [CHANGELOG](CHANGELOG.md) and `docs/adr/`):
36+
37+
- **HikariCP** is the default connection pool. Tune via `PoolConfig`; inspect via
38+
`jeHibernate.getPoolHealth()`.
39+
- **Flyway migrations** run before Hibernate boots (`V001__init.sql` under
40+
`classpath:db/migration`). Liquibase is opt-in.
41+
- **`ddl-auto` now defaults to `validate`** (was `update`) — migrations own the schema.
42+
- Published coordinate is now `de.jexcellence.hibernate:jehibernate-core` (was `JEHibernate`).
43+
44+
### Plugin quickstart (existing users)
45+
46+
```kotlin
47+
implementation("de.jexcellence.hibernate:jehibernate-core:4.0.0")
48+
runtimeOnly("com.mysql:mysql-connector-j:9.3.0")
49+
// Keep 3.x schema behaviour if you are not ready for migrations yet:
50+
// .configuration(c -> c.ddlAuto("update")) — or set jehibernate.migration.enabled=false
51+
```
52+
53+
```java
54+
var je = JEHibernate.fromProperties(getDataFolder(), "database", "hibernate.properties");
55+
var users = je.repositories().get(UserRepository.class);
56+
```
57+
58+
### Spring Boot quickstart (new users)
59+
60+
```kotlin
61+
implementation("de.jexcellence.hibernate:jehibernate-spring-boot:4.0.0")
62+
```
63+
64+
```java
65+
@Configuration
66+
class JEHibernateConfig {
67+
@Bean(destroyMethod = "close")
68+
JEHibernate jeHibernate(DataSource dataSource) {
69+
return JEHibernate.builder()
70+
.configuration(c -> c
71+
.database(DatabaseType.POSTGRESQL)
72+
.url("jdbc:postgresql://localhost:5432/app")
73+
.dataSource(dataSource) // reuse Spring's pool — JEHibernate won't close it
74+
.ddlAuto("validate"))
75+
.scanPackages("com.example")
76+
.build();
77+
}
78+
}
79+
```
80+
81+
### Migration guide
82+
83+
| Property | Default | Meaning |
84+
|---|---|---|
85+
| `jehibernate.migration.enabled` | `true` | Run migrations at bootstrap |
86+
| `jehibernate.migration.tool` | `flyway` | `flyway` \| `liquibase` \| `none` |
87+
| `jehibernate.migration.location` | `classpath:db/migration` | Flyway location / Liquibase changelog path |
88+
| `jehibernate.pool.maximumPoolSize` | `10` | HikariCP max connections |
89+
| `jehibernate.pool.minimumIdle` | `2` | HikariCP min idle connections |
90+
91+
Add `org.flywaydb:flyway-core` to the classpath to enable Flyway; omit it and migration is a
92+
silent no-op. Put SQL files in `src/main/resources/db/migration/V001__init.sql`,
93+
`V002__...`, etc.
94+
95+
### Multi-tenancy (optional, off by default)
96+
97+
```java
98+
// DISCRIMINATOR: a @TenantId column on entities; strict leak-guard throws if no tenant is bound.
99+
config.multiTenancy(MultiTenancyConfig.discriminator());
100+
```
101+
102+
```java
103+
try (var ignored = TenantContext.open("acme")) {
104+
noteRepo.findAll(); // sees only acme's data
105+
}
106+
```
107+
108+
Strategies: `DISCRIMINATOR` (row-level), `SCHEMA` (schema-per-tenant, shared pool), `DATABASE`
109+
(database-per-tenant). Full details in [docs/multi-tenancy-guide.md](docs/multi-tenancy-guide.md).
110+
22111
## Table of Contents
23112

24113
- [Installation](#installation)
@@ -61,7 +150,7 @@ JEHibernate wraps Hibernate ORM with a clean, fluent API that eliminates 65%+ of
61150

62151
```kotlin
63152
dependencies {
64-
implementation("de.jexcellence.hibernate:JEHibernate:3.0.1")
153+
implementation("de.jexcellence.hibernate:jehibernate-core:4.0.0")
65154

66155
// Pick your database driver
67156
runtimeOnly("com.h2database:h2:2.4.240") // H2 (embedded, dev/testing)

0 commit comments

Comments
 (0)