MyBatis框架学习(五)——MybatisPlus

官网:https://baomidou.com/ MyBatis Plus

1. 框架部署

使用 Spring Initializr 构建一个SpringBoot 项目,选则 spring web,引入的依赖如下所示

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

将原本的 application.properties 文件改为 application.yml,并配置数据库如下:

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/novels?useUnicode=true&characterEncoding=utf8
    username: root
    password: ma123456

在数据库的URL中的3306后面加的 novels 是要指定连接的数据库。

2. 快速使用

2.1 创建存储类

在SpringBoot的启动类下面创建一个包 com.mr.pojo ,该包下面放置要存储数据的类,注意,该类的名称就是你要连接数据库中表的名称,否则会连接报错,比如我要存储的数据库中表是books,那么这里我就建立一个名为 Books.java 的类,内容如下

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Books {
    private int num;
    private String name;
    private String author;
}

2.2 创建映射

在SpringBoot的启动类下面创建一个包 com.mr.pojo ,该包下面放置要映射的接口,这我们创建一个名为 BookMapper 的接口,接口如下:

public interface BookMapper extends BaseMapper<Books> {}

当继承了 BaseMapper 类后,该接口就能够进行CRUD,即增删查改操作了。

除此之外还需要添加 @MapperScan("com.**.mapper") 到SpringBoot的启动类前面座位注解,表示扫描的映射文件在哪里,如果在这里要写全名的话,需要写 main/java 后面到映射文件的全部目录。

2.3 测试

在test中写上如下查询全部操作:

@SpringBootTest
class MergeMybatisPlusApplicationTests {

    @Autowired
    private BookMapper bookMapper;

    @Test
    void contextLoads() {
        List<Books> books = bookMapper.selectList(null);
        books.forEach(System.out::println);
    }
}

其中 selectList(null) 中的 null 其实应该写一个条件构造器,如果构造器为空,则查询全部。

3. 配置日志

application.yml 新的一行行首配置以下内容:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

就能够得到日志的输出,如下所示:
在这里插入图片描述
上述日志输出能够显示查询的SQL语句是什么,连接类型是什么,查询到的内容是什么。

4. 自动填充数据

在工作中我们经常会对数据库的一个新数据以及数据的更新记录其初始化或者更新的时间,但是这种更新我们不希望每次都手动更新,而是希望在每次插入或更新数据时就自动更新时间的值,为此要用到MyBatis-plus的自动填充功能。

4.1 设置属性自动填充

我们对Books这个存储类添加两个属性 createTimeupdateTime ,前者需要在数据插入的时候进行自动更新,后者需要在数据插入和数据修改时进行自动更新,数据库里也自然增加两个这两个属性,存储类的设置如下:

public class Books {
    private int num;
    private String name;
    private String author;

    // insert操作时更新
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    // update操作insert操作时更新
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}

只需要加一个 TableField 注解就行。

注意,这里使用的是驼峰命名法,其在数据库中操作时会将驼峰命名法自动转为create_time这样的格式,所以如果你数据库中的属性是createTime则会报错。

4.2 创建自动填充类

pojo 目录的同级目录下创建一个 handler 的目录,里面新建一个java类,用作自动填充类的配置类,代码如下所示:

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

我们自己写的累继承了 MetaObjectHandler ,只需要实现他写的 MetaObjectHandler 里面的一些方法即可,再用 component 注解将其添加到springIoc里面,就完成了配置类的初始化。

4.3 测试

在测试类中进行如下测试,可以看到,系统在数据库中的 create_timeupdate_time 两个属性中的值进行了自动装填。

@Test
public void testInsert(){
    Books book = new Books();
    book.setNum(12);
    book.setName("占星术杀人魔法");
    book.setAuthor("岛田庄司");
    int result = bookMapper.insert(book);
    System.out.println(result);
}

5. 乐观锁

乐观锁在进行任何操作的时候都不进行上锁,其操作方式一般如下:

  1. 对每一条数据设置一个 version 属性;
  2. 更新操作时,获取数据库中该条记录的 version 值;
  3. 进行更新时,如果记录的 version 值与拿到的 version 值一样才进行更新,否则更新失败。

5.1 添加字段

在数据库以及实体类中添加 version 属性字段当做乐观锁,其中实体类的改变如下所示:

public class Books {
    private int num;
    private String name;
    private String author;

    // insert操作时更新
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    // update操作insert操作时更新
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    @Version
    private int version;
}

增加了一个 version 字段,其中的注释 @Version 表示该属性是乐观锁。

5.2 配置乐观锁

pojo 目录的同级目录下创建一个 config 的目录,里面新建一个java类,用作MyBatisPlus的配置类,这个时候可以将对MyBatisPlus的全部配置都放到里面去,包括前面的 @MapperScan("com.**.mapper") 扫描映射文件,配置文件如下:

@MapperScan("com.**.mapper")
@EnableTransactionManagement  // 开启注解事务管理,等价于xml配置方式的 <tx:annotation-driven />
@Configuration  //配置类
public class MyBatisPlusConfig {
    //注册乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

5.3 测试

测试代码如下所示

//测试乐观锁成功!
@Test
public void testOptimisticLocker(){
    Books book = bookMapper.selectById(12);;
    book.setName("斜屋犯罪");

    bookMapper.updateById(book);
}

//测试乐观锁失败!多线程下
@Test
public void testOptimisticLocker2(){
    //线程1
    Books book1 = bookMapper.selectById(12);;

    book1.setName("御手洗洁的异或");
    //模拟另一个线程执行了插队操作
    Books book2 = bookMapper.selectById(12);;
    book2.setName("钟表馆事件");
    bookMapper.updateById(book1);
    
    bookMapper.updateById(book2);    //如果没有乐观锁就会覆盖队线程的值
}

在测试例子2即乐观锁失败的案例中,我们可以从日志中清楚看到乐观锁阻止了第二个例子的更新,日志如下:
在这里插入图片描述

6. 查询和更新

查询操作测试如下的情况:ID查询、批量查询、条件查询,更新操作虽然属性名是 updateById ,但是也必须传一个对象进去,示例如下:

//更新操作
@Test
public void testUpdate(){
    Books book = bookMapper.selectById(12);
    book.setName("占星术杀人魔法");

    bookMapper.updateById(book);
}

//单个查询,查询ID
@Test
public void testSelectID(){
    Books book = bookMapper.selectById(12);
    System.out.println(book);
}

//批次查询
@Test
public void testSelectBatch(){
    List<Books> users = bookMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

//按条件查询
@Test
public void testSelectMap(){
    HashMap<String, Object> map = new HashMap<>();
    //自定义要查询键值对
    map.put("author", "岛田庄司");
    List<Books> users = bookMapper.selectByMap(map);
    users.forEach(System.out::println);
}

7. 分页查询

MyBatisPlus中内置有分页查询的插件,直接使用就行,首先在 MyBatisPlusConfig.java 这个配置文件中对分页插件进行定义,定义如下:

//注册分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor2() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
    return interceptor;
}

对分页插件进行测试,测试如下:

//进行分页查询
@Test
public void testSelectPage(){
    //两个参数分别是  返回所处的页,每个页面的大小
    Page<Books> page = new Page<>(1, 3);
    //后一个参数是条件构造器
    Page<Books> pageBooks = bookMapper.selectPage(page, null);
    List<Books> books = pageBooks.getRecords();
    books.forEach(System.out::println);
}

8. 逻辑删除

删除操作与查询操作类似,也有根据 ID删除、批量删除、按条件删除三种类型,只需要将select变为delete即可,而逻辑删除则并不是进行数据库中的删除,我们选择在数据库中添加一位 delete 属性位,delete=0 表示用户并未删除该条数据,delete=1 表示用户删除了用户数据,那么这条数据就对用户不可见了,相当于一个回收站的操作,就防止了数据的丢失。下面我们来进行逻辑删除的操作。

8.1 添加字段

在实体类中定义一个逻辑删除的字段,要想让MyBatisPlus知道其为逻辑删除字段,还需要在前面加一个 @TableLogic 的注解,定义如下:

public class Books {
    private int id;
    private String name;
    private String author;

    // insert操作时更新
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    // update操作insert操作时更新
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    @Version
    private int version;

    @TableLogic
    private int deleted;
}

在配置文件 application.yml 文件里面配置逻辑删除与未删除的值,配置如下:

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

8.2 测试

测试时只需要正常删除就行,测试如下:

//逻辑删除示例
@Test
public void testLogicDeleted(){
    int result = bookMapper.deleteById(13);
    System.out.println(result);
}

可以看到,id=13 的数据记录的 deleted 字段已经被置1了。

9. 条件构造器

条件构造器的官网示例

这里我展示几个例子

9.1 Between

即查找在 [val1,val2] 的值,使用如下:

@Test
public void testBetween(){
    QueryWrapper<Books> wrapper = new QueryWrapper<>();
    wrapper.between("id", 5, 9);
    List<Books> books = bookMapper.selectList(wrapper);
    books.forEach(System.out::println);
}

9.2 模糊查询

@Test
public void testLike(){
    QueryWrapper<Books> wrapper = new QueryWrapper<>();
    wrapper.like("author","人");//相当于 %人%
    List<Books> books = bookMapper.selectList(wrapper);
    books.forEach(System.out::println);
}

9.3 子查询

使用 inSql 进行子查询语句,

@Test
public void testSonSelect(){
    QueryWrapper<Books> wrapper = new QueryWrapper<>();
    wrapper.inSql("id","select id from books where id > 7")
           .like("author","人");
    List<Books> books = bookMapper.selectList(wrapper);
    books.forEach(System.out::println);
}

9.4 排序

orderByAsc 可以对列进行排序,支持多列的输入,升序,orderByDesc 是降序。

@Test
public void testOrder(){
    QueryWrapper<Books> wrapper = new QueryWrapper<>();
    wrapper.orderByAsc("id");
    List<Books> books = bookMapper.selectList(wrapper);
    books.forEach(System.out::println);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值