实现多租户功能可以使用MybatisPlus插件TenantLineInnerInterceptor
,该插件内部通过拦截SQL语句,并在SQL语句中添加租户ID的条件,从而实现多租户数据隔离。
实现步骤
1. 添加MybatisPlus依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本</version>
</dependency>
2. 创建租户实体类
创建一个租户实体类,包含租户ID和租户名称等属性。例如:
@Data
public class Tenant {
private Long id;
private String name;
}
3. 配置多租户拦截器
在MybatisPlusConfig
类中配置多租户拦截器TenantLineInnerInterceptor
。
@Configuration
public class MybatisPlusConfig {
@Autowired
private DataSource dataSource;
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
// 从当前线程中获取租户ID,用于添加到SQL语句中
Long tenantId = TenantContext.getTenantId();
if (tenantId != null) {
return new LongValue(tenantId);
} else {
throw new RuntimeException("获取租户ID失败");
}
}
@Override
public String getTenantIdColumn() {
// 指定对应数据库表中存储租户ID的字段名
return "tenant_id";
}
@Override
public boolean ignoreTable(String tableName) {
// 忽略的表名,如系统公共表,不需要加租户ID条件
return tableName.equalsIgnoreCase("sys_config");
}
}));
return interceptor;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setPlugins(mybatisPlusInterceptor());
return factory.getObject();
}
}
在上面的代码中,我们使用TenantLineInnerInterceptor
作为多租户拦截器,并创建一个TenantLineHandler
实现类,并注入TenantLineInnerInterceptor
中。
在TenantLineHandler
实现类中,我们需要实现3个方法:
getTenantId()
方法:从当前线程中获取租户ID,用于添加到SQL语句中。getTenantIdColumn()
方法:指定对应数据库表中存储租户ID的字段名。ignoreTable()
方法:指定忽略的表名,不需要加租户ID条件。
4. 操作数据库
在具体操作数据库的地方,设置当前线程的租户ID。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void addUser(User user) {
// 设置当前线程的租户ID
TenantContext.setTenantId(user.getTenantId());
userDao.insert(user);
}
@Override
public void deleteUserById(Long userId) {
// 设置当前线程的租户ID
User user = userDao.selectById(userId);
TenantContext.setTenantId(user.getTenantId());
userDao.deleteById(userId);
}
}
在上面的代码中,我们使用TenantContext
类来设置当前线程的租户ID,该类通过ThreadLocal
来实现。
示例说明
示例1
在实际使用中,我们可以将多租户拦截器和DataSource
配置文件分离,单独在application.yml
中进行配置。
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT%2B8
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
type-aliases-package: com.example.demo.entity
global-config:
db-config:
id-type: auto
banner: false
configuration:
map-underscore-to-camel-case: true
interceptor:
- com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor
tenant-line:
tenant-id-column: tenant_id
ignore-tables: sys_config
在上面的配置中,我们使用interceptor
和tenant-line
来配置多租户拦截器,并指定租户ID字段名和忽略的表名。
示例2
在使用多租户拦截器时,由于需要从当前线程中获取租户ID,因此需要先在UserServiceImpl
类中设置当前线程的租户ID。例如:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void addUser(User user) {
TenantContext.setTenantId(user.getTenantId());
userDao.insert(user);
}
@Override
public void deleteUserById(Long userId) {
User user = userDao.selectById(userId);
TenantContext.setTenantId(user.getTenantId());
userDao.deleteById(userId);
}
}
在这种情况下,如果其他线程并发访问时可能导致租户ID被覆盖,从而出现数据错误。因此,我们可以使用AOP技术,在调用业务方法前动态设置租户ID。例如:
@Aspect
@Component
public class TenantAspect {
@Before("execution(* com.example.demo.service.*.*(..)) && args(param,..)")
public void before(JoinPoint joinPoint, Object param) {
if (param instanceof TenantSupport) {
Long tenantId = ((TenantSupport) param).getTenantId();
TenantContext.setTenantId(tenantId);
} else {
throw new RuntimeException("实现接口TenantSupport才能使用多租户功能");
}
}
}
在上面的代码中,我们定义了一个AOP切面,在调用业务方法前动态解析TenantSupport
接口中的租户ID,并设置到当前线程中。
使用该方式时,需要让需要实现多租户的实体类实现TenantSupport
接口,例如:
@Data
public class User implements TenantSupport {
private Long id;
private String username;
private String password;
private Long tenantId;
}
在上面的代码中,我们让User
实体类实现了TenantSupport
接口,并在其中添加了tenantId
属性作为租户ID。在实际使用中,通过设置该属性的值来动态设置租户ID。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:基于MybatisPlus插件TenantLineInnerInterceptor实现多租户功能 - Python技术站