编写代码
配置(spring boot 2.1及以上(内置jdbc8驱动)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/bigdata?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=952700
启动类(添加 @MapperScan 注解,扫描 Mapper 文件夹)
@SpringBootApplication
@MapperScan("com.chengzi.demomptest.mapper")
public class DemompTestApplication {
public static void main(String[] args) {
SpringApplication.run(DemompTestApplication.class, args);
}
}
添加mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
}
查看sql输出日志
#mybatis 日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
主键策略
插入操作
@Test
void testAdd(){
User user = new User();
user.setName("chengzi");
user.setAge(18);
user.setEmail("123@qq.com");
// 返回值是影响的行数
int insert = userMapper.insert(user);
System.out.println(insert);
}
MP的主键策略
ASSIGH_ID
MyBatis-Plus默认的主键策略是:ASSIGN_ID (使用了雪花算法)19位
ASSIGN_UUID 19位 AUTO(自动增长) INPUT(自己set)
@TableId(type = IdType.ASSIGN_ID)
private String id;
要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
自动填充和乐观锁
更新操作
注意:update是生成的sql自动是动态sql:update user set age = ?where id = ?
// 修改
@Test
void testUpdate(){
User user = new User();
user.setId(1932410622691172353L);
user.setName("xigua");
// 返回值是影响的行数
int count = userMapper.updateById(user);
System.out.println(count);
}
自动填充
需求描述:项目中经常遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用Mybatis Plus的自动填充功能,完成这些字段的赋值操作
实体类修改
// 实体上增加字段并添加自动填充注解
// 添加的时候设置值
@TableField(fill = FieldFill.INSERT)
private Data createTime;
// 添加和修改的时候设置值
@TableField(fill = FieldFill.INSERT_UPDATE)
private Data updateTime;
实现元对象处理器接口
注意:不要忘记添加 @Component 注解
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// MP执行添加操作,这个方法执行
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
// MP执行修改操作,这个方法执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
乐观锁
场景
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
接下来介绍如何在Mybatis-Plus项目中,使用乐观锁
乐观锁实现流程
修改实体类
添加 @Version 注解
@Version
private Integer version;
创建配置文件
创建包config,创建文件MybatisPlusConfig.java
此时可以删除主类中的 @MapperScan 扫描注解
/**
* 乐观锁插件
*/
@Configuration
@MapperScan("com.chengzi.demomptest.mapper")
public class MpConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 添加乐观锁插件
return interceptor;
}
}
查询
通过多个id批量查询
完成了动态sql的foreach的功能
// 多个id批量查询
@Test
void testSelect1() {
userMapper.selectBatchIds(Arrays.asList(1, 2, 3)).forEach(System.out::println);
}
简单的条件查询
通过map封装查询条件
注意:map中的key对应数据库中的列名。如:数据库user_id,实体类是userId,这时map的key需要填写user_id
// 简单条件查询
@Test
void testSelect2() {
Map<String, Object> map = new HashMap<>();
map.put("name", "Jack");
map.put("age", 20);
userMapper.selectByMap(map).forEach(System.out::println);
}
分页
分页插件
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
添加分页插件
配置类中添加@Bean配置
@Configuration
public class MyBatisPlusConfig {
/**
* 分页插件(3.4.0+ 版本使用 MybatisPlusInterceptor 统一管理插件)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件(核心)
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
// 可选配置:设置分页参数
paginationInterceptor.setMaxLimit(500L); // 单页最大限制数量,默认 500 条,-1 表示不限制
paginationInterceptor.setDbType(DbType.MYSQL); // 指定数据库类型(自动识别时可省略)
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}
编写分页代码
- 插件Page对象,传入两个参数:当前页、每页记录数
- 调用mp的方法实现分页
// 分页查询
@Test
void testSelectPage() {
Page<User> page = new Page<>(1, 5);
Page<User> userPage = userMapper.selectPage(page, null);
// 返回对象得到分页中的所有数据
long pages = userPage.getPages(); // 总页数
System.out.println("总页数:" + pages);
long current = userPage.getCurrent(); // 当前页
System.out.println("当前页:" + current);
List<User> records = userPage.getRecords(); // 当前页数据
records.forEach(System.out::println);
long total = userPage.getTotal(); // 总记录数
System.out.println("总记录数:" + total);
boolean hasPrevious = userPage.hasPrevious(); // 是否有上一页
System.out.println("是否有上一页:" + hasPrevious);
boolean hasNext = userPage.hasNext(); // 是否有下一页
System.out.println("是否有下一页:" + hasNext);
}
删除与逻辑删除
删除
根据id删除记录
// 根据id删除
@Test
void testDelete() {
int i = userMapper.deleteById(1932430505864773634L);
System.out.println(i);
}
批量删除
// 根据id批量删除
@Test
void testDeleteBatchIds() {
int i = userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
System.out.println(i);
}
简单条件删除
//简单条件删除
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
int result = userMapper.deleteByMap(map);
System.out.println(result);
}
逻辑删除
物理删除和逻辑删除
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
逻辑删除的使用场景:
可以进行数据恢复
有关联数据,不便删除
逻辑删除实现流程
数据库修改
添加 deleted字段
ALTER TABLE `user` ADD COLUMN `deleted` boolean DEFAULT false
实体类修改
添加deleted 字段,并加上 @TableLogic 注解
@TableLogic
private Integer deleted;
配置(可选)
application.properties 加入以下配置,此为默认值,如果你的默认值和mp默认的一样,该配置可无
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
条件构造器和常用接口
wapper介绍
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : 查询条件封装
UpdateWrapper : Update 条件封装
AbstractLambdaWrapper : 使用Lambda 语法
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
测试用例
ge 大于等于、gt 大于、le 小于等于、lt 小于、isNull 空、isNotNull 非空
@Test
public void testQuery() {
QueryWrapper<User>queryWrapper = newQueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}
eq 等于、ne 不等于
注意:seletOne()返回的是一条实体记录,当出现多条时会报错
@Test
public void testSelectOne() {
QueryWrapper<User>queryWrapper = newQueryWrapper<>();
queryWrapper.eq("name", "Tom");
Useruser = userMapper.selectOne(queryWrapper);//只能返回一条记录,多余一条则抛出异常
System.out.println(user);
}
between、notBetween
包含大小边界
@Test
public void testSelectCount() {
QueryWrapper<User>queryWrapper = new QueryWrapper<>();
queryWrapper.between("age", 20, 30);
Long count = userMapper.selectCount(queryWrapper);
System.out.println(count);
}
like、notLike、likeLeft、likeRight
selectMaps()返回Map集合列表,通常配合select()使用
@Test
public void testSelectMaps() {
QueryWrapper<User>queryWrapper = new QueryWrapper<>();
queryWrapper
.select("name", "age")
.like("name", "e")
.likeRight("email", "5");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
maps.forEach(System.out::println);
}
orderBy、orderByDesc、orderByAsc
@Test
public void testSelectListOrderBy() {
QueryWrapper<User>queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age", "id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
查询方式
查询方式 |
说明 |
setSqlSelect |
设置 SELECT 查询字段 |
where |
WHERE 语句,拼接 + WHERE 条件 |
and |
AND 语句,拼接 + AND 字段=值 |
andNew |
AND 语句,拼接 + AND (字段=值) |
or |
OR 语句,拼接 + OR 字段=值 |
orNew |
OR 语句,拼接 + OR (字段=值) |
eq |
等于= |
allEq |
基于 map 内容等于= |
ne |
不等于<> |
gt |
大于> |
ge |
大于等于>= |
lt |
小于< |
le |
小于等于<= |
like |
模糊查询 LIKE |
notLike |
模糊查询 NOT LIKE |
in |
IN 查询 |
notIn |
NOT IN 查询 |
isNull |
NULL 值查询 |
isNotNull |
IS NOT NULL |
groupBy |
分组 GROUP BY |
having |
HAVING 关键词 |
orderBy |
排序 ORDER BY |
orderAsc |
ASC 排序 ORDER BY |
orderDesc |
DESC 排序 ORDER BY |
exists |
EXISTS 条件语句 |
notExists |
NOT EXISTS 条件语句 |
between |
BETWEEN 条件语句 |
notBetween |
NOT BETWEEN 条件语句 |
addFilter |
自由拼接 SQL |
last |
拼接在最后,例如:last(“LIMIT 1”) |