在 MyBatis 中实现动态 tenant 功能,通常是为了支持多租户应用,每个租户的数据是隔离的。以下是实现动态 tenant 的一种常见方法:
-
使用 ThreadLocal 存储当前租户信息:
- 创建一个 ThreadLocal 变量来存储当前线程的租户信息。
- 在请求进入应用时,根据请求头或其他方式获取租户信息,并设置到 ThreadLocal 中。
- 在执行数据库操作时,从 ThreadLocal 中获取租户信息,并在 SQL 语句中使用。
-
在 MyBatis 的 Mapper XML 文件中编写动态 SQL:
- 使用
标签来判断当前线程的租户信息是否存在,如果存在则将其作为参数传递给 SQL 语句。 - 例如,假设有一个租户 ID 为
tenantId
,可以在 SQL 语句中使用${tenantId}
作为参数。
- 使用
-
在 Service 层调用 Mapper 方法:
- 在 Service 层中,从 ThreadLocal 中获取租户 ID,并调用 Mapper 方法执行数据库操作。
public User getUserByIdAndTenant(int userId) { // 从 ThreadLocal 中获取租户 ID int tenantId = TenantContext.getCurrentTenantId(); // 调用 Mapper 方法 User user = userMapper.selectUserByTenantId(tenantId, userId); return user; }
-
清理 ThreadLocal:
- 在请求结束或线程结束时,需要清理 ThreadLocal 中的租户信息,以避免内存泄漏。
- 可以在过滤器(Filter)中或线程结束时的回调方法中进行清理。
public class TenantContext { private static final ThreadLocalcurrentTenant = new ThreadLocal<>(); public static void setCurrentTenantId(int tenantId) { currentTenant.set(tenantId); } public static int getCurrentTenantId() { return currentTenant.get(); } public static void clear() { currentTenant.remove(); } }
在过滤器中:
public class TenantFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 在请求进入时设置租户信息 int tenantId = getTenantIdFromRequest(request); TenantContext.setCurrentTenantId(tenantId); try { chain.doFilter(request, response); } finally { // 在请求结束时清理租户信息 TenantContext.clear(); } } private int getTenantIdFromRequest(ServletRequest request) { // 根据请求头或其他方式获取租户信息 return 1; // 示例返回值 } }
通过以上步骤,可以在 MyBatis 中实现动态 tenant 功能。需要注意的是,这种方法在单个请求内是有效的,因为 ThreadLocal 是线程局部变量。如果应用是多线程的,需要确保在每个线程中都正确地设置和清理租户信息。