哈喽~大家好,这篇来看看MyBatis-Plus DQL与其他知识点
个人主页:个人主页
系列专栏: 【Java框架】
与这篇相关的文章:
Springboot 整合与文件配置 Springboot 整合与文件配置_程序猿追的博客-CSDN博客 springboot 项目起步讲解及自动装配原理 springboot 项目起步讲解及自动装配原理_程序猿追的博客-CSDN博客 MyBatis-Plus介绍与项目起步讲解 MyBatis-Plus介绍与项目起步讲解_程序猿追的博客-CSDN博客
目录
一、前言
二、环境搭建
1、测试搭建
三、常见功能
1、QueryWrapper
2、LambdaQueryWrapper
三、多条件构建
1、and 与 or
2、null判定
四、查询投影
1、查询指定字段
五、聚合查询
六、分组查询
七、等值查询
八、范围查询
九、模糊查询
十、排序查询
十一、映射匹配兼容性
SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL。
1. 数据查询语言DQL 数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE 子句组成的查询块: SELECT <字段名表> FROM <表或视图名> WHERE <查询条件>
2 .数据操纵语言DML 数据操纵语言DML主要有三种形式:INSERT、UPDATE、DELETE
3. 数据定义语言DDL 数据定义语言DDL用来创建数据库中的各种对象——表、视图、索引、同义词、聚簇等如:CREATE TABLE(表) / VIEW (视图 ) / INDEX (索引 ) / SYN (同义词) / CLUSTER (簇)
注:DDL操作是隐性提交的!不能rollback
4. 数据控制语言DCL 数据控制语言DCL用来授予或回收访问数据库的某种特权,并控制数据库操纵事务发生的时间及效果,对数据库实行监视等。
GRANT:授权。
ROLLBACK [WORK] TO [SAVEPOINT]:回退到某一点。
ROLLBACK:回滚
回滚命令使数据库状态回到上次最后提交的状态。
COMMIT :提交。
新建一个springboot项目
pom依赖
com.baomidou
mybatis-plus-boot-starter
3.4.1
org.springframework.boot
spring-boot-starter
com.alibaba
druid
1.1.16
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
org.projectlombok
lombok
1.18.12
实体类
//lombok
@Data
@TableName("user")
public class User {
private Long id;
private String name;
@TableField(value = "pwd",select = false)
private String password;
private Integer age;
private String tel;
@TableField(exist = false)
private Integer online;
}
dao层
@Mapper
public interface UserDao extends BaseMapper {
}
application.yml 文件
# dataSource
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
username: root
password: 123456
main:
banner-mode: off
# mp日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false
主启动类
@SpringBootApplication
public class Mybatisplus02DqlApplication {
public static void main(String[] args) {
SpringApplication.run(Mybatisplus02DqlApplication.class, args);
}
}
小知识
测试的时候,控制台打印的日志比较多,速度有点慢而且不利于查看运行结果,所以接下来我们把这个日志处理下
取消初始化spring日志打印,resources目录下添加logback.xml,名称固定,内容如下:
取消MybatisPlus启动banner图标
application.yml 文件
# mybatis-plus日志控制台输出
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: off # 关闭mybatisplus启动图标
取消SpringBoot的log打印
application.yml 文件
spring:
main:
banner-mode: off # 关闭SpringBoot启动图标(banner)
常见功能如下图
@Test
void testGetAll(){
QueryWrapper qw = new QueryWrapper();
qw.lt("age",18);
List userList = userDao.selectList(qw);
System.out.println(userList);
}
说明:继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取。
lt: 小于(<) ,最终的sql语句为
SELECT id,name,password,age,tel FROM user WHERE (age < ?)
第一种方式介绍完后,有个小问题就是在写条件的时候,容易出错,比如age写错,就会导致查询不成功
QueryWrapper的基础上使用lambda
@Test
void testGetAll(){
QueryWrapper qw = new QueryWrapper();
qw.lambda().lt(User::getAge, 10);//添加条件
List userList = userDao.selectList(qw);
System.out.println(userList);
}
User::getAget,为lambda表达式中的,类名::方法名,最终的sql语句为:
SELECT id,name,password,age,tel FROM user WHERE (age < ?)
注意:构建LambdaQueryWrapper的时候泛型不能省。
此时我们再次编写条件的时候,就不会存在写错名称的情况,但是qw后面多了一层lambda()调用
@Test
void testGetAll(){
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.lt(User::getAge, 10);
List userList = userDao.selectList(lqw);
System.out.println(userList);
}
LambdaQueryWrapper :与 QueryWrapper 查询类似,不过使用的是 Lambda 语法。
上面都是一个条件,那如果有多个条件该如何构建呢?
需求:查询数据库表中,年龄在10岁到30岁之间的用户信息
@Test
void testGetAll(){
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.lt(User::getAge, 30);
lqw.gt(User::getAge, 10);
List userList = userDao.selectList(lqw);
System.out.println(userList);
}
gt:大于(>),最终的SQL语句为
SELECT id,name,password,age,tel FROM user WHERE (age < ? AND age > ?)
构建多条件的时候,可以支持链式编程
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List userList = userDao.selectList(lqw);
System.out.println(userList);
需求:查询数据库表中,年龄小于10或年龄大于30的数据
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
List userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
or()就相当于我们sql语句中的or
关键字,不加默认是and
,最终的sql语句为:
SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)
//模拟页面传递过来的查询数据
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);
//null判定
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.lt(User::getAge, uq.getAge2());
if( null != uq.getAge()) {
lqw.gt(User::getAge, uq.getAge());
}
List userList = userDao.selectList(lqw);
System.out.println(userList);
目前我们在查询数据的时候,什么都没有做默认就是查询表中所有字段的内容,我们所说的查询投影即不查询所有字段,只查询出指定内容的数据。
void testGetAll(){
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.select(User::getId,User::getName,User::getAge);
List userList = userDao.selectList(lqw);
System.out.println(userList);
}
select(...)方法用来设置查询的字段列,可以设置多个,最终的sql语句为:
SELECT id,name,age FROM user
如果使用的不是lambda,就需要手动指定字段
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
QueryWrapper lqw = new QueryWrapper();
lqw.select("id","name","age","tel");
List userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
最终的sql语句为:SELECT id,name,age,tel FROM user
需求:聚合函数查询,完成count、max、min、avg、sum的使用
count:总记录数、max:最大值、min:最小值、avg:平均值、sum:求和
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
QueryWrapper lqw = new QueryWrapper();
// 计数
//lqw.select("count(*) as count");
//SELECT count(*) as count FROM user
// 最大值
//lqw.select("max(age) as maxAge");
//SELECT max(age) as maxAge FROM user
// 最小值
//lqw.select("min(age) as minAge");
//SELECT min(age) as minAge FROM user
// 求和
//lqw.select("sum(age) as sumAge");
//SELECT sum(age) as sumAge FROM user
// 平均值
lqw.select("avg(age) as avgAge");
//SELECT avg(age) as avgAge FROM user
List
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
QueryWrapper lqw = new QueryWrapper();
lqw.select("count(*) as count,tel");
lqw.groupBy("tel");
List
groupBy为分组,最终的sql语句为
SELECT count(*) as count,tel FROM user GROUP BY tel
注意:聚合与分组查询,无法使用lambda表达式来完成
MP只是对MyBatis的增强,如果MP实现不了,我们可以直接在DAO接口中使用MyBatis的方式实现
查询条件
前面我们只使用了lt()和gt(),除了这两个方法外,MP还封装了很多条件对应的方法,这一节我们重点把MP提供的查询条件方法进行学习下。
MP的查询条件有很多:范围匹配(> 、 = 、between)、模糊匹配(like)、空判定(null)、包含性匹配(in)、分组(group)、排序(order)
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);
}
}
eq(): 相当于 =
,对应的sql语句为
SELECT id,name,password,age,tel FROM user WHERE (name = ? AND password = ?)
selectList:查询结果为多个或者单个
selectOne:查询结果为单个
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.between(User::getAge, 10, 30);
//SELECT id,name,password,age,tel FROM user WHERE (age BETWEEN ? AND ?)
List userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
gt():大于(>)、ge():大于等于(>=)、lt():小于(<)、lte():小于等于(<=)、between():between ? and ?
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.likeLeft(User::getName, "J");
//SELECT id,name,password,age,tel FROM user WHERE (name LIKE ?)
List userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
like():前后加百分号,如 %xxx%、likeLeft():前面加百分号,如 %xxx、likeRight():后面加百分号,如 xxx%
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
LambdaQueryWrapper lwq = new LambdaQueryWrapper<>();
/**
* condition :条件,返回boolean,
当condition为true,进行排序,如果为false,则不排序
* isAsc:是否为升序,true为升序,false为降序
* columns:需要操作的列
*/
lwq.orderBy(true,false, User::getId);
userDao.selectList(lw);
System.out.println(lwq);
}
}
orderBy排序
condition:条件,true则添加排序,false则不添加排序
isAsc:是否为升序,true升序,false降序
columns:排序字段,可以有多个
orderByAsc/Desc(单个column):按照指定字段进行升序/降序
orderByAsc/Desc(多个column):按照多个字段进行升序/降序
orderByAsc/Desc
condition:条件,true添加排序,false不添加排序
多个columns:按照多个字段进行排序
除了上面介绍的这几种查询条件构建方法以外还会有很多其他的方法,比如isNull,isNotNull,in,notIn等等方法可供选择,具体参考官方文档的条件构造器来学习使用,具体的网址为:
前面我们已经能从表中查询出数据,并将数据封装到模型类中,这整个过程涉及到一张表和一个模型类:
之所以能够成功的从表中获取并封装到模型对象中,原因是表的字段列名和模型类的属性名一样。
那么问题就来了:
问题1:表字段与编码属性设计不同步
当表的列名和模型类的属性名发生不一致,就会导致数据封装不到模型对象,这个时候就需要其中一方做出修改,那如果前提是两边都不能改又该如何解决?
MP给我们提供了一个注解
@TableField
,使用该注解可以实现模型类属性名和表的列名之间的映射关系
问题2:编码中添加了数据库中未定义的属性
当模型类中多了一个数据库表不存在的字段,就会导致生成的sql语句中在select的时候查询了数据库不存在的字段,程序运行就会报错。
具体的解决方案用到的还是
@TableField
注解,它有一个属性叫exist
,设置该字段是否在数据库表中存在,如果设置为false则不存在,生成sql语句查询的时候,就不会再查询该字段了。
问题3:采用默认查询开放了更多的字段查看权限
查询表中所有的列的数据,就可能把一些敏感数据查询到返回给前端,这个时候我们就需要限制哪些字段默认不要进行查询。解决方案是
@TableField
注解的一个属性叫select
,该属性设置默认是否需要查询该字段的值,true(默认值)表示默认查询该字段,false表示默认不查询该字段。
知识点1:@TableField
名称 | @TableField |
---|---|
类型 | 属性注解 |
位置 | 模型类属性定义上方 |
作用 | 设置当前属性对应的数据库表中的字段关系 |
相关属性 | value(默认):设置数据库表字段名称 exist:设置属性在数据库表字段中是否存在,默认为true,此属性不能与value合并使用 select:设置属性是否参与查询,此属性与select()映射配置不冲突 |
问题4:表名与编码开发设计不同步
该问题主要是表的名称和模型类的名称不一致,导致查询失败。
解决方案是使用MP提供的另外一个注解@TableName
来设置表与模型类之间的对应关系。
知识点2:@TableName
名称 | @TableName |
---|---|
类型 | ==类注解== |
位置 | 模型类定义上方 |
作用 | 设置当前类对应于数据库表关系 |
相关属性 | value(默认):设置数据库表名称 |
代码演示
步骤1:修改数据库表user为tbl_user
直接查询会报错,原因是MP默认情况下会使用模型类的类名首字母小写当表名使用。
步骤2:模型类添加@TableName注解
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
步骤3:将字段password修改成pwd
直接查询会报错,原因是MP默认情况下会使用模型类的属性名当做表的列名使用
步骤4:使用@TableField映射关系
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
@TableField(value="pwd")
private String password;
private Integer age;
private String tel;
}
步骤5:添加一个数据库表不存在的字段
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
@TableField(value="pwd")
private String password;
private Integer age;
private String tel;
private Integer online;
}
直接查询会报错,原因是MP默认情况下会查询模型类的所有属性对应的数据库表的列,而online不存在
步骤6:使用@TableField排除字段
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
@TableField(value="pwd")
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
}
步骤7:查询时将pwd隐藏
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
@TableField(value="pwd",select=false)
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
}
不积跬步无以至千里,趁年轻,使劲拼,给未来的自己一个交代!向着明天更好的自己前进吧!