Hibernate 6 租户隔离:使用切面自动应用 tenant_id 条件
可以使用 Hibernate 的拦截器来实现自动应用'tenant_id'的租户条件。具体步骤如下:
- 创建一个实现了'org.hibernate.Interceptor'接口的拦截器类,重写'onPrepareStatement'方法,在 SQL 语句执行前拦截并修改 SQL 语句,添加'tenant_id'条件。
public class TenantInterceptor extends EmptyInterceptor {
private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>();
public static void setTenantId(String tenantId) {
TENANT_ID.set(tenantId);
}
public static void clearTenantId() {
TENANT_ID.remove();
}
@Override
public String onPrepareStatement(String sql) {
String tenantId = TENANT_ID.get();
if (tenantId != null) {
sql = addTenantCondition(sql, tenantId);
}
return super.onPrepareStatement(sql);
}
private String addTenantCondition(String sql, String tenantId) {
String condition = " tenant_id = " + tenantId;
int index = sql.indexOf("where");
if (index == -1) {
index = sql.indexOf("WHERE");
}
if (index != -1) {
return sql.substring(0, index + 5) + condition + " and " + sql.substring(index + 5);
} else {
return sql + " where " + condition;
}
}
}
- 在 Spring 配置文件中配置拦截器,并将其注入到 SessionFactory 中。
<bean id="tenantInterceptor" class="com.example.TenantInterceptor"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<list>
<value>com.example.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.generate_statistics">false</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_versioned_data">true</prop>
<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
<prop key="hibernate.connection.autocommit">false</prop>
<!--配置拦截器-->
<prop key="hibernate.session_factory.interceptor">com.example.TenantInterceptor</prop>
</props>
</property>
</bean>
- 在 Controller 中设置当前租户的'tenant_id'。
@RestController
@RequestMapping("/api")
public class ApiController {
@Autowired
private SessionFactory sessionFactory;
@GetMapping("/setTenantId")
public String setTenantId(@RequestParam("tenantId") String tenantId) {
TenantInterceptor.setTenantId(tenantId);
//测试查询
Session session = sessionFactory.getCurrentSession();
List<User> userList = session.createQuery("from User", User.class).list();
return "success";
}
}
- 在事务结束后清除当前租户的'tenant_id'。
@Transactional
public void doSomething() {
//业务逻辑处理
...
}
//事务结束后清除租户ID
TenantInterceptor.clearTenantId();
这样,每次执行 SQL 语句时都会自动添加'tenant_id'条件,实现了租户隔离。
原文地址: https://www.cveoy.top/t/topic/oKKt 著作权归作者所有。请勿转载和采集!