Spring的JPA支持的好处
- 更容易且更强大的持久化单元配置,不必使用META-INF/persistence.xml
- 更容易测试
- 自动EntityManager管理,且可注入DAO Bean中
- 常见的数据访问异常,异常层次结构变为Spring的
- 集成事务管理
在Spring容器中配置JPA
Spring在同一个项目中提供三种不同方法配置EntityManagerFactory。
- LocalEntityManagerFactoryBean
最基础且功能最有限的方法,从META-INF/persistence.xml中读取JPA配置,不允许使用Spring管理的DataSource实例且不支持分布式事务管理 - 通过JNDI进行EntityManagerFactory查找
此时的Spring EntityManagerFactory Bean只不过是一个从JNDI获取的持久化单元委托 - LocalContainerEntityFactoryBean,最强大和灵活的JPA配置方法,对EntityManagerFactory完全控制
使用LocalContainerEntityManagerFactoryBean配置和使用JPA
接上一节的Book和Student类创建配置类
@Configuration
public class Ch5Configuration {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:tcp://localhost/~/test");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Map<String,?> jpaProperties() {
Map<String,String> jpaPropertiesMap = new HashMap<String,String>();
jpaPropertiesMap.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
jpaPropertiesMap.put("hibernate.hbm2ddl.auto", "update");
return jpaPropertiesMap;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factoryBean =
new LocalContainerEntityManagerFactoryBean();
factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan("com.wiley.beginningspring.ch5");
factoryBean.setJpaPropertyMap(jpaProperties());
return factoryBean;
}
}
为使用Spring管理的DataSource和EntityManagerFactory,主函数中也要创建Spring容器
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(
Ch5Configuration.class);
EntityManagerFactory entityManagerFactory = applicationContext
.getBean(EntityManagerFactory.class);
EntityManager entityManager = entityManagerFactory
.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Student student = new Student();
student.setFirstName("Madoka");
student.setLastName("Kaname");
entityManager.persist(student);
transaction.commit();
entityManager.close();
}
}
完成插入操作后查看数据库:
学生成功被插入。
基于纯JPA实现DAO
可以在字段或方法级别使用@PersistenceContext和@PersistenceUnit注解,前者表示对EntityManagerFactory的依赖,后者表示对EntityManager的依赖
使用@PersistenceUnit获取EntityManagerFactory
此时需要在Dao实现类中加入EntityManagerFactory字段并在其上放置@PersistenceUnit注解将entityManagerFactory注入到DAO类中。
public class StudentDaoJpaImpl {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public void save(Student student) {
EntityManager entityManager = entityManagerFactory
.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(student);
transaction.commit();
entityManager.close();
}
}
此时还要在配置文件中加入DAO类的Bean:
@Bean
public StudentDaoJpaImpl studentDao() {
StudentDaoJpaImpl dao = new StudentDaoJpaImpl();
return dao;
}
然后在主函数里用该DAO类插入一个Student实例:
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(
Ch5Configuration.class);
StudentDaoJpaImpl dao = applicationContext.getBean(StudentDaoJpaImpl.class);
Student student = new Student();
student.setFirstName("Sayaka");
student.setLastName("Miki");
dao.save(student);
}
}
运行程序发现插入成功
使用@PersistenceContext获取EntityManager
此时要用@EnableTransactionManagement注解标识配置类启用Spring容器管理事务,新配置类为:
@Configuration
@EnableTransactionManagement
public class Ch5Configuration {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:tcp://localhost/~/test");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private Map<String,?> jpaProperties() {
Map<String,String> jpaPropertiesMap = new HashMap<String,String>();
jpaPropertiesMap.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
jpaPropertiesMap.put("hibernate.hbm2ddl.auto", "update");
return jpaPropertiesMap;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factoryBean =
new LocalContainerEntityManagerFactoryBean();
factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan("com.wiley.beginningspring.ch5");
factoryBean.setJpaPropertyMap(jpaProperties());
return factoryBean;
}
@Bean
public StudentDaoJpaImpl studentDao() {
StudentDaoJpaImpl dao = new StudentDaoJpaImpl();
return dao;
}
@Bean
@Autowired
public PlatformTransactionManager transactionManager(
EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public BookDao bookDao() {
BookDaoJpaImpl bean = new BookDaoJpaImpl();
return bean;
}
@Bean
public BookService bookService() {
BookServiceImpl bean = new BookServiceImpl();
bean.setBookDao(bookDao());
return bean;
}
}
然后创建BookDao接口和它的实现类进行持久化操作。由于@PersistenceContext注解是与EntityManager绑定,因此被注入的是EntityManager实例。
public interface BookDao {
public void save(Book book);
}
public class BookDaoJpaImpl implements BookDao {
@PersistenceContext
private EntityManager entityManager;
@Override
public void save(Book book) {
entityManager.persist(book);
}
}
再创建服务层的BookService接口和其实现类处理事务管理。
public interface BookService {
public void save(Book book);
}
@Transactional
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save(Book book) {
bookDao.save(book);
}
}
以上二者的Bean都已经定义在了新配置文件中完成了向服务层注入dao层。
最后在主函数中执行对bookService的Bean查找,对新的Book实体进行持久化操作。
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(Ch5Configuration.class);
BookService bookService = applicationContext.getBean(BookService.class);
Book book = new Book();
book.setName("The Order of Things");
bookService.save(book);
}
}
运行得到结果,成功插入书籍
处理和转换异常
只需要用@Repository注解标注DAO实现类,
@Repository
public class BookDaoJpaImpl implements BookDao {
@PersistenceContext
private EntityManager entityManager;
@Override
public void save(Book book) {
entityManager.persist(book);
}
}
然后在配置类中加入PersistenceExceptionTranslationPostProcessor的Bean,
@Bean
public static PersistenceExceptionTranslationPostProcessor
persistenceExceptionTranslationPostProcessor() {
PersistenceExceptionTranslationPostProcessor bean =
new PersistenceExceptionTranslationPostProcessor();
return bean;
}
就可以在服务层通过一个try-catch语句捕捉转化为DataAccessException的异常了。
@Override
public void save(Book book) {
try{
//Perform some business logic here
bookDao.save(book);
} catch (DataAccessException ex) {
//handle the data access exception without depending on particular data access technology used beneath
}
在Spring环境中进一步配置JPA
JpaDialect
通过该接口可以配置如下高级功能:
- 应用特定事务语义,比如自定义隔离级别或者事务超时
- 获取事务性JDBC连接,以便向基于JDBC的DAO公开
- 将PersistenceExcept转换到Spring DataAccessException的高级功能
默认实现为不提供任何上述功能的DefaultJpaDialect,使用上述功能需要指定特定方言类,比如HibernateJpaDialect。
JpaVendorAdapter
用于激活与供应商有关的高级功能,例如可以将LocalContainerEntityManagerFactoryBean中一下代码注释掉集中配置和管理:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factoryBean =
new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
//factoryBean.setJpaPropertyMap(jpaProperties());
//factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
//factoryBean.setJpaDialect(new HibernateJpaDialect());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
return factoryBean;
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setDatabase(Database.H2);
return jpaVendorAdapter;
}
JPA加载时编制
在配置类上添加@EnableLoadTimeWeaving启用加载时编织