SpringData JPA与MyBatis对比
MyBatis 把 SQL 的控制权交给开发者,这是它的优点也是它的难点。动态 SQL、缓存机制、参数处理都是需要掌握的知识点。本文讲实际项目中的使用经验和优化技巧。
设计理念对比
| 特性 |
JPA |
MyBatis |
| 设计思想 |
ORM(对象关系映射) |
SQL Mapping(SQL映射) |
| SQL 控制 |
自动生成 |
手动编写 |
| 学习曲线 |
较陡 |
平缓 |
| 灵活性 |
较低 |
高 |
| 性能调优 |
较难 |
容易 |
| 复杂查询 |
较麻烦 |
方便 |
| 数据库迁移 |
容易 |
较麻烦 |
| 缓存 |
内置二级缓存 |
需手动配置 |
JPA 示例
#
实体定义
@Entity @Table(name = "t_user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "user_name", nullable = false, length = 50) private String username; @Column(name = "email") private String email; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dept_id") private Department department; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List<Order> orders; }
|
#
Repository
public interface UserRepository extends JpaRepository<User, Long> { List<User> findByUsernameContainingAndStatus(String username, Integer status); @Query("SELECT u FROM User u WHERE u.department.name = :deptName") List<User> findByDepartmentName(@Param("deptName") String deptName); @Query(value = "SELECT * FROM t_user WHERE create_time > ?1", nativeQuery = true) List<User> findRecentUsers(LocalDateTime date); Page<User> findByStatus(Integer status, Pageable pageable); @Modifying @Query("UPDATE User u SET u.status = ?1 WHERE u.id = ?2") int updateStatus(Integer status, Long id); }
|
#
使用
@Service public class UserService { @Autowired private UserRepository userRepository; public User getUser(Long id) { return userRepository.findById(id).orElse(null); } public Page<User> listUsers(int page, int size) { return userRepository.findByStatus(1, PageRequest.of(page, size)); } }
|
MyBatis 示例
#
Mapper 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="userResultMap" type="com.example.entity.User"> <id property="id" column="id"/> <result property="username" column="user_name"/> <result property="email" column="email"/> <association property="department" javaType="Department"> <id property="id" column="dept_id"/> <result property="name" column="dept_name"/> </association> </resultMap> <select id="selectById" resultMap="userResultMap"> SELECT u.*, d.name as dept_name FROM t_user u LEFT JOIN t_department d ON u.dept_id = d.id WHERE u.id = #{id} </select> <select id="selectByCondition" resultMap="userResultMap"> SELECT u.*, d.name as dept_name FROM t_user u LEFT JOIN t_department d ON u.dept_id = d.id <where> <if test="username != null"> AND u.user_name LIKE CONCAT('%', #{username}, '%') </if> <if test="status != null"> AND u.status = #{status} </if> </where> ORDER BY u.create_time DESC </select> <insert id="insert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO t_user (user_name, email, status) VALUES (#{username}, #{email}, #{status}) </insert> <update id="update"> UPDATE t_user <set> <if test="username != null">user_name = #{username},</if> <if test="email != null">email = #{email},</if> </set> WHERE id = #{id} </update> </mapper>
|
#
Mapper 接口
@Mapper public interface UserMapper { User selectById(Long id); List<User> selectByCondition(@Param("username") String username, @Param("status") Integer status); int insert(User user); int update(User user); }
|
#
使用
@Service public class UserService { @Autowired private UserMapper userMapper; public User getUser(Long id) { return userMapper.selectById(id); } public List<User> searchUsers(String username, Integer status) { return userMapper.selectByCondition(username, status); } }
|
MyBatis-Plus(增强)
@Mapper public interface UserMapper extends BaseMapper<User> { }
@Service public class UserService extends ServiceImpl<UserMapper, User> { public List<User> getActiveUsers() { return lambdaQuery() .eq(User::getStatus, 1) .like(StringUtils.isNotBlank(name), User::getUsername, name) .list(); } public IPage<User> getUserPage(int current, int size) { return page(new Page<>(current, size), new QueryWrapper<User>().orderByDesc("create_time")); } }
|
选型建议
| 场景 |
推荐 |
| 快速开发、CRUD 为主 |
JPA / MyBatis-Plus |
| 复杂 SQL、性能敏感 |
MyBatis |
| 多表关联、对象关系复杂 |
JPA |
| 遗留系统、SQL 已存在 |
MyBatis |
| 团队熟悉 SQL |
MyBatis |
| 团队熟悉 Hibernate |
JPA |
| 需要数据库无关性 |
JPA |
混合使用
@Service public class OrderService { @Autowired private OrderRepository orderRepository; @Autowired private ReportMapper reportMapper; public Order getOrder(Long id) { return orderRepository.findById(id).orElse(null); } public List<Map<String, Object>> getReport() { return reportMapper.selectComplexReport(); } }
|
总结
- JPA:适合面向对象、快速开发、简单查询的场景
- MyBatis:适合复杂 SQL、性能优化、遗留系统的场景
- MyBatis-Plus:在 MyBatis 基础上提供 CRUD 便利,兼顾灵活性和效率
实际项目中,可以根据模块特点选择不同的持久层方案,甚至混合使用。
核心要点
动态 SQL 的常用标签:if、where、choose、foreach
一级缓存和二级缓存的区别及使用场景
参数处理:#{} 和 ${} 的区别,防止 SQL 注入
批量操作的优化:使用 foreach 或 ExecutorType.BATCH
总结
MyBatis 是一个灵活的 ORM 框架,掌握它的核心特性可以提升开发效率。在实际项目中,合理使用缓存和批量操作可以显著提升性能。