# Mybatis-Plus 实战指南:核心功能解析与最佳实践
Mybatis-Plus是MyBatis的强大增强工具,在保留MyBatis所有特性的基础上,提供了丰富的功能简化开发、提高效率。本文将深入探讨Mybatis-Plus的核心功能和使用实践。
## Mybatis-Plus 核心架构
Mybatis-Plus在MyBatis基础上进行了深度封装,主要包含以下核心模块:
- **核心模块**:提供基础CRUD操作、条件构造器等功能
- **代码 生成器**:自动化生成Entity、Mapper、Service等代码
- **分页插件**:简化分页查询操作
- **性能分析插件**:监控SQL执行性能
## 项目配置与集成
### 依赖配置
```xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<"lker.jiebaobifen.com">
<"tyjg.jiebaobifen.com">
<"wtsa.jiebaobifen.com">
```
### 基础配置类
```java
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
// 配置元数据注入
@Bean
public IMetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUsername());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUsername());
}
};
}
}
```
### 应用配置
```yaml
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
<"gssm.jiebaobifen.com">
<"ywop.jiebaobifen.com">
<"ohrr.jiebaobifen.com">
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
```
## 实体类与Mapper设计
### 实体类定义
```java
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String email;
private Integer age;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Version
private Integer version;
@TableLogic
private Integer deleted;
}
// 枚举类型处理
public enum UserStatus {
ACTIVE(1, "激活"),
INACTIVE(0, "未激活"),
LOCKED(-1, "锁定");
private final Integer code;
private final String desc;
UserStatus(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
@Getter
public static class StatusConverter implements AttributeConverter<UserStatus, Integer> {
@Override
public Integer convertToDatabaseColumn(UserStatus attribute) {
return attribute != null ? attribute.getCode() : null;
}
@Override
public UserStatus convertToEntityAttribute(Integer dbData) {
return Arrays.stream(UserStatus.values())
.filter(status -> status.getCode().equals(dbData))
.findFirst()
.orElse(null);
}
}
}
```
### Mapper接口定义
```java
public interface UserMapper extends BaseMapper<User> {
// 自定义查询方法
@Select("SELECT * FROM sys_user WHERE age > #{age}")
List<User> selectUsersByAge(@Param("age") Integer age);
// 复杂查询
List<User> selectUsersWithConditions(@Param("username") String username,
@Param("minAge") Integer minAge,
@Param("maxAge") Integer maxAge);
// 分页查询
Page<User> selectUserPage(Page<User> page, @Param("query") UserQuery query);
}
// 对应的XML映射
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<"ccpg.jiebaobifen.com">
<"itka.jiebaobifen.com">
<"cqqd.jiebaobifen.com">
<select id="selectUsersWithConditions" resultType="com.example.entity.User">
SELECT * FROM sys_user
WHERE deleted = 0
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
<if test="maxAge != null">
AND age <= #{maxAge}
</if>
ORDER BY create_time DESC
</select>
</mapper>
```
## 核心功能实战
### 1. 条件构造器使用
```java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> findActiveUsers(String keyword, Integer minAge) {
// 使用QueryWrapper构建查询条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(keyword), "username", keyword)
.ge(minAge != null, "age", minAge)
.eq("deleted", 0)
.orderByDesc("create_time");
return userMapper.selectList(wrapper);
}
public Page<User> findUsersByPage(UserQuery query) {
// 分页查询
Page<User> page = new Page<>(query.getPageNum(), query.getPageSize());
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
wrapper.like(StringUtils.isNotBlank(query.getUsername()),
User::getUsername, query.getUsername())
.between(query.getMinAge() != null && query.getMaxAge() != null,
User::getAge, query.getMinAge(), query.getMaxAge())
.eq(User::getDeleted, 0);
return userMapper.selectPage(page, wrapper);
}
public boolean updateUserEmail(Long userId, String newEmail) {
// 使用UpdateWrapper进行更新
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("id", userId)
.set("email", newEmail)
.set("update_time", LocalDateTime.now());
return userMapper.update(null, wrapper) > 0;
}
}
```
### 2. 事务管理
```java
@Service
@Transactional(rollbackFor = Exception.class)
<"ixyu.jiebaobifen.com">
<"alfv.jiebaobifen.com">
<"uvjo.jiebaobifen.com">
public class UserTransactionalService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserLogMapper userLogMapper;
public void createUserWithLog(User user) {
// 插入用户
userMapper.insert(user);
// 记录日志
UserLog userLog = new UserLog();
userLog.setUserId(user.getId());
userLog.setAction("CREATE");
userLog.setCreateTime(LocalDateTime.now());
userLogMapper.insert(userLog);
// 如果这里出现异常,整个事务会回滚
if (user.getAge() < 0) {
throw new RuntimeException("年龄不能为负数");
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUserInNewTransaction(User user) {
userMapper.updateById(user);
}
}
```
### 3. 批量操作优化
```java
@Service
public class BatchOperationService {
@Autowired
private UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
public void batchInsertUsers(List<User> users) {
// 使用Mybatis-Plus的批量插入
users.forEach(user -> {
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
});
// 批量插入,每批1000条
int batchSize = 1000;
for (int i = 0; i < users.size(); i += batchSize) {
int end = Math.min(i + batchSize, users.size());
List<User> batchList = users.subList(i, end);
// 使用自定义的批量插入方法
batchInsert(batchList);
}
}
private void batchInsert(List<User> users) {
SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH);
try {
UserMapper batchMapper = sqlSession.getMapper(UserMapper.class);
for (User user : users) {
batchMapper.insert(user);
}
sqlSession.commit();
} finally {
sqlSession.close();
}
}
public void batchUpdateUsers(List<User> users) {
// 使用UpdateWrapper进行批量更新
<"yard.jiebaobifen.com">
<"hziu.jiebaobifen.com">
<"glrw.jiebaobifen.com">
users.forEach(user -> {
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("id", user.getId())
.set("email", user.getEmail())
.set("update_time", LocalDateTime.now());
userMapper.update(null, wrapper);
});
}
}
```
## 高级特性应用
### 1. 多租户数据隔离
```java
@Configuration
public class TenantConfig {
@Bean
public MybatisPlusInterceptor multiTenantInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 多租户插件
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
// 从当前上下文中获取租户ID
return new StringValue(getCurrentTenantId());
}
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public boolean ignoreTable(String tableName) {
// 忽略不需要租户隔离的表
return "sys_tenant".equals(tableName);
}
}));
return interceptor;
}
private String getCurrentTenantId() {
// 从安全上下文中获取当前租户ID
return SecurityUtils.getCurrentUserTenantId();
}
}
```
### 2. 数据权限控制
```java
@Component
public class DataPermissionInterceptor implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
// 解析SQL,添加数据权限过滤条件
String originalSql = boundSql.getSql();
String dataPermissionSql = addDataPermissionCondition(originalSql);
// 使用反射修改BoundSql中的SQL
ReflectUtil.setFieldValue(boundSql, "sql", dataPermissionSql);
}
private String addDataPermissionCondition(String sql) {
// 根据当前用户权限添加数据过滤条件
String userDeptCondition = getCurrentUserDeptCondition();
if (StringUtils.isNotBlank(userDeptCondition)) {
// 在WHERE条件中添加部门过滤
return SqlParserUtil.addWhereCondition(sql, userDeptCondition);
}
return sql;
}
private String getCurrentUserDeptCondition() {
User currentUser = SecurityUtils.getCurrentUser();
if (currentUser != null && !currentUser.isAdmin()) {
return "dept_id = '" + currentUser.getDeptId() + "'";
}
return null;
}
}
```
## 性能优化实践
### 1. SQL执行监控
```java
@Slf4j
@Component
<"hgee.jiebaobifen.com">
<"xmun.jiebaobifen.com">
<"dkcn.jiebaobifen.com">
public class SqlLogInterceptor implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
long startTime = System.currentTimeMillis();
MDC.put("sqlStartTime", String.valueOf(startTime));
}
@Override
public void afterQuery(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql,
List<Object> result) {
long startTime = Long.parseLong(MDC.get("sqlStartTime"));
long endTime = System.currentTimeMillis();
long costTime = endTime - startTime;
if (costTime > 1000) { // 超过1秒的SQL记录警告日志
log.warn("慢SQL检测 - 执行时间: {}ms, SQL: {}", costTime, boundSql.getSql());
}
MDC.remove("sqlStartTime");
}
}
```
### 2. 查询结果缓存
```java
@Service
@CacheConfig(cacheNames = "user")
public class UserCacheService {
@Autowired
private UserMapper userMapper;
@Cacheable(key = "'user:' + #id")
public User getUserById(Long id) {
return userMapper.selectById(id);
}
@CacheEvict(key = "'user:' + #user.id")
public void updateUser(User user) {
userMapper.updateById(user);
}
@Caching(evict = {
@CacheEvict(key = "'user:' + #id"),
@CacheEvict(key = "'user:list'")
})
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
}
```
## 最佳实践总结
### 1. 代码规范
- 实体类使用Lombok简化代码
- 字段命名遵循数据库规范(下划线命名)
- 为常用查询字段添加数据库索引
### 2. 性能优化
- 合理使用分页,避免一次性查询大量数据
- 批量操作使用批处理模式
- 复杂查询使用原生XML映射
### 3. 事务管理
- 明确的事务边界定义
- 合理的事务传播机制选择
- 异常处理与事务回滚策略
### 4. 安全考虑
- SQL注入防护(使用参数化查询)
- 数据权限控制
- 敏感数据脱敏处理
Mybatis-Plus通过丰富的功能和合理的抽象,显著提升了开发效率。遵循上述最佳实践,可以构建出既高效又维护性良好的数据访问层。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3102296/ ,如需转载,请注明出处,否则将追究法律责任。