Hatena::Groupprogram

ひとり開発日記。 このページをアンテナに追加 RSSフィード

2013/07/26 (Fri)

Guice-Persistで、トランザクションが有効にならない

| Guice-Persistで、トランザクションが有効にならない - ひとり開発日記。 を含むブックマーク はてなブックマーク - Guice-Persistで、トランザクションが有効にならない - ひとり開発日記。

Resteasy + Google Guice + EclipseLink + Querydsl で、アプリ作っているのですが、トランザクションが有効にならなくて "org.jboss.resteasy.spi.UnhandledException: javax.persistence.TransactionRequiredException: Exception Description: No transaction is currently active" が出てしまい、すごく困っておりました。

サービスの抽象基底クラス AbstractUpdatableService

public abstract class AbstractUpdatableService<T extends AbstractUpdatableEntity> {

    @Inject
    protected EntityManager em;

    @Inject
    protected JPQLTemplates jpqlTemplates;

    @Inject
    protected DateHelper dateHelper;

    protected JPAUpdateClause update(EntityPathBase<T> entity) {
        return new JPAUpdateClause(em, entity, jpqlTemplates);
    }
}

実クラス DepartmentService

public class DepartmentService extends AbstractUpdatableService<Department> {

    /** 
     * リネームします
     * 
     * @param id
     * @param label
     */
    @Transactional
    public void rename(Long id, String label) {
        Date now = dateHelper.now();
        update(department)
                .where(department.id.eq(id))
                .set(department.label, label)
                .set(department.modified, now)
                .execute();
    }
}

GuiceのDI用Moduleクラス

public class ConfigModule extends AbstractModule {

    @Override
    protected void configure() {
        // JPA
        install(new JpaPersistModule("dbsetting"));
        // Template
        bind(SQLTemplates.class).to(MySQLTemplates.class).asEagerSingleton();
        bind(JPQLTemplates.class).to(EclipseLinkTemplates.class).asEagerSingleton();
        // Initializer
        bind(PersistInitializer.class).asEagerSingleton();

        bind(DepartmentService.class).in(Singleton.class);
    }

    /** 永続層サービス初期化クラス */
    public static class PersistInitializer {
        @Inject
        public PersistInitializer(PersistService service) {
            service.start();
        }
    }
}

これ困ったことに、jUnitでテストすると、トランザクションかかるのに、実際のサーバーで動かすと、トランザクションかからないんですよね。

実際、JpaLocalTxnInterceptorの中でブレイクポイント設定すると、EntityTransaction#beginメソッド呼んでるし…、で、???状態に…。

上記で分かったんですが、EntityManagerはシングルトンじゃないので、呼ぶ順番によっては、トランザクションが開始されていないインスタンスがDIされるようで、それを回避するにはEntityManagerのProviderをDIして、そこからEntityManagerを取得する、と…。

なので、AbstractUpdatableServiceを以下のように修正。

public abstract class AbstractUpdatableService<T extends AbstractUpdatableEntity> {

    /** EntityManager Provider */
    @Inject
    protected Provider<EntityManager> emProvider;

    /** JPQL Templates */
    @Inject
    protected JPQLTemplates jpqlTemplates;

    /** DateHelper */
    @Inject
    protected DateHelper dateHelper;

    protected JPAUpdateClause update(EntityPathBase<T> entity) {
        return new JPAUpdateClause(em(), entity, jpqlTemplates);
    }

    protected EntityManager em() {
        return emProvider.get();
    }

無事、トランザクションがかかった状態で実行されるようになりました…。

トラックバック - http://program.g.hatena.ne.jp/halflite/20130726