介绍:
Mybatis-Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发,提高效率而生。
数据库表:
-- 创建表
CREATE TABLE tbl_employee(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(50),
email VARCHAR(50),
gender CHAR(1),
age INT
);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Tom','[email protected]',1,22);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Jerry','[email protected]',0,25);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Black','[email protected]',1,30);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('White','[email protected]',0,35);
mybatis-plus常用的实体类注解
@TableName:对数据表名注解
@TableId:表主键标识
@TableId(value = "id", type = IdType.AUTO):自增
@TableId(value = "id", type = IdType.ID_WORKER_STR):分布式全局唯一ID字符串类型
@TableId(value = "id", type = IdType.UUID):32位UUID字符串
@TableField:表字段标识
@TableField(exist = false):表示该属性不为数据库表字段,但又是必须使用的。
@Version:乐观锁注解、标记
@EnumValue:通枚举类注解
@TableLogic:表字段逻辑处理注解(逻辑删除)
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tbl_employee")
public class Employee {
@TableId(value = "id",type= IdType.AUTO)
private Integer id;
@TableField("last_name")
private String lastName;
private String email;
private Integer gender;
private Integer age;
}
让编写的mapper接口继承BaseMapper接口
/**
* 基于Mybatis‐plus实现: 让XxxMapper接口继承 BaseMapper接口即可.
* BaseMapper : 泛型指定的就是当前Mapper接口所操作的实体类类型
*/
@Repository
public interface EmployeeMapper extends BaseMapper<Employee> {
}
测试:
@SpringBootTest
@RunWith(SpringRunner.class)
public class EmployeeMapperTest {
@Autowired
private EmployeeMapper employeeMapper;
@Test
public void select(){
Object o = employeeMapper.selectById(1);
System.out.println(o);
}
@Test
public void insert(){
Employee employee = new Employee(null, "xushu", "dasda", 1, 211);
employeeMapper.insert(employee);
}
@Test
public void update(){
Employee employee = new Employee(4, "xushu", "dasda", 1, 211);
employeeMapper.updateById(employee);
}
@Test
public void delete(){
Employee employee = new Employee(4, "xushu", "dasda", 1, 211);
int i = employeeMapper.deleteById(4);
}
@Test
public void selectList(){
HashMap<String, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("gender",1);
List<Employee> employees = employeeMapper.selectByMap(objectObjectHashMap);
System.out.println(employees);
}
}
(1)Wrapper :条件构造抽象类,最顶端父类,抽象类中提供3个方法以及其他方法.
(2)AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件,QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
(3)AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。 (4)LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper (5)LambdaUpdateWrapper : Lambda 更新封装Wrapper
(6)QueryWrapper : Entity 对象封装操作类,不是用lambda语法,自身的内部属性 entity 也用于生成 where 条件 该类的重要方法:
具体用法请查该地址:mybatis-plus条件构造器
样例:
@Test
public void selectWrapper(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.
between("age",18,30);
List<Employee> list = employeeService.list(wrapper);
System.out.println(employeeService.list(wrapper));
}
@Test
public void updateWrapper(){
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.set("last_name","wang").eq("id",1);
boolean update = employeeService.update(wrapper);
System.out.println(update);
selectWrapper();
}
// 使用lambda表达式查询
@Test
public void lambdaSelect(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.lambda()
.select(Employee::getLastName,Employee::getAge)
.eq(Employee::getLastName,"wang");
System.out.println(employeeService.list(wrapper));
}
在yaml文件中使用配置
mybatis-plus:
configuration:
// 开启驼峰命名法
map-underscore-to-camel-case: true
global-config:
db-config:
// 设置全局的主键自增,不需要给每一个都配置
id-type: auto
测试样例
@Test
public void insertWrapper(){
Employee employee = new Employee(null,"hei","dasd",1,32);
employeeService.save(employee);
System.out.println(employee);
}
物理删除: 在删除的时候直接将数据从数据库干掉DELTE
逻辑删除: 从逻辑层面控制删除,通常会在表里添加一个逻辑删除的字段比如 enabled 、is_delete ,数据默认是有效的(值为1), 当用户删除时将数据修改UPDATE 0, 在查询的时候就只查where enabled=1.
方法一:在数据库中增加一个判断是否删除的字段
@TableLogic()
private Integer enabled;
mybatis-plus对于是否删除的默认判断:0是未删除,1是已删除
由于不太符合常用的规则,可以自定义是否删除的标志
/**
* 未删除的数据为1,已删除的数据为0
*/
@TableLogic(value = "1",delval = "0")
private Integer enabled;
方法二:在yaml文件中配置是否删除
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
id-type: auto
logic-delete-field: enabled #判断是否删除的字段,逻辑删除的字段
logic-not-delete-value: 1 #没有删除1
logic-delete-value: 0 #已删除是0
在表中添加 create_date和modify_date两个字段,如何做到自动填充数据
方法一:不建议使用
设置create_date、modify_date的默认值为CURRENT_TIMESTA-MP
方法二:使用mybatis-plus带的注解@TableField
// 创建时间,希望在添加数据的时候填充:当前时间
@TableField(value = "create_date",fill = FieldFill.INSERT)
private Date createDate;
// 修改时间:希望在修改数据和添加数据的时候填充
@TableField(value = "modify_date",fill = FieldFill.INSERT_UPDATE)
private Date modifyDate;
添加一个配置类MyMetaObjectHandler
用于设置填充的时间
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 插入时:创建时间和修改时间
this.setFieldValByName("createDate",new Date(),metaObject);
this.setFieldValByName("modifyDate",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("modifyDate",new Date(),metaObject);
}
}
SQL分析打印:可以查看SQL的执行时间,最大的好处时显示除mybatis执行的sql的具体语句
样例:
2022-04-16 18:31:17.425 DEBUG 22096 --- [ main] c.e.q.mapper.EmployeeMapper.update : ==> Parameters: black(String), 2055(String), 1(Integer), 98(Integer), 2022-04-16 18:31:16.959(Timestamp), 10(Integer)
Consume Time:351 ms 2022-04-16 18:31:17
Execute SQL:UPDATE tbl_employee SET last_name='black', email='2055', gender=1, age=98, modify_date='2022-04-16T18:31:16.959+0800' WHERE enabled=1 AND (id = 10)
第一步:添加依赖
>
>p6spy >
>p6spy >
>3.9.1 >
>
第二步:更改yaml中的配置
将url和driver-class-name更改
spring:
datasource:
url: jdbc:p6spy:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
type: com.alibaba.druid.pool.DruidDataSource
第三步:添加一个spy.properties文件
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
注意!
driver-class-name 为 p6spy 提供的驱动类
url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
打印出 sql 为 null,在 excludecategories 增加 commit
批量操作不打印 sql,去除 excludecategories 中的 batch
批量操作打印重复的问题请使用 MybatisPlusLogFactory (3.2.1 新增)
该插件有性能损耗,不建议生产环境使用。
第一:什么是乐观锁 悲观锁:
悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事 务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。 假设功能并发量非常大,就需要使用synchronized
来处理高并发下产生线程不安全问题, 会使其他线程进行挂起等待从 而影响系统吞吐量 。
乐观锁:乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突
,所以在数据进行提交更新的时候,才会正 式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场 景,这样可以提高程序的吞吐量。 假设功能产生并发几率极少,采用乐观锁版本机制对比, 如果有冲突 返回给用户错 误的信息
1.mybatis的配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
2在实体类的字段上加上@Version注解
@Version
private Integer version;
样例:
@Test
public void testCAS(){
// 线程1 age50 version1
Employee employee1 = employeeService.getById(1);
// 线程2 age50 version 1
Employee employee2 = employeeService.getById(1);
employee1.setAge(100);
employee2.setAge(50);
System.out.println(employeeService.updateById(employee1));
System.out.println(employeeService.updateById(employee2));
}
直接在启动类中增加就可
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
xml文件配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.quickstart.mapper.EmployeeMapper">
<select id="getByGender" resultType="com.example.quickstart.pojo.Employee">
SELECT * FROM tbl_employee
</select>
<select id="selectAll" resultType="com.example.quickstart.pojo.Employee">
SELECT * FROM tbl_employee
</select>
</mapper>
mapper层(持久层)
/**
* 基于Mybatis‐plus实现: 让XxxMapper接口继承 BaseMapper接口即可.
* BaseMapper : 泛型指定的就是当前Mapper接口所操作的实体类类型
*/
@Repository
public interface EmployeeMapper extends BaseMapper<Employee> {
IPage<Employee> getByGender(IPage page);
List<Employee> selectAll();
}
样例测试
@Test
public void xmlPage(){
IPage<Employee> iPage = new Page<>(5,1);
IPage<Employee> page = employeeMapper.getByGender(iPage);
System.out.println(page.getSize());
List<Employee> records = page.getRecords();
System.out.println(page.getPages());
System.out.println(records);
}
测试:
@Test
public void page(){
IPage<Employee> iPage = new Page<>(1,3);
IPage<Employee> page = employeeImplService.page(iPage);
List<Employee> records = page.getRecords();
for (Employee record: records){
System.out.println(record);
}
System.out.println(page.getPages());
}