JPA
Spring JPA 位于 org.springframework.orm.jpa 包下,为 Java Persistence API 提供了全面的支持。它的集成方式与 Hibernate 类似,同时能够感知底层的实现,以提供额外的特性。
Spring 环境下 JPA 设置的三种选项
Spring 提供了三种方式来设置 JPA EntityManagerFactory。
1. 使用 LocalEntityManagerFactoryBean
这种方式仅适用于简单的部署环境,如独立应用程序或集成测试。它遵循 JPA 的 Java SE 引导方式,无法引用现有的 JDBC DataSource Bean,且不支持全局事务。
<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="myPersistenceUnit"/>
</bean>2. 从 JNDI 获取 EntityManagerFactory
当你部署到 Jakarta EE 服务器(如 WebLogic, WildFly)时可以使用此选项。服务器会自动探测持久化单元并将其实例化。
<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>3. 使用 LocalContainerEntityManagerFactoryBean
这是在 Spring 应用中最强大的 JPA 设置选项。 它允许你完全控制 EntityManagerFactory 的配置,支持自定义数据源,并能很好地整合本地和全局事务。
<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 注入
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();
}
}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 协调器。
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean>理解 JpaDialect 和 JpaVendorAdapter
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 管理的DataSourceBean。 - 灵活扫描:在现代 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)