补充专栏:
Mybatis_好学且牛逼的马的博客-优快云博客
https://blog.youkuaiyun.com/king_model/category_13090429.html
一、MyBatis 与 MyBatis-Plus 关系深度解析
1.1 架构关系图
text
应用程序
↓
MyBatis-Plus (增强层)
↓ 通用Mapper、条件构造器、分页插件
MyBatis (核心层)
↓ SqlSession、Executor、StatementHandler
JDBC (驱动层)
↓
数据库
1.2 核心关系说明
-
MyBatis-Plus 是 MyBatis 的增强工具,不是替代品
-
完全兼容:所有 MyBatis 原生功能都可以正常使用
-
功能增强:在 MyBatis 基础上添加了通用 CRUD、条件构造器等便捷功能
-
无侵入性:引入 MyBatis-Plus 不会影响现有 MyBatis 功能
二、完整依赖配置
2.1 Maven 依赖
xml
<dependencies>
<!-- MyBatis-Plus 启动器(包含MyBatis核心) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 连接池(可选,starter中已包含HikariCP) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2.2 配置文件
yaml
# application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
# MyBatis 原生配置
mybatis:
mapper-locations: classpath:mapper/*.xml # XML映射文件位置
type-aliases-package: com.example.entity # 实体类别名
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰
cache-enabled: true # 开启缓存
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL日志
# MyBatis-Plus 配置
mybatis-plus:
configuration:
# 继承mybatis的配置,可以覆盖
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: assign_id # 主键策略
logic-delete-field: deleted # 逻辑删除字段
logic-delete-value: 1 # 逻辑删除值
logic-not-delete-value: 0 # 逻辑未删除值
mapper-locations: classpath*:/mapper/**/*.xml # 支持多路径
三、实体类与 Mapper 设计
3.1 基础实体类(使用 MyBatis-Plus 注解)
java
@Data
@TableName("sys_user") // MyBatis-Plus 表名注解
public class User {
/**
* MyBatis-Plus 主键策略
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 普通字段
*/
private String username;
private String password;
private String email;
private Integer age;
/**
* 数据库字段名映射
*/
@TableField("phone_number")
private String phoneNumber;
/**
* 非数据库字段
*/
@TableField(exist = false)
private String confirmPassword;
/**
* 逻辑删除字段
*/
@TableLogic
private Integer deleted;
/**
* 乐观锁字段
*/
@Version
private Integer version;
/**
* 自动填充字段
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 枚举字段(使用MyBatis原生typeHandler)
*/
@TableField(typeHandler = UserStatusTypeHandler.class)
private UserStatus status;
}
/**
* 用户状态枚举
*/
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 方法...
}
3.2 自定义 TypeHandler(MyBatis 原生功能)
java
// 枚举类型处理器
@MappedJdbcTypes(JdbcType.INTEGER)
@MappedTypes(UserStatus.class)
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
UserStatus parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getCode());
}
@Override
public UserStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
return rs.wasNull() ? null : UserStatus.valueOf(code);
}
@Override
public UserStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
return rs.wasNull() ? null : UserStatus.valueOf(code);
}
@Override
public UserStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
return cs.wasNull() ? null : UserStatus.valueOf(code);
}
}
// 在枚举中添加转换方法
public enum UserStatus {
// ... 枚举值
public static UserStatus valueOf(Integer code) {
for (UserStatus status : values()) {
if (status.getCode().equals(code)) {
return status;
}
}
throw new IllegalArgumentException("未知的状态码: " + code);
}
}
3.3 Mapper 接口设计
java
/**
* 用户Mapper - 同时使用MyBatis-Plus和MyBatis原生功能
*/
@Repository
public interface UserMapper extends BaseMapper<User> {
// ========== MyBatis-Plus 自动生成的方法 ==========
// 以下方法无需实现,BaseMapper已提供:
// int insert(User user);
// int deleteById(Serializable id);
// int updateById(User user);
// User selectById(Serializable id);
// List<User> selectList(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
// 等等...
// ========== 自定义方法(使用MyBatis XML配置) ==========
/**
* 复杂查询:根据用户名或邮箱查询用户
*/
List<User> selectByUsernameOrEmail(@Param("keyword") String keyword);
/**
* 联表查询:查询用户及其角色信息
*/
List<UserWithRoleVO> selectUserWithRoles(@Param("userId") Long userId);
/**
* 分页查询:自定义分页(复杂SQL)
*/
List<User> selectUserPage(@Param("page") Page<User> page,
@Param("query") UserQueryDTO query);
/**
* 批量插入
*/
int batchInsert(@Param("list") List<User> userList);
/**
* 更新用户状态
*/
int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);
/**
* 存储过程调用:重置用户密码
*/
void resetUserPassword(@Param("userId") Long userId);
}
四、XML 映射文件配置
4.1 UserMapper.xml
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- 自定义结果映射(复杂查询使用) -->
<resultMap id="UserWithRoleResultMap" type="com.example.vo.UserWithRoleVO" extends="BaseResultMap">
<collection property="roles" ofType="com.example.entity.Role">
<id column="role_id" property="id"/>
<result column="role_name" property="roleName"/>
<result column="role_code" property="roleCode"/>
</collection>
</resultMap>
<!-- 基础列(可被其他resultMap引用) -->
<sql id="Base_Column_List">
id, username, password, email, age, phone_number, deleted,
version, create_time, update_time, status
</sql>
<!-- 方法1:根据用户名或邮箱查询用户 -->
<select id="selectByUsernameOrEmail" resultType="com.example.entity.User">
SELECT
<include refid="Base_Column_List"/>
FROM sys_user
WHERE deleted = 0
AND (username LIKE CONCAT('%', #{keyword}, '%')
OR email LIKE CONCAT('%', #{keyword}, '%'))
ORDER BY create_time DESC
</select>
<!-- 方法2:联表查询用户及其角色 -->
<select id="selectUserWithRoles" resultMap="UserWithRoleResultMap">
SELECT
u.id, u.username, u.email,
r.id as role_id, r.role_name, r.role_code
FROM sys_user u
LEFT JOIN sys_user_role ur ON u.id = ur.user_id
LEFT JOIN sys_role r ON ur.role_id = r.id
WHERE u.id = #{userId} AND u.deleted = 0
</select>
<!-- 方法3:自定义分页查询 -->
<select id="selectUserPage" resultType="com.example.entity.User">
SELECT
<include refid="Base_Column_List"/>
FROM sys_user
<where>
deleted = 0
<if test="query.username != null and query.username != ''">
AND username LIKE CONCAT('%', #{query.username}, '%')
</if>
<if test="query.email != null and query.email != ''">
AND email LIKE CONCAT('%', #{query.email}, '%')
</if>
<if test="query.minAge != null">
AND age >= #{query.minAge}
</if>
<if test="query.maxAge != null">
AND age <= #{query.maxAge}
</if>
<if test="query.status != null">
AND status = #{query.status, typeHandler=com.example.handler.UserStatusTypeHandler}
</if>
</where>
ORDER BY create_time DESC
</select>
<!-- 方法4:批量插入 -->
<insert id="batchInsert">
INSERT INTO sys_user (
id, username, password, email, age, phone_number,
create_time, update_time, status
) VALUES
<foreach collection="list" item="user" separator=",">
(
#{user.id}, #{user.username}, #{user.password}, #{user.email},
#{user.age}, #{user.phoneNumber}, NOW(), NOW(),
#{user.status, typeHandler=com.example.handler.UserStatusTypeHandler}
)
</foreach>
</insert>
<!-- 方法5:更新用户状态 -->
<update id="updateStatus">
UPDATE sys_user
SET status = #{status, typeHandler=com.example.handler.UserStatusTypeHandler},
update_time = NOW()
WHERE id = #{id} AND deleted = 0
</update>
<!-- 方法6:调用存储过程 -->
<update id="resetUserPassword" statementType="CALLABLE">
{call sp_reset_user_password(#{userId, mode=IN})}
</update>
<!-- 动态SQL示例:根据条件更新 -->
<update id="updateSelective">
UPDATE sys_user
<set>
<if test="username != null">username = #{username},</if>
<if test="email != null">email = #{email},</if>
<if test="age != null">age = #{age},</if>
<if test="phoneNumber != null">phone_number = #{phoneNumber},</if>
<if test="status != null">status = #{status, typeHandler=com.example.handler.UserStatusTypeHandler},</if>
update_time = NOW()
</set>
WHERE id = #{id} AND deleted = 0
</update>
</mapper>
五、配置类整合
5.1 MyBatis-Plus 配置类
java
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
/**
* MyBatis-Plus 拦截器配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInterceptor.setMaxLimit(1000L); // 单页分页条数限制
paginationInterceptor.setOverflow(false); // 页码超出范围时回到第一页
interceptor.addInnerInterceptor(paginationInterceptor);
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防止全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
/**
* 自动填充处理器
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 设置默认状态
this.strictInsertFill(metaObject, "status", UserStatus.class, UserStatus.ACTIVE);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
};
}
/**
* 自定义配置(可选)
*/
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
// 开启驼峰命名转换
configuration.setMapUnderscoreToCamelCase(true);
// 配置默认的枚举类型处理器
configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);
};
}
}
5.2 MyBatis 原生配置(可选)
java
@Configuration
public class MybatisConfig {
/**
* 自定义类型处理器注册
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 自定义类型处理器
org.apache.ibatis.session.Configuration configuration =
new org.apache.ibatis.session.Configuration();
configuration.setDefaultEnumTypeHandler(UserStatusTypeHandler.class);
sessionFactory.setConfiguration(configuration);
return sessionFactory.getObject();
}
}
六、Service 层整合
6.1 基础 Service 接口
java
public interface UserService extends IService<User> {
// ========== MyBatis-Plus 自动生成的方法 ==========
// 以下方法无需实现:
// boolean save(User entity);
// boolean removeById(Serializable id);
// boolean updateById(User entity);
// User getById(Serializable id);
// List<User> list();
// Page<User> page(Page<User> page);
// ========== 自定义业务方法 ==========
/**
* 注册用户
*/
Result<User> register(UserRegisterDTO registerDTO);
/**
* 用户登录
*/
Result<UserLoginVO> login(UserLoginDTO loginDTO);
/**
* 复杂分页查询
*/
PageResult<UserVO> queryUserPage(UserQueryDTO queryDTO);
/**
* 批量更新用户状态
*/
Result<Void> batchUpdateStatus(List<Long> userIds, UserStatus status);
/**
* 导出用户数据
*/
void exportUsers(UserQueryDTO queryDTO, HttpServletResponse response);
/**
* 获取用户详情(包含角色信息)
*/
UserDetailVO getUserDetail(Long userId);
}
6.2 Service 实现类
java
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Result<User> register(UserRegisterDTO registerDTO) {
// 1. 校验用户名是否已存在
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername, registerDTO.getUsername());
Long count = baseMapper.selectCount(queryWrapper);
if (count > 0) {
return Result.error("用户名已存在");
}
// 2. 创建用户
User user = new User();
user.setUsername(registerDTO.getUsername());
user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
user.setEmail(registerDTO.getEmail());
user.setAge(registerDTO.getAge());
user.setPhoneNumber(registerDTO.getPhoneNumber());
// 3. 保存用户(使用MyBatis-Plus的save方法)
boolean success = save(user);
if (success) {
log.info("用户注册成功: {}", user.getUsername());
return Result.success(user);
} else {
return Result.error("用户注册失败");
}
}
@Override
public PageResult<UserVO> queryUserPage(UserQueryDTO queryDTO) {
// 使用自定义的XML分页查询
Page<User> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
List<User> users = userMapper.selectUserPage(page, queryDTO);
// 转换为VO
List<UserVO> userVOS = users.stream()
.map(this::convertToVO)
.collect(Collectors.toList());
return new PageResult<>(
userVOS,
page.getTotal(),
page.getPages(),
page.getCurrent(),
page.getSize()
);
}
@Override
public Result<UserLoginVO> login(UserLoginDTO loginDTO) {
// 1. 查询用户(使用MyBatis-Plus的lambda查询)
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername, loginDTO.getUsername())
.eq(User::getDeleted, 0);
User user = baseMapper.selectOne(queryWrapper);
if (user == null) {
return Result.error("用户不存在");
}
// 2. 验证密码
if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
return Result.error("密码错误");
}
// 3. 检查用户状态
if (user.getStatus() != UserStatus.ACTIVE) {
return Result.error("用户状态异常");
}
// 4. 生成token等逻辑...
UserLoginVO loginVO = convertToLoginVO(user);
return Result.success(loginVO);
}
@Override
public Result<Void> batchUpdateStatus(List<Long> userIds, UserStatus status) {
if (userIds == null || userIds.isEmpty()) {
return Result.error("用户ID列表不能为空");
}
// 使用MyBatis原生XML更新
for (Long userId : userIds) {
userMapper.updateStatus(userId, status);
}
log.info("批量更新用户状态成功,用户数: {}", userIds.size());
return Result.success();
}
@Override
public UserDetailVO getUserDetail(Long userId) {
// 使用复杂的联表查询(MyBatis XML)
List<UserWithRoleVO> userWithRoles = userMapper.selectUserWithRoles(userId);
if (userWithRoles.isEmpty()) {
throw new BusinessException("用户不存在");
}
return convertToDetailVO(userWithRoles.get(0));
}
// 转换方法
private UserVO convertToVO(User user) {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
return vo;
}
private UserLoginVO convertToLoginVO(User user) {
UserLoginVO vo = new UserLoginVO();
BeanUtils.copyProperties(user, vo);
// 生成token等...
return vo;
}
private UserDetailVO convertToDetailVO(UserWithRoleVO userWithRole) {
UserDetailVO vo = new UserDetailVO();
BeanUtils.copyProperties(userWithRole, vo);
return vo;
}
}
七、复杂查询场景实战
7.1 动态条件查询(MyBatis-Plus + 原生MyBatis)
java
@Service
@Slf4j
public class UserQueryService {
@Autowired
private UserMapper userMapper;
/**
* 复杂动态查询 - 结合MyBatis-Plus和原生MyBatis
*/
public List<User> complexQuery(UserComplexQueryDTO queryDTO) {
// 对于简单条件,使用MyBatis-Plus的LambdaQueryWrapper
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
// 动态添加条件
lambdaWrapper.eq(queryDTO.getStatus() != null, User::getStatus, queryDTO.getStatus())
.ge(queryDTO.getMinAge() != null, User::getAge, queryDTO.getMinAge())
.le(queryDTO.getMaxAge() != null, User::getAge, queryDTO.getMaxAge())
.like(StringUtils.isNotBlank(queryDTO.getKeyword()), User::getUsername, queryDTO.getKeyword());
// 对于复杂条件(如日期范围、联表查询),使用原生MyBatis
if (queryDTO.getStartTime() != null || queryDTO.getEndTime() != null) {
// 切换为复杂查询
return userMapper.selectByComplexCondition(queryDTO);
}
// 执行MyBatis-Plus查询
return userMapper.selectList(lambdaWrapper);
}
/**
* 统计查询 - 使用MyBatis原生XML处理复杂统计
*/
public UserStatisticsVO getUserStatistics() {
return userMapper.selectUserStatistics();
}
}
// 在Mapper中添加对应方法
public interface UserMapper extends BaseMapper<User> {
/**
* 复杂条件查询
*/
List<User> selectByComplexCondition(@Param("query") UserComplexQueryDTO query);
/**
* 用户统计
*/
UserStatisticsVO selectUserStatistics();
}
7.2 XML 中的复杂查询
xml
<!-- UserMapper.xml -->
<select id="selectByComplexCondition" resultType="com.example.entity.User">
SELECT
<include refid="Base_Column_List"/>
FROM sys_user u
<where>
u.deleted = 0
<if test="query.status != null">
AND u.status = #{query.status, typeHandler=com.example.handler.UserStatusTypeHandler}
</if>
<if test="query.minAge != null">
AND u.age >= #{query.minAge}
</if>
<if test="query.maxAge != null">
AND u.age <= #{query.maxAge}
</if>
<if test="query.keyword != null and query.keyword != ''">
AND (u.username LIKE CONCAT('%', #{query.keyword}, '%')
OR u.email LIKE CONCAT('%', #{query.keyword}, '%'))
</if>
<if test="query.startTime != null">
AND u.create_time >= #{query.startTime}
</if>
<if test="query.endTime != null">
AND u.create_time <= #{query.endTime}
</if>
</where>
ORDER BY u.create_time DESC
</select>
<select id="selectUserStatistics" resultType="com.example.vo.UserStatisticsVO">
SELECT
COUNT(*) as totalUsers,
COUNT(CASE WHEN status = 1 THEN 1 END) as activeUsers,
COUNT(CASE WHEN status = 0 THEN 1 END) as inactiveUsers,
AVG(age) as avgAge,
MAX(age) as maxAge,
MIN(age) as minAge
FROM sys_user
WHERE deleted = 0
</select>
八、事务管理与性能优化
8.1 事务配置
java
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
/**
* 事务管理器
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* 事务模板
*/
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
}
// 事务使用示例
@Service
@Slf4j
public class UserTransactionService {
@Autowired
private UserMapper userMapper;
@Autowired
private TransactionTemplate transactionTemplate;
/**
* 声明式事务
*/
@Transactional(rollbackFor = Exception.class)
public Result<Void> updateUserWithTransaction(User user) {
// 1. 更新用户基本信息
userMapper.updateById(user);
// 2. 记录操作日志(同一个事务)
// operationLogService.saveLog(...);
// 3. 发送消息通知
// messageService.sendNotification(...);
return Result.success();
}
/**
* 编程式事务
*/
public Result<Void> updateUserWithProgrammaticTransaction(User user) {
return transactionTemplate.execute(status -> {
try {
// 业务操作
userMapper.updateById(user);
// 其他操作...
return Result.success();
} catch (Exception e) {
status.setRollbackOnly();
log.error("事务执行失败", e);
return Result.error("操作失败");
}
});
}
}
8.2 性能优化配置
java
@Configuration
public class PerformanceConfig {
/**
* 二级缓存配置
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer<Object> serializer =
new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(Lazy);
serializer.setObjectMapper(mapper);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
/**
* MyBatis 缓存配置
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.disableCachingNullValues()
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
九、测试用例
9.1 单元测试
java
@SpringBootTest
@Transactional // 测试后回滚
class UserServiceTest {
@Autowired
private UserService userService;
@Autowired
private UserMapper userMapper;
@Test
void testSaveUser() {
User user = new User();
user.setUsername("testuser");
user.setPassword("encodedPassword");
user.setEmail("test@example.com");
user.setAge(25);
boolean success = userService.save(user);
assertTrue(success);
assertNotNull(user.getId());
}
@Test
void testComplexQuery() {
UserComplexQueryDTO queryDTO = new UserComplexQueryDTO();
queryDTO.setKeyword("test");
queryDTO.setMinAge(20);
queryDTO.setMaxAge(30);
List<User> users = userService.complexQuery(queryDTO);
assertNotNull(users);
}
@Test
void testBatchOperation() {
List<User> userList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setUsername("batchuser" + i);
user.setPassword("password");
user.setEmail("user" + i + "@example.com");
userList.add(user);
}
// 使用MyBatis原生批量插入
int result = userMapper.batchInsert(userList);
assertEquals(5, result);
}
@Test
void testOptimisticLock() {
User user = userMapper.selectById(1L);
user.setEmail("new@example.com");
// 模拟并发修改
User user2 = userMapper.selectById(1L);
user2.setEmail("conflict@example.com");
userMapper.updateById(user2); // 先更新,version+1
int result = userMapper.updateById(user); // 应该失败
assertEquals(0, result); // 乐观锁冲突,更新失败
}
}
十、最佳实践总结
10.1 使用场景建议
| 场景 | 推荐技术 | 理由 |
|---|---|---|
| 简单CRUD | MyBatis-Plus | 代码简洁,开发效率高 |
| 复杂查询 | 原生MyBatis XML | SQL可控性强,支持复杂逻辑 |
| 联表查询 | 原生MyBatis XML | 结果映射灵活,性能可控 |
| 存储过程 | 原生MyBatis | 原生支持,配置简单 |
| 批量操作 | 原生MyBatis | 性能更好,控制更细 |
| 动态条件 | MyBatis-Plus Lambda | 类型安全,代码简洁 |
10.2 配置建议
yaml
# 生产环境配置
mybatis-plus:
configuration:
cache-enabled: true
lazy-loading-enabled: false
multiple-result-sets-enabled: true
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl # 生产环境使用SLF4J
global-config:
db-config:
id-type: assign_id
logic-delete-field: deleted
10.3 代码规范
-
实体类:使用MyBatis-Plus注解配置表关系
-
简单查询:优先使用MyBatis-Plus LambdaQueryWrapper
-
复杂SQL:使用原生MyBatis XML配置
-
事务管理:合理使用@Transactional注解
-
性能优化:合理使用缓存,避免N+1查询
10.4 常见问题解决
-
主键冲突:确保主键策略配置正确
-
字段映射错误:检查@TableField配置
-
事务不生效:检查@Transactional配置和代理模式
-
缓存问题:清理缓存或检查缓存配置
-
分页异常:检查分页插件配置
通过合理结合 MyBatis 和 MyBatis-Plus 的优势,可以构建出既高效又灵活的数据访问层!

434

被折叠的 条评论
为什么被折叠?



