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

news2024/12/23 12:30:56

文章目录

    • 连表查询
    • 逻辑删除
    • 多租户

连表查询

引入 mybatis-plus-join-boot-starter 依赖

<dependency>
    <groupId>com.github.yulichang</groupId>
    <artifactId>mybatis-plus-join-boot-starter</artifactId>
    <version>1.5.1</version>
</dependency>

继承 MPJBaseMapper

public interface RoleMapper extends MPJBaseMapper<Role> {}

public interface UserMapper extends MPJBaseMapper<User> {
    public UserRole findUserRole();
}

观察源码,MPJBaseMapper 继承了 BaseMapper,并添加了连表查询的功能。

public interface MPJBaseMapper<T> extends BaseMapper<T>, JoinMapper<T> {}

定义一个 JavaBean 存储用户和角色信息的组合。包含用户 ID (id)、角色 ID (rid)、用户名 (userName) 和角色名 (roleName)。

@Data
public class UserRole {
    private Integer id;
    private Integer rid;
    private String userName;
    private String roleName;
}

分别通过 MPJLambdaWrapperMPJQueryWrapper 来构建查询,获取包含用户和角色信息的 UserRole 对象列表。

@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    @Test
    public void mpj(){
        MPJLambdaWrapper<User> lambdaWrapper = JoinWrappers.lambda(User.class)
            .select(User::getId,User::getUserName)
            .selectAs(Role::getId,UserRole::getRid)
            .select(Role::getRoleName)
            .leftJoin(Role.class,Role::getId,User::getUserRole);
        List<UserRole> userRoleList = userMapper.selectJoinList(UserRole.class, lambdaWrapper);
        userMapper.findUserRole();
    }
}
MPJQueryWrapper<User> queryWrapper = new MPJQueryWrapper<>();
queryWrapper.select("t.id,userName,t1.id as rid,roleName");
queryWrapper.leftJoin("smbms_role t1 on t.userRole = t1.id");
List<UserRole> userRoleList = userMapper.selectJoinList(UserRole.class, queryWrapper);
userMapper.findUserRole();

以上两种方式较少使用,最常用的还是使用 XML 文件来编写 SQL 语句

这种方式可以提供更细粒度的控制,并且使得 SQL 语句更加集中和清晰。

<mapper namespace="com.hz.mapper.UserMapper">
    <select id="findUserRole" resultType="com.hz.pojo.dto.userRole">
        SELECT t.id,userName,t1.id as rid,roleName
        FROM smbms_user t LEFT JOIN smbms_role t1 on t.userRole = t1.id
    </select>
</mapper>
@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    @Test
    public void mpj(){
        userMapper.findUserRole();
    }
}

逻辑删除

逻辑删除是一种优雅的数据管理策略,它通过在数据库中标记记录为“已删除”而非物理删除,来保留数据的历史痕迹,同时确保查询结果的整洁性。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 的全局逻辑删除属性。

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: isdeleted # 全局逻辑删除字段名
      logic-delete-value: 1 # 逻辑已删除值
      logic-not-delete-value: 0 # 逻辑未删除值

2、使用 @TableLogic 注解

在这里插入图片描述

确保数据库表中已添加了逻辑删除字段,并设置默认值为逻辑未删除值。在实体类中,对应数据库表的逻辑删除字段上添加 @TableLogic 注解。

import com.baomidou.mybatisplus.annotation.TableLogic;
public class User {
    @TableLogic
    private Integer isdeleted;
}

3、测试

@SpringBootTest
public class ProviderServiceImplTest {
    @Resource
    private ProviderService providerService;
    @Test
    public void find() {
        QueryWrapper<Provider> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "proCode", "proDesc", "proName");
        providerService.list(queryWrapper);
    }
}
JDBC Connection [HikariProxyConnection@23218037 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e7f19b4] will not be managed by Spring
==>  Preparing: SELECT id,proCode,proDesc,proName FROM smbms_provider WHERE isdeleted=0
==> Parameters: 
<==    Columns: id, proCode, proDesc, proName
<==    ...

再测试一下删除

@Test
public void delete() {
    providerService.removeById(17);
}
==>  Preparing: UPDATE smbms_provider SET isdeleted=1 WHERE id=? AND isdeleted=0
==> Parameters: 17(Integer)
<==    Updates: 1

公共实体类(Common Entity Class)将多个实体类共有的属性和行为封装到一个单独的类中。

@Data
@TableName("smbms_provider")
public class Provider extends BaseEntity{
	// 数据库自增 ID 作为主键
	@TableId(value = "id", type = IdType.AUTO)
	private Integer id;   //id
	private String proCode; //供应商编码
	// 映射到数据库字段 proName as pname
	@TableField("proName")
	private String pname; //供应商名称
	private String proDesc; //供应商描述
	private String proContact; //供应商联系人
	private String proPhone; //供应商电话
	private String proAddress; //供应商地址
	private String proFax; //供应商传真
}
@Data
public class BaseEntity implements Serializable {
    private Integer createdBy; //创建者
    private String creationDate; //创建时间
    private Integer modifyBy; //更新者
    private String modifyDate;//更新时间
    @TableLogic
    private Integer isdeleted;
}

多租户

MyBatis-Plus 提供了 TenantLineInnerInterceptor 插件用于实现多租户的数据隔离。通过这个插件,可以确保每个租户只能访问自己的数据,从而实现数据的安全隔离。

关键属性

属性名类型默认值描述
tenantLineHandlerTenantLineHandler租户处理器( TenantId 行级 )

TenantLineHandler 接口定义了以下方法:

public interface TenantLineHandler {

    /**
     * 获取租户 ID 值表达式,只支持单个 ID 值
     *
     * @return 租户 ID 值表达式
     */
    Expression getTenantId();

    /**
     * 获取租户字段名
     * 默认字段名叫: tenant_id
     *
     * @return 租户字段名
     */
    default String getTenantIdColumn() {
        return "tenant_id";
    }

    /**
     * 根据表名判断是否忽略拼接多租户条件
     * 默认都要进行解析并拼接多租户条件
     *
     * @param tableName 表名
     * @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
     */
    default boolean ignoreTable(String tableName) {
        return false;
    }

    /**
     * 忽略插入租户字段逻辑
     *
     * @param columns        插入字段
     * @param tenantIdColumn 租户 ID 字段
     * @return
     */
    default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
        return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));
    }
}

使用一下,先定义一个实现类 CustomTenantHandle 继承 TenantLineHandler 接口

@Component
public class CustomTenantHandler implements TenantLineHandler {
    @Override
    public Expression getTenantId() {
        int userId = 2;
        return new LongValue(userId);
    }
    @Override
    public String getTenantIdColumn() {
        return "createdBy";
    }
    @Override
    public boolean ignoreTable(String tableName) {
        return false; // 拼接
    }
}

多租户数据库中,不同租户的数据存储在同一张表中,通过租户ID来区分。分页查询结合租户过滤条件可以确保每个租户只能访问到自己的数据。另外,分页也可以限制查询结果的数量,从而提高查询效率。

@Configuration
public class MybatisPlusConfig {
    @Resource
    private CustomTenantHandler customTenantHandler;
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 多租户
        TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
        tenantInterceptor.setTenantLineHandler(customTenantHandler);
        interceptor.addInnerInterceptor(tenantInterceptor);
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

测试一下

@Test
public void findpage() {
    Page<Provider> page = new Page<>(1,5);
    providerService.page(page,null);
}

查询到 4 条记录

JDBC Connection [HikariProxyConnection@227080339 wrapping com.mysql.cj.jdbc.ConnectionImpl@259b85d6] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM smbms_provider WHERE isdeleted = 0 AND smbms_provider.createdBy = 2
==> Parameters: 
<==    Columns: total
<==        Row: 4
<==      Total: 1
==>  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 ?
==> Parameters: 5(Long)
<== ...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2264226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【java面向对象编程】第七弹----Object类、类变量与类方法

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;javase &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 一、Object类 1.1equa…

【常微分方程讲义1.1】方程的种类发展与完备

方程在数学历史中不断发展&#xff0c;逐步趋于完备。从最初的简单代数方程到包含函数、算子甚至泛函的更复杂方程&#xff0c;数学家通过不断的扩展和深化&#xff0c;逐渐建立起更为丰富和多元的方程类型体系。方程的种类之所以不断演变&#xff0c;部分是因为解决实际问题的…

《庐山派从入门到...》板载按键启动!

《庐山派从入门到...》板载按键启动&#xff01; 《庐山派从入门到...》板载按键启动&#xff01; 视频内容大致如下 我们之前了解了GPIO的输出模式使用方法&#xff0c;并且成功点灯&#xff0c;很明显本篇要来分享的自然是GPIO的输入模式 正好回顾一下之前学的python基础包…

Android笔试面试题AI答之Android基础(3)

文章目录 1.谈一谈 Android 的安全机制一、系统架构层面的安全设计二、核心安全机制三、其他安全机制与措施 2.Android 的四大组件是哪四大&#xff1f;3.Android 的四大组件都需要在清单文件中注册吗&#xff1f;4.介绍几个常用的Linux命令一、文件和目录管理二、用户和权限管…

黑马商城项目—服务调用

使用起因 之前我们完成了拆分购物车模块,但当我们进行测试查询时: 我们注意到&#xff0c;其中与商品有关的几个字段中:最新状态和库存为默认值,最新价格为空&#xff01;这就是因为我们注释掉了查询购物车时&#xff0c;查询商品信息的相关代码。 那么&#xff0c;我们该如何…

RK356x bsp 5 - 海华AW-CM358SM Wi-Fi/Bt模组调试记录

文章目录 1、环境介绍2、目标3、海华AW-CM358SM3.1、基本信息3.2、支持SDIO3.03.3、电气特性 4、适配流程步骤5、让SDIO控制器工作&#xff0c;且可以扫到WIFI卡5.1、dts配置5.2、验证 6、Wi-Fi 适配6.1、dts配置6.2、驱动移植6.2.1、kernel menuconfig6.2.2、传统驱动移植6.2.…

VBA编程:自定义函数 - 字符串转Hex数据

目录 一、自定义函数二、语法将字符串转换为hex数据MID函数:返回一个字符串中指定位置和长度的子串LEN函数:返回一个字符串的长度(字符数)Asc函数三、定义变量和数据类型变量声明的基本语法常见的数据类型四、For循环基本语法五、&运算符一、自定义函数 定义:用户定义…

工业大数据分析算法实战-day12

文章目录 day12时序分解STL&#xff08;季节性趋势分解法&#xff09;奇异谱分析&#xff08;SSA&#xff09;经验模态分解&#xff08;EMD&#xff09; 时序分割ChangpointTreeSplitAutoplait有价值的辅助 时序再表征 day12 今天是第12天&#xff0c;昨天主要是针对信号处理算…

基于Python Scrapy的豆瓣Top250电影爬虫程序

Scrapy安装 Python实现一个简单的爬虫程序&#xff08;爬取图片&#xff09;_python简单扒图脚本-CSDN博客 创建爬虫项目 创建爬虫项目&#xff1a; scrapy startproject test_spider 创建爬虫程序文件&#xff1a; >cd test_spider\test_spider\spiders >scrapy g…

【Linux系统编程】:信号(2)——信号的产生

1.前言 我们会讲解五种信号产生的方式: 通过终端按键产生信号&#xff0c;比如键盘上的CtrlC。kill命令。本质上是调用kill()调用函数接口产生信号硬件异常产生信号软件条件产生信号 前两种在前一篇文章中做了介绍&#xff0c;本文介绍下面三种. 2. 调用函数产生信号 2.1 k…

腾讯云智能结构化OCR:以多模态大模型技术为核心,推动跨行业高效精准的文档处理与数据提取新时代

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大三学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

内核执行时动态的vmlinux的反汇编解析方法及static_branch_likely机制

一、背景 在之前的博客里&#xff0c;我们讲到了tracepoint&#xff08;内核tracepoint的注册回调及添加的方法_tracepoint 自定义回调-CSDN博客&#xff09;和kprobe&#xff08;获取任意一个进程的共享内存的fd对应的资源&#xff0c;增加引用&#xff0c;实现数据的接管——…

Burp与其他安全工具联动及代理设置教程

Burp Suite 是一款功能强大的 Web 安全测试工具&#xff0c;其流量拦截和调试功能可以与其他安全工具&#xff08;如 Xray、Yakit、Goby 等&#xff09;实现联动&#xff0c;从而提升渗透测试的效率。本文将详细讲解 Burp 与其他工具联动的原理以及代理设置的操作方法&#xff…

Git配置公钥步骤

GIt公钥的配置去除了git push输入账号密码的过程&#xff0c;简化了push流程。 1.生成SSH公钥和私钥 ssh-keygen -t rsa -b 4096 -C “your_emailexample.com” 遇到的所有选项都按回车按默认处理。获得的公钥私钥路径如下&#xff1a; 公钥路径 : ~/.ssh/id_rsa.pub 私钥路径…

【蓝桥杯选拔赛真题96】Scratch风车旋转 第十五届蓝桥杯scratch图形化编程 少儿编程创意编程选拔赛真题解析

目录 scratch风车旋转 一、题目要求 编程实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、python资料 scratc…

未来 AI 在企业应用中的重心

1. LLM 中精度、参数、数据、性能、以及成本之间的权衡是什么&#xff1f; 在大型语言模型&#xff08;LLM, Large Language Models&#xff09;中&#xff0c;精度、参数数量、训练数据量、性能和成本之间的权衡是一个复杂且多维度的问题。以下是这些因素之间关系的简要分析&…

Docker 安装 禅道-21.2版本-外部数据库模式

Docker 安装系列 1、拉取最新版本&#xff08;zentao 21.2&#xff09; [rootTseng ~]# docker pull hub.zentao.net/app/zentao Using default tag: latest latest: Pulling from app/zentao 55ab1b300d4b: Pull complete 6b5749e5ef1d: Pull complete bdccb03403c1: Pul…

排序算法 (插入,选择,冒泡,希尔,快速,归并,堆排序)

排序:经常在算法题中作为一个前置操作,为了之后的贪心or else做个铺垫,虽然我们经常都只是调用个sort,但是了解一些排序算法可以扩充下知识库 排序的分类: 从存储设备角度&#xff1a; ✓ 内排序&#xff1a;在排序过程中所有数据元素都在内存中&#xff1b; ✓ 外排序&a…

web复习(五)

一、补零 二、打印出五行五列的星星 三、用户输入行数和列数并打印相应行数和列数的⭐ 四、打印倒三角星星&#xff08;第一行一个&#xff0c;第二行两个...&#xff0c;以此类推&#xff09; 五、用户输入秒数&#xff0c;可以自动转换为时分秒 六、随机点名