🥫

QurakusでMultiple Persistance Unitsを使う

2021/02/14に公開

前回書いた図みたいに、複数のデータソースにアクセスできることを目指したとき、過去にそれが試みてできなかったことを思い出した。
具体的には、どのようにすれば、複数のHibernateの接続先を定義できるのかがよくわからず諦めていた。

が、実は1.8.0からできるようになっている。

Quarkus 1.8 released - Multiple Persistence Units, Micrometer, jbang, GraalVM 20.2

https://quarkus.io/blog/quarkus-1-8-0-final-released/

自分がやりたかったことは "Multiple Persistance Units" と呼ぶらしい。
必要な設定は次の3つ。

  1. EntityManagerのクラスのInject時に @PersistenceUnit として他の Persistence Unitと区別可能なデータソースのnameを指定する
  2. application.propertiesに PersistanceUnit用のデータソースを指定する
  3. 使用するEntityを、Persistance Unit毎にパッケージ分けする

大体のことはここに書いてある。

Quarkus - Using Hibernate ORM and JPA

https://quarkus.io/guides/hibernate-orm#multiple-persistence-units

記載例としては、

1.の例:
1つのPersistance Unitしか使わない場合には不要だった@PersistenceUnitが追加されている。
ここで指定している名前と2.に書くPersistence Unitの名前を合致させる必要がある。

@Inject
@PersistenceUnit("dummy1")
EntityManager em;

2.の例:

わかりやすさのために自分はdummy1で全て統一してしまっているが、ここには3つの話が載っていると思う。

# dummy1
quarkus.datasource."dummy1".db-kind=postgresql
quarkus.datasource."dummy1".username=postgres
quarkus.datasource."dummy1".password=postgres
quarkus.datasource."dummy1".jdbc.url=jdbc:postgresql://localhost:15432/postgres
quarkus.hibernate-orm."dummy1".database.generation=drop-and-create
quarkus.hibernate-orm."dummy1".datasource=dummy1
quarkus.hibernate-orm."dummy1".packages=dev.tkhm.graphbff.infrastructure.dummy1

読みづらいけど、次のように理解している。

①の青い囲み左の部分はPersistence Unitが探しにくるKeyになっている。
②の青い囲み右の部分はPersistence Unitと紐づけるEntityが入ったパッケージの指定をしている。(~infrastructure.dummy1パッケージの下に作ったEntityは"dummy1"Persistence Unitと紐づく)
③の緑の線の部分はPersistence UnitとDataSourceを紐づけている。

3.の例:
dummy1とdummy2、それぞれ別のPersistence Unitを使う場合にこのようにする。2.の例と平仄を合わせる必要がある。

infrastructure
├── dummy1
│   └── Dummy1OrmEntity.java
└── dummy2
    └── Dummy2OrmEntity.java

これができることがわかったので、本格的にQuarkusでのGraphQLをBFFとして使う、ということがそれほどの難易度なく実現できそうだと確認できた。

ついでに検索で引っかかったので次の質問にも回答しておいた。

Quarkus: EntityManager injection with multiple datasources - Stack Overflow

https://stackoverflow.com/questions/59430688/quarkus-entitymanager-injection-with-multiple-datasources/66193200#66193200

ところで、ドメインモデルとしてのEntityとORMのためのEntity、持つ値が被る箇所も多くて困るんだけど、これどうにかならないかなあ。
ドメイン側のEntityをORMのためのEntity側で継承して使って、最後は親クラスにアップキャストして返せばいいかと思ってやってみたんだけど、その方法だとORMのEntityがTypedQueryでクラス不一致を宣告されてしまう......。多分 @Entity のアノテーションの仕組み、内部的な振る舞いを理解しないとできないのかも。
とりあえずは愚直にORM側のEntityをドメイン側のEntityに変換することで対応しているけれど、通常どうするんだろう。小さな悩みの種。

Discussion