【SSM 框架 | day28 MyBatis 和 MyBatis-Plus】

部署运行你感兴趣的模型镜像

补充专栏:

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 &lt;= #{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 &lt;= #{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 &lt;= #{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 使用场景建议

场景推荐技术理由
简单CRUDMyBatis-Plus代码简洁,开发效率高
复杂查询原生MyBatis XMLSQL可控性强,支持复杂逻辑
联表查询原生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 代码规范

  1. 实体类:使用MyBatis-Plus注解配置表关系

  2. 简单查询:优先使用MyBatis-Plus LambdaQueryWrapper

  3. 复杂SQL:使用原生MyBatis XML配置

  4. 事务管理:合理使用@Transactional注解

  5. 性能优化:合理使用缓存,避免N+1查询

10.4 常见问题解决

  1. 主键冲突:确保主键策略配置正确

  2. 字段映射错误:检查@TableField配置

  3. 事务不生效:检查@Transactional配置和代理模式

  4. 缓存问题:清理缓存或检查缓存配置

  5. 分页异常:检查分页插件配置

通过合理结合 MyBatis 和 MyBatis-Plus 的优势,可以构建出既高效又灵活的数据访问层!

您可能感兴趣的与本文相关的镜像

Seed-Coder-8B-Base

Seed-Coder-8B-Base

文本生成
Seed-Coder

Seed-Coder是一个功能强大、透明、参数高效的 8B 级开源代码模型系列,包括基础变体、指导变体和推理变体,由字节团队开源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值