mybatisPlus多租户LambdaQueryWrapper方法
时间: 2023-11-14 17:12:38 浏览: 266
MybatisPlus是一个优秀的ORM框架,它提供了LambdaQueryWrapper方法来进行多租户查询。LambdaQueryWrapper是MybatisPlus提供的一个查询构造器,它可以通过Lambda表达式来构建查询条件,从而实现多租户查询。在LambdaQueryWrapper中,我们可以使用eq方法来设置查询条件,例如:lambdaQueryWrapper.eq(Entity::getTenantId,"tenantId"),其中Entity::getTenantId表示实体类中的租户ID属性,"tenantId"表示租户ID的值。这样就可以查询出租户ID为"tenantId"的记录了。同时,LambdaQueryWrapper也支持多表关联查询,可以通过join方法来实现。需要注意的是,MybatisPlus官网并不支持多表关联查询,但是可以通过第三方插件或者手写SQL来实现。如果需要补充信息,可以使用LambdaQueryWrapper的链式调用来添加查询条件,例如:wrapper.eq(ArticleType::getId,vo.getType())。
相关问题
帮我实现MybatisPlus LambdaQueryWrapper 动态切换 schema
<think>好的,用户想用MyBatis Plus的LambdaQueryWrapper动态切换schema。首先,我得回忆一下MyBatis Plus的相关知识。LambdaQueryWrapper通常用于构建类型安全的查询条件,但默认情况下,它可能不支持动态切换schema,因为schema通常是在数据源配置或者实体类注解里写死的。
那动态切换schema有哪些方法呢?可能的方法包括在实体类上动态设置schema,或者直接修改SQL语句中的表名前缀。不过,实体类的注解通常是静态的,所以可能需要用其他方式动态改变。比如,通过AOP拦截,在执行查询前修改表名。
另外,MyBatis Plus提供了表名处理器TableNameHandler,可以用来动态处理表名。但具体怎么结合LambdaQueryWrapper使用呢?可能需要自定义一个TableNameHandler,然后根据当前上下文条件,在生成SQL时替换表名前缀为指定的schema。
用户可能遇到的问题是如何在运行时根据不同的条件切换不同的schema。比如,多租户场景,每个租户有不同的schema。这时候,每次查询前需要动态设置schema,而不需要修改实体类或Mapper的注解。
可能的步骤是:首先,创建一个自定义的TableNameHandler,并注册到MyBatis Plus的配置中。然后,在每次执行查询前,通过某种方式(比如ThreadLocal)设置当前的schema,这样在生成SQL时,TableNameHandler会自动将表名替换为schema.table的形式。
需要注意的是,这种方法可能会影响所有使用该TableNameHandler的表,所以需要确保只在需要切换schema的地方使用。另外,还要处理可能的线程安全问题,比如用ThreadLocal来保存当前schema,避免多线程之间的干扰。
还有,用户可能希望保持LambdaQueryWrapper的链式调用,所以需要找到一个不影响现有代码结构的方法。也许可以通过在LambdaQueryWrapper外部设置schema,或者在Wrapper构建过程中动态添加schema信息。
另外,还可以考虑在数据源层面切换不同的schema,但这样可能需要多个数据源配置,可能比较复杂。相比之下,动态修改表名前缀可能更轻量。
总结下来,可能的解决方案是:通过自定义TableNameHandler,动态修改表名,添加schema前缀,结合ThreadLocal来在运行时切换不同的schema。这样用户在使用LambdaQueryWrapper时,只需要设置当前schema,后续生成的SQL会自动带上对应的schema前缀。</think>在 MyBatis-Plus 中实现 LambdaQueryWrapper 动态切换 Schema(数据库模式)的核心思路是 **动态修改表名**,以下是具体实现步骤:
---
### 一、问题分析
MyBatis-Plus 默认通过实体类注解 `@TableName` 或全局配置读取表名,但 Schema 通常是静态配置的。动态切换 Schema 需实现以下能力:
1. 运行时动态识别目标 Schema
2. 在生成的 SQL 语句中拼接 `schema.table` 格式
---
### 二、解决方案
#### 方法 1:通过自定义 TableNameHandler(推荐)
通过拦截表名生成过程,动态添加 Schema 前缀。
##### 实现步骤:
1. **创建 Schema 上下文工具类**
用于存储当前线程的 Schema 信息
```java
public class SchemaContext {
private static final ThreadLocal<String> SCHEMA_HOLDER = new ThreadLocal<>();
public static void setSchema(String schema) {
SCHEMA_HOLDER.set(schema);
}
public static String getSchema() {
return SCHEMA_HOLDER.get();
}
public static void clear() {
SCHEMA_HOLDER.remove();
}
}
```
2. **自定义 TableNameHandler**
继承 `TableNameHandler` 并重写动态表名逻辑
```java
public class DynamicSchemaTableNameHandler implements TableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
String schema = SchemaContext.getSchema();
if (StringUtils.isNotBlank(schema)) {
return schema + "." + tableName; // 输出 schema.table
}
return tableName; // 默认无 schema
}
}
```
3. **配置 MyBatis-Plus 注入处理器**
在配置类中注册自定义处理器
```java
@Configuration
public class MybatisPlusConfig {
@Bean
public DynamicSchemaTableNameHandler dynamicSchemaTableNameHandler() {
return new DynamicSchemaTableNameHandler();
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
List<TableNameHandler> handlers = new ArrayList<>();
handlers.add(dynamicSchemaTableNameHandler());
configuration.setTableNameHandlerChain(handlers);
};
}
}
```
4. **使用 LambdaQueryWrapper 动态切换 Schema**
```java
// 设置当前线程 Schema
SchemaContext.setSchema("tenant_001");
// 执行查询(自动拼接 schema)
List<User> users = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.eq(User::getStatus, 1)
);
// 清理上下文(重要!)
SchemaContext.clear();
```
---
#### 方法 2:动态修改实体类注解(需谨慎)
通过反射临时修改 `@TableName` 的 `schema` 属性(**注意线程安全问题**)
```java
public class TableNameUtils {
public static void setSchema(Class<?> entityClass, String schema) {
TableName tableName = entityClass.getAnnotation(TableName.class);
InvocationHandler handler = Proxy.getInvocationHandler(tableName);
try {
Field memberValues = handler.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map<String, Object> values = (Map<String, Object>) memberValues.get(handler);
values.put("schema", schema);
} catch (Exception e) {
throw new RuntimeException("动态修改 @TableName 失败", e);
}
}
}
// 使用示例
TableNameUtils.setSchema(User.class, "tenant_002");
userMapper.selectList(Wrappers.lambdaQuery());
```
---
### 三、注意事项
1. **线程安全**
- 使用 `ThreadLocal` 存储 Schema 避免多线程污染
- 务必在 `finally` 块中调用 `SchemaContext.clear()`
2. **全局影响**
- 方法 1 会影响所有使用该 TableNameHandler 的实体类
- 如需精准控制,可在 `DynamicSchemaTableNameHandler` 中添加表名过滤逻辑
3. **性能**
- 动态表名解析对性能影响极小,可放心使用
---
### 四、最佳实践
```java
try {
// 动态设置 Schema
SchemaContext.setSchema("tenant_001");
// 复杂查询示例
List<User> users = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.eq(User::getRole, "admin")
.between(User::getCreateTime, startDate, endDate)
.orderByAsc(User::getId)
);
} finally {
SchemaContext.clear(); // 确保清理
}
```
通过以上方案,您可以在不修改原有业务代码的前提下,实现 LambdaQueryWrapper 的动态 Schema 切换能力。
mybatisplus分页查询数据权限
### MyBatis Plus 中带有数据权限的分页查询实现
在MyBatis Plus中,分页查询通过集成内置的分页插件来简化操作。为了实现带数据权限控制的分页查询,在原有基础上加入逻辑处理以确保只有授权的数据可见。
#### 配置分页插件
首先需配置分页拦截器`PaginationInterceptor`以便支持分页功能:
```java
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
```
此配置允许应用程序利用MyBatis Plus提供的便捷分页特性[^1]。
#### 创建自定义SQL注入器
对于涉及复杂业务场景下的数据权限过滤,则可通过继承`AbstractSqlInjector`来自定义SQL构建过程中的额外校验逻辑。这一步骤并非直接关联到分页本身,而是为后续实施细粒度访问控制做准备。
#### 添加全局租户或部门级联筛选
如果存在多租户或多部门架构需求,可以在每次执行数据库交互前动态附加WHERE子句用于限定特定范围内的资源访问。例如基于当前登录用户的所属单位自动追加分部ID字段匹配条件:
```sql
AND dept_id IN (SELECT id FROM sys_dept WHERE path LIKE concat('%', #{currentDeptPath}, '%'))
```
上述做法能够有效缩小结果集规模从而间接提升性能表现的同时也满足了安全合规的要求。
#### 编写Service层接口及其实现类
针对具体业务对象编写服务接口及其对应的实现方式,这里假设有一个名为User的服务用来获取用户列表信息并应用分页与权限验证机制:
```java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public IPage<UserVO> selectPageVo(Page pageCondition) {
LambdaQueryWrapper<User> queryWrapper = Wrappers.<User>lambdaQuery()
.eq(User::getIsDeleted, false); // 基础查询条件
// 动态拼接更多个性化约束项...
Page<User> resultPage = this.page(pageCondition, queryWrapper);
List<UserVO> voList = BeanUtil.copyToList(resultPage.getRecords(), UserVO.class);
return new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal()).setRecords(voList);
}
}
```
这段代码展示了如何组合使用Lambda表达式构造灵活可变的查询语句以及完成最终的结果映射转换工作流程。
#### Controller 层调用示例
最后是在控制器层面接收前端传入参数并转发给相应的service进行实际运算处理:
```java
@RestController
@RequestMapping("/users")
public class UserController {
private final IUserService userService;
public UserController(IUserService userService){
this.userService = userService;
}
@GetMapping("/page")
public Result<IPage<UserVO>> getUserPages(@RequestParam Integer pageNum,
@RequestParam Integer pageSize) {
Page pageParam = new Page<>(pageNum, pageSize);
IPage<UserVO> userPage = userService.selectPageVo(pageParam);
return Result.success(userPage);
}
}
```
以上即完成了整个从请求解析到最后响应输出全过程的设计思路概述。
阅读全文
相关推荐










