推荐:MyBatis Plus汇总
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")
这个一定要加上。
我们先在数据库中添加几行数据,方便演示。
我们来演示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();
}
}
结果如下:
插入成功。
再来测试一下。
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汇总
写博客是博主记录自己的学习过程,如果有错误,请指正,谢谢!