# @TenantId on PostgreSQL (PostgreSQL 17.11), Hibernate 7.4.14.Final == DDL Hibernate generated (read back from the catalog) column amount numeric nullable=YES column id bigint nullable=NO column number character varying nullable=YES column tenant_id character varying nullable=NO unique UNIQUE (tenant_id, number) == insert: nobody sets tenant_id in Java -- as acme: save(INV-1) sql> insert into invoice (amount,number,tenant_id) values (?,?,?) entity.tenantId after save = acme -- as globex: save(INV-1) - same invoice number, allowed by UNIQUE(tenant_id, number) sql> insert into invoice (amount,number,tenant_id) values (?,?,?) ids: acme INV-1=1, globex INV-1=2 (one IDENTITY column shared by all tenants) == reads are filtered -- as acme: findAll() sql> select i1_0.id,i1_0.amount,i1_0.number,i1_0.tenant_id from invoice i1_0 where i1_0.tenant_id = ? rows = 2, numbers = [INV-1, INV-2] -- as globex: findById(acme's id 1) sql> select i1_0.id,i1_0.amount,i1_0.number,i1_0.tenant_id from invoice i1_0 where i1_0.id=? and i1_0.tenant_id = ? result = Optional.empty == bulk HQL is scoped too -- as acme: update Invoice set amount = 0 sql> update invoice i1_0 set amount=0 where i1_0.tenant_id = ? rows updated = 2 (globex's row untouched) == what @TenantId does NOT cover: native SQL -- as acme: native query select count(*) from invoice result = 3 (acme owns 2 rows; the native query saw every tenant) == no tenant set -- strict resolver (throws when TenantContext is empty): repo.findAll() with no tenant org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction <- caused by com.ankurm.multitenancy.tenant.MissingTenantException: no tenant in TenantContext: refusing to open a tenant-scoped session -- lenient resolver (returns null): repo.findAll() with no tenant org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction <- caused by org.hibernate.HibernateException: SessionFactory configured for multi-tenancy, but no tenant identifier specified == writing a row for a different tenant than the session's -- as acme: new Invoice with tenantId manually set to globex, saveAndFlush org.springframework.dao.DataIntegrityViolationException: assigned tenant id differs from current tenant id [globex != acme] for entity com.ankurm.multitenancy.discriminator.Invoice.tenantId <- caused by org.hibernate.PropertyValueException: assigned tenant id differs from current tenant id [globex != acme] for entity com.ankurm.multitenancy.discriminator.Invoice.tenantId