mybatis plus
文章目录
mybatis 快速实现
导入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
在 dao 层,继承 BaseMapper 加泛型
@Mapper
public interface UserDao extends BaseMapper<User> {
}
新增
User user = new User();
int i = userDao.insert(user);
根据 ID 删除
int i = userDao.deleteById(-306130942);
根据 ID 修改
必须传入 ID 字段
它会根据 ID 修改,对象的数值
对象的空字段,不修改
@Test
void testUpdate(){
User user = new User();
user.setId(1);
user.setName("Tom");
int i = userDao.updateById(user);
}
根据 ID 查数据
@Test
void testById() {
User user = userDao.selectById(2);
System.out.println(user);
}
查全部数据
@Test
void contextLoads() {
List<User> users = userDao.selectList(null);
System.out.println(users);
}
service 层创建dao层接口,进行调用
QueryWrapper<planClient> 可以做过滤条件
创建这个方法指定
@Override
public List<planClient> getPlanClient() {
//
QueryWrapper<planClient> wrapper = new QueryWrapper<>();
return planClientDao.selectList(wrapper);
}
分页实现
先创建 Page 对象,两个属性,看第几页,看几条数据
@Test
void testGetByPage() {
Page page = new Page(2, 2);
userDao.selectPage(page, null);
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());
}
这个时候打印发现,数据查询的是全部
添加一个拦截器,就好了。拦截对象是 Page lei
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 1. 创建 MybatisPlusInterceptor 拦截器对象
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 2. 添加分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
Mybatis 日志打印
在 yml 配置文件里
#打印SQL日志到控制台
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
DQL 编程控制
条件查询(lt, gt)
mybatisPlus 里有一个 wrapper 类,这个就是构建查询条件的
第一种方式
容易出错,因为字段是字符串形式的
@Test
void contextLoads() {
// 创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
// lt 是小于,表示小于十八的
wrapper.lt("age", 18);
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
第二种方式
@Test
void contextLoads() {
// 创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
// lt 是小于,表示小于十八的,使用 lambda 表示字段
wrapper.lambda().lt(User::getAge, 10);
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
第三种方式
简化上面的
@Test
void contextLoads() {
// 创建lambda对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// lt 是小于,表示小于十八的
wrapper.lt(User::getAge, 10);
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
多条件查询 and or
and 关系
大于10岁并且小于16岁
@Test
void contextLoads() {
// 创建对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 查询 大于10岁小于16岁
wrapper.lt(User::getAge, 16).gt(User::getAge, 10);
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
加括号的方式 and
- ne 不等于
- .and(wr -> 等于里面的条件加括号
// 判断是否存在重复的
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.and(wr -> wr.ne(SysUser::getId, sysUser.getId()))
.and(wr -> wr.eq(SysUser::getUserName, sysUser.getUserName()).or().eq(SysUser::getNickName, sysUser.getNickName()));
or 关系 or()
小于 10岁 或者 大于30岁
@Test
void contextLoads() {
// 创建对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 查询 大于30岁或者小于10岁
wrapper.lt(User::getAge, 10).or().gt(User::getAge, 30);
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
处理空值
如果表达式为 true 才进行该字段查询
wrapper.lt (布尔表达式, 字段, 值)
需要加入模型类继承domain 类,表示上限和下限,一般是数值类型
wrapper 后面的条件如果不等于空才会进行条件查询
@Test
void contextLoads() {
UserQuery query = new UserQuery();
/*模拟页面传递的数据*/
query.setAge(10);
query.setAge2(30);
// 创建对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 查询 大于30岁或者小于10岁
wrapper.lt(query.getAge() != null, User::getAge, query.getAge())
.or().gt(query.getAge2() != null, User::getAge, query.getAge2());
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
查询投影
用法1:我们只查询某些字段
方式一:
@Test
void contextLoads() {
// 创建对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 只查询id,name,age
wrapper.select(User::getId,User::getName,User::getAge);
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
方式二:
@Test
void contextLoads() {
// 创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 只查询id,name,age
wrapper.select("id","name","age");
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
用法2:我们查询总数
@Test
void contextLoads() {
// 创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 查询总数
wrapper.select("count(*) as count");
// 调用 selectMaps转换 map
List<Map<String, Object>> maps = userDao.selectMaps(wrapper);
System.out.println(maps);
}
用法3:还可以分组
@Test
void contextLoads() {
// 创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 查询总数
wrapper.select("count(*) as count, tel");
// 分组
wrapper.groupBy("tel");
// 调用 selectMaps转换 map
List<Map<String, Object>> maps = userDao.selectMaps(wrapper);
System.out.println(maps);
}
查询条件符号
查询单个(selectOne)
@Test
void contextLoads() {
// 创建对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// eq 等同于 ==
wrapper.eq(User::getName, "Jerry").eq(User::getPassword, "Jerry");
// 调用 selectOne 查询单个用户
User user = userDao.selectOne(wrapper);
System.out.println(user);
}
范围查询(between)
@Test
void contextLoads() {
// 创建对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// between(10,30) 10到30之间
wrapper.between(User::getAge, 10, 30);
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
模糊匹配(like)
其中 like 是前后都有百分号,likeLeft 和 likeRight 是一边有
@Test
void contextLoads() {
// 创建对象
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// like 模糊查询
wrapper.like(User::getName,"j");
List<User> users = userDao.selectList(wrapper);
System.out.println(users);
}
映射关系
表名对应不上时
需要在类上面加 @TableName
@TableName("数据库表名")
public class User {
private Integer id;
private String name;
private String password;
字段名对应不上时
@TableName("tb_user")
public class User {
private Integer id;
private String name;
@TableField(value = "数据库表中字段")
private String password;
javaBean 表字段多于数据库表时
因为这个字段是新增的,数据库表并没用,会报错
不知道这个列,未知,找不到
Unknown column ‘online’ in ‘field list’
加上 @TableField(exist = false),表示不存在
@TableName("tb_user")
public class User {
private Integer id;
private String name;
@TableField(value = "pwd")
private String password;
// 不存在的字段
@TableField(exist = false)
private Integer online;
}
不参与查询
给字段加 select = false
@TableField(value = "pwd", select = false)
private String password;
因为不参与查询,数据将为空
DML编程控制
id 生成策略
配置 主键自增,默认数据库的形式
@TableId(type = IdType.AUTO)
@TableName("tb_user")
public class User {
// 数据库默认 id 自增
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
@TableField(value = "pwd", select = false)
}
设置完上面的,会发现数据库表 ID 数据有数据了
自己生成 ID (INPUT)
@TableId(type = IdType.INPUT)
需要关闭数据库自增
当新增数据的时候,对象里ID字段必须合法,有数据
@TableName("tb_user")
public class User {
@TableId(type = IdType.INPUT)
private Integer id;
}
雪花算法 (ASSIGN_ID)
同自己生成
- 如果自己传 ID 了,那么使用自己的 ID
- 如果没有传递,他自动生成了一个 ID
@Test
void contextLoads() {
User user = new User();
// user.setId(667);
user.setName("高");
user.setAge(17);
user.setPassword("123");
user.setTel("13633103713");
int insert = userDao.insert(user);
System.out.println(insert);
}
==> Preparing: INSERT INTO tb_user ( id, name, pwd, age, tel ) VALUES ( ?, ?, ?, ?, ? )
可以看出下面这个ID 太长了,怎么生成的呢
==> Parameters: -796807167(Integer), 高(String), 123(String), 17(Integer), 13633103783(String)
雪花算法生成原理
生成一个串,64 的二进制,也就是一个 long 的值
时间戳并不是当前时间的算法
全局设置
ID 生成策略
在 yml 里
mybatis-plus:
global-config:
db-config:
id-type: assign_id
表名前缀
mybatis-plus:
global-config:
db-config:
# ID 生成策略
id-type: assign_id
# 表名前缀自动添加
table-prefix: tb_
批量删除(deleteBatchIds)
@Test
void contextLoads() {
List<Integer> list = new ArrayList<>();
// 添加数据
Collections.addAll(list, -1279160319, -796807167, -624852991);
// 以集合方式删除数据
int i = userDao.deleteBatchIds(list);
System.out.println(i);
}
批量查询(selectBatchIds)
@Test
void contextLoads() {
List<Integer> list = new ArrayList<>();
// 添加数据
Collections.addAll(list, 2, 1, 3);
// 以集合方式查询数据
List<User> users = userDao.selectBatchIds(list);
System.out.println(users);
}
Map方式新增数据
逻辑删除(@TableLogic)
为数据设置是否可同状态字段,删除时状态为不可用状态,数据保留在数据库中
- 在数据库里添加字段,deleted
- 在JavaBean 里加字段 deleted 保持和数据库一致
注:必须和数据库完全保持一致,数据库 1 是删除,这里就是 1
// value 表示未删除,正常,delval 表示删除
@TableLogic(value = "0", delval = "1")
private Integer deleted;
此时:在运行删除命令,修改的将是字段了,不会删除啦
@Test
void contextLoads() {
int i = userDao.deleteById(1);
System.out.println(i);
}
可以看到执行的命令,是 update
==> Preparing: UPDATE tb_user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 1(Integer)
细节:如果修改了删除的话,查找的时候默认会不查询已经逻辑删除的字段
全局修改逻辑删除字段
mybatis-plus:
global-config:
db-config:
# 表示删除字段
logic-delete-field: deleted
# 表示没有删除的值
logic-not-delete-value: 0
# 表示已经删除的值
logic-delete-value: 1
优化日志
关闭 logback 启动信息
创建一个 logback.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration></configuration>
关闭 mybatis banner信息
mybatis-plus:
global-config:
banner: false
关闭 boot banner信息
spring:
main:
banner-mode: off
乐观锁(并发量不超过2000)
- 通常我们会在数据库里加字段,表示当前是否加锁,version
- 如果version = 1,别人拿到的也是 = 1,那么就不能修改,修改完并加1
- 这个锁是累加的,需要拦截器拦截一下
@Version
private Integer version;
拦截器设置
@Configuration
public class mybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 创建拦截器对象
MybatisPlusInterceptor mpi = new MybatisPlusInterceptor();
// 添加乐观锁拦截器
mpi.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mpi;
}
}
执行
@Test
void contextLoads() {
// 先把version 信息 查询出来
User user = userDao.selectById(2);
// 更新
user.setName("Jerry888");
int insert = userDao.updateById(user);
System.out.println(insert);
}
==> Preparing: UPDATE tb_user SET name=?, age=?, tel=?, version=? WHERE id=? AND version=? AND deleted=0
==> Parameters: Jerry888(String), 4(Integer), 16688886666(String), 2(Integer), 2(Integer), 1(Integer)
<== Updates: 1
可以看到我们更新的时候,第一个线程会把version 加一
当第二个线程再更新的时候,就查询不出来了,会更新失败
注意:有时候写了这个字段就要使用它,否则会报异常,也看不出来是啥错误,只写着正在关闭spring事务
代码生成器
新增两个坐标
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
建立一个代码生成类
执行此类,即可快速生成
public class CodeGenerator {
// 代码生成类
public static void main(String[] args) {
// 配置数据库信息
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatisplus_db", "root", "1234")
//全局配置
.globalConfig((scanner, builder) -> {
// 设置作者
builder
// 开启 swagger 模式
// .enableSwagger()
// 覆盖但是已经过时,已经失效 建议使用3.5.1
.fileOverride()
//指定输出目录
.outputDir(System.getProperty("user.dir") + "/src/main/java")
// 默认打开目录,加上不打开
.disableOpenDir()
// 设置作者
.author("作者");
})
.packageConfig((scanner, builder) -> {
// 设置包名, 数据层和实体层
builder.parent("com.aaa").mapper("dao").entity("domain");
})
// 策略配置
.strategyConfig((scanner, builder) -> {
// 设置生成ID策略
builder.entityBuilder().idType(IdType.ASSIGN_ID)
// 乐观锁字段
.versionPropertyName("version")
// 逻辑删除字段
.logicDeletePropertyName("deleted")
// 开启Lombok
.enableLombok();
// 设置数据接口名,%s为占位符
builder.mapperBuilder().formatMapperFileName("%sDao");
// 开启 rest风格
builder.controllerBuilder().enableRestStyle();
// 指定打印表
builder.addInclude("menu");
})
.execute();
}
}
业务层简便写法
查询单个
CommunicationTerminal terminal = query().select("terminal_number").eq("id", id).one();