SpringBoot收尾+myBatis plus

一、數據傳遞

返回值為:字符串
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")@ResponseBodypublic 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")@ResponseBodypublic 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 {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("進入servlet");resp.getWriter().println("<h1>hello world</h1>");};@Overrideprotected 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")@Beanpublic 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.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=GMTusername: rootpassword: 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 {@Autowiredprivate UserDao userDao;@Testpublic 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 {@Autowiredprivate UserDao userDao;//分頁查詢@Testvoid 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 {@Beanpublic 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的*/@Testvoid 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的*/@Testvoid 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:大于(>)*/@Testvoid 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,*     、構建多條件的時候,可以支持鏈式編程*/@Testvoid 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;
}
@Testvoid 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給我們提供的簡化方式:

 ?@Testvoid 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);}

查詢投影

目前我們在查詢數據的時候,什么都沒有做默認就是查詢表中所有字段的內容,我們所說的查詢投影即不查詢所有字段,只查詢出指定內容的數據。

@Testvoid 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,就需要手動指定字段@Testvoid 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:求和

//聚合查詢@Testvoid 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的查詢使用

 //分組查詢@Testvoid 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.等值查詢

需求:根據用戶名和密碼查詢用戶信息

  @Testvoid 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()進行范圍查詢

 //范圍查詢@Testvoid 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進行模糊查詢

//模糊查詢@Testvoid 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降序@Testvoid 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語句查詢的時候,就不會再查詢該字段了。

?采用默認查詢開放了更多的字段查看權限

查詢表中所有的列的數據,就可能把一些敏感數據查詢到返回給前端,這個時候我們就需要限制哪些字 段默認不要進行查詢。解決方案是 @TableField 注解的一個屬性叫 select ,該屬性設置默認是否需要 查詢該字段的值,true(默認值)表示默認查詢該字段,false表示默認不查詢該字段。

表名與編碼開發設計不同步?

該問題主要是表的名稱和模型類的名稱不一致,導致查詢失敗,這個時候通常會報如下錯誤信息:
Table 'databaseName.tableNaem' doesn't exist,翻譯過來就是數據庫中的表不存在。
DML編程控制
1.id生成策略控制

2.ID生成策略對比?

NONE: 不設置id生成策略,MP不自動生成,約等于INPUT,所以這兩種方式都需要用戶手動設置,
但是手動設置第一個問題是容易出現相同的ID造成主鍵沖突,為了保證主鍵不沖突就需要做很多
判定,實現起來比較復雜
AUTO:數據庫ID自增,這種策略適合在數據庫服務器只有1臺的情況下使用,不可作為分布式ID使用
ASSIGN_UUID:可以在分布式的情況下使用,而且能夠保證唯一,但是生成的主鍵是32位的字符
串,長度過長占用空間而且還不能排序,查詢性能也慢
ASSIGN_ID:可以在分布式的情況下使用,生成的是Long類型的數字,可以排序性能也高,但是生
成的策略和服務器時間有關,如果修改了系統時間就有可能導致出現重復主鍵(雪花算法)

?簡化配置

只需要在配置文件中添加如下內容:?

mybatis-plus:global-config:db-config:id-type: assign_id

配置起來還是比較繁瑣,簡化方式為在配置文件中配置如下內容:?

mybatis-plus:global-config:db-config:table-prefix: tbl_
設置表的前綴內容,這樣MP就會拿 tbl_ 加上模型類的首字母小寫,就剛好組裝成數據庫的表名。

多記錄操作?

根據ID 批量刪除,參數是一個集合,可以存放多個id值。
需求:根據傳入的id集合將數據庫表中的數據刪除掉。
 @Testvoid testDelete(){//刪除多條指定數據List<Long> list=new ArrayList<>();list.add(1402551342481838081L);list.add(1402551344241838054L);list.add(1402851342487778081L);userMapper.deleteBatchIds(list);}
需求:根據傳入的ID集合查詢用戶信息
 //根據傳入的ID集合查詢用戶信息@Testvoid testGetByIds(){//查詢指定多條數據List<Long> list=new ArrayList<>();list.add(1L);list.add(2L);list.add(3L);userMapper.selectBatchIds(list);}
邏輯刪除
修改數據庫表添加 deleted
字段名可以任意,內容也可以自定義,比如 0 代表正常, 1 代表刪除,可以在添加列的同時設置其默認 值為 0 正常。

?實體類添加屬性

(1)添加與數據庫表的列對應的一個屬性名,名稱可以任意,如果和數據表列名對不上,可以使用 @TableField進行關系映射,如果一致,則會自動對應。

(2)標識新增的字段為邏輯刪除字段,使用 @TableLogic

?運行刪除方法

   @Testvoid testDelete2(){userMapper.deleteById(1L);}
從測試結果來看,邏輯刪除最后走的是update操作,會將指定的字段修改成刪除狀態對應的值。

思考:邏輯刪除,對查詢有沒有影響呢?

執行查詢操作

 @Testvoid testFind(){System.out.println(userMapper.selectList(null));}
運行測試,會發現打印出來的sql語句中會多一個查詢條件
可想而知,MP的邏輯刪除會將所有的查詢都添加一個未被刪除的條件,也就是已經被刪除的數據
是不應該被查詢出來的。
如果還是想把已經刪除的數據都查詢出來該如何實現呢?
如果每個表都要有邏輯刪除,那么就需要在每個模型類的屬性上添加 @TableLogic 注解,如何優
?
在配置文件中添加全局配置,如下:

?

介紹完邏輯刪除,邏輯刪除的本質為:
邏輯刪除的本質其實是修改操作。如果加了邏輯刪除字段,查詢數據時也會自動帶上邏輯刪除字段。

樂觀鎖?

樂觀鎖的實現方式:
數據庫表中添加version列,比如默認值給1
第一個線程要修改數據之前,取出記錄時,獲取當前數據庫中的version=1
第二個線程要修改數據之前,取出記錄時,獲取當前數據庫中的version=1
第一個線程執行更新時,set version = newVersion where version = oldVersion
newVersion = version+1 [2]
oldVersion = version [1]
第二個線程執行更新時,set version = newVersion where version = oldVersion
newVersion = version+1 [2]
oldVersion = version [1]
假如這兩個線程都來更新數據,第一個和第二個線程都可能先執行
假如第一個線程先執行更新,會把version改為2
第二個線程再更新的時候,set version = 2 where version = 1,此時數據庫表的數據
version已經為2,所以第二個線程會修改失敗
假如第二個線程先執行更新,會把version改為2
第一個線程再更新的時候,set version = 2 where version = 1,此時數據庫表的數據
version已經為2,所以第一個線程會修改失敗
不管誰先執行都會確保只能有一個線程更新數據,這就是MP提供的樂觀鎖的實現原理
分析。
通俗來講:

咱們可以把樂觀鎖的這個過程想象成兩個人借同一本書的場景,這樣就好理解了:

你可以把數據庫里的那條數據當成一本暢銷書,而version列就像是這本書的 “借閱次數標簽”,一開始這標簽上寫著 “1”(默認值 1)。

現在有甲和乙兩個人都想借這本書,并且借完后要在標簽上把次數加 1。

  • 甲先來借書,他看到標簽上是 “1”,心里記下了這個數字,打算借完后改成 “2”。
  • 乙緊接著也來了,他看到的標簽也是 “1”,同樣打算借完后改成 “2”。

這時候就有兩種情況:

  1. 甲先借到了書,他按照自己的想法,把標簽從 “1” 改成了 “2”,然后把書還回去了。
    等乙再來借書時,他拿出自己記下的 “1” 去核對標簽,發現標簽已經是 “2” 了,跟自己記的不一樣,就知道這本書被別人動過了,自己借不了了,只能放棄。

  2. 要是乙先借到了書,他把標簽從 “1” 改成 “2” 還回去。
    甲再來時,同樣發現標簽變成了 “2”,和自己記的 “1” 對不上,也借不了,只能放棄。所以不管是甲先還是乙先,最終只有一個人能成功借書并改標簽,另一個人會因為標簽對不上而失敗。這就是樂觀鎖的意思:它樂觀地認為大家不會搶著修改數據,但會通過 “版本號(version)” 來核對,一旦發現版本對不上,就說明數據被別人改過了,自己的修改就失敗,避免了沖突。

實現步驟
步驟1:數據庫表添加列
列名可以任意,比如使用 version ,給列設置默認值為 1
步驟2:在模型類中添加對應的屬性

步驟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 {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){//1.定義Mp攔截器MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();//2.添加樂觀鎖攔截器mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mybatisPlusInterceptor;}
}

?步驟4:執行更新操作

 /*** 測試樂觀鎖*/@Testvoid testUpdate(){User user=new User();user.setId(4L);user.setName("王升1");userMapper.updateById(user);}
你會發現,這次修改并沒有更新version字段,原因是沒有攜帶version數據。
添加version數據
  @Testvoid testUpdate2(){User user=new User();user.setId(4L);user.setName("王升2");user.setVersion(1);userMapper.updateById(user);}
你會發現,我們傳遞的是1MP會將1進行加1,然后,更新回到數據庫表中。
所以要想實現樂觀鎖,首先第一步應該是拿到表中的version,然后拿version當條件在將version1
新回到數據庫表中,所以我們在查詢的時候,需要對其進行查詢
  @Testvoid testUpdate3(){//1.先通過要修改的數據id將當前數據查詢出來User user=userMapper.selectById(4L);//2.將要修改的屬性逐一設置進去user.setName("王升3");userMapper.updateById(user);}
大概分析完樂觀鎖的實現步驟以后,我們來模擬一種加鎖的情況,看看能不能實現多個人修改同一個數
據的時候,只能有一個人修改成功。
@Testvoid 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);}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/91358.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/91358.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/91358.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于 Spring Boot 實現動態路由加載:從數據庫到前端菜單的完整方案

在后臺管理系統中&#xff0c;不同用戶角色往往擁有不同的操作權限&#xff0c;對應的菜單展示也需動態調整。動態路由加載正是解決這一問題的核心方案 —— 根據登錄用戶的權限&#xff0c;從數據庫查詢其可訪問的菜單&#xff0c;封裝成前端所需的路由結構并返回。本文將詳細…

VitePress學習-自定義主題

VitePress-自定義主題 代碼倉庫 基礎了解 初始化項目的時候選擇 custom theme 運行后會發現頁面挺丑的。 如果想要用默認主題怎么辦呢&#xff0c;修改Layout。 使用默認主題的Layout <script setup lang"ts"> import { useData } from vitepress; impo…

【GEO從入門到精通】生成式引擎與其他 AI 技術的關系

2.1.3 生成式引擎與其他 AI 技術的關系生成式引擎作為人工智能領域的創新力量&#xff0c;與其他 AI 技術緊密相連&#xff0c;相互促進&#xff0c;共同推動 生成式引擎優化&#xff08;GEO&#xff09; 的發展。這些技術使生成式引擎能夠為消費者提供更加個性化和精準的內容。…

JAVAEE--4.多線程案例

設計模式1.單例模式1.1餓漢模式1.2懶漢模式(單線程版)1.3懶漢模式(多線程版本)1.4懶漢模式(多線程版本進階版)2.阻塞隊列3.定時器4.線程池1.單例模式設計模式是"軟性約束",不是強制的,可以遵守也可以不遵守,按照設計模式寫代碼使代碼不會太差框架是"硬性約束&qu…

量化感知訓練(QAT)流程

WHAT&#xff1a;量化感知訓練&#xff08;Quantization-Aware Training, QAT&#xff09; 是一種在模型訓練階段引入量化誤差的技術。它的核心思想是&#xff1a;通過在前向傳播時插入“偽量化節點”引入量化誤差&#xff0c;將權重和激活模擬為低精度&#xff08;如 int8&…

docker 用于將鏡像打包為 tar 文件

docker save 是 Docker 中用于將鏡像打包為 tar 文件的命令&#xff0c;常用于鏡像的備份、遷移或離線傳輸。以下是其核心用法和注意事項&#xff1a;一、基本語法bashdocker save [選項] IMAGE [IMAGE...] > 文件名.tar # 或 docker save -o 文件名.tar IMAGE [IMAGE...]IM…

設計模式(六)創建型:單例模式詳解

設計模式&#xff08;六&#xff09;創建型&#xff1a;單例模式詳解單例模式&#xff08;Singleton Pattern&#xff09;是 GoF 23 種設計模式中最簡單卻最常被誤用的創建型模式。其核心價值在于確保一個類在整個應用程序生命周期中僅存在一個實例&#xff0c;并提供一個全局訪…

PostgreSQL AND OR 操作符詳解

PostgreSQL AND & OR 操作符詳解 在數據庫查詢中,AND 和 OR 是兩種常見的邏輯操作符,用于組合多個查詢條件。PostgreSQL 作為一款功能強大的開源關系型數據庫管理系統,同樣支持這些操作符。本文將詳細介紹 PostgreSQL 中的 AND 和 OR 操作符,并探討它們在查詢中的應用…

RabbiteMQ安裝-ubuntu

Ubuntu 1.安裝Erlang RabbitMQ需要Erlang語言的支持&#xff0c;在安裝RabbitMQ之前需要安裝Erlang #更新軟件包 sudo apt-get update#安裝erlang sudo apt-get install erlang查看erlang版本 roothcss-ecs-027f:/# erl Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [sm…

Linux驅動20 --- FFMPEG視頻API

目錄 一、FFMPEG 視頻 API 的使用 1.1 介紹 1.2 整體編程過程 獲取核心上下文指針 打開輸入流文件 獲取輸入流 獲取編碼器 初始化解碼器 申請輸出流指針 獲取顯示數據空間大小 申請輸出顯示空間 綁定輸出流和輸出顯示空間 申請格式轉換上下文 申請輸入流指針 讀取一幀數據 發…

OpenBayes 一周速覽丨Self Forcing 實現亞秒級延遲實時流視頻生成;邊緣AI新秀,LFM2-1.2B采用創新性架構超越傳統模型

公共資源速遞 This Weekly Snapshots &#xff01; 5 個公共數據集&#xff1a; * AF-Chat 音頻對話文本數據集 * ArtVIP 機器交互式圖像數據集 * Updesh 印度語合成文本數據集 * Medical Information 藥品信息數據集 * Nemotron-Math-HumanReasoning 數學推理數據集…

[NOIP2002 提高組] 均分紙牌

題目描述有N堆紙牌&#xff0c;編號分別為 1,2,…,N。每堆上有若干張&#xff0c;但紙牌總數必為N的倍數。可以在任一堆上取若干張紙牌&#xff0c;然后移動。移牌規則為&#xff1a;在編號為1堆上取的紙牌&#xff0c;只能移到編號為2的堆上&#xff1b;在編號為N的堆上取的紙…

【音視頻】WebRTC-Web 音視頻采集與播放

一、打開攝像頭 打開攝像頭首先需要有一個html的video標簽&#xff1a; id "local-video"&#xff0c;是為了后續的js腳本調用這個對象autoplay是設置打開后自動播放&#xff0c;playsinline則是為了兼容移動端 <video id "local-video" autoplay p…

數據治理平臺如何選?深度解析國產化全棧方案與行業落地實踐

“數據治理平臺廠商有哪些&#xff1f;”國內主流廠商包括阿里云、華為、百分點科技等&#xff0c;各有所長。其中&#xff0c;百分點科技憑借在應急管理、智慧公安及央國企數字化領域的深度實踐&#xff0c;打造了行業特色鮮明的數據治理解決方案。百分點科技的數據治理解決方…

限流算法詳解:固定窗口、滑動窗口、令牌桶與漏桶算法全面對比

限流&#xff08;Rate Limiting&#xff09;是保障系統穩定性和服務質量的關鍵機制&#xff0c;尤其在高并發、突發流量、攻擊防護等場景中至關重要。本文將詳細介紹四種主流限流算法&#xff1a;固定窗口&#xff08;Fixed Window&#xff09;滑動窗口&#xff08;Sliding Win…

Sentinel 搭建應用層面與網關層面的流控保護

源碼&#xff1a;妖精的尾巴/spring-cloud-alibaba Nacos 和 Sentinel Dashboard 我這里全是使用window 本地運行的&#xff0c;需要自行下載運行 服務層面&#xff1a; 當你在某個具體的服務上使用Sentinel時&#xff0c;更多的是關注該服務內部資源的保護。例如&#xff0c…

純血鴻蒙 AudioRenderer+AudioCapturer+RingBuffer 實現麥克風采集+發聲

總共兩個類&#xff0c;放到代碼里&#xff0c;就可以快速完成K歌的效果&#xff0c;但應用層這么做延遲是比較高的&#xff0c;只是做一個分享。 類代碼 import { audio } from kit.AudioKit; import { BusinessError } from kit.BasicServicesKit; import { AudioBufferFlow,…

洛谷 P1601 A+B Problem(高精)普及-

題目描述 高精度加法&#xff0c;相當于 ab problem&#xff0c;不用考慮負數。 輸入格式 分兩行輸入。a,b≤10500a,b \leq 10^{500}a,b≤10500。 輸出格式 輸出只有一行&#xff0c;代表 ababab 的值。 輸入輸出樣例 #1 輸入 #1 1 1輸出 #1 2輸入輸出樣例 #2 輸入 #2 1001 909…

Matrix Theory study notes[6]

文章目錄linear spacereferenceslinear space a basis of linear space VkV^kVk,which is x1,x2,...xkx_1,x_2,...x_kx1?,x2?,...xk?,can be called as a coordinate system.let vector v∈Vkv \in V^kv∈Vk and it can be linear expressed on this basis as va1x1a2x2...…

專線與專線之間的區別

下面我們從定義、技術特點、適用場景、優缺點等多個維度來詳細對比&#xff1a;? 一、四種方案簡要定義技術方案定義MPLS 專線運營商基于 MPLS 技術提供的私有虛擬網絡&#xff0c;邏輯隔離、安全可靠VPN over Internet利用公網加密通道&#xff08;如IPSec&#xff09;構建虛…