複数のEntityManagerを切り替える(on CDI)
データベースが複数あるなどで、接続先を選びたい場合。
ついでにあやふやにしたままだったアノテーションも整理。
参考
JPA Module
環境
- Java8SE
- Tomcat8
※なので@PersistenceContextが使えません。使えるともう少し楽? - Weld 2.3.1
単一EntityManager(変更前)
public class EntityManagerProducer { @Produces @RequestScoped protected EntityManager createEntityManager() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("testProvider"); return factory.createEntityManager(); } protected void closeEntityManager(@Disposes EntityManager entityManager) { if (entityManager.isOpen()) { entityManager.close(); } } }
※@Producesはインジェクションを行うメソッド or フィールドに指定する
※@Disposesはオブジェクトの破棄を行うメソッドのパラメータに指定する
限定アノテーション
@Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface TestDS {}
@Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface CommonDS {}
※今回は2つを切り替えるのでアノテーションを2つ作成
※@Qualifierはインジェクションするimplを指定する時に使う
※@Retentionはアノテーションの有効範囲指定、指定しなければRetentionPolicy.CLASSになる
※ここでは実行時に切り替えが必要なのでRetentionPolicy.RUNTIMEを指定
※@Targetはアノテーションを指定できる対象指定
persistence.xml
<persistence-unit name="testProvider" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <non-jta-data-source>java:comp/env/jdbc/testSource</non-jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> </persistence-unit> <persistence-unit name="commonProvider" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <non-jta-data-source>java:comp/env/jdbc/commonSource</non-jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> </persistence-unit>
※Tomcatのデータソースにはjdbc/testSource、jdbc/commonSourceがある状態で、persistenceにそれぞれのデータソースを参照する設定を書く
EntityManger(変更後)
public class EntityManagerProducer { @Produces @TestDS @RequestScoped protected EntityManager createEntityManager() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("testProvider"); return factory.createEntityManager(); } @Produces @CommonDS @RequestScoped protected EntityManager createCommonEntityManager() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("commonProvider"); return factory.createEntityManager(); } protected void closeEntityManager(@Disposes @TestDS EntityManager entityManager) { if (entityManager.isOpen()) { entityManager.close(); } } protected void closeCommonEntityManager(@Disposes @CommonDS EntityManager entityManager) { if (entityManager.isOpen()) { entityManager.close(); } } }
※限定アノテーション毎に@Producesのメソッドを作成
※同じく@Disposesのメソッドもそれぞれ用意し、パラメータに限定アノテーションを指定する
Inject
public class UserService { @Inject @TestDS private EntityManager entityManager; }
public class GroupService { @Inject @CommonDS private EntityManager entityManager; }
※Injectと共に限定アノテーションも指定すると、対象のEntityManagerをInjectできる
余談
@Disposesを横着して
protected void closeCommonEntityManager(@Disposes @CommonDS @TestDS EntityManager entityManager) { if (entityManager.isOpen()) { entityManager.close(); } }
とか限定アノテーション両方とも指定したらエラーで怒られたのは内緒の話。
WELD-001424: The following disposal methods were declared but did not resolve to a producer method
closeするだけだから纏めさせて欲しい。。。