两种主流 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 JPAMyBatis-Plus
实体映射@Entity, @Table, @Column@TableName, @TableId
基础 CRUD继承 JpaRepository 自动获得继承 BaseMapper 自动获得
条件查询方法名推导(如 findByEmailQueryWrapper.eq("email", ...)
自定义 SQL@Query 写 JPQL写 XML 或 @Select 注解
分页需手动组合 Pageable + ListIPage + 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