mybatis-plus的多租户功能实现

本文介绍了一种基于mybatis-plus的多租户系统设计方案,包括创建包含租户ID的数据表、利用缓存管理租户ID、配置mybatis-plus自动添加租户ID条件等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.创建一个数据表(包含租户ID字段–tenant_id)

CREATE TABLE `t_sys_user` (
  `user_id` bigint(50) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_name` varchar(30) NOT NULL COMMENT '用户名',
  `user_password` varchar(128) NOT NULL COMMENT '用户密码',
  `salt` varchar(64) DEFAULT NULL COMMENT '加密盐',
  `user_phone` varchar(20) DEFAULT NULL COMMENT '手机号',
  `user_email` varchar(20) DEFAULT NULL COMMENT '邮箱',
  `user_title` varchar(20) DEFAULT NULL COMMENT '职称',
  `creater_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
  `creater_name` varchar(30) DEFAULT NULL COMMENT '创建人名称',
  `creater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updater_id` bigint(20) DEFAULT NULL COMMENT '更新人ID',
  `updater_name` varchar(30) DEFAULT NULL COMMENT '更新人名称',
  `updater_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `role_ids` varchar(200) DEFAULT NULL,
  `role_names` varchar(300) DEFAULT NULL,
  `del_tag` int(1) DEFAULT '0' COMMENT '逻辑删除(0 未删除,1 已删除)',
  `version` bigint(20) DEFAULT '1',
  `tenant_id` bigint(20) NOT NULL COMMENT '服务商ID',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8;

2.编写一个缓存租户id的类:

@Component
public class ApiContext {
    private static final String KEY_CURRENT_TENANT_ID = "KEY_CURRENT_TENANT_ID";
    private static final Map<String, Object> mContext = new ConcurrentHashMap<>();

    public void setCurrentTenantId(Long providerId) {
        mContext.put(KEY_CURRENT_TENANT_ID, providerId);
    }
    public Long getCurrentTenantId() {
        return (Long) mContext.get(KEY_CURRENT_TENANT_ID);
    }
}

3.在分页的时候,自动在sql拼接租户id的字段作为where后面的条件,具体在mybatis-plus的配置类里面配置,如下:



import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.lz.hehuorenservice.common.component.ApiContext;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
    @Autowired
    private ApiContext apiContext;
    // 最新版  分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //注册乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        /**
         * 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题
         */
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                // 从当前系统上下文中取出当前请求的服务商ID,通过解析器注入到SQL中。
                Long currentProviderId = apiContext.getCurrentTenantId();

                if (null == currentProviderId) {
                    throw new RuntimeException("Get CurrentProviderId error.");
                }
                return new LongValue(currentProviderId);

            }

            // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
            @Override
            public boolean ignoreTable(String tableName) {
                return  false;
               // return "t_sys_user".equalsIgnoreCase(tableName);
            }
        }));
        return interceptor;
    }

}

4.在登录的时候获取到用户的租户ID,并缓存到第二步的类里面,提供给第三步的mybatis-plus的配置类使用,本人是整合在spring security里面,所以在用户的实现类的loadUserByUsername方法里面,获取到用户信息后,把租户id缓存到第二步的类里面。如下配置:

@Service
public class UserServiceImpl extends BaseServiceImpl<User, Long>
    implements UserService, UserDetailsService {

  @Autowired UserDao userDao;
  @Autowired
  private ApiContext apiContext;

  @Override
  public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
    User user = userDao.getUserByName(userName);
    apiContext.setCurrentTenantId(user.getTenantId());
    if (user == null) {
      throw new UsernameNotFoundException("账户不存在");
    }
    Set<String> permissions = userDao.getPermissionByUserId(user.getUserId());
    user.setPermissions(permissions);
    return user;
  }
 }

5.需要单独排除登录的接口不需要拼接租户id。因为登录之前是无法获取到租户id的,只有在登录成功后,才能获取到租户id,并进行租户id的缓存。所以要在mapper的方法里面的登录方法上,加一个过滤方法的注释@InterceptorIgnore(tenantLine = “1”)。如下:

@Mapper
public interface UserDao extends BaseDao<User, Long> {

  @InterceptorIgnore(tenantLine = "1")
  User getUserByName(String userName);
}

完成以上5个步骤,就可以实现多租户的功能。

6.mybatis-plus版本说明:

 <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3.4</version>
</dependency>

7.期间遇到个问题:

1.使用mybatis-plus的自带的分页的方法查询分页,count(*)的sql统计语句无法拼接上租户id。只有自己实现的分页方法才自动拼接上租户id。(如果路过的高手知道怎么实现,请多多指点)。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值