• 22-07-30 西安 MybatisPlus


    让自己变得稀缺的最好的办法就是让自己拥有多维度的能力,这种把多种能力结合在一起,并且为同一个目标服务,就会发挥巨大的价值。                                                  --------《认知红利》


    mybatisplus官网 (MP)对MyBatis进一步简化

    MyBatis-Plus

    1、MybatisPlus入门案例

    创建springboot工程mybatis-plus

    1、核心依赖如下

    1. <dependency>
    2. <groupId>com.baomidougroupId>
    3. <artifactId>mybatis-plus-boot-starterartifactId>
    4. <version>3.3.1version>
    5. dependency>
    6. <dependency>
    7. <groupId>mysqlgroupId>
    8. <artifactId>mysql-connector-javaartifactId>
    9. dependency>

    2、在application.properties配置文件中添加 MySQL 数据库的相关配置

    1. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    2. spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
    3. spring.datasource.username=root
    4. spring.datasource.password=123456

    3、启动类

    在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹

    在mapper文件夹下编写Mapper接口,因为我们已经在启动类上加了@MapperScan注解,所以这里的mapper接口什么都不写也没啥问题,至于@Repository的作用,下面会说

    单元测试

    测试类的包名称要和启动类的包名称一致

    IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。

    为了避免报错,可以在 dao 层 的接口上添加 @Repository 注解

    1. import org.junit.jupiter.api.Test;
    2. @SpringBootTest
    3. public class MybatisPlusApplicationTests {
    4. @Autowired
    5. private UserMapper userMapper;
    6. @Test
    7. public void test1(){
    8. System.out.println(("----- selectAll method test ------"));
    9. //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装
    10. //所以不填写就是无任何条件
    11. List users = userMapper.selectList(null);
    12. users.forEach(System.out::println);
    13. }
    14. }

    测试结果:

    为了观察sql,我们加入以下配置到application.properties

    1. #mybatis日志
    2. mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

    此时再运行,控制台就可打印sql了


    2、MybatisPlus的CRUD

    目前,我们的user表中的字段如下,值得注意的是表的id并不是自增的 

    1.插入操作-主键生成策略

    执行如下代码,会发现很有意思的

    现象:我们在添加user的时候并没有设置id,且数据库的id也不是自增的,但是最后一行的user对象中id却有输出

    1. @Test
    2. public void testInsert(){
    3. User user = new User();
    4. user.setName("谷小姐");
    5. user.setAge(18);
    6. user.setEmail("6665559999@qq.com");
    7. int result = userMapper.insert(user);
    8. System.out.println(result); //影响的行数
    9. System.out.println(user); //id自动回填
    10. }

    控制台打印如下:

    是因为mybatisplus默认实现自动回填id,这个id的生成策略又是什么呢


    主键生成策略:

    可以通过@TableId注解的 type属性来设置主键id的生成策略

    默认使用如下:

    1. @TableId(type = IdType.ASSIGN_ID)
    2. private Long id;

    (1)ASSIGN_ID(雪花算法)

            如果不设置类型值,默认则使用IdType.ASSIGN_ID策略(自3.3.0起)。该策略会使用雪花算法自动生成主键ID,主键类型为长整型或字符串(分别对应的MySQL的表字段为BIGINT和VARCHAR),确保分布式系统的一个唯一id值

    (2)ASSIGN_UUID(排除中划线的UUID)

            如果使用IdType.ASSIGN_UUID策略,并重新自动生成排除中划线 的UUID作为主键。主键类型为String,对应MySQL字段为VARCHAR(32),包含(-)的话UUID是36长度的,(-)占了4个。

    (3)AUTO(数据库ID自增)

            对于像MySQL这样的支持主键自动递增的数据库,我们可以使用IdType.AUTO策略。 但是要求数据库字段是整形

    (4)INPUT(插入前自行设置主键值)

    (5)NONE(无状态)

            如果使用IdType.NONE策略,表示未设置主键类型,使用默认的规则

    ------------------------

    补充:

    雪花算法(SnowFlake)是 Twitter 开源的分布式 id 生成算法,在分布式环境下产生不重复的id。使用一个 64 bit 的 long 型的数字作为全局唯一 id。
    公式: 0 (最高位预留 1位) + 时间戳 (41 位) + 机器 ID (10 位) + 随机序列 (12 位) = 64 bit (long类型的值,最长19个数,例如1545580694141997057)


    2. 更新操作-自动填充-版本号修改

    现象:根据id修改名字的时候,只传了id和名字,拼的是动态sql,只修改名字,并不是完全的覆盖。

    1. @Test
    2. public void testUpdateById(){
    3. User user = new User();
    4. user.setId(1553211962987388930L);
    5. user.setAge(30);
    6. //UPDATE user SET age=? WHERE id=?
    7. int result = userMapper.updateById(user);
    8. System.out.println(result);
    9. }

    自动填充

    项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。

    我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:

    1.在数据库表user表添加datetime类型的新字段create_time、update_time,在实体类也添加对应的属性,命名格式:驼峰命名

    1. import java.util.Date;
    2. @Data
    3. public class User {
    4. ......
    5. //添加
    6. @TableField(fill = FieldFill.INSERT)
    7. private Date createTime;
    8. //@TableField(fill = FieldFill.UPDATE)
    9. //添加修改
    10. @TableField(fill = FieldFill.INSERT_UPDATE)
    11. private Date updateTime;
    12. }

    2.在实体类上对应的属性上添加注解 配置自动填充

    3.实现元对象处理器接口

    MyMetaObjectHandler是随便起的名字,要注意加@Component注解

    1. import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    2. import java.util.Date;
    3. @Component
    4. public class MyMetaObjectHandler implements MetaObjectHandler {
    5. @Override
    6. public void insertFill(MetaObject metaObject) {
    7. //写实体类属性名
    8. this.setFieldValByName("createTime", new Date(), metaObject);
    9. this.setFieldValByName("updateTime", new Date(), metaObject);
    10. }
    11. @Override
    12. public void updateFill(MetaObject metaObject) {
    13. this.setFieldValByName("updateTime", new Date(), metaObject);
    14. }
    15. }

    测试添加功能时自动填充:

    1. @Test
    2. public void testInsert(){
    3. User user = new User();
    4. user.setName("悠米");
    5. user.setAge(19);
    6. user.setEmail("ym_888@qq.com");
    7. int result = userMapper.insert(user);
    8. }

    打印的sql日志

    数据库变化

    测试修改功能的自动填充

    1. @Test
    2. public void testUpdateById(){
    3. User user = new User();
    4. user.setId(1553547529604321282L);
    5. user.setAge(24);
    6. int result = userMapper.updateById(user);
    7. }

    打印sql日志

    数据库变化 ​​​​


    乐观锁-版本号修改

    当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新。

    乐观锁:在高并发的情况下保证数据的安全

    1.数据库表user添加字段version【加默认值0或者使用@TableField,这里使用默认值】

     2.实体类加字段,字段上加注解@Version

    1. @Version
    2. private Integer version;

    3.配置乐观锁的插件

    在 MybatisPlusConfig(自定义配置类) 中注册 OptimisticLockerInterceptor 锁的拦截器, 也可以直接在启动类配置乐观锁的插件。我们选择在配置类中加入乐观锁的插件

    1. @SpringBootConfiguration
    2. public class MybatisPlusConfig {
    3. /**
    4. * 乐观锁插件
    5. */
    6. @Bean
    7. public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    8. return new OptimisticLockerInterceptor();
    9. }
    10. }

    在测试类得这么测试,不要像上面那样直接new一个user,然后修改。一定要先去查询

    1. @Test
    2. public void testUpdateById(){
    3. //查询
    4. User user = userMapper.selectById(1553554443532869633L);
    5. //修改数据
    6. user.setName("Helen Yao");
    7. user.setEmail("helen@qq.com");
    8. //执行更新
    9. userMapper.updateById(user);
    10. }

     实现方式

    1. 取出记录时,获取当前version
    2. 更新时,带上这个version
    3. 执行更新时, set version = newVersion where version = oldVersion
    4. 如果version不对,就更新失败

    看源码可知: Long或者int,版本号则加1  ; 时间类型则返回当前时间


    3.查询(id查询、批量查询、条件查询、分页查询)

    3.1 id查询

    1. @Test
    2. public void testSelectById(){
    3. //根据id查询记录
    4. User user = userMapper.selectById(1L);
    5. System.out.println(user);
    6. }

     3.2 多个id批量查询

    1. @Test
    2. public void testSelectBatchIds(){
    3. //通过多个id批量查询
    4. List users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    5. //java的写法要会
    6. users.forEach(System.out::println);
    7. }

     3.3 map条件查询

    通过map封装查询条件

    注意:map中的key对应的是数据库中的列名。例如数据库user_id,实体类是userId,这时map的key需要填写user_id

    1. @Test
    2. public void testSelectByMap(){
    3. HashMap map = new HashMap<>();
    4. // 列名
    5. map.put("name", "悠米");
    6. map.put("age", 28);
    7. List users = userMapper.selectByMap(map);
    8. users.forEach(System.out::println);
    9. }

    3.4 Page 分页查询

    MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

    在MybatisPlusConfig配置类中加入分页插件配置,也可以写在启动类

    1. /**
    2. * 分页插件
    3. */
    4. @Bean
    5. public PaginationInterceptor paginationInterceptor() {
    6. return new PaginationInterceptor();
    7. }

    测试:最终通过page对象获取相关数据

    查询第一页,每页5条数据

    1. @Test
    2. public void testSelectPage() {
    3. Page page = new Page<>(1,5);
    4. //第二个参数表示查询条件,null表示查询所有
    5. userMapper.selectPage(page, null);
    6. page.getRecords().forEach(System.out::println);
    7. }

    limit m,n          m:从第几条开始   n:查询多少条

    page对象 里还有很多关于分页的信息,列如:

    1. System.out.println(page.getCurrent());//当前第几页
    2. System.out.println(page.getPages());//总页数
    3. System.out.println(page.getSize());//每页显示数量
    4. System.out.println(page.getTotal());//总记录数
    5. System.out.println(page.hasNext());//是否有下一页
    6. System.out.println(page.hasPrevious());//是否有上一页

    4.删除(id删除、批量删除、条件删除、逻辑删除)

    4.1 id删除

    1. @Test
    2. public void testDeleteById(){
    3. //根据id删除 【物理删除】
    4. int result = userMapper.deleteById(1L);
    5. System.out.println(result);
    6. }

     4.2 多个id批量删除

    1. @Test
    2. public void testDeleteBatchIds() {
    3. //多个id批量删除
    4. int result = userMapper.deleteBatchIds(Arrays.asList(3, 4, 5));
    5. System.out.println(result);
    6. }

     4.3 条件查询删除

    1. @Test
    2. public void testDeleteByMap() {
    3. HashMap map = new HashMap<>();
    4. map.put("name", "Helen");
    5. map.put("age", 18);
    6. //简单的条件查询删除
    7. int result = userMapper.deleteByMap(map);
    8. System.out.println(result);
    9. }

    4.4 逻辑删除

    • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
    • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录

    1、数据库表user中添加字段deleted(或者is_deleted)

    2、实体类添加deleted 字段和@TableLogic 注解

    1. @TableLogic
    2. private Integer deleted;

    3、application.properties 配置是否被删除的逻辑值

    此为默认值,如果你的默认值和mp默认的一样,该配置可无。

    1. mybatis-plus.global-config.db-config.logic-delete-value=1
    2. mybatis-plus.global-config.db-config.logic-not-delete-value=0

    测试逻辑删除

    1. /**
    2. * 测试 逻辑删除
    3. */
    4. @Test
    5. public void testLogicDelete() {
    6. int result = userMapper.deleteById(2L);
    7. System.out.println(result);
    8. }

    注意:被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作

    此时再去查询该条数据

    1. @Test
    2. public void testSelectById(){
    3. User user = userMapper.selectById(2L);
    4. System.out.println(user);
    5. }


    3、MybatisPlus的条件构造器

    拼接各种条件(查询条件、删除条件、修改条件


    QueryWrapper

    注意:以下条件构造器的方法入参中的 column 均表示数据库字段

    1、ge、gt、le、lt、isNull、isNotNull

    1. @Test
    2. public void testSelect() {
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. //年龄大于等于25岁的
    5. queryWrapper.ge("age", 25);
    6. List users = userMapper.selectList(queryWrapper);
    7. //int delete = userMapper.delete(queryWrapper);
    8. System.out.println(users);
    9. }

    2、eq、ne

    ne 不等于

    1. @Test
    2. public void testSelectOne() {
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. queryWrapper.eq("name", "飘絮");
    5. //seletOne返回的是一条实体记录,当出现多条时会报错
    6. User user = userMapper.selectOne(queryWrapper);
    7. System.out.println(user);
    8. }

    3、between、notBetween

    1. @Test
    2. public void testSelectCount() {
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. //between 包含大小边界
    5. queryWrapper.between("age", 20, 30);
    6. Integer count = userMapper.selectCount(queryWrapper);
    7. System.out.println(count);
    8. }

    4、like、notLike、likeLeft、likeRight

    1. @Test
    2. public void testLike(){
    3. QueryWrapper userQueryWrapper = new QueryWrapper<>();
    4. // userQueryWrapper.like("name","d");// %d%
    5. // userQueryWrapper.likeLeft("name","d"); //%d
    6. userQueryWrapper.likeRight("name","米"); // d%
    7. List users = userMapper.selectList(userQueryWrapper);
    8. System.out.println(users);
    9. }

    5、orderBy、orderByDesc、orderByAsc

    1. @Test
    2. public void testSelectListOrderBy() {
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. queryWrapper.orderByDesc("age");
    5. List users = userMapper.selectList(queryWrapper);
    6. users.forEach(System.out::println);
    7. }

    6、last

    1. @Test
    2. public void testSelectListLast() {
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. //直接拼接到 sql 的最后,只能调用一次,多次调用以最后一次为准
    5. queryWrapper.last("limit 3");
    6. //sql注入的风险:永远为真的条件
    7. //queryWrapper.last(" or 1=1");
    8. List users = userMapper.selectList(queryWrapper);
    9. users.forEach(System.out::println);
    10. }

    ​​​​​​​

    7、指定要查询的列

    1. @Test
    2. public void testSelectListColumn() {
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. // select 限制返回的字段
    5. queryWrapper.select("id", "name", "age");
    6. List users = userMapper.selectList(queryWrapper);
    7. users.forEach(System.out::println);
    8. }

  • 相关阅读:
    深度学习中的激活函数
    `算法题解` `LibreOJ` #2491. 「BJOI2018」求和
    《多GPU大模型训练与微调手册》
    统一身份认证实现,推广的可能性及优缺点?
    数据结构之快速排序(重点)
    leetcode 654 最大二叉树
    Catheon Gaming任命Activision Blizzard前亚太区负责人Mark Aubrey担任首席执行官
    GUI程序设置
    30天学习之-自动化测试
    oracle 12c GI卸载流程
  • 原文地址:https://blog.csdn.net/m0_56799642/article/details/126072605