SpringBoot整合MyBatis-Plus:从入门到精通
mhr18 2025-06-03 23:59 8 浏览 0 评论
一、MyBatis-Plus基础介绍
1.1 MyBatis-Plus核心概念
MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。它具有以下特性:
特性 | 说明 | 优势 |
无侵入 | 只做增强不做改变,引入它不会对现有工程产生影响 | 可以平滑地切换到MyBatis-Plus |
损耗小 | 启动即会自动注入基本CRUD操作 | 基本CRUD操作无需再手动编写 |
强大的CRUD操作 | 内置通用Mapper、通用Service | 少量配置即可实现大部分CRUD操作 |
支持Lambda形式调用 | 通过Lambda表达式,方便的编写各类查询条件 | 代码更简洁,更易维护 |
支持主键自动生成 | 支持多达4种主键策略(内含分布式唯一ID生成器) | 解决分布式系统ID生成问题 |
支持ActiveRecord模式 | 支持ActiveRecord形式调用,实体类只需继承Model类即可进行CRUD操作 | 更简单的CRUD实现方式 |
内置代码生成器 | 采用代码或Maven插件可快速生成Mapper、Model、Service、Controller层代码 | 减少约80%的编码工作 |
内置分页插件 | 基于MyBatis物理分页,开发者无需关心具体操作 | 配置好插件后,写分页等同于普通List查询 |
内置性能分析插件 | 可输出SQL语句以及其执行时间 | 能快速定位慢查询 |
内置全局拦截插件 | 提供全表delete、update操作智能分析阻断 | 预防误操作 |
内置SQL注入剥离器 | 支持SQL注入剥离,有效预防SQL注入攻击 | 系统更安全 |
1.2 MyBatis-Plus与MyBatis的关系
MyBatis-Plus并不是要取代MyBatis,而是在MyBatis的基础上提供更多便利的功能。它们之间的关系可以用以下图示表示:
MyBatis-Plus保留了MyBatis的所有特性,同时添加了以下增强功能:
- 自动生成基本CRUD操作
- 条件构造器简化复杂查询
- 多种主键生成策略
- ActiveRecord模式支持
- 内置分页、性能分析等插件
1.3 MyBatis-Plus适用场景
MyBatis-Plus特别适合以下开发场景:
- 需要快速开发的Web应用
- 需要大量标准CRUD操作的系统
- 需要简化MyBatis配置的项目
- 需要统一代码风格和规范的团队
- 需要快速生成基础代码的原型项目
二、SpringBoot整合MyBatis-Plus基础配置
2.1 环境准备与依赖引入
在开始整合之前,我们需要准备以下环境:
- JDK 1.8+
- Maven 3.0+
- SpringBoot 2.x
- 数据库(MySQL 5.7+)
在pom.xml中添加必要的依赖:
<!-- SpringBoot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.2 数据库配置
在application.yml中配置数据库连接信息:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mp_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
# MyBatis-Plus配置
mybatis-plus:
configuration:
# 日志实现
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 下划线转驼峰
map-underscore-to-camel-case: true
global-config:
db-config:
# 主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: auto
# 逻辑删除配置
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为0)
2.3 实体类与Mapper创建
2.3.1 创建基础实体类
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
@Data
@TableName("user") // 指定表名,如果表名与类名一致(忽略大小写),可以省略
public class User {
/**
* 主键
* @TableId 注解用于标识主键
* type = IdType.AUTO 表示数据库ID自增
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 年龄
*/
private Integer age;
/**
* 邮箱
*/
private String email;
/**
* 创建时间
* @TableField 注解用于标识非主键字段
* fill = FieldFill.INSERT 表示插入时自动填充
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新时间
* fill = FieldFill.INSERT_UPDATE 表示插入和更新时自动填充
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 逻辑删除字段
* value 未删除的值
* delval 已删除的值
*/
@TableLogic
private Integer deleted;
}
2.3.2 创建Mapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
/**
* UserMapper接口
* 继承BaseMapper<T>即可获得基本的CRUD功能
* 无需编写mapper.xml文件
*/
public interface UserMapper extends BaseMapper<User> {
// 可以在这里添加自定义的接口方法
// 如果需要自定义SQL,可以在resources/mapper/UserMapper.xml中编写
}
2.3.3 添加Mapper扫描
在SpringBoot启动类上添加@MapperScan注解:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描Mapper接口所在的包
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2.4 自动填充功能实现
MyBatis-Plus提供了自动填充功能,可以在插入或更新时自动填充某些字段(如创建时间、更新时间等)。实现步骤如下:
- 创建元对象处理器:
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 自动填充处理器
*/
@Component // 不要忘记加@Component注解
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入时的填充策略
* @param metaObject 元对象
*/
@Override
public void insertFill(MetaObject metaObject) {
// 根据属性名称设置填充值
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
}
/**
* 更新时的填充策略
* @param metaObject 元对象
*/
@Override
public void updateFill(MetaObject metaObject) {
// 根据属性名称设置填充值
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}
三、MyBatis-Plus基础CRUD操作
3.1 插入操作
MyBatis-Plus提供了多种插入方法:
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
public class InsertTest {
@Autowired
private UserMapper userMapper;
/**
* 插入一条记录
*/
@Test
public void testInsert() {
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
user.setAge(20);
user.setEmail("zhangsan@example.com");
// insert方法会返回影响的行数
int rows = userMapper.insert(user);
System.out.println("影响行数:" + rows);
// 插入后,自动生成的主键会回填到实体对象中
System.out.println("自动生成的主键ID:" + user.getId());
}
/**
* 批量插入
*/
@Test
public void testBatchInsert() {
List<User> userList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setUsername("用户" + i);
user.setPassword("pwd" + i);
user.setAge(20 + i);
user.setEmail("user" + i + "@example.com");
userList.add(user);
}
// 循环插入
for (User user : userList) {
userMapper.insert(user);
}
// 或者使用Service层的saveBatch方法(后面会介绍)
}
}
3.2 更新操作
MyBatis-Plus提供了多种更新方法:
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
@SpringBootTest
public class UpdateTest {
@Autowired
private UserMapper userMapper;
/**
* 根据ID更新
*/
@Test
public void testUpdateById() {
// 先查询出要更新的实体
User user = userMapper.selectById(1L);
// 修改字段值
user.setUsername("张三修改后");
user.setAge(25);
// 根据ID更新,null值字段不会更新
int rows = userMapper.updateById(user);
System.out.println("影响行数:" + rows);
}
/**
* 条件更新
*/
@Test
public void testUpdate() {
// 方法一:使用UpdateWrapper
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("username", "张三")
.set("age", 30)
.set("email", "newemail@example.com");
int rows = userMapper.update(null, updateWrapper);
System.out.println("影响行数:" + rows);
// 方法二:使用LambdaUpdateWrapper(推荐)
User user = new User();
user.setAge(35);
int rows2 = userMapper.update(user,
new UpdateWrapper<User>().lambda()
.eq(User::getUsername, "张三")
);
System.out.println("影响行数:" + rows2);
}
}
3.3 删除操作
MyBatis-Plus提供了多种删除方法:
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.Arrays;
@SpringBootTest
public class DeleteTest {
@Autowired
private UserMapper userMapper;
/**
* 根据ID删除
*/
@Test
public void testDeleteById() {
int rows = userMapper.deleteById(1L);
System.out.println("影响行数:" + rows);
}
/**
* 根据ID批量删除
*/
@Test
public void testDeleteBatchIds() {
// 参数是ID的集合
int rows = userMapper.deleteBatchIds(Arrays.asList(2L, 3L, 4L));
System.out.println("影响行数:" + rows);
}
/**
* 条件删除
*/
@Test
public void testDelete() {
// 构造删除条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20);
int rows = userMapper.delete(queryWrapper);
System.out.println("影响行数:" + rows);
}
/**
* 逻辑删除
* 配置了逻辑删除后,调用delete方法实际上是更新逻辑删除字段
*/
@Test
public void testLogicDelete() {
int rows = userMapper.deleteById(5L);
System.out.println("影响行数:" + rows);
// 实际执行的是:UPDATE user SET deleted=1 WHERE id=5 AND deleted=0
}
}
3.4 查询操作
MyBatis-Plus提供了丰富的查询方法:
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@SpringBootTest
public class SelectTest {
@Autowired
private UserMapper userMapper;
/**
* 根据ID查询
*/
@Test
public void testSelectById() {
User user = userMapper.selectById(1L);
System.out.println(user);
}
/**
* 根据ID批量查询
*/
@Test
public void testSelectBatchIds() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
users.forEach(System.out::println);
}
/**
* 查询所有
*/
@Test
public void testSelectList() {
List<User> users = userMapper.selectList(null); // 参数为null表示查询所有
users.forEach(System.out::println);
}
/**
* 条件查询
*/
@Test
public void testSelectByWrapper() {
// 方法一:使用QueryWrapper
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20) // age > 20
.like("username", "张") // username like '%张%'
.isNotNull("email"); // email is not null
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
// 方法二:使用LambdaQueryWrapper(推荐)
List<User> users2 = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.gt(User::getAge, 20)
.like(User::getUsername, "张")
.isNotNull(User::getEmail)
);
users2.forEach(System.out::println);
}
/**
* 只查询部分字段
*/
@Test
public void testSelectPartialFields() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "username", "age") // 只查询这三个字段
.gt("age", 20);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
// 使用Lambda方式
List<User> users2 = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.select(User::getId, User::getUsername, User::getAge)
.gt(User::getAge, 20)
);
users2.forEach(System.out::println);
}
/**
* 查询一条记录
*/
@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", "张三");
// 查询一条记录,如果结果多于一条会抛出异常
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
/**
* 计数查询
*/
@Test
public void testSelectCount() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20);
Long count = userMapper.selectCount(queryWrapper);
System.out.println("年龄大于20的用户数:" + count);
}
/**
* 查询结果映射为Map
*/
@Test
public void testSelectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("username", "age")
.gt("age", 20);
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
}
四、条件构造器详解
4.1 条件构造器概述
MyBatis-Plus提供了强大的条件构造器,可以方便地构造各种查询条件。主要分为以下几类:
构造器类型 | 说明 |
QueryWrapper | 查询条件构造器,用于构建WHERE条件 |
UpdateWrapper | 更新条件构造器,用于构建UPDATE语句的SET和WHERE条件 |
LambdaQueryWrapper | 基于Lambda表达式的查询条件构造器,避免硬编码字段名 |
LambdaUpdateWrapper | 基于Lambda表达式的更新条件构造器,避免硬编码字段名 |
4.2 QueryWrapper常用方法
以下是QueryWrapper的常用方法及其说明:
4.3 Lambda条件构造器
Lambda条件构造器可以避免硬编码字段名,推荐使用:
@Test
public void testLambdaWrapper() {
// LambdaQueryWrapper示例
List<User> users = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.eq(User::getUsername, "张三")
.gt(User::getAge, 20)
.orderByDesc(User::getCreateTime)
);
// LambdaUpdateWrapper示例
userMapper.update(
null,
Wrappers.<User>lambdaUpdate()
.set(User::getAge, 25)
.set(User::getEmail, "newemail@example.com")
.eq(User::getUsername, "张三")
);
}
4.4 复杂条件组合
条件构造器支持复杂的条件组合:
@Test
public void testComplexCondition() {
// 复杂条件示例
List<User> users = userMapper.selectList(
new QueryWrapper<User>()
.select("id", "username", "age", "email")
.gt("age", 18)
.and(wrapper -> wrapper
.like("username", "张")
.or()
.like("username", "李")
)
.between("create_time", "2023-01-01", "2023-12-31")
.orderByDesc("age")
.orderByAsc("id")
.last("limit 10") // 直接拼接SQL语句
);
// Lambda方式
List<User> users2 = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.select(User::getId, User::getUsername, User::getAge, User::getEmail)
.gt(User::getAge, 18)
.and(wrapper -> wrapper
.like(User::getUsername, "张")
.or()
.like(User::getUsername, "李")
)
.between(User::getCreateTime, LocalDate.of(2023, 1, 1), LocalDate.of(2023, 12, 31))
.orderByDesc(User::getAge)
.orderByAsc(User::getId)
.last("limit 10")
);
}
五、MyBatis-Plus高级功能
5.1 分页查询
MyBatis-Plus提供了强大的分页功能,配置和使用步骤如下:
- 配置分页插件:
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/**
* 新的分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
- 使用分页查询:
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class PageTest {
@Autowired
private UserMapper userMapper;
/**
* 基本分页查询
*/
@Test
public void testSelectPage() {
// 参数1:当前页
// 参数2:每页显示条数
Page<User> page = new Page<>(1, 5);
// 条件构造器
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20);
// 执行分页查询
IPage<User> userPage = userMapper.selectPage(page, queryWrapper);
// 获取分页数据
System.out.println("总记录数:" + userPage.getTotal());
System.out.println("总页数:" + userPage.getPages());
System.out.println("当前页:" + userPage.getCurrent());
System.out.println("每页大小:" + userPage.getSize());
// 获取分页数据列表
List<User> users = userPage.getRecords();
users.forEach(System.out::println);
}
/**
* 自定义SQL分页查询
* 1. 在Mapper接口中定义方法
* 2. 在Mapper.xml中编写SQL
*/
@Test
public void testCustomPage() {
Page<User> page = new Page<>(1, 5);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20);
// 执行自定义分页查询
IPage<User> userPage = userMapper.selectUserPage(page, queryWrapper);
// 获取分页数据
System.out.println("总记录数:" + userPage.getTotal());
System.out.println("当前页数据:");
userPage.getRecords().forEach(System.out::println);
}
}
在Mapper接口中添加方法:
public interface UserMapper extends BaseMapper<User> {
/**
* 自定义分页查询
* @param page 分页对象
* @param queryWrapper 查询条件
* @return 分页结果
*/
IPage<User> selectUserPage(IPage<User> page, @Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
}
在Mapper.xml中添加SQL:
<select id="selectUserPage" resultType="com.example.demo.entity.User">
SELECT * FROM user ${ew.customSqlSegment}
</select>
5.2 逻辑删除
逻辑删除配置已在2.2节中展示,这里演示如何使用:
@Test
public void testLogicDelete() {
// 删除操作实际上是更新 deleted 字段
int rows = userMapper.deleteById(1L);
System.out.println("影响行数:" + rows);
// 查询时会自动带上 deleted=0 条件
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
// 如果需要查询包含已删除的数据,可以使用@SqlParser注解或全局配置
}
5.3 自动填充
自动填充功能已在2.4节中配置,这里演示效果:
@Test
public void testAutoFill() {
User user = new User();
user.setUsername("测试自动填充");
user.setPassword("123456");
user.setAge(20);
// 插入时自动填充createTime和updateTime
userMapper.insert(user);
// 更新时自动填充updateTime
user.setAge(25);
userMapper.updateById(user);
}
5.4 乐观锁
乐观锁实现步骤:
- 在表中添加version字段
- 在实体类中添加version字段并添加@Version注解
- 配置乐观锁插件
// 1. 实体类中添加
@Version
private Integer version;
// 2. 配置乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
使用示例:
@Test
public void testOptimisticLocker() {
// 查询
User user = userMapper.selectById(1L);
// 修改
user.setUsername("new name");
// 模拟其他线程已经修改了数据
User user2 = userMapper.selectById(1L);
user2.setUsername("other thread");
userMapper.updateById(user2);
// 当前线程更新,此时version已经不匹配,更新会失败
int rows = userMapper.updateById(user);
System.out.println("影响行数:" + rows); // 0
}
六、MyBatis-Plus进阶功能
6.1 ActiveRecord模式
ActiveRecord模式是一种数据访问设计模式,它允许实体类直接操作数据库:
- 实体类继承Model类:
import com.baomidou.mybatisplus.extension.activerecord.Model;
@Data
@TableName("user")
public class User extends Model<User> {
// 字段定义...
}
- 使用示例:
@Test
public void testActiveRecord() {
// 插入
User user = new User();
user.setUsername("AR模式");
user.setPassword("123456");
user.setAge(20);
user.insert(); // 直接调用实体类的方法
// 查询
User user2 = new User();
user2.setId(1L);
User result = user2.selectById();
System.out.println(result);
// 更新
user2.setUsername("修改后的名字");
user2.updateById();
// 删除
user2.deleteById();
// 条件查询
List<User> users = user2.selectList(
new QueryWrapper<User>().gt("age", 20)
);
}
6.2 多数据源配置
在实际项目中,有时需要连接多个数据库:
- 添加依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
- 配置多数据源:
spring:
datasource:
dynamic:
primary: master # 设置默认的数据源或者数据源组,默认值即为master
strict: false # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
url: jdbc:mysql://localhost:3306/master?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave1:
url: jdbc:mysql://localhost:3306/slave1?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave2:
url: jdbc:mysql://localhost:3306/slave2?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
- 使用@DS注解切换数据源:
@Service
@DS("master") // 默认使用master数据源
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@DS("slave1") // 方法上使用slave1数据源
public List<User> getAllFromSlave1() {
return baseMapper.selectList(null);
}
@DS("slave2") // 方法上使用slave2数据源
public List<User> getAllFromSlave2() {
return baseMapper.selectList(null);
}
}
6.3 代码生成器
MyBatis-Plus提供了强大的代码生成器:
- 添加依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
- 创建生成器代码:
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mp_demo?useSSL=false", "root", "123456")
.globalConfig(builder -> {
builder.author("author") // 设置作者
.enableSwagger() // 开启swagger模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://mybatis-plus"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.example") // 设置父包名
.moduleName("demo") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://mybatis-plus")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("user") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
6.4 自定义SQL注入器
MyBatis-Plus允许自定义SQL方法:
- 定义自定义方法:
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class DeleteAllMethod extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
// 执行的SQL
String sql = "delete from " + tableInfo.getTableName();
// Mapper接口方法名
String method = "deleteAll";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
}
}
- 创建SQL注入器:
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import java.util.List;
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 添加自定义方法
methodList.add(new DeleteAllMethod());
return methodList;
}
}
- 注册SQL注入器:
@Bean
public MySqlInjector mySqlInjector() {
return new MySqlInjector();
}
- 在Mapper接口中添加方法:
public interface UserMapper extends BaseMapper<User> {
/**
* 自定义方法:删除所有数据
* @return 影响行数
*/
int deleteAll();
}
- 使用自定义方法:
@Test
public void testCustomMethod() {
int rows = userMapper.deleteAll();
System.out.println("删除行数:" + rows);
}
七、MyBatis-Plus最佳实践
7.1 性能优化建议
- 合理使用索引:为查询条件中的字段添加索引
- 避免全表扫描:尽量不要使用不带条件的selectList(null)
- 只查询需要的字段:使用select()方法指定查询字段
- 合理使用缓存:对于不常变的数据可以使用缓存
- 批量操作:使用批量插入、更新方法减少数据库交互次数
7.2 事务管理
SpringBoot中结合@Transactional使用MyBatis-Plus:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* 事务示例
*/
@Transactional(rollbackFor = Exception.class)
public void transferMoney(Long fromUserId, Long toUserId, int amount) {
// 扣钱
User fromUser = userMapper.selectById(fromUserId);
fromUser.setMoney(fromUser.getMoney() - amount);
userMapper.updateById(fromUser);
// 加钱
User toUser = userMapper.selectById(toUserId);
toUser.setMoney(toUser.getMoney() + amount);
userMapper.updateById(toUser);
// 如果这里抛出异常,事务会回滚
// int i = 1 / 0;
}
}
7.3 异常处理
MyBatis-Plus常见的异常及处理方式:
异常类型 | 原因 | 解决方案 |
MybatisPlusException | MyBatis-Plus通用异常 | 检查错误信息,修正相应操作 |
TooManyResultsException | selectOne方法返回了多条结果 | 确保查询条件能唯一确定一条记录,或使用selectList |
PagingException | 分页参数错误 | 检查分页参数是否合法 |
TableInfoException | 实体类与数据库表映射错误 | 检查@TableName注解和表名是否正确 |
SqlInjectorException | SQL注入器异常 | 检查自定义SQL注入器实现 |
MyBatisSystemException | MyBatis系统异常 | 检查SQL语句和映射配置 |
全局异常处理示例:
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理MyBatis-Plus异常
*/
@ExceptionHandler(MybatisPlusException.class)
public Result handleMybatisPlusException(MybatisPlusException e) {
return Result.fail(e.getMessage());
}
/**
* 处理其他异常
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
return Result.fail("系统异常:" + e.getMessage());
}
}
7.4 与SpringBoot其他组件整合
- 整合Swagger生成API文档:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("MyBatis-Plus Demo API")
.description("MyBatis-Plus整合示例")
.version("1.0")
.build())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
.paths(PathSelectors.any())
.build();
}
}
- 整合Redis缓存:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 设置缓存有效期30分钟
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues();
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
}
}
在Service中使用缓存:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Cacheable(value = "user", key = "#id")
@Override
public User getById(Serializable id) {
return super.getById(id);
}
@CacheEvict(value = "user", key = "#user.id")
@Override
public boolean updateById(User user) {
return super.updateById(user);
}
}
八、实际项目案例
8.1 用户管理系统实现
下面我们实现一个完整的用户管理模块,包含以下功能:
- 用户分页查询
- 用户添加/修改
- 用户删除(逻辑删除)
- 用户条件查询
8.1.1 实体类
@Data
@TableName("sys_user")
@ApiModel(value = "User对象", description = "用户表")
public class User extends Model<User> {
@TableId(type = IdType.AUTO)
@ApiModelProperty(value = "主键ID")
private Long id;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "年龄")
private Integer age;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "角色ID")
private Long roleId;
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "更新时间")
private Date updateTime;
@TableLogic
@ApiModelProperty(value = "逻辑删除标志")
private Integer deleted;
@TableField(exist = false)
@ApiModelProperty(value = "角色名称")
private String roleName;
}
8.1.2 Mapper接口
public interface UserMapper extends BaseMapper<User> {
/**
* 自定义分页查询(包含角色名称)
* @param page 分页对象
* @param queryWrapper 查询条件
* @return 分页结果
*/
IPage<User> selectUserPageWithRole(IPage<User> page, @Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
}
8.1.3 Mapper XML
<select id="selectUserPageWithRole" resultType="com.example.demo.entity.User">
SELECT u.*, r.name as role_name
FROM sys_user u
LEFT JOIN sys_role r ON u.role_id = r.id
${ew.customSqlSegment}
</select>
8.1.4 Service接口
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {
/**
* 分页查询用户列表(包含角色名称)
* @param page 分页参数
* @param user 查询条件
* @return 分页结果
*/
IPage<User> selectUserPageWithRole(IPage<User> page, User user);
}
8.1.5 Service实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public IPage<User> selectUserPageWithRole(IPage<User> page, User user) {
return baseMapper.selectUserPageWithRole(page,
Wrappers.<User>lambdaQuery()
.like(StringUtils.isNotBlank(user.getUsername()), User::getUsername, user.getUsername())
.eq(user.getAge() != null, User::getAge, user.getAge())
.eq(user.getRoleId() != null, User::getRoleId, user.getRoleId())
);
}
}
8.1.6 Controller
@RestController
@RequestMapping("/user")
@Api(tags = "用户管理")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/page")
@ApiOperation("分页查询用户列表")
public Result<IPage<User>> page(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
User user) {
Page<User> page = new Page<>(pageNum, pageSize);
return Result.success(userService.selectUserPageWithRole(page, user));
}
@PostMapping
@ApiOperation("添加用户")
public Result<Void> add(@RequestBody User user) {
userService.save(user);
return Result.success();
}
@PutMapping
@ApiOperation("修改用户")
public Result<Void> update(@RequestBody User user) {
userService.updateById(user);
return Result.success();
}
@DeleteMapping("/{id}")
@ApiOperation("删除用户")
public Result<Void> delete(@PathVariable Long id) {
userService.removeById(id);
return Result.success();
}
@GetMapping("/{id}")
@ApiOperation("根据ID查询用户")
public Result<User> getById(@PathVariable Long id) {
return Result.success(userService.getById(id));
}
}
8.1.7 统一返回结果
@Data
@ApiModel(value = "统一返回结果")
public class Result<T> implements Serializable {
@ApiModelProperty(value = "状态码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private T data;
public static <T> Result<T> success() {
return success(null);
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("成功");
result.setData(data);
return result;
}
public static <T> Result<T> fail(String message) {
return fail(500, message);
}
public static <T> Result<T> fail(Integer code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
}
8.2 复杂查询示例
8.2.1 多表关联查询
@Test
public void testMultiTableQuery() {
// 查询用户及其角色信息
List<User> users = userMapper.selectList(
Wrappers.<User>query()
.select("u.*", "r.name as role_name")
.from("sys_user u")
.leftJoin("sys_role r on u.role_id = r.id")
.eq("u.deleted", 0)
.orderByAsc("u.create_time")
);
// 使用Lambda方式
List<User> users2 = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.select(User.class, info ->
!info.getColumn().equals("password") &&
!info.getColumn().equals("deleted"))
.eq(User::getDeleted, 0)
.orderByAsc(User::getCreateTime)
);
}
8.2.2 子查询
@Test
public void testSubQuery() {
// 查询年龄大于平均年龄的用户
List<User> users = userMapper.selectList(
Wrappers.<User>query()
.inSql("id", "select id from sys_user where age > (select avg(age) from sys_user)")
);
// 使用Lambda方式
List<User> users2 = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.inSql(User::getId, "select id from sys_user where age > (select avg(age) from sys_user)")
);
}
8.2.3 分组统计
@Test
public void testGroupBy() {
// 按角色分组统计用户数量
List<Map<String, Object>> result = userMapper.selectMaps(
Wrappers.<User>query()
.select("role_id", "count(*) as user_count")
.groupBy("role_id")
);
// 使用Lambda方式
List<Map<String, Object>> result2 = userMapper.selectMaps(
Wrappers.<User>lambdaQuery()
.select(User::getRoleId, "count(*) as user_count")
.groupBy(User::getRoleId)
);
}
九、MyBatis-Plus常见问题与解决方案
9.1 常见问题汇总
问题描述 | 可能原因 | 解决方案 |
字段值为null时没有更新 | MyBatis-Plus默认策略是null值不更新 | 1. 在字段上添加@TableField(update="%s") 2. 使用UpdateWrapper的set方法 |
实体类属性名与数据库字段名不一致 | 未正确配置字段映射 | 1. 使用@TableField注解指定字段名 2. 配置全局下划线转驼峰 |
分页查询不生效 | 未配置分页插件 | 按照5.1节配置分页插件 |
逻辑删除不生效 | 1. 未配置逻辑删除 2. 字段名配置错误 | 1. 按照2.2节配置逻辑删除 2. 检查字段名是否正确 |
自动填充不生效 | 1. 未实现MetaObjectHandler 2. 字段注解配置错误 | 1. 按照2.4节实现处理器 2. 检查@TableField注解配置 |
多数据源切换无效 | 1. 未正确配置多数据源 2. @DS注解位置不正确 | 1. 检查多数据源配置 2. @DS注解应加在Service类或方法上 |
批量插入效率低 | 使用了循环单条插入 | 使用Service的saveBatch方法 |
生成的SQL不符合预期 | 条件构造器使用错误 | 检查条件构造器的链式调用是否正确 |
实体类继承Model后CRUD方法冲突 | 与Service层方法冲突 | 避免同时使用ActiveRecord模式和Service模式 |
9.2 性能调优建议
- 批量操作:使用saveBatch、updateBatchById等方法减少数据库交互次数
- 合理使用索引:为查询条件中的字段添加索引
- 只查询必要字段:避免使用select *,只查询需要的字段
- 避免N+1查询:使用@TableField(exist = false)标注非数据库字段,避免自动查询
- 合理使用缓存:对于不常变的数据使用缓存
- SQL优化:使用MyBatis-Plus的SQL打印功能,分析慢SQL并进行优化
- 分页优化:对于大数据量分页,使用优化后的分页方式:
// 优化后的分页查询方式(对于大数据量)
@Test
public void testOptimizePage() {
// 传统分页(大数据量性能差)
// Page<User> page = new Page<>(10000, 10);
// 优化分页(先定位到起始记录)
Page<User> page = new Page<User>().setCurrent(10000).setSize(10);
userMapper.selectPage(page,
Wrappers.<User>lambdaQuery()
.orderByAsc(User::getId)
);
}
9.3 安全注意事项
- 防止SQL注入:
- 始终使用条件构造器而不是拼接SQL
- 如果必须使用SQL,使用参数化查询
- 敏感数据保护:
- 密码等敏感字段不要直接存储在数据库中,应该加密存储
- 查询时排除敏感字段:
@Test
public void testExcludeSensitiveFields() {
List<User> users = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.select(User.class, info ->
!info.getColumn().equals("password") &&
!info.getColumn().equals("salt"))
);
}
- 权限控制:
- 在Service层进行权限校验
- 使用注解进行方法级权限控制
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
十、MyBatis-Plus源码解析与扩展
10.1 核心架构分析
MyBatis-Plus的核心架构可以分为以下几层:
核心组件:
- SqlInjector:SQL注入器,负责将BaseMapper中的方法注入到MyBatis中
- ISqlRunner:SQL执行器,提供直接执行SQL的能力
- TableInfoHelper:表信息助手,负责解析实体类与数据库表的映射关系
- GlobalConfig:全局配置,包含各种全局策略配置
- IDataSource:数据源处理器,用于多数据源场景
10.2 执行流程分析
以selectById方法为例,执行流程如下:
10.3 自定义扩展点
MyBatis-Plus提供了多个扩展点,可以按需扩展:
- 自定义ID生成器:
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import org.springframework.stereotype.Component;
@Component
public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Number nextId(Object entity) {
// 实现自定义ID生成策略
return IdWorker.getId(); // 使用默认的雪花算法
}
@Override
public String nextUUID(Object entity) {
// 实现自定义UUID生成策略
return IdWorker.get32UUID();
}
}
- 自定义SQL注入器(已在6.4节介绍)
- 自定义元对象处理器(已在2.4节介绍)
- 自定义分页插件:
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
public class MyPaginationInterceptor extends PaginationInnerInterceptor {
public MyPaginationInterceptor(IDialect dialect) {
super(dialect);
}
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 自定义分页前处理
super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}
@Override
public void afterQuery(Object parameter, Page<?> page) {
// 自定义分页后处理
super.afterQuery(parameter, page);
}
}
10.4 插件开发示例
开发一个SQL执行时间统计插件:
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Signature;
import java.util.Properties;
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlCostInterceptor implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 记录开始时间
long start = System.currentTimeMillis();
// 将开始时间存入BoundSql
boundSql.setAdditionalParameter("_sql_start_time", start);
}
@Override
public void afterQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, List<Object> result) {
// 获取开始时间
Long start = (Long) boundSql.getAdditionalParameter("_sql_start_time");
if (start != null) {
// 计算耗时
long cost = System.currentTimeMillis() - start;
// 打印SQL执行时间
System.out.println("SQL执行耗时:" + cost + "ms");
// 可以在这里添加慢SQL监控逻辑
if (cost > 1000) {
System.out.println("慢SQL警告:" + boundSql.getSql());
}
}
}
}
配置插件:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加SQL耗时统计插件
interceptor.addInnerInterceptor(new SqlCostInterceptor());
return interceptor;
}
总结
本文全面介绍了SpringBoot整合MyBatis-Plus的各个方面,从基础配置到高级功能,再到实际项目应用和源码扩展。主要内容包括:
- MyBatis-Plus的核心概念与优势
- SpringBoot与MyBatis-Plus的整合步骤
- 基础CRUD操作与条件构造器的使用
- 高级功能如分页、逻辑删除、乐观锁等
- ActiveRecord模式、多数据源、代码生成器等进阶功能
- 性能优化、事务管理、异常处理等最佳实践
- 完整项目案例的实现
- 常见问题解决方案
- 源码分析与自定义扩展
MyBatis-Plus作为MyBatis的增强工具,可以极大地提高开发效率,减少样板代码的编写。通过本文的学习,相信读者已经掌握了MyBatis-Plus的核心用法,能够在实际项目中灵活运用。
最后,建议在实际项目中:
- 根据团队规范统一使用方式
- 合理使用各种高级特性
- 注意性能优化和安全问题
- 根据项目需求进行适当扩展
希望本文能对你的开发工作有所帮助!
关注我?别别别,我怕你笑出腹肌找我赔钱。
头条对markdown的文章显示不太友好,想了解更多的可以关注微信公众号:“Eric的技术杂货库”,后期会有更多的干货以及资料下载。
相关推荐
- 一文读懂Prometheus架构监控(prometheus监控哪些指标)
-
介绍Prometheus是一个系统监控和警报工具包。它是用Go编写的,由Soundcloud构建,并于2016年作为继Kubernetes之后的第二个托管项目加入云原生计算基金会(C...
- Spring Boot 3.x 新特性详解:从基础到高级实战
-
1.SpringBoot3.x简介与核心特性1.1SpringBoot3.x新特性概览SpringBoot3.x是建立在SpringFramework6.0基础上的重大版...
- 「技术分享」猪八戒基于Quartz分布式调度平台实践
-
点击原文:【技术分享】猪八戒基于Quartz分布式调度平台实践点击关注“八戒技术团队”,阅读更多技术干货1.背景介绍1.1业务场景调度任务是我们日常开发中非常经典的一个场景,我们时常会需要用到一些不...
- 14. 常用框架与工具(使用的框架)
-
本章深入解析Go生态中的核心开发框架与工具链,结合性能调优与工程化实践,提供高效开发方案。14.1Web框架(Gin,Echo)14.1.1Gin高性能实践//中间件链优化router:=...
- SpringBoot整合MyBatis-Plus:从入门到精通
-
一、MyBatis-Plus基础介绍1.1MyBatis-Plus核心概念MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提...
- Seata源码—5.全局事务的创建与返回处理
-
大纲1.Seata开启分布式事务的流程总结2.Seata生成全局事务ID的雪花算法源码3.生成xid以及对全局事务会话进行持久化的源码4.全局事务会话数据持久化的实现源码5.SeataServer创...
- Java开发200+个学习知识路线-史上最全(框架篇)
-
1.Spring框架深入SpringIOC容器:BeanFactory与ApplicationContextBean生命周期:实例化、属性填充、初始化、销毁依赖注入方式:构造器注入、Setter注...
- OpenResty 入门指南:从基础到动态路由实战
-
一、引言1.1OpenResty简介OpenResty是一款基于Nginx的高性能Web平台,通过集成Lua脚本和丰富的模块,将Nginx从静态反向代理转变为可动态编程的应用平台...
- 你还在为 Spring Boot3 分布式锁实现发愁?一文教你轻松搞定!
-
作为互联网大厂后端开发人员,在项目开发过程中,你有没有遇到过这样的问题:多个服务实例同时访问共享资源,导致数据不一致、业务逻辑混乱?没错,这就是分布式环境下常见的并发问题,而分布式锁就是解决这类问题的...
- 近2万字详解JAVA NIO2文件操作,过瘾
-
原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。从classpath中读取过文件的人,都知道需要写一些读取流的方法,很是繁琐。最近使用IDEA在打出.这个符号的时候,一行代...
- 学习MVC之租房网站(十二)-缓存和静态页面
-
在上一篇<学习MVC之租房网站(十一)-定时任务和云存储>学习了Quartz的使用、发邮件,并将通过UEditor上传的图片保存到云存储。在项目的最后,再学习优化网站性能的一些技术:缓存和...
- Linux系统下运行c++程序(linux怎么运行c++文件)
-
引言为什么要在Linux下写程序?需要更多关于Linux下c++开发的资料请后台私信【架构】获取分享资料包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdf...
- 2022正确的java学习顺序(文末送java福利)
-
对于刚学习java的人来说,可能最大的问题是不知道学习方向,每天学了什么第二天就忘了,而课堂的讲解也是很片面的。今天我结合我的学习路线为大家讲解下最基础的学习路线,真心希望能帮到迷茫的小伙伴。(有很多...
- 一个 3 年 Java 程序员 5 家大厂的面试总结(已拿Offer)
-
前言15年毕业到现在也近三年了,最近面试了阿里集团(菜鸟网络,蚂蚁金服),网易,滴滴,点我达,最终收到点我达,网易offer,蚂蚁金服二面挂掉,菜鸟网络一个月了还在流程中...最终有幸去了网易。但是要...
- 多商户商城系统开发全流程解析(多商户商城源码免费下载)
-
在数字化商业浪潮中,多商户商城系统成为众多企业拓展电商业务的关键选择。这类系统允许众多商家在同一平台销售商品,不仅丰富了商品种类,还为消费者带来更多样的购物体验。不过,开发一个多商户商城系统是个复杂的...
你 发表评论:
欢迎- 一周热门
-
-
Redis客户端 Jedis 与 Lettuce
-
高并发架构系列:Redis并发竞争key的解决方案详解
-
redis如何防止并发(redis如何防止高并发)
-
开源推荐:如何实现的一个高性能 Redis 服务器
-
redis安装与调优部署文档(WinServer)
-
Redis 入门 - 安装最全讲解(Windows、Linux、Docker)
-
一文带你了解 Redis 的发布与订阅的底层原理
-
Redis如何应对并发访问(redis控制并发量)
-
oracle数据库查询Sql语句是否使用索引及常见的索引失效的情况
-
Java SE Development Kit 8u441下载地址【windows版本】
-
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)