在 MyBatis 中设置 tenant 标识,通常是为了实现多租户应用的数据隔离。以下是一些常见的方法来实现这一功能:
ThreadLocal
是 Java 提供的一个线程本地变量,它可以让变量与线程绑定,实现线程数据的隔离。
步骤:
mybatis-config.xml
),添加一个类型处理器(TypeHandler)来处理 tenant 的类型转换。<typeHandlers>
<typeHandler handler="com.example.TenantTypeHandler" javaType="java.lang.String" jdbcType="VARCHAR"/>
</typeHandlers>
TenantTypeHandler
类,用于处理 tenant 的存储和读取。public class TenantTypeHandler extends BaseTypeHandler<String> {
private static final ThreadLocal<String> currentTenant = new InheritableThreadLocal<>();
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
currentTenant.set(parameter);
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
public static String getCurrentTenant() {
return currentTenant.get();
}
public static void clear() {
currentTenant.remove();
}
}
TenantTypeHandler.getCurrentTenant()
获取当前线程的 tenant 标识,并在 SQL 中使用该标识进行数据隔离。示例:
String sql = "SELECT * FROM ${tenant}.table_name WHERE tenant = #{tenant}";
在 MyBatis 的 Mapper XML 文件中,可以使用 #{}
来引用方法参数,这样 MyBatis 会自动将方法参数传递给 SQL 语句中的 ${tenant}
占位符。
另一种方法是使用数据库视图或存储过程来封装数据查询,并在其中根据 tenant 标识过滤数据。
步骤:
示例(视图):
CREATE VIEW tenant_data AS
SELECT * FROM original_table
WHERE tenant_id = #{tenantId};
示例(存储过程):
DELIMITER //
CREATE PROCEDURE GetTenantData(IN tenantId INT)
BEGIN
SELECT * FROM original_table WHERE tenant_id = tenantId;
END //
DELIMITER ;
在 MyBatis 的 Mapper XML 文件中,可以调用该存储过程:
<select id="selectTenantData" parameterType="int" statementType="CALLABLE">
{call GetTenantData(#{tenantId})}
</select>
ThreadLocal
存储 tenant 信息时,需要注意线程安全问题,避免数据泄露或被意外修改。