# 一. MyBatis-Plus 常用功能
下面我将详细介绍 MyBatis-Plus 中的一些常用功能。
条件构造器
条件构造器可以帮助我们快速构建 SQL 查询条件,避免手动拼接 SQL 语句。MyBatis-Plus 提供了一个 Wrapper
接口和一些实现类,用于表示查询条件的封装类。
例如,以下代码演示了基本的条件查询:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", "admin");
List<User> userList = userMapper.selectList(queryWrapper);
在这个例子中,我们创建了一个 QueryWrapper
对象,并调用了其 eq
方法来添加查询条件。最后调用了 Mapper 的 selectList
方法来执行查询并返回结果。
除了 eq
方法,还有很多其他的方法可以用来添加查询条件,例如:
• ne:不等于
• gt:大于
• ge:大于等于
• lt:小于
• le:小于等于
• like:模糊匹配
• in:包含在指定值列表中
• notIn:不包含在指定值列表中
• isNull:为 NULL
• isNotNull:不为 NULL
• and:添加 AND 连接符
• or:添加 OR 连接符
- 等等
可以根据实际需要选择合适的方法来构造查询条件。
自动填充字段
在数据库中,有些字段的值是需要自动生成或者从其他地方获取的,例如创建时间、更新时间等。MyBatis-Plus 提供了一个 MetaObjectHandler
接口,用于自动填充这些字段的值。
例如,以下代码演示了如何配置自动填充:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
}
在这个例子中,我们实现了 MetaObjectHandler
接口,并重写了其中的 insertFill
和 updateFill
方法。这两个方法会在插入和更新操作时自动填充指定的字段,使用了 Java 8 的 LocalDateTime.now
方法来获取当前时间。
为了让 MyBatis-Plus 自动使用我们定义的 MetaObjectHandler
实现类,还需要在配置文件中添加对应的配置项:
mybatis-plus.global-config.meta-object-handler=com.example.MyMetaObjectHandler
## 分页查询
分页查询是 Web 应用中经常使用的功能之一。MyBatis-Plus 提供了一个 Page
类和相应的 Mapper 方法,可以方便地进行分页查询。
例如,以下代码演示了分页查询的基本用法:
IPage<User> userPage = new Page<>(1, 10);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("status", 1);
userMapper.selectPage(userPage, queryWrapper);
List<User> userList = userPage.getRecords();
在这个例子中,我们创建了一个 Page
对象,并指定了页码和每页数据数量,然后创建了一个 QueryWrapper
对象并添加了查询条件。最后调用了 Mapper 的 selectPage
方法来执行分页查询。
除了基本的分页查询外,MyBatis-Plus 还提供了一些高级的分页查询功能,例如按照多个字段排序、自定义返回结果等。
二.高级功能
以下是 MyBatis-Plus 常用的高级功能及其实现方式:
一、性能分析插件
MyBatis-Plus 提供了性能分析插件,可以用来分析 SQL 执行性能。步骤如下:
- 引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
- 在 application.yml 文件中添加配置
mybatis-plus:
configuration:
# 开启驼峰命名转换(如果数据库字段为 user_id,则实体类属性为 userId)
map-underscore-to-camel-case: true
# 开启性能分析插件
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# SQL执行分析拦截器,用于输出每条 SQL 语句及其执行时间
sql-explain-type: com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackSqlParser
global-config:
# 是否打印 SQL 日志
sql-show: true
- 在 Mapper 接口上添加注解
@Mapper
public interface UserMapper extends BaseMapper<User> {
/**
* 查询所有用户
* @return 用户列表
*/
@Select("select * from user")
@InterceptorIgnore(tenantLine = "true")
List<User> selectAll();
}
二、乐观锁插件
MyBatis-Plus 也提供了乐观锁插件,可以用来解决并发更新问题。步骤如下:
- 引入依赖和配置
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
mybatis-plus:
configuration:
# 开启驼峰命名转换(如果数据库字段为 user_id,则实体类属性为 userId)
map-underscore-to-camel-case: true
global-config:
# 是否开启逻辑删除
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
# 自动填充
meta-object-handler: com.baomidou.mybatisplus.extension.handlers.MybatisDefaultMetaObjectHandler
# 注入主键生成器
id-type: auto
configuration:
# 开启乐观锁插件
plugins:
- com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor
- 在实体类中添加 @Version 注解
@Data
public class User {
/**
* 用户ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 版本号
*/
@Version
private Integer version;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 手机号码
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 是否删除
*/
@TableLogic
private Integer deleted;
}
- 在 Mapper 接口中使用 @Update 注解
@Mapper
public interface UserMapper extends BaseMapper<User> {
/**
* 根据ID更新用户信息
* @param user 用户对象
* @return 更新结果
*/
@Update("<script>" +
"update user set " +
"<if test='username != null'>username=#{username}</if>" +
"<if test='password != null'>, password=#{password}</if>" +
"<if test='phone != null'>, phone=#{phone}</if>" +
"<if test='email != null'>, email=#{email}</if>" +
"where id=#{id} and version=#{version}" +
"</script>")
int updateUserById(User user);
}
在这个示例中,我们使用了动态 SQL 语句拼接,通过判断对象中的属性是否为空来决定是否加入 SET 子句中。同时还需要注意,为了实现乐观锁机制,我们在 WHERE 子句中添加了一个版本号的判断条件。
三、多数据源的配置
MyBatis-Plus 支持多数据源配置,可以通过 DynamicDataSource 根据不同的数据源进行动态切换。步骤如下:
- 引入依赖和配置
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- druid 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- HikariCP 数据源 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${hikari.version}</version>
</dependency>
spring:
datasource:
# 主数据源配置
url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
# 开启驼峰命名转换(如果数据库字段为 user_id,则实体类属性为 userId)
map-underscore-to-camel-case: true
global-config:
# 是否开启逻辑删除
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
# 自动填充
meta-object-handler: com.baomidou.mybatisplus.extension.handlers.MybatisDefaultMetaObjectHandler
# 注入主键生成器
id-type: auto
configuration:
# 开启性能分析插件
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# SQL执行分析拦截器,用于输出每条 SQL 语句及其执行时间
sql-explain-type: com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackSqlParser
# 开启乐观锁插件
plugins:
- com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor
- 创建多个数据源的配置类
@Configuration
public class DataSourceConfig {
/**
* 主数据源配置
*/
@Bean(name = "masterDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 从数据源1配置
*/
@Bean(name = "slave1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave1")
public DataSource slave1DataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 从数据源2配置
*/
@Bean(name = "slave2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave2")
public DataSource slave2DataSource() {
return DruidDataSourceBuilder.create().build();
}
}
在这个示例中,我们通过注解的方式创建了三个数据源,并分别对应主数据源和两个从数据源。
- 创建数据源路由器
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
在这个示例中,我们通过继承 AbstractRoutingDataSource 类,并实现 determineCurrentLookupKey 方法来实现数据源的路由切换。其中 DataSourceContextHolder 是一个线程安全的上下文类,用于存储当前数据源。
好的,以下是创建数据源切换器的步骤:
- 创建数据源切换器
@Component
@Aspect
public class DataSourceAspect {
/**
* 定义切入点:所有配置了 @DS 注解的方法
*/
@Pointcut("@annotation(com.baomidou.dynamic.datasource.annotation.DS)")
public void dsPointCut() {}
/**
* 在执行方法前动态切换数据源
*/
@Before("dsPointCut()")
public void before(JoinPoint point) {
// 获取方法上的 @DS 注解
DS ds = point.getTarget().getClass().getAnnotation(DS.class);
if (ds == null) {
// 如果类上没有 @DS 注解,则获取方法上的 @DS 注解
MethodSignature signature = (MethodSignature) point.getSignature();
ds = signature.getMethod().getAnnotation(DS.class);
}
// 切换数据源
if (ds != null) {
DataSourceContextHolder.setDataSource(ds.value());
}
}
/**
* 在执行方法后清除数据源
*/
@After("dsPointCut()")
public void after(JoinPoint point) {
DataSourceContextHolder.clearDataSource();
}
}
在这个示例中,我们通过使用 AOP 技术,在目标方法执行前动态切换数据源。具体实现方式是:在切入点方法上定义一个 @DS 注解,并在 before 方法中根据该注解的值切换数据源。需要注意的是,如果类和方法都有 @DS 注解,方法上的注解优先级更高。
- 测试多数据源配置
最后,我们可以通过编写一个测试类来验证多数据源配置是否生效:
@RunWith(SpringRunner.class)
@SpringBootTest
public class DataSourceTest {
@Autowired
private UserService userService;
@Test
public void test() {
// 切换到主数据源
DataSourceContextHolder.setDataSource(DataSourceType.MASTER);
User user1 = userService.getById(1L);
System.out.println(user1);
// 切换到从数据源1
DataSourceContextHolder.setDataSource(DataSourceType.SLAVE1);
User user2 = userService.getById(1L);
System.out.println(user2);
// 切换到从数据源2
DataSourceContextHolder.setDataSource(DataSourceType.SLAVE2);
User user3 = userService.getById(1L);
System.out.println(user3);
}
}
在这个示例中,我们通过切换数据源的方式,依次查询了三个不同数据源中的用户信息。如果没有出现异常,则说明多数据源配置已经生效。
四、分页插件
MyBatis-Plus 还提供了分页插件,可以用来实现分页查询。步骤如下:
- 引入依赖和配置
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
mybatis-plus:
configuration:
# 开启驼峰命名转换(如果数据库字段为 user_id,则实体类属性为 userId)
map-underscore-to-camel-case: true
global-config:
# 是否开启逻辑删除
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
# 自动填充
meta-object-handler: com.baomidou.mybatisplus.extension.handlers.MybatisDefaultMetaObjectHandler
# 注入主键生成器
id-type: auto
configuration:
# 开启分页插件
plugins:
- com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor
- 在 Mapper 接口中使用 @Select 注解,并传入 Page 对象作为参数
@Mapper
public interface UserMapper extends BaseMapper<User> {
/**
* 分页查询用户列表
* @param page 分页对象
* @param username 用户名
* @param email 邮箱
* @return 用户列表
*/
@Select("<script>" +
"select * from user " +
"<where>" +
"<if test='username != null'>and username like concat('%',#{username},'%')</if>" +
"<if test='email != null'>and email like concat('%',#{email},'%')</if>" +
"</where>" +
"order by id asc" +
"</script>")
List<User> selectUserList(Page<User> page, @Param("username")String username, @Param("email")String email);
}
- 在 Controller 层中调用查询方法,传入 Page 对象并设置分页参数
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
/**
* 分页查询用户列表
* @param pageNum 当前页码
* @param pageSize 每页条数
* @param username 用户名
* @param email 邮箱
* @return 用户列表
*/
@GetMapping("/users")
public ResultVO selectUserList(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
String username,
String email) {
// 设置分页参数
Page<User> page = new Page<>(pageNum, pageSize);
// 调用查询方法
List<User> userList = userMapper.selectUserList(page, username, email);
// 封装结果
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("total", page.getTotal());
resultMap.put("pageNum", page.getCurrent());
resultMap.put("pageSize", page.getSize());
resultMap.put("list", userList);
return ResultVO.success(resultMap);
}
}
以上就是 MyBatis-Plus 常用的高级功能,并给出了具体实现的步骤和代码