# 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 ```bash ./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.