SpringでJPA(JPAのAPIを直接使う版)

昨日はSpringFrameworkの勉強会に行ってきました。「設定しだいでJPAAPIが直接使えるはず」と言ったものの実際には試したことはなかったりして。

試してみました。
必要なのは

  • PersistenceAnnotationBeanPostProcessorを設定ファイルに定義すること。

もし例外をSpringのものに変換したいなら

  • PersistenceExceptionTranslationPostProcessorを指定する。
  • DAOに@Repositoryをつける。

JpaDaoSupportとかを使うよりはJPAAPIを直接使うのがおすすめとSpringのドキュメントにも書いてあったような気がするのでJpaDaoSupportとかJpaTempleteにうれしさを感じない場合はこっちを使うのがよさそうです。

もちろん、個人的にはSpringよりSeasar + S2Hibernate-JPA使ってほしいですけど:p

Daoの実装
@Repository
public class EmpDaoImpl implements EmpDao {

  @PersistenceContext
  private EntityManager em;

  public void remove(Emp e) {
    em.remove(e);
  }

  public List<Emp> findAll() {
    return em.createQuery("SELECT e FROM Emp e").getResultList();
  }
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="jdbc.properties" />
  </bean>

  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName"
      value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="maxActive" value="50" />
    <property name="maxWait" value="600000" />    
  </bean>

  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
  </bean>

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

  <!-- @PersistenceContextとか@PersistenceUnitを認識してインジェクションするBeanのpost-processor-->
  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
  <!-- @Repositoryがついているクラスでおきた例外を変換するBeanのpost-processor -->
  <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

  <bean id="empDao" class="study.jpa.EmpDaoImpl"/>

  <tx:annotation-driven />

</beans>
テストコード

親クラスのAbstractJpaTestsはSpringのクラス。テストケースをトランザクション内で実行してくれる。

public class EmpDaoTest extends AbstractJpaTests {

  private EmpDao empDao;

  public void setEmpDao(EmpDao empDao) {
    this.empDao = empDao;
  }

  @Override
  protected String getConfigPath() {
    return "/applicationContext.xml";
  }
  
  // メソッド呼び出しがまたがってもOKであることを確認
  public void testTransactionalEntityManager() throws Exception {
    List<Emp> emps = empDao.findAll();
    assertEquals(3, emps.size());
    empDao.remove(emps.get(0));
    assertEquals(2, empDao.findAll().size());
  }

  // 例外がSpringの例外に変換されることを確認
  public void testExceptionTranslation() throws Exception {
    try {
      empDao.remove(null);
      fail();
    } catch (InvalidDataAccessApiUsageException ignore) {
    }
  }
}

テーブルの定義とかエンティティとかDAOのインターフェースとかpersistence.xmlとかは省略。