我正在尝试创建一个多租户架构,其中每个表都有一个namespace
字段。无论何时执行任何方法,ThreadLocal
存储都可以访问当前namespace
。我想要的是我的业务层完全不知道命名空间的概念。举个例子:
@Entity
@Table(name = "users")
class Users {
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
...
}
表users
定义为:
CREATE TABLE users(
first_name CHARACTER VARYING,
last_name CHARACTER VARYING,
namespace CHARACTER VARYING NOT NULL
...
);
为了简洁起见,我正在跳过主键和其他字段。对SELECT * FROM users WHERE (condition)
类型的任何查询都必须基本上为SELECT * FROM users WHERE (condition & namespace = :1)
,或者就spring-data
而言,findUsersByName(String name)
应为findUsersByNameAndNamespace(String name, String namespace)
。此外,每个save
操作都应该要求使用命名空间填充模型。
但是,我不想在模型定义中包含命名空间。这是一个双面问题:
fetch
都需要在查询中设置命名空间。save
都需要在模型中保存命名空间。虽然,我完全不知道如何解决第一个问题,但是第二个问题可以(部分)通过使用具有TenantedModel
属性的父类namespace
来解决,然后使用该属性进行设置一个Hibernate拦截器。问题是业务层仍然能够看到namespace
属性。
使用带有Spring Data的JPA有没有一种干净的方法来执行此操作?
答案 0 :(得分:1)
您可以使用Interceptor来修改hibernate生成的SQL。
public String onPrepareStatement(String sql) {
String newSQLWithNamespace = sql+" and (namespace = "+ nameSpaceValue+")";
return super.onPrepareStatement(newSQLWithNamespace);
}
参见例如Add a column to all MySQL Select Queries in a single shot
您的拦截器可以读取线程本地命名空间值并更改SQL。