返回

Mybatis Plus 多租户:改造路上踩坑指南

后端

前言

最近公司的老项目要改造多租户,于是我进入了大坑,本文写点遇到的坑以及解决方案,每次遇到问题在网上搜了好久,记录下来,防止以后忘掉。

方案

网上有很多方案,我们采用了在数据源上做文章。数据源通过一个叫做Druid的数据源池进行管理,具体方案是让Druid根据不同的租户自动切换数据源。

坑一:数据源无法自动切换

Druid的数据源切换是通过一个叫做ShardingFilter的过滤器来实现的,ShardingFilter根据请求中的某个字段来决定使用哪个数据源。我们在请求头中添加了一个叫做tenantId的字段,值是租户的ID。

public class TenantIdFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String tenantId = request.getHeader("tenantId");
        if (tenantId != null) {
            DruidDataSourceHolder.setTenantId(tenantId);
        }
        filterChain.doFilter(request, response);
    }
}

然后在Druid的配置中添加ShardingFilter,并将ShardingFilter的strategy设置为tenantId。

<filter>
    <name>shardingFilter</name>
    <class>com.alibaba.druid.filter.FilterAdapter</class>
    <init-param>
        <name>strategy</name>
        <value>tenantId</value>
    </init-param>
</filter>

但是在测试的时候发现,数据源并没有自动切换。排查问题发现,原来是Druid的配置中没有启用ShardingFilter。

<property name="filters" value="shardingFilter"/>

坑二:数据源切换后,事务失效

在数据源切换后,事务失效了。这是因为Druid的数据源池是独立的,每个数据源都有自己的事务管理器。所以,当数据源切换后,就需要重新创建一个事务管理器。

@Transactional(propagation = Propagation.REQUIRED)
public void saveUser(User user) {
    if (user.getTenantId() != null) {
        DruidDataSourceHolder.setTenantId(user.getTenantId());
    }
    userDao.save(user);
}

坑三:数据源切换后,Mybatis Plus的插件失效

在数据源切换后,Mybatis Plus的插件失效了。这是因为Mybatis Plus的插件是针对特定的数据源写的。当数据源切换后,Mybatis Plus的插件就无法找到对应的