天天看點

MyBatis-Plus 之AR模式

推薦:​​MyBatis Plus彙總​​

MyBatis-Plus 之AR模式

首先建立一個資料庫表,如下圖所示:

MyBatis-Plus 之AR模式

然後建立一個Spring Boot項目,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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.kaven</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </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>      
spring:
  application:
    name: mybatis-plus
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: ITkaven@123
    url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false

server:
  port: 8085

logging:
  level:
    root: warn
    com.kaven.mybatisplus.dao: trace
  pattern:
    console: '%p%m%n'      

實體類User:

package com.kaven.mybatisplus.entity;

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

@EqualsAndHashCode(callSuper = false)
@TableName("user")
@Data
public class User extends Model<User>{

    private static final long serialVersionUID = -7431770058790241734L;

    @TableId
    private String id;

    @TableField(value = "username" , condition = SqlCondition.LIKE)
    private String username;

    @TableField("password")
    private String password;

    @TableField(value = "age" , condition = "%s>#{%s}")
    private Integer age;

    /**
     * 使用 @TableField(exist = false) ,表示該字段在資料庫中不存在 ,是以不會插入資料庫中
     * 使用 transient 、 static 修飾屬性也不會插入資料庫中
     */
    @TableField(exist = false)
    private String phone;
}      

實體類User要繼承​

​Model<User>​

​​,并且要加上​

​lombok​

​​的注解​

​@EqualsAndHashCode(callSuper = false)​

​​,還要生成 ​

​serialVersionUID = -7431770058790241734L;​

​。

​​IDEA 自動給實作了 Serializable 接口的類建立 serialVersionUID​​

​Model<T>​

​源碼如下:

package com.baomidou.mybatisplus.extension.activerecord;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * ActiveRecord 模式 CRUD
 * <p>
 * 必須存在對應的原始mapper并繼承baseMapper并且可以使用的前提下
 * 才能使用此 AR 模式 !!!
 * </p>
 *
 * @param <T>
 * @author hubin
 * @since 2016-11-06
 */
public abstract class Model<T extends Model<?>> implements Serializable {

    private static final long serialVersionUID = 1L;

    private transient Log log = LogFactory.getLog(getClass());

    /**
     * 插入(字段選擇插入)
     */
    public boolean insert() {
        SqlSession sqlSession = sqlSession();
        try {
            return SqlHelper.retBool(sqlSession.insert(sqlStatement(SqlMethod.INSERT_ONE), this));
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 插入 OR 更新
     */
    public boolean insertOrUpdate() {
        return StringUtils.checkValNull(pkVal()) || Objects.isNull(selectById(pkVal())) ? insert() : updateById();
    }

    /**
     * 根據 ID 删除
     *
     * @param id 主鍵ID
     */
    public boolean deleteById(Serializable id) {
        SqlSession sqlSession = sqlSession();
        try {
            return SqlHelper.retBool(sqlSession.delete(sqlStatement(SqlMethod.DELETE_BY_ID), id));
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 根據主鍵删除
     */
    public boolean deleteById() {
        Assert.isFalse(StringUtils.checkValNull(pkVal()), "deleteById primaryKey is null.");
        return deleteById(pkVal());
    }

    /**
     * 删除記錄
     *
     * @param queryWrapper 實體對象封裝操作類(可以為 null)
     */
    public boolean delete(Wrapper<T> queryWrapper) {
        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);
        map.put(Constants.WRAPPER, queryWrapper);
        SqlSession sqlSession = sqlSession();
        try {
            return SqlHelper.retBool(sqlSession.delete(sqlStatement(SqlMethod.DELETE), map));
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 更新(字段選擇更新)
     */
    public boolean updateById() {
        Assert.isFalse(StringUtils.checkValNull(pkVal()), "updateById primaryKey is null.");
        // updateById
        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);
        map.put(Constants.ENTITY, this);
        SqlSession sqlSession = sqlSession();
        try {
            return SqlHelper.retBool(sqlSession.update(sqlStatement(SqlMethod.UPDATE_BY_ID), map));
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 執行 SQL 更新
     *
     * @param updateWrapper 實體對象封裝操作類(可以為 null,裡面的 entity 用于生成 where 語句)
     */
    public boolean update(Wrapper<T> updateWrapper) {
        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(2);
        map.put(Constants.ENTITY, this);
        map.put(Constants.WRAPPER, updateWrapper);
        // update
        SqlSession sqlSession = sqlSession();
        try {
            return SqlHelper.retBool(sqlSession.update(sqlStatement(SqlMethod.UPDATE), map));
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 查詢所有
     */
    public List<T> selectAll() {
        SqlSession sqlSession = sqlSession();
        try {
            return sqlSession.selectList(sqlStatement(SqlMethod.SELECT_LIST));
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 根據 ID 查詢
     *
     * @param id 主鍵ID
     */
    public T selectById(Serializable id) {
        SqlSession sqlSession = sqlSession();
        try {
            return sqlSession.selectOne(sqlStatement(SqlMethod.SELECT_BY_ID), id);
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 根據主鍵查詢
     */
    public T selectById() {
        Assert.isFalse(StringUtils.checkValNull(pkVal()), "selectById primaryKey is null.");
        return selectById(pkVal());
    }

    /**
     * 查詢總記錄數
     *
     * @param queryWrapper 實體對象封裝操作類(可以為 null)
     */
    public List<T> selectList(Wrapper<T> queryWrapper) {
        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);
        map.put(Constants.WRAPPER, queryWrapper);
        SqlSession sqlSession = sqlSession();
        try {
            return sqlSession.selectList(sqlStatement(SqlMethod.SELECT_LIST), map);
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 查詢一條記錄
     *
     * @param queryWrapper 實體對象封裝操作類(可以為 null)
     */
    public T selectOne(Wrapper<T> queryWrapper) {
        return SqlHelper.getObject(log, selectList(queryWrapper));
    }

    /**
     * 翻頁查詢
     *
     * @param page         翻頁查詢條件
     * @param queryWrapper 實體對象封裝操作類(可以為 null)
     */
    public <E extends IPage<T>> E selectPage(E page, Wrapper<T> queryWrapper) {
        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(2);
        map.put(Constants.WRAPPER, queryWrapper);
        map.put("page", page);
        SqlSession sqlSession = sqlSession();
        try {
            page.setRecords(sqlSession.selectList(sqlStatement(SqlMethod.SELECT_PAGE), map));
        } finally {
            closeSqlSession(sqlSession);
        }
        return page;
    }

    /**
     * 查詢總數
     *
     * @param queryWrapper 實體對象封裝操作類(可以為 null)
     */
    public Integer selectCount(Wrapper<T> queryWrapper) {
        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);
        map.put(Constants.WRAPPER, queryWrapper);
        SqlSession sqlSession = sqlSession();
        try {
            return SqlHelper.retCount(sqlSession.<Integer>selectOne(sqlStatement(SqlMethod.SELECT_COUNT), map));
        } finally {
            closeSqlSession(sqlSession);
        }
    }

    /**
     * 執行 SQL
     */
    public SqlRunner sql() {
        return new SqlRunner(getClass());
    }

    /**
     * 擷取Session 預設自動送出
     */
    protected SqlSession sqlSession() {
        return SqlHelper.sqlSession(getClass());
    }

    /**
     * 擷取SqlStatement
     *
     * @param sqlMethod sqlMethod
     */
    protected String sqlStatement(SqlMethod sqlMethod) {
        return sqlStatement(sqlMethod.getMethod());
    }

    /**
     * 擷取SqlStatement
     *
     * @param sqlMethod sqlMethod
     */
    protected String sqlStatement(String sqlMethod) {
        //無法确定對應的mapper,隻能用注入時候綁定的了。
        return SqlHelper.table(getClass()).getSqlStatement(sqlMethod);
    }

    /**
     * 主鍵值
     */
    protected Serializable pkVal() {
        return (Serializable) ReflectionKit.getFieldValue(this, TableInfoHelper.getTableInfo(getClass()).getKeyProperty());
    }

    /**
     * 釋放sqlSession
     *
     * @param sqlSession session
     */
    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(getClass()));
    }
}      

可以看到,​

​Model<T>​

​​中其實就是實作了一些CRUD方法,讓實體類User繼承​

​Model<T>​

​​,這樣實體類User不就也可以進行一些CRUD了嗎?那麼是否還需要Mapper接口呢?​

​Model<T>​

​源碼中有一段注釋如下:

* ActiveRecord 模式 CRUD
 * <p>
 * 必須存在對應的原始mapper并繼承baseMapper并且可以使用的前提下
 * 才能使用此 AR 模式 !!!
 * </p>      

是以還是需要Mapper接口的,内部實作肯定還是需要用到它的。

Mapper接口UserMapper:

package com.kaven.mybatisplus.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kaven.mybatisplus.entity.User;
import org.springframework.stereotype.Component;


@Component
public interface UserMapper extends BaseMapper<User> {}      

啟動類:

package com.kaven.mybatisplus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.kaven.mybatisplus.dao")
public class AppRun {
    public static void main(String[] args) {
        SpringApplication.run(AppRun.class , args);
    }
}      

​@MapperScan(basePackages = "com.kaven.mybatisplus.dao")​

​這個一定要加上。

我們先在資料庫中添加幾行資料,友善示範。

MyBatis-Plus 之AR模式

我們來示範​

​insertOrUpdate()​

​這個方法吧。

​insertOrUpdate()​

​源碼:

/**
     * 插入 OR 更新
     */
    public boolean insertOrUpdate() {
        return StringUtils.checkValNull(pkVal()) || Objects.isNull(selectById(pkVal())) ? insert() : updateById();
    }      

​pkVal()​

​源碼:

/**
     * 主鍵值
     */
    protected Serializable pkVal() {
        return (Serializable) ReflectionKit.getFieldValue(this, TableInfoHelper.getTableInfo(getClass()).getKeyProperty());
    }      

​pkVal()​

​​通過反射得到主鍵值,是以​

​insertOrUpdate()​

​​首先判斷主鍵是否為​

​null​

​​,如果為​

​null​

​​,則調用​

​insert()​

​​,如果不為​

​null​

​​,再通過​

​selectById(pkVal())​

​​從資料庫中查詢該主鍵值的資料,如果為​

​null​

​​,則調用​

​insert()​

​​,否則調用​

​updateById()​

​​,這裡不講太詳細(如​

​insert()​

​時主鍵生成的政策),可以自己去看看源碼。

測試方法:

package com.kaven.mybatisplus.dao;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.kaven.mybatisplus.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperARTest {

    @Test
    public void selectList(){
        User user = new User();
        user.setUsername("jack");
        user.setAge(22);
        user.setPassword("jack");
        user.insertOrUpdate();
    }
}      

結果如下:

MyBatis-Plus 之AR模式

插入成功。

再來測試一下。

package com.kaven.mybatisplus.dao;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.kaven.mybatisplus.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperARTest {

    @Test
    public void selectList(){
        User user = new User();
        user.setId("1315624551155220481");
        user.setUsername("jack");
        user.setAge(23);
        user.setPassword("hn");
        user.insertOrUpdate();
    }
}      

​"1315624551155220481"​

​是之前插入成功的資料的主鍵。

結果如下:

MyBatis-Plus 之AR模式

可以看到,是先查詢,再更新的,過程和上面的方法分析是一緻的。

其他方法的用法也是差不多的,這裡就不示範了,想了解條件構造器等更多内容,可以看我之前的部落格,都彙總在下面這篇部落格中,會不斷增加新的内容。