Home/Learn/Hibernate & JPA/Multi-Tenancy in Hibernate

Multi-Tenancy in Hibernate

Advanced
Advanced

Hibernate supports SCHEMA, DATABASE, and DISCRIMINATOR multi-tenancy strategies; the CurrentTenantIdentifierResolver and ConnectionProvider resolve the tenant per request.

Overview

Multi-tenancy allows a single application instance to serve multiple customers (tenants) with isolated data. Hibernate 6 supports three strategies: SCHEMA (one DB, separate schemas per tenant), DATABASE (separate DB instances per tenant), and DISCRIMINATOR (one schema, tenant_id column on every table — least isolation, simplest ops). The active tenant is resolved per-request via CurrentTenantIdentifierResolver (reads a thread-local or request header). A multi-tenant ConnectionProvider or DataSource router supplies the correct connection. Spring Boot 3 pairs well with AbstractRoutingDataSource for SCHEMA and DATABASE strategies.

Schema-Per-Tenant Strategy

In the SCHEMA strategy, all tenants share one database instance but each has its own schema. Hibernate prepends the schema name to every SQL statement. A TenantContext ThreadLocal holds the current tenant ID for the duration of the request.

Java — TenantContext + CurrentTenantIdentifierResolver
// TenantContext — store/clear per request
public class TenantContext {
    private static final ThreadLocal<String> CURRENT = new ThreadLocal<>();
    public static void set(String tenantId)  { CURRENT.set(tenantId); }
    public static String get()               { return CURRENT.get(); }
    public static void clear()               { CURRENT.remove(); }
}

// Servlet filter (or Spring Security filter) sets the tenant
@Component
public class TenantFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        String tenant = ((HttpServletRequest) req).getHeader("X-Tenant-Id");
        TenantContext.set(tenant);
        try { chain.doFilter(req, res); }
        finally { TenantContext.clear(); }
    }
}

// CurrentTenantIdentifierResolver
@Component
public class TenantIdentifierResolver
        implements CurrentTenantIdentifierResolver<String> {
    @Override
    public String resolveCurrentTenantIdentifier() {
        String tenant = TenantContext.get();
        return (tenant != null) ? tenant : "public";  // fallback to default schema
    }
    @Override
    public boolean validateExistingCurrentSessions() { return true; }
}

AbstractRoutingDataSource for Multi-Tenant Connections

AbstractRoutingDataSource routes to the correct DataSource based on the current tenant key. Each tenant entry maps to its own connection pool. Pair with Hibernate's MultiTenancyStrategy.SCHEMA or DATABASE configuration.

Java + Properties — AbstractRoutingDataSource
// Multi-tenant DataSource router
public class TenantRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.get();
    }
}

// Configuration
@Configuration
public class MultiTenantDataSourceConfig {

    @Bean
    public DataSource dataSource(TenantProperties props) {
        Map<Object, Object> sources = new HashMap<>();
        for (var entry : props.getTenants().entrySet()) {
            sources.put(entry.getKey(), buildHikariDataSource(entry.getValue()));
        }

        TenantRoutingDataSource router = new TenantRoutingDataSource();
        router.setTargetDataSources(sources);
        router.setDefaultTargetDataSource(sources.get("public"));
        router.afterPropertiesSet();
        return router;
    }

    // application.yml
    // multitenancy.tenants.acme.url=jdbc:postgresql://db:5432/acme
    // multitenancy.tenants.globex.url=jdbc:postgresql://db:5432/globex
}

// Hibernate config — tell it about multi-tenancy
spring.jpa.properties.hibernate.multiTenancy=SCHEMA
spring.jpa.properties.hibernate.tenant_identifier_resolver=com.example.TenantIdentifierResolver

Discriminator (Row-Level) Multi-Tenancy

The DISCRIMINATOR strategy adds a tenant_id column to every table and Hibernate automatically appends WHERE tenant_id = ? to every query. This offers the simplest ops (single schema, single DB) but weakest isolation — a misconfiguration could expose cross-tenant data.

Java — @TenantId discriminator strategy (Hibernate 6.1+)
// Hibernate 6 discriminator strategy — entity annotation
@Entity
@Table(name = "orders")
@TenantId          // marks the tenant discriminator field (Hibernate 6.1+)
public class Order {

    @Id @GeneratedValue
    private Long id;

    @TenantId
    private String tenantId;     // automatically populated from CurrentTenantIdentifierResolver

    private OrderStatus status;
    private BigDecimal total;
    // ...
}

// Hibernate 6 automatically adds tenant filter to all queries:
// SELECT * FROM orders WHERE tenant_id = 'acme' AND id = 42
//
// For bulk updates, the tenant filter is also applied:
// UPDATE orders SET status = 'SHIPPED' WHERE tenant_id = 'acme' AND id IN (...)

// Hibernate configuration
spring.jpa.properties.hibernate.multiTenancy=DISCRIMINATOR

Key Points to Remember

  • 1SCHEMA: shared DB, separate schema per tenant — good isolation, moderate ops complexity.
  • 2DATABASE: separate DB instance per tenant — highest isolation, highest resource cost.
  • 3DISCRIMINATOR: single schema + tenant_id column — simplest ops, weakest isolation.
  • 4CurrentTenantIdentifierResolver resolves the tenant ID per request (e.g. from a header or JWT claim).
  • 5AbstractRoutingDataSource routes JDBC connections to the correct tenant data source.
  • 6@TenantId (Hibernate 6.1+) marks the discriminator field; Hibernate appends the filter automatically.

Interview Questions

Sign in to ask Aria
1

What are the three multi-tenancy strategies in Hibernate and when would you use each?

HardAmazon
2

How does CurrentTenantIdentifierResolver work in a web application?

MediumSalesforce
3

What are the trade-offs between SCHEMA and DISCRIMINATOR multi-tenancy?

HardUber
4

How do you prevent cross-tenant data leaks in the DISCRIMINATOR strategy?

HardNetflix
5

How would you implement tenant-specific connection pools with Spring Boot?

HardPivotal

Ask Aria about Multi-Tenancy in Hibernate

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…