• 9.MyBatis-Plus


    1、前期准备
    a. 创建数据库
    1. CREATE TABLE USER
    2. (
    3. id BIGINT(20)NOT NULL COMMENT '主键ID',
    4. NAME VARCHAR(30)NULL DEFAULT NULL COMMENT '姓名',
    5. age INT(11)NULL DEFAULT NULL COMMENT '年龄',
    6. email VARCHAR(50)NULL DEFAULT NULL COMMENT '邮箱',
    7. PRIMARY KEY (id)
    8. );
    9. INSERT INTO user (id, name, age, email)VALUES
    10. (1, 'Jone', 18, 'test1@baomidou.com'),
    11. (2, 'Jack', 20, 'test2@baomidou.com'),
    12. (3, 'Tom', 28, 'test3@baomidou.com'),
    13. (4, 'Sandy', 21, 'test4@baomidou.com'),
    14. (5, 'Billie', 24, 'test5@baomidou.com');
    b. 配置项目环境

    配置Java编译器:

    项目和文件的编码:

    c. 添加依赖
    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter</artifactId>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.springframework.boot</groupId>
    8. <artifactId>spring-boot-starter-test</artifactId>
    9. <scope>test</scope>
    10. </dependency>
    11. <!--mybatis-plus-->
    12. <dependency>
    13. <groupId>com.baomidou</groupId>
    14. <artifactId>mybatis-plus-boot-starter</artifactId>
    15. <version>3.3.1</version>
    16. </dependency>
    17. <!--mysql依赖-->
    18. <dependency>
    19. <groupId>mysql</groupId>
    20. <artifactId>mysql-connector-java</artifactId>
    21. </dependency>
    22. <!--lombok用来简化实体类-->
    23. <dependency>
    24. <groupId>org.projectlombok</groupId>
    25. <artifactId>lombok</artifactId>
    26. <optional>true</optional>
    27. </dependency>
    28. </dependencies>
    d. 配置文件
    1. # 配置MySQL
    2. spring:
    3. datasource:
    4. driver-class-name: com.mysql.cj.jdbc.Driver
    5. url: jdbc:mysql://localhost:3306/syt_mp?serverTimezone=GMT%2B8
    6. username: root
    7. password: 0903he0419
    • 这里的 url 使用了 ?serverTimezone=GMT%2B8 后缀,因为8.0版本的jdbc驱动需要添加这个后缀,否则运行测试用例报告如下错误:java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more
    • 这里的 driver-class-name 使用了 com.mysql.cj.jdbc.Driver ,在 jdbc 8 中 建议使用这个驱动,否则运行测试用例的时候会有 WARN 信息
    e. 启动类
    1. @SpringBootApplication
    2. @MapperScan("com.lemon.demomp.mapper")
    3. public class DemompApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(DemompApplication.class, args);
    6. }
    7. }
    • 在启动类添加注解:@MapperScan("com.lemon.demomp.mapper")
    f. 添加实体
    1. @Data
    2. public class User {
    3. private Long id;
    4. private String name;
    5. private Integer age;
    6. private String email;
    7. }
    g. 添加mapper
    1. @Repository
    2. public interface UserMapper extends BaseMapper<User> {
    3. }
    • 为了避免报错,可以在dao 层 的接口上添加 @Repository 注
    h. 查看 sql 输出日志
    1. # mybatis日志
    2. mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    2、主键策略

    插入操作:

    1. //添加
    2. @Test
    3. public void testAdd() {
    4. User user = new User();
    5. user.setName("lucy");
    6. user.setAge(20);
    7. user.setEmail("1243@qq.com");
    8. int insert = userMapper.insert(user);
    9. System.out.println(insert);
    10. }
    a. ASSIGN_ID

    MyBatis-Plus默认的主键策略是:ASSIGN_ID (使用了雪花算法)

    1. @TableId(type = IdType.ASSIGN_ID)
    2. private Long id;
    b. AUTO 自增策略

    需要在创建数据表的时候设置主键自增

    实体字段中配置@TableId(type = IdType.AUTO)

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

    要想影响所有实体的配置,可以设置全局主键配置

    1. #全局设置主键生成策略
    2. mybatis-plus.global-config.db-config.id-type=auto

    3、自动填充

    注意:update时生成的sql自动是动态sql:UPDATE user SET age=? WHERE id=?

    更新操作:

    1. // 修改
    2. @Test
    3. public void testUpdate() {
    4. User user = new User();
    5. user.setId(1722996412305653764L);
    6. user.setName("朱棣");
    7. int count = userMapper.updateById(user);
    8. System.out.println(count);
    9. }

    需求描述:项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作

    a. 修改数据库

    在User表中添加datetime类型的新的字段 create_time、update_time

    b. 修改实体类

    实体上增加字段并添加自动填充注解:

    1. @TableField(fill = FieldFill.INSERT)
    2. private Date createTime; //create_time
    3. @TableField(fill = FieldFill.INSERT_UPDATE)
    4. private Date updateTime; //update_time
    c. 实现元对象处理接口

    src/hander/MyMetaObjectHandler

    1. @Component
    2. public class MyMetaObjectHandler implements MetaObjectHandler {
    3. // mp执行添加操作,这个方法执行
    4. @Override
    5. public void insertFill(MetaObject metaObject) {
    6. this.setFieldValByName("createTime", new Date(), metaObject);
    7. this.setFieldValByName("updateTime", new Date(), metaObject);
    8. }
    9. // mp 执行修改操作,这个方法执行
    10. @Override
    11. public void updateFill(MetaObject metaObject) {
    12. this.setFieldValByName("updateTime", new Date(), metaObject);
    13. }
    14. }
    4、乐观锁

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

    乐观锁实现方式:

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

    乐观锁实现流程:

    a. 修改数据库

    在user表中新增version字段数据类型为int,给其设定一个默认值(假设为1)

    b. 修改实体类

    添加 @Version 注解

    1. @Version
    2. private Integer version;
    c. 创建配置类

    创建包config,创建文件MybatisPlusConfig.java

    此时可以删除主类中的 @MapperScan 扫描注解

    1. @Configuration
    2. @MapperScan("com.lemon.demomp.mapper")
    3. public class MpConfig {
    4. /**
    5. * 乐观锁插件
    6. */
    7. @Bean
    8. public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    9. return new OptimisticLockerInterceptor();
    10. }
    11. }
    d. 测试方法

    根据输出结果可以看到,首先经过查询,user的version是1,然后在更改user的name属性,此时version变为2。自此乐观锁已经生效。

    1. @Test
    2. public void testOptimisticLocker() {
    3. // 1. 根据id进行查询
    4. User user = userMapper.selectById(1722996412305653768L);
    5. user.setName("张三");
    6. int count = userMapper.updateById(user);
    7. System.out.println(count);
    8. }
    5、查询
    a. 批量查询

    通过多个id进行批量查询

    SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id IN ( ? , ? , ? )

    ==> Parameters: 1(Integer), 2(Integer), 3(Integer)

    1. /*
    2. * 批量查询
    3. * */
    4. @Test
    5. public void testSelectByIds() {
    6. List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    7. System.out.println(users);
    8. }
    b. 简单条件查询

    SELECT id,name,age,email,create_time,update_time,version FROM user WHERE name = ? AND age = ?

    ==> Parameters: Jack(String), 20(Integer)

    1. /*
    2. * 简单条件查询
    3. * */
    4. @Test
    5. public void testSelectByMap() {
    6. Map<String, Object> columnMap = new HashMap<>();
    7. columnMap.put("name", "Jack");
    8. columnMap.put("age", 20);
    9. List<User> users = userMapper.selectByMap(columnMap);
    10. System.out.println(users);
    11. }
    6、分页查询
    a. 添加分页插件

    在配置类中注册分页插件:src/config/MpConfig

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

    Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user LIMIT ?,?

    ==> Parameters: 0(Long), 3(Long)

    1. /*
    2. * 测试selectPage分页
    3. * */
    4. @Test
    5. public void testSelectPage() {
    6. Page<User> page = new Page<>(1, 3);
    7. Page<User> userPage = userMapper.selectPage(page, null);
    8. // 返回对象得到分页所有数据
    9. long pages = userPage.getPages(); //总页数
    10. long current = userPage.getCurrent(); //当前页
    11. List<User> records = userPage.getRecords();//查询数据集合
    12. long total = userPage.getTotal();//总记录数
    13. boolean hasNext = userPage.hasNext();//是否有下一页
    14. boolean hasPrevious = userPage.hasPrevious();//是否有上一页
    15. System.out.println(pages);
    16. System.out.println(current);
    17. System.out.println(records);
    18. System.out.println(total);
    19. System.out.println(hasNext);
    20. System.out.println(hasPrevious);
    21. }
    c. 测试selectMapsSelect

    Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user LIMIT ?,?

    ==> Parameters: 0(Long), 5(Long)

    1. /*
    2. * 测试selectMapsPage
    3. * */
    4. @Test
    5. public void testSelectMapsPage() {
    6. //Page不需要泛型
    7. Page<Map<String, Object>> page = new Page<>(1, 5);
    8. Page<Map<String, Object>> pageParam = userMapper.selectMapsPage(page, null);
    9. List<Map<String, Object>> records = pageParam.getRecords();
    10. records.forEach(System.out::println);
    11. System.out.println(pageParam.getCurrent());
    12. System.out.println(pageParam.getPages());
    13. System.out.println(pageParam.getSize());
    14. System.out.println(pageParam.getTotal());
    15. System.out.println(pageParam.hasNext());
    16. System.out.println(pageParam.hasPrevious());
    17. }
    7、删除
    a. 根据id删除记录

    Preparing: DELETE FROM user WHERE id=?

    ==> Parameters: 1(Long)

    1. /*
    2. * 根据id删除记录
    3. * */
    4. @Test
    5. public void testDelById() {
    6. int isDeleted = userMapper.deleteById(1L);
    7. System.out.println(isDeleted);
    8. }
    b. 批量删除

    Preparing: DELETE FROM user WHERE id IN ( ? , ? , ? )

    ==> Parameters: 2(Integer), 3(Integer), 4(Integer)

    1. /*
    2. * 批量删除
    3. * */
    4. @Test
    5. public void testDelBatchByIds() {
    6. int isDeleted = userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));
    7. System.out.println(isDeleted);
    8. }
    c. 简单条件删除

    Preparing: DELETE FROM user WHERE name = ? AND age = ?

    ==> Parameters: Billie(String), 24(Integer)

    1. /*
    2. *
    3. * 简单条件删除
    4. * */
    5. @Test
    6. public void testDelByMap() {
    7. Map<String, Object> map = new HashMap<>();
    8. map.put("name", "Billie");
    9. map.put("age", 24);
    10. int isDeleted = userMapper.deleteByMap(map);
    11. System.out.println(isDeleted);
    12. }
    8、逻辑删除

    物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据

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

    逻辑删除的使用场景

    • 可以进行数据恢复
    • 有关联数据,不便删除
    a. 修改数据库

    添加逻辑删除字段

    ALTER TABLE `user` ADD COLUMN deleted BOOLEAN DEFAULT FALSE COMMENT '逻辑删除'
    b. 修改实体类

    添加deleted 字段,并加上 @TableLogic 注解

    1. @TableLogic
    2. private Integer deleted;
    c. 配置文件(可选)

    application.properties 加入以下配置,此为默认值,如果你的默认值和mp默认的一样,该配置可无

    1. mybatis-plus:
    2. global-config:
    3. db-config:
    4. id-type: auto
    5. logic-delete-value: 1
    6. logic-not-delete-value: 0
    d. 测试逻辑删除

    Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0

    ==> Parameters: 6(Long)

    测试后发现,数据并没有被删除,deleted字段的值由0变成了1

    测试后分析打印的sql语句,是一条update

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

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

    MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断

    SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE deleted=0

    1. /*
    2. * 测试逻辑删除后的查询
    3. * */
    4. @Test
    5. public void testLogicDeleteSelect() {
    6. List<User> users = userMapper.selectList(null);
    7. users.forEach(System.out::println);
    8. }
    9、条件构造器

    Wrapper

    条件构造抽象类,最顶端父类

    AbstractWrapper

    用于查询条件封装,生成 sql 的 where 条件

    QueryWrapper

    查询条件封装

    UpdateWrapper

    Update 条件封装

    AbstractLambdaWrapper

    使用Lambda 语法

    LambdaQueryWrapper

    用于Lambda语法使用的查询Wrapper

    LambdaUpdateWrapper

    Lambda 更新封装Wrapper

    a. ge、gt、le、lt、isNull、isNotNull

    UPDATE user SET deleted=1 WHERE deleted=0 AND (name IS NOT NULL AND age >= ? AND email IS NOT NULL)

    ==> Parameters: 50(Integer)

    1. /*
    2. * ge、gt、le、lt、isNull、isNotNull
    3. * delete
    4. * */
    5. @Test
    6. public void testQuery() {
    7. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    8. queryWrapper.isNotNull("name")
    9. .ge("age",50)
    10. .isNotNull("email");
    11. int deleted = userMapper.delete(queryWrapper);
    12. System.out.println("deleted return count= " + deleted);
    13. }
    b. eq、ne

    SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE deleted=0 AND (name = ?)

    ==> Parameters: 张三(String)

    注意:seletOne()返回的是一条实体记录,当出现多条时会报错

    1. /*
    2. * eq、ne
    3. * selectOne
    4. * */
    5. @Test
    6. public void testSelectOne() {
    7. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    8. queryWrapper.eq("name", "张三");
    9. User user = userMapper.selectOne(queryWrapper);
    10. System.out.println(user);
    11. }
    c. between、notBetween

    SELECT COUNT( 1 ) FROM user WHERE deleted=0 AND (age BETWEEN ? AND ?)

    ==> Parameters: 60(Integer), 80(Integer)

    1. /*
    2. * between,notBetween
    3. * selectCount
    4. * */
    5. @Test
    6. public void testSelectCount() {
    7. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    8. queryWrapper.between("age", 60, 80);
    9. Integer count = userMapper.selectCount(queryWrapper);
    10. System.out.println(count);
    11. }
    d. like、notLike、likeLeft、likeRight

    SELECT name,age FROM user WHERE deleted=0 AND (name LIKE ? AND email LIKE ?)

    ==> Parameters: %e%(String), 5%(String)

    selectMaps()返回Map集合列表,通常配合select()使用

    1. /*
    2. * like,notLike,likeLeft,likeRight
    3. * selectMaps
    4. * */
    5. @Test
    6. public void testSelectMaps() {
    7. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    8. queryWrapper.select("name", "age")
    9. .like("name", "e")
    10. .likeRight("email", "5");
    11. List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
    12. maps.forEach(System.out::println);
    13. }
    e. orderBy、orderByDesc、orderByAsc

    SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE deleted=0 ORDER BY age DESC,id DESC

    1. /*
    2. * orderBy、orderByDesc、orderByAsc
    3. * selectList
    4. * */
    5. @Test
    6. public void testSelectList() {
    7. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    8. queryWrapper.orderByDesc("age", "id");
    9. List<User> users = userMapper.selectList(queryWrapper);
    10. users.forEach(System.out::println);
    11. }
    10、查询方式

    setSqlSelect

    设置 SELECT 查询字段

    where

    WHERE 语句,拼接 + WHERE 条件

    and

    AND 语句,拼接 + AND 字段=值

    andNew

    AND 语句,拼接 + AND (字段=值)

    or

    OR 语句,拼接 + OR 字段=值

    orNew

    OR 语句,拼接 + OR (字段=值)

    eq

    等于=

    allEq

    基于 map 内容等于=

    ne

    不等于<>

    gt

    大于>

    ge

    大于等于>=

    lt

    小于<

    le

    小于等于<=

    like

    模糊查询 LIKE

    notLike

    模糊查询 NOT LIKE

    in

    IN 查询

    notIn

    NOT IN 查询

    isNull

    NULL 值查询

    isNotNull

    IS NOT NULL

    groupBy

    分组 GROUP BY

    having

    HAVING 关键词

    orderBy

    排序 ORDER BY

    orderAsc

    ASC 排序 ORDER BY

    orderDesc

    DESC 排序 ORDER BY

    exists

    EXISTS 条件语句

    notExists

    NOT EXISTS 条件语句

    between

    BETWEEN 条件语句

    notBetween

    NOT BETWEEN 条件语句

    addFilter

    自由拼接 SQL

    last

    拼接在最后,例如:last(“LIMIT 1”)

  • 相关阅读:
    [100天算法】-面试题 17.17.多次搜索(day 43)
    springboot-自动配置
    【剑指Offer】11.旋转数组的最小数字
    手把手教你部署Jenkins教程,小白也能学会(多图预警)!
    [附源码]计算机毕业设计基于SpringBoot的实验填报管理系统
    农村污水处理站如何实现数据采集和远程监控?
    手写raft(二) 实现日志复制
    【Linux】Linux常用操作命令(三)
    Python---练习:求世界杯小组赛的总成绩(涉及:布尔类型转换为整型)
    「Java核心面试系列」面试竟然连这MySQL面试核心25问,都不会?
  • 原文地址:https://blog.csdn.net/Yuanshigou9/article/details/134430432