一篇文章帶你掌握MyBatis簡化框架——MyBatisPlus 我們在前面的文章中已經學習了目前開發所需的主流框架 類似於我們所學習的SpringBoot框架用於簡化Spring開發,我們的國人大大也開發了一款MyBatisPlus框架用來簡化MyBatis開發 下麵讓我們來逐步掌握MyBatis ...
一篇文章帶你掌握MyBatis簡化框架——MyBatisPlus
我們在前面的文章中已經學習了目前開發所需的主流框架
類似於我們所學習的SpringBoot框架用於簡化Spring開發,我們的國人大大也開發了一款MyBatisPlus框架用來簡化MyBatis開發
下麵讓我們來逐步掌握MyBatisPlus的內容吧~
MyBatisPlus簡介
首先我們來簡單介紹一下MyBatisPlus:
- MyBatisPlus(MP)是基於MyBatis框架基礎上開發的增強型工具,旨在簡化開發,提高效率
MyBatisPlus開發具有三種開發方式:
- 基於MyBatis使用MyBatisPlus
- 基於Spring使用MyBatisPlus
- 基於SpringBoot使用MyBatisPlus
MyBatisPlus入門案例
我們以基於SpringBoot使用MyBatisPlus為案例來展示MyBatisPlus開發的便捷性
SpringBoot使用MyBatis
首先我們回憶一下SpringBoot使用MyBatis開發的相關步驟:
- 創建SpringBoot工程
- 勾選配置使用技術
- 設置DataSource相關屬性(JDBC參數)
- 定義數據層介面映射配置
其中最為繁瑣的就是數據層介面的配置,需要書寫大量的@註解來進行資料庫的查詢
package com.itheima.dao;
import com.itheima.domain.Book;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface BookDao {
@Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})")
public int save(Book book);
@Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
public int update(Book book);
@Delete("delete from tbl_book where id = #{id}")
public int delete(Integer id);
@Select("select * from tbl_book where id = #{id}")
public Book getById(Integer id);
@Select("select * from tbl_book")
public List<Book> getAll();
}
SpringBoot使用MyBatisPlus
我們的SpringBoot使用MyBatisPlus大量簡化了數據層代碼書寫
我們下麵依次介紹整體步驟:
- 創建項目(SpringBoot項目,勾選相應技術棧)
- 導入相關依賴坐標
<?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.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>mybatisplus_01_quickstart</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--由於SpringBoot中未整合MyBatisPlus,所以我們需要手動添加MyBatisPlus依賴坐標-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--德魯伊坐標-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置環境yaml
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: root
- 書寫資料庫實體類
package com.itheima.domain;
import lombok.*;
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
@java.lang.Override
public java.lang.String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", age=" + age +
", tel='" + tel + '\'' +
'}';
}
}
- 數據層書寫
package com.itheima.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Mapper;
// @Mapper為了使掃描到該數據層包(SpringBoot中提及)
@Mapper
// 註意:MyBatisPlus不需要書寫方法,我們直接繼承BaseMapper類,並表明我們所使用的實體類即可
public interface UserDao extends BaseMapper<User> {
// 不需要書寫方法,在BaseMapper類中為我們配置了大量的資料庫方法來查詢,新增,修改,刪除
}
- 測試
package com.itheima;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
@Autowired
private UserDao userDao;
// 我們只需要得到UserDao類,並調用其方法即可(MyBatisPlus提供方法)
// 在這裡我們僅調用selectById方法,當作測試
@Test
void testGetById(){
User user = userDao.selectById(2L);
System.out.println(user);
}
}
至此,我們的第一個MyBatisPlus案例就結束了
MyBatisPlus概述
在使用過MyBatisPlus後,我們就可以重新介紹一下MyBatisPlus了:
- MyBatisPlus(MP)是基於MyBatis框架基礎上開發的增強型工具,旨在簡化開發,提高效率
- 官網:MyBatis-Plus (baomidou.com)
MyBatisPlus特征:
- 無侵入:製作增強,不做改變,不會對現有工程產生影響
- 強大的CRUD操作:內置通用的Mapper,少量配置即可實現單表CRUD操作
- 支持Lambda:編寫查詢條件無需擔心欄位錯誤
- 支持逐漸自動生成
- 內置分頁插件
最後提及一句:MyBatisPlus是由國人開發,官網也是以中文書寫,具有中國風範~
標準數據層開發
我們在前面已經大致掌握了MyBatisPlus的具體操作流程,下麵讓我們更加細膩的分析數據層開發
Lombok依賴坐標
在正式開始講解數據層開發前,為大家提供一個簡單可靠的依賴:
- Lombok依賴
那麼這個坐標具有什麼作用呢
- Lombok依賴可以用來簡化實體類的開發
- Lombok,屬於Java類庫,提供了一組註解,簡化POJO實體類開發
我們採用一個簡單的案例來展示:
- 首先我們需要導入該坐標
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
- 我們的實體類中產生多個新註解
package com.itheima.domain;
import lombok.*;
//lombok
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
/*
lombok為我們提供了多個註解:
@Setter:提供所有set方法
@Getter:提供所有Get方法
@ToString:提供ToString重構方法
@NoArgsConstructor:無參構造
@AllArgsConstructor:有參構造
其中我們最常用的註解是:
@Data:包括了除構造函數外的所有方法(Set,Get,ToString,hashCode,equals)
*/
標準數據層開發(簡單版)
首先我們來列出一些我們通常開發中會使用的數據層語句:
功能 | MP介面 |
---|---|
新增 | int insert(T t) |
刪除 | int deleteById(Serializable id) |
修改 | int updateById(T t) |
根據id查詢 | T selectById(Serializable id) |
查詢全部 | List |
上述方法我們無需在數據層定義,直接測試即可:
package com.itheima;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
// 自動裝配數據層
@Autowired
private UserDao userDao;
// 註意:id的數據類型為LONG,數值後需要加L
// 註意:下述部分方法需要返回id,實體類,null
// 新增
@Test
void testSave(){
User user = new User();
user.setName("黑馬程式員");
user.setPassword("itheima");
user.setAge(12);
user.setTel("4006184000");
userDao.insert(user);
}
// 刪除
@Test
void testDelete(){
userDao.deleteById(1L);
}
// 更新(註意:根據id進行更新,更新與原數據不同的數值,null值不進行更新)
@Test
void testUpdate(){
User user = new User();
user.setId(1L);
user.setName("Tom888");
user.setPassword("tom888");
userDao.updateById(user);
}
// 根據id查詢
@Test
void testGetById(){
User user = userDao.selectById(2L);
System.out.println(user);
}
// 查詢全部
@Test
void testGetAll() {
List<User> userList = userDao.selectList(null);
System.out.println(userList);
}
}
標準數據層開發(分頁查詢)
我們將分頁查詢單獨列為一個小節進行講解:
功能 | MP介面 |
---|---|
分頁查詢 | IPage<T> selectPage(IPage<T> page) |
MyBatisPlus的分頁查詢需要一些前置條件,我們下麵一一講述:
- 添加攔截器,進行分頁操作:
// MyBatisPlus的分頁操作需要添加攔截器
// 我們在Java文件夾下創建Config文件夾,創建MPConfig的Java類作為配置類
package com.itheima.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 作為配置類,讓spring可以直接掃描
@Configuration
public class MpConfig {
// 需要設置為Bean
@Bean
public MybatisPlusInterceptor mpInterceptor(){
//1.定義Mp攔截器MybatisPlusInterceptor(相當於外層攔截器)
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
//2.添加具體的攔截器PaginationInnerInterceptor(相當於在大攔截器中添加小攔截器)
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
- 直接測試即可
package com.itheima;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetByPage(){
// selectPage需要兩個參數IPage和Wrapper,IPage具有Page的實現類,Wrapper我們會在後續講到,這裡暫設為null
//IPage對象封裝了分頁操作相關的數據,第一個參數為開始數,第二個數為本頁可展示數據數
IPage page = new Page(2,3);
userDao.selectPage(page,null);
// 下述為page本身攜帶的一些方法,用於查看相關數據
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());
}
}
- 查看日誌
# 如果我們想要查看實際開發的資料庫語句,我們可以選擇開啟日誌進行查看
# 查看日誌方法設置在yaml配置文件中
# 開啟mp的日誌(輸出到控制台)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
DQL編程式控制制
我們在前面已經講解了簡單的數據層開發,下麵我們將會進一步講解DQL的編程內容
清除版本圖
在正式開始前,我們講一些簡單輕鬆的知識點:
- 我們在每次開啟伺服器後都會出現SpringBoot和MyBatisPlus的版本圖,有時會影響我們代碼的閱讀
那麼我們該如何清除呢
- 設置logback.xml文件
<!--我們在sources中新創一個spring config 文件-->
<!--設置一個configuration雙標簽,不用填寫內容即可-->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>
- 在yaml配置文件中做清除工作
# 我們只需要將spring和MyBatisPlus的banner設置為false即可關閉版本圖
# 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: root
main:
banner-mode: off
# mp日誌
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false
基本條件查詢
在介紹條件查詢前,我們需要先來介紹Wrapper:
- Wrapper屬於一種介面參數
- Wrapper是我們的條件查詢中所攜帶的進行條件判斷的參數
- 介面方法的參數中,會出現各種 Wrapper,比如 queryWrapper、updateWrapper 等
接下來我們就來介紹三種基本條件查詢:
- 按條件查詢
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
//方式一:按條件查詢
// 我們的條件查詢一般採用QueryWrapper類型的類來創造Wrapper條件判斷參數
QueryWrapper qw = new QueryWrapper();
// 我們採用QueryWrapper的一些方法來進行給予判斷條件,後續我們會進行介紹
// 這裡採用lt小於方法,後面跟String類型字元串代表資料庫列名,在跟數值表示值
qw.lt("age",18);
// 我們採用userDao的selectList方法根據qw條件判斷機制來進行獲取數據
// 獲取了User數值的List,並列印即可
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
}
}
- lambda格式按條件查詢(方法版)
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
//方式二:lambda格式按條件查詢
QueryWrapper<User> qw = new QueryWrapper<User>();
// MyBatisPlus可支持Lambda表達式,我們使用lambda方法使後續操作均變為Lambda表達式形式
// 我們的String資料庫列名可以採用Lambda表達式形式書寫
qw.lambda().lt(User::getAge, 10);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
}
}
- lambda格式按條件查詢(繼承類版)
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
//方式三:lambda格式按條件查詢
// 這裡直接繼承LambdaQueryWrapper,後續操作可以直接採用Lambda表達式,不用攜帶方法lambda
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
除了上面三種基本條件查詢外,我們可以發現條件查詢是可以疊加使用的,主要分為兩種疊加方式:
- 正常疊加
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
// 10到30歲之間
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
// 依次分開書寫
lqw.lt(User::getAge, 30);
lqw.gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
- 鏈式疊加
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
// 10到30歲之間
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);
}
}
最後我們還要介紹兩種組合查詢條件形式:
- 並且(and)
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
//並且關係
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//並且關係:10到30歲之間(直接鏈式書寫即可)
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
- 或者(or)
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
//或者關係
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//或者關係:小於10歲或者大於30歲(在條件之間採用or方法來隔離)
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
空值處理
我們在做實際項目處理時,會出現各種查詢框架
例如價格查詢搜索產品,但我們可能不會同時給出最低價限制和最高價限制
我們以代碼來做一個簡單示例:
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
// 模擬頁面傳遞過來的查詢數據
UserQuery uq = new UserQuery();
// 如果我們下麵的數據有一個沒有設置,就會導致查詢語句中的?沒有填充,導致搜索失敗
uq.setAge(10);
uq.setAge2(null);
// null判定
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, uq.getAge2());
lqw.gt(User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList)
}
}
在之前我們的null值處理大部分都是採用if語句來進行判斷:
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
// 模擬頁面傳遞過來的查詢數據
UserQuery uq = new UserQuery();
// 可能出現有任意一個數沒有賦值
uq.setAge(10);
uq.setAge2(null);
// null判定
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, uq.getAge2());
// 正常我們採用if來進行判斷是否有值,若有值則加入操作
// 但當if語句過多,導致代碼冗雜
if( null != uq.getAge()) {
lqw.gt(User::getAge, uq.getAge());
}
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
MyBatisPlus給出了一種新的判定方法來決定是否載入該語句:
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
// 模擬頁面傳遞過來的查詢數據
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);
// LambdaQueryWrapper的各種方法中均攜帶了一個判定條件在最前面的參數中,當成立執行後續操作,不成立直接跳過
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
// 先判定第一個參數是否為true,如果為true連接當前條件
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);
}
}
查詢投影
我們選擇性的查詢資料庫列稱為查詢投影,接下來讓我們來介紹查詢投影的實現方法
查詢投影的實現我們大致分為兩種類型:
- 查詢結果包含模型類中部分屬性
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
// 查詢投影
// 當我們所查詢的內容屬於實體類中包含的屬性,我們可以採用QW或LambdaQW來實現
// 我們大部分採用LambdaQW來實現,因為帶有自動識別,不易出錯
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
// 我們採用select方法來添加查詢種類,LambdaQueryWrapper採用Lambda表達式添加
lqw.select(User::getId,User::getName,User::getAge);
QueryWrapper<User> lqw = new QueryWrapper<User>();
// 我們採用select方法來添加查詢種類,QueryWrapper採用String添加
lqw.select("id","name","age","tel");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
- 查詢結果包含模型類中未定義屬性
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
// 查詢投影
// 未定義的屬性我們只能採用QueryWrapper的String字元串來書寫,例如count(*)和分組條件
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count, tel");
// QueryWrapper提供了分組方法groupBy,參數為String類型的資料庫列名
lqw.groupBy("tel");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);
}
}
查詢條件展示
首先我們給出所有查詢條件官網鏈接:條件構造器 | MyBatis-Plus (baomidou.com)
如果有需要可以上網查詢相關構造方法
下麵我們僅對一些常用查詢條件進行展示:
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.domain.query.UserQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
class Mybatisplus02DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
//條件查詢
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
// eq等同於=
lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//範圍查詢 lt le gt ge eq between
lqw.between(User::getAge,10,30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//模糊匹配 like
lqw.likeLeft(User::getName,"J");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
// 查詢全部
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
}
}
映射處理
最後介紹的依舊是我們在MyBatis中也出現的老問題:
- 當我們的實現類和資料庫表出現不同點時我們該如何處理
我們在下麵分為幾種情況來講解:
- 表名與編碼開發設計不同步
// 假設我們的資料庫表名為tbl_user,但我們的實體類設計為User
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
// 採用註解@TableName,後面跟對應的資料庫表名
@TableName("tbl_user")
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
- 列名與編碼開發設計不同步
// 假設我們的資料庫密碼設計為pwd,但是我們的實體類密碼屬性設計為password
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
//lombok
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
// 採用@TableField註解,後面採用屬性value=資料庫列名
@TableField(value = "pwd")
private String password;
private Integer age;
private String tel;
}
- 該列不應當被列印時
// 例如我們的pwd密碼,在查詢時不應當被查詢,但是我們採用select * from tbl_user來查詢,如何屏蔽
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
//lombok
@Data
@TableName("tbl_user")
public class User {
private Long id;
private String name;
// 採用@TableField註解,後面採用屬性select設置為false,即為不可查詢
@TableField(value = "pwd",select = false)
private String password;
private Integer age;
private String tel;
}
- 當實體類中出現資料庫中不存在的列時
// 例如我們設計了一個屬性online判斷是否線上,不用放於資料庫中,我們該如何在select * 中去除該屬性
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
//lombok
@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,則判斷該屬性不存在於資料庫中
@TableField(exist = false)
private Integer online;
}
我們將上述內容分離出來主要解釋兩個註解:
- @TableName
- 名稱:@TableName
- 類型:類註解
- 位置:模型類定義上方
- 作用:設置當前類對應的資料庫表關係
- 相關屬性:value設置資料庫表名稱
- @TableField
- 名稱:@TableField
- 類型:屬性註解
- 位置:模型類屬性定義上方
- 作用:設置當前屬性眾多關係
- 相關屬性:value設置資料庫欄位名稱,exist設置屬性在資料庫欄位中是否存在,select設置屬性是否參與查詢
DML編程式控制制
這一章節我們來講一些MyBatisPlus中經常用到的操作,下麵我們一一介紹
ID生成策略控制
我們在實際開發中會有需要ID生成需求,對於不同的情況需要採取不同的ID生成方法:
- 員工編號:遞增ID生成
- 快遞單號:地區ID生成
- 網路編號:隨機ID生成
因此MyBatisPlus提供了一種新的註解來實現這類需求:
- 名稱:@TableId
- 類型:屬性註解
- 位置:模型類中用於表示主鍵的屬性定義上方
- 作用:設置當前類中主鍵屬性的生成策略
- 相關屬性:value設置資料庫主鍵名稱,type設置主鍵生成策略具體參考IdType枚舉值
ID生成策略枚舉值:
- AUTO(0):使用資料庫id自增策略控制id生成
- NONE(1):不設置ID生成策略
- INPUT(2):用戶手工輸入ID
- ASSIGN_ID(3):雪花演算法生成ID(可相容數值型與字元串型)
- ASSIGN_UUID(4):以UUID生成演算法作為ID生成策略
我們給出示例演示:
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
//設置表名映射關係
@TableName("tbl_user")
public class User {
//設置主鍵生成策略(這裡設置為自增)
@TableId(type = IdType.AUTO)
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;
private Integer deleted;
private Integer version;
}
我們也可以同一設置@TableId的type屬性,使整個項目的@TableId都以一種形態執行:
# 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: root
main:
banner-mode: off
# mp日誌
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false
db-config:
id-type: assign_id # 設置為雪花演算法設置ID
多記錄操作
我們在實際開發中常常會進行多條記錄操作:
- 根據主鍵刪除多條操作
- 根據主鍵查詢多條操作
MyBatisPlus也為我們提供了相對應的方法:
package com.itheima;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testDelete(){
//刪除指定多條數據
//deleteBatchIds參數為數組形式,我們提供id的數組即可
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);
userDao.deleteBatchIds(list);
}
@Test
void testSelect()}{
//查詢指定多條數據
//selectBatchIds參數為數組形式,我們提供id的數組即可
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
userDao.selectBatchIds(list);
}
}
邏輯刪除
我們在實際開發中面對項目的刪除操作有時並非是真正的刪除操作:
- 例如對於一些數據的存儲,我們需要在年終總結中計算總產量,所以所有利益收入不能刪除
- 但對於一些離職的員工,關於他們的數據需要暫時性刪除,因而產生矛盾
因而我們提出了邏輯刪除的概念:
- 我們在總列表中添加一個屬性deleted來設置是否被刪除
- 當deleted為1時,當作數據刪除;當deleted為1時,當作數據存在
- 我們在實際開發中以一種虛擬的刪除思想(邏輯刪除)來代替真正的數據刪除
下麵我們來講解如何在MyBatisPlus中實現這種思想:
- 資料庫中添加deleted列
ALTER TABLE tb_user ADD deleted int(1) DEFAULT 0;
- 實體庫中添加deleted屬性,並用註解註明為邏輯刪除屬性
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
//設置表名映射關係
@TableName("tbl_user")
public class User {
//設置主鍵生成策略
@TableId(type = IdType.ASSIGN_ID)
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;
//邏輯刪除欄位,標記當前記錄是否被刪除(value原值,delval修改值)
@TableLogic(value = "0" ,delval = "1")
private Integer deleted;
}
/*
@TableLogic
在設置之後,我們採用delete方法時,不會直接刪除數據,而是將該數據值從value變為delval
類似於:UPDATE tbl_user SET deleted = 1 WHERE id = ? AND deleted = 0;
*/
同樣,我們的邏輯刪除值也可以進行統一設置:
# 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: root
main:
banner-mode: off
# mp日誌
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false
db-config:
id-type: assign_id
table-prefix: tbl_
# 邏輯刪除欄位名
logic-delete-field: deleted
# 邏輯刪除字面值:未刪除為0
logic-not-delete-value: 0
# 邏輯刪除字面值:刪除為1
logic-delete-value: 1
樂觀鎖
我們在業務開發中也會遇到多線程的問題:
- 例如我們的秒殺活動,在之前多線程中也有提及過的問題
- 如果我們不希望我們的剩餘商品數變為負數,那麼就需要設置Lock來解決這個問題
首先我們來講解一下樂觀鎖的基本概念:
- 樂觀鎖首先讀取當前該商品的狀態
- 然後利用當前商品的狀態為條件來修改商品狀態,並且將該商品狀態進行修改
- 如果用戶A和用戶B同時讀取商品狀態,當用戶A的WHERE條件成立後修改商品,則用戶B的WHERE條件就不再成立無法修改
在MyBatisPlus中我們採用樂觀鎖的概念來解決:
- 在資料庫中新添version屬性
ALTER TABLE tb_user ADD version int(11) DEFAULT 1;
- 實體類中新添version屬性並加上註解
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
//設置表名映射關係
@TableName("tbl_user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
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;
@TableLogic(value = "0" ,delval = "1")
private Integer deleted;
// version版本註解(樂觀鎖)
@Version
private Integer version;
}
- 使用攔截器
/*
我們來講解一下為什麼需要使用攔截器
我們將前面我們介紹的樂觀鎖概念轉化為語句:
SELETE version FROM tbl_user
UPDATE tbl_user SET ...(用戶修改) version=version+1 WHERE id = ? AND version = version(我們之前讀取的version)
倘若用戶操作前有其他用戶操作,那麼version就會發生變化,導致用戶無法找到對應的數據,無法操作
因為我們需要對前面的version進行修改,我們需要將語句攔截下來進行一定修改,所以這裡採用攔截器
*/
package com.itheima.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mpInterceptor() {
//1.定義Mp攔截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
//2.添加具體的攔截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//3.添加樂觀鎖攔截器
mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mpInterceptor;
}
}
- 測試
package com.itheima;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testUpdate(){
// 我們設置一個實例User,id為1,version為1
User user = new User();
user.setId(1L);
user.setName("Jock666");
user.setVersion(1);
userDao.updateById(user);
// 我們用user,user2來當作兩個用戶
// 假設兩個用戶同時讀取數據
User user = userDao.selectById(3L); //version=1
User user2 = userDao.selectById(3L); //version=1
// user用戶首先進行操作(這時 實例version為1 操作成立 ,操作結束後version變為2)
// UPDATE tbl_user SET ...(用戶修改) version=2 WHERE id = 1 AND version = 1
user.setName("Jock aaa");
userDao.updateById(user2);
// user2用戶開始操作(這時 實例version為2 但前面讀取的version為1,讀取不到數據,無法操作)
// UPDATE tbl_user SET ...(用戶修改) version=2 WHERE id = 1 AND version = 1(已讀取不到數據)
user2.setName("Jock bbb");
userDao.updateById(user);
}
}
結束語
好的,關於MyBatisPlus的內容就介紹到這裡,希望能為你帶來幫助!
附錄
該文章屬於學習內容,具體參考B站黑馬程式員李老師的SSM框架課程
這裡附上鏈接:MyBatisPlus-01-MybatisPlus入門案例_嗶哩嗶哩_bilibili