Mybatis-plus提供了TenantLineInnerInterceptor全局租户插件,提供了租户数据隔离,刚好最近一个老项目面临多租户改造,老项目代码冗余多,不可控性高,所以多租户我打算采用逻辑隔离+租户插件的方式来做.
1、配置
1.1 自定义多租户策略配置
多租户插件的开启非常简单,自己实现mybatis-plus的 plugins包下的tenantLineHandler接口,接口提供
1,getTenantId() 获取租户 ID 值表达式,只支持单个 ID 值
2,getTenantIdColumn() 获取租户字段名
3,ignoreTable() 根据表名判断是否忽略拼接多租户条件
4,ingoreInsert() 忽略插入租户字段逻辑
四个方法,实现这四个方法并提供相应的返回值即可自定义租户策略.其中,getTenantId()方法需要提供获取当前线程租户Id的方法,getTenantIdColumn方法需要提供全局租户ID字段名称,ignoreTable 可以自定义不需要开启多租户的表名,我这里反向配置了需要开启多租户的表名,根据实际情况配置即可.
public class MyTenantLineHandler implements TenantLineHandler {
private static List<String> tables = Lists.newArrayList(
"sys_user"
);
@Override
public Expression getTenantId() {
Long tenantId = UserUtil.getTenantId();
return tenantId == null ? new LongValue(1) : new LongValue(tenantId);
}
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public boolean ignoreTable(String tableName) {
return !tables.contains(tableName);
// return TenantLineHandler.super.ignoreTable(tableName);
}
}
2、将插件配置到mybatis-plus中
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new MyTenantLineHandler()));
return mybatisPlusInterceptor;
}
3、开启mybatis-plus插件
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setPlugins(new Interceptor[]{mybatisPlusInterceptor,new PageInterceptor()});
4、调试
4.1 单元测试(插入)
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Rollback
class SysUserServiceTest extends BaseTest {
@Autowired
private SysUserMapper sysUserMapper;
@Test
void add() {
SysUserEntity sysUserEntity = new SysUserEntity();
sysUserEntity.setName("test1");
sysUserEntity.setUsername("test1");
sysUserEntity.setPassword("test1");
sysUserMapper.insert(sysUserEntity);
}
}
结果: 由于这里这里使用的是单元测试,所以userUtils获取的userInfo是空,所以走了默认的租户 = 1 的逻辑.这里可以看到插入的时候租户字段已经被拼接上了.
4.2 单元测试(查询)
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Rollback
class SysUserServiceTest extends BaseTest {
@Autowired
private SysUserMapper sysUserMapper;
@Test
void detail() {
SysUserEntity sysUserEntity = sysUserMapper.selectById(22);
}
}
结果同上,也自动拼接上了租户条件
4.2 单元测试(删除)
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Rollback
class SysUserServiceTest {
@Autowired
private SysUserMapper sysUserMapper;
@Test
void delete() {
sysUserMapper.deleteById(22);
}
}
结果一致,这里我配置了逻辑删除,所以删除实际执行的是更新语句
5、其他配置
5.1 如果我们想要某个表被多租户插件管理,但是某个自定义sql语句不被多租户插件管理,则需要在sql上加上 @InterceptorIgnore(tenantLine = "true") 注解,这个注解是mybatis-plus提供的,内置了许多插件的开关,这里我们将tenantLine插件关掉.
@InterceptorIgnore(tenantLine = "true")
@Select("select * from sys_user where id = 22")
SysUserEntity selectById(@Param("id") Long id);
5.2 这样调用的时候这条sql就不会被多租户插件管控了.
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Rollback
class SysUserServiceTest extends BaseTest {
@Autowired
private SysUserMapper sysUserMapper;
@Test
void ignoreTenant(){
sysUserMapper.selectById(22);
}
}
结论,mybatis-plus的多租户插件在某些老项目多租户改造,同源同架构多租户方案上,还是有一定的使用便利性的.但是注意,同样为mybatis-plus的插件 InsertBatchSomeColumn批量插入插件下,却对多租户插件无效,并且插入数据时,如果提前设置了entity中的租户字段的值,那么多租户插件也会直接不生效.