11.1.2 构建不依赖于 Spring 的 Hibernate 代码

在 Spring 和 Hibernate 的早期岁月中,编写 Repository 类将会涉及到使用 Spring 的 HibernateTemplate。HibernateTemplate 能够保证每个事务使用同一个 Session。但是这种方式的弊端在于我们的 Repository 实现会直接与 Spring 耦合。

现在的最佳实践是不再使用 HibernateTemplate,而是使用上下文 Session (Contextual session)。通过这种方式,会直接将 Hibernate SessionFactory 装配到 Repository 中,并使用它来获取 Session,如下面的程序清单所示。

程序清单 11.1 借助 Hibernate Session 实现不依赖于 Spring 的 Repository
@Inject
public HibernateSpitterRepository(SessionFactory sessionFactory) {
  this.sessionFactory = sessionFactory;
}

private Session currentSession() {
  return sessionFactory.getCurrentSession();
}
	
public long count() {
  return findAll().size();
}

public Spitter save(Spitter spitter) {
  Serializable id = currentSession().save(spitter);
  return new Spitter((Long) id, 
          spitter.getUsername(), 
	      spitter.getPassword(), 
	      spitter.getFullName(),
	      spitter.getEmail(), 
	      spitter.isUpdateByEmail());
}

public Spitter findOne(long id) {
  return (Spitter) currentSession().get(Spitter.class, id); 
}

public Spitter findByUsername(String username) {		
  return (Spitter) currentSession() 
			.createCriteria(Spitter.class) 
			.add(Restrictions.eq("username", username))
			.list().get(0);
}

public List<Spitter> findAll() {
  return (List<Spitter>) currentSession() 
			.createCriteria(Spitter.class).list(); 
}

在程序清单 11.1 中有几个地方需要注意。首先,我们通过 @Inject 注解让 Spring 自动将一个 SessionFactory 注入到 HibernateSpitterRepository 的 sessionFactory 属性中。接下来,在 currentSession() 方法中,我们使用这个 SessionFactory 来获取当前事务的 Session。

另外需要注意的是,我们在类上使用了 @Repository 注解,这会为我们做两件事情。首先,@Repository 是 Spring 的另一种构造性注解,它能够像其他注解一样被 Spring 的组件扫描所扫描到。这样就不必明确声明 HibernateSpitterRepository bean 了,只要这个 Repository 类在组件扫描所涵盖的包中即可。

除了帮助减少显式配置以外,@Repository 还有另外一个用处。让我们回想一下模板类,它有一项任务就是捕获平台相关的异常,然后使用 Spring 统一非检查型异常的形式重新抛出。如果我们使用 Hibernate 上下文 Session 而不是 Hibernate 模板的话,那异常转换会怎么处理呢?

为了给不使用模板的 Hibernate Repository 添加异常转换功能,我们只需在 Spring 应用上下文中添加一个 PersistenceExceptionTranslationPostProcessor bean:

@Bean
public BeanPostProcessor persistenceTranslation() {
  return new PersistenceExceptionTranslationPostProcessor();
}

PersistenceExceptionTranslationPostProcesso r是一个 bean 后置处理器(bean post-processor),它会在所有拥有 @Repository 注解的类上添加一个通知器(advisor),这样就会捕获任何平台相关的异常并以 Spring 非检查型数据访问异常的形式重新抛出。

现在,Hibernate 版本的 Repository 已经完成了。我们开发时,没有依赖 Spring 的特定类(除了 @Repository 注解以外)。这种不使用模板的方式也适用于开发纯粹的基于 JPA 的 Repository,让我们再尝试开发另一个 SpitterRepository 实现类,这次我们使用的是 JPA。

Last updated