推荐:​​MyBatis Plus汇总​​

MyBatis-Plus 之AR模式

首先创建一个数据库表,如下图所示:

MyBatis-Plus 之AR模式_spring

然后创建一个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模式_后端_02


我们来演示​​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模式_后端_03


插入成功。

再来测试一下。

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模式_sql_04


可以看到,是先查询,再更新的,过程和上面的方法分析是一致的。

其他方法的用法也是差不多的,这里就不演示了,想了解条件构造器等更多内容,可以看我之前的博客,都汇总在下面这篇博客中,会不断增加新的内容。

​​MyBatis Plus汇总​​

写博客是博主记录自己的学习过程,如果有错误,请指正,谢谢!