#508
Multi-tenant
One database, many companies. Hibernate adds the tenant filter to every query for you.
Technique: Hibernate @TenantId + CurrentTenantIdentifierResolver + sub-domain / session resolution
Read the code:
src/main/java/com/study/advlab/core/TenantFilter.javasrc/main/java/com/study/advlab/core/TenantIdentifierResolver.javasrc/main/java/com/study/advlab/tenancy/TenantService.java
This request
Northwind Electronics
tenant_id =
1 · code northwindFound by: Signed-in user "nina.admin" (session)
Open a company by its sub-domain (no DNS needed - browsers send *.localhost to this PC):
Proof that the data is isolated
productRepository.count() JPA, @TenantId |
41 |
SELECT COUNT(*) FROM product WHERE tenant_id = ? | 41 |
SELECT COUNT(*) FROM product native, no filter |
110 |
productRepository.findById(42)
- a product of Siam Sport & Outdoor |
empty |
The repository methods have no tenant parameter. Hibernate adds where tenant_id = ? to every query of an
entity with @TenantId, and fills tenant_id on insert.
Native SQL (JdbcTemplate) is not filtered - every hand-written query in this project passes
tenant_id = ? itself. Forgetting it is the classic multi-tenant bug.
Companies rows per table - native SQL, like a database admin sees it
| Company | Users | product | customer | purchase | review | txn | kb_article | lead | face_person | experiment | geo_zone | chat_message | Plan | Status |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Northwind Electronics
id 1 · northwind |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |||
| Siam Sport & Outdoor
id 2 · siamsport |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |||
| Blue Harbor Home
id 3 · blueharbor |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Three ways to do multi-tenancy
| Approach | Isolation | Cost |
|---|---|---|
| Shared tables + tenant_id (this lab) | by code - filter every query | cheapest, easiest to run |
| Schema per tenant | by database schema | medium, migrations × N |
| Database per tenant | strongest, own backups | most expensive |
SQL Server can add a safety net with Row-Level Security (a security policy on tenant_id), so even a forgotten WHERE cannot leak.