MybatisPlus简单到入门

一、MybatisPlus简介

1、入门案例(重点):

1.SpringBoot整合MP1).创建新模块选择,Spring项初始化。2).选择当前模块使用的技术,只保留MySQL Driver就行,不要选择mybatis避免与后面导入mybatisPlus的依赖发生冲突。`使用联网创建工程,有两个弊端,所以在以后工作中基本不使用:1、必须联网2、只能选最近几个比较新的SpringBoot版本,企业开发追求稳定,一般不会用太新的版本`企业工作中,直接创建一个Maven工程就行,自行指定继承的SpringBoot父工程3).企业中手动创建项目,指定版本group: com.itheima项目名:mp_demojdk:使用8
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.0</version><relativePath/> <!-- lookup parent from repository -->
</parent><dependencies><!--注意事项1:由于mybatisPlus并未被收录到idea的系统内置配置,无法直接选择加入--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><!--数据源 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
   4).制作实体类与表结构(类名与表名对应,属性名与字段名对应)
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (id bigint(20) primary key auto_increment,name varchar(32) not null,password  varchar(32) not null,age int(3) not null ,tel varchar(32) not null
);
insert into user values(null,'tom','123456',12,'12345678910');
insert into user values(null,'jack','123456',8,'12345678910');
insert into user values(null,'jerry','123456',15,'12345678910');
insert into user values(null,'tom','123456',9,'12345678910');
insert into user values(null,'snake','123456',28,'12345678910');
insert into user values(null,'张益达','123456',22,'12345678910');
insert into user values(null,'张大炮','123456',16,'12345678910');
package com.itheima.domain;
//pojo, entity, domain@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;//可以使用lombok自动生成//自行添加getter、setter、toString()等方法
}
  5).设置Jdbc参数(application.yml)
spring:datasource:type: com.alibaba.druid.pool.DruidDataSource #可选,如果使用必须引入Druid坐标driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTCusername: rootpassword: root
  6).定义数据接口,继承BaseMapper@Mapper
public interface UserDao extends BaseMapper<User> {//注意:BaseMapper后边必须指定泛型User,以此来确定操作的表
}
  7).测试类中注入dao接口,测试功能SpringBoot引导类(必须放到某一个包下)@SpringBootApplication
public class Mybatisplus01QuickstartApplication {public static void main(String[] args) {SpringApplication.run(Mybatisplus01QuickstartApplication.class, args);}
}
/************************************************************************************/打开自动生成的测试类,编写单元测试:@SpringBootTest
public class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll() {List<User> userList = userDao.selectList(null);System.out.println(userList);}@Testvoid testFindById() {//业务层: get(list), remove, modify(update), save//数据层:select, delete, update, insertUser user = userDao.selectById(1);System.out.println(user);}
}

2、MyBatisPlus概述

MyBatisPlus介绍- MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率。
- 官网:https://mybatis.plus/  https://mp.baomidou.comMyBatisPlus特性:- 无侵入:只做增强不做改变,不会对现有工程产生影响
- 强大的 CRUD 操作:内置通用 BaseMapper,少量配置即可实现单表CRUD 操作
- 支持 Lambda:编写查询条件无需担心字段写错
- 支持主键自动生成(雪花算法)
- 内置分页插件(MyBatis分页需要用到Pagehelper通过拦截器给SQL追加limit 0,10)
- ……

二、标准数据层开发【重点】

1、CRUD操作

功能自定义接口MP接口
新增boolean save(T t)int insert(T t)
删除boolean delete(int id)int deleteById(Serializable id)
修改boolean update(T t)int updateById(T t)
根据id查询T getById(int id)T selectById(Serializable id)
查询全部List<T> getAllList<T> selectList()
分页查询PageInfo<T> getAll(int page,int size)IPage<T> selectPage(IPage<T> page)
按条件查询List<T> getAll(Condition condition)IPage<T> selectPage(Wrapper<T> queryWrapper)
编写单元测试,完成对user表的增删查改:
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testSave() {User user = new User();user.setName("程序员");user.setPassword("it");user.setAge(12);user.setTel("400000");userDao.insert(user);}@Testvoid testDelete() {userDao.deleteById(2L);}@Testvoid testUpdate() {User user = new User();user.setId(1L);user.setName("Tom888");user.setPassword("tom888");userDao.updateById(user);}@Testvoid testGetById() {User user = userDao.selectById(2L);System.out.println(user);}@Testvoid testGetAll() {List<User> userList = userDao.selectList(null);System.out.println(userList);}
} 

2、Lombok插件【常用】

Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。
<!--自动生成get,set,toString等方法-->
<!--实体类上添加@Data即可-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>常用注解:@Data,为当前实体类在编译期设置对应的get/set方法,无参/无参构造方法,toString方法,hashCode方法,equals方法等
package com.itheima.domain;import lombok.*;
/*1 生成getter和setter方法:@Getter、@Setter生成toString方法:@ToString生成equals和hashcode方法:@EqualsAndHashCode2 统一成以上所有:@Data3 生成空参构造: @NoArgsConstructor生成全参构造: @AllArgsConstructor4 lombok还给我们提供了builder的方式创建对象,好处就是可以链式编程。 @Builder【扩展】*/
@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;
}

3、分页功能

1、引入Pagehelper坐标(通过拦截器给sql语句加limit 0,10)
2、Pagehelper.startPage(size, page)
1).设置分页拦截器作为Spring管理的bean@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){//1 创建MybatisPlusInterceptor拦截器对象MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();//2 添加分页拦截器:当调用selectPage方法时自动在最后追加分页实现:limit 0,10mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mpInterceptor;}
}2).执行分页查询//分页查询
@Test
void testSelectPage(){//1 创建IPage分页对象,设置分页参数IPage<User> page=new Page<>(1,3);//2 执行分页查询userDao.selectPage(page,null);//框架底层,会把查询到的数据放到page对象中//page.setRecords(new ArrayList<>());//page.setTotal(100);//3 获取分页结果System.out.println("当前页码值:"+page.getCurrent());System.out.println("每页显示数:"+page.getSize());System.out.println("总页数:"+page.getPages());System.out.println("总条数:"+page.getTotal());System.out.println("当前页数据:"+page.getRecords());
}
3).输出SQL到控制台spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTCusername: rootpassword: root
# 开启mp的日志(输出到控制台)
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

三、DQL编程控制【重点】

DQL:数据查询Query语言(select)
DML:数据操作语言(insert,update,delete)

1、简化日志【了解】

SpringBoot底层是logback框架记录日志的
做法:在resources下新建一个logback.xml文件,名称固定,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration></configuration>
# 取消SpringBoot启动banner图标#spring:main:banner-mode: off # 关闭SpringBoot启动图标(banner)## 取消MybatisPlus启动banner图标# mybatis-plus日志控制台输出
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:banner: off # 关闭mybatisplus启动图标

2、条件查询方式

1、条件查询方式MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。//方式一:按条件查询
QueryWrapper<User> qw = new QueryWrapper<>();
//lt小于:less than; gt大于:greater than
//le小于等于:less than or equal
//ge大于等于:greater than or equal
qw.lt("age", 18); //where age < 18
List<User> userList = userDao.selectList(qw); //select * from user
System.out.println(userList);
2、lambda格式按条件查询//方式二:lambda格式按条件查询qw.lambda()
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge, 18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
3、lambda格式按条件查询(推荐)//方式三:lambda格式按条件查询
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.lt(User::getAge, 18);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);//方式三变种1:通过Wrappers的静态方法获取
//LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
//lqw.lt(User::getAge, 18);
//List<User> userList = userDao.selectList(lqw);
//System.out.println(userList);//方式三变种2:链式编程
//List<User> users = userDao.selectList(
//        Wrappers.<User>lambdaQuery() //产生查询条件对象
//                .lt(User::getAge, 18));
//System.out.println(users);

3、组合条件查询

1、并且关系(and)//并且关系:and
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//并且关系:10到30岁之间,默认多个条件使用and连接
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
2、或者关系(or)//或者关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//或者关系:小于18岁或者大于10岁
//where age < 18 or age > 10
lqw.lt(User::getAge, 18).or().gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
3NULL值处理在多条件查询中,有条件的值为空1).模拟用户查询条件类:UserQuery 继承需要限制条件的类,定义相同类型的属性限制需要被限制的字段。@Data
public class UserQuery extends User {//此时对年龄进行限制private Integer age2; //最大年龄
}
/********************************************************************************************************/2).if语句控制条件追加//模拟页面传递过来的查询数据
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, uq.getAge2());//null判定
if (null != uq.getAge()) {lqw.gt(User::getAge, uq.getAge());
}
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
/*************************************************************************************************************/3).条件参数控制【常用】//null判断:第二种,条件参数控制
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//先判定第一个参数是否为true,如果为true连接当前条件
lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2());
lqw.gt(null != uq.getAge(), User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);/**************************************************************************************************************/4).条件参数控制(链式编程)//先判定第一个参数是否为true,如果为true连接当前条件
//lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2());
//lqw.gt(null != uq.getAge(), User::getAge, uq.getAge());
lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2()).gt(null != uq.getAge(), User::getAge, uq.getAge());

4、查询投影

1.查询结果包含实体类中部分属性【常用】LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId, User::getName, User::getAge);/*QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id", "name", "age", "tel");*/List<User> userList = userDao.selectList(lqw);
System.out.println(userList);    
2 查询结果包含实体类中未定义的属性【了解】这个统计SQL在企业开发中,一般会使用MyBatis在xml中编写sql
SELECT COUNT(*) count,tel FROM user GROUP BY tel
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count, tel"); //统计lqw.groupBy("tel"); //分组
//qw.lambda().groupBy(User::getTel);List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);//System.out.println(userDao.selectCount(null)); //查询总记录数
# 3、查询条件设置多条件查询有哪些组合?- 范围匹配(> 、 = 、between)
- 模糊匹配(like)
- 空判定(null)
- 包含性匹配(in)
- 分组(group)
- 排序(order by)
- ……
- 用户登录(eq匹配)LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//eq()等同于=
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
//如果查询返回的不是一条数据或者null,如果有多条直接抛异常
User loginUser = userDao.selectOne(lqw); 
System.out.println(loginUser);
- 购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt le gt ge eq between
lqw.between(User::getAge, 10, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 查信息,搜索新闻(非全文检索版:like匹配)//模糊匹配
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//%,_必须是一个
//likeLeft() == where name like '%J'
//likeRight() == where name like 'J%'
lqw.like(User::getName, "J"); //where name like '%J%'
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
/*****************************************************************/
- 排序//排序
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//lqw.orderByAsc(User::getAge); //升序
lqw.orderByDesc(User::getAge); //降序
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
# 查询API- 更多查询条件设置参看https://baomidou.com/pages/10c804/

5、映射匹配兼容【了解】

# 创建新的表tbl_user做测试:USE mybatisplus_db;
CREATE TABLE tbl_user (id BIGINT ( 20 ) PRIMARY KEY auto_increment,`name` VARCHAR ( 32 ) NOT NULL,pwd VARCHAR ( 32 ) NOT NULL,age INT ( 3 ) NOT NULL,tel VARCHAR ( 32 ) NOT NULL,deleted INT(1) DEFAULT '0',version INT(11) DEFAULT '1'
);insert into tbl_user(name,pwd,age,tel) values('snake','123456',28,'12345678910');
insert into tbl_user(name,pwd,age,tel) values('张益达','123456',22,'12345678910');
insert into tbl_user(name,pwd,age,tel) VALUES('张大炮','123456',16,'12345678910');
// 1.表字段与编码属性设计不同步在模型类属性上方,使用@TableField属性注解,通过value属性,设置当前属性对应的数据库表中的字段关系。@TableField(value = "pwd")//给password定义了别名对应数据库中的字段
private String password;// 2.编码中添加了数据库中未定义的属性在模型类属性上方,使用@TableField注解,通过exist属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。//select online from user;
@TableField(exist = false) //设置的属性在数据库表字段中是否存在(默认为true存在)
private Integer online;// 3.采用默认查询开放了更多的字段查看权限在模型类属性上方,使用@TableField注解,通过select属性:设置该属性是否参与查询。此属性与select()映射配置不冲突。
@TableField(value="pwd",select=false) //将密码阶段设置不参与查询
private String password;//4.表名与编码开发设计不同步在模型类上方,使用@TableName注解,通过value属性,设置当前类对应的数据库表名称。@Data
@TableName("tbl_user")
public class User {/*id为Long类型,因为数据库中id为bigint类型,并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型*/private Long id;private String name;@TableField(value = "pwd",select = false)private String password;private Integer age;private String tel;@TableField(exist = false) //表示online字段不参与CRUD操作private Boolean online;
}

四、DML编程控制【重点】

DQL:数据查询语言(select)DML:数据操作语言(insert, update, delete)

1.ID生成策略控制(Insert)

不同的表应用不同的id生成策略- 日志:自增(1,2,3,4,……)
- 购物订单:特殊规则(FQ23948AK3843)
- 外卖单:关联地区日期等信息(10 04 20200314 34 91)
- 关系表:可省略id
- ……- id生成策略控制
名称:@Tableld
类型:属性注解
位置:模型类中用于表示主键的属性定义上方
作用:设置当前类中主键属性的生成策略
相关属性type:设置主键属性的生成策略,值参照IdType枚举值
public class User{@Tableld(type = IdType.AUTO)/***   AUTO(0):使用数据库id自增策略控制id生成*   NONE(1):不设置id生成策略*  INPUT(2):用户手工输入id*  ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型)*  ASSIGN_UUID(4):以UUID生成算法作为id生成策略*/private Long id;
} 
** 雪花(snowflake)算法:Twitter开源的一个生成不重复ID的算法scala **- 其核心思想是:第一个是符号位,0代表是正数;使用41bit作为毫秒数;10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号。
0001001101110110101011000011010100110001101000010001000000000010
占位符时间戳(41)数据中心(5)机器ID(5)流水号
    计算机使用一个叫做实时时钟(Real-Time Clock,简称 RTC)的硬件设备来跟踪时间。RTC一般以晶振的形式存在,每秒钟产生一次脉冲。操作系统通过读取RTC的脉冲来确定时间的流逝,从而计算出当前的日期和时间。当我们电脑中硬件设备受到外界影响就会导致时间的不准确过快或者过慢,从而会使得时间校正,而在这段或快或慢的时间段中可能出现由雪花算法生成的id,但是矫正完之后,又有可能生成新的雪花id和上一个id相同。- 时钟回拨:可能导致生成重复的ID,可以使用第三方成熟方案:百度IdGenerator,美团Leaf
全局策略配置mybatis-plus:global-config:db-config:id-type: assign_id #雪花算法table-prefix: tbl_   # tbl_ + User.java -> select * from tbl_user# 设置前缀为tbl_的表的id 设置成雪花算法生成的id

2.批量操作

1、按照主键删除多条记录//删除指定多条数据
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);//where id in (1,2,3)
userDao.deleteBatchIds(list);2、 根据主键查询多条记录//查询指定多条数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
List<User> users = userDao.selectBatchIds(list);

3.逻辑删除

  
- 删除操作业务问题:业务数据从数据库中丢弃
- 物理删除:将数据从硬盘(数据库)当中删除(delete from user where id = 1)
- 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,
- 数据保留在数据库中(update user set deleted=1 where id = 1)1、数据库表中添加逻辑删除标记字段,deleted默认值为0- 注意:不要使用SQL的关键字做为表名或者字段名,容易出错所以:订单order - orders是否删除字段delete -> deleted
2、实体类中添加对应字段,并设定当前字段为逻辑删除标记字段@Data
public class User {private Long id;//逻辑删除字段,标记当前记录是否被删除//@TableLogic(value = "0", delval = "1")private Integer deleted;}
3、配置全局逻辑删除字面值2和3,任选一种即可
mybatis-plus:global-config:db-config:table-prefix: tbl_# 逻辑删除字段名logic-delete-field: deleted# 逻辑删除字面值:未删除为0logic-not-delete-value: 0# 逻辑删除字面值:删除为1logic-delete-value: 1
- 逻辑删除本质:其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。/删除一条数据,观察字段deleted变化//deleted会由0 -> 1userDao.deleteById(1450393635949465602L);`说明:当加入了逻辑删除之后,后续查询或者更新时,自动多一个条件(where deleted = 0)`

4.乐观锁与悲观锁

乐观锁和悲观锁都是用于解决并发场景下的数据竞争问题,但是却是两种完全不同的思想。- 它们的使用非常广泛,也不局限于某种编程语言或数据库。数据库自身解决并发两种策略:- 悲观锁(Pessimistic Lock):很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,降低了性能,适用于写多读少。
三种常见的悲观锁解决方案的区别:1. synchronized关键字:- 线程访问资源时自动获取锁,代码块执行完毕后自动释放锁。- 无法手动中断获取锁的线程,只能等待锁的释放。- 只支持独占锁,不支持公平性设置。- 隐式地进行锁的获取和释放,使用相对较简单。2. ReentrantLock类:- 可以手动地获取锁和释放锁,更灵活地控制锁的粒度。- 可以设置是否公平获取锁,即等待时间较长的线程优先获取锁。- 支持可中断获取锁,即等待获取锁的线程可被中断。- 支持多个条件变量(Condition),可以精确地进行线程通信和等待。3. ReadWriteLock接口:- 提供读写锁的支持,可以允许多个线程同时读取共享数据,但只允许一个线程写入数据。- 读锁是共享的,可同时被多个线程持有,但写锁是独占的,只能被一个线程持有。- 读锁的并发性能好于独占锁,适用于读操作频繁、写操作较少的场景。- 可以通过读写锁的方式提高并发性能,减少对共享资源的争用。总的来说,synchronized是最基本且简便的悲观锁实现方式,ReentrantLock类提供了更高级的功能和灵活性,而ReadWriteLock接口则适用于读多写少的场景。根据具体需求和场景,可以选择合适的锁机制来保障线程安全性和性能。
-- 关闭数据库自动提交-- 在查询语句后添加for update,加行锁
select * from tb_user where id = 1 for update;-- 更新数据
-- update xxx-- 事务提交或回滚后,锁会释放
commit;
rollback;
- 乐观锁(Optimistic Lock)很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,它并非是真的锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制;使用读多写少,性能比悲观锁高。
1.数据库表中添加锁标记字段, 默认值为1
2.实体类中添加对应字段,并设定当前字段为逻辑删除标记字段@Data
public class User {private Long id;//省略其他属性@Version //用这个字段实现乐观锁private Integer version;
}
3、配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装- 如果是常用的功能,直接就能用(逻辑删除)
- 乐观锁并不是所有的更新都要用,如果需要用,得自己来添加拦截器
//mybatis plus的配置类@Configuration
public class MpConfig {@Beanpublic MybatisPlusInterceptor mpInterceptor() {//1.定义Mp拦截器MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();//2.添加分页拦截器mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());//3.添加乐观锁拦截器: set version = version+1 where version =?mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mpInterceptor;}
}
4、使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行@Test
void testUpdate() throws InterruptedException {//1.先通过要修改的数据id将当前数据查询出来//目的:在更新之前必须先知道当前的versionUser user = userDao.selectById(222L);System.out.println(user);//2.将要修改的属性逐一设置进去user.setName("Jock888");Thread.sleep(15 * 1000); //在休眠期间使用MySQL客户端去修改version值//UPDATE tbl_user SET name=?, age=?, tel=?, version=?// WHERE id=? AND version=? AND deleted=0int num = userDao.updateById(user);if (num == 0 ) { //在我查询 和 修改 之间,有其他人(线程)修改了此数据,导致版本号发生了变化//数据库受影响行数==0,说明修改失败,有两种处理方案://1. 提示用户修改失败,让用户自行决定是否再次修改System.err.println("修改失败,需要再次修改");//2. TODO 重新调用查询+修改,自动重试(危险)} else {System.out.println("修改成功");}
}
- 1、乐观锁实现,必须先查询,再更新- 2、乐观锁拦截器负责添加2个sql片段:update user set version = 原来的版本号+1 where version=原来查询出来的版本号 //UPDATE tbl_user SET name=?, age=?, tel=?, version=? WHERE id=? AND version=?

五、代码生成器【了解】

1.MyBatisPlus提供模板

根据数据库中的表字段,自动生成实体类、数据层、业务层、表现层代码。
第一步:添加代码生成器相关依赖
<!--代码生成器-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</version>
</dependency><!--模板引擎:velocity, jsp, freemarker-->
<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version>
</dependency>
第二步:编写代码生成器类public class Generator {public static void main(String[] args) {//1. 创建代码生成器对象,执行生成代码操作AutoGenerator autoGenerator = new AutoGenerator();//设置生成目录autoGenerator.setGlobalConfig(new GlobalConfig().setOutputDir("c:/work"));//2. 数据源相关配置:读取数据库中的信息,根据数据库表结构生成代码DataSourceConfig dataSource = new DataSourceConfig();dataSource.setDriverName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");dataSource.setUsername("root");dataSource.setPassword("root");autoGenerator.setDataSource(dataSource);//3. 执行生成操作autoGenerator.execute();}
}

2.开发者自定义配置

//设置全局配置GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");   //设置代码生成位置globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("Wen阿杜");    //设置作者
globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
//设置包名相关配置PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.abc");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("pojo");    //设置实体类包名:entity, domain
packageInfo.setMapper("dao");   //设置数据层包名:mapper
autoGenerator.setPackageInfo(packageInfo);
//策略设置StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tbl_user");  //设置当前参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("tbl_");
//设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名  例如: User = tbl_user - tbl_strategyConfig.setRestControllerStyle(true);    //设置是否启用Rest风格
strategyConfig.setVersionFieldName("version");  //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted");  //设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true);  //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);

3、其他实现

- 使用MyBatisX插件
- 开源:https://gitee.com/renrenio/renren-security/tree/master/renren-generator
- 公司自己实现的……

MyBatis和MyBatisPlus的使用场景:

  • 单表操作:使用MP,不用写SQL语句
  • 多表关联查询:使用MyBatis在xml中写SQL实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/55254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Go版]算法通关村第十四关白银——堆高效解决的经典问题(在数组找第K大的元素、堆排序、合并K个排序链表)

目录 题目&#xff1a;在数组中找第K大的元素解法1&#xff1a;维护长度为k的最小堆&#xff0c;遍历n-k个元素&#xff0c;逐一和堆顶值对比后&#xff0c;和堆顶交换&#xff0c;最后返回堆顶复杂度&#xff1a;时间复杂度 O ( k ( n − k ) l o g k ) O(k(n-k)logk) O(k(n−…

CSS实现内凹圆角,从而实现圆角边框

1、代码 <!DOCTYPE html> <html><head><style>.uu {position: relative;width: 400px;height: 300px;}img {width: 100%;height: 100%;z-index: 1;}.box_right_top {background-image: radial-gradient(circle at left bottom, transparent 50px, whi…

统信OS国产操作系统身份证读卡器社保卡读卡web网页开发使用操作流程

用于DONSEE系列身份证阅读器谷歌Chrome火狐Firefox插件&#xff0c;支持的型号有&#xff1a;EST-100、EST-100GS、EST-100G、EST-100U、EST-200G、EST-J13X等。 本方案无缝支持最新版本谷歌Chrome火狐Firefox等网页浏览器&#xff0c;支持H5、Vue、React、Node.js、Electron、…

【面向对象编程的三大特征:封装;继承;多态】

面向对象编程的三大特征&#xff1a;封装&#xff1b;继承&#xff1b;多态 前言&#xff1a; 最近在B站搜到韩顺平老师的java入门课&#xff0c;这里记录下实践&#xff1b; 熟悉&&掌握面向对象三大特征&#xff1a;封装&#xff1b;继承&#xff1b;多态; 多态是基…

设计模式--单例模式(Singleton Pattern)

一、什么是单例模式 单例模式是一种创建型设计模式&#xff0c;它旨在确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问该实例。换句话说&#xff0c;单例模式限制了类的实例化次数为一个&#xff0c;并提供一种在应用程序中共享一个实例的方式。这对于需要只有…

Linux系统编程:线程控制

目录 一. 线程的创建 1.1 pthread_create函数 1.2 线程id的本质 二. 多线程中的异常和程序替换 2.1 多线程程序异常 2.2 多线程中的程序替换 三. 线程等待 四. 线程的终止和分离 4.1 线程函数return 4.2 线程取消 pthread_cancel 4.3 线程退出 pthread_exit 4.4 线程…

Day44|leetcode 518.零钱兑换II、377. 组合总和 Ⅳ

完全背包理论基础 视频链接&#xff1a;带你学透完全背包问题&#xff01; 和 01背包有什么差别&#xff1f;遍历顺序上有什么讲究&#xff1f;_哔哩哔哩_bilibili 完全背包与01背包不同的地方就是&#xff1a;01背包每种物品只能取一次&#xff0c;而完全背包每种物品可以取…

2023年国赛数学建模思路 - 案例:粒子群算法

文章目录 1 什么是粒子群算法&#xff1f;2 举个例子3 还是一个例子算法流程算法实现建模资料 # 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 什么是粒子群算法&#xff1f; 粒子群算法&#xff08;Pa…

yolov5中添加ShuffleAttention注意力机制

ShuffleAttention注意力机制简介 关于ShuffleAttention注意力机制的原理这里不再详细解释.论文参考如下链接here   yolov5中添加注意力机制 注意力机制分为接收通道数和不接受通道数两种。这次属于接受通道数注意力机制,这种注意力机制由于有通道数要求,所示我们添加的时候…

学习JAVA打卡第四十四天

Scanner类 ⑴Scanner对象 scanner对象可以解析字符序列中的单词。 例如&#xff1a;对于string对象NBA 为了解析出NBA的字符序列中的单词&#xff0c;可以如下构造一个scanner对象。 将正则表达式作为分隔标记&#xff0c;即让scanner对象在解析操作时把与正则表达式匹配的字…

【Linux】多线程概念线程控制

文章目录 多线程概念Linux下进程和线程的关系pid本质上是轻量级进程id&#xff0c;换句话说&#xff0c;就是线程IDLinux内核是如何创建一个线程的线程的共享和独有线程的优缺点 线程控制POSIX线程库线程创建线程终止线程等待线程分离 多线程概念 Linux下进程和线程的关系 在…

使用StreamLold写入 Starrocks报错:Caused by org

问题描述 使用StreamLoad写入Starrocks报错&#xff0c;报这个错误:Caused by: org.apache.http.ProtocolException: Content-Length header already present 代码案例 引入依赖 <!-- Starrocks使用StreamLoad发送Http请求 --><dependency><groupId>or…

Reactive响应式编程系列:一个Demo了解如何将事件驱动变成响应式

Reactive响应式编程系列&#xff1a;解密Lettuce如何实现响应式_lettuce原理_飞向札幌的班机的博客-CSDN博客Reactive响应式编程系列&#xff1a;解密Lettuce如何实现响应式_lettuce原理_飞向札幌的班机的博客-CSDN博客上面两篇文章也许介绍的内容过细&#xff0c;导致无法清晰…

系统架构设计高级技能 · 面向服务架构设计理论与实践

系列文章目录 系统架构设计高级技能 软件架构概念、架构风格、ABSD、架构复用、DSSA&#xff08;一&#xff09;【系统架构设计师】 系统架构设计高级技能 系统质量属性与架构评估&#xff08;二&#xff09;【系统架构设计师】 系统架构设计高级技能 软件可靠性分析与设计…

Jtti :sql server怎么备份数据库?

在 SQL Server 中备份数据库是一项重要的操作&#xff0c;它可以确保你的数据在意外情况下得以恢复。以下是在 SQL Server 中备份数据库的基本步骤&#xff1a; 使用 SQL Server Management Studio (SSMS) 进行备份&#xff1a; 打开 SQL Server Management Studio(SSMS)并连接…

【自动驾驶】TI SK-TDA4VM 开发板上电调试,AI Demo运行

1. 设备清单 TDA4VM Edge AI 入门套件【略】USB 摄像头(任何符合 V4L2 标准的 1MP/2MP 摄像头,例如:罗技 C270/C920/C922)全高清 eDP/HDMI 显示屏最低 16GB 高性能 SD 卡连接到互联网的 100Base-T 以太网电缆【略】UART电缆外部电源或电源附件要求: 标称输出电压:5-20VDC…

Jmeter 接口测试总结

背景介绍 对于 Android 项目来说&#xff0c;使用的是 Java 开发&#xff0c;网络请求接口的数量庞大且复杂&#xff0c;测试人员无法很直观的判断、得出网络请求是否存在问题。另一方面&#xff0c;为了验证请求接口是否能够在大负荷条件下&#xff0c;长时间、稳定、正常的运…

【JMeter】常用线程组设置策略

目录 一、前言 二、单场景基准测试 1.介绍 2.线程组设计 3.测试结果 三、单场景并发测试 1.介绍 2.线程组设计 3.测试结果 四、单场景容量/爬坡测试 1.介绍 2.线程组设计 3.测试结果 五、混合场景容量/并发测试 1.介绍 六、稳定性测试 1.介绍 2.线程组设计 …

C++中的const成员变量和成员函数

在类中&#xff0c;如果你不希望某些数据被修改&#xff0c;可以使用const关键字加以限定。const 可以用来修饰成员变量和成员函数。 const成员变量 const 成员变量的用法和普通 const 变量的用法相似&#xff0c;只需要在声明时加上 const 关键字。初始化 const 成员变量只有…

【跟小嘉学 Rust 编程】十八、模式匹配(Patterns and Matching)

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…