一、MyBatis-Plus简介
MyBatis-Plus (简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为 简化开发、提高效率而生。Mybatis-Plus提供了通用的Mapper和Service,可以在不编写任何SQL语句的前提下,快速的实现单表的增删改查(CURD),批量,逻辑删除,分页等操作。只要把MyBatis-Plus的特性到优秀插件,以及多数据源的配置进行详细讲解。
特性:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出 慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
代码及文档地址
官方地址: Redirect
代码发布地址:
Github: GitHub - baomidou/mybatis-plus: An powerful enhanced toolkit of MyBatis for simplify development
Gitee: mybatis-plus: mybatis 增强工具包,简化 CRUD 操作。 文档 http://baomidou.com低代码组件库 http://aizuda.com
文档发布地址: 简介 | MyBatis-Plus
二、入门案例
1、创建数据库和表
CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTERSET utf8mb4 */; use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名 ',
`age` int(11) DEFAULT NULL COMMENT '年龄 ',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱 ',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),(5, 'Billie', 24, 'test5@baomidou.com');
2、创建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 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.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qcby</groupId>
<artifactId>mybatisPlus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatisPlus</name>
<description>mybatisPlus</description>
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、idea中安装lombok插件
4、编写代码
①编写application.xml文件(配置连接数据库信息和日志信息)
spring:
# 配置数据源信息
datasource:
# 配置数据源类型 spring默认的连接池
type: com.zaxxer.hikari.HikariDataSource
# 配置连接数据库信息
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
username: root
password: 123456
# 配置MyBatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
注意:
1、驱动类driver-class-name
spring boot 2.0(内置jdbc5驱动),驱动类使用:
driver-class-name: com.mysql.jdbc.Driver
spring boot 2.1及以上(内置jdbc8驱动),驱动类使用:
driver-class-name: com.mysql.cj.jdbc.Driver
否则运行测试用例的时候会有WARN 信息
2、连接地址url
MySQL5.7版本的url:
jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
MySQL8.0版本的url:
jdbc:mysql://localhost:3306/mybatis_plus?
serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
否则运行测试用例报告如下错误:
java.sql.SQLException:The server timezone value'Öйú±ê׼ʱ¼ä' is unrecognized or representsmore
②在Spring Boot启动类中添加@MapperScan注解,扫描mapper包
@SpringBootApplication
@MapperScan("com.qcby.mybatisplus.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
③添加实体
package com.qcby.mybatisplus.model;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//Alt+7 查看类的所有方法和属性
@Data //lombok注解
@AllArgsConstructor //有参构造器
@NoArgsConstructor //无参构造器
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
④添加mapper
BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的 实体类型
/**
* 继承baseMapper中的方法
* 共19个方法,9个跟条件构造无关
*/
@Repository //用于标识数据访问对象
public interface UserMapper extends BaseMapper<User> {
}
@Repository注解的主要功能和特点包括:
- 异常转换:Spring框架会自动将DAO层抛出的运行时异常转换为DataAccessException,这是一种Spring特有的运行时异常,用于封装底层数据库访问错误。这有助于上层应用程序或服务层处理这些异常,而不需要了解底层数据访问的详细错误。
- 事务管理:通过Spring的事务管理支持,你可以为使用@Repository注解的类中的方法自动添加事务支持。这意味着你可以在这些方法上添加@Transactional注解,以便自动进行事务管理,例如开始事务、提交或回滚事务。
- 组件扫描:@Repository注解使得Spring框架能够通过组件扫描(component scanning)自动检测到该类,并将其注册为Spring应用上下文中的一个bean。这样,你就可以在其他地方通过自动装配(autowiring)来注入和使用这个DAO。
- 集成测试:在集成测试中,你可以使用@Repository注解来模拟或替换实际的数据库访问逻辑,以便在测试环境中更容易地控制数据访问行为
⑤测试
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectList(){
//selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
//userMapper.selectList(null).forEach(System.out::println);
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
三、基本CRUD操作(创建(Create)、读取(Retrieve)、更新(Update)和删除(Delete))
1、BaseMapper
BaseMapper中的方法,大多方法中都有Wrapper类型的形参,此为条件构造器,可针 对于SQL语句设置不同的条件,若没有条件,则可以为该形参赋值null,即查询(删除/修改)所 有数据
MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,我们可以直接使用,接口如 下:
package com.baomidou.mybatisplus.core.mapper;
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
* @param entity 实体对象 */
int insert(T entity);
/**
* 根据 ID 删除
* @param id 主键ID */
int deleteById(Serializable id);
/**
* 根据实体(ID)删除
* @param entity 实体对象
* @since 3.4.4 */
int deleteById(T entity);
/**
* 根据 columnMap 条件,删除记录
* @param columnMap 表字段 map 对象 */
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where
语句)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 删除(根据ID 批量删除)
* @param idList 主键ID列表(不能为 null 以及 empty) */
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 根据 ID 修改
* @param entity 实体对象 */
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
* @param entity 实体对象 (set 条件值 ,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成
where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
* @param id 主键ID */
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
* @param idList 主键ID列表(不能为 null 以及 empty) */
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 查询(根据 columnMap 条件)
* @param columnMap 表字段 map 对象 */
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,查询一条记录
*<p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录 , 注意:多条数据会报异常 </p>
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) { List<T> ts = this.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(ts)) {
if (ts.size() != 1) {
throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
}
return ts.get(0);
}
return null;
}
/**
* 根据 Wrapper 条件,查询总记录数
* @param queryWrapper 实体对象封装操作类(可以为 null) */
Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null) */
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* @param queryWrapper 实体对象封装操作类(可以为 null) */
List<Map<String, Object>>selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
* @param queryWrapper 实体对象封装操作类(可以为 null) */
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null) */
<P extends IPage<T>>P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类 */
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
2、插入
@Test
public void testInsert(){
User user = new User(null, "张三", 23, "zhangsan@atguigu.com",0);
//INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
int result = userMapper.insert(user);
System.out.println("受影响行数:"+result);
//1765621679929593857
System.out.println("id自动获取:"+user.getId());
}
最终执行的结果,所获取的id为1765621679929593857
这是因为MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id
3、删除
①通过id删除记录
@Test
public void testDeleteById(){
//通过id删除用户信息
//DELETE FROM user WHERE id=?
int result = userMapper.deleteById(1765621679929593857L);
System.out.println("受影响行数:"+result);
}
②通过id批量删除记录
@Test
public void testDeleteBatchIds(){
//通过多个id批量删除
//DELETE FROM user WHERE id IN ( ? , ? , ? )
List<Long> idList = Arrays.asList(1L, 2L, 3L);
int result = userMapper.deleteBatchIds(idList);
System.out.println("受影响行数:"+result);
}
③通过map条件删除记录
@Test
public void testDeleteByMap(){
//根据map集合中所设置的条件删除记录
//DELETE FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 23);
map.put("name", "张三");
int result = userMapper.deleteByMap(map);
System.out.println("受影响行数:"+result);
}
4、修改
@Test
public void testUpdateById(){
User user = new User(4L, "admin", 22, null,0);
//UPDATE user SET name=?, age=? WHERE id=?
int result = userMapper.updateById(user);
System.out.println("受影响行数:"+result);
}
5、查询
①根据id查询用户信息
@Test
public void testSelectById(){
//根据id查询用户信息
//SELECT id,name,age,email FROM user WHERE id=?
User user = userMapper.selectById(4L);
System.out.println(user);
}
②根据多个id查询多个用户信息
@Test
public void testSelectBatchIds(){
//根据多个id查询多个用户信息
//SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
List<Long> idList = Arrays.asList(4L, 5L);
List<User> list = userMapper.selectBatchIds(idList);
list.forEach(System.out::println);
}
③通过map条件查询用户信息
@Test
public void testSelectByMap(){
//通过map条件查询用户信息
//SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 22);
map.put("name", "admin");
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
④查询所有数据
@Test
public void testSelectList(){
//selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
//查询所有用户信息
//SELECT id,name,age,email FROM user
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
6、通用Service
官网地址: CRUD 接口 | MyBatis-Plus A3
①MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑
②创建Service接口和实现类
/**
* UserService继承IService模板提供的基础功能
*/
@Repository
public interface UserService extends IService<User> {
}
/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现
* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implementsUserService {
}
③测试查询记录数
@Autowired
private UserService userService;
@Test
public void testGetCount(){
long count = userService.count();
System.out.println("总记录数:" + count);
}
④测试批量插入
@Test
public void testSaveBatch(){
// SQL长度有限制,海量数据插入单条SQL无法实行,
// 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("ybc" + i);
user.setAge(20 + i);
users.add(user);
}
//SQL:INSERT INTO user ( name, age ) VALUES ( ?, ? )
userService.saveBatch(users);
}
四、常用注解
1、@TableName:用于指定实体类(Java Bean)与数据库中的表之间的映射关系。
MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决定,且默认操作的表名和实体类型的类名一致,如果表名与实体类名不一致,有两种解决方法。假如数据库表名为t_user,实体类名为user
①添加@TableName注解
@TableName("t_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
②通过全局配置解决问题
如果实体类所对应的表都有固定的前缀,例如t_或tb_此时,可以使用MyBatis-Plus提供的全局配置,为实体类所对应的表名设置默认的前缀,那么就不需要在每个实体类上通过@TableName标识实体类对应的表
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
2、@TableId:用于标识实体类中的主键属性,并与数据库表的主键列进行映射
MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认基于雪花算法的策略生成id
①默认主键策略
如果数据库表的主键是自增的,并且 Java 类的属性名与数据库表的列名一致,你可以简单地使用 @TableId 注解,无需指定任何属性。
②指定主键生成策略
你可以使用 type 属性来指定主键的生成策略。IdType.ASSIGN_ID (默 认),基于雪花算法的策略生成数据id,与数据库id是否设置自增无关; IdType.AUTO 表示主键自增,使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,否则无效。
public class User {
@TableId(value="uid",type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
③指定列名
如果 Java 类的属性名与数据库表的列名不一致,你可以使用 value 属性来指定数据库中的列名。
④配置全局主键策略
mybatis-plus:
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
# 配置MyBatis-Plus的主键策略
id-type: auto
3、@TableField:用于标识实体类中的非主键属性与数据库表中的列之间的映射关系。
当实体类的属性名与数据库表的列名不一致时,可以使用这个注解来指定正确的映射关系。
①实体类的驼峰命名与数据表的下划线会自动转化
若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格例如实体类属性userName,表中字段user_name此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格相当于在MyBatis中配置
②指定列名
当 Java 类的属性名与数据库表的列名不一致时,可以使用 value 属性来指定正确的列名。
public class User {
private Long id;
@TableField("user_name")
private String name;
// ... 其他属性和方法 ...
}
③排除某个字段
如果不希望某个字段被 MyBatis-Plus 自动映射到数据库的列,可以使用 exist 属性设置为 false。
public class User {
private Long id;
@TableField(exist = false)
private transient String tempField; // 用于临时存储,不映射到数据库列
// ... 其他属性和方法 ...
}
④控制字段的插入和更新行为
可以使用 insertStrategy 和 updateStrategy 属性来控制字段在插入和更新操作时的行为。例如,你可以设置某个字段在插入时忽略,但在更新时包含。
public class User {
private Long id;
@TableField(insertStrategy = FieldStrategy.IGNORED)
private String createTime; // 插入时忽略此字段
// ... 其他属性和方法 ...
}
⑤填充策略
可以使用 fill 属性配合 MyBatis-Plus 的 MetaObjectHandler 来实现自动填充策略,比如自动填充创建时间和更新时间。
public class User {
private Long id;
@TableField(fill = FieldFill.INSERT)
private Date createTime; // 插入时自动填充
@TableField(fill = FieldFill.UPDATE)
private Date updateTime; // 更新时自动填充
// ... 其他属性和方法 ...
}
4、@TableLogic:用于实现逻辑删除的功能
逻辑删除并非真正从数据库中删除数据,而是在数据表中添加一个标识字段(通常为 deleted 或 is_deleted),通过修改这个标识字段的值来表示数据是否被“删除”。这样做的好处是保留了数据,同时又能够通过查询条件方便地排除掉被“删除”的数据。
public class User {
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic
private Integer isDeleted;
}
默认情况下,MyBatis-Plus 会认为 deleted 字段的值为 1 表示记录已被逻辑删除,值为 0 表示记录未被删除。但你也可以通过 @TableLogic 注解的 value 和 delval 属性来自定义这些值。
import com.baomidou.mybatisplus.annotation.TableLogic;
public class User {
private Long id;
private String name;
// 逻辑删除字段,当值为true时表示该记录已被逻辑删除,否则表示未删除
@TableLogic(value = "is_deleted", delval = "true")
private Boolean isDeleted;
// ... 其他属性和方法 ...
}