【Mybatis-Plus】连表查询 逻辑删除 多租户

[复制链接]
发表于 2025-11-23 22:46:57 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
连表查询

引入 mybatis-plus-join-boot-starter 依靠
  1. <dependency>
  2.     <groupId>com.github.yulichang</groupId>
  3.     <artifactId>mybatis-plus-join-boot-starter</artifactId>
  4.     <version>1.5.1</version>
  5. </dependency>
复制代码
继承 MPJBaseMapper
  1. public interface RoleMapper extends MPJBaseMapper<Role> {}
  2. public interface UserMapper extends MPJBaseMapper<User> {
  3.     public UserRole findUserRole();
  4. }
复制代码
观察源码,MPJBaseMapper 继承了 BaseMapper,并添加了连表查询的功能。
  1. public interface MPJBaseMapper<T> extends BaseMapper<T>, JoinMapper<T> {}
复制代码
界说一个 JavaBean 存储用户和脚色信息的组合。包罗用户 ID (id)、脚色 ID (rid)、用户名 (userName) 和脚色名 (roleName)。
  1. @Data
  2. public class UserRole {
  3.     private Integer id;
  4.     private Integer rid;
  5.     private String userName;
  6.     private String roleName;
  7. }
复制代码
分别通过 MPJLambdaWrapper 和 MPJQueryWrapper 来构建查询,获取包罗用户和脚色信息的 UserRole 对象列表。
  1. @SpringBootTest
  2. class UserMapperTest {
  3.     @Resource
  4.     private UserMapper userMapper;
  5.     @Test
  6.     public void mpj(){
  7.         MPJLambdaWrapper<User> lambdaWrapper = JoinWrappers.lambda(User.class)
  8.             .select(User::getId,User::getUserName)
  9.             .selectAs(Role::getId,UserRole::getRid)
  10.             .select(Role::getRoleName)
  11.             .leftJoin(Role.class,Role::getId,User::getUserRole);
  12.         List<UserRole> userRoleList = userMapper.selectJoinList(UserRole.class, lambdaWrapper);
  13.         userMapper.findUserRole();
  14.     }
  15. }
复制代码
  1. MPJQueryWrapper<User> queryWrapper = new MPJQueryWrapper<>();
  2. queryWrapper.select("t.id,userName,t1.id as rid,roleName");
  3. queryWrapper.leftJoin("smbms_role t1 on t.userRole = t1.id");
  4. List<UserRole> userRoleList = userMapper.selectJoinList(UserRole.class, queryWrapper);
  5. userMapper.findUserRole();
复制代码
以上两种方式较少使用,最常用的照旧使用 XML 文件来编写 SQL 语句
这种方式可以提供更细粒度的控制,而且使得 SQL 语句更加会合和清晰。
  1. <mapper namespace="com.hz.mapper.UserMapper">
  2.     <select id="findUserRole" resultType="com.hz.pojo.dto.userRole">
  3.         SELECT t.id,userName,t1.id as rid,roleName
  4.         FROM smbms_user t LEFT JOIN smbms_role t1 on t.userRole = t1.id
  5.     </select>
  6. </mapper>
复制代码
  1. @SpringBootTest
  2. class UserMapperTest {
  3.     @Resource
  4.     private UserMapper userMapper;
  5.     @Test
  6.     public void mpj(){
  7.         userMapper.findUserRole();
  8.     }
  9. }
复制代码
逻辑删除

逻辑删除是一种优雅的数据管理计谋,它通过在数据库中标志纪录为“已删除”而非物理删除,来生存数据的汗青陈迹,同时确保查询结果的整齐性。MyBatis-Plus 提供了便捷的逻辑删除支持,使得这一计谋的实验变得简单高效。
MyBatis-Plus 的逻辑删除功能会在实验数据库利用时自动处置处罚逻辑删除字段。以下是它的工作方式:


  • 插入:逻辑删除字段的值不受限定。
  • 查找:自动添加条件,过滤掉标志为已删除的纪录。
  • 更新:防止更新已删除的纪录。
  • 删除:将删除利用转换为更新利用,标志纪录为已删除。
比方:


  • 删除:update user set deleted=1 where id = 1 and deleted=0
  • 查找:select id,name,deleted from user where deleted=0
使用步调
1、设置全局属性
在 application.yml 中设置 MyBatis-Plus 的全局逻辑删除属性。
  1. mybatis-plus:
  2.   global-config:
  3.     db-config:
  4.       logic-delete-field: isdeleted # 全局逻辑删除字段名
  5.       logic-delete-value: 1 # 逻辑已删除值
  6.       logic-not-delete-value: 0 # 逻辑未删除值
复制代码
2、使用 @TableLogic 注解

确保数据库表中已添加了逻辑删除字段,并设置默认值为逻辑未删除值。在实体类中,对应数据库表的逻辑删除字段上添加 @TableLogic 注解。
  1. import com.baomidou.mybatisplus.annotation.TableLogic;
  2. public class User {
  3.     @TableLogic
  4.     private Integer isdeleted;
  5. }
复制代码
3、测试
  1. @SpringBootTest
  2. public class ProviderServiceImplTest {
  3.     @Resource
  4.     private ProviderService providerService;
  5.     @Test
  6.     public void find() {
  7.         QueryWrapper<Provider> queryWrapper = new QueryWrapper<>();
  8.         queryWrapper.select("id", "proCode", "proDesc", "proName");
  9.         providerService.list(queryWrapper);
  10.     }
  11. }
复制代码
  1. JDBC Connection [HikariProxyConnection@23218037 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e7f19b4] will not be managed by Spring
  2. ==>  Preparing: SELECT id,proCode,proDesc,proName FROM smbms_provider WHERE isdeleted=0
  3. ==> Parameters:
  4. <==    Columns: id, proCode, proDesc, proName
  5. <==    ...
复制代码
再测试一下删除
  1. @Test
  2. public void delete() {
  3.     providerService.removeById(17);
  4. }
复制代码
  1. ==>  Preparing: UPDATE smbms_provider SET isdeleted=1 WHERE id=? AND isdeleted=0
  2. ==> Parameters: 17(Integer)
  3. <==    Updates: 1
复制代码
公共实体类(Common Entity Class)将多个实体类共有的属性和举动封装到一个单独的类中。
  1. @Data
  2. @TableName("smbms_provider")
  3. public class Provider extends BaseEntity{
  4.         // 数据库自增 ID 作为主键
  5.         @TableId(value = "id", type = IdType.AUTO)
  6.         private Integer id;   //id
  7.         private String proCode; //供应商编码
  8.         // 映射到数据库字段 proName as pname
  9.         @TableField("proName")
  10.         private String pname; //供应商名称
  11.         private String proDesc; //供应商描述
  12.         private String proContact; //供应商联系人
  13.         private String proPhone; //供应商电话
  14.         private String proAddress; //供应商地址
  15.         private String proFax; //供应商传真
  16. }
复制代码
  1. @Data
  2. public class BaseEntity implements Serializable {
  3.     private Integer createdBy; //创建者
  4.     private String creationDate; //创建时间
  5.     private Integer modifyBy; //更新者
  6.     private String modifyDate;//更新时间
  7.     @TableLogic
  8.     private Integer isdeleted;
  9. }
复制代码
多租户

MyBatis-Plus 提供了 TenantLineInnerInterceptor 插件用于实现多租户的数据隔离。通过这个插件,可以确保每个租户只能访问自己的数据,从而实现数据的安全隔离。
关键属性
属性名范例默认值形貌tenantLineHandlerTenantLineHandler租户处置处罚器( TenantId 行级 )TenantLineHandler 接口界说了以下方法:
  1. public interface TenantLineHandler {
  2.     /**
  3.      * 获取租户 ID 值表达式,只支持单个 ID 值
  4.      *
  5.      * @return 租户 ID 值表达式
  6.      */
  7.     Expression getTenantId();
  8.     /**
  9.      * 获取租户字段名
  10.      * 默认字段名叫: tenant_id
  11.      *
  12.      * @return 租户字段名
  13.      */
  14.     default String getTenantIdColumn() {
  15.         return "tenant_id";
  16.     }
  17.     /**
  18.      * 根据表名判断是否忽略拼接多租户条件
  19.      * 默认都要进行解析并拼接多租户条件
  20.      *
  21.      * @param tableName 表名
  22.      * @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
  23.      */
  24.     default boolean ignoreTable(String tableName) {
  25.         return false;
  26.     }
  27.     /**
  28.      * 忽略插入租户字段逻辑
  29.      *
  30.      * @param columns        插入字段
  31.      * @param tenantIdColumn 租户 ID 字段
  32.      * @return
  33.      */
  34.     default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
  35.         return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));
  36.     }
  37. }
复制代码
使用一下,先界说一个实现类 CustomTenantHandle 继承 TenantLineHandler 接口
  1. @Component
  2. public class CustomTenantHandler implements TenantLineHandler {
  3.     @Override
  4.     public Expression getTenantId() {
  5.         int userId = 2;
  6.         return new LongValue(userId);
  7.     }
  8.     @Override
  9.     public String getTenantIdColumn() {
  10.         return "createdBy";
  11.     }
  12.     @Override
  13.     public boolean ignoreTable(String tableName) {
  14.         return false; // 拼接
  15.     }
  16. }
复制代码
多租户数据库中,差异租户的数据存储在同一张表中,通过租户ID来区分。分页查询连合租户过滤条件可以确保每个租户只能访问到自己的数据。别的,分页也可以限定查询结果的数量,从而进步查询服从。
  1. @Configuration
  2. public class MybatisPlusConfig {
  3.     @Resource
  4.     private CustomTenantHandler customTenantHandler;
  5.     @Bean
  6.     public MybatisPlusInterceptor mybatisPlusInterceptor() {
  7.         MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  8.         // 多租户
  9.         TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
  10.         tenantInterceptor.setTenantLineHandler(customTenantHandler);
  11.         interceptor.addInnerInterceptor(tenantInterceptor);
  12.         interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  13.         return interceptor;
  14.     }
  15. }
复制代码
测试一下
  1. @Test
  2. public void findpage() {
  3.     Page<Provider> page = new Page<>(1,5);
  4.     providerService.page(page,null);
  5. }
复制代码
查询到 4 条纪录
  1. JDBC Connection [HikariProxyConnection@227080339 wrapping com.mysql.cj.jdbc.ConnectionImpl@259b85d6] will not be managed by Spring
  2. ==>  Preparing: SELECT COUNT(*) AS total FROM smbms_provider WHERE isdeleted = 0 AND smbms_provider.createdBy = 2
  3. ==> Parameters:
  4. <==    Columns: total
  5. <==        Row: 4
  6. <==      Total: 1
  7. ==>  Preparing: SELECT id, proCode, proName AS pname, proDesc, proContact, proPhone, proAddress, proFax, createdBy, creationDate, modifyBy, modifyDate, isdeleted FROM smbms_provider WHERE isdeleted = 0 AND smbms_provider.createdBy = 2 LIMIT ?
  8. ==> Parameters: 5(Long)
  9. <== ...
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表