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 国际许可协议 进行许可。
  目录