MyBatisPlus学习笔记 学习使用看这一篇就够了(一)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MyBatisPlus学习笔记 学习使用看这一篇就够了(一)

四、条件构造器wrapper


相关条件构造器

条件构造器:包含所有条件信息超详细。


eq:=


in:in ()


insql:in (sql)


like:like ‘%xx%’


强烈推荐使用lambda形式来筛选出指定字段!



4.1、QueryWrapper(独有select)



普通筛选字段以及where查询


介绍一下普通绑定属性以及lambda形式(推荐)绑定:


@SpringBootTest
class MybatisplusexerApplicationTests {
    @Resource
    private UserMapper userMapper;
    @Test
    public void select() throws JsonProcessingException {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //静态绑定形式
//        queryWrapper
                .select("last_name","email")
                .eq("age",18);
        //lambda表达式(可动态绑定指定字段)  => SELECT last_name AS lastName,email FROM user WHERE (age = ?)
        queryWrapper.lambda()
                .select(User::getLastName,User::getEmail)
                .eq(User::getAge,18);
        List<User> users = userMapper.selectList(queryWrapper);
        System.out.println(users);
    }
}




子查询配合模糊查询


@Resource
private UserMapper userMapper;
@Test
public void select() throws JsonProcessingException {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    //子查询 and 模糊查询  => SELECT id,last_name AS lastName,age,email FROM user WHERE (age IN (18,22) AND email LIKE ?)  默认多个条件时就是使用and连接!
    queryWrapper.lambda()
        .inSql(User::getAge,"18,22")   //子查询
        .like(User::getEmail,"baomidou.com");  //模糊查询
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}



4.2、UpdateWrapper(独有set方法)


更新指定记录的某个字段


强烈推荐使用lambda表达式:因为只要你对应实体类字段没有写错,就不会有问题其是动态的


@Resource
private UserService userService;
@Test
public void select() throws JsonProcessingException {
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    //采用lambda表达式   => UPDATE user SET last_name=? WHERE (id = ?)
    updateWrapper.lambda()
        .set(User::getLastName,"changlu")  //更新某个字段
        .eq(User::getId,1);
    userService.update(updateWrapper);//配合service层
}



五、全局id生成策略


IdType选项


其他一系列配置


我们可以通过全局yml配置的形式来为主键id设置生成策略:


# mp配置
mybatis-plus:
  global-config:
    db-config:
      id-type: auto  # 全局配置


单表指定字段设置:


@TableId(value = "id",type = IdType.AUTO)


注意点:若是设置id策略为自增,那么一定要在数据库的指定表中的主键id字段勾选自动递增,不然的话mp插入报异常,一定要注意下这个点。




六、逻辑删除


逻辑删除


逻辑删除:在指定的表中添加一个字段默认为1,当我们想要删除该表的某条记录时,将该字段标注为0表示删除,并不是真正的将该条记录给删除掉。


mp中配置逻辑删除十分简单:


表中添加任意一个字段,设置为int类型,默认1表示未删除。

实体类中对该字段添加@TableLogic注解,可以单独对该注解进行配置来描述未删除、已删除分别的状态表示。也可进行全局配置。

配置好注解后,我们使用mp给我们在mapper或service的增强方法的select与remove时都会执行逻辑删除操作!


详细过程


step1:表中添加字段,并且设置默认值为1



step2:可以进行局部配置(针对单表)或者全局配置(针对所有的表)


局部配置


@TableLogic(value = "1",delval = "0")  //局部配置逻辑删除,未删除为1,删除为0
private Integer deleted;


全局配置


mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted   # 配置逻辑删除的字段
      logic-delete-value: 0   # 删除标注1
      logic-not-delete-value: 1  # 未删除标注0


测试:我们来进行删除与查询操作


①插入


@Resource
private UserService userService;
@Test
public void save() throws JsonProcessingException {
    User user = new User(null, "xiaotian", 18, "86547528@qq.com");
    userService.save(user);
}


由于我们对deleted设置了默认值,所以我们插入对应实体类时不设置该字段也会给我们自动添加上默认值1,并且其中id为自动生成。



②删除:也就是逻辑删除,一旦我们配置好@TableLogic执行删除操作就都是逻辑删!


@Test
public void update() throws JsonProcessingException {
    //逻辑删除:删除刚刚新增的记录
    userService.remove(new QueryWrapper<User>().lambda().eq(User::getLastName,"xiaotian"));
}





mp的删除操作底层实际上就是对指定表示字段置为删除状态 ,也就是执行一个更新字段的操作!


③查询:设置逻辑删除的表在进行查询时,就会自动过滤掉已经是删除状态的记录


@Test
public void select() throws JsonProcessingException {
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}



额外说明:一般对于某个表设置逻辑删除时,正常层面业务中是不会执行物理删除操作的,若是想要进行物理删除操作呢,就只能我们来编写xml的sql文件来进行删除了!



七、自动填充


自动填充功能


对于数据库表,我们需要自动填充的字段包含:id, gmt_create, gmt_modified,后两个是创建与更新时间,创建是在插入时自动填充,而更新是在插入与修改时进行更新!


有两种方式来实现自动填充:


①数据库层面


对于日期字段可以设置默认值,在mysql5.7版本可以设置根据当前时间戳更新,而在5.5不能设置时间戳更新。



CURRENT_TIMESTAMP


设置好以后,进行插入或者更新时我们不传参则会进行自动更新时间戳!



②mp提供的自动填充


首先是指定自动填充字段设置注解


@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;


编写handler执行器,在进行插入与更新时来对指定标注了注解执行填充操作,插入时对两个字段都进行填充,更新时只对修改字段进行填充:


@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        //第一个是更新的字段名;第二个为更新的值;第三个为指定的原对象
        this.setFieldValByName("gmtCreate",new Date(),metaObject);
        this.setFieldValByName("gmtModified",new Date(),metaObject);
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.setFieldValByName("gmtModified",new Date(),metaObject);
    }
}



测试:


@Test
public void save() throws JsonProcessingException {
    User user = new User(null, "xiaomei", 20, "86547528@qq.com");
    userService.save(user);
}
@Test
public void update() throws JsonProcessingException {
    User user = new User(null, "mining", 28, "86547528@qq.com");
    userService.saveOrUpdate(user,new QueryWrapper<User>().lambda().eq(User::getLastName,"xiaomei"));
}


下面是更新时的sql打印:



此时我们就通过在代码层面实现了自动填充的效果!而不是直接在数据库上进行操作。



八、执行 SQL 分析打印


执行 SQL 分析打印


效果:




配置


①引入依赖


<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.1</version>
</dependency>


②yml配置数据源:


driver-class-name: com.p6spy.engine.spy.P6SpyDriver  # 设置为p6spy的驱动
# jdbc:p6spy:前缀包含p6spy
url: jdbc:p6spy:mysql://127.0.0.1:3306/mybatisplusExer?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai



③在resource目录下新建spy.properties文件:


#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2



此时就已经配置完成,我们执行sql语句即可出现对应效果!


说明:在生产环境不建议进行分析配置,有可能会影响性能!


再介绍一个IDEA插件:Mybatis-log,对于执行的sql语句会在一个小窗口显示:





九、代码生成器(学习)


代码生成器、代码生成器配置


可看视频:图灵学院—mp的代码生成器讲解


引入依赖:


<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.0</version>
</dependency>
<--  辅助 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>


效果:根据指定的数据库来进行代码自动生成




重点关注这几行:


25行设置指定数据库。
35行指定生成的目录文件名称。
36行指定的包路径,根据对应的项目。
54行,过滤数据库表的前缀,如一些t_blog、t_user我们在生成对应的实体类时是不需要这些前缀的此时就可以进行过滤。
   public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");//默认为当前工程路径,如D:\workspace\workspace_idea\mybatisplusexer
        gc.setOutputDir(projectPath + "/src/main/java");//设置输出文件路径
        gc.setAuthor("changlu");
        gc.setOpen(false);//代码生成是不是要打开文件夹
        gc.setSwagger2(true); //生成Swagger2 注解
        gc.setBaseResultMap(true);//每个mapper文件中都生成通用结果映射集
        gc.setFileOverride(true);//下次生成文件时进行覆盖(不设置的话当进行第二次生成就会在同一个目录产生相同的文件)
//        gc.setEnableCache(true);//开启二级缓存
        //设置对应文件名称
        gc.setEntityName("%sEntity");//生成实体类文件名,如:%sEntity 生成 UserEntity
        gc.setMapperName("%sMapper");//生成dao,这里我们配置生成如 UserMapper
        gc.setMapperName("%sMapper");//生成mapper.xml文件,这里生成如 UserMapper.xml
        gc.setServiceName("%sService");//生成service接口
        gc.setServiceImplName("%sServiceImpl");//生成service实现类
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);
        // 包配置
        PackageConfig pc = new PackageConfig();
        //生成的模块名,也就是生成目标指定文件名的路径下
//        pc.setModuleName(scanner("模块名"));
        pc.setModuleName("blog");
        pc.setParent("com.changlu.mybatisplusexer");
        mpg.setPackageInfo(pc);
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);//表名生成策略:下划线转驼峰,如pms_pro=>PmsPro
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//字段名生成策略,同上,如last_name=>lastName
//        strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        strategy.setEntityLombokModel(true);//支持lombok
//        strategy.setRestControllerStyle(true);//controller中设置@RestController
        // 公共父类
//        strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
        //要生成的表名 多个用逗号分隔   若是想要按照前缀生成多张表:strategy.setLikeTable(new LikeTable("pms_"))
//        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        //默认控制器映射配置为:pms_pro=> controller @ReuqestMapping("/pms/pmsProduct")  也就是/模块名/表名
//        strategy.setControllerMappingHyphenStyle(true);//驼峰转连字符,如pms_pro=> controller @ReuqestMapping("/pms/pms_product")
        //设置表的替换前缀:这里就是起到过滤作用
        strategy.setTablePrefix("t_");
        strategy.setEntityTableFieldAnnotationEnable(true);//生成字段注解
        mpg.setStrategy(strategy);
//        mpg.setTemplateEngine(new FreemarkerTemplateEngine());  //根据默认模板生成
        mpg.execute();
    }



插件

1、乐观锁使用

介绍


乐观锁:一直保持乐观,总认为其他人没有抢占锁,通常使用一个version版本控制字段来保证更新的原子性,保证其是没有被干扰的。对于乐观锁是很容易产生失败的情况,其是非阻塞的。


悲观锁:一直保持着悲观的心态,当操作一个公共资源的前会去拿锁,若是锁已被别人先拿走就会进入阻塞状态,会等待锁释放,一旦释放抢占锁,来进行操作。



实操


step1:给表添加版本控制字段





@Version
private Integer version;


step2:配置插件,在第9行进行添加插件


@Configuration
@MapperScan("com.changlu.mapper")  //使用了分配类,即可将启动器的自动打包放置在这里
public class MybatisPlusConfig {
    // 最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));  //分页插件,设置方言为MySQL
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//添加乐观锁插件
        return interceptor;
    }
}


此时我们就配置完成了,使用也十分简单,当我们想要对某条记录进行修改时,先查询,接着进行更新,mp就会自动为我们实现底层的判断version字段操作。


乐观锁实现过程:


取出记录时,获取当前version。

新时,带上这个version。

执行更新时, set version = newVersion where version = oldVersion。

如果version不对,就更新失败。

若是我们自己手写实现乐观锁应该是这样的:①查询指定字段,将对应version字段也查询出来。②执行更新业务操作。③业务操作完成后进行更新,我们需要额外带上更新判断条件version=xxx,这个xxx就是我们初始查询出来version。


mp中就很简单了,你只要在更新前先查一下拿到对应的实体类(该实体类中有version字段嘛),接着调用mp提供给我们的update方法,我们无需自己去考虑version的判断,mp会自动来为我们进行添加version的更改逻辑。


测试:


@Test
public void test(){
    User user = userMapper.selectById("2");//模拟线程1
    User user2 = userMapper.selectById("2");//模拟线程2
    user.setUserName("xiaoxiao");//线程1执行业务操作
    user2.setUserName("dada");//线程2执行业务操作
    System.out.println(userService.updateById(user));//进行更新字段,此时数据库中该条记录version+1
    System.out.println(userService.updateById(user2));//这里再进行更新时由于version版本不匹配就会失败!
}



总结:


当你某条记录使用update()时,该实体类的内部属性必须要有version字段值,若是为nullnamemp就不会走乐观锁实现了。

我们可以通过自己填充或者查询数据库的方式来得到实体类(推荐使用selectById先拿到),接着调用mp的update()即可实现乐观锁。

注意点:当你在mp中执行update()更新操作时,它会对该实体类的version进行加1,若是重试多次就会依次累加去与数据库比对


@Test
public void test(){
    User user = new User((long)2, "hello", 22, "test",1);
    User user2 = new User((long)2, "hello2", 22, "test",1);
    //进行更新操作(2次)
    boolean b;
    b  = userService.updateById(user);
    user.setVersion(2);
    b  = userService.updateById(user);
    boolean b2;
    int bb2 = 1;
    //进行重试操作:重试时,mp会先将该实体类中的version字段进行自增之后再进行比对数据库(直至插入成功)
    do{
        b2  = userService.updateById(user2);
        System.out.println("bb2:"+bb2+++",user2:"+user2);
    }while(!b2);
}




搞了好久,这里仅仅是用调用接口的角度去推测内部的实现操作,未来之后进行深入源码底层来去探究实现!



实际业务

1、查询指定记录中的某个字段



根据字段查询某条记录的某个字段


@Resource
private UserMapper userMapper;
@Test
public void testSelect() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // SELECT name FROM user WHERE (name = ?)
    queryWrapper.select("name").eq("name","Jone");
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}




查询出除开某个字段其他所有字段



@Resource
private UserMapper userMapper;
@Test
public void testSelect() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // SELECT id,name FROM user  ,省略掉了age与email
    queryWrapper.select(User.class, info -> !info.getColumn().equals("age")
                        && !info.getColumn().equals("email"));
    List<User> users = userMapper.selectList(queryWrapper);
    System.out.println(users);
}



注意:我测试了去除id,最终还是给我查出来id了!可能是主键原因。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
298 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库
|
3月前
|
Java 关系型数据库 数据库连接
mybatis-plus学习
MyBatis-Plus ,MyBatis 最佳搭档,只做增强不做改变,为简化开发、提高效率而生。
51 5
|
4月前
|
安全 Java 数据库连接
后端框架的学习----mybatis框架(3、配置解析)
这篇文章详细介绍了MyBatis框架的核心配置文件解析,包括环境配置、属性配置、类型别名设置、映射器注册以及SqlSessionFactory和SqlSession的生命周期和作用域管理。
后端框架的学习----mybatis框架(3、配置解析)
|
4月前
|
Java 数据库连接 mybatis
后端框架的学习----mybatis框架(9、多对一处理和一对多处理)
这篇文章介绍了在MyBatis框架中如何处理多对一和一对多的关联查询,通过定义`<resultMap>`和使用`<association>`与`<collection>`元素来实现对象间的关联映射。
|
4月前
|
Java 数据库连接 测试技术
后端框架的学习----mybatis框架(8、lombok)
这篇文章介绍了如何在MyBatis框架中使用lombok库来简化Java实体类的编写,包括在IDEA中安装Lombok插件、在项目中导入lombok依赖以及在实体类上使用Lombok提供的注解。
|
4月前
|
Java 数据库连接 数据库
后端框架的学习----mybatis框架(6、日志)
这篇文章介绍了如何在MyBatis框架中使用日志功能,包括配置MyBatis的日志实现、使用log4j作为日志工具,以及如何通过配置文件控制日志级别和输出格式。
|
4月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(5、分页)
这篇文章介绍了如何在MyBatis框架中实现分页功能,包括使用SQL的`limit`语句进行分页和利用MyBatis的`RowBounds`对象进行分页的方法。
|
4月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(7、使用注解开发)
这篇文章讲述了如何使用MyBatis框架的注解方式进行开发,包括在接口上使用注解定义SQL语句,并通过动态代理实现对数据库的增删改查操作,同时强调了接口需要在核心配置文件中注册绑定。
|
7月前
|
SQL Java 数据库连接
【Mybatis】深入学习MyBatis:概述、主要特性以及配置与映射
【Mybatis】深入学习MyBatis:概述、主要特性以及配置与映射
【Mybatis】深入学习MyBatis:概述、主要特性以及配置与映射
|
6月前
|
Java 数据库连接 Maven
Mybatis学习
Mybatis学习
33 0