1. 程式人生 > 其它 >MyBatisPlus 入門教程,這篇很贊

MyBatisPlus 入門教程,這篇很贊

在之前的文章中我們經常使用MybatisPlus進行增刪改查,可能有些小夥伴對mybatisplus不是很熟悉,今天特意出了一般入門級的教程,我自己也是一邊學習一邊寫的,有什麼地方寫的不好的地方請留意指出。

快速入門的小例子

準備資料庫和測試資料

#建立使用者表
CREATE TABLE user (
    id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主鍵',
    name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
    age INT(11) DEFAULT NULL COMMENT '年齡',
    email VARCHAR(50) DEFAULT NULL COMMENT '郵箱',
    manager_id BIGINT(20) DEFAULT NULL COMMENT '直屬上級id',
    create_time DATETIME DEFAULT NULL COMMENT '建立時間',
    CONSTRAINT manager_fk FOREIGN KEY (manager_id)
        REFERENCES user (id)
)  ENGINE=INNODB CHARSET=UTF8;

#初始化資料:
INSERT INTO user (id, name, age, email, manager_id, create_time)
VALUES (1087982257332887553, '大boss', 40, '[email protected]', NULL, '2019-01-11 14:20:20'),
			(1088248166370832385, '王天風', 25, '[email protected]', 1087982257332887553, '2019-02-05 11:12:22'),
			(1088250446457389058, '李藝偉', 28, '[email protected]', 1088248166370832385, '2019-02-14 08:31:16'),
			(1094590409767661570, '張雨琪', 31, '[email protected]', 1088248166370832385, '2019-01-14 09:15:15'),
			(1094592041087729666, '劉紅雨', 32, '[email protected]', 1088248166370832385, '2019-01-14 09:48:16');

配置資料庫資訊

在專案的resources目錄下新建application.yml檔案,內容如下:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8
    username: root
    password: nomore532
    driver-class-name: com.mysql.cj.jdbc.Driver


mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    root: warn
    com.demo01.Mapper: trace
  pattern:
    console: "%p%m%n"

新建實體型別

在專案根目錄下新建一個包,名字為Entity,然後,新建一個名字為User.java的實體型別。

package com.demo01.Entity;


import lombok.Data;


import java.time.LocalDateTime;

@Data
public class User {

    //主鍵
    private Long id;
    //使用者名稱
    private String name;
    //郵箱
    private String email;
    //年齡
    private  Integer age;
    //直屬上級
    private  Long managerId;
    //建立時間
    private LocalDateTime createTime;
}

注意:@Data註解能在編譯是自動生成get和set方法。

新建Mapper包,並建立UserMapper介面類。

在專案的根目錄下新建一個名為Mapper包,並建立UserMapper.java介面類,繼承MyBatis-PlusBaseMapper基類。

package com.demo01.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.demo01.Entity.User;

public interface UserMapper extends BaseMapper<User> {
}

注意:MyBatisPlus的BaseMapper基類需要存入一個泛型,這個泛型是要操作的實體型別。

並在啟動型別新增掃描路徑

@SpringBootApplication
@MapperScan("com.demo01.Mapper")
public class Demo01Application {

    public static void main(String[] args) {
        SpringApplication.run(Demo01Application.class, args);
    }

}

新建測試方法

查詢所有的使用者資訊

package com.demo01;


import com.demo01.Entity.User;
import com.demo01.Mapper.UserMapper;
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(classes = Demo01Application.class)
class Demo01ApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void  select() {
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
}

執行結果如下:

通用Mapper

新增(Create)方法

在測試目錄下新建一個測試類,名字為InserTest.java,內容如下:

package com.demo01;


import com.demo01.Entity.User;
import com.demo01.Mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;




@SpringBootTest(classes = Demo01Application.class)
class InsertTests {

    @Autowired
    private UserMapper userMapper;


    @Test
    public void  insert(){
        User user = new User();
        user.setName("劉強東");
        user.setAge(37);
        user.setEmail("[email protected]");
        user.setManagerId(1087982257332887553L);
        int rows = userMapper.insert(user);
        System.out.println("影響行數"+rows);

    }
}

注意:insert方法需要的引數是一個實體,返回引數是影響行數

執行結果如下:

查資料庫結構如下:

常用註解

@TableName

描述:表名註解

屬性 型別 必須指定 預設值 描述
value String "" 表名
schema String "" schema
keepGlobalPrefix boolean false 是否保持使用全域性的 tablePrefix 的值(如果設定了全域性 tablePrefix 且自行設定了 value 的值)
resultMap String "" xml 中 resultMap 的 id
autoResultMap boolean false 是否自動構建 resultMap 並使用(如果設定 resultMap 則不會進行 resultMap 的自動構建並注入)
excludeProperty String[] {} 需要排除的屬性名(@since 3.3.1)

@TableId

描述:主鍵註解

屬性 型別 必須指定 預設值 描述
value String "" 主鍵欄位名
type Enum IdType.NONE 主鍵型別

type的型別包括以下幾種:

  • AUTO:資料庫ID自增。
  • NONE:無狀態,該型別為未設定主鍵型別(註解裡等於跟隨全域性,全局裡約等於 INPUT)
  • INPUT:insert前自行set主鍵值
  • ASSIGN_ID:分配ID(主鍵型別為Number(Long和Integer)或String)(since 3.3.0),使用介面IdentifierGenerator的方法nextId(預設實現類為DefaultIdentifierGenerator雪花演算法)
  • ASSIGN_UUID :分配UUID,主鍵型別為String(since 3.3.0),使用介面IdentifierGenerator的方法nextUUID(預設default方法)

TableField

描述:欄位註解(非主鍵)

詳細的註解請檢視MybatisPlus的官網

排查非表字段的三種方式

  • transient:不參與序列化
  • static
  • TableField(exist=false)

MybatisPlus查詢方法(Retrieve)

普遍查詢方法

selectById

    @Test
    public void  selectByIdTest(){

        User user = userMapper.selectById(1435065643693645826L);
        System.out.println(user);

    }

執行結果:

selectBatchIds

    @Test
    public void  selectByIds(){
        List<Long> idsList = Arrays.asList(
                1088248166370832385L,
                1094590409767661570L,
                1435065643693645826L
                );
        List<User> users = userMapper.selectBatchIds(idsList);
        users.forEach(System.out::println);

    }

執行結果:

selectByMap

    @Test
    public void  selectByMapTest(){
        //map.put("name","王天風")
        //map.put("age",25)
        //where name="王天風" and age=25
        Map<String,Object> columnMap = new HashMap<>();
        columnMap.put("name","王天風");
        columnMap.put("age",25);
        List<User> users = userMapper.selectByMap(columnMap);
        users.forEach(System.out::println);

    }

注意:columnMap中的鍵是資料庫中的欄位,不是實體型別的屬性。

執行結果:

以條件構造器為引數的查詢方法

selectList

    /**
     * 1、名字中包含雨並且年齡小於40
     * 	name like '%雨%' and age<40
     */
    @Test
    public  void selectByWrapper(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","雨").lt("age",40);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果

      /**
     * 2、名字中包含雨年並且齡大於等於20且小於等於40並且email不為空
     *    name like '%雨%' and age between 20 and 40 and email is not null
     */
    @Test
    public  void selectByWrapper2(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","雨").between("age",20,40).isNotNull("email");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果:

    /***
     * 3、名字為王姓或者年齡大於等於25,按照年齡降序排列,年齡相同按照id升序排列
     *    name like '王%' or age>=25 order by age desc,id asc
     */
    @Test
    public  void selectByWrapper3(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name","王").or().ge("age",25).orderByDesc("age").orderByAsc("id");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    /**
     * 4、建立日期為2019年2月14日並且直屬上級為名字為王姓
     *       date_format(create_time,'%Y-%m-%d')='2019-02-14'
     *       and manager_id in (select id from user where name like '王%')
    */

    @Test
    public  void selectByWrapper4(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.apply("date_format(create_time,'%Y-%m-%d') = {0}","2019-02-14")
                    .inSql("manager_id","select id from user where name like '王%'");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果:

    /**
     * 5、名字為王姓並且(年齡小於40或郵箱不為空)
     *     name like '王%' and (age<40 or email is not null)
     */
    @Test
    public  void selectByWrapper5(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name","王").and(wq->wq.lt("age",40).or().isNotNull("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果:

    /**
     * 6、名字為王姓或者(年齡小於40並且年齡大於20並且郵箱不為空)
     *     name like '王%' or (age<40 and age>20 and email is not null)
     */

    @Test
    public  void selectByWrapper6(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name","王").or(wq->wq.lt("age",40).gt("age",20).isNotNull("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果:

    /**
     * 7、(年齡小於40或郵箱不為空)並且名字為王姓
     *     (age<40 or email is not null) and name like '王%'
     */

    @Test
    public  void selectByWrapper7(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //queryWrapper.and(wq->wq.lt("age",40).or().isNotNull("email")).and(wq->wq.likeRight("name","王"));
        queryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email"))
                .likeRight("name","王");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    };

執行結果:

    /**
     * 8、年齡為30、31、34、35
     *     age in (30、31、34、35)
     */
    @Test
    public  void selectByWrapper8(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("age",Arrays.asList(30,31,34,35));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果:

    /**
     * 9、只返回滿足條件的其中一條語句即可
     * limit 1
     */
    @Test
    public  void selectByWrapper9(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name","王").or(wq->wq.lt("age",40).gt("age",20).isNotNull("email")).last("limit 1");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果:

select中欄位不全出現的處理方法

   /**
     * 10、名字中包含雨並且年齡小於40(需求1加強版)
     * 第一種情況:select id,name
     * 	           from user
     * 	           where name like '%雨%' and age<40
     * 第二種情況:select id,name,age,email
     * 	           from user
     * 	           where name like '%雨%' and age<40
     */    
	@Test
    public  void selectByWrapper10(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id","name").like("name","雨").lt("age",40);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    @Test
    public  void selectByWrapper11(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","雨").lt("age",40)
                .select(User.class,info-> !info.getColumn().equals("create_time") && !info.getColumn().equals("manager_id")) ;
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }	

執行結果:

條件構造器中condition作用

condition作用是構造的條件中如何為true就加入,為false就不加入條件。
AbstractWrapper<T, String, QueryWrapper<T>>的原始碼可以看到很多方法都有condition引數,它是一個布林型的引數,意思就是是否將該sql語句(像in()like())加在總sql語句上,如下圖所示。

    @Test
    public void  testCondition() {
        String name="王";
        String email="";
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(!StringUtils.isEmpty(name),"name",name)
                .like(!StringUtils.isEmpty(email),"email",email);

        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);

    }

執行結果:

建立條件構造器是傳入實體物件

    @Test
    public  void selectByWrapperEntity(){
        User whereuser = new User();
        whereuser.setName("劉紅雨");
        whereuser.setAge(32);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(whereuser);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);

    }

預設情況下條件是等值,如下圖,如果需要設定為like,需要在實體屬性添加註解。

   ...省略...
	@TableField(condition = SqlCondition.LIKE)
    private String name;
  ...省略...

condition引數可以自定義。

執行結果:

條件構造器中allEq用法

allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)

個別引數說明:
params : key為資料庫欄位名,value為欄位值
null2IsNull : 為true則在mapvaluenull時呼叫 isNull 方法,為false時則忽略valuenull

  • 例1: allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null
  • 例2: allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'
    @Test
    public  void selectAllEq(){

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Map<String,Object> params = new HashMap<String,Object>();
        params.put("name","王天風");
        params.put("age",25);
        queryWrapper.allEq(params);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);

    }

執行結果:

其他條件構造器的使用方法

selectMaps

有些時候返回的結果不需要是整個實體類的屬性,可能只需要某幾個欄位的資料,如下:

    @Test
    public  void selectByWrapperMaps(){

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id","name").like("name","雨").lt("age",40);
        List<Map<String,Object>> users = userMapper.selectMaps(queryWrapper);
        users.forEach(System.out::println);

    }

執行結果:

另外一種需求場景是統計查詢。如下:

  /**
     * 11、按照直屬上級分組,查詢每組的平均年齡、最大年齡、最小年齡。
     * 並且只取年齡總和小於500的組。
     * select avg(age) avg_age,min(age) min_age,max(age) max_age
     * from user
     * group by manager_id
     * having sum(age) <500
     */
    @Test
    public  void selectByWrapperMaps2(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("avg(age) avg_age","min(age) min_age","max(age) max_age")
                .groupBy("manager_id").having("sum(age)<{0}",500);
        List<Map<String,Object>> users = userMapper.selectMaps(queryWrapper);
        users.forEach(System.out::println);
    }

執行結果:

selectObjs

只返回第一列的資料。

    @Test
    public  void selectByObjs(){

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id","name").like("name","雨").lt("age",40);
        List<Object> users = userMapper.selectObjs(queryWrapper);
        users.forEach(System.out::println);

    }

執行結果:

selectCount

查詢總記錄數

    @Test
    public  void selectWrapperCount(){

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","雨").lt("age",40);
        int rows = userMapper.selectCount(queryWrapper);
        System.out.println("總記錄數:"+rows);

    }

執行結果:

Lambda條件構造器

三種方法建立lambda條件構造器:
LambdaQueryWrapper<User> lambda = ``new ``QueryWrapper<User>().lambda()``; ``LambdaQueryWrapper<User> userLambdaQueryWrapper = ``new ``LambdaQueryWrapper<User>()``; ``LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>``_lambdaQuery_``()``;

    @Test
    public  void selectLambda(){
        //LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
        //LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>();
        LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>lambdaQuery();
        lambdaQuery.like(User::getName,"雨").lt(User::getAge,40);
        List<User> users = userMapper.selectList(lambdaQuery);
        users.forEach(System.out::println);
    }

執行結果:

    @Test
    public  void selectLambda2(){

        List<User> users = new LambdaQueryChainWrapper<User>(userMapper)
                .like(User::getName, "雨").lt(User::getAge, 40).list();
        users.forEach(System.out::println);
    }

執行結果:

使用條件構造器的自定義SQL

MP版本需要大於3.0.7

首先在UserMapper類中自定義方法。如下:

@Mapper
public interface UserMapper extends BaseMapper<User> {

    @Select("select * from user ${ew.customSqlSegment}")
    List<User> selectAll(@Param(Constants.WRAPPER) Wrapper<User> wrappers);
}

注意:${ew.customSqlSegment}名字是固定寫法。

編寫測試方法

    @Test
    public  void  selectMy(){
        LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>lambdaQuery();
        lambdaQuery.like(User::getName,"雨").lt(User::getAge,40);
        List<User> users = userMapper.selectAll(lambdaQuery);
        users.forEach(System.out::println);
    }

執行結果如下:

分頁查詢

MP分頁外掛實現物理分頁

在專案目錄中新建一個包,名字為config,並建立一個類,名字為MyBatisPlusConfig,內容如下:

package com.demo03.Config;

import com.baomidou.mybatisplus.annotation.DbType;
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;

@Configuration
public class MybatisPlusConfig {


    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }
}

編寫測試實現方法

    @Test
    public  void  selectPage(){
        LambdaQueryWrapper<User> lambdaQuery =Wrappers.<User>lambdaQuery();
        lambdaQuery.gt(User::getAge,20);
        Page<User> userPage = new Page<>(1, 2);
        Page<User> userPage1 = userMapper.selectPage(userPage, lambdaQuery);
        System.out.println(userPage1);
    }

AR模式、主鍵策略和基本配置

AR模式

Active Record(活動記錄),是一種領域模型模式,特點是一個模型類對應關係型資料庫中的一個表,而模型類的一個例項對應表中的一行記錄。簡單來說,就是通過實體類操作資料庫的增刪改查。
使用前提需要實體類繼承Model類。如下:

package com.demo03.Entity;

import com.baomidou.mybatisplus.annotation.SqlCondition;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.time.LocalDateTime;

@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {
    //主鍵
    private Long id;
    //使用者名稱
    @TableField(condition = SqlCondition.LIKE)
    private String name;
    //郵箱
    private String email;
    //年齡
    private  Integer age;
    //直屬上級
    private  Long managerId;
    //建立時間
    private LocalDateTime createTime;
}

新建測試類

package com.demo03;

import com.demo03.Entity.User;
import com.demo03.Mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ARTests {

    @Test
    public void  insert(){

        User user = new User();
        user.setName("馬雲");
        user.setAge(37);
        user.setEmail("[email protected]");
        boolean rows = user.insert();
        System.out.println("影響行數:"+rows);


    }
}

主鍵策略

MP定義了6中主鍵策略。

@Getter
public enum IdType {
    /**
     * 資料庫ID自增
     */
    AUTO(0),
    /**
     * 該型別為未設定主鍵型別(註解裡等於跟隨全域性,全局裡約等於 INPUT)
     */
    NONE(1),
    /**
     * 使用者輸入ID
     * <p>該型別可以通過自己註冊自動填充外掛進行填充</p>
     */
    INPUT(2),

    /* 以下3種類型、只有當插入物件ID 為空,才自動填充。 */
    /**
     * 分配ID (主鍵型別為number或string),
     * 預設實現類 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花演算法)
     *
     * @since 3.3.0
     */
    ASSIGN_ID(3),
    /**
     * 分配UUID (主鍵型別為 string)
     * 預設實現類 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
     */
    ASSIGN_UUID(4),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
     */
    @Deprecated
    ID_WORKER(3),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
     */
    @Deprecated
    ID_WORKER_STR(3),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
     */
    @Deprecated
    UUID(4);

    private final int key;

    IdType(int key) {
        this.key = key;
    }
}

區域性主鍵策略實現

在實體類主鍵通過TableId註解方式。

    @TableId(type = IdType.AUTO)
    private Long id;

全域性主鍵策略實現

在配置檔案中配置全域性主鍵ID。

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

MP基本配置

詳細資訊檢視官網中的配置