MyBatis-Plus
什么是 MyBatis?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。消除了 JDBC 的复杂性,MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis vs JPA
MyBatis 的优势
- SQL 语句可以自由控制,更灵活、性能较高。
- SQL 与代码分离、易于阅读和维护
- 提供 XML 标签,支持编写动态 SQL 语句
MyBatis 的劣势
- 简单的 CRUD 操作还得写 SQL 语句(XML 或注解)
- XML 中有大量的 SQL 要维护
- MyBatis 自身功能很有限,但支持 Plugin
MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
官网
码云
GitHun
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
框架结构
快速入门
建库建表
#创建用户表
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, 'boss@baomidou.com', NULL
, '2019-01-11 14:20:20'),
(1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553
, '2019-02-05 11:12:22'),
(1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385
, '2019-02-14 08:31:16'),
(1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385
, '2019-01-14 09:15:15'),
(1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385
, '2019-01-14 09:48:16');
引入依赖
<?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.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.soulboy</groupId>
<artifactId>mp-test-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mp-test-01</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- SpringBoot启动类 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- SpringBoot test 启动类 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Mysql JDBC 驱动类 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Mybatis-Plus 启动器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置
application.yml
编码
启动类
package com.soulboy.mptest01;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.soulboy.mptest01.dao")
public class MpTest01Application {
public static void main(String[] args) {
SpringApplication.run(MpTest01Application.class, args);
}
}
实体类
package com.soulboy.mptest01.domain;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
Mapper
package com.soulboy.mptest01.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.soulboy.mptest01.domain.User;
public interface UserMapper extends BaseMapper<User>{
}
测试
package com.soulboy.mptest01;
import com.soulboy.mptest01.dao.UserMapper;
import com.soulboy.mptest01.domain.User;
import org.junit.Assert;
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.sql.SQLOutput;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MpTest01ApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void select() {
List<User> list = userMapper.selectList(null);
Assert.assertEquals(5,list.size());
list.forEach(System.out::println);
}
}
Create
新增方法
- 默认 POJO 属性值为 null,生成 SQL 的语句中不会带有该列。
- 默认采用雪花算法的自增 ID,填充主键字。
- 下划线自动转驼峰(managerId => manager_id)
// 这里没有设置email属性,因此email属性的默认值为null,当数据的值为null时,sql语句中不会出现email列,相关列的数据不会改变。
@Test
public void insert(){
User user = new User();
user.setName("秃秃");
user.setAge(31);
user.setManagerId(1088248166370832385L);
user.setCreateTime(LocalDateTime.now());
int rows = userMapper.insert(user);
System.out.println("影响记录数: "+ rows); //影响记录数: 1
}
注解
- @TableName:显式指定实体类对应的数据库表名。
@Data
@TableName("mp_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
- @TableId:显式指定主键。
@Data
@TableName("mp_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
- @TableField:显式指定属性对应数据库中的列名。
@Data
@TableName("mp_user")
public class User {
@TableId
private Long id;
@TableField("name")
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
}
排除非表字段
实体类的有些数据只是为了存放临时数据。
- transient:标识的属性不会参与序列化。
private transient String remark;//备注(在数据库中没有对应的字段)
- static:需要手动设置静态的 set 和 get 方法。
private static String remark;//备注(在数据库中没有对应的字段)
- @TableField(:需要手动设置静态的 set 和 get 方法。
@TableField(exist=false):推荐使用这种方式。
private String remark;//备注(在数据库中没有对应的字段)
Select
基查询方法
@Test
public void select() {
List<User> list = userMapper.selectList(null);
Assert.assertEquals(5,list.size());
list.forEach(System.out::println);
}
@Test
public void selectById() {
User user = userMapper.selectById(1094590409767661570L);
System.out.println(user);
}
@Test
public void selectByIds() {
List<Long> idList = Arrays.asList(1094590409767661570L, 1088248166370832385L);
List<User> users = userMapper.selectBatchIds(idList);
for (User user : users) {
System.out.println(user);
}
}
@Test
public void selectByMap(){
//Map的key是数据库中的列名(不是实体类的属性名),Map的值是过滤条件
//SELECT id,create_time,name,manager_id,email,age FROM user WHERE name = ? AND age = ?
HashMap<String, Object> columnMap = new HashMap<>();
columnMap.put("name", "王天风");
columnMap.put("age", 25);
List<User> users = userMapper.selectByMap(columnMap);
users.forEach(System.out::println);
}
以条件构造器为参数的查询方法
select 中字段不全出现的处理方法
默认查询返回表中全部的列。
条件构造器中 condition 的作用
@Test
public void testCondition(){
String name = "王";
String email = "";
//// Preparing: SELECT id,create_time,name,manager_id,email,age FROM user WHERE (name LIKE ?)
condition(name, email);
}
private void condition(String name, String email){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.like(StringUtils.isNotEmpty(name),"name", name)
.like(StringUtils.isNotEmpty(email), "email", email);
List<User> userList = userMapper.selectList(userQueryWrapper);
userList.forEach(System.out::println);
}
创建条件构造器时传入实体对象
默认传入 Entity 中属性值不为 null 的字段将作为查询条件,默认使用等值判断。
@Test
public void selectByWrapperEntity(){
User whereUser = new User();
whereUser.setName("秃秃");
whereUser.setAge(31);
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(whereUser);
// Preparing: SELECT id,create_time,name,manager_id,email,age FROM user WHERE name=? AND age=?
// Parameters: 秃秃(String), 31(Integer)
List<User> userList = userMapper.selectList(userQueryWrapper);
userList.forEach(System.out::println);
}
如果需要该表 Entity 默认的等值条件判断,需要修改 Entity 实体类属性上面的默认规则。
条件构造器中 allEq 用法
key 为数据库字段名,value 为字段值。
@Test
public void selectByWrapperAllEq(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
HashMap<String, Object> params = new HashMap<>();
params.put("name","王天风");
params.put("age", 25);
//params.put("age", null); //is null
//userQueryWrapper.allEq(params,false); //false代表:params中key为null的条件会被忽略
userQueryWrapper.allEq(params);
List<User> userList = userMapper.selectList(userQueryWrapper);
userList.forEach(System.out::println);
//User(id=1088248166370832385, name=王天风, age=25, email=wtf@baomidou.com, managerId=1087982257332887553, createTime=2019-02-05T11:12:22)
}
其他以条件构造器为参数的查询方式
// List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 只需要实体类的少数字段的时候,如果返回整个实体类,不需要的字段为null,这样做并不优雅,这个时候可以使用QueryWrapper的selectMaps方法,并且之前使用select指定需要的列名。
@Test
public void selectByWrapperMaps() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.select("id","name").like("name", "雨").lt("age", 40);
List<Map<String, Object>> userList = userMapper.selectMaps(userQueryWrapper);
userList.forEach(System.out::println);
//{name=张雨琪, id=1094590409767661570}
//{name=刘红雨, id=1094592041087729666}
}
统计分组
/**
* 按照直属上级分组,查询每组的平均年龄、最大年龄、最小年龄。
* 并且只取年龄总和小于500的组。
* select avg(age) avg_age, min(age) min_age, max(age) max_age from user group by manager_id having sum(age) < 500
*
* DEBUG==> Preparing: SELECT avg(age) avg_age,min(age) min_age,max(age) max_age FROM user GROUP BY manager_id HAVING sum(age)<?
* DEBUG==> Parameters: 500(Integer)
*/
@Test
public void selectByWrapperMaps2() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.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>> userList = userMapper.selectMaps(userQueryWrapper);
userList.forEach(System.out::println);
//{max_age=40, avg_age=40.0000, min_age=40}
//{max_age=25, avg_age=25.0000, min_age=25}
//{max_age=32, avg_age=30.5000, min_age=28}
}
/**
* 适用于条查询结果只需要第一列(每个实体中的第一列):查询的是2列,但是只有第一列被设置到List<Object>中。
* TRACE<== Row: 1094590409767661570, 张雨琪
* TRACE<== Row: 1094592041087729666, 刘红雨
*/
@Test
public void selectByWrapperObjs() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.select("id","name").like("name", "雨").lt("age", 40);
List<Object> userList = userMapper.selectObjs(userQueryWrapper);
userList.forEach(System.out::println);
// 1094590409767661570
// 1094592041087729666
}
/**
* 查询结果必须是一条记录或者没有结果,如果查询结果多余一条会报错:返回Entity。
*/
@Test
public void selectByWrapperOne() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.like("name", "红雨").lt("age", 40);
User user = userMapper.selectOne(userQueryWrapper);
System.out.println(user);
}
Lambda 条件构造器
自定义 SQL
使用条件构造器的自定义 SQL
注解的方式
UserMapper
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.soulboy.mptest01.domain.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper extends BaseMapper<User>{
@Select("select * from user ${ew.customSqlSegment}")
List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
}
测试
@Test
public void selectMy() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
lambdaQueryWrapper.likeRight(User::getName, "王")
.and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
List<User> userList = userMapper.selectAll(lambdaQueryWrapper);
userList.forEach(System.out::println);
//User(id=1088248166370832385, name=王天风, age=25, email=wtf@baomidou.com, managerId=1087982257332887553, createTime=2019-02-05T11:12:22)
}
XML 的方式
pom
mapper 的 XML 配置文件没有被编译,在 pom 文件中添加。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java/</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
application.yml
mybatis-plus:
mapper-locations: classpath:com/soulboy/mptest01/mapper/*.xml
UserMapper 接口
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.soulboy.mptest01.domain.User;
import org.apache.ibatis.annotations.Param;
//import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper extends BaseMapper<User>{
List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="com.soulboy.mptest01.dao.UserMapper">
<select id="selectAll" resultType="com.soulboy.mptest01.domain.User">
select * from user ${ew.customSqlSegment}
</select>
</mapper>
测试
@Test
public void selectMy() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
lambdaQueryWrapper.likeRight(User::getName, "王")
.and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
List<User> userList = userMapper.selectAll(lambdaQueryWrapper);
userList.forEach(System.out::println);
//User(id=1088248166370832385, name=王天风, age=25, email=wtf@baomidou.com, managerId=1087982257332887553, createTime=2019-02-05T11:12:22)
}
分页
MyBatis 分页介绍
MyBatis 的分页并非是物理分页,一次查询出所有数据存储在本地内存中,缺点如下:
- 第一次查询速度慢
- 如果表中的记录数多,则会占用大量的物理内存
MP 分页插件实现物理分页
配置类
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
使用 MP 的分页插件(物理分页)
/**
* selectPage()
* 年龄大于等于26岁的
*/
@Test
public void selectByPages(){
//QueryWrapper<User> query = Wrappers.<User>query();
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//列名(不是实体类中的属性名)
userQueryWrapper.ge("age", 26);
//分页信息 (当前页码,每页条数)
Page<User> page = new Page<>(1, 2);
IPage<User> iPage = userMapper.selectPage(page, userQueryWrapper);
System.out.println("总页数: " + iPage.getPages()); //
System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
List<User> userList = iPage.getRecords();
userList.forEach(System.out::println);
//User(id=1087982257332887553, name=大boss, age=40, email=boss@baomidou.com, managerId=null, createTime=2019-01-11T14:20:20)
//User(id=1088250446457389058, name=李艺伟, age=28, email=lyw@baomidou.com, managerId=1088248166370832385, createTime=2019-02-14T08:31:16)
}
/**
* selectMapsPage()
* 年龄大于等于26岁的
*/
@Test
public void selectByPages2(){
//QueryWrapper<User> query = Wrappers.<User>query();
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//列名(不是实体类中的属性名)
userQueryWrapper.ge("age", 26);
//分页信息 (当前页码,每页条数)
Page<User> page = new Page<>(1, 2);
IPage<Map<String, Object>> iPage = userMapper.selectMapsPage(page, userQueryWrapper);
System.out.println("总页数: " + iPage.getPages()); //
System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
List<Map<String, Object>> userList = iPage.getRecords();
userList.forEach(System.out::println);
//{create_time=2019-01-11 14:20:20.0, name=大boss, id=1087982257332887553, email=boss@baomidou.com, age=40}
//{create_time=2019-02-14 08:31:16.0, manager_id=1088248166370832385, name=李艺伟, id=1088250446457389058, email=lyw@baomidou.com, age=28}
}
/**
* 不需要总记录数:第三个参数可以传入false,这样每次查询就不会发出count()统计总记录数,避免性能损耗。
* 年龄大于等于26岁的
*/
@Test
public void selectByPages3(){
//QueryWrapper<User> query = Wrappers.<User>query();
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//列名(不是实体类中的属性名)
userQueryWrapper.ge("age", 26);
//分页信息 (当前页码,每页条数)
Page<User> page = new Page<>(1, 2,false);
IPage<Map<String, Object>> iPage = userMapper.selectMapsPage(page, userQueryWrapper);
System.out.println("总页数: " + iPage.getPages()); //
System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
List<Map<String, Object>> userList = iPage.getRecords();
userList.forEach(System.out::println);
//{create_time=2019-01-11 14:20:20.0, name=大boss, id=1087982257332887553, email=boss@baomidou.com, age=40}
//{create_time=2019-02-14 08:31:16.0, manager_id=1088248166370832385, name=李艺伟, id=1088250446457389058, email=lyw@baomidou.com, age=28}
}
多表自定义查询
Mapper
import java.util.List;
public interface UserMapper extends BaseMapper<User>{
List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
IPage<User> selectUserPage(Page<User> page,@Param(Constants.WRAPPER)Wrapper<User> wrapper);
}
Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="com.soulboy.mptest01.dao.UserMapper">
<select id="selectAll" resultType="com.soulboy.mptest01.domain.User">
select * from user ${ew.customSqlSegment}
</select>
<select id="selectUserPage" resultType="com.soulboy.mptest01.domain.User">
select * from user ${ew.customSqlSegment}
</select>
</mapper>
测试方法
/**
* 自定义方法
*/
@Test
public void selectMyPage(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.ge("age", 26);
Page<User> page = new Page<>(1, 2);
IPage<User> iPage = userMapper.selectUserPage(page, userQueryWrapper);
System.out.println("总页数: " + iPage.getPages()); //
System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
List<User> userList = iPage.getRecords();
userList.forEach(System.out::println);
// 总页数: 3
// 总记录数: 5
// User(id=1087982257332887553, name=大boss, age=40, email=boss@baomidou.com, managerId=null, createTime=2019-01-11T14:20:20)
// User(id=1088250446457389058, name=李艺伟, age=28, email=lyw@baomidou.com, managerId=1088248166370832385, createTime=2019-02-14T08:31:16)
}
Update
/**
* updateById
* 属性为null的字段不会参与Set操作
*/
@Test
public void updateByIds(){
User user = new User();
user.setId(1088248166370832385L);
user.setAge(26);
user.setEmail("wtf2@baomidou.com");
int rows = userMapper.updateById(user);
System.out.println("影响记录数: "+ rows);
// DEBUG==> Preparing: UPDATE user SET email=?, age=? WHERE id=?
// DEBUG==> Parameters: wtf2@baomidou.com(String), 26(Integer), 1088248166370832385(Long) DEBUG<== Updates: 1
// 影响记录数: 1
}
/**
* updateById
* 属性为null的字段不会参与Set操作
*/
@Test
public void updateByWrapper(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "李艺伟").eq("age", 28);
User user = new User();
user.setEmail("lyw2019@baomidou.com");
user.setAge(29);
int rows = userMapper.update(user, updateWrapper);
System.out.println("影响记录数: "+ rows);
// DEBUG==> Preparing: UPDATE user SET email=?, age=? WHERE (name = ? AND age = ?)
// DEBUG==> Parameters: lyw2019@baomidou.com(String), 29(Integer), 李艺伟(String), 28(Integer)
// DEBUG<== Updates: 1
// 影响记录数: 1
}
/**
* 当只需要更新少数字段的时候,创建实体类则会显得比较麻烦。
* 可以在Wrapper后面使用链式调用增加新的设置条件
*/
@Test
public void updateByWrapper2(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "李艺伟").eq("age", 29).set("age",30);
int rows = userMapper.update(null, updateWrapper);
System.out.println("影响记录数: "+ rows);
// DEBUG==> Preparing: UPDATE user SET age=? WHERE (name = ? AND age = ?)
// DEBUG==> Parameters: 30(Integer), 李艺伟(String), 29(Integer)
// DEBUG<== Updates: 1
// 影响记录数: 1
}
/**
* lambdaUpdate
*/
@Test
public void updateByLambda(){
LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.<User>lambdaUpdate();
lambdaUpdateWrapper.eq(User::getName, "李艺伟").eq(User::getAge, 30).set(User::getAge, 31);
int rows = userMapper.update(null, lambdaUpdateWrapper);
System.out.println("影响记录数: " + rows);
}
/**
* lambdaUpdate
*/
@Test
public void updateByLambdaChain(){
//是否更新成功:大于0会返回true,否则会返回false
boolean status = new LambdaUpdateChainWrapper<User>(userMapper)
.eq(User::getName, "李艺伟").eq(User::getAge, 31).set(User::getAge, 32).update();
System.out.println("更新是否成功: " + status);
// DEBUG==> Preparing: UPDATE user SET age=? WHERE (name = ? AND age = ?)
// DEBUG==> Parameters: 32(Integer), 李艺伟(String), 31(Integer)
// DEBUG<== Updates: 1
// 更新是否成功: true
}
Delete
/**
* deleteById
* 根据主键删除
*/
@Test
public void deleteById(){
int rows = userMapper.deleteById(1178959693804662786L);
System.out.println("删除记录数: " + rows);
}
/**
* deleteByMap
* 条件删除
*/
@Test
public void deleteByMap(){
HashMap<String, Object> columMap = new HashMap<>();
columMap.put("name", "斑秃");
columMap.put("age", 39);
int rows = userMapper.deleteByMap(columMap);
System.out.println("删除记录数: " + rows);
// DEBUG==> Preparing: DELETE FROM user WHERE name = ? AND age = ?
// DEBUG==> Parameters: 斑秃(String), 39(Integer)
// DEBUG<== Updates: 1
// 删除记录数: 1
}
/**
* deleteBatchIds
* 批量删除
*/
@Test
public void deleteBatchIds(){
int rows = userMapper.deleteBatchIds(Arrays.asList(1094592041087729666L, 1088250446457389058L));
System.out.println("删除记录数: " + rows);
}
/**
* deleteByWrapper
* 根据条件包装器封装删除条件
*/
@Test
public void deleteByWrapper(){
LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.<User>lambdaQuery();
userLambdaQueryWrapper.eq(User::getAge, 27).or().gt(User::getAge, 39);
int rows = userMapper.delete(userLambdaQueryWrapper);
System.out.println("删除记录数: " + rows);
}
ActiiveRecord
Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。简而言之就是可以通过模型了进行 CRUD。
- 实体类继续 Model 抽象类
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;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
//@TableName("mp_user")
public class User extends Model<User> {
private static final long serialVersionUID = 1L;
@TableId
private Long id;
// @TableField("name")
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
//private transient String remark;//备注(在数据库中没有对应的字段)
}
- Mapper 类需要继承 BaseMapper 接口
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.soulboy.mptest01.domain.User;
import org.apache.ibatis.annotations.Param;
//import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper extends BaseMapper<User>{
List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
IPage<User> selectUserPage(Page<User> page,@Param(Constants.WRAPPER)Wrapper<User> wrapper);
}
测试
/**
* 新增方法
*/
@Test
public void insertAR(){
User user = new User();
user.setName("大斑秃");
user.setAge(35);
user.setEmail("datutu@bantu.com");
user.setManagerId(1088248166370832385L);
user.setCreateTime(LocalDateTime.now());
int rows = userMapper.insert(user);
System.out.println("插入是否成功: "+ rows); //插入成功返回true
// DEBUG==> Preparing: INSERT INTO user ( id, create_time, name, manager_id, email, age ) VALUES ( ?, ?, ?, ?, ?, ? )
// DEBUG==> Parameters: 1181047125463207937(Long), 2019-10-07T11:22:31.581(LocalDateTime), 大斑秃(String), 1088248166370832385(Long), datutu@bantu.com(String), 35(Integer)
// DEBUG<== Updates: 1
}
/**
* 查询方法
* 查询出来的是新对象user1,没有把值设置到user中,user是空对象。
* selectById()
*/
@Test
public void selectByIdAR(){
User user = new User();
User user1 = user.selectById("1181047125463207937L");
System.out.println(user1);
}
/**
* 查询方法
* 查询出来的是新对象user1,没有把值设置到user中,user是空对象。
* selectById()
*/
@Test
public void selectByIDAR2(){
User user = new User();
user.setId(1181047125463207937L);
User user1 = user.selectById();
System.out.println(user1);
}
/**
* 更新方法
* updateById()
*/
@Test
public void updateByIdAR(){
User user = new User();
user.setId(1181047125463207937L);
user.setName("小斑秃");
boolean status = user.updateById();
System.out.println("是否更新成功: " + status);
}
/**
* 删除方法:删除不存在的逻辑上属于成功
* deleteById
*/
@Test
public void deleteByIdAR(){
User user = new User();
user.setId(1181047125463207937L);
boolean status = user.deleteById();
System.out.println("是否删除成功: " + status);
}
/**
* 如果不设置ID就是插入;如果设置ID会根据ID查询数据库,如果存在执行update,不存在依然执行insert
*/
@Test
public void insertOrUpdate(){
User user = new User();
//user.setId(xxx); //如果设置ID会根据ID查询数据库,如果存在执行update,不存在依然执行insert
user.setName("张强");
user.setAge(28);
user.setEmail("zq@baomidou.com");
user.setManagerId(1088248166370832385L);
user.setCreateTime(LocalDateTime.now());
boolean status = user.insertOrUpdate();
System.out.println("插入or更新是否成功: " + status);
}
主键策略
策略生效需要修改为表的主键为自增
alter table user modify id bigint(20) auto_increment primary key;
局部主键策略实现
import com.baomidou.mybatisplus.annotation.IdType;
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;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
//@TableName("mp_user")
public class User extends Model<User> {
private static final long serialVersionUID = 1L;
//@TableId(type = IdType.NONE) //无主键策略 需要自己生成主键
//以下三种算法只有当主键ID为空才会自动填充
//@TableId(type = IdType.ID_WORKER) //雪花算法
//@TableId(type = IdType.ID_WORKER_STR) //雪花算法字符串类型
//@TableId(type = IdType.UUID) //数据库中主键类型需要改为varchar 实体类中的属性类型需要改为String
//@TableId
private Long id;
// @TableField("name")
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
//private transient String remark;//备注(在数据库中没有对应的字段)
}
全局主键策略实现
application.yml 局部的@TableId(type = IdType.UUID) 可以注释掉
如果全局策略和局部策略都设置了,局部策略优先级更高。
mybatis-plus:
mapper-locations: classpath:com/soulboy/mptest01/mapper/*.xml
global-config:
db-config:
id-type: uuid
MP 配置
通用 Service
UserService 接口(需要继承 IService
import com.baomidou.mybatisplus.extension.service.IService;
import com.soulboy.mptest01.domain.User;
public interface UserService extends IService<User> {
}
UserServiceImpl 实现类 (需要继承 ServiceImpl<TMapper,T> 并且实现 UserService )
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.soulboy.mptest01.dao.UserMapper;
import com.soulboy.mptest01.domain.User;
import com.soulboy.mptest01.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
通用 Service
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.soulboy.mptest01.domain.User;
import com.soulboy.mptest01.service.UserService;
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.sql.Wrapper;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
@Autowired
private UserService userService;
@Test
public void getOne(){
User one = userService.getOne(Wrappers.<User>lambdaQuery().gt(User::getAge, 25),false);
System.out.println(one);
}
/**
* saveBatch
*/
@Test
public void Batch(){
User user1 = new User();
user1.setName("爱因斯坦1");
user1.setAge(28);
User user2 = new User();
user2.setName("爱因斯坦2");
user2.setAge(29);
List<User> userList = Arrays.asList(user1, user2);
boolean status = userService.saveBatch(userList);//成功则返回true
System.out.println(status);
// DEBUG==> Preparing: INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? )
// DEBUG==> Parameters: 1181459698335604738(Long), 爱因斯坦1(String), 28(Integer)
// DEBUG==> Parameters: 1181459698494988289(Long), 爱因斯坦2(String), 29(Integer)
// true
}
/**
* saveOrUpdateBatch
*/
@Test
public void BatchMix(){
User user1 = new User();
user1.setName("爱因斯坦4");
user1.setAge(31);
User user2 = new User();
user2.setId(1181459698494988289L);
user2.setName("爱因斯坦2");
user2.setAge(32);
List<User> userList = Arrays.asList(user1, user2);
boolean status = userService.saveOrUpdateBatch(userList);//成功则返回true
System.out.println(status);
}
/**
* lambdaQuery()
* 条件查询
*/
@Test
public void BatchChain(){
List<User> userList = userService.lambdaQuery().gt(User::getAge, 25).like(User::getName, "雨").list();
userList.forEach(System.out::println);
}
/**
* lambdaUpdate()
* 条件更新
*/
@Test
public void BatchChain2(){
boolean status = userService.lambdaUpdate().eq(User::getAge, 25).set(User::getAge, 26).update();
System.out.println(status);
}
/**
* lambdaUpdate().where().remove()
* 条件删除
*/
@Test
public void BatchChain3(){
boolean status = userService.lambdaUpdate().eq(User::getAge, 24).remove();
System.out.println(status);
}
}