CSDN话题挑战赛第2期
参赛话题:学习笔记
Mybatis-plus
学习之路,长路漫漫,需要您的陪伴。
关注一波,您的关注是我最大的动力。
文章目录
文章目录接上篇文章 Mybatis-plus快速入门(1)
四、常用注解
1、@TableName
① 问题
也许有同学发现了,在上一篇文章 Mybatis-plus快速入门(1) 中我们使用Mybatis-plus进行简单的CRUD时并没有指定表名,只是在Mapper接口继承BaseMapper时,设置了泛型User,而操作的表为User表
由此可知,MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决定,且默认操作的表名和实体类型的类名一致
② 测试
测试如下,将user表改为t_user表,测试查询功能
程序抛出异常,Table ‘mybatis_plus.user’ doesn’t exist,因为现在的表名为t_user,而默认操作的表名和实体类型的类名一致,即user表
③ 添加@TableName
在实体类类型上添加@TableName(“t_user”),标识实体类对应的表,即可成功执行SQL语句
④ 全局配置
在开发的过程中,我们经常遇到以上的问题,即实体类所对应的表都有固定的前缀,例如t_ 或tbl_
此时,可以使用MyBatis-Plus提供的全局配置,在application.yaml中为实体类所对应的表名设置默认的前缀,那么就不需要在每个实体类上通过@TableName标识实体类对应的表
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#设置Mybatis-plus全局配置
global-config:
db-config:
#设置实体类所对应表的统一前缀
table-prefix: t_
#设置统一的主键生成策略
2、@TableId
我们在使用MyBatis-Plus实现CRUD时,会默认将id作为主键列,并在插入数据时,默认基于雪花算法的策略生成id。
① 问题
若实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus会自动识别uid为主键列吗?
我们实体类中的属性id改为uid,将表中的字段id也改为uid,测试添加功能
程序抛出异常,Field ‘uid’ doesn’t have a default value,说明MyBatis-Plus没有将uid作为主键赋值
② 添加@TableId
在实体类中uid属性上通过@TableId将其标识为主键,即可成功执行SQL语句
③ @TableId的value属性
若实体类中主键对应的属性为id,而表中表示主键的字段为uid,此时若只在属性id上添加注解@TableId,则抛出异常Unknown column ‘id’ in ‘field list’,即MyBatis-Plus仍然会将id作为表的主键操作,而表中表示主键的是字段uid
此时需要通过@TableId注解的value属性,指定表中的主键字段,@TableId(“uid”)或@TableId(value=“uid”)
④ @TableId的type属性
type属性用来定义主键策略,这里我们介绍其中的两种常用的:
值 | 描述 |
---|---|
IdType.ASSIGN_ID(默认) | 基于雪花算法的策略生成数据id,与数据库id是否设置自增无关 |
IdType.AUTO | 使用数据库的自增策略,注意,该类型请确保数据库设置了id自增 |
可在实体类对应的字段上进行设置:
//将属性所对应的字段指定为主键
//tableId注解的value属性用于指定主键的字段
//tableId注解的type属性用于设置主键生成策略
@TableId(value = "uid",type = IdType.AUTO)
private Long uid;
也可在application.yaml中配置全局主键策略:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#设置Mybatis-plus全局配置
global-config:
db-config:
#设置实体类所对应表的统一前缀
table-prefix: t_
#设置统一的主键生成策略
id-type: auto
⑤ 雪花算法
雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的
主键的有序性。
核心思想:
长度共64bit(一个long型)。
首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负
数是1,所以id一般是正数,最高位是0。
41bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。
10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)。
12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。
优点
整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高
3、@TableField
我们可以发现,MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和表中的字段名一致
如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?
情况①
若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格
例如实体类属性userName,表中字段user_name
此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格
相当于在MyBatis中配置
情况②
若实体类中的属性和表中的字段不满足情况1
例如实体类属性name,表中字段username
此时需要在实体类属性上使用@TableField(“username”)设置属性所对应的字段名
4、@TableLogic
① 逻辑删除
- 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
- 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
- 使用场景:可以进行数据恢复
② 实现逻辑删除
步骤一、在User表中增加一个int型的字段is_deleted,初始值为0,代表未删除
步骤二、实体类中添加逻辑删除属性
步骤三、测试
测试删除功能,真正执行的是修改,从日志输出的sql语句可以看出
UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
测试查询功能,被逻辑删除的数据默认不会被查询
SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
五、条件构造器和常用接口
1、wrapper介绍
-
Wrapper : 条件构造抽象类,最顶端父类
-
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
-
QueryWrapper : 查询条件封装
-
UpdateWrapper : Update 条件封装
-
AbstractLambdaWrapper : 使用Lambda 语法
-
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
-
LambdaUpdateWrapper : Lambda 更新封装Wrapper
-
-
-
2、QueryWrapper
① 组装查询条件
@Test
public void test01() {
//查询用户名包含a,年龄在20-30之间,邮箱信息不为null的用户信息
//日志输出的sql语句:SELECT uid,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("user_name",'a')
.between("age",20,30).isNotNull("email");
List<User> list = usermapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
② 组装排序条件
@Test
public void test02() {
//查询用户信息,按照年龄进行降序排序,若年龄相同,则按照id升序排序
//日志输出的sql语句:SELECT uid,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,uid ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age")
.orderByAsc("uid");
List<User> list = usermapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
③ 组装删除条件
@Test
public void test03() {
//删除邮箱地址为null的用户信息
//增加了逻辑删除,所以变化为更新了
//日志输出的sql语句:UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int res = usermapper.delete(queryWrapper);
System.out.println("res = " + res);
}
④ 条件的优先级
@Test
public void test04() {
//将(年龄大于20并且名字中含有a)或邮箱为null的用户信息修改
//日志输出的sql语句:UPDATE t_user SET user_name=?, email=? WHERE is_deleted=0 AND (age > ? AND user_name LIKE ? OR email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age",20).like("user_name",'a')
.or().isNull("email");
User user = new User();
user.setName("小明");
user.setEmail("test@atguigu.com");
int res = usermapper.update(user, queryWrapper);
System.out.println("res = " + res);
}
@Test
public void test05() {
//将名字中含有a并且(年龄大于20或邮箱为null)的用户信息修改
//日志输出的sql语句:UPDATE t_user SET user_name=?, email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//lambda表达式内的逻辑优先运算
queryWrapper.like("user_name",'a')
.and(i->i.gt("age",20).or().isNull("email"));
User user = new User();
user.setName("Sandy");
user.setEmail("test@atguigu.com");
int res = usermapper.update(user, queryWrapper);
System.out.println("res = " + res);
}
⑤ 组装select语句
@Test
public void test06() {
//查询用户的用户名,年龄,邮箱
//日志输出的sql语句:SELECT user_name,age,email FROM t_user WHERE is_deleted=0
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("user_name","age","email");
//查询的是部分字段,没必要直接对应实体类对象,直接map集合即可
List<Map<String, Object>> maps = usermapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
⑥ 实现子查询
@Test
public void test07() {
//通过子查询查询用户id小于等于10的用户信息
//日志输出的查询语句:SELECT uid,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (uid IN (Select uid from t_user where uid <= 10))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("uid","Select uid from t_user where uid <= 10");
List<User> list = usermapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
3、UpdateWrapper
@Test
public void test08() {
//将名字中含有a并且(年龄大于20或邮箱为null)的用户信息修改
//日志输出的sql语句:UPDATE t_user SET user_name=?,email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
//UpdateWrapper设置修改条件的同时也可设置修改字段
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper.like("user_name",'a')
.and(i->i.gt("age",20).or().isNull("email"));
userUpdateWrapper.set("user_name","小黑").set("email","xiaohei@atguigu.com");
int res = usermapper.update(null, userUpdateWrapper);
System.out.println("res = " + res);
}
4、condition
在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响SQL执行的结果
思路①
@Test
public void test09() {
//开发中的场景
//日志输出的sql语句:SELECT uid,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND age < ?)
//定义查询条件,有可能为null(用户未输入或未选择)
String userName="小";
Integer ageBegin=null;
Integer ageEnd=30;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if(StringUtils.isNotBlank(userName)) {
queryWrapper.like("user_name",userName);
}
if(ageBegin!=null) {
queryWrapper.gt("age",ageBegin);
}
if(ageEnd!=null) {
queryWrapper.lt("age",ageEnd);
}
List<User> list = usermapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
思路②
@Test
public void test10() {
//相比test09更方便的写法
//日志输出的sql语句:SELECT uid,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND age < ?)
String userName="小";
Integer ageBegin=null;
Integer ageEnd=30;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(userName),"user_name",userName)
.gt(ageBegin!=null,"age",ageBegin)
.lt(ageEnd!=null,"age",ageEnd);
List<User> list = usermapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
5、LambdaQueryWrapper
@Test
public void test11() {
//LambdaQueryWrapper为了防止字段名写错,可以使用函数式接口访问实体类中的某一个属性所对应的字段名
//日志输出的sql语句
String userName="小";
Integer ageBegin=null;
Integer ageEnd=30;
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(StringUtils.isNotBlank(userName),User::getName,userName)
.gt(ageBegin!=null,User::getAge,ageBegin)
.lt(ageEnd!=null,User::getAge,ageEnd);
List<User> list = usermapper.selectList(lambdaQueryWrapper);
list.forEach(System.out::println);
}
6、LambdaUpdateWrapper
@Test
public void test12() {
//将名字中含有a并且(年龄大于20或邮箱为null)的用户信息修改
//日志输出的sql语句:UPDATE t_user SET user_name=?,email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL)) //UpdateWrapper设置修改条件的同时也可设置修改字段
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.like(User::getName,'a')
.and(i->i.gt(User::getAge,20).or().isNull(User::getEmail));
lambdaUpdateWrapper.set(User::getName,"小黑").set(User::getEmail,"xiaohei@atguigu.com");
int res = usermapper.update(null, lambdaUpdateWrapper);
System.out.println("res = " + res);
}
点赞,关注,收藏,您的支持是我更新的最大动力!!!