【Java】JPA

你希望深入了解 JPA 这个 ORM 规范,我会从它的定义、核心设计思想、核心组件、常用注解、基本使用方式以及与 Hibernate 的关系等方面,为你做全面且易懂的讲解,帮你彻底搞懂 JPA。

一、JPA 是什么?

JPA(Java Persistence API,Java 持久化 API)不是一个具体的 ORM 框架 ,而是 Java EE 官方制定的一套对象关系映射的标准规范(接口 + 注解/XML)。

简单理解:

  • JPA 是"接口/规则",定义了如何将 Java 对象映射到数据库表、如何执行 CRUD 操作等标准;
  • Hibernate、EclipseLink、OpenJPA 是 JPA 的实现(具体干活的框架),其中 Hibernate 是最主流、最成熟的实现,也是 Spring Data JPA 默认的底层实现。

JPA 诞生的核心目的:统一 Java 持久层的开发标准,避免开发者被某一个 ORM 框架绑定(比如早期只用 Hibernate 会导致代码和框架强耦合)。

二、JPA 的核心设计思想

JPA 遵循"面向对象"的设计思路,核心是将数据库表与 Java 实体类(Entity)做映射,让你通过操作 Java 对象来完成数据库操作,无需编写原生 SQL(也支持自定义 SQL)。

核心目标:

  • 消除 JDBC 代码的冗余(不用手动写 ConnectionPreparedStatement、结果集映射等);
  • 以面向对象的方式操作数据库(比如用 entityManager.persist(user) 代替 INSERT 语句);
  • 提供统一的 API,降低切换 ORM 实现的成本(比如从 Hibernate 切换到 EclipseLink 只需改配置)。

三、JPA 的核心组件

JPA 的核心由 3 部分组成,也是你使用 JPA 必须掌握的核心内容:

1. 核心 API(接口)
API 接口 作用
EntityManager JPA 的核心操作接口,用于执行实体的增删改查、获取事务、创建查询等
EntityManagerFactory 用于创建 EntityManager 的工厂类(重量级对象,全局只创建一个)
EntityTransaction 事务管理接口(也可结合 Spring 事务使用,更推荐)
Query/TypedQuery JPA 的查询接口,支持 JPQL/SQL 查询
2. 注解(核心,用于对象-表映射)

JPA 的核心注解都在 javax.persistence 包下(Spring Boot 中已自动引入),常用注解如下:

注解 作用
@Entity 标记类为 JPA 实体类,对应数据库中的一张表
@Table 配置实体类对应的数据库表名(默认类名小写)
@Id 标记字段为主键
@GeneratedValue 配置主键生成策略(如自增、UUID 等)
@Column 配置字段对应的数据库列名、长度、非空、唯一等属性(默认字段名)
@Transient 标记字段不映射到数据库(仅内存使用)
@OneToOne 一对一关联关系
@OneToMany 一对多关联关系
@ManyToOne 多对一关联关系
@ManyToMany 多对多关联关系
3. 查询语言:JPQL

JPQL(Java Persistence Query Language)是 JPA 定义的面向对象的查询语言,基于实体类和属性(而非数据库表和字段),最终会被 JPA 实现(如 Hibernate)翻译成原生 SQL。

比如:

  • JPQL:SELECT u FROM User u WHERE u.userName = ?1(User 是实体类名,userName 是实体属性)
  • 对应的 SQL:SELECT * FROM t_user WHERE user_name = ?

JPQL 支持分页、排序、聚合函数(COUNT/SUM 等),也支持原生 SQL 嵌入。

四、JPA 的基本使用(纯 JPA + Spring Boot 两种方式)

方式 1:纯 JPA 原生使用(理解底层原理)
java 复制代码
// 1. 定义实体类
import javax.persistence.*;

@Entity // 标记为实体类
@Table(name = "t_user") // 对应数据库表 t_user
public class User {
    @Id // 主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键(MySQL 适用)
    private Long id;

    @Column(name = "user_name", length = 50, nullable = false, unique = true) 
    // 映射字段:列名 user_name,长度 50,非空,唯一
    private String userName;

    @Column(nullable = false) // 非空
    private Integer age;

    @Transient // 该字段不映射到数据库
    private String tempField;

    // 必须有无参构造器(JPA 反射需要)
    public User() {}

    // 有参构造器、getter/setter 省略
}

// 2. 原生 JPA 操作(手动管理 EntityManager)
public class JpaNativeDemo {
    public static void main(String[] args) {
        // 1. 创建 EntityManagerFactory(全局唯一,重量级)
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa_demo");
        
        // 2. 创建 EntityManager(轻量级,每次操作创建/关闭)
        EntityManager em = emf.createEntityManager();
        
        // 3. 开启事务
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        
        try {
            // 4. 操作数据库:保存用户
            User user = new User();
            user.setUserName("张三");
            user.setAge(25);
            em.persist(user); // 插入数据(对应 INSERT)
            
            // 5. 查询用户
            User findUser = em.find(User.class, 1L); // 根据主键查询(对应 SELECT)
            System.out.println("查询到用户:" + findUser.getUserName());
            
            // 6. 修改用户
            findUser.setAge(26);
            em.merge(findUser); // 更新数据(对应 UPDATE)
            
            // 7. 删除用户
            // em.remove(findUser); // 删除数据(对应 DELETE)
            
            // 8. 提交事务
            tx.commit();
        } catch (Exception e) {
            // 回滚事务
            tx.rollback();
            e.printStackTrace();
        } finally {
            // 关闭资源
            em.close();
            emf.close();
        }
    }
}

// 3. 配置文件(META-INF/persistence.xml)
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             version="2.1">
    <persistence-unit name="jpa_demo" transaction-type="RESOURCE_LOCAL">
        <!-- JPA 实现类(Hibernate) -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <!-- 映射的实体类 -->
        <class>com.example.entity.User</class>
        <properties>
            <!-- 数据库连接信息 -->
            <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test?useSSL=false"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="123456"/>
            <!-- Hibernate 配置 -->
            <property name="hibernate.hbm2ddl.auto" value="update"/> <!-- 自动更新表结构 -->
            <property name="hibernate.show_sql" value="true"/> <!-- 打印 SQL -->
            <property name="hibernate.format_sql" value="true"/> <!-- 格式化 SQL -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/> <!-- 数据库方言 -->
        </properties>
    </persistence-unit>
</persistence>
方式 2:Spring Boot + Spring Data JPA(实际开发主流)

Spring Data JPA 是 Spring 对 JPA 的进一步封装,提供了通用的 CRUD 接口,无需手动写基础操作代码,是实际开发中最常用的方式:

步骤 1:引入依赖(pom.xml)
xml 复制代码
<dependencies>
    <!-- Spring Boot JPA 起步依赖(自动引入 Hibernate) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- Spring Boot Web(方便测试) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
步骤 2:配置 application.yml
yaml 复制代码
spring:
  # 数据库连接配置
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  # JPA 配置
  jpa:
    hibernate:
      ddl-auto: update # 表结构策略:none(无)、create(每次创建)、update(更新)、validate(校验)
    show-sql: true # 打印 SQL
    properties:
      hibernate:
        format_sql: true # 格式化 SQL
    open-in-view: false # 关闭 Open Session In View(避免性能问题)
    database-platform: org.hibernate.dialect.MySQL8Dialect # 数据库方言
步骤 3:编写 Repository 接口(核心,无需写实现)
java 复制代码
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;

// 继承 JpaRepository<实体类, 主键类型>,自动获得 CRUD 方法
public interface UserRepository extends JpaRepository<User, Long> {
    // 1. 按方法名自动生成 SQL(无需写 JPQL/SQL)
    User findByUserName(String userName); // 根据用户名查询
    List<User> findByAgeGreaterThan(Integer age); // 查询年龄大于指定值的用户
    
    // 2. 自定义 JPQL 查询(推荐)
    @Query("SELECT u FROM User u WHERE u.age BETWEEN :minAge AND :maxAge")
    List<User> findByAgeRange(@Param("minAge") Integer minAge, @Param("maxAge") Integer maxAge);
    
    // 3. 自定义原生 SQL 查询
    @Query(value = "SELECT * FROM t_user WHERE age = ?1", nativeQuery = true)
    List<User> findByAge(Integer age);
}
步骤 4:编写 Service 和 Controller(测试)
java 复制代码
// Service 层(整合事务)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    // 保存用户(事务)
    @Transactional
    public User saveUser(User user) {
        return userRepository.save(user); // 内置 save 方法
    }

    // 根据 ID 查询
    public User getUserById(Long id) {
        // findById 返回 Optional,避免空指针
        return userRepository.findById(id).orElse(null);
    }

    // 查询所有用户
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    // 自定义查询:按年龄范围
    public List<User> getUsersByAgeRange(Integer min, Integer max) {
        return userRepository.findByAgeRange(min, max);
    }

    // 删除用户
    @Transactional
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

// Controller 层(测试接口)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    // 新增用户
    @PostMapping
    public User addUser(@RequestBody User user) {
        return userService.saveUser(user);
    }

    // 根据 ID 查询
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    // 查询所有
    @GetMapping
    public List<User> getAll() {
        return userService.getAllUsers();
    }

    // 按年龄范围查询
    @GetMapping("/age-range")
    public List<User> getByAgeRange(@RequestParam Integer min, @RequestParam Integer max) {
        return userService.getUsersByAgeRange(min, max);
    }

    // 删除用户
    @DeleteMapping("/{id}")
    public String delete(@PathVariable Long id) {
        userService.deleteUser(id);
        return "删除成功";
    }
}

五、JPA 的核心优势与适用场景

优势:
  1. 标准化:遵循 Java 官方规范,不绑定具体实现,可灵活切换 ORM 框架;
  2. 简化开发:无需写基础 CRUD 代码,Spring Data JPA 按方法名自动生成 SQL;
  3. 面向对象:以操作 Java 对象的方式操作数据库,符合面向对象编程思想;
  4. 灵活查询:支持 JPQL、原生 SQL、方法名查询三种方式,兼顾简洁性和灵活性;
  5. 无缝整合 Spring:与 Spring Boot/Spring 事务/AOP 等生态完美融合。
适用场景:
  • 中大型企业级应用,追求代码规范、低耦合;
  • 业务逻辑以简单 CRUD 为主,无需复杂 SQL;
  • 希望快速开发,减少重复的数据库操作代码;
  • 需兼顾跨数据库兼容性(JPA 自动适配不同数据库方言)。
局限性:
  • 复杂 SQL(如多表关联、分组聚合、存储过程)不如 MyBatis 灵活;
  • 自动生成的 SQL 可能存在性能问题(需手动优化);
  • 学习成本略高(需理解 ORM 思想、JPQL、关联关系等)。

总结

  1. JPA 是ORM 规范,而非具体框架,Hibernate 是其最主流的实现,Spring Data JPA 是 Spring 对 JPA 的进一步封装(开发首选);
  2. JPA 的核心是通过注解 实现 Java 实体类与数据库表的映射,通过 EntityManager 或 Spring Data JPA 接口完成数据库操作;
  3. Spring Boot + Spring Data JPA 是实际开发的主流方式,核心优势是简化 CRUD 开发、统一规范、整合 Spring 生态,适合中大型应用的快速开发。
相关推荐
郝学胜-神的一滴2 小时前
Linux多线程编程:深入理解pthread_cancel函数
linux·服务器·开发语言·c++·软件工程
Trouvaille ~2 小时前
【C++篇】让错误被温柔对待(下):异常高级特性与最佳实践
运维·开发语言·c++·异常·raii·编程实践·基础入门
计算机毕设指导62 小时前
基于微信小程序的设备报修系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij-idea
没有bug.的程序员2 小时前
服务治理体系:从零到一的全景落地指南
java·开发语言·数据库·微服务·架构
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于SpringBoot的足球运动员训练计划管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
kylezhao20192 小时前
C#上位机开发数据持久化:xml数据导入导出
xml·开发语言·c#
-Xie-2 小时前
Redis(十六)——底层数据结构(一)
java·数据结构·redis
海南java第二人2 小时前
Spring Boot自定义注解深度解析:从入门到实战进阶
java·spring boot·后端
2501_909800812 小时前
Java多线程
java·开发语言·多线程