Files
..

multitenancy

Companion module for the ankurm.com article Multi-Tenancy with Hibernate 7 and Spring Boot 4: @TenantId vs Schema-per-Tenant (https://ankurm.com/hibernate-multi-tenancy-spring-boot-tenantid-vs-schema/). Every claim in the article comes from a test here; each test writes what it observed to output/. All tests run against a real PostgreSQL 17 container started by Testcontainers (needs a Docker daemon).

Versions

Component Version Notes
Hibernate ORM 7.4.14.Final newest 7.x GA on Maven Central on 2026-10-29; overrides the 7.4.13.Final Spring Boot 4.1.2 manages
Spring Boot 4.1.2 parent POM
Spring Security (JOSE only) 7.1.2 spring-security-oauth2-jose, Nimbus JWT; no security filter chain
PostgreSQL / driver 17.11 / 42.7.14 postgres:17-alpine; Testcontainers 2.0.5
Java 25 (Temurin 25.0.5+8)

Run

./scripts/run-all.sh      # javap dump + mvn clean test; regenerates everything in output/

Layout

Package What
tenant TenantContext (ThreadLocal), JwtTenantFilter, TenantIdResolver (Hibernate resolver), TenantHibernateConfig, TenantNames allow-list
discriminator DiscriminatorApp, Invoice with @TenantId, repository, controller
schema SchemaApp, Invoice (no tenant column), SearchPathConnectionProvider (shared pool; also the deliberately leaky variant), PoolPerTenantConnectionProvider, SchemaProvisioner

Output

File What it shows
00-javap.txt javap of the tenancy types in the shipped jars; Boot's HibernatePropertiesCustomizer package
01-api-shape.txt packages, generics and setting names by reflection
02-discriminator.txt @TenantId DDL and SQL, cross-tenant findById, bulk HQL, native-SQL bypass, no-tenant and mismatched-tenant failures
03-schema-per-tenant.txt schema-per-tenant on one shared pool: SQL, overlapping ids, ghost tenant, hostile name, pool connection count
04-search-path-leak.txt a provider that forgets to reset search_path vs the correct one
05-pool-per-tenant.txt one Hikari pool per tenant: connections per pool in pg_stat_activity
06-http-discriminator.txt / 07-http-schema.txt JWT to tenant over real HTTP: 401 / 403 / 404 / 200 cases

Not covered (not run)

Row-level security, database-per-tenant (separate servers), L2 cache with multi-tenancy, Envers, Flyway per schema, JWKS/asymmetric keys, @Async / virtual-thread propagation of the tenant, and any load or latency measurement.