Java開發學習(四十二)----MyBatisPlus查詢語句之條件查詢

2022-11-22 06:01:09

一、條件查詢的類

  • MyBatisPlus將書寫複雜的SQL查詢條件進行了封裝,使用程式設計的形式完成查詢條件的組合。

這個我們在前面都有見過,比如查詢所有和分頁查詢的時候,都有看到過一個Wrapper類,這個類就是用來構建查詢條件的,如下圖所示:

那麼條件查詢如何使用Wrapper來構建呢?

二、環境構建

在構建條件查詢之前,我們先來準備下環境

  • 建立一個SpringBoot專案

    參考Java開發學習(三十五)----SpringBoot快速入門及起步依賴解析

  • pom.xml中新增對應的依賴

    <?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>
        </parent>
        <groupId>com.itheima</groupId>
        <artifactId>mybatisplus_02_dql</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
    ​
            <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>
    ​
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    ​
        </dependencies>
    ​
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    ​
    </project>
    ​
  • 編寫UserDao介面

    @Mapper
    public interface UserDao extends BaseMapper<User> {
    }
  • 編寫模型類

    @Data
    public class User {
        private Long id;
        private String name;
        private String password;
        private Integer age;
        private String tel;
    }
  • 編寫引導類

    @SpringBootApplication
    public class Mybatisplus02DqlApplication {
    ​
        public static void main(String[] args) {
            SpringApplication.run(Mybatisplus02DqlApplication.class, args);
        }
    ​
    }
  • 編寫組態檔

    # 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
    # MybatisPlus紀錄檔
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  • 編寫測試類

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    ​
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            List<User> userList = userDao.selectList(null);
            System.out.println(userList);
        }
    }

    最終建立的專案結構為:

  • 測試的時候,控制檯列印的紀錄檔比較多,速度有點慢而且不利於檢視執行結果,所以接下來我們把這個紀錄檔處理下:

    • 取消初始化spring紀錄檔列印,resources目錄下新增logback.xml,名稱固定,內容如下:

      <?xml version="1.0" encoding="UTF-8"?>
      <configuration>
      </configuration>
    • 取消MybatisPlus啟動banner圖示

      application.yml新增如下內容:

      # mybatis-plus紀錄檔控制檯輸出
      mybatis-plus:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        global-config:
          banner: off # 關閉mybatisplus啟動圖示
    • 取消SpringBoot的log列印

      application.yml新增如下內容:

      spring:
        main:
          banner-mode: off # 關閉SpringBoot啟動圖示(banner)

解決控制檯列印紀錄檔過多的相關操作可以不用去做,一般會被用來方便我們檢視程式執行的結果。

三、構建條件查詢

在進行查詢的時候,我們的入口是在Wrapper這個類上,因為它是一個介面,所以我們需要去找它對應的實現類,關於實現類也有很多,說明我們有多種構建查詢條件物件的方式,

  1. 先來看第一種:QueryWrapper

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
        QueryWrapper qw = new QueryWrapper();
        qw.lt("age",18);
        List<User> userList = userDao.selectList(qw);
        System.out.println(userList);
    }
}
  • lt: 小於(<) ,最終的sql語句為

    SELECT id,name,password,age,tel FROM user WHERE (age < ?)

第一種方式介紹完後,有個小問題就是在寫條件的時候,容易出錯,比如age寫錯,就會導致查詢不成功

  1. 接著來看第二種:QueryWrapper的基礎上使用lambda

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
        QueryWrapper<User> qw = new QueryWrapper<User>();
        qw.lambda().lt(User::getAge, 10);//新增條件
        List<User> userList = userDao.selectList(qw);
        System.out.println(userList);
    }
}
  • User::getAge,為lambda表示式中的,類名::方法名,最終的sql語句為:

SELECT id,name,password,age,tel FROM user WHERE (age < ?)

注意:構建LambdaQueryWrapper的時候泛型不能省。

此時我們再次編寫條件的時候,就不會存在寫錯名稱的情況,但是qw後面多了一層lambda()呼叫

  1. 接著來看第三種:LambdaQueryWrapper

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.lt(User::getAge, 10);
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}

這種方式就解決了上一種方式所存在的問題。

四、多條件構建

三種構建查詢物件的方式,每一種都有自己的特點,所以用哪一種都行,剛才都是一個條件,那如果有多個條件該如何構建呢?

需求:查詢資料庫表中,年齡在10歲到30歲之間的使用者資訊

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
        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);
    }
}
  • 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的資料

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}
  • or()就相當於我們sql語句中的or關鍵字,不加預設是and,最終的sql語句為:

    SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)

五、null判定

先來看一張圖,

  • 我們在做條件查詢的時候,一般會有很多條件可以供使用者進行選擇查詢。

  • 這些條件使用者可以選擇使用也可以選擇不使用,比如我要查詢價格在8000以上的手機

  • 在輸入條件的時候,價格有一個區間範圍,按照需求只需要在第一個價格輸入框中輸入8000

  • 後臺在做價格查詢的時候,一般會讓 price>值1 and price <值2

  • 因為前端沒有輸入值2,所以如果不處理的話,就會出現 price>8000 and price < null問題

  • 這個時候查詢的結果就會出問題,具體該如何解決?

需求:查詢資料庫表中,根據輸入年齡範圍來查詢符合條件的記錄

使用者在輸入值的時候,

如果只輸入第一個框,說明要查詢大於該年齡的使用者

如果只輸入第二個框,說明要查詢小於該年齡的使用者

如果兩個框都輸入了,說明要查詢年齡在兩個範圍之間的使用者

思考第一個問題:後臺如果想接收前端的兩個資料,該如何接收?

我們可以使用兩個簡單資料型別,也可以使用一個模型類,但是User類中目前只有一個age屬性,如:

@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}

使用一個age屬性,如何去接收頁面上的兩個值呢?這個時候我們有兩個解決方案

方案一:新增屬性age2,這種做法可以但是會影響到原模型類的屬性內容

@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
    private Integer age2;
}

方案二:新建一個模型類,讓其繼承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;
}

環境準備好後,我們來實現下剛才的需求:

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @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);
    }
}

上面的寫法可以完成條件為非空的判斷,但是問題很明顯,如果條件多的話,每個條件都需要判斷,程式碼量就比較大,來看MybatisPlus給我們提供的簡化方式:

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @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);
    }
}
  • lt()方法

    condition為boolean型別,返回true,則新增條件,返回false則不新增條件