可以使用 Hibernate 的拦截器来实现自动应用'tenant_id'的租户条件。具体步骤如下:

  1. 创建一个实现了'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;
        }
    }
}
  1. 在 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>
  1. 在 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";
    }
}
  1. 在事务结束后清除当前租户的'tenant_id'。
@Transactional
public void doSomething() {
    //业务逻辑处理
    ...
}
//事务结束后清除租户ID
TenantInterceptor.clearTenantId();

这样,每次执行 SQL 语句时都会自动添加'tenant_id'条件,实现了租户隔离。

Hibernate 6 租户隔离:使用切面自动应用 tenant_id 条件

原文地址: https://www.cveoy.top/t/topic/oKKt 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录