MyBatis-Plus

MyBatis-Plus

MP 快速入门

  1. 建立基于 SpringBoot 的 Maven 工程,并引入相关依赖,
<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-thymeleaf</artifactId>

</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-devtools</artifactId>

    <scope>runtime</scope>

    <optional>true</optional>

</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-configuration-processor</artifactId>

    <optional>true</optional>

</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>

    <artifactId>lombok</artifactId>

    <optional>true</optional>

</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-test</artifactId>

    <scope>test</scope>

</dependency>

<dependency>
    <groupId>com.alibaba</groupId>

    <artifactId>druid-spring-boot-starter</artifactId>

    <version>1.2.8</version>

</dependency>


<dependency>
    <groupId>mysql</groupId>

    <artifactId>mysql-connector-java</artifactId>

</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-jdbc</artifactId>

</dependency>


<dependency>
    <groupId>com.baomidou</groupId>

    <artifactId>mybatis-plus-boot-starter</artifactId>

    <version>3.5.2</version>

</dependency>

  1. 为 SpringBoot 工程添加 application.yml 配置文件
server:
  port: 8080
  servlet:
    context-path: /ls
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: 'jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=Asia/Shanghai'
  1. 编写 SpringBoot 的启动器,同时使用注解扫描 Mapper 接口
@SpringBootApplication
@MapperScan(basePackages = {"com.ls.lsmp0720.mapper"})
public class Lsmp0720Application {

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

}
  1. 编写 POJO 实体 Bean
@Data
@ToString
@NoArgsConstructor
@TableName(value = "user") // 对应的表名
public class User {
    @TableId(type = IdType.AUTO) // 指定主键,主键的值的生成策略
    private Integer userId;

    private String userName;

    private String roleCode;

    private String userPass;

    @TableField(condition = SqlCondition.LIKE) // 用于条件判断时 判断形式
    private String cname;

    private String isLogin;
    @TableField(value = "telphone")
    private String telephone;

    private String address;

    private String nation;

    private String email;

    private String userImg;

    private String gender; // 1 2

    @TableField(exist = false)// 在进行表的相关操作时去掉该字段(表中不存在),否则会报错
    private String sex; // 性别描述 女 男
}

排除非表字段还可使用: transiant static 但推荐@TableField(exist = false)

  1. 定义 Mapper 接口并继承 MP 的 BaseMapper 接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
  1. 编写测试类
  • baseMapper的一些测试:
@Test // 改
void contextLoadsUpdate() {
    User user = new User();
    user.setUserId(712);
    user.setAddress("济南长清");
    user.setRoleCode("plain");
    user.setTelephone("18012344321");
    userMapper.updateById(user);
}

@Test // 查
void contextLoadsByMap() {
    Map<String,Object> map = new HashMap<>();
    map.put("role_code","plain");
    map.put("address","济南浪曦");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}
 
@Test // 增
void contextLoadsInsert() {
    User newUser = new User();
    newUser.setUserName("jacky");
    newUser.setUserPass("123456");
    newUser.setCname("杰克王");
    userMapper.insert(newUser);
}

条件构造器

MP 框架提供了强大的条件构造器,使用条件构造器可以用来组装更加复杂的查询 SQL。条件构造器通过 Wrapper 类(抽象类) 来实现,Wrapper 中提供了大量的条件组合

  • like eq lt
@Test
void contextLoadsWrapper001() {
    // 条件构造器 是 复杂操作的基础. Wrapper类是抽象类
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    // 查询cname中有小 且 性别是1 id<705
    userQueryWrapper.like("cname","小")
            .eq("gender",1)
            .lt("user_id",705);
    List<User> users = userMapper.selectList(userQueryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • like: 两边模糊 likeRight: 右模糊 likeLeft: 左模糊
  • between isNotNull
@Test
void contextLoadsWrapper002() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // id 在7005-712之间,email不是null的
    queryWrapper.between("user_id",705,712)
            .isNotNull("email");
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • or() orderByDesc

    • 条件1.or().条件2
@Test
void contextLoadsWrapper003() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 查询 地址 是 济南长清 或者 角色为管理员并且性别是1的 最后按照id降序排序
    queryWrapper.eq("address","济南长清")
            .or()
            .eq("role_code","manager").eq("gender",1)
            .orderByDesc("user_id");
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • 应用mysql函数(自定义代码块)
@Test
void contextLoadsWrapper004() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 应用mysql函数
    // 查询 用户名长度是5个的 并且 性别是1
    //queryWrapper.apply("char_length(user_name)={0} and gender = {1}",5,1);
    queryWrapper.apply("char_length(user_name)={0}",5).eq("gender",1);
    // 占位符从0开始计算
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • {0} {1} 为占位符
  • inSql 子查询
@Test
void contextLoadsWrapper005() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // inSql 子查询 用到in语句里
    // 查询 是管理员 并且user_id在地址为济南浪曦的id集合当中
    queryWrapper.eq("role_code","manager")
            .inSql("user_id","select user_id from user where address = '济南浪曦'");
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • and 将某些条件作为整体
    • 查询角色是plain的 并且 (要么是乌克兰 要么邮箱是空的)
@Test
void contextLoadsWrapper006() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
  queryWrapper.eq("role_code","plain")
            .and(wr -> wr.eq("nation","乌克兰").or().isNull("email"));
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • neated() 嵌套 (跟and()一致,推荐使用and())

@Test
void contextLoadsWrapper007() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 查询角色是plain 并且 (要么是乌克兰 要么邮箱是空的)
    queryWrapper.eq("role_code","plain")
            .nested(wr -> wr.eq("nation","乌克兰").or().isNull("email"));
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • 指定查询或排除字段
    • select(String… columns) 指定要查的列,适用于要查询出来的列较少的情况下
@Test
void contextLoadsWrapperA() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 指定要查询出来的列,适用于要查询出来的列较少的情况下
    queryWrapper.like("cname","小")
            .select("user_id","user_name","user_pass","cname");
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • select(Class,Predicated predicate) 指定排除一些列,适用于 较少列不需要查询的情况下
@Test
void contextLoadsWrapperB() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 指定排除一些列,适用于 较少列不需要查询的情况下
    queryWrapper.like("cname","小")
            .select(User.class,info -> !"user_img".equals(info.getColumn()) && !"gender".equals(info.getColumn()));
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}
  • condition 参数(后面查询的条件)

@Test
void contextLoads072201() {
    // 当 cname 的值非空时 才进行条件查询
    String cname = "小";
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like(StringUtils.hasLength(cname),"cname",cname);
    List<User> users = userMapper.selectList(queryWrapper);
    for (User user : users) {
        System.out.println(user);
    }
}

Wrapper 构造方法

QueryWrapper 条件构造器提供了有参的构造方法,可以将一个 POJO 对象作为参数传递给构造方法,用于创 建 QueryWrapper 对象。使用这种方式创建的条件对象,会将 POJO 对象中不为 null 的属性值作为 where 条件, 组成查询 sql

  • 默认使用“等值”判断
  • 要在实体 Bean 上使用注解@TableField 来修饰,@TableField 的属性 condition 默认就是采用等值连 接的方式,更改 condition 的属性值就可以更改连接方式

@Test
void contextLoads072202() {
    User user = new User();
    user.setRoleCode("plain");
    user.setNation("中国");
    user.setCname("小");// 在javabean中设置为模糊
    // 使用javabean创建条件构造器,默认对javabean中非空的属性 进行等值的拼接
    QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
    queryWrapper.orderByDesc("user_id");
    List<User> users = userMapper.selectList(queryWrapper);
    for (User u : users) {
        System.out.println(u);
    }
}

map

  • allEq 将map里的键值对进行等值条件拼接
@Test
void contextLoads072203() {
    // allEq 将map里的键值对进行等值条件拼接
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    Map<String,Object> map = new HashMap<>();
    map.put("role_code","plain");
    map.put("nation","中国");
    queryWrapper.allEq(map);
    List<User> users = userMapper.selectList(queryWrapper);
    for (User u : users) {
        System.out.println(u);
    }
}
  • userMapper.selectMaps(queryWrapper) 查询少量列 组装为map 适合查询少量的列
@Test
void contextLoads072204() {
    // 查询少量列 组装为map 适合查询少量的列
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("user_name","l")
            .eq("gender",1)
            .select("user_name","gender");
    List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
    for (Map<String, Object> map : maps) {
        System.out.println(map);
    }

}
  • selectCount()
@Test
void contextLoads072205() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("user_name","l")
            .eq("gender",1);
    Long count = userMapper.selectCount(queryWrapper);
    System.out.println("名字里有l且性别是1的人数: " + count);
}

lambda 条件构造器

优点: 通过javabean的成员变量 替代 表的列名

  • Lambda 条件构造器提供了三种创建方式:
    • LambdaQueryWrapper wrapper1 = new LambdaQueryWrapper<>();
    • LambdaQueryWrapper wrapper2 = new QueryWrapper().lambda();
    • LambdaQueryWrapper wrapper3 = Wrappers.lambdaQuery();
@Test
void contextLoads072206() {
    // lambda 条件构造器 优点: 通过javabean的成员变量 替代 表的列名
    LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
    // 查询昵称里有小字的 id降序排序
    lambdaQueryWrapper.like(User::getCname,"小")
            .orderByDesc(User::getUserId);
    List<User> users = userMapper.selectList(lambdaQueryWrapper);
    users.forEach(System.out::println);
}

自定义sql

  • 注解版

    • 必须写@Param(“ew”) ${ew.customSqlSegment}用来拼接wrapper中的语句
@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("select user_name,user_pass,email from user ${ew.customSqlSegment} ")
    List<User> selectMy01(@Param("ew") Wrapper<User> wrapper);// 查询用户名,密码和邮箱

    List<User> selectMy02(@Param("ew") Wrapper<User> wrapper);// 查询用户名,密码和电话
}
  • xml版

<mapper namespace="com.ls.lsmp0720.mapper.UserMapper">

    <select id="selectMy02" resultType="com.ls.lsmp0720.bean.User">
        select user_name,user_pass,telphone as telephone from user ${ew.customSqlSegment}
    </select>

    <!-- 自定义sql语句字段和列名的注解配置会失效,需要自己写别名才能封装到bean中 -->
</mapper>

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    lazy-loading-enabled: true
    aggressive-lazy-loading: false # 默认开
  #    map-underscore-to-camel-case: true 默认
  mapper-locations: 'classpath*:/mapper/**/*.xml'

classpath*:/mapper/**/*.xml 为默认值,若在此,可不配

  • test
@Test
void contextLoads072207() {
    User user = new User();
    user.setRoleCode("plain");
    user.setCname("小");
    QueryWrapper<User> wrapper = new QueryWrapper<>(user);

    //List<User> users = userMapper.selectMy01(wrapper);
    List<User> users = userMapper.selectMy02(wrapper);

    users.forEach(System.out::println);
}

MP分页插件

  • 配置类中加入插件
@Configuration
public class MyConfig {

    //MybatisPlusInterceptor 核心插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}
  • 测试
@Test
void contextLoads072208() {
    // IPage<T> 和 实现类 Page<T> 是分页模型 约等于 之前的PageBean<T>
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByDesc("user_id");
    // 分页查询 泛型是User            是否封装总记录数
    Page<User> page = new Page<>(1,4,true);
    page = userMapper.selectPage(page,wrapper);
    // userMapper.selectMapPage() 需要入参page的泛型为Map,适用于查询列数不多
    // Page<Map<String,Object>>

    System.out.println("总记录数 = "+page.getTotal());
    System.out.println("每页条数 = "+page.getSize());
    System.out.println("总页数 = "+page.getPages());
    List<User> users = page.getRecords();// 获取查询结果
    users.forEach(System.out::println);
}
  • 自定义 sql 分页

    在进行分页操作时,可能查询的数据并不是来源于一张表,可能会是多表的联合查询。这种情况 selectPage 方 法与 selectMapsPage 方法就不再适用。此时就可以使用自定义 sql 的分页查询,定义方法时第一个参数必须是 page 对象,第二个参数为 Wrapper 对象

  • UserMapper接口

Page<User> selectMyPage01(Page<User> page, @Param("ew") Wrapper<User> wrapper);
  • UserMapper.xml
<select id="selectMyPage01" resultType="com.ls.lsmp0720.bean.User">
    select user_name,user_pass,telphone as telephone,address,email from user ${ew.customSqlSegment}
</select>

  • test
@Test
void contextLoads072209() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByDesc("user_id");
    Page<User> page = new Page<>(1,4,true);
    page = userMapper.selectMyPage01(page, wrapper);
    System.out.println("总记录数 = "+page.getTotal());
    System.out.println("每页条数 = "+page.getSize());
    System.out.println("总页数 = "+page.getPages());
    List<User> users = page.getRecords();
    users.forEach(System.out::println);
}

更新

@Test
void contextLoads072210Update() {
    //1.按id更新,更新javabean的非空成员变量对应的字段
    /*User user = new User();
    user.setUserId(713);
    user.setTelephone("19900001111");
    user.setAddress("天津");
    userMapper.updateById(user);*/
    
    //2.按UpdateWrapper更新
    /*UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    User user = new User();// 更新条件
    user.setEmail("jiji@qq.com");
    wrapper.eq("cname","吉吉国王");// 筛选条件
    userMapper.update(user,wrapper);*/

    //3.按UpdateWrapper更新
    UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    User user = new User();
    user.setIsLogin("0");// 更新条件
    wrapper.eq("cname","吉吉国王") // 其他的like lt isNull等条件的方法都可以使用
            .set("gender",1);// 更新条件
    userMapper.update(user,wrapper);
}

删除

@Test
void contextLoads072210Delete() {
    // 1. 按照id删除
    //userMapper.deleteById(716);
   /* User user = new User();
    user.setUserId(715);
    userMapper.deleteById(user);*/

    // 2. 按照Wrapper删除
    /*QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("user_name","2").eq("user_pass","2");
    userMapper.delete(wrapper);*/

    //3. 按照多个id删除
    /*List<Integer> list = new ArrayList<>();
    list.add(718);
    list.add(719);
    userMapper.deleteBatchIds(list);*/

    //4. 按照map删除
    Map<String,Object> map = new HashMap<>();
    map.put("user_name","a");
    map.put("user_pass","a");
    userMapper.deleteByMap(map);
}

ActiveRecord 模式

让javabean具备基本的增删改查操作,必须有对应的Mapper接口 继承BaseMapper

  • 让模型类继承 Model 抽象类
public class User extends Model<User> {
  • 测试
@Test
void contextLoads072211() {
    // 通用service里有单表的操作方法,方法名系列 跟 mapper里的方法名系列 有区别
    User user = new User();
    user.setUserName("xionger");
    user.setUserPass("123");
    user.setCname("熊二");
    userService.save(user);
}

@Test
void contextLoads072210AR() {
    // 让javabean具备基本的增删改查操作,必须有对应的Mapper接口 继承BaseMapper
    User user = new User();
    user.setUserId(712);
    user.setIsLogin("0");
    user.setAddress("上海");
    user.updateById();
}

主键策略

局部主键策略 (推荐)

  • type = IdType.AUTO 采用数据库中的主键自增
  • type = IdType.INPUT 采用用户的输入
  • type = IdType.ASSIGN_ID 如果用户未输入,则采用mp的算法生成主键值,可以是整数,也可以是字符串
  • type = IdType.ASSIGN_UUID 如果用户未输入,则采用mp的算法生成主键值,是字符串

全局主键策略

mybatis-plus:

 global-config:

  db-config:

   id-type: xxx

mp配置

https://baomidou.com/config/

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    lazy-loading-enabled: true # 这个跟下面搭配使用
    aggressive-lazy-loading: false # 默认开
  #    map-underscore-to-camel-case: true 默认
  mapper-locations: 'classpath*:/mapper/**/*.xml'
  global-config:
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0
#  type-aliases-package: com.ls.lsmp0720.bean 不推荐

通用Service

  1. 定义 Service 接口继承 MP 框架提供的 IService 接口
  2. 定义 Service 接口的实现类,继承 ServiceImpl 类,再实现自定义 Service 接口
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
@Test
void contextLoads072211() {
    // 通用service里有单表的操作方法,方法名系列 跟 mapper里的方法名系列 有区别
    User user = new User();
    user.setUserName("xionger");
    user.setUserPass("123");
    user.setCname("熊二");
    userService.save(user); // 更新
}

MyBatis Plus 进阶

逻辑删除

在执行 deleteById 操作时,不再是 delete 操作,而是转变为 update 操作,并 且自动的增加了逻辑删除字段的判断。将 deleted 字段更新为指定值

  • yml配置

  • 在实体 bean 中使用@TableLogic 注解修饰逻辑删 除字段

@Data
@ToString
@NoArgsConstructor
@TableName("employee")
public class Employee {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private Long manageId;
    private String createTime;
    @Version
    private Integer version;// 乐观锁字段
    @TableLogic()
    @TableField(select = false)// 不再查询该字段
    private Integer deleted; // 逻辑删除字段 0: 未删除 1: 已删除
}
  • test
@Test
void contextLoads072212() {
    // 逻辑删除字段
    // employeeDao.deleteById(1312931436365443079L);
    // 当进行查询或更新时,mp自动排除 已经逻辑删除的记录
    QueryWrapper<Employee> wrapper = new QueryWrapper<>();
    wrapper.le("age",200);
    List<Employee> employees = employeeDao.selectList(wrapper);
    employees.forEach(System.out::println);
}
  • 自定义sql中 关于逻辑字段的查询 既不自动将逻辑删除字段纳入条件,也不排除查询逻辑删除字段
@Mapper
public interface EmployeeDao extends BaseMapper<Employee> {
    @Select("select id,name,age,email,manage_id,create_time,version from employee ${ew.customSqlSegment}") // 手动排除查询逻辑删除字段
    List<Employee> selectMy01(@Param("ew")Wrapper<Employee> wrapper);
}
@Test
void contextLoads072213() {
    QueryWrapper<Employee> wrapper = new QueryWrapper<>();
    wrapper.le("age",200)
            .eq("deleted",0);// 手动选择
    List<Employee> employees = employeeDao.selectMy01(wrapper);
    employees.forEach(System.out::println);
}

自动填充

填充原理是直接给 entity 的属性设置值,setFieldValByName 方法是 MetaObjectHandler 的默认方法,调用此方法就可以为实体对象的属性完成赋值操作.

  • 使用注解@TableField 对要进行自动填充在字段进行修饰

@TableField(fill = FieldFill.INSERT)
private String createTime;
  • 使用属性 fill 来指定填充的类型。并在 Spring 容器中,增加一个元数据类型处理器 MetaObjet
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        boolean has = metaObject.hasSetter("createTime");// 表中有这个字段
        if (has){
            Object createTime = metaObject.getValue("createTime");
            if (createTime == null){// 还没有被赋值过
                // insert 插入时,自动填充的属性 createTime
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String value = sdf.format(new Date());
                setFieldValByName("createTime",value,metaObject);
            }
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {

    }
}
  • 测试
@Test
void contextLoads072214() {
    // 自动填充
    Employee e = new Employee();
    e.setName("大朱静");
    e.setAge(98);
    employeeDao.insert(e);
}

乐观锁插件

乐观锁的意图,当要更新表的一条记录的时候,希望这条记录没有被别人更新,是为了防止更新冲突的问题。乐观锁的实现方式,其中一种最为常见的就是版本号的机制。取出一条记录时,会获得这条记录当前的版本号,如果在 更新时版本号没有发生变化,说明这条记录没有被别人更新,如果版本号已经发生变化了,则不再更新这条记录。
执行过程为:取出记录时,获取当前版本 version;更新时,带上这个版本 version;版本正确则更新成功,错 误则更新失败。
乐观锁功能实现:

  • 配置 OptimisticLockerInterceptor 乐观锁插件类
@Configuration
public class MyConfig {

    //MybatisPlusInterceptor 核心插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());// 乐观锁
        
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 分页
        return mybatisPlusInterceptor;
    }
}
  • 使用注解@Version 修饰实体
@Version
private Integer version;// 乐观锁字段
  • 测试
@Test
void contextLoads072215() {
    // 乐观锁字段
    Employee e1 = new Employee();
    e1.setId(1312931436365443081L);
    e1.setEmail("da1@qq.com");
    e1.setVersion(1);
    employeeDao.updateById(e1);
    System.out.println("-------------------");
    System.out.println(e1);// 更新后的字段回填 更新后版本号在原来的基础上加一

    Employee e2 = new Employee();
    e2.setId(1312931436365443081L);
    e2.setEmail("dadada@126.com");
    e2.setVersion(1);// 修改失败
    employeeDao.updateById(e2);
}
  • 同一个程序中同一个wrapper不能复用,因为会将连个版本都加上
@Test
void contextLoads072216() {
    // 同一个wrapper不能复用
    Employee e1 = new Employee();
    e1.setEmail("e4@qq.com");
    e1.setVersion(4);
    UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
    wrapper.eq("id",1312931436365443081L);// 筛选
    employeeDao.update(e1,wrapper);
    System.out.println("========================");

    Employee e2 = new Employee();
    e2.setEmail("suibian@qq.com");
    e2.setVersion(5);
    employeeDao.update(e2,wrapper);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值