mybatis-plus 2.3
1.简介
Mybatis 增强工具包 - 只做增强不做改变,简化CRUD
操作
2. 优点
- 无侵入:Mybatis-Plus 在 Mybatis 的基础上进行扩展,只做增强不做改变,引入 Mybatis-Plus 不会对您现有的 Mybatis 构架产生任何影响,而且 MP 支持所有 Mybatis 原生的特性
- 依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring
- 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作
- 预防Sql注入:内置Sql注入剥离器,有效预防Sql注入攻击
- 通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题
- 支持热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
- 支持ActiveRecord:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作
- 支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(P.S. 比 Mybatis 官方的 Generator 更加强大!)
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 支持关键词自动转义:支持数据库关键词(order、key…)自动转义,还可自定义关键词
- 内置分页插件:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于写基本List查询
- 内置性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作
3. 官方文档
4. 快速入门
4.1 配置
Maven依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.3</version>
</dependency>
applicationContext.xml中配置
取代了原本的SqlSession,使用的是
com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
包
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 导入数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 配置实体类中的别名 -->
<property name="typeAliasesPackage" value="com.entity"></property>
<!-- 引入MP的全局策略 -->
<property name="globalConfig" ref="globalConfiguration"></property>
<!-- 配置xml文件 -->
<property name="mapperLocations">
<list>
<value>classpath:mapper/*-mapper.xml</value>
</list>
</property>
</bean>
mybatis-plus全局策略文件 , 需要使用就要在sqlsessionactory中给globalConfing中注入全局策略文件
<!-- 创建MP的全局策略文件 -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<property name="dbColumnUnderline" value="true"></property>
<!-- 全局主键生成策略-->
<property name="idType" value="0"></property>
<!-- 全局表前缀策略-->
<property name="tablePrefix" value="tbl_"></property>
</bean>
4.2 编写代码
写一个测试代码
创建实体类User 和对应数据库表
private int id;
private String name;
private int age;
private String phone;
创建原始 Mapper 类并继承通用 BaseMapper
创建Mapper类时 必须要先在applicationContext.xml 中创建接口或注解创建映射器
<!-- MapperScannerConfigurer 支持过滤由指定的创建接口或注解创建映射器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--把dao层下面的所以接口加入到ioc容器,并且以自身的名称首字母小写配置一个别名-->
<property name="basePackage" value="com.dao"></property>
</bean>
public interface UserDao extends BaseMapper<User> {
}
4.2.1 测试新增方法
public class UserService {
ApplicationContext con = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper usermapper= (UserMapper)con.getBean(UserMapper.class);
AddressMapper addressMapper = (AddressMapper)con.getBean(AddressMapper.class);
/**
* 测试 MP-insert()
* 插入一条记录
*/
@Test
public void insertTest01(){
User user = new User();
user.setName("zs");
user.setPwd("123456");
user.setAddid(1);
Integer row = usermapper.insert(user);
//判断是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
/**
* 测试 MP-insertAllColumn()
* 插入一条记录
*/
@Test
public void insertTest02(){
User user = new User();
user.setName("ls");
user.setPwd("123456");
user.setAddid(1);
Integer row = usermapper.insertAllColumn(user);
//判断是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
}
4.2.2 删除方法 - 结合构造器
/**
* 测试 MP-delete() 可以用构造器
* 根据 entity 条件,删除记录
* 需要一个new EntityWrapper<T>构造器 参数 后面加加上就是条件 可以跟N的条件
* .eq("数据库表的字段","参数值") 第一个参数是 数据库表的字段,第二个参数是 字段里的值
*/
@Test
public void deleteUserTest01(){
//第一种写法
// EntityWrapper<User> ew = new EntityWrapper<User>();
// ew.eq("id", "1");
// ew.eq("name","zs");
// Integer row = usermapper.delete(ew);
//第二种写法
Integer row = usermapper.delete(new EntityWrapper<User>()
.eq("id", "1")
);
//判断是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
/**
* 测试 MP - deleteBatchIds()
* 删除(根据ID 批量删除)
* idList – 主键ID列表
* 是一个List集合 数组不行
*/
@Test
public void deleteUserTest02(){
// int[] arr = new int[]{1,2};
List<Integer> userids = new ArrayList<Integer>();
userids.add(2);
userids.add(3);
Integer row = usermapper.deleteBatchIds(userids);
//判断是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
/**
* 测试 MP - deleteById()
* 根据 ID 删除
*/
@Test
public void deleteUserTest03(){
Integer row = usermapper.deleteById(2);
//判断是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
/**
* 测试 MP - deleteByMap()
* 根据 Map 条件,删除记录
* key -- 表的字段
* value -- 参数值
*/
@Test
public void deleteUserTest04(){
Map<String,Object> map = new HashMap<String, Object>();
map.put("id","1");
map.put("name","zs");
Integer row = usermapper.deleteByMap(map);
//判断是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
4.2.3 修改方法 - 结合构造器
/**
* 测试 MP - update() 可以用构造器
* 根据 whereEntity 条件,更新记录
* 第一个参数entity – 实体对象 第二个参数(修改的条件)wrapper – 实体对象封装操作类(可以为 null)
* 执行是会判断entity实体类中的每个属性值是否为空,为空则不作改变
*/
@Test
public void updateTest01(){
User user = new User();
user.setName("测试");
// user.setPwd("789");
user.setAddid(2);
//创建一个wrapper实体对象
EntityWrapper<User> ew = new EntityWrapper<User>();
ew.eq("id","2");
Integer row = usermapper.update(user, ew);
//构造器
// Integer row = usermapper.update(user,
// new EntityWrapper<User>().eq("id","2")
// );
//判断执行结果
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
/**
* 测试 MP- updateById()
* 根据主键id进行修改,定义entity实体对象时要给主键id赋值,没有给主键id赋值也会执行成功,但是数据库数据不会有改变
* 插入数据时,会根据实体类的每个属性进行非空判断,只有非空的属性所对应的字段才会出现在SQL语句中。
*/
@Test
public void updateTest02(){
User user = new User();
user.setId(5); //不给id赋值正常执行(不报错),但是数据不会改变
user.setName("测试根据id修改");
// user.setPwd("789");
user.setAddid(2);
//执行的时候会判断user对象中的所有属性是否为空,如果为空则为空的数据就不会发生任改变
Integer row = usermapper.updateById(user);
//验证执行是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
/**
* 测试 MP - updateAllColumnById()
* 根据 ID 修改 不给id时 也不会对数据库数组有改变
* 插入数据时,不管属性是否为空,属性所对应的字段都会出现在SQL语句中。
*/
@Test
public void updateTest03(){
User user = new User();
user.setId(3); //不给id赋值正常执行(不报错),但是数据不会改变
user.setName("updateAllColumnById测试2");
// user.setPwd("789"); //插入数据时,不管属性是否为空,属性所对应的字段都会出现在SQL语句中。
user.setAddid(2);
//执行的时候会判断user对象中的所有属性是否有为空的属性,如果为空则 为空的属性值就会在数据库中直接改为空(null)
Integer row = usermapper.updateAllColumnById(user);
//验证执行是否成功
if(row>0){
System.out.println("执行成功");
}
System.out.println("受影响行数:"+row);
}
/**
* 测试 MP - updateForSet
* 根据 whereEntity 条件,更新记录
* (知道有这么个东西,不会用)
*/
@Test
public void updateTest04(){
usermapper.updateForSet("ceshi",new EntityWrapper<User>());
}
4.2.4 查询方法 - 结合构造器 - 分页
/**
* 查询测试 -- selectById()
* 根据 ID 查询
* id – 主键ID
*/
@Test
public void selectTest01(){
User user = usermapper.selectById(2);
//验证是否查询到数据
if(user != null){
System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPwd()+"--"+user.getAddid());
}
}
/**
* 查询测试 -- selectBatchIds()
* 查询(根据ID 批量查询)
* idList – 主键ID列表 List集合
*/
@Test
public void selectTest02(){
List<Integer> idList = new ArrayList<Integer>();
idList.add(2);
idList.add(3);
idList.add(4);
List<User> list = usermapper.selectBatchIds(idList);
//遍历查询的所有数据
for (User user : list) {
System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPwd()+"--"+user.getAddid());
}
}
/**
* 查询测试 -- selectByMap()
* 查询(根据 Map 多条件查询)
* columnMap – 表字段 map 对象
* key 表中的字段名
* value 参数值
*/
@Test
public void selectTest03(){
Map<String,Object> map = new HashMap<String, Object>();
map.put("id","2");
map.put("name","测试");
List<User> list = usermapper.selectByMap(map);
//遍历查询的所有数据
for (User user : list) {
System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPwd()+"--"+user.getAddid());
}
}
/**
* 查询测试 -- selectCount()
* 根据 Wrapper 条件,查询总记录数
*/
@Test
public void selectTest04(){
Integer count = usermapper.selectCount(new EntityWrapper<User>()
.like("name","小")
);
System.out.println("根据like模糊查询的总数是:"+count);
}
/**
* 查询测试 -- selectList()
* 根据 构造器EntityWrapper 多条件,查询全部记录
* 不给参数默认查询全部
*
*/
@Test
public void selectTest05(){
// Integer[] userid = new Integer[]{2,3,4};
// EntityWrapper<User> ew = new EntityWrapper<User>();
// ew.in("id",userid); //in查询多种参数
List<User> list = usermapper.selectList(null);
//遍历查询的所有数据
for (User user : list) {
System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPwd()+"--"+user.getAddid());
}
}
/**
* 测试 --- selectMaps()
* 根据 Wrapper 条件,查询全部记录
* 可以为 null 查询全部
* return: List<Map<String, Object>>
*
*/
@Test
public void selectTest06(){
EntityWrapper<User> ew = new EntityWrapper<User>();
ew.ge("id","4");
List<Map<String, Object>> maps = usermapper.selectMaps(ew); //带条件查询
// List<Map<String, Object>> maps = usermapper.selectMaps(null); //无条件查询
//遍历结果集
for (Map<String, Object> map : maps) {
//key .keySet
//value .values()
// for (Object s : map.keySet()) {
// System.out.print(s);
// }
for (Map.Entry<String, Object> entr : map.entrySet()) {
System .out.println(entr.getKey()+":"+entr.getValue());
}
System.out.println();
}
}
/**
* 测试查询 -- selectObjs()
* 根据 Wrapper 条件,查询全部记录
* 注意: 只返回第一个字段的值
*/
@Test
public void selectTest08(){
EntityWrapper<User> ew = new EntityWrapper<User>();
ew.eq("id","2");
List<Object> list = usermapper.selectObjs(null);
for (Object o : list) {
System.out.println(o);
}
}
/**
* 测试查询 --- selectOne()
* 根据 entity 条件,查询一条记录
* 实体类中的 int类型的值必须要赋值,如果不赋值则默认是0,因此数据库中对于的值也必须要是0(否则报错,查不到对于的数据)
* 要么关闭单独关闭数据库映射,但是这样也拿不到值
* String 类型的值为默认值null的话就不根据此字段查询
*/
@Test
public void selectTest09(){
User user1 = new User();
user1.setId(2);
// user1.setName("测试");
user1.setAddid(1);
// user1.setPwd(null);
User user = usermapper.selectOne(user1);
System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPwd()+"--"+user.getAddid());
}
分页查询方法
/**
* 查询测试 -- selectMapsPage()
* 根据 Wrapper 条件,查询全部记录(并翻页)
* rowBounds – 分页查询条件(可以为 RowBounds.DEFAULT)
* wrapper – 实体对象封装操作类 (查询条件)
*/
@Test
public void selectTest07(){
Page page = new Page(3,2);
List<Map<String, Object>> maps = usermapper.selectMapsPage(page, null);
//遍历结果集
for (Map<String, Object> map : maps) {
//key .keySet
//value .values()
// for (Object s : map.keySet()) {
// System.out.print(s);
// }
for (Map.Entry<String, Object> entr : map.entrySet()) {
System .out.println(entr.getKey()+":"+entr.getValue());
}
System.out.println();
}
}
/**
* 测试查询 -- selectPage()
* 根据 entity 条件,查询全部记录(并翻页)
* rowBounds – 分页查询的条件(可以为 RowBounds.DEFAULT 查询全部)
* 选创建一个page对象 或者直接 在第一个参数中 new
* current 是第几页开始 底层是从0开始
* wrapper – 实体对象封装操作类(可以为 null
*/
@Test
public void selectTest010(){
Page page = new Page(2,3);
List<User> list = usermapper.selectPage(page, null);
System.out.println("总条数:"+page.getTotal());
System.out.println("当前页码:"+page.getCurrent());
System.out.println("总页码:"+page.getPages());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
for (User user : list) {
System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPwd()+"--"+user.getAddid());
}
}
4.3 常用注解
4.3.1 @TableName(“表名”)
当表名为 mp_user 时,在不改变 实体类的名字的情况下,通过添加 @TableName(“表名mp_user”)的方式来指定对应的数据库表名,以此解决在不编写sql语句的情况下,实体类的名字与数据库表名不一致而导致无法进行表操作的问题。
属性 类型 必须指定 默认值 描述 value String 否 “” 表名 schema String 否 “” schema keepGlobalPrefix boolean 否 false 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值) resultMap String 否 “” xml 中 resultMap 的 id autoResultMap boolean 否 false 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入) excludeProperty String[] 否 {} 需要排除的属性名(@since 3.3.1)
@TableName("mp_user") //此注解用于标识当前实体类所对应的表,编译时生效
public class User{
.....................
}
4.3.2 @TableField(“表字段名”)
使用 @TableField(“name”)来直接指定 user 实体类中某一个属性所对应的数据库表中的哪一列
常用的有 exist = false/true 是否和数据库表字段发生映射
属性 类型 必须指定 默认值 描述 value String 否 “” 数据库字段名 el String 否 “” 映射为原生 #{ ... }
逻辑,相当于写在 xml 里的#{ ... }
部分exist boolean 否 true 是否为数据库表字段 condition String 否 “” 字段 where
实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的%s=#{%s}
,参考(opens new window)update String 否 “” 字段 update set
部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于el
属性)insertStrategy Enum N DEFAULT 举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
updateStrategy Enum N DEFAULT 举例:IGNORED: update table_a set column=#{columnProperty}
whereStrategy Enum N DEFAULT 举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
fill Enum 否 FieldFill.DEFAULT 字段自动填充策略 select boolean 否 true 是否进行 select 查询 keepGlobalFormat boolean 否 false 是否保持使用全局的 format 进行处理 jdbcType JdbcType 否 JdbcType.UNDEFINED JDBC类型 (该默认值不代表会按照该值生效) typeHandler Class<? extends TypeHandler> 否 UnknownTypeHandler.class 类型处理器 (该默认值不代表会按照该值生效) numericScale String 否 “” 指定小数点后保留的位数
private Long id;
//用户名
@TableField("name")//当前注解用于指定属性对应的表字段
private String username;
4.3.3 @TableId
当前注解用于标注 javabean 中哪个字段是 主键 id
属性 类型 必须指定 默认值 描述 value String 否 “” 主键字段名 type Enum 否 IdType.NONE 主键类型
//主键
@TableId //主键策略
private Long id;
4.4 ActiveRecord 模式
AR模式的使用需要满足两个条件,如下:
条件1:必须存在对应的原始mapper并继承
Model<User>
泛型类,并配置相应类注解,
public class User extends Model<User>{
}
条件2: 在 mapper 接口 中要 继承 BaseMapper<Object.class> 泛型类
package com.mp.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mp.entity.User;
public interface UserMapper extends BaseMapper<User>{
}
测试使用
/**
* 测试 mybatisplus AR模式
*/
@Test
public void modle(){
User user1 = new User();
List<User> list = user1.selectAll();
for (User user : list) {
System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPwd()+"--"+user.getAddid());
}
}
5. mybatisplus扩展
5.1 逻辑删除
说明:
只对自动注入的sql起效:
- 插入: 不作限制
- 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
- 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
- 删除: 转变为 更新
例如:
- 删除:
update user set deleted=1 where id = 1 and deleted=0
- 查找:
select id,name,deleted from user where deleted=0
字段类型支持说明:
- 支持所有数据类型(推荐使用
Integer
,Boolean
,LocalDateTime
)- 如果数据库字段使用
datetime
,逻辑未删除值和已删除值支持配置为字符串null
,另一个值支持配置为函数来获取值如now()
附录:
- 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
- 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
第一步:
在
applicationContext.xml
中的mybatisplus
全局策略文件globalConfiguration
中在添加逻辑删除
<!-- 创建MP的全局策略文件 -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
....
<!-- 逻辑删除 -->
<property name="sqlInjector" ref="logicSqlInjector"></property>
<!-- 配置逻辑删除字段 -->
<property name="logicDeleteValue" value="1"></property>
<!-- 配置注册的字段 -->
<property name="logicNotDeleteValue" value="0"></property>
....
</bean>
ref引用配置
<!-- 逻辑删除 -->
<bean id="logicSqlInjector" class="com.baomidou.mybatisplus.mapper.LogicSqlInjector"></bean>
第二步:
实体类字段上加上
@tablelogic
注解
@TableLogic //逻辑删除注解
private int addid;
第三步:
测试使用
逻辑删除只要是执行了
delete
删除语句时 mp 就会给你执行update
修改语句 修改配置了注解的字段改为配置中预定的值, 而且在配置了逻辑删除后, 在执行mybatisplus的
CRUD
操作都会带一个条件
/**
* 逻辑删除
* 删除: 转变为 更新
*/
@Test
public void logicDelete(){
// Integer integer = usermapper.deleteById(5); //单个删除
Integer name = usermapper.delete(new EntityWrapper<User>().like("name", "逻辑删除")); //条件删除
if(name > 0){
System.out.println("删除成功");
}
}
实际执行的sql语句
Time:14 ms - ID:com.mapper.UserMapper.delete
Execute SQL:
UPDATE
tbl_user
SET
addid=1
WHERE
addid=0
AND (
name LIKE '%逻辑删除%'
)]
5.2 配置全局共属性默认值
在mp全局策略中添加
<!-- 全局共属性默认值 -->
<property name="metaObjectHandler" ref="metaObjectHandler"></property>
ref引用配置
注意这里的class 是自己的创建的类
<!-- 全局公共属性默认值 -->
<bean id="metaObjectHandler" class="com.Handler.MyObjectHandler"></bean>
MyObjectHandler类
需要继承一个 MetaObjectHandler
配置完之后在执行mp的CRUD操作时就会给配置的公共属性附一个默认值
public class MyObjectHandler extends MetaObjectHandler {
/**
* 新增时自动注入的值
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
Object ob = getFieldValByName("pwd",metaObject);
//等于空说明该字段需要附一个默认值,不为空则说明用自定义的值
if(ob == null){
//第一个值是需要添加的默认的属性名(实体类中的属性名),第二个参数是默认值,第三个参数metaObject
setFieldValByName("pwd","公共属性默认值",metaObject);
}
}
/**
* 修改是自动注入的值
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
Object ob = getFieldValByName("pwd",metaObject);
//等于空说明该字段需要附一个默认值,不为空则说明用自定义的值
if(ob == null){
//第一个值是需要添加的默认的属性名(实体类中的属性名),第二个参数是默认值,第三个参数metaObject
setFieldValByName("pwd","公共属性默认值",metaObject);
}
}
}
5.3 乐观锁
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
使用方法
字段上加上@Version
注解
@Version
private Integer version;
说明:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1
newVersion
会回写到entity
中- 仅支持
updateById(id)
与update(entity, wrapper)
方法 - 在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
在
sqlSessionFactoryBean
中给plugins
注入值
<property name="plugins">
<list>
.... <!-- 这里还有其他插件-->
<!-- 注册乐观锁-->
<bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor"></bean>
</list>
</property>
测试使用
只对
updateById
方法起作用
/**
* 乐观锁测试 updateById只查询单个值, 乐观锁起作用
*/
@Test
public void versino(){
User user = new User();
user.setId(4);
user.setName("测试乐观锁333");
user.setVersion(2);
Integer update = usermapper.updateById(user);
if(update > 0 ){
System.out.println("修改成功");
}
}
/**
* 测试update的乐观锁, update根据条件查询能查出多个值, 不能确定修改某一个值 所有乐观锁没用
*/
@Test
public void version2(){
User user = new User();
user.setName("测试乐观锁444");
usermapper.update(user,new EntityWrapper<User>().eq("id","5").eq("version","0"));
}