【技術】JavaSE環境におけるJPAエンティティークラスの自動登録

10130 ワード

コンテナのサポートがない環境では、JPAのエンティティーはpersistenceにあるのが一般的である.xmlには、次のように1つずつ登録されています.
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 3     <persistence-unit name="security-common">
 4         <class>org.gems.han.security.common.MenuVO</class>
 5         <class>org.gems.han.security.common.MenuItemVO</class>
 6         <class>org.gems.han.security.common.ModuleVO</class>
 7         <class>org.gems.han.security.common.RoleVO</class>
 8         <class>org.gems.han.security.common.UserVO</class>
 9         <class>org.gems.han.security.common.UserRoleVO</class>
10     </persistence-unit>
11 </persistence>

皆さんは私と同じように面倒な感じがするかどうか分からないし、疑問もあります.実体クラスごとに@Entityがマークされているのに、どうしてもう一度登録しなければならないのですか.@Entityタグを利用して、エンティティクラスの登録を免除する方法はありますか?研究を経て、以下の案を見つけて、みんなに分かち合います.
まず、私が使用しているJPA実装がEclipselinkであることを説明し、私の案もこの実装の下で検証しただけです.
EntityManagerFactoryがPersistenceProviderによって作成されたことを知っています.このクラスは異なるJPA実装において異なる実装クラスがあり、Eclipselinkではorg.eclipse.persistence.jpa.PersistenceProvider、私たちはまずこのクラスを継承し、その中を上書きする方法createEntityManagerFactoryImplは、以下の通りです.
1     @Override
2     protected EntityManagerFactoryImpl createEntityManagerFactoryImpl(PersistenceUnitInfo puInfo, Map properties,
3             boolean requiresConnection) {
4         List<String> classNameList = puInfo.getManagedClassNames();
5         List<String> entityClassNameList =getManagedClassNames();
6         classNameList.addAll(entityClassNameList);
7         return super.createEntityManagerFactoryImpl(puInfo, properties, requiresConnection);
8     }

原理説明:4行目、persistence.xmlにエンティティクラスが登録されていない場合、classNameリストは空のリストになります(nullではなくemptyリストに注意してください).5行目で、メソッドgetManagedClassNameによってエンティティークラスを取得します.6行目は、エンティティクラスをリストに追加します.7行目では、親メソッドを呼び出し、ファクトリクラスの作成を実現します.次のキーはgetManagedClassNamesメソッドの実装です.ここではgoogleのオープンソースプロジェクトguavaを使用してjavaクラスをスキャンし、@Entityタグのあるエンティティクラスをフィルタします.コードは以下の通りです.
 1     public List<String> getManagedClassNames() {
 2         List<String> managedClassNameList = new ArrayList<>();
 3         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 4         ImmutableSet<ClassInfo> cs = null;
 5         try {
 6             cs = ClassPath.from(loader).getTopLevelClasses();
 7         } catch (IOException e) {
 8             e.printStackTrace();
 9         }
10         managedClassNameList = new ArrayList<>();
11         if (cs != null && cs.isEmpty() == false) {
12             for (ClassInfo ci : cs) {
13                 Class<?> c = null;
14                 try {
15                     c = loader.loadClass(ci.getName());
16                 } catch (Throwable ex) {
17                 }
18                 if (c != null) {
19                     Entity entity = c.getAnnotation(Entity.class);
20                     if (entity != null) {
21                         managedClassNameList.add(c.getName());
22                     }
23                 }
24             }
25         }
26         return managedClassNameList;
27     }

ここでクラスローダ(loader)は具体的には使用するが、特殊なClassLoaderを使用する場合はorgを上書きする必要がある.eclipse.persistence.jpa.PersistenceProviderのgetClassLoaderメソッドは、実行時にエンティティクラスが正常にロードされることを保証します.
また、PersisitenceProviderは、プロファイルpersistenceによるものではなく、SPIによってロードすることが実験で分かった.xmlでの設定.そのため、META-INFでservices/javaxを追加する必要がある.persistence.spi.PersistenceProviderファイル、このファイルに自分のPersistenceProviderを書き込みます.この点についてはまだ疑問がありますが、実践の中で検証してください.