一、数据传递
返回值为:字符串
package com.apesource.springboot_web_04.controller;
import com.apesource.springboot_web_04.pojo.Emp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 返回值为:字符串
*/
@Controller
@RequestMapping("/string")
public class StringController_01 {
/**
* 进入首页
*
*/
@RequestMapping("/show")
public String show(){
return "index";
}
}
package com.apesource.springboot_web_04.controller;
import com.apesource.springboot_web_04.pojo.Emp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/json")
public class JsonController_02 {
/**
* @ResponseBody 对象===>json
* 位置:1.类
* 2.方法
*
* @RequestBody json===>对象
* 位置:方法参数
*
* @RestController = @Controller + @ResponseBody
*/
@RequestMapping("/show1")
@ResponseBody
public List<Emp> show1(){
//1.模拟数据库
Emp emp1=new Emp(1,"张毅老师","男");
Emp emp2=new Emp(2,"张毅老师","男");
Emp emp3=new Emp(3,"张毅老师","男");
List<Emp> list=new ArrayList<>();
list.add(emp1);
list.add(emp2);
list.add(emp3);
return list;
}
@RequestMapping("/show2")
@ResponseBody
public String show2(){
return "helloWorld";
}
}
二、文件上传
1.添加坐标
<!--文件上传-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
2.UserController
package com.apesource.springboot_web_04.controller;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Controller
@RequestMapping("/for")
public class UserController {
//进入测试页面
@RequestMapping("/show")
public String show(){
return "index";
}
//文件上传
@RequestMapping("/fileupload")
public String fileupload(String uname, MultipartFile upic, HttpServletRequest request){
System.out.println("用户名:"+uname);
System.out.println(upic);
System.out.println(upic.getOriginalFilename());
System.out.println(upic.getName());
//方式1.将文件upic以流的方式写入当前服务器磁盘(应用服务器)
//方式2.文件服务器(七牛云)
//构造一个带指定 Region 对象的配置类
Configuration cfg = new Configuration(Region.autoRegion());
//...其他参数参考类注释
UploadManager uploadManager = new UploadManager(cfg);
//...生成上传凭证,然后准备上传
String accessKey = "Tfv6J4DIRakNREBeGc8eNL21xEAP2uXsyvUa8esk";
String secretKey = "Dr5lD7XypDenJ6i14DtqxKK87dkA9ux47xn53tUB";
String bucket = "2025ks";
//默认不指定key的情况下,以文件内容的hash值作为文件名
String key = null;
try {
byte[] uploadBytes = upic.getBytes();
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
try {
Response response = uploadManager.put(uploadBytes, key, upToken);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);//获取文件名
System.out.println(putRet.hash);//获取文件hash值
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
//ignore
}
}
} catch (Exception ex) {
//ignore
}
return "success";
}
}
3.index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
文件上传:
<ol>
<li>坐标</li>
<li>制作页面-form表单编码</li>
<li>通过*****接受文件</li>
</ol>
<hr/>
<form action="fileupload" method="post" enctype="multipart/form-data">
用户名:<input name="uname"/><br/>
图片:<input name="upic" type="file"/><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
4.success.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
成功页面
</body>
</html>
三、注册Servlet、Filter、Listener
而由于 Spring Boot 默认是以 jar 包的方式运行嵌入式Servlet容器来启动应用,没有web.xml文件,
Spring提供以下Bean来注册三大组件
ServletRegistrationBean 注册自定义Servlet
FilterRegistrationBean 注册自定义Filter
ServletListenerRegistrationBean 注册自定义Listener
第一种:
package com.apesource.springboot_web_04.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入servlet");
resp.getWriter().println("<h1>hello world</h1>");
};
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
@ServletComponentScan包扫描 (入口类)
@ServletComponentScan
@SpringBootApplication
public class SpringbootWeb04Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWeb04Application.class, args);
}
}
第二种::配置类
package com.apesource.springboot_web_04.config;
import com.apesource.springboot_web_04.servlet.MyServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
@Configuration
public class MyMvcConfig {
//注册替换Servlet
//替换:@WebServlet(urlPatterns="/myServlet")
@Bean
public ServletRegistrationBean doServlet(){
ServletRegistrationBean<MyServlet> bean=new ServletRegistrationBean<MyServlet>();
bean.setServlet(new MyServlet());
bean.setUrlMappings(Arrays.asList("/myServlet"));
return bean;
}
}
四、切换为其他嵌入式Servlet容器
SpringBoot 默认针对Servlet容器提供以下支持:
Tomcat(默认使用)
Jetty :支持长连接项目(如:聊天页面)
Undertow : 不支持 JSP , 但是并发性能高,是高性能非阻塞的容器
默认Tomcat服务器
在spring-boot-starter-web启动器中默认引入了tomcat容器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.1.0.RELEASE</version>
<scope>compile</scope>
</dependency>
切换 Jetty 容器
dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除tomcat容器 -->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--引入其他的Servlet容器-->
<dependency>
<artifactId>spring-boot-starter-jetty</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
Servlet容器:运行启动类就可启动,或将项目打成可执行的 jar 包
优点:简单、快捷;
缺点:默认不支持JSP、优化定制比较复杂使用定制器, 还需要知道 每个功能 的底层原理
外置Servlet容器:配置 Tomcat, 将项目部署到Tomcat中运行
五、restFul
满足这些约束条件和原则的应用程序或设计就是 RESTful。
设计RESTful风格的API:
1、在RESTful风格的架构中, 每个网址代表一种资源,所以网址中不能有动词,只能有名词。而且所
用的名词往往与数据库的表名对应。
2、HTTP动词设计: GET (获取资源) POST (新建资源) PUT (更新资源,客户端提供改变后的完整资源) DELETE (删除资源)
请求方式
含义
GET /zoos 列出所有动物园
POST /zoos 新建一个动物园
GET /zoos/ID 获取某个指定动物园的信息
PUT /zoos/ID 更新某个指定动物园的信息(提供该动物园的全部信息)
DELETE /zoos 删除某个动物园
GET /zoos/lD/animals 列出某个指定动物园的所有动物
DELETE /zoos/lD/animals/ID 删除某个指定动物园的指定动物
六、springboot与mybtais及mybatis-plus整合
MyBatisPlus的入门案例与简介,这个和其他课程都不太一样,其他的课程都是先介绍概念,然后再写入门案例。而对于MyBatisPlus的学习,我们将顺序做了+调整,主要的原因MyBatisPlus主要是对 MyBatis的简化,所有我们先体会下它简化在哪,然后再学习它是什么,以及它帮我们都做哪些事。
MybatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提供效率。
开发方式
基于SpringBoot使用MyBatisPlus
步骤1:创建数据库及表
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (
id bigint(20) primary key auto_increment,
name varchar(32) not null,
password varchar(32) not null,
age int(3) not null ,
tel varchar(32) not null
);
insert into user values(1,'Tom','tom',18,'13991267980');
insert into user values(2,'Jerry','jerry',18,'13991267980');
insert into user values(3,'Jock','Jock',18,'13991267980');
步骤2:创建SpringBoot工程
步骤3:勾选配置使用技术
说明: 由于MP并未被收录到idea的系统内置配置,无法直接选择加入,需要手动在pom.xml中配置添加
步骤4:pom.xml补全依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
步骤5:添加MP的相关配置信息
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=GMT
username: root
password: 132456
步骤6:根据数据库表创建实体类
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
//setter...getter...toString方法略
}
步骤7:创建Dao接口
@Mapper
public interface UserDao extends BaseMapper<User>{
}
步骤8:编写引导类
@SpringBootApplication
public class Mybatisplus01QuickstartApplication {
public static void main(String[] args) {
SpringApplication.run(Mybatisplus01QuickstartApplication.class, args);
}
}
说明:Dao接口要想被容器扫描到,有两种解决方案:
方案一:在Dao接口上添加@Mapper注解,并且确保Dao处在引导类所在包或其子包中
该方案的缺点是需要在每一Dao接口中添加注解
方案二:在引导类上添加@MapperScan注解,其属性为所要扫描的Dao所在包
该方案的好处是只需要写一次,则指定包下的所有Dao接口都能被扫描到,@Mapper就可以
不写。
步骤9:编写测试类
@SpringBootTest
class MpDemoApplicationTests {
@Autowired
private UserDao userDao;
@Test
public void testGetAll() {
List<User> userList = userDao.selectList(null);
System.out.println(userList);
}
}
分页功能
分页查询使用的方法是:
IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper)
IPage:用来构建分页查询条件
Wrapper:用来构建条件查询的条件,目前我们没有可直接传为Null
IPage:返回值,你会发现构建分页条件和方法的返回值都是IPage
IPage是一个接口,我们需要找到它的实现类来构建它,具体的实现类,可以进入到IPage类中按ctrl+h, 会找到其有一个实现类为Page。
步骤1:调用方法传入参数获取返回值
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
@Autowired
private UserDao userDao;
//分页查询
@Test
void testSelectPage(){
//1 创建IPage分页对象,设置分页参数,1为当前页码,3为每页显示的记录数
步骤2:设置分页拦截器
这个拦截器MP已经为我们提供好了,我们只需要将其配置成Spring管理的bean对象即可。
步骤3:运行测试程序
如果想查看MP执行的SQL语句,可以修改application.yml配置文件,
三.DQL编程控制
增删改查四个操作中,查询是非常重要的也是非常复杂的操作,这块需要我们重点学习下,这节我们主
要学习的内容有:
条件查询方式
查询投影
查询条件设定
字段映射与表名映射
IPage<User> page=new Page<>(1,3);
//2 执行分页查询
userDao.selectPage(page,null);
//3 获取分页结果
System.out.println("当前页码值:"+page.getCurrent());
System.out.println("每页显示数:"+page.getSize());
System.out.println("一共多少页:"+page.getPages());
System.out.println("一共多少条数据:"+page.getTotal());
System.out.println("数据:"+page.getRecords());
}
}
步骤2:设置分页拦截器
这个拦截器MP已经为我们提供好了,我们只需要将其配置成Spring管理的bean对象即可。
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1 创建MybatisPlusInterceptor拦截器对象
MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
//2 添加分页拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
步骤3:运行测试程序
如果想查看MP执行的SQL语句,可以修改application.yml配置文件,
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印SQL日志到控制台
DQL编程控制
增删改查四个操作中,查询是非常重要的也是非常复杂的操作,这块需要我们重点学习下,这节我们主要学习的内容有:
条件查询方式
查询投影
查询条件设定
字段映射与表名映射
条件查询
1.构建条件查询
在进行查询的时候,我们的入口是在Wrapper这个类上,因为它是一个接口,所以我们需要去找它对应的实现类,关于实现类也有很多,说明我们有多种构建查询条件对象的方式,
先来看第一种:QueryWrapper
/**
* QueryWrapper的lt小于()方法
* 查询年龄小于19的
*/
@Test
void testGetAll(){
QueryWrapper qw=new QueryWrapper();
qw.lt("age",19);
List<User> userlist = userMapper.selectList(qw);
System.out.println(userlist);
}
lt: 小于(<) ,最终的sql语句为 select id,name,password,age,tel FROM user WHERE (age < ?)
接着来看第二种:LambdaQueryWrapper
/**
* LambdaQueryWrapper的lt小于()方法
* 查询年龄小于19的
*/
@Test
void testGetAll2(){
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.lt(User::getAge,19);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
多条件构建
需求:查询数据库表中,年龄在10岁到30岁之间的用户信息
/**
* ,年龄在10岁到30岁之间的用户信息
* gt:大于(>)
*/
@Test
void testGetAll3(){
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.lt(User::getAge,30);
lqw.gt(User::getAge,10);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
gt:大于(>),最终的SQL语句为:SELECT id,name,password,age,tel FROM user WHERE (age < ? AND age > ?)
构建多条件的时候,可以支持链式编程
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
查询数据库表中,年龄小于10或年龄大于30的数据
/**
* 年龄小于10或年龄大于30的
* or()就相当于我们sql语句中的or关键字,不加默认是and,
* 、构建多条件的时候,可以支持链式编程
*/
@Test
void testGetAll4(){
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.lt(User::getAge,10).or().gt(User::getAge,30);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
or()就相当于我们sql语句中的or关键字,不加默认是and,最终的sql语句为:
SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)
null判定
需求:查询数据库表中,根据输入年龄范围来查询符合条件的记录
用户在输入值的时候,
如果只输入第一个框,说明要查询大于该年龄的用户
如果只输入第二个框,说明要查询小于该年龄的用户
如果两个框都输入了,说明要查询年龄在两个范围之间的用户
我们可以使用两个简单数据类型,也可以使用一个模型类,但是User类中目前只有一个age属性
新建一个模型类,让其继承User类,并在其中添加age2属性,UserQuery在拥有User属性后同时添加了age2属性。
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
@Data
public class UserQuery extends User {
private Integer age2;
}
@Test
void testGetAll(){
//模拟页面传递过来的查询数据
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
if(null != uq.getAge2()){
lqw.lt(User::getAge, uq.getAge2());
}
if( null != uq.getAge()) {
lqw.gt(User::getAge, uq.getAge());
}
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
上面的写法可以完成条件为非空的判断,但是问题很明显,如果条件多的话,每个条件都需要判断,代码量就比较大,来看MP给我们提供的简化方式:
@Test
void testGetAll(){
//模拟页面传递过来的查询数据
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(null!=uq.getAge2(),User::getAge, uq.getAge2());
lqw.gt(null!=uq.getAge(),User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
查询投影
目前我们在查询数据的时候,什么都没有做默认就是查询表中所有字段的内容,我们所说的查询投影即不查询所有字段,只查询出指定内容的数据。
@Test
void testGetAll7(){
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.select(User::getId,User::getName,User::getAge);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
// 如果使用的不是lambda,就需要手动指定字段
@Test
void testGetAll8(){
QueryWrapper<User> lqw=new QueryWrapper<User>();
lqw.select("id","name","age","tel");
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
聚合查询
需求:聚合函数查询,完成count、max、min、avg、sum的使用
count:总记录数
max:最大值
min:最小值
avg:平均值
sum:求和
//聚合查询
@Test
void testGetAll9(){
QueryWrapper<User> lqw=new QueryWrapper<User>();
// lqw.select("count(*) as count");
// lqw.select("max(age) as maxAge");
// lqw.select("min(age) as minAge");
// lqw.select("sum(age) as sumAge");
lqw.select("avg(age) as avgAge");
List<Map<String, Object>> maps = userMapper.selectMaps(lqw);
System.out.println(maps);
}
分组查询
需求:分组查询,完成 group by的查询使用
//分组查询
@Test
void testGetAll10(){
QueryWrapper<User> lqw=new QueryWrapper<User>();
lqw.select("count(*) as count,tel");
lqw.groupBy("tel");
List<Map<String, Object>> maps = userMapper.selectMaps(lqw);
System.out.println(maps);
}
注意:聚合与分组查询,无法使用lambda表达式来完成
查询条件
前面我们只使用了lt()和gt(),除了这两个方法外,MP还封装了很多条件对应的方法,这一节我们重点把
MP提供的查询条件方法进行学习下。
MP的查询条件有很多:
范围匹配(> 、 = 、between)
模糊匹配(like)
空判定(null)
包含性匹配(in)
分组(group)
排序(order)
......
1.等值查询
需求:根据用户名和密码查询用户信息
@Test
void testGetAll11(){
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");
User user = userMapper.selectOne(lqw);
System.out.println(user);
}
eq(): 相当于 =,对应的sql语句为:SELECT id,name,password,age,tel FROM user WHERE (name = ? AND password = ?)
selectList:查询结果为多个或者单个
selectOne:查询结果为单个
2.范围查询
需求:对年龄进行范围查询,使用lt()、le()、gt()、ge()、between()进行范围查询
//范围查询
@Test
void testGetAll12(){
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.between(User::getAge,10,30);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
gt():大于(>)
ge():大于等于(>=)
lt():小于(<)
lte():小于等于(<=)
between():between ? and ?
3.查询表中name属性的值以J开头的用户信息,使用like进行模糊查询
//模糊查询
@Test
void testGetAll14(){
LambdaQueryWrapper<User> lqw=new LambdaQueryWrapper<User>();
lqw.like(User::getName,"J");
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
like():前后加百分号,如 %J%
likeLeft():前面加百分号,如 %J
likeRight():后面加百分号,如 J%
4.排序查询::查询所有数据,然后按照id降序
//排序查询
// 需求:查询所有数据,然后按照id降序
@Test
void testGetAll15(){
LambdaQueryWrapper<User> lwq=new LambdaQueryWrapper<>();
lwq.orderBy(true,false,User::getId);
userMapper.selectList(lwq);
}
映射匹配兼容性
前面我们已经能从表中查询出数据,并将数据封装到模型类中,这整个过程涉及到一张表和一个模型类:
那么问题就来了:
问题1:表字段与编码属性设计不同步
当表的列名和模型类的属性名发生不一致,就会导致数据封装不到模型对象,这个时候就需要其中一方做出修改,那如果前提是两边都不能改又该如何解决?
MP给我们提供了一个注解@TableField,使用该注解可以实现模型类属性名和表的列名之间的映射关系
问题2:编码中添加了数据库中未定义的属性
当模型类中多了一个数据库表不存在的字段,就会导致生成的sql语句中在select的时候查询了数据库不
存在的字段,程序运行就会报错,错误信息为:
Unknown column '多出来的字段名称' in 'field list'
具体的解决方案用到的还是@TableField注解,它有一个属性叫exist,设置该字段是否在数据库表
中存在,如果设置为false则不存在,生成sql语句查询的时候,就不会再查询该字段了。
采用默认查询开放了更多的字段查看权限

表名与编码开发设计不同步


DML编程控制

2.ID生成策略对比
简化配置
只需要在配置文件中添加如下内容:
mybatis-plus:
global-config:
db-config:
id-type: assign_id
配置起来还是比较繁琐,简化方式为在配置文件中配置如下内容:
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
多记录操作
@Test
void testDelete(){
//删除多条指定数据
List<Long> list=new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402551344241838054L);
list.add(1402851342487778081L);
userMapper.deleteBatchIds(list);
}
//根据传入的ID集合查询用户信息
@Test
void testGetByIds(){
//查询指定多条数据
List<Long> list=new ArrayList<>();
list.add(1L);
list.add(2L);
list.add(3L);
userMapper.selectBatchIds(list);
}

实体类添加属性
(1)添加与数据库表的列对应的一个属性名,名称可以任意,如果和数据表列名对不上,可以使用 @TableField进行关系映射,如果一致,则会自动对应。
(2)标识新增的字段为逻辑删除字段,使用 @TableLogic
运行删除方法
@Test
void testDelete2(){
userMapper.deleteById(1L);
}

思考:逻辑删除,对查询有没有影响呢
执行查询操作
@Test
void testFind(){
System.out.println(userMapper.selectList(null));
}


乐观锁
咱们可以把乐观锁的这个过程想象成两个人借同一本书的场景,这样就好理解了:
你可以把数据库里的那条数据当成一本畅销书,而version
列就像是这本书的 “借阅次数标签”,一开始这标签上写着 “1”(默认值 1)。
现在有甲和乙两个人都想借这本书,并且借完后要在标签上把次数加 1。
- 甲先来借书,他看到标签上是 “1”,心里记下了这个数字,打算借完后改成 “2”。
- 乙紧接着也来了,他看到的标签也是 “1”,同样打算借完后改成 “2”。
这时候就有两种情况:
甲先借到了书,他按照自己的想法,把标签从 “1” 改成了 “2”,然后把书还回去了。
等乙再来借书时,他拿出自己记下的 “1” 去核对标签,发现标签已经是 “2” 了,跟自己记的不一样,就知道这本书被别人动过了,自己借不了了,只能放弃。要是乙先借到了书,他把标签从 “1” 改成 “2” 还回去。
甲再来时,同样发现标签变成了 “2”,和自己记的 “1” 对不上,也借不了,只能放弃。所以不管是甲先还是乙先,最终只有一个人能成功借书并改标签,另一个人会因为标签对不上而失败。这就是乐观锁的意思:它乐观地认为大家不会抢着修改数据,但会通过 “版本号(version)” 来核对,一旦发现版本对不上,就说明数据被别人改过了,自己的修改就失败,避免了冲突。


步骤3:添加乐观锁的拦截器
package com.apesource.spring_mybatis_plus_01.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1.定义Mp拦截器
MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
//2.添加乐观锁拦截器
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
步骤4:执行更新操作
/**
* 测试乐观锁
*/
@Test
void testUpdate(){
User user=new User();
user.setId(4L);
user.setName("王升1");
userMapper.updateById(user);
}
@Test
void testUpdate2(){
User user=new User();
user.setId(4L);
user.setName("王升2");
user.setVersion(1);
userMapper.updateById(user);
}
@Test
void testUpdate3(){
//1.先通过要修改的数据id将当前数据查询出来
User user=userMapper.selectById(4L);
//2.将要修改的属性逐一设置进去
user.setName("王升3");
userMapper.updateById(user);
}
@Test
void testUpdate4(){
User user=userMapper.selectById(3L);
User user2=userMapper.selectById(3L);
user2.setName("Jock aaa");
userMapper.updateById(user2);
user.setName("Jock bbb");
userMapper.updateById(user);
}