IDEA自动注入警告问题
SpringBoot
- YML文件
- SpringBoot用法
- 整合Mybatis/MybatisPlus
- 查询方法
- selectObjs 查询符合条件的第一列数据
- selectList 返回对象信息
- selectMap 返回有效字段
- selectPage 分页查询返回对象
- selectMapsPage 分页查询返回有效字段
- 条件构造器的使用
- 特殊字符(eq,gt,lt,select)
- or
- like
- orderBy
- in
1.SpringBoot介绍
Spring Boot是由Pivotal团队提供的全新框架,设计目的是用来简化Spring应用的搭建以及开发过程
作用: SpringBoot的推出 让程序员更加专注于业务处理, SpringBoot是学习微服务框架基石(SpringCloud)
Spring 和SpringBoot关系 :SpringBoot是Spring的工具API框架
2.SpringBoot项目说明
2.1安装SpringBoot插件
- IDEA破解版本,自带SpringBoot插件 无需安装
- IDEA社区版本.需要手动安装一个SpringBoot插件.
2.2 创建SpringBoot项目
- 或在线创建直接导入
springboot将公共的常用的功能以Filter插件的形式进行管理,直接查询勾选依赖项即可
SpringBoot项目内置了Tomcat服务器,启动项目Tomcat服务器随之启动,Tomcat是项目部署的一个容器,项目需要持久的在服务器上运行,
2.2 SpringBoot项目结构
POM文件说明
- parent标签
父级工程 管理jar包的配置,SpringBoot将现有主流的框架都进行了整合,包含Spring,SpringMVC,Mybatis,以及其他主流框架radies , 在内部完成了jar包的依赖的配置.如果用户需要,则只添加某些核心包那么所有的依赖都会按照规则自动的下载.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- 依赖配置项
Springboot通过启动项的方式(spring-boot-starter) ,进行jar包文件的加载,同时这些功能中的配置项(Spring中写的cinfig) , SpringBoot有些也会自动的完成.无特殊的要求 无需手动的配置.开箱即用的思想!即加上jar包无需配置即可使用
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- build标签
关于build标签说明:
1.build标签它是maven工程的要求.
2.作用: 完成项目的打包/发布等一系列的功能.
3.该标签的使用是SpringBoot必须的,如果没有该标签则项目无法使用jar包运行
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.3 SpringBoot Maven操作
项目打包
在target目录下找jar包,或是去本地库根据坐标寻找jar包
java命令运行项目
-
dos命令
根目录输入cmd (command )
1.cd 切换目录
2.dir 查看当前目录结构
3.cls 清屏
4.tab键自动补齐 -
将jar包文件放置到指定的目录下,根目录中不能有中文空格等特殊字符,jar包一般以项目名或端口号命名
-
java命令
-
项目关闭
1.直接将dos窗口关闭
2.ctrl + c 万能的关闭指令
常见项目问题
2.4 jar包依赖传递性(Maven的工作原理)
- 问题: 当引入spring-boot-starter-web包时, 其他jar包文件如何依赖的?
maven jar包依赖的传递性
- A.jar 运行时依赖于 B.jar
- B.jar 运行时依赖于 C.jar
此时运行A.jar会自动加载B和C的依赖
- Maven的工作原理:
1.当maven解析pom.xml文件时,会根据maven坐标先去本地库查找指定的jar包文件
2.当jar包加载完成之后,由于该项目也是maven工程,所以maven工具会解析该项目的pom.xml文件 , 根据POM.xml文件 再次加载依赖包 直到所有的jar包依赖加载完成
maven 如何保证jar包依赖传输的安全性
传输加密机制: SHA1算法
SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
- 常识:
-
java中常见hash码为8位16进制数,共有(2^32)中组合
一位数有16种可能即 (2^4 ), 8位数 即 (24)8 -
如果给定的key相同,那么计算所得的hashcode必然相同,key不同结果有可能一样(哈希碰撞,概率极低,位数越高碰撞几率越小)
-
一个数据1kb,一个数据1mb hash计算一样快
- 数据占据内存中的一块区域,hash计算时,会在当前数据当中取点进行计算(不同hash算法取点的个数不同),不会因为数据大小而改变取点个数,所以计算速度相同
- 原理:
判断传递前后的sha1算法生成的消息摘要(40位16进制数)是否相同,如果相同则标识传输正常,如果不同,则文件有问题.
3. SpringBoot 入门案例
3.1 配置文件(resource)
application.properties
- 说明: 虽然properties文件是SpringBoot默认配置文件.但是其中编码格式比较繁琐,不便于查看.所以在项目中一般使用YML文件.直接改名即可
yml文件说明
YML文件语法:
- yml数据结构是K-V结构
- k与v 中间需要使用冒号空格连接
- yml配置文件有父子级关系,所以需要注意缩进项的位置
servlet用来接参数的
context -path: /jt 访问该项目是输入网址害得加/jt
3.2 @Value属性赋值
- 需求:在YML配置文件中编辑key=value结构,之后利用注解为属性赋值.
编辑YML配置文件
server:
port: 8090 #配置端口
servlet: #web项目发布路径
context-path: / #/表示根目录
#无业务交叉所以与server同级
#定义dept属性值 YML文件默认支持UTF-8
#配置文件本身就是字符串,赋值不要加引号
dept:
id: 100
name: 财务部
为Dept对象赋值
配置文件已经归于Spring容器进行管理
@Component //将对象交给Spring容器管理
public class Dept {
//spel表达式
//注入具体值 使用@value
//注入对象的引用 使用Autowire
@Value("${dept.id}")
private Integer id;
@Value("${dept.name}")
private String name;
Spring测试类中,需要手动调用容器,在容器中获取对象,执行方法
Springboot测试类简化了流程
注意事项: 以后写代码都必须写到主启动类的同包及子包中
因为包扫描看的是主启动类的位置
/**
* 在执行测试方法时,自动启动容器,所有对象都在容器内部,
* 所有对象想要手機用,直接注入即可
*/
@SpringBootTest
class DemoApplicationTests {
//容器启动成功之后,SpringBoot负责讲对象进行注入,对象的引用(注入)
@Autowired
private Dept dept;
@Test
void contextLoads() {
System.out.println(dept);
}
}
3.3 利用properties文件为属性赋值
- dept.properties文件
#程序读取时,默认iso-8859-1
dept.id2=888
dept.name2=SpringBoot测试
- 实体类
@Component //将对象交给Spring容器管理
@PropertySource(value = "classpath:/dept.properties",encoding ="utf-8")//加载指定的配置文件
public class Dept {
//spel表达式
//注入具体值 使用@value
//注入对象的引用 使用Autowire
@Value("${dept.id}")
private Integer id;
@Value("${dept.name}")
private String name;
//通过配置文件为属性赋值
@Value("${dept.id2}")
private Integer id2;
@Value("${dept.name2}")
private String name2;
3.4 Lombok插件
安装插件,添加jar包
<!--引入插件lombok 自动的set/get/构造方法插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
@Data
:ombok插件提供 自动生成get/set/toString/equals/hashcode等方法
@Accessors(chain = true)
:链式加载注解,相当于重写了set方法,返回this对象
- 之所以不能连续点方法,是因为set方法返回值是void,想办法让他返回原对象即可,this代表本类对象
4. 补充:进制转换
二进制—>八进制:
二进制—>十进制:按权展开求和
十进制—>二进制:
八进制—>二进制:
5. SpringBoot 用法
5.1 环境切换
- 通过云的方式动态连接配置中心(现在的yml),环境切换为配置中心提供服务
- 软件一般会在多个不同的环境中运行. 开发阶段有一个dev环境.开发完成会进行测试.则会有测试环境.最终项目部署到用户的服务中 生产环境.如果每次切换环境,都需要手动的修改配置文件,则造成诸多的不便.
需求: 简化环境切换带来的影响
多环境编辑
要求: 如果采用多环境测试,则要求每个环境中的数据项都应该保持一致.(不能只改端口,业务数据也要添加)缺失可能导致项目启动异常.
多环境配置:
- 关键语法"—" 环境分割 (画布裁剪)
- 每个环境都应该有自己的名称
- 默认环境名称
全部配置
#默认环境选项
spring:
profiles:
#默认环境配置名称
active: test
#环境分割
---
#YML文件语法
## 1.YML数据结构k-v结构
## 2.k与v 需要使用 :"空格" 连接
## 3.YMl配置文件有父子级关系 所以注意缩进项的位置
spring:
config:
activate:
on-profile: prod
server:
port: 80 #配置端口
servlet: #web项目发布路径
context-path: / #/表示根目录
#定义dept属性值 YML文件默认支持UTF-8
dept:
id: 100
name: 财务部
#环境分割线
---
# 每个环境都应该有自己的名称
spring:
config:
activate:
on-profile: test
server:
port: 8080 #配置端口
servlet: #web项目发布路径
context-path: / #/表示根目录
#定义dept属性值 YML文件默认支持UTF-8
dept:
id: 100
name: 集團本部
5.2 热部署
在开发阶段每次修改完源码都要重启服务器,程序才能生效. 热部署可以让程序自动的完成监控,重启服务器.
引入jar包
<!--支持热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
配置IDEA环境
组合键: ctrl + shift + alt + /
或者 ctrl + alt + a
5.3 SpringBoot整合Mybatis
POM+yml配置+xml+接口
5.3.1 导入数据库
课前资料1.数据库
5.3.2 创建项目
5.3.3 导入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jt</groupId>
<artifactId>springboot_demo_2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_demo_2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--
spring-boot-starter 启动项
当程序解析到该jar包时,就会按照jar包内的配置实例化对象
包括数据源的配置. 加载数据源......
没有数据库的链接,springboot并不知道要连接哪个数据库
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--引入数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<!--只对当前项目有效-->
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--true该jar包文件父子项目之间不传递.-->
<!--<optional>true</optional>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--spring整合mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--在项目打包时将lombok包除外.-->
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Maven的jar包作用范围
- test范围是指测试范围有效,在编译和打包时都不会使用这个依赖
- compile范围是指编译范围内有效,在编译和打包时都会将依赖存储进去
- provided依赖,在编译和测试过程中有效,最后生成的war包时不会加入 例如:
servlet-api,因为servlet-api tomcat服务器已经存在了,如果再打包会冲突 - runtime在运行时候依赖,在编译时候不依赖
默认依赖范围是compile
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<!--只对当前项目有效,表示两个项目之间依赖不传递-->
<optional>true</optional>
</dependency>
5.3.4 编辑application.yml
- 可以理解成SpringBoot启动项有个属性叫jdbc,它要求我们添加数据库的链接,我们就在核心配置文件中添加需求的配置
- 当程序执行,Spring容器去加载这些核心配置项,这些属性交给了容器进行管理,由于有自动化的配置(开箱即用),只要容器中有数据,自动化配置就会生效,即SpringBoot成功关联数据库
server:
port: 8090
#SpringBoot 开箱即用
#对应pom文件中的启动项
#<dependency>
# <groupId>org.springframework.boot</groupId>
# <artifactId>spring-boot-starter-jdbc</artifactId>
#</dependency>
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/jtadmin?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
#检查密码是否正确
password: root
#SpringBoot整合MybatisPlus配置
mybatis-plus:
#定义别名包
type-aliases-package: com.jt.pojo
mapper-locations: classpath:/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
#添加MP日志 打印执行的sql
logging:
level:
com.jt.mapper: debug
数据源参数说明
url: jdbc:mysql://127.0.0.1:3306/jtadmin?
3306为数据库的服务端口,8080位Tomcat服务器占用端口,不可混为一谈serverTimezone=GMT%2B8
%2B 是转义字符代表 “+” 号 .服务时区,新版本的驱动要求必须配置时区&useUnicode=true&characterEncoding=utf8
使用Unicode编码字符集 , 要求字符UTF-8编码&autoReconnect=true
是否自动重连.&allowMultiQueries=true
是否允许批量操作 同时执行多个sql!
Mybatis配置说明
定义别名包:对相同的包路径进行公共的抽取
#SpringBoot整合Mybatis配置项
mybatis:
#定义别名包
type-aliases-package: com.jt.pojo
mapper-locations: classpath:/mybatis/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
5.3.5 编辑映射文件和接口
- namespace
1.Mapper.xml配置文件的唯一标识 , 需要与Mapper接口保持一致.
如:UserMapper.findAll();
我们写的是方法,数据库并不认识,只能识别sql语句,UserMapper所调用的方法(findAll)应与映射文件的id(sql)进行绑定,绑定之前应该找到UserMapper接口所关联的配置文件才行,关联映射文件后,通过方法映射Sql语句!!! - 查询标签必须有返回值
- resultType :直接返回POJO对象的数据 适用于单表查询
- resultMap: 适用于关联查询的结果封装 一般采用第三方对象接收,如查询AB两张表,结果为pojoA和pojoB,谁接收都不合适
resultType="包名.类名"
resultType="com.jt.pojo.DemoUser"
pojo对象DemoUser与数据库表demo_user
进行了对应
优化:将不变的包路径进行抽取,因为变化的只有类名.
解决方案: 定义别名包即可
yml配置文件中包路径进行公共的抽取,定义别名包
type-aliases-package: com.jt.pojo
则可以直接返回对象的名称:resultType="DemoUser"
程序解析时: 首先根据别名包的名称进路径的拼接 com.jt.pojo.DemoUser
映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jt.mapper.DemoUserMapper">
<!--编辑更新操作 #mybatis中的一种表达式写法 -->
<update id="updateUser">
update demo_user set name=#{nowName} where name=#{oldName}
and sex=#{sex}
</update>
<!--
完成用户入库操作 id="与方法名称保持一致"
sql结束时不需要添加;号 在Mysql数据库中执行;号没有问题,
但是如果在Oracle中执行则必然报错.
-->
<insert id="insertUser">
insert into demo_user(id,name,age,sex)
value(null,#{name},#{age},#{sex})
</insert>
<select id="findAll" resultType="DemoUser">
select * from demo_user
</select>
<!--更新操作的标签就是为了方便程序员开发时使用,程序
执行时没有任何的差别,所以可以混用
<insert id="">
</insert>-->
</mapper>
将Mapper接口交给容器管理
@MapperScan("com.jt.mapper")
主启动类添加注解
mapper接口
package com.jt.mapper;
/*注意事项: basemapper必须添加泛型对象 切记!!!*/
//@Mapper //表示将mapper接口交给Spring容器管理,即可以通过Autowire的方式进行动态的注入
public interface DemoUserMapper extends BaseMapper<DemoUser> {
//使用MP不要重载里边的方法 容易解析异常
List<DemoUser> findAll();
void insertUser(DemoUser user);
void updateUser(String oldName, String nowName, String sex);
}
5.4 测试类
主启动类,管理了接口测试时才可以注入,测试类位置应与主启动类位置对应
@SpringBootApplication //标识主启动类的注解
@MapperScan("com.jt.mapper") //将Mapper接口交给容器管理
public class SpringBootRun {
public static void main(String[] args) {
SpringApplication.run(SpringBootRun.class,args);
}
}
@SpringBootTest
public class TestSpringBoot {
@Autowired
//通过jdk动态代理创建的代理对象
private DemoUserMapper userMapper;
/**
* Spring未优化前需要在容器中获取对象
* UserService userService = context.getBean(UserService.class);
*/
@Test
public void testFindAll(){
//检查是否为代理对象
System.out.println(userMapper.getClass());
/**
* class com.sun.proxy.$Proxy73
*/
//有数据说明整合mybatis成功
List<DemoUser> userList = userMapper.findAll();
System.out.println(userList);
}
5.5 SpringBoot创建接口代理对象流程
- 原先mybatis 获取sql过程
public class MybatisTest02 {
SqlSession session = null;
@Before
public void beforeMethod() throws Exception {
//1.读取mybatis核心配置文件中的配置信息(起名:mybatis-config.xml)
InputStream in= Resources.getResourceAsStream("mybatis-config.xml");
//2.基于读取的配置信息获取SqlSessionFactory对象(工厂)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.通过工厂对象打开与数据库的连接(即获取SqlSession对象,类似于connection)
//提交方式二:true: 表示自动提交事务, 默认是false, 表示关闭自动提交, 需要手动提交!
session = factory.openSession( true );
//4.获取由Mybatis提供的接口的子类对象实例(参数为接口的字节码对象),底层利用反射剖析这个接口
EmpMapper mapper = session.getMapper(EmpMapper.class);
//接口的实现类中实现了定义的查询方法
List<Emp> list = mapper.findAll();
for (Emp emp : list) {
System.out.println(emp);
}
}
- 模拟mybatis提供的EmpMapper接口的实现类
构造实现类对象时将连接作为参数传递进来,实现类的目的是执行sql方法,两个重要参数是namespace和id- namespace : 通过字节码对象获取实现类的接口数组(Interfaces),根据下标获取接口全类名getName(),即namespace
- id : 通过当前线程获取方法调用栈的信息(StackTrace),由于栈内存结构特殊性(先进后出),执行sql的方法总是在第二位(栈底总是是用户调用的测试方法),通过getMethodName()获取当前执行的方法名即id
将namespace和id拼接起来就可以通过session执行
public class EmpMapperImpl implements EmpMapper{
//当外部有需求与实现类提供的功能一样,就会去new实现类对象
/**new实现类对象时,将session对象作为参数传递进来,保存在实现类对象内部,类中就可以使用session对象提供的方法*/
public EmpMapperImpl( SqlSession session ) {
this.session = session;
}
private SqlSession session;
/* 查询所有的员工信息 */
public List<Emp> findAll() {
// 1.获取当前这个类的父接口的全类名(=namespace)
String interName = this.getClass().getInterfaces()[0].getName();
// 2.获取当前方法的名字(=SQL标签的id值)
StackTraceElement[] st = Thread.currentThread().getStackTrace();
String methodName = st[1].getMethodName();
List<Emp> list = session.selectList( interName+"."+methodName );
return list;
}
}
SpringBoot根据pom文件加载依赖项,Spring根据这些配置实例化对象,放入Spring容器进行统一管理
容器启动成功之后,通过容器即可注入要使用的对象
@Autowired
//通过jdk动态代理创建的代理对象
private DemoUserMapper userMapper;
6. SpringBoot 操作DB练习
先写方法,根据报错在接口中创建方法,复制方法名在映射文件中编写sql
如果前端传过来的数据封装在map集合中,#{ }占位符名称要和Map集合的key保持一致!
如果前端传过来的数据封装在POJO对象中,#{ }占位符名称要在POJO对象中有对应的getXxx方法,或者有对应的属性(名称和属性名相同)
- 入库操作
<insert id="insertUser">
insert into demo_user(id,name,age,sex)
value(null,#{name},#{age},#{sex})
</insert>
//入库操作
@Test
public void testInsert(){
DemoUser user = new DemoUser();
user.setId(null).setName("mybatis信息").setAge(18).setSex("男");
userMapper.insertUser(user);
}
- 更新操作
<update id="updateUser">
update demo_user set name=#{nowName} where name=#{oldName}
and sex=#{sex}
</update>
/**将mybatis name="mybatis信息" 改为"测试信息". 条件 sex="男"
update demo_user set name="测试信息"
where name="mybatis信息" and sex="男"
*/
@Test
public void testUpdate(){
//1.封装数据
String oldName = "mybatis信息";
String nowname = "测试信息";
String sex = "男";
userMapper.updateUser(oldName,nowname,sex);
}
7. Spring-MybatisPlus
MyBatis-Plus (简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
7.1 MP特点
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作(xml文件中的sql)
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求(无法解决多表关联查询)
7.2 MP原理
之前操作数据库采用sql(面向过程的语言)方法进行编辑. 但是如果所有的单表操作都由程序员完成.则开发效率低. MP开发了一种机制实现以面向对象的方式操作数据库即 动态拼接sql
- 1.对象与数据库中的表实现一对一映射—@TableName , 对象是包装体,我们操作的是对象中的属性,对象属性要与表中的字段一对一映射—@TableId+@TableField
- 2.MP采用BaseMapper的方式 将公共的接口方法进行了抽取. 采用泛型T的方式进行约束
- 3 .MP将用户操作的对象方法(
userMapper.selectList(null)
)在底层自动的转化为Sql语句!
对象转化Sql原理
int userMapper.insert(T entity);
Sql语句: insert into 表名(字段名) value(属性值…)
7.3 MP入门案例
导入jar包
苞米豆家的jar包
<!--spring整合mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
编辑POJO对象
MP以面向对象的方式操作数据库.其中对象与表 , 属性与字段都一对一映射
- @TableName
如果如果没有添加注解的属性值,则默认以类名(DemoUser)当做表名(demouser) - @TableId
标识主键 ,type
属性取值IdType.AUTO
表示定义主键自增
有特殊需求,要求取唯一性数据,如订单号,因为每次新增订单号都需要找到末位订单号再进行自增操作,数据量较大时,主键自增的方式效率较低
主键可设置为UUID的取值方式,type
属性取值IdType.ASSIGN_UUID
表示定义主键UUID的取值方式- UUID是一种随机HASH算法,HASH算法以当前时间戳T为参数能够保证得到的ID不重复
- @TableField
标识属性与表字段映射
value属性表示表字段名称,若与属性名相同,可省略不写
exist属性取false表示当前属性不是数据库的字段,但在项目中必须使用,这样在执行入库操作时,mybatis-plus就会忽略这个属性,不会报错
@Data
@Accessors(chain = true)
@TableName("demo_user")
public class DemoUser {
@TableId(type = IdType.AUTO)
//@TableId(type = IdType.ASSIGN_UUID)
private Integer id;
//@TableField(value = "name",exist = true) 名称一致可以省略
private String name;
private Integer age;
private String sex;
}
编辑Mapper接口
MP提供BaseMapper接口,接口中编辑了常见单表的CRUD操作,用于规范化常见方法的书写,我们的mapper接口在继承BaseMapper时注意添加泛型,泛型即与数据库表对应的接口对象的名称BaseMapper<DemoUser>
,使用MP注意不要重写里面提供的方法,容易解析异常
//@Mapper
public interface DemoUserMapper extends BaseMapper<DemoUser> { }
编译YML配置文件
- MP增强了Mybatis, MP内部包含了Mybatis 所以将Mybatis的包删除,否则内部jar包异常
- 修改mybatis名称
测试MP提供的单表查询
//测试MybatisPlus
@Test
public void testMP(){
List<DemoUser> userList = userMapper.selectList(null);
System.out.println(userList);
/**
* com.jt.mapper.DemoUserMapper.selectList : ==>
* Preparing: SELECT id,name,age,sex FROM demo_user
*/
}
7.4 常见MP的API
添加日志打印
yml配置文件中添加日志打印配置,接口包路径对应日志级别debug
#添加MP日志 打印执行的sql
logging:
level:
com.jt.mapper: debug
入库操作
/**
* 入库操作
* 结果: ==> Preparing: INSERT INTO demo_user ( name, age, sex ) VALUES ( ?, ?, ? ) 预编译
* ==> Parameters: MP测试(String), 19(Integer), 男(String)
* <== Updates: 1
*/
@Test
public void insert(){
DemoUser user = new DemoUser();
user.setName("MP测试").setSex("男").setAge(19);
userMapper.insert(user);
}
更新操作
updateById
方法 , 原理是根据对象中不为null的属性当做set条件. 且Id必须赋值 并且ID当做唯一where条件
/**
* 更新操作:
* com.jt.mapper.DemoUserMapper.updateById :
* ==> Preparing: UPDATE demo_user SET name=?, age=? WHERE id=?
* ==> Parameters: 英雄联盟(String), 22(Integer), 231(Integer)
* <== Updates: 1
*/
@Test
public void updateById(){
DemoUser user = new DemoUser();
user.setName("英雄联盟").setAge(22).setId(231);
userMapper.updateById(user);
}
update(entity, updateWrapper);
参数说明:
1.entity 实体对象 需要修改的数据进行封装
2.updateWrapper 条件构造器
/**
* 需求: 更新数据
* 将name="中午吃什么" 改为name="晚上吃什么"
* 性别: 改为 其他
* Sql:
* update demo_user set name="xxx",sex="其他"
* where name="xxxx"
* 结果:
* Preparing: UPDATE demo_user SET name=?, sex=? WHERE (name = ?)
* Parameters: 卡特琳娜(String), 其他(String), 英雄联盟(String)
*
* Preparing: SELECT id,name,age,sex FROM demo_user WHERE (sex = ? AND name = ?)
* Parameters: 其他(String), 卡特琳娜(String)
*
*/
@Test
public void testSelect10(){
DemoUser user = new DemoUser();
user.setName("卡特琳娜").setSex("其他");
UpdateWrapper<DemoUser> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "英雄联盟");
userMapper.update(user, updateWrapper);
QueryWrapper<DemoUser> queryWrapper1 = new QueryWrapper<>();
queryWrapper1.eq("sex", "其他").eq("name", "卡特琳娜");
DemoUser user1 = userMapper.selectOne(queryWrapper1);
System.out.println(user1);
}
查询操作
根据主键ID查询
返回的一条记录
/**
* 需求:id=21的用户 根据ID查询数据
* 结果: Preparing: SELECT id,name,age,sex FROM demo_user WHERE id=?
* Parameters: 21(Integer)
* DemoUser(id=21, name=孙尚香D, age=18, sex=女)
*/
@Test
public void testSelect(){
DemoUser user = userMapper.selectById(21);
System.out.println(user);
}
根据对象查询
selectObjs
返回表中第一列数据,由于不知道第一列数据类型,所以返回对象类型组成的list集合
应用场景:MP虽然不支持多表关联查询,但是可以单表自关联查询
1.一般根据条件查询Id的值,查询之后为后续的sql提供数据支持
2.有时用户只需要查询ID的值,并不需要其他数据项时 使用objs.
/**
* 需求: 查询表中第一列数据 selectObjs
* 说明: queryWrapper=null 不需要where条件
* 结果:Preparing: SELECT id,name,age,sex FROM demo_user
* Parameters:
* Total: 48
* [1, 3, 4, 5, 6, 7, 8, 9, 11, 12, 16, 17, 18, 21, 22, 23, 24,
* 25, 27, 31, 38, 39, 40, 41, 43, 44, 46, 49, 50, 51, 52, 53, 55,
* 58, 62, 66, 68, 172, 173, 182, 194, 196, 227, 228, 229, 230, 231, 232]
*/
@Test
public void testSelect11(){
List objs = userMapper.selectObjs(null);
System.out.println(objs);
}
如下:此处查询到的objs可以作为参数按需查询
/**
* 需求: 查询表中所有男性用户的ID
* 结果: Preparing: SELECT id,name,age,sex FROM demo_user WHERE (sex = ?)
* Parameters: 男(String)
* Total: 30
* [1, 3, 4, 5, 6, 7, 8, 9, 22, 23, 24, 25, 43, 49, 50, 51, 52,
* 53, 55, 58, 62, 66, 68, 182, 194, 196, 227, 229, 231, 232]
*/
@Test
public void testSelect7(){
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("sex","男");
List objs = userMapper.selectObjs(queryWrapper);
System.out.println(objs);
}
selectList()
返回的是满足所有条件的List集合单条数据包含表中所有列
selectList()
需要传递queryWrapper对象(条件构造器) , 用于拼接Where条件,值可以为null
原则:根据对象中不为null的属性生成Where条件,具体操作时,需要先封装要执行的条件对象,将条件对象传递给条件构造器,用于构造Where条件,左侧一定要加泛型才可以链式加载多个条件
给查询方法selectList()
传递条件构造器,即可完成查询
实现面向对象的方式操作数据库
/**
* 根据属性查询
* 需求:查询name="白骨精" sex=女 的数据 List
* 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE name=? AND sex=?
* Parameters: 白骨精(String), 女(String)
* [DemoUser(id=41, name=白骨精, age=3000, sex=女)]
*/
@Test
public void test2(){
DemoUser user = new DemoUser();
DemoUser user1 = user.setName("白骨精").setSex("女");
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>(user1);
List<DemoUser> users = userMapper.selectList(queryWrapper);
System.out.println(users);
}
根据条件构造器查询
条件构造器为空则查询泛型所对应的表的全部数据
条件构造器参数 :
- @param condition 执行条件,表示该条件是否加入最后生成的sql,默认为true
- @param column 字段 ,表示数据库字段,
- @param val 值
/*Preparing: SELECT id,name,age,sex FROM demo_user
==> Parameters:
<== Total: 48*/
List<DemoUser> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
特殊字符的用法
如果查询条件中有特殊关系符,则使用特殊转义字符查询
特殊关系符 | 转义 |
---|---|
> | gt |
< | lt |
= | eq |
> = | ge |
< = | le |
< > | ne |
默认连接符: AND 不用写
or直接点
/**
* 需求: 查询age>18岁 并且性别为女的用户
* Sql: select * from demo_user where age > 18 and sex="女"
* 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE (age > ? AND sex = ?)
* Parameters: 18(Integer), 女(String)
* Total: 6
* */
@Test
public void testSelect2(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18).eq("sex", "女");
List<DemoUser> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
OR的用法
主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)
/**
* 需求: 查询 age>18 or age < 50 的数据
* Sql: select * from demo_user where age > 18 or age < 50
* 结果: SELECT id,name,age,sex FROM demo_user WHERE (age > ? OR age < ?)
* 18(Integer), 50(Integer)
* Total: 48
*/
@Test
public void testSelect2(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18).or().lt("age", 50);
List<DemoUser> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
Like的用法
看 % 的位置 决定左右
释义 | Sql | 条件构造代码 |
---|---|---|
查询name中以精开头的数据 | name like " 精 % " | likeRight |
查询name中以精结尾的数据 | name like " % 精 " | LikeLeft |
查询name中含"精"字的数据 | name like " % 精 % " | Like |
查询name中不包含"精"字的数据 | notlike " % 精 % " | notLike |
notLikle : 查询与条件不匹配的数据
/**
* like关键字
* 需求: 查询name中包含"精"字的数据
* 结果: Preparing: SELECT id,name,age,sex FROM demo_user WHERE (name LIKE ?)
* Parameters: %精%(String)
* Total: 2
*/
@Test
public void testSelect3(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
QueryWrapper<DemoUser> like = queryWrapper.like("name", "精");
List<DemoUser> userList = userMapper.selectList(like);
System.out.println(userList);
}
Order by的用法(排序)
orderByDesc()
降序排列,参数为字段column
/**
* 查询sex=男的数据,以id倒序排列
* Sql: select * from demo_user where sex='男' order by id desc
* 结果: Preparing: SELECT id,name,age,sex FROM demo_user WHERE (sex = ?) ORDER BY id DESC
* Parameters: 男(String)
*/
@Test
public void testSelect4(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("sex", "男")
.orderByDesc("id");
List<DemoUser> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
IN的用法
in(R column, Collection<?> value)
集合形式传递参数
in(R column, Object... values)
可变参数形式传递,注意是对象类型
- 可变参数形式
==> Preparing: SELECT id,name,age,sex FROM demo_user WHERE (id IN (?,?,?,?,?))
==> Parameters: 1(Integer), 3(Integer), 5(Integer), 6(Integer), 7(Integer)
<== Total: 5
值得注意,用户查询数据时 1,3,5,6,7 是从前端传递来的参数,本身就是可变参数,此种方式不符合动态要求
参数应是一个数组.
MP在解析时,把数组整体当做了对象查询,并没有根据数组内的数据进行查询,需要将数组转化成集合
API:
传递可变参数的泛型对象
List<Integer> asList = Arrays.asList(1, 3, 5, 6, 7);
要么传递一个数组对象,所以数组类型不可用基本数据类型int
参与转换,应用包装类型
int[] ids = {1,3,5,6,7}; x
Integer[] ids = {1,3,5,6,7};
/**
* 5.查询id= 1,3,5,6,7的用户
* Sql: select * from demo_user where id in (xxx,xx,xx)
* 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE (id IN (?,?,?,?,?))
* Parameters: 1(Integer), 3(Integer), 5(Integer), 6(Integer), 7(Integer)
* Total: 5
*/
@Test
public void testSelect5(){
Integer[] ids = {1,3,5,6,7};
List<Integer> idlist = Arrays.asList(ids);
//List<Integer> asList = Arrays.asList(1, 3, 5, 6, 7);
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
// queryWrapper.in("id",ids); 数组必须为包装类型,不如list集合好用
queryWrapper.in("id",idlist);
List<DemoUser> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
condition参数用法—可变参数(动态查询)
condition: 内部编辑一个判断的条件, 如果返回值结果为true 则拼接该字段 . 如果为false 则不拼接该字段.
select * from demo_user
where name!=null name=xxx and age!=null age>xxx
name不为空才会执行 name= xxx , 同理age不为空才会执行后面的语句
此处 name!=null 和 age!=null 就是 condition
CollectionUtils.isEmpty(ageList)
判断集合是否为空
public static boolean isEmpty(@Nullable Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
-
StringUtils.isEmpty(name)
判断字符串是否为空 基本不用 -
StringUtils.hasLength(str)
判断字符串是否有效
public static boolean hasLength(@Nullable CharSequence str) {
return str != null && str.length() > 0;
}
- 如果调用的是对象(DemoUser)的
equals()
方法,则一定要重写后再使用,此处调用的是String 的equals()
,底层已经重写,比的就是内容
boolean flag = name !=null && !"".equals(name);
/**
* 需求: 如果根据name属性和age属性查询数据. 有时某个数据可能为null,要求动态查询!!!
* where name=xxx age>xxxx mybatis中的if标签
*
* 伪Sql: select * from demo_user
* where name!=null name=xxx and age!=null age>xxx
*
* 结果:SELECT id,name,age,sex FROM demo_user WHERE (name = ? AND age > ?)
* Parameters: 小乔(String), 18(Integer)
* Total: 1---[DemoUser(id=43, name=小乔, age=19, sex=男)]
*/
@Test
public void testSelect6(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
String name = "小乔";
int age = 18;
//boolean flag = name !=null && !"".equals(name);
queryWrapper.eq(StringUtils.hasLength(name),"name", name)
.gt(age>0,"age",age);
List<DemoUser> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
- 案例二 含有condition参数的like和in关键字的sql
/**
* 需求:查询name包含字符小,并且age属于[17 , 18 , 19 , 20 ]。
* 伪sql: select * from demo_user
* where (name LIKE %小%) and age IN (?,?,?,?,?)
* 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE (name LIKE ? AND age IN (?,?,?,?))
* Parameters: %小%(String), 17(Integer), 18(Integer), 19(Integer), 20(Integer)
* Total: 5
* [DemoUser(id=11, name=小乔, age=17, sex=女),
* DemoUser(id=43, name=小乔, age=19, sex=男),
* DemoUser(id=49, name=小兰兰, age=18, sex=男),
* DemoUser(id=53, name=小明, age=18, sex=男),
* DemoUser(id=227, name=小法, age=20, sex=男) ]
*/
@Test
public void testcondition(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
String name = "小";
List<Integer> asList = Arrays.asList(17, 18, 19, 20);
queryWrapper.like(StringUtils.hasLength(name),"name",name)
.in(!asList.isEmpty(),"age",asList);
List<DemoUser> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
查询指定字段的数据
与 selectObjs 对比记忆
/**
* 需求: 想查询name/sex字段
* queryWrapper.select("name","sex"); 挑选指定字段
* Preparing: SELECT name,sex FROM demo_user
* [ DemoUser(id=null, name=黑熊精, age=null, sex=男),
* DemoUser(id=null, name=金角大王, age=null, sex=男),
* DemoUser(id=null, name=银角大王, age=null, sex=男),
* DemoUser(id=null, name=唐僧, age=null, sex=男), .....]
*/
@Test
public void testSelect8(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper();
queryWrapper.select("name","sex");
List objs = userMapper.selectList(queryWrapper);
System.out.println(objs);
}
返回有效字段
上述方法查询结果封装在了DemoUser对象中,结果有冗余数据,并不方便,应只返回有效字段更为高效,不能用DemoUser对象封装,selectMap要求返回数据使用Map集合封装,K取表字段名,所以是String类型,字段值类型繁杂,所以为objs类型,由于存在多条数据,所以最外层嵌套List集合.
/**
* 需求: 想查询name/age字段 要求只返回有效字段
* 返回有效字段的查询:selectMaps
* 结果: Preparing: SELECT name,age FROM demo_user
* Total: 48
* [ {name=黑熊精, age=3000},
* {name=金角大王, age=3000},
* {name=银角大王, age=4000},
* {name=唐僧, age=30},..... ]
*/
@Test
public void testSelect9(){
QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name","age");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
System.out.println(maps);
}