SpringData JPA与MyBatis对比

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);

// JPQL
@Query("SELECT u FROM User u WHERE u.department.name = :deptName")
List<User> findByDepartmentName(@Param("deptName") String deptName);

// 原生 SQL
@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

<!-- UserMapper.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; // JPA

@Autowired
private ReportMapper reportMapper; // MyBatis

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 便利,兼顾灵活性和效率

实际项目中,可以根据模块特点选择不同的持久层方案,甚至混合使用。

核心要点

  1. 动态 SQL 的常用标签:if、where、choose、foreach

  2. 一级缓存和二级缓存的区别及使用场景

  3. 参数处理:#{} 和 ${} 的区别,防止 SQL 注入

  4. 批量操作的优化:使用 foreach 或 ExecutorType.BATCH

总结

MyBatis 是一个灵活的 ORM 框架,掌握它的核心特性可以提升开发效率。在实际项目中,合理使用缓存和批量操作可以显著提升性能。


   转载规则


《SpringData JPA与MyBatis对比》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录