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 protected] 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("[email protected]"); 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 protected] 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("[email protected]"); 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会自动开启驼峰命名风格映射!!!