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
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.