SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(中),springinaction笔记
SpringInAction笔记(十一)—— 使用对象-关系映射持久化数据(中),springinaction笔记
Spring与Java持久化API
Java持久化API(Java Persistence API,JPA)诞生在EJB 2实体Bean的 废墟之上,并成为下一代Java持久化标准。JPA是基于POJO的持久化机制,它从Hibernate和Java数据对象(Java Data Object,JDO)上借鉴了很多理念并加入了Java 5注解的特性。
在Spring中使用JPA的第一步是要在Spring应用上下文中将实体管理器工厂(entity manager factory)按照bean的形式来进行配置。
11.2.1 配置实体管理器工厂
基于JPA的应用程序需要使用EntityManagerFactory 的实现类来获取EntityManager实例。JPA定义了两种类型的实体管理器:
- 应用程序管理类型(Application-managed):当应用程序向实体 管理器工厂直接请求实体管理器时,工厂会创建一个实体管理 器。在这种模式下,程序要负责打开或关闭实体管理器并在事务 中对其进行控制。这种方式的实体管理器适合于不运行在Java EE容器中的独立应用程序。
- 容器管理类型(Container-managed):实体管理器由Java EE创 建和管理。应用程序根本不与实体管理器工厂打交道。相反,实 体管理器直接通过注入或JNDI来获取。容器负责配置实体管理器 工厂。这种类型的实体管理器最适用于Java EE容器,在这种情 况下会希望在persistence.xml指定的JPA配置之外保持一些自己对 JPA的控制。
以上的两种实体管理器实现了同一个EntityManager接口。
应用程序管理类型的EntityManager是由EntityManagerFactory创建的,而EntityManagerFactory是通过 PersistenceProvider的createEntityManagerFactory() 方法得到的。与此相对,容器管理类型的EntityManagerFactory是通过PersistenceProvider的 createContainerEntityManager Factory()方法获得的。
不管使用哪种EntityManagerFactory,Spring 都会负责管理EntityManager。如果你使用的是应用程序管理类型的实体管理器,Spring承担了应用程序的角色并以透明的方式处理EntityManager。在容器管理的场景下,Spring会担当容器的角色。
这两种实体管理器工厂分别由对应的Spring工厂Bean创建:
- LocalEntityManagerFactoryBean生成应用程序管理类型的EntityManager-Factory;
- LocalContainerEntityManagerFactoryBean生成容器管理类型的Entity-ManagerFactory。
不管选择应用程序管理类型,还是容器管理类型的 EntityManager Factory,对于基于Spring的应用程序来讲是完
全透明的。当组合使用Spring和JPA时,处 理EntityManagerFactory的复杂细节被隐藏了起来,数据访问代码只需关注它们的真正目标即可,也就是数据访问。
配置应用程序管理类型的JPA
对于应用程序管理类型的实体管理器工厂来说,它绝大部分配置信息 来源于一个名为persistence.xml的配置文件。这个文件必须位于类路径下的META-INF目录下。 persistence.xml的作用在于定义一个或多个持久化单元。持久化单元是同一个数据源下的一个或多个持久化类。简单来讲, persistence.xml列出了一个或多个的持久化类以及一些其他的配置如 数据源和基于XML的配置文件。
如下是一个典型的persistence.xml文件,它是用于Spittr应用程序的:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0">
<persistence-unit name="spitterPU">
<class>com.habuma.spittr.domain.Spitter</class>
<class>com.habuma.spittr.domain.Spittle</class>
<properties>
<property name="toplink.jdbc.driver" value="org.hsqldb.jdbcDriver" />
<property name="toplink.jdbc.url"
value="jdbc:hsqldb:hsql://localhost/spitter/spitter" />
<property name="toplink.jdbc.user" value="sa" />
<property name="toplink.jdbc.password" value="" />
</properties>
</persistence-unit>
</persistence>
通过以下的@Bean注解方法在Spring中声明LocalEntityManagerFactoryBean:
@Bean
public LocalEntityManagerFactoryBean entityManagerFactoryBean() {
LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
emfb.setPersistenceUnitName("spitterPU");
return emfb;
}
赋给persistenceUnitName属性的值就是persistence.xml中持久化单元的名称。
创建应用程序管理类型的EntityManagerFactory都是在 persistence.xml中进行的,而这正是应用程序管理的本意。在应用程序管理的场景下(不考虑Spring时),完全由应用程序本身来负责获 取EntityManagerFactory,这是通过JPA实现的 PersistenceProvider做到的。如果每次请求EntityManagerFactory时都需要定义持久化单元,那代码将会迅速膨胀。通过将其配置在persistence.xml中,JPA就能够在这个特定的位置查找持久化单元定义了。
关于在应用程序管理中使用JPA的示例,请参考使用 Spring Data JPA 简化 JPA 开发
使用容器管理类型的JPA
容器管理的JPA采取了一个不同的方式。当运行在容器中时,可以使用容器(在我们的场景下是Spring)提供的信息来生 成EntityManagerFactory。
如下的@Bean注解方法声明了在Spring中如何使用LocalContainerEntity-ManagerFactoryBean来配置容器管理类型的JPA:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setDataSource(dataSource);
emfb.setJpaVendorAdapter(jpaVendorAdapter);
emfb.setJpaProperties(hibernateProperties());
emfb.setPackagesToScan("spittr");
return emfb;
}
private Properties hibernateProperties() {
Properties props = new Properties();
//输出格式化后的sql,更方便查看
props.setProperty("hibernate.format_sql", "false");
//指定Hibernate启动的时候自动创建或更新表结构
props.setProperty("hibernate.hbm2ddl.auto", "update");
//关闭beanvalitionFactory自动验证
props.setProperty("javax.persistence.validation.mode", "none");
return props;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager manager = new JpaTransactionManager();
manager.setEntityManagerFactory(emf);
return manager;
}
LocalContainerEntityManagerFactoryBean 会扫描spittr包,查找带有@Entity注解的类。因此,没有必要在persistence.xml文件中进行声明了。同时,因为DataSource也是注入到LocalContainerEntityManagerFactoryBean中的,所以也没有必要在 persistence.xml文件中配置数据库信息了。那么结论就是,persistence.xml文件完全没有必要存在了!你尽可以将其删除,让 LocalContainerEntityManagerFactoryBean来处理这些事情。
jpaVendorAdapter属性用于指明所使用的是哪一个厂商的JPA实现。Spring提供了多个JPA厂商适配器:
- EclipseLinkJpaVendorAdapter
- HibernateJpaVendorAdapter
- OpenJpaVendorAdapter
在本例中,我们使用Hibernate作为JPA实现,所以将其配置 为Hibernate-JpaVendorAdapter:
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
//设置所使用的数据库为MySQL
adapter.setDatabase(Database.MYSQL);
//是否在控制台打印生成的SQL语句
adapter.setShowSql(true);
//是否在控制台打印生成的SQL语句
adapter.setGenerateDdl(false);
adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
return adapter;
}
从JNDI获取实体管理器工厂
使用如下的Java配置来获取EntityManagerFactory:
@Bean(destroyMethod="")
@Profile("jndi")
public JndiObjectFactoryBean jndiDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();
//jndiObjectFB.setJndiName("jndi/spittrDB");
jndiObjectFB.setJndiName("java:comp/env/jndi/spittrDB");
jndiObjectFB.setResourceRef(true);
jndiObjectFB.setProxyInterface(javax.sql.DataSource.class);
return jndiObjectFB;
}
尽管这种方法没有返回EntityManagerFactory,但是它的结果就 是一个EntityManagerFactory bean。这是因为它所返回的 JndiObjectFactoryBean是FactoryBean接口的实现,它能够创建EntityManagerFactory。 不管你采用何种方式得到EntityManagerFactory,一旦得到这样的对象,接下来就可以编写Repository了。
11.2.2 编写基于JPA的Repository
正如Spring对其他持久化方案的集成一样,Spring对JPA集成也提供了JpaTemplate模板以及对应的支持类JpaDaoSupport。但是,为了实现更纯粹的JPA方式,基于模板的JPA已经被弃用了。
程序清单11.2 不使用Spring模板的纯JPA Repository
JpaSpittleRepository:
package spittr.data;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import spittr.Spittle;
@Repository
@Transactional
public class JpaSpittleRepository implements SpittleRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Spittle> findSpittles(int count) {
// TODO Auto-generated method stub
return (List<Spittle>) entityManager.createQuery("select s from Spittle s order by s.postedTime desc")
.setMaxResults(count)
.getResultList();
}
@Override
public Spittle findOne(long id) {
// TODO Auto-generated method stub
return entityManager.find(Spittle.class, id);
}
@Override
public void save(Spittle spittle) {
// TODO Auto-generated method stub
entityManager.persist(spittle);
}
@Override
public List<Spittle> findBySpitterId(long spitterId) {
// TODO Auto-generated method stub
return (List<Spittle>) entityManager.createQuery("select s from Spittle s, Spitter sp where s.spitter = sp and sp.id=? order by s.postedTime desc")
.setParameter(1, spitterId)
.getResultList();
}
@Override
public void delete(long id) {
// TODO Auto-generated method stub
entityManager.remove(findOne(id));
}
@Override
public long count() {
// TODO Auto-generated method stub
return entityManager.createQuery("select s from Spittle s").getResultList().size();
}
}
JpaSpitterRepository:
package spittr.data;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import spittr.Spitter;
@Repository
@Transactional
public class JpaSpitterRepository implements SpitterRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public void save(Spitter spitter) {
// TODO Auto-generated method stub
entityManager.persist(spitter);
}
@Override
public Spitter findByUsername(String username) {
// TODO Auto-generated method stub
return (Spitter) entityManager.createQuery("select s from Spitter s where s.username=?").setParameter(1, username).getSingleResult();
}
@Override
public Spitter findOne(long id) {
// TODO Auto-generated method stub
return entityManager.find(Spitter.class, id);
}
@Override
public List<Spitter> findAll() {
// TODO Auto-generated method stub
return (List<Spitter>) entityManager.createQuery("select s from Spitter s").getResultList();
}
@Override
public long count() {
// TODO Auto-generated method stub
return findAll().size();
}
}
借助@PersistentContext注解为JpaSpitterRepository 设置EntityManager。这样的话,在每个方法中就没有必要再通过 EntityManagerFactory创建EntityManager了。@PersistenceContext并不会真正注入EntityManager——至少,精确来讲不是这样的。它没有将真正的EntityManager设置给Repository,而是给了它一 个EntityManager的代理。真正的EntityManager是与当前事务相关联的那一个,如果不存在这样的EntityManager的话,就会创 建一个新的。这样的话,我们就能始终以线程安全的方式使用实体管 理器。
JpaSpitterRepository使用了@Repository 和@Transactional注解。@Transactional表明这个Repository 中的持久化方法是在事务上下文中执行的。 对于@Repository注解,它的作用与开发Hibernate上下文Session版 本的Repository时是一致的。由于没有使用模板类来处理异常,所以我们需要为Repository添加@Repository注解,这 样PersistenceExceptionTranslationPostProcessor就会知道要将这个bean产生的异常转换成Spring的统一数据访问异常。
既然提到了 PersistenceExceptionTranslationPostProcessor,要记住的是我们需要将其作为一个bean装配到Spring中,就像我们在 Hibernate样例中所做的那样:
@Bean
public BeanPostProcessor persistenceTranceslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
提醒一下,不管对于JPA还是Hibernate,异常转换都不是强制要求的。如果你希望在Repository中抛出特定的JPA或Hibernate异常,只需将PersistenceException-TranslationPostProcessor省略掉即可,这样原来的异常就会正常地处理。但是,如果使用了Spring 的异常转换,你会将所有的数据访问异常置于Spring的体系之下,这样以后切换持久化机制的话会更容易。
相关文章
- 暂无相关文章
用户点评