Reputation: 1
In my application I have switched to a multi-tenant database. I am using Java 21 with hibernate 6.5.2, and using @TenantId to discriminate between clients (Not using Spring). Same database, same schema, different tenant_id column on every table. After making the switch all the entities on my backend now have a tenant_id string with the @TenantId annotation. Hibernate sees the @TenantId annotation and automatically filters it, persisting objects and fetching objects that have the matching tenantId supplied to hibernate. However, this isn't working well when using @JoinColumn.
Example- I have an object that has an inner object, that child object is connected using @JoinColumn
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "some_type", referencedColumnName = "name")
private SomeType someType;
The 'SomeType' object also has a tenant_id column with the @TenantId annotation. The problem is my entity manager query is fetching this inner object based solely on the referencedColumnName and does not use the @TenantId filter on the SomeType object. This throws an error because the referencedColumnName is unique per tenant but when another tenant has the same value the referencedColumnName tries to return both records.
I could change the database so that the referenced column is joined on the primary key of the SomeType object, but we like having the referenced column on the name since it's easier to look at and understand when we have to manually look at table data. I'm wondering if anyone has any experience with this and knows if there is something I can add or change that would get this working without any large refactors?
I have previously tried using the @Where annotation on the someType object and also @FilterDef on the SomeType class with no results.
Upvotes: 0
Views: 471
Reputation: 916
I'm late to the game on this one, but a composite primary key would do the trick as well. Your primary entity would be keyed on (tenant_id, primary_id) and your secondary entity would be keyed on (tenant_id, secondary_id). The join would look like this:
@ManyToOne
@JoinColumn(name="tenant_id", referencedColumnName="tenant_id")
@JoinColumn(name="secondary_id", referencedColumnName="secondary_id")
private SecondaryEntity secondaryEntity;
In this way you could maintain whatever type of data you want in the secondary_id
field and you can map it directly in the primary entity's table.
While I agree that a single field primary key on all tables is optimal, multi-tenancy based on a field discriminator introduces a large potential for data leaks that the composite keys alleviate well.
Upvotes: 0
Reputation: 1
Well, Life must go on, so the solution for me is to go back to using join column on primary keys instead of a unique string, which was unique per tenant but is no longer unique with multiple tenants. Since the primary key still remains unique this is the answer, it's not as human-readable when looking at table data, but I guess nothings perfect.
Upvotes: 0