【Spring Boot】010-Spring Boot整合Mybatis
https://blog.csdn.net/qq_29689343/article/details/108621835
MyBatis Plus 为简化开发而生!
MyBatis Plus(简称 MyBatis-Plus 或 MP)是 MyBatis 的增强工具包,它在 MyBatis 的基础上提供了很多便捷的功能,简化了开发过程。
只做增强,不做改变、效率至上,功能丰富。
官网:https://baomidou.com/
github:https://github.com/baomidou/mybatis-plus
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.3.2version>
dependency>
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.17version>
<relativePath/>
parent>
<groupId>com.zibogroupId>
<artifactId>study-mpartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>study-mpname>
<description>study-mpdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.3.2version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-base:latestbuilder>
image>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
project>
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
CREATE TABLE `student` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名字',
`age` int unsigned NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='学生';
INSERT INTO `study`.`student`(`id`, `name`, `age`) VALUES (1, '訾博', 27);
INSERT INTO `study`.`student`(`id`, `name`, `age`) VALUES (2, 'zibo', 25);
INSERT INTO `study`.`student`(`id`, `name`, `age`) VALUES (3, 'zb', 23);
MyBatisPlus 拥有非常优秀的单表 CRUD 基础能力,而这个能力需要在实体类上做一些改动。通过标注 @TableName
注解,相当于告诉 MyBatisPlus 当前这个 Student
类要映射到 student
表(默认表名策略是驼峰转下划线);通过给 id
属性标注 @TableId
注解,并声明 ID 类型为 auto ,相当于适配 MySQL 中的自增主键。其他属性与数据库中映射均一致,就不再需要添加新注解了。
package com.zibo.studymp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("student")
public class Student {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer age;
}
MyBatisPlus 的单表 CRUD 能力来自一个内置的基础接口 BaseMapper
,通过继承 BaseMapper
并注明实体类的泛型类型,即可拥有单表的 CRUD 能力。
package com.zibo.studymp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zibo.studymp.entity.Student;
public interface StudentMapper extends BaseMapper<Student> {
Student getByName(String name);
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zibo.studymp.mapper.StudentMapper">
<select id="getByName" resultType="com.zibo.studymp.entity.Student">
SELECT *
FROM `student`
WHERE `name` = #{name}
select>
mapper>
@MapperScan({“com.zibo.**.mapper”})
package com.zibo.studymp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan({"com.zibo.**.mapper"})
public class StudyMpApplication {
public static void main(String[] args) {
SpringApplication.run(StudyMpApplication.class, args);
}
}
package com.zibo.studymp;
import com.zibo.studymp.entity.Student;
import com.zibo.studymp.mapper.StudentMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class StudyMpApplicationTests {
@Autowired
private StudentMapper studentMapper;
@Test
void contextLoads() {
Student student = studentMapper.selectById(1);
System.out.println(student);
System.out.println("==================================");
student = studentMapper.getByName("zibo");
System.out.println(student);
}
}
Student(id=1, name=訾博, age=27)
==================================
Student(id=2, name=zibo, age=25)
MyBatisPlus 提供的重要基础能力,就是替我们开发者实现了基本的单表 CRUD 操作,我们在编写具体的业务模块时,单表的 CRUD 可以完全不需要编写了,仅需要继承 BaseMapper
接口,该 Mapper 接口就可以自动拥有单表 CRUD 的能力。
// 插入一条记录
int insert(T entity);
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
Wrapper 是 MyBatisPlus 编程式查询、修改数据的重要特性,这种特性类似于 Hibernate 中的 Criteria 机制(也就是 QBC 查询)。MyBatis 提供的 Wrapper 机制拥有对单表查询的灵活条件构造、投影查询、聚合查询等能力。下面通过几个简单示例来了解 Wrapper 的使用。
@Test
void test01() {
// wrapper 基本使用
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.eq("name", "zibo");
Student student = studentMapper.selectOne(wrapper);
System.out.println(student);
}
为了更容易维护可能变化的实体模型类属性,MyBatisPlus 提供了 LambdaWrapper ,使用这种类型的 Wrapper 将属性的字符串变量改为 Lambda 表达式,以此实现代码的高可维护性。
@Test
void test02() {
// lambda 式使用
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Student::getName, "zibo");
Student student = studentMapper.selectOne(wrapper);
System.out.println(student);
// lambda 式使用:链式调用
String name = "訾博";
student = studentMapper
.selectOne(new LambdaQueryWrapper<Student>()
.eq(StringUtils.isNotBlank(name), Student::getName, name)
);
System.out.println(student);
}
如果一个表的列特别多,而我们只需要查其中几列数据时,投影查询就显得非常重要了,通过指定需要查询的列,来达到节省数据库流量带宽的目的。
测试代码
@Test
void test03() {
// 投影查询
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Student::getName, "zibo");
wrapper.select(Student::getName, Student::getAge);
Student student = studentMapper.selectOne(wrapper);
System.out.println(student);
}
查询结果
Student(id=null, name=zibo, age=25)
对于单表查询来讲,聚合查询也是一个常见的查询场景。虽然 MyBatisPlus 没有对几种聚合函数提供 API 的定义,不过我们可以传入 SQL 片段来曲线实现聚合查询。
测试代码
@Test
void test04() {
// 聚合查询
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.select("max(age) as age");
Student student = studentMapper.selectOne(wrapper);
System.out.println(student);
}
执行结果
Student(id=null, name=null, age=27)
MyBatisPlus 考虑到我们在项目开发中可能会用到的几种主键类型,它给予了一些基础实现和配置:
默认情况下,MyBatisPlus 使用的主键策略是使用了雪花算法的 ASSIGN_ID 策略。
下面简单了解 MyBatisPlus 中的两个简单实用的特性。
逻辑删除是代替 delete 物理删除的一种更适合项目开发的数据删除机制,它通过设置一个特殊的标志位,将需要删除的数据设置为“不可见”,并在每次查询数据时只查询标志位数据值为“可见”的数据,这样的设计即是逻辑删除。MyBatisPlus 使用逻辑删除非常简单,只需要两步即可。
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
package com.zibo.studymp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("student")
public class Student {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer age;
@TableLogic
private Integer deleted;
}
@Test
void deleteTest() {
// 删除
int result = studentMapper.deleteById(1);
System.out.println(result);
}
乐观锁是高并发下控制的手段,它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。换句话说,乐观锁希望一条即将被更新的数据,没有被其他人操作过。
乐观锁的实现方式如下:
MyBatisPlus 中实现的乐观锁机制是通过插件实现。使用乐观锁需要以下两个步骤:
@Configuration(proxyBeanMethods = false)
public class MyBatisPlusConfiguration {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
@Version
注解package com.zibo.studymp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
@TableName("student")
public class Student {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer age;
@TableLogic
private Integer deleted;
@Version
private Integer version;
}
以此法编写完毕后,在 student
表的单表数据操作时,乐观锁就会介入处理。
需要注意的是,MyBatisPlus 支持的乐观锁,可以对以下的数据类型予以支持:
- int long
- Integer Long
- Date Timestamp LocalDateTime
分页查询是项目开发中非常常见的业务场景,对于 MyBatis 的分页插件而言可能之前比较常见的是 PageHelper ,MyBatisPlus 已经考虑到了分页查询的场景,它提供了一个专门用于分页的插件,通过简单的配置就可以使用分页的特性。
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(new MySqlDialect()));
return interceptor;
}
在进行分页查询时,需要传入 IPage
对象,并返回 IPage
模型或 List
集合。以下是几个示例。
/**
* 使用IPage作为入参和返回值
* @param query
* @return
*/
IPage<Student> page(IPage<Student> query);
/**
* 使用集合作为返回值
* @param query
* @return
*/
List<Student> pageList(IPage<Student> query);
/**
* 使用IPage和其他参数共同作为入参
* @param page
* @param params
* @return
*/
IPage<Student> pageParams(@Param("page") IPage<Student> page, @Param("params") Map<String, Object> params);