Skip to content

JPA

Spring JPA 位于 org.springframework.orm.jpa 包下,为 Java Persistence API 提供了全面的支持。它的集成方式与 Hibernate 类似,同时能够感知底层的实现,以提供额外的特性。

Spring 环境下 JPA 设置的三种选项

Spring 提供了三种方式来设置 JPA EntityManagerFactory

1. 使用 LocalEntityManagerFactoryBean

这种方式仅适用于简单的部署环境,如独立应用程序或集成测试。它遵循 JPA 的 Java SE 引导方式,无法引用现有的 JDBC DataSource Bean,且不支持全局事务。

xml
<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="myPersistenceUnit"/>
</bean>

2. 从 JNDI 获取 EntityManagerFactory

当你部署到 Jakarta EE 服务器(如 WebLogic, WildFly)时可以使用此选项。服务器会自动探测持久化单元并将其实例化。

xml
<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>

3. 使用 LocalContainerEntityManagerFactoryBean

这是在 Spring 应用中最强大的 JPA 设置选项。 它允许你完全控制 EntityManagerFactory 的配置,支持自定义数据源,并能很好地整合本地和全局事务。

xml
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="someDataSource"/>
    <property name="loadTimeWeaver">
        <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
    </property>
</bean>

基于 JPA 实现 DAO

虽然 EntityManagerFactory 是线程安全的,但 EntityManager 不是。Spring 注入的 EntityManager 实际上是一个 “共享代理”,它会将调用委托给当前事务关联的真实 EntityManager

使用 @PersistenceContext 注入

java
public class ProductDaoImpl implements ProductDao {

    @PersistenceContext
    private EntityManager em;

    public Collection loadProductsByCategory(String category) {
        Query query = em.createQuery("from Product as p where p.category = :category");
        query.setParameter("category", category);
        return query.getResultList();
    }
}
kotlin
class ProductDaoImpl : ProductDao {

    @PersistenceContext
    private lateinit var em: EntityManager

    fun loadProductsByCategory(category: String): Collection<*> {
        val query = em.createQuery("from Product as p where p.category = :category")
        query.setParameter("category", category)
        return query.resultList
    }
}

提示

通过 @PersistenceContext 注入的 EntityManager 是由 Spring 管理的,它能自动感知当前的 Spring 事务。


Spring 驱动的 JPA 事务

推荐的 JPA 策略是使用 JpaTransactionManager 进行本地事务管理。它支持事务隔离级别、只读优化等特性,且不需要重量级的 JTA 协调器。

xml
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emf"/>
</bean>

理解 JpaDialectJpaVendorAdapter

  • JpaDialect:允许 Spring 访问特定供应商(如 Hibernate 或 EclipseLink)的高级特性,例如获取底层的 JDBC 连接、翻译特定的持久化异常等。
  • JpaVendorAdapter:是一个更宽泛的适配器,它结合了 JpaDialect 与其他供应商特定的设置。使用 HibernateJpaVendorAdapter 是配置 Hibernate JPA 的最简便方式。

补充教学

1. @PersistenceContext@Autowired 的区别

这是很多初学者困惑的地方。为什么注入 EntityManager 要用专用注解?

  • 原因EntityManager 实例本身不是线程安全的。
  • Spring 的魔法:当你使用 @PersistenceContext 时,Spring 注入的不是一个真实的 EntityManager 实例,而是一个 代理(Proxy)
  • 行为:每当你调用代理的方法时,它会去当前线程查找是否存在已绑定的 EntityManager(通常由 @Transactional 启动)。如果没有,它会创建一个临时的。这保证了在单例 DAO 中并发访问时,每个线程都能拿到属于自己的、在事务上下文中的实体管理器。

2. 为什么 LocalContainerEntityManagerFactoryBean 是首选?

相比于简单的 LocalEntityManagerFactoryBean,容器版本提供了以下核心优势:

  • 完全解耦:你的 persistence.xml 不需要再包含数据库驱动、URL 和密码信息。这些全部交给 Spring 管理的 DataSource Bean。
  • 灵活扫描:在现代 Spring Boot 应用中,你甚至不需要 persistence.xml,Spring 可以自动扫描实体类并将其注册到持久化单元。
  • 强大的后置处理:它可以轻松集成多数据源、多持久化单元。

3. 理解 Load-Time Weaving (LTW)

有些 JPA 实现(例如 EclipseLink)需要通过“字节码转换”来增强实体类(用于实现延迟加载、脏检查等)。

  • LTW:是指在类被装载到 JVM 时,动态地修改字节码。
  • Spring 的角色:Spring 通过 LoadTimeWeaver 抽象了这一过程,使得你可以针对不同的服务器(Tomcat, JBoss)或直接使用 JVM 代理(Spring Instrument)来开启此功能。
  • Hibernate 用户注意:Hibernate 通常不需要 LTW,因为它默认使用代理对象(Proxies)而不是直接增强实体类字节码。 No browser pages are currently open. Running terminal commands:
  • pnpm run docs:dev (in f:\project\doc, running for 43m3s)

Based on Spring Framework.