SpringBoot与MybatisPlus MybatisPlus

SpringBoot与MybatisPlus MybatisPlus

一、mybatisPlus概述

MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。

MyBatisPlus的愿景是成为MyBatis最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍。

官方网址:MyBatis-Plus 

二、SpringBoot集成MyBatisPlus

1、创建SpringBoot项目,添加MyBatisPlus起步依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
<!-- MyBatisPlus -->
<dependency>
  <groupId>***.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.0</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.26</version>
</dependency>
<!-- lombok -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>

2、在SpringBoot配置文件中配置数据源

# 数据源
spring:
  datasource:
    driver-class-name: ***.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///mybatis?serverTimezone=UTC
    username: root
    password: 123456

3、编写实体类

@Data
public class User {
    public Integer id;
    public String username;
    public String sex;
    public String address;
    public Integer a***ount;
}

 4、编写Mapper接口,继承BaseMapper

public interface UserMapper extends BaseMapper<User> {

}

5、在 SpringBoot启动类中添加 @MapperScan 注解,扫描Mapper文件夹

@SpringBootApplication
@MapperScan("***.zj.mapper")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

6、测试Mapper方法

@SpringBootTest
public class TestUserMapper {

    @Resource
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = userMapper.selectById(10);
        System.out.println(user);
    }
}

二、MyBatisPlus  CRUD

2.1 添加

1、配置文件开启SQL日志打印

# 开启SQL日志
mybatis-plus:
  configuration:
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2、测试添加方法

    @Test
    public void testAdd(){
       User user = new User(null,"梅川内酷","男","临沂市",3000);
       userMapper.insert(user);
        // MyBatisPlus插入成功后,可直接直接获取主键的值
        System.out.println(user.getUserId());
    }

2.2 相关注解

我们将user类中的字段修改为和数据库表中的字段不一致。将表名称改为tb_user, 这样表中的字段、表名都和实体类不一致。这样mybatosplus无法自动将字段和属性对应。也就无法生成正确的sql语句。此时需要使用注解来指定映射关系。

@TableName

作用:指定类为哪个表的实体类

位置:类上方

@TableId

作用:指定实体类的属性为主键

位置:属性上方

属性:

  • value:主键字段名

  • type:主键策略

描述
NONE 无状态,不设置主键类型
AUTO 自增主键
INPUT 插入前自行设置主键值
ASSIGN_ID 默认策略,使用雪花算法自动生成主键ID,主键类型为整形或字符串。(雪花算法:微博开源的分布式ID生成算法,使用一个64位的Long类型数字作为全局唯一ID。在分布式系统中的应用十分广泛,且ID引入了时间戳,基本上保持自增)
ASSIGN_UUID 自动生成排除中划线的UUID作为主键,主键类型为

@TableField

作用:在属性和列名不同的情况下,指定映射关系

位置:非主键属性上方

@Data
@AllArgsConstructor
@TableName("tb_user")
public class User {
    @TableId(value = "id",type = IdType.AUTO)
    public Integer UserId;
    @TableField("username")
    public String userName;
    @TableField("sex")
    public String userSex;
    @TableField("address")
    public String userAddress;
    @TableField("a***ount")
    public Integer userA***ount;
}

2.3 修改

    @Test
    public void testUpdate(){
        User user = new User(1,"梅川丘酷","男","烟台市",4000);
        userMapper.updateById(user);
    }

2.4 删除

 根据id删除

    @Test
    public void testDeleteById(){
        userMapper.deleteById(8);
    }

批量删除

    @Test
    public void testDeleteBatch(){
        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(9);
        ids.add(10);
        ids.add(11);
        userMapper.deleteBatchIds(ids);
    }

根据字段的条件删除

    @Test
    public void testDeleteMap(){
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("sex","男");  ;//删除所有字段(字段是数据库列名不是类的属性名)值是男的数据
        userMapper.deleteByMap(map);
    }

2.5 查询

根据id查询

    @Test
    public void testFindById(){
        User user = userMapper.selectById(1);
        System.out.println(user);
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE id=?
==> Parameters: 5(Integer)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 5, 翟玲娇, 女, 长沙市, 1000
<==      Total: 1

根据id批量查询

    @Test
    public void testFindBatch(){
        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(5);
        ids.add(6);
        ids.add(12);
        List<User> users = userMapper.selectBatchIds(ids);
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE id IN ( ? , ? , ? )
==> Parameters: 5(Integer), 6(Integer), 12(Integer)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 5, 翟玲娇, 女, 长沙市, 1000
<==        Row: 6, 张晓, 女, 青岛市, 2500
<==        Row: 12, 唐宛凝, 女, 石家庄市, 3334
<==      Total: 3

根据字段条件查询

    @Test
    public void testFindByMap(){
        Map<String,Object> map = new HashMap<String,Object>();
        //条件之间是and关系
        map.put("username","张晓");
        map.put("address","青岛市");
        List<User> users = userMapper.selectByMap(map);
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE address = ? AND username = ?
==> Parameters: 青岛市(String), 张晓(String)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 6, 张晓, 女, 青岛市, 2500
<==      Total: 1

 2.7 条件构造器

 Mybatis-Plus通过QueryWrapper对象让用户自由的构建SQL条件,简单便捷,没有额外的负担,能够有效提高开发效率。

条件参数说明:

查询方式 说明
or 或条件语句
and 且条件语句
like 模糊查询 like
notLike 模糊查询 not Like
exists exists 条件语句
notExists not Exists 条件语句
isNull null 值查询
isNotNull is Not Null 查询
in in 查询
notIn not in 查询
groupBy 分组查询
orderBy 排序查询
having 分组后筛选
eq 等于 =
ne 不等于 <>
between between 条件语句
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=

 示例1:查询账户余额在1000-3000之间的用户的信息

    @Test
    public void testFindWrapper(){
         //创建条件构造器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.ge("a***ount", 1000).lt("a***ount", 3000);
        List<User> users = userMapper.selectList(wrapper);
        for (User user : users) {
            System.out.println(user);
        }
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE (a***ount >= ? AND a***ount < ?)
==> Parameters: 1000(Integer), 3000(Integer)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 5, 翟玲娇, 女, 长沙市, 1000
<==        Row: 6, 张晓, 女, 青岛市, 2500
<==      Total: 2

示例2:查询账户小于1000或者大于等于9000的用户

    @Test
    public void testFindWrapper(){
         //创建条件构造器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.lt("a***ount", 2000).or().ge("a***ount", 9000);
        List<User> users = userMapper.selectList(wrapper);
        for (User user : users) {
            System.out.println(user);
        }
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE (a***ount < ? OR a***ount >= ?)
==> Parameters: 2000(Integer), 9000(Integer)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 5, 翟玲娇, 女, 长沙市, 1000
<==        Row: 16, 郑怜雪, 女, 重庆市, 555900
<==        Row: 19, 黎飒, 男, 深圳市, 9000
<==      Total: 3

示例3:查询名字中有张的用户,按照账户升序排序。

    @Test
    public void testFindWrapper(){
         //创建条件构造器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.like("username", "张").orderByAsc("a***ount");
        List<User> users = userMapper.selectList(wrapper);
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE (username LIKE ?) ORDER BY a***ount ASC
==> Parameters: %张%(String)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 6, 张晓, 女, 青岛市, 2500
<==        Row: 21, 张三, 男, 昆明市, 5000
<==      Total: 2

2.8 分页查询

1、在配置类或启动类配置分页插件

// 注册插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  return interceptor;
}

2、测试分页查询

    @Test
    public void testFindPage(){
        //分页条件;第一个参数表示从第几条开始,第二个参数表示取几条数据。
        Page page = new Page(0,2);
        IPage<User> iPage = userMapper.selectPage(page, null);//null表示查询全部
        System.out.println("结果集:"+iPage.getRecords());
        System.out.println("总页数:"+iPage.getPages());
        System.out.println("总条数:"+iPage.getTotal());
        System.out.println("当前页:"+iPage.getCurrent());
        System.out.println("每页条数:"+iPage.getSize());

    }
==>  Preparing: SELECT COUNT(*) AS total FROM tb_user
==> Parameters: 
<==    Columns: total
<==        Row: 8
<==      Total: 1
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user LIMIT ?
==> Parameters: 2(Long)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 5, 翟玲娇, 女, 长沙市, 1000
<==        Row: 6, 张晓, 女, 青岛市, 2500
<==      Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5ff2b8ca]
结果集:[User(UserId=5, userName=翟玲娇, userSex=女, userAddress=长沙市, userA***ount=1000), User(UserId=6, userName=张晓, userSex=女, userAddress=青岛市, userA***ount=2500)]
总页数:4
总条数:8
当前页:1
每页条数:2

2.9 全局配置

 假如数据库的所有表都以tb_开头,主键都是自增的。如果针对每一个实体类都要添加相关注解比较麻烦,可以在SpringBoot配置文件中进行全局配置,该配置在所有的实体类中都生效。

mybatis-plus:
  # 全局配置
  global-config:
    db-config:
      #主键类型
      id-type: AUTO
      # 设置表名前缀
      table-prefix: tb_
      # 是否使用驼峰转下划线命名,默认开启 ,例如 实体类中的UserAddress属性等于数据库中的 user_address字段
      table-underline: true

三、ActiveRecord

Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。

在MyBatisPlus中,AR模式即在实体类中封装了对数据库的访问,而不通过mapper类。

1、虽然操作数据库不通过mapper类,但需要编写mapper类并继承BaseMapper

public interface UserMapper extends BaseMapper<User> {

}

2、实体类继承Model类,开启AR模式

@Data
@AllArgsConstructor
@TableName("tb_user")
public class User  extends Model<User> {
    @TableId(value = "id",type = IdType.AUTO)
    public Integer UserId;
    @TableField("username")
    public String userName;
    @TableField("sex")
    public String userSex;
    @TableField("address")
    public String userAddress;
    @TableField("a***ount")
    public Integer userA***ount;
}

四、ActiveRecord增删改查

 4.1 AR添加

    @Test
    public void testAdd(){
        User user = new User(null, "李四", "男", "北京市", 12000);
        user.insert();
    }
==>  Preparing: INSERT INTO tb_user ( username, sex, address, a***ount ) VALUES ( ?, ?, ?, ? )
==> Parameters: 李四(String), 男(String), 北京市(String), 12000(Integer)
<==    Updates: 1

4.2 AR根据id修改

    @Test
    public void testUpdate(){
        // 创建实体类对象
        User user = new User();
        // 设置需要更新的属性
        user.setUserA***ount(13000);
        // 设置需要修改的id
        user.setUserId(22);
        // 根据主键进行更新,没有设置的值忽略
        user.updateById();
    }
==>  Preparing: UPDATE tb_user SET a***ount=? WHERE id=?
==> Parameters: 13000(Integer), 22(Integer)
<==    Updates: 1

4.3 AR根据id查询

    @Test
    public void testFindById(){
        User user = new User();
        user.setUserId(16);
        User user1 = user.selectById();
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE id=?
==> Parameters: 16(Integer)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 16, 郑怜雪, 女, 重庆市, 555900
<==      Total: 1

4.4 AR删除

    @Test
    public void testDeleteById(){
        User user = new User();
        user.setUserId(21);
        user.deleteById();
    }
==>  Preparing: DELETE FROM tb_user WHERE id=?
==> Parameters: 21(Integer)
<==    Updates: 1

4.5 AR查询所有

    @Test
    public void testFindAll(){
        User user = new User();
        List<User> users = user.selectAll();
    }
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user
==> Parameters: 
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 5, 翟玲娇, 女, 长沙市, 1000
<==        Row: 6, 张晓, 女, 青岛市, 2500
<==        Row: 12, 唐宛凝, 女, 石家庄市, 3334
<==        Row: 16, 郑怜雪, 女, 重庆市, 555900
<==        Row: 18, 梅川内酷, 男, 临沂市, 3000
<==        Row: 19, 黎飒, 男, 深圳市, 9000
<==        Row: 20, 黄林, 男, 昆明市, 5000
<==        Row: 22, 李四, 男, 北京市, 13000
<==      Total: 8

4.6 AR分页查询

    @Test
    public void testFindPage(){
        // 创建分页条件
        Page page = new Page(0,2);
        // 查询构造器
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        // 查询名字包含"张"的学生,按照账户升序排序
        queryWrapper.like("username","张").orderByAsc("a***ount");

        User user = new User();
        // 分页查询
        IPage iPage = user.selectPage(page, queryWrapper);
        // 打印分页数据
        System.out.println("结果集:"+iPage.getRecords());
        System.out.println("总页数:"+iPage.getPages());
        System.out.println("总条数:"+iPage.getTotal());
        System.out.println("当前页:"+iPage.getCurrent());
        System.out.println("每页条数:"+iPage.getSize());
    }
==>  Preparing: SELECT COUNT(*) AS total FROM tb_user WHERE (username LIKE ?)
==> Parameters: %张%(String)
<==    Columns: total
<==        Row: 2
<==      Total: 1
==>  Preparing: SELECT id AS UserId,username,sex AS userSex,address AS userAddress,a***ount AS userA***ount FROM tb_user WHERE (username LIKE ?) ORDER BY a***ount ASC LIMIT ?
==> Parameters: %张%(String), 2(Long)
<==    Columns: UserId, username, userSex, userAddress, userA***ount
<==        Row: 6, 张晓, 女, 青岛市, 2500
<==        Row: 23, 张三, 男, 北京市, 12000
<==      Total: 2

注意:AR分页查询也需要配置分页插件 

五、MyBatisPlus插件

5.1 插件概述 

MyBatis插件机制

MyBatis插件就是对Executor、StatementHandler、ParameterHandler、ResultSetHandler这四个接口上的方法进行拦截,利用JDK动态代理机制,为这些接口的实现类创建代理对象,在执行方法时,先去执行代理对象的方法,从而执行自己编写的拦截逻辑。

  • Executor

    MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过 ResultSetHandler进行自动映射。

  • StatementHandler

    MyBatis直接让数据库执行sql脚本的对象。

  • ParameterHandler

    MyBatis实现Sql入参设置的对象。

  • ResultSetHandler

    MyBatis把ResultSet集合映射成POJO的接口对象。

MyBatisPlus常用插件

MyBatisPlus依据MyBatis插件机制,为我们提供了一些开发中常用的插件,我们在开发中使用即可。

常用插件:

  • 自动分页: PaginationInnerInterceptor
  • 防止全表更新与删除: BlockAttackInnerInterceptor
  • 乐观锁: OptimisticLockerInnerInterceptor

这些插件都实现了InnerInterceptor接口

5.2 分页插件

在配置类或启动类配置分页插件,之前用过,不配置插件的话分页功能无法实现。

// 注册插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  return interceptor;
}

5.3 防止全表更新与删除插件

作用:分析删除/更新语句,防止小白或者恶意进行删除/更新全表操作。

注意:

  • 该插件只支持 MySQL5.6.3 以上版本
  • 该插件只建议在开发环境中使用,不建议在生产环境使用

插件使用:

1、在配置类或启动类配置防止全表更新与删除插件

// 注册插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  // 分页插件
  interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  // 防止全表更新与删除插件
  interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
  return interceptor;
}

2、测试全表删除

    @Test
    public void testDeleteAll() {
        User user = new User();
        user.delete(new QueryWrapper());
    }

 5.4 乐观锁插件

修改数据库中的数据时,为了避免同时被其他人修改,最好的办法就是对该数据进行加锁以防止并发。

锁的设计分为悲观锁和乐观锁:

  • 悲观锁:悲观锁对数据被外界修改持保守态度。即在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现往往依靠数据库提供的锁机制。
  • 乐观锁:乐观锁在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做

 MyBatisPlus乐观锁插件实现方式:

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

使用MyBatisPlus乐观锁插件:

1、注册乐观锁插件

// 注册插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  // 分页插件
  interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
  // 防止全表更新与删除插件
  interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
  // 乐观锁插件
  interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
  return interceptor;
}

2、修改实体类,添加version列并在该属性上面增加@Version

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tb_user")
public class User  extends Model<User> {
    @TableId(value = "id",type = IdType.AUTO)
    public Integer UserId;
    @TableField("username")
    public String userName;
    @TableField("sex")
    public String userSex;
    @TableField("address")
    public String userAddress;
    @TableField("a***ount")
    public Integer userA***ount;

    @Version
    private Integer version;
}

3、修改数据库表,添加一列整型version字段并设置默认值为0

4、测试修改功能 

    @Test
    public void testUpdate1() {
        User user = new User();
        user.setUserId(18);
        user.setUserName("梅川丘酷");
        //如果版本号和数据库一致更新成功,版本号+1,如果不一致更新失败
        user.setVersion(0);
        user.updateById();
    }
==>  Preparing: UPDATE tb_user SET username=?, version=? WHERE id=? AND version=?
==> Parameters: 梅川丘酷(String), 1(Integer), 18(Integer), 0(Integer)
<==    Updates: 1

 六、MyBatisPlus逻辑删除

6.1 逻辑删除概念

 在实际开发中,由于数据十分重要,为了避免误删除数据导致数据无法找回,我们往往不会使用物理删除,即从数据库中直接删除数据。而是采用逻辑删除的方式,即不会真正在数据库删除数据,而是通过一个变量代表它被删除。

 

deleted属性代表该数据是否删除,0代表未删除,1代表已删除。此时增删改查的Sql语句发生变化:

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据。
  • 更新: 追加where条件防止更新到已删除数据。
  • 删除: 转变为更新

例如:

  • 删除: update user set deleted=1 where id = 1 and deleted=0
  • 查找: select id,name,deleted from user where deleted=0

6.2 逻辑删除使用

 1、在配置文件配置逻辑删除

# mybatis-plus相关配置
mybatis-plus:
 # 全局配置
  global-config:
   db-config:
   # 全局逻辑删除的字段名
    logic-delete-field: deleted
   # 逻辑已删除值(默认为 1)
    logic-delete-value: 1
   # 逻辑未删除值(默认为 0)
    logic-not-delete-value: 0

2、修改实体类,添加逻辑删除属性

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tb_user")
public class User  extends Model<User> {
    @TableId(value = "id",type = IdType.AUTO)
    public Integer UserId;
    @TableField("username")
    public String userName;
    @TableField("sex")
    public String userSex;
    @TableField("address")
    public String userAddress;
    @TableField("a***ount")
    public Integer userA***ount;

    @Version
    private Integer version;
    /*逻辑删除*/
    @TableLogic
    private Integer deleted;
}

3、修改数据库表,添加一列整型deleted字段并设置默认值为0

4、测试删除和查询方法,会看到删除时将deleted字段变为1,查询时添加条件deleted=0

    @Test
    public void testDelete() {
        User user = new User();
        user.deleteById(23);
    }
==>  Preparing: UPDATE tb_user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 23(Integer)
<==    Updates: 1

bug:

Caused by: org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1
	at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:218)
	at org.yaml.snakeyaml.reader.StreamReader.ensureEnoughData(StreamReader.java:176)
	at org.yaml.snakeyaml.reader.StreamReader.ensureEnoughData(StreamReader.java:171)
	at org.yaml.snakeyaml.reader.StreamReader.peek(StreamReader.java:126)
	at org.yaml.snakeyaml.scanner.ScannerImpl.scanToNextToken(ScannerImpl.java:1177)
	at org.yaml.snakeyaml.scanner.ScannerImpl.fetchMoreTokens(ScannerImpl.java:287)
	at org.yaml.snakeyaml.scanner.ScannerImpl.checkToken(ScannerImpl.java:227)
	at org.yaml.snakeyaml.parser.ParserImpl$ParseImplicitDocumentStart.produce(ParserImpl.java:195)
	at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:158)
	at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:148)
	at org.yaml.snakeyaml.***poser.***poser.checkNode(***poser.java:72)
	at org.yaml.snakeyaml.constructor.BaseConstructor.checkData(BaseConstructor.java:114)
	at org.yaml.snakeyaml.Yaml$1.hasNext(Yaml.java:543)
	at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:160)
	at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:134)
	at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:75)
	at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:50)
	at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadDocuments(ConfigFileApplicationListener.java:562)
	at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:518)
	... 37 ***mon frames omitted
Caused by: java.nio.charset.MalformedInputException: Input length = 1

这里要将编码格式设置为utf-8,然后再删除yml配置文件,再重新写一个yml。

 七、MyBatisPlus扩展

7.1 自动填充

由于有了逻辑删除字段,那么向数据库插入数据时候,都需要设置deleted=0,而每次插入数据时都要设置该值十分繁琐,于是MyBatisPlus提供了自动填充功能。

1、为实体类的自动填充字段添加@TableField 

@TableLogic
// 自动填充字段
@TableField(fill = FieldFill.INSERT)
private Integer deleted;

填充策略:

描述
DEFAULT 默认不处理
INSERT 插入操作填充字段
UPDATE 更新操作填充字段
INSERT_UPDATE 插入操作和更新操作均填充字段

2、自定义填充类实现MetaObjectHandler接口

package ***.zj.meta;

import ***.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.***ponent;

@***ponent
public class MyMetaObjectHandler implements MetaObjectHandler {

    /* 插入时填充逻辑
     * metaObject 元对象
     * */
    @Override
    public void insertFill(MetaObject metaObject) {
        /* 参数1:填充字段名
         * 参数2:参数值
         * 参数3:元对象*/
      this.setFieldValByName("deleted",0,metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {

    }
}

3、测试插入数据

    @Test
    public void testAdd() {
        User user = new User();
        user.setUserName("小刚");
        user.setUserAddress("淄博市");
        user.setUserSex("男");
        user.setUserA***ount(4000);
        user.insert();
    }
==>  Preparing: INSERT INTO tb_user ( username, sex, address, a***ount, deleted ) VALUES ( ?, ?, ?, ?, ? )
==> Parameters: 小刚(String), 男(String), 淄博市(String), 4000(Integer), 0(Integer)
<==    Updates: 1: 1

 在插入数据的时候,如果将数据库的deleted字段设置默认值为0的话,其实不需要使用mybatisplus的自动填充,因为在添加数据的时候自动就赋值为0了。

7.2 SQL注入器

 MyBatisPlus方法是有限的,我们可以使用SQL注入器自定义全局方法,注入到全局中,这样所有的Mapper类都能使用该方法,接下来我们自定义一个deleteAll方法。

1、创建注入方法类,继承AbstractMethod

public class DeleteAll extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        //1.定义sql语句
        String sql = "delete from "+tableInfo.getTableName();
        //2.定义方法名
        String methodName = "deleteAll";
        //3.构建sqlSource对象,负责将sql传递到数据库
        SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
        //4.构建删除方法
        return this.addDeleteMappedStatement(mapperClass,methodName,sqlSource);
    }
}

2、创建SQL自动注入器,继承AbstractSqlInjector

@***ponent
public class MySqlInject extends AbstractSqlInjector {

    //注入自定义方法集合
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        ArrayList<AbstractMethod> abstractMethods = new ArrayList<>();
        abstractMethods.add(new DeleteAll());
        return abstractMethods;
    }
}

3、注销防止全表更新与删除插件

4、在Mapper接口中定义deleteAll方法

public interface UserMapper extends BaseMapper<User> {

    void deleteAll();

}

5、测试deleteAll方法,测试的时候不能使用AR模式,因为该方法不存在User类中,而是自定义的。

@Test
public void testDeleteAll() {
  userMapper.deleteAll();
}

7.3 代码生成器

如果不想手动编写实体类等文件,MyBaitsPlus提供了代码生成器,它可以读取数据库的表信息,生成MyBaitsPlus代码供我们使用,之前我们学过MyBatis的代码生成器MyBatis Generator,这二者的区别是:

  • MBG基于xml文件配置的,MyBaitsPlus代码生成器是基于Java代码配置的。
  • MBG可生成实体类、Mapper接口、Mapper映射文件;MyBaitsPlus代码生成器可生成实体类、Mapper接口、Mapper映射文件、Service类、Controller类

 1、添加代码生成器所需的依赖

<!-- MyBatisPlus代码生成器 -->
<dependency>
  <groupId>***.baomidou</groupId>
  <artifactId>mybatis-plus-generator</artifactId>
  <version>3.5.1</version>
</dependency>
<!-- MyBatisPlus代码生成器需要的模板引擎 -->
<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity-engine-core</artifactId>
  <version>2.2</version>
</dependency>

2、编写代码生成器

package ***.zj.Generator;

import ***.baomidou.mybatisplus.generator.FastAutoGenerator;
import ***.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

public class MyGenerator {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql:///mybatis", "root", "123456")
                // 全局配置
                .globalConfig(builder -> {
                    builder.author("zhangjin") // 设置作者
                            .***mentDate("MM-dd") // 注释日期格式
                            .outputDir(System.getProperty("user.dir") + "/src/main/java") // 指定输出目录
                            .fileOverride(); //覆盖文件
                })
                // 包配置
                .packageConfig(builder -> {
                    builder.parent("***.zj") // 包名前缀
                            .entity("pojo") //实体类包名
                            .mapper("mapper") //mapper接口包名
                            .service("service") //service包名
                            .controller("controller") //controller包名
                            .xml("mapper"); //映射文件包名
                })
                // 策略配置
                .strategyConfig(builder -> {
                    builder.addInclude("tb_user") // 设置需要生成的表名,可以有多个
                            .addTablePrefix("tb_") // 设置表名前缀
                            .entityBuilder() // 开始实体类配置
                            .enableLombok() // 开启lombok模型
                            .naming(NamingStrategy.underline_to_camel) //表名下划线转驼峰
                            .columnNaming(NamingStrategy.underline_to_camel);//列名下划线转驼峰
                })
                .execute();
    }
}

3、运行代码生成器即可生成代码

7.4 MybatisX生成代码

MybatisX是一款基于IDEA的快速开发插件,为效率而生。

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Marketplace,输入 mybatisx 搜索并安装。

1、在IDEA中连接数据库

2、如下操作可以根据数据库表生成Mybaits代码

 

 7.5 MybatisX生成映射配置、代码跳转

 1、在Mapper接口中编写方法

public interface StudentMapper extends BaseMapper<Student> {
  List<Student> selectAllBySname(String sname);
}

2、如下操作即可在映射文件中自动生成映射配置

 

代码跳转

点击Mapper接口或映射文件前的小鸟图案,即可快速在Mapper接口与映射文件间跳转

转载请说明出处内容投诉
CSS教程_站长资源网 » SpringBoot与MybatisPlus MybatisPlus

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买