两种主流 Java 企业级持久层方案
详情
| 方案 | 技术栈 | 定位 |
|---|---|---|
| 方案一 | JDBC ← JPA ← Hibernate ← Spring Data JPA | 全自动 ORM 方案(面向对象) |
| 方案二 | JDBC ← MyBatis ← MyBatis-Plus | 半自动 SQL 映射方案(面向 SQL) |
⚠️ 注意:两者都基于 JDBC,但 JPA 与 MyBatis 互斥,不能共存。
一、核心对比:优缺点 & 适用人群
| 维度 | 方案一:Spring Data JPA | 方案二:MyBatis + MyBatis-Plus |
|---|---|---|
| 抽象层级 | 高(操作对象,不写 SQL) | 中(写 SQL,但映射自动化) |
| SQL 控制力 | 弱(自动生成,难干预) | 强(完全掌控每条 SQL) |
| 开发效率(CRUD) | 高(方法名即查询) | 中(MP 提供 BaseMapper) |
| 复杂查询支持 | 弱(需 JPQL / Specification) | 强(原生 SQL + 动态标签) |
| 性能调优 | 困难(N+1、懒加载陷阱) | 容易(SQL 可见、可 EXPLAIN) |
| 学习曲线 | 较高(需理解 Session、缓存、事务) | 较低(会 SQL 即可上手) |
| 跨数据库迁移 | 好(Hibernate 自动适配方言) | 一般(SQL 可能含方言) |
| 典型用户 | 欧美企业、Spring 生态重度用户 | 中国互联网公司(阿里、腾讯等) |
| 适合团队 | 面向对象思维强、DDD 实践者 | DBA 主导、SQL 优化需求高 |
二、统一业务场景实践
我们要实现以下功能:
- 创建用户
- 根据邮箱查用户
- 分页查询用户(按姓名模糊搜索)
- 判断邮箱是否已存在
实体字段:
id (Long), email (String), name (String)三、方案一:Spring Data JPA 实现
1. 依赖(Maven)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>2. 实体类
import javax.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String email;
private String name;
// getters / setters / toString 略
}3. Repository 接口(无需实现类!)
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
// 自动生成:根据 email 查询
User findByEmail(String email);
// 自动生成:判断是否存在
boolean existsByEmail(String email);
// 自动生成:分页 + 模糊查询(忽略大小写)
List<User> findByNameContainingIgnoreCase(String name, org.springframework.data.domain.Pageable pageable);
// 如需更复杂逻辑,可用 @Query(非必须)
@Query("SELECT u FROM User u WHERE u.name LIKE %:keyword%")
List<User> searchByName(@Param("keyword") String keyword);
}4. Service 使用
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(String email, String name) {
if (userRepository.existsByEmail(email)) {
throw new IllegalArgumentException("Email already exists");
}
User user = new User();
user.setEmail(email);
user.setName(name);
return userRepository.save(user); // 自动 insert 或 update
}
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
public Page<User> searchUsers(String name, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return new PageImpl<>(
userRepository.findByNameContainingIgnoreCase(name, pageable),
pageable,
userRepository.count()
);
}
}优点体现:
- 无 SQL、无实现类、无模板代码
- 方法名自动转为查询逻辑
四、方案二:MyBatis + MyBatis-Plus 实现
1. 依赖(Maven)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>2. 实体类(无需 JPA 注解)
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("users") // 指定表名
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String email;
private String name;
// getters / setters / toString 略
}3. Mapper 接口(继承 BaseMapper)
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
public interface UserMapper extends BaseMapper<User> {
// MP 已提供 selectById, insert, update, delete 等
// 自定义方法:根据 email 查询
User selectByEmail(@Param("email") String email);
// MP 的 QueryWrapper 可完成大部分查询,无需 XML
// 但复杂 SQL 仍可写 XML(此处省略)
}💡 实际上,连
selectByEmail都可以不用写,用QueryWrapper即可!
4. (可选)XML 文件(如果不用 Wrapper)
<!-- resources/mapper/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">
<select id="selectByEmail" resultType="com.example.entity.User">
SELECT * FROM users WHERE email = #{email}
</select>
</mapper>若使用 MyBatis-Plus 的
QueryWrapper,则不需要 XML!
5. Service 使用(推荐用 Wrapper)
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User createUser(String email, String name) {
// 检查邮箱是否存在
boolean exists = userMapper.exists(
new QueryWrapper<User>().eq("email", email)
);
if (exists) {
throw new IllegalArgumentException("Email already exists");
}
User user = new User();
user.setEmail(email);
user.setName(name);
userMapper.insert(user); // 自动填充 ID
return user;
}
public User getUserByEmail(String email) {
// 方式1:用自定义方法(需 XML 或注解)
// return userMapper.selectByEmail(email);
// 方式2:用 QueryWrapper(推荐,无需 XML)
return userMapper.selectOne(
new QueryWrapper<User>().eq("email", email)
);
}
public IPage<User> searchUsers(String name, int page, int size) {
return userMapper.selectPage(
new Page<>(page, size),
new QueryWrapper<User>().like("name", name)
);
}
}优点体现:
- SQL 可控(必要时可写 XML 优化)
QueryWrapper提供链式 API,避免手写 SQL- 分页插件开箱即用
五、关键差异总结(通过 User 示例)
| 功能 | Spring Data JPA | MyBatis-Plus |
|---|---|---|
| 实体映射 | 用 @Entity, @Table, @Column | 用 @TableName, @TableId |
| 基础 CRUD | 继承 JpaRepository 自动获得 | 继承 BaseMapper 自动获得 |
| 条件查询 | 方法名推导(如 findByEmail) | QueryWrapper.eq("email", ...) |
| 自定义 SQL | 用 @Query 写 JPQL | 写 XML 或 @Select 注解 |
| 分页 | 需手动组合 Pageable + List | IPage + Page 插件自动分页 |
| 是否需要 XML | ❌ 不需要 | ⚠️ 简单查询不需要,复杂查询建议用 |
六、如何选择?——适用人群建议
选 Spring Data JPA
- 习惯面向对象编程,讨厌写 SQL;
- 项目模型清晰,以 CRUD 为主;
- 团队采用 领域驱动设计(DDD);
- 需要快速搭建后台管理系统;
- 数据库可能切换(如从 MySQL 到 PostgreSQL)。
选 MyBatis / MyBatis-Plus
- 你是 SQL 高手,希望掌控每条查询;
- 项目有复杂报表、多表 JOIN、动态条件;
- 数据库由 DBA 设计,表结构不“面向对象”;
- 需要 极致性能优化(如避免 N+1);
- 在 中国互联网公司 工作(生态更成熟)。
结语
- Spring Data JPA:“约定优于配置”,适合标准化、模型驱动的系统。
- MyBatis-Plus:“灵活可控 + 开发效率”,适合 SQL 导向、性能敏感的场景。
两者都是成熟的企业方案,没有绝对优劣,只有是否匹配团队和业务。
现代趋势:MyBatis-Plus 极大缩小了与 Spring Data JPA 的开发效率差距,同时保留 SQL 控制力,因此在国内成为事实标准;而 Spring Data JPA 在国际 Spring 生态中仍是首选。
关联网络
演化日志
- v0.1 (2026-02-02):补充关联网络、演化日志、待办事项
待办事项
- Maven 依赖
- 表结构不“面向对象”;
- 领域驱动设计(DDD);
- 按照是什么?没有之前?为什么使用?核心组件/概念,使用步骤,示例代码,局限性。这样的流程为我详细解释下DDD