2. 条件构造器
2.1 条件构造器作用
//创建一个查询条件构造器对象,所有条件都放进去
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "John"); // eq添加等于条件
queryWrapper.ne("age", 30); // ne添加不等于条件
queryWrapper.like("email", "@gmail.com"); // like添加模糊匹配条件
等同于:
delete from user where name = "John" and age != 30
and email like "%@gmail.com%"
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
使用MyBatis-Plus的条件构造器,你可以构建灵活、高效的查询条件,而不需要手动编写复杂的 SQL 语句。它提供了许多方法来支持各种条件操作符,并且可以通过链式调用来组合多个条件。这样可以简化查询的编写过程,并提高开发效率。
2.2 条件构造器继承结构
条件构造器类结构:
Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : 查询/删除条件封装
- UpdateWrapper : 修改条件封装
- AbstractLambdaWrapper : 使用Lambda 语法
- LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
2.3 基于QueryWrapper条件构造器
@Test
public void test01(){
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE '%a%' AND age >=20 AND age <=30 AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
/**普通调用,代码稍显冗长
queryWrapper.like("username", "a")
queryWrapper.between("age", 20, 30)
queryWrapper.isNotNull("email");
**/
//链式调用
queryWrapper.like("username", "a")
.between("age", 20, 30)
.isNotNull("email");
//返回的对象可能是多个,故用selectList
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out:: println);
封装排序条件(orderByDesc/orderByAsc):
@Test
public void test02(){
//按年龄降序查询用户,如果年龄相同则按id升序排列
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
封装删除条件:(仍未QueryWrapper)
@Test
public void test03(){
//删除email为空的用户
//DELETE FROM t_user WHERE (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
//条件构造器也可以构建删除语句的条件
int result = userMapper.delete(queryWrapper);
System.out.println("受影响的行数:" + result);
}
and和or关键字使用(修改):
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
//UPDATE t_user SET age=18, email=user@sunsplanter.com WHERE username LIKE '%a%' OR email IS NULL AND age > 20)
queryWrapper
//(like or isnull) and gt
.like("username", "a")
.or()
.isNull("email");
.gt("age", 20)
User user = new User();
user.setAge(18);
user.setEmail("user@sunsplanter.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
指定列映射查询(select):
@Test
public void test05() {
//查询用户信息的username和age字段
//SELECT username,age FROM t_user
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("username", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
condition实现条件判断,可以叠加上述所有语句使用
@Test
public void testQuick3(){
String name = "root";
int age = 18;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//当name不为null, age > 1 时,才允许作为条件添加进构造器,一个作为相等判断,一个作为大于判断
//例如仅当前端传来的name不为空时,才判断是否相等
//方案1: 手动判断
if (!StringUtils.isEmpty(name)){
queryWrapper.eq("name",name);
}
if (age > 1){
queryWrapper.gt("age",age);
}
//方案2: 拼接condition判断.格式为eq(condition,列名,值)
//每个条件拼接方法都condition参数,这是一个比较运算,为true追加当前条件!
//仅当name非空时,才加入name = root条件进构造器中
queryWrapper.eq(!StringUtils.isEmpty(name),"name",name)
.gt(age>1,"age",age);
}
2.4 基于 UpdateWrapper条件构造器
使用queryWrapper进行更新的两个问题
a. 要准备一个实体类,将要改成的数据放入实体类
b. 原本为null的数据改不了(update方法只能改非空的数据)
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
//UPDATE t_user SET age=18, email=user@sunsplanter.com WHERE username LIKE '%a%' AND age > 20 OR email IS NULL)
queryWrapper
.like("username", "a")
.gt("age", 20)
.or()
.isNull("email");
//要修改成下列数据
User user = new User();
user.setAge(18);
user.setEmail("user@sunsplanter.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
使用updateWrapper:
a. 直接携带修改数据,无需声明实体对象
b. 指定任意修改值
@Test
public void testQuick2(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//将id = 3 的email设置为null, age = 18
updateWrapper.eq("id",3)
.set("email",null) // set 指定列和结果
.set("age",18);
//如果使用updateWrapper 实体对象写null即可!
int result = userMapper.update(null, updateWrapper);
System.out.println("result = " + result);
}
LambdaQueryWrapper属于QueryWrapper
LambdaUpdateWrapper属于UpdateWrapper
用法一样 , 功能增强 , 类似MB与MP
2.5 基于LambdaQueryWrapper组装条件
LambdaQueryWrapper对比QueryWrapper优势
QueryWrapper 示例代码:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "John")
.ge("age", 18)
.orderByDesc("create_time")
.last("limit 10");
List<User> userList = userMapper.selectList(queryWrapper);
LambdaQueryWrapper 示例代码:
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "John")
.ge(User::getAge, 18)
.orderByDesc(User::getCreateTime)
.last("limit 10");
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
从上面的代码对比可以看出,相比于 QueryWrapper,LambdaQueryWrapper 使用了实体类的属性引用(例如 `User::getName`、`User::getAge`),
而不是字符串来表示字段名,这提高了代码的可读性和可维护性。
2.6 方法引用回顾:
方法引用是 Java 8 中引入的一种语法特性,它提供了一种简洁的方式来直接引用已有的方法或构造函数。方法引用可以替代 Lambda 表达式,使代码更简洁、更易读。
Java 8 支持以下几种方法引用的形式:
1. 静态方法引用: 引用静态方法,语法为 `类名::静态方法名`。
2. 实例方法引用: 引用实例方法,语法为 `实例对象::实例方法名`。
3. 对象方法引用: 引用特定对象的实例方法,语法为 `类名::实例方法名`。
4. 构造函数引用: 引用构造函数,语法为 `类名::new`。
演示代码:
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Tom", "Alice");
// 使用 Lambda 表达式
names.forEach(name -> System.out.println(name));
// 使用方法引用
names.forEach(System.out::println);
}
}
lambdaQueryWrapper使用案例:
@Test
public void testQuick4(){
String name = "root";
int age = 18;
//使用QueryWrapper
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//每个条件拼接方法都condition参数,这是一个比较运算,为true追加当前条件!
//eq(condition,列名,值)
queryWrapper.eq(!StringUtils.isEmpty(name),"name",name)
.eq(age>1,"age",age);
// 使用lambdaQueryWrapper
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//注意: 需要使用方法引用
//技巧: 类名::方法名
lambdaQueryWrapper.eq(!StringUtils.isEmpty(name), User::getName,name);
List<User> users= userMapper.selectList(lambdaQueryWrapper);
System.out.println(users);
}
LambdaUpdateWrapper使用案例
@Test
public void testQuick2(){
//使用UpdateWrapper
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//将id = 3 的email设置为null, age = 18
updateWrapper.eq("id",3)
.set("email",null) // set 指定列和结果
.set("age",18);
//使用lambdaUpdateWrapper
LambdaUpdateWrapper<User> updateWrapper1 = new LambdaUpdateWrapper<>();
updateWrapper1.eq(User::getId,3)
.set(User::getEmail,null)
.set(User::getAge,18);
//如果使用updateWrapper 实体对象写null即可!
int result = userMapper.update(null, updateWrapper);
System.out.println("result = " + result);
}
3. 核心注解使用
3.1 @TableName注解
- 描述:表名注解,将实体类和表绑定(用于实体类名和xxxMapper.java的xxx不匹配时)
- 一个最简单的例子是,数据库表一般都是t_xxx,但实体类不会有t,这时二者就不匹配了
- 使用位置:实体类
public interface UserMapper extends BaseMapper<User> {
}
此接口对应的方法为什么会自动触发 user表的crud呢?
默认情况下, 根据指定的<实体类>的名称对应数据库表名,属性名对应数据库的列名!
但是不是所有数据库的信息和实体类都完全映射!
例如: 表名 t_user → 实体类 User 这时候就不对应了!
@TableName("sys_user") //对应数据库表名
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
特殊情况:如果表名和实体类名相同(忽略大小写)可以省略该注解!
其他解决方案:全局设置前缀
mybatis-plus: # mybatis-plus的配置
global-config:
db-config:
#所有的实体类xxx都与sys_xxx表名自动匹配
table-prefix: sys_ # 表名前缀字符串
3.1 @TableId 注解
- 描述:主键注解
- 使用位置:实体类主键字段之上
- 使用情况:
a.主键的字段名和属性名不一致,用value属性绑定二者
b.插入数据时,用type属性指定生成主键的策略
实体类:
@TableName("sys_user")
public class User {
//主键自增长,使用的前提是数据库中的主键字段必须提前设置了自增长
@TableId(value="主键列名",type = IdType_AUTO)
private Long id;
private String name;
private Integer age;
}
测试方法:
public void testTableId(){.
User user = new User();
user.setName("zhangsan");
user.setAge(20);
userMapper.insert(user);
}
会成功插入一条数据,其中id字段为默认的雪花算法生成的。
当然,也可以全局配置修改主键策略:
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
# 配置MyBatis-Plus的主键策略
id-type: auto
雪花算法:
**你需要记住的: 雪花算法生成的数字,需要使用Long 或者 String类型主键!!**
雪花算法(Snowflake Algorithm)是一种用于生成唯一ID的算法 , 用于解决分布式系统中生成全局唯一ID的需求。
在传统的自增ID生成方式中,使用单点数据库生成ID会成为系统的瓶颈,而雪花算法通过在分布式系统中生成唯一ID,避免了单点故障和性能瓶颈的问题。
雪花算法生成的ID是一个64位的整数,由以下几个部分组成:
1. 时间戳:41位,精确到毫秒级,可以使用69年。
2. 节点ID:10位,用于标识分布式系统中的不同节点。
3. 序列号:12位,表示在同一毫秒内生成的不同ID的序号。
通过将这三个部分组合在一起,雪花算法可以在分布式系统中生成全局唯一的ID,并保证ID的生成顺序性。
雪花算法的工作方式如下:
1. 当前时间戳从某一固定的起始时间开始计算,可以用于计算ID的时间部分。
2. 节点ID是分布式系统中每个节点的唯一标识,可以通过配置或自动分配的方式获得。
3. 序列号用于记录在同一毫秒内生成的不同ID的序号,从0开始自增,最多支持4096个ID生成。
需要注意的是,雪花算法依赖于系统的时钟,需要确保系统时钟的准确性和单调性,否则可能会导致生成的ID不唯一或不符合预期的顺序。
雪花算法是一种简单但有效的生成唯一ID的算法,广泛应用于分布式系统中,如微服务架构、分布式数据库、分布式锁等场景,以满足全局唯一标识的需求。
- @TableField
-
描述:字段注解(非主键)
-
使用场景 :
a. 区分该字段是否在数据库中存储.有一些字段仅在程序运行过程中存在 , 不需要持久化保存 ,于是提前声明这个字段不存在表里 , 不要去表里找.
b. 绑定实体属性和表的字段
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname")
private String name;
private Integer age;
@TableField(exist = false)
private String email;
}
MyBatis-Plus会自动开启驼峰命名风格映射!!!