Spring 4とHibernate 4では、次のコードを使用して、Reflectionを使用して現在の環境からHibernate構成オブジェクトを取得できました。
@Autowired LocalContainerEntityManagerFactoryBean lcemfb;
EntityManagerFactoryImpl emf = (EntityManagerFactoryImpl) lcemfb.getNativeEntityManagerFactory();
SessionFactoryImpl sf = emf.getSessionFactory();
SessionFactoryServiceRegistryImpl serviceRegistry = (SessionFactoryServiceRegistryImpl) sf.getServiceRegistry();
Configuration cfg = null;
try {
Field field = SessionFactoryServiceRegistryImpl.class.getDeclaredField("configuration");
field.setAccessible(true);
cfg = (Configuration) field.get(serviceRegistry);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
SchemaUpdate update = new SchemaUpdate(serviceRegistry, cfg);
Hibernate 5では、いくつかのMetadataImplementor
を使用する必要がありますが、これらはどのオブジェクトからも使用できないようです。また、MetadataSources
をserviceRegistry
とともに使用しようとしました。しかし、それは間違った種類のServiceRegistry
だと言っていました。
これを機能させる他の方法はありますか?
OPのリクエストに従って、Aviadの回答を追加して、回答を完成させたいと思います。
内部:
MetadataImplementorのインスタンスを取得するための回避策は、Javaの ServiceLoader 機能を介して SessionFactoryBuilderFactory のインスタンスを登録することです。この登録済みサービスの getSessionFactoryBuilder メソッドは、休止状態がブートストラップされるときに、自身のインスタンスを使用して MetadataImplementor によって呼び出されます。コード参照は以下のとおりです。
したがって、最終的にMetadataImplementorのインスタンスを取得するには、SessionFactoryBuilderFactoryを実装して登録し、ServiceLoaderがこのサービスを認識できるようにする必要があります。
SessionFactoryBuilderFactoryの実装:
public class MetadataProvider implements SessionFactoryBuilderFactory {
private static MetadataImplementor metadata;
@Override
public SessionFactoryBuilder getSessionFactoryBuilder(MetadataImplementor metadata, SessionFactoryBuilderImplementor defaultBuilder) {
this.metadata = metadata;
return defaultBuilder; //Just return the one provided in the argument itself. All we care about is the metadata :)
}
public static MetadataImplementor getMetadata() {
return metadata;
}
}
上記を登録するには、次のパスに単純なテキストファイルを作成します(Mavenプロジェクトであると仮定すると、最終的にクラスパスで「META-INF」フォルダーを使用できる必要があります)。
src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory
また、テキストファイルの内容は、SessionFactoryBuilderFactoryの実装の完全修飾クラスパスを示す1行(複数のインスタンスを登録する必要がある場合は複数行にすることもできます)にする必要があります。たとえば、上記のクラスの場合、パッケージ名が「com.yourcompany.prj」の場合、ファイルの内容は次のようになります。
com.yourcompany.prj.MetadataProvider
アプリケーション、Springアプリ、またはスタンドアロンの休止状態を実行すると、休止状態がブートストラップされると、静的メソッドを介してMetadataImplementorのインスタンスを使用できるようになります。
更新1:
Spring経由で注入できる方法はありません。私はHibernateのソースコードを詳しく調べましたが、メタデータオブジェクトはSessionFactory(Springから取得したもの)のどこにも格納されていません。したがって、注入することはできません。ただし、Springの方法で必要な場合は、2つのオプションがあります。
LocalSessionFactoryBean-> MetadataSources-> MetadataBuilder
LocalSessionFactoryBeanはSpringで構成するもので、MetadataSourcesのオブジェクトを持っています。 MetadataSourcesはMetadataBuilderを作成し、次にMetadataBuilderがMetadataImplementorを作成します。上記のすべての操作は何も保存せず、ただちにオブジェクトを作成して戻ります。 MetaDataのインスタンスが必要な場合は、上記のクラスを拡張および変更して、返される前にそれぞれのオブジェクトのローカルコピーを格納するようにする必要があります。そうすれば、MetadataImplementorへの参照を持つことができます。ただし、APIは時間の経過とともに変化する可能性があるため、本当に必要な場合を除いて、この方法はお勧めしません。
一方、SessionFactoryからMetaDataImplemetorを構築しても構わない場合は、次のコードが役立ちます。
EntityManagerFactoryImpl emf=(EntityManagerFactoryImpl)lcemfb.getNativeEntityManagerFactory();
SessionFactoryImpl sf=emf.getSessionFactory();
StandardServiceRegistry serviceRegistry = sf.getSessionFactoryOptions().getServiceRegistry();
MetadataSources metadataSources = new MetadataSources(new BootstrapServiceRegistryBuilder().build());
Metadata metadata = metadataSources.buildMetadata(serviceRegistry);
SchemaUpdate update=new SchemaUpdate(serviceRegistry,metadata); //To create SchemaUpdate
// You can either create SchemaExport from the above details, or you can get the existing one as follows:
try {
Field field = SessionFactoryImpl.class.getDeclaredField("schemaExport");
field.setAccessible(true);
SchemaExport schemaExport = (SchemaExport) field.get(serviceRegistry);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
この問題の基本的な考え方は次のとおりです。
一部の所有者に必要なデータを格納するorg.hibernate.integrator.spi.Integrator
の実装。実装をサービスとして登録し、必要な場所で使用します。
ここで見つけることができる作業例 https://github.com/valery-barysok/spring4-hibernate5-stackoverflow-34612019
org.hibernate.integrator.api.integrator.Integrator
クラスを作成する
import hello.HibernateInfoHolder;
import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
public class Integrator implements org.hibernate.integrator.spi.Integrator {
@Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
HibernateInfoHolder.setMetadata(metadata);
HibernateInfoHolder.setSessionFactory(sessionFactory);
HibernateInfoHolder.setServiceRegistry(serviceRegistry);
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
}
META-INF/services/org.hibernate.integrator.spi.Integrator
ファイルを作成
org.hibernate.integrator.api.integrator.Integrator
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
new SchemaExport((MetadataImplementor) HibernateInfoHolder.getMetadata()).create(true, true);
new SchemaUpdate(HibernateInfoHolder.getServiceRegistry(), (MetadataImplementor) HibernateInfoHolder.getMetadata()).execute(true, true);
}
}
これを見てください:
public class EntityMetaData implements SessionFactoryBuilderFactory {
private static final ThreadLocal<MetadataImplementor> meta = new ThreadLocal<>();
@Override
public SessionFactoryBuilder getSessionFactoryBuilder(MetadataImplementor metadata, SessionFactoryBuilderImplementor defaultBuilder) {
meta.set(metadata);
return defaultBuilder;
}
public static MetadataImplementor getMeta() {
return meta.get();
}
}
このスレッド を見てください、これはあなたのニーズに答えているようです
さて、私はこれに行きます:
public class SchemaTranslator {
public static void main(String[] args) throws Exception {
new SchemaTranslator().run();
}
private void run() throws Exception {
String packageName[] = { "model"};
generate(packageName);
}
private List<Class<?>> getClasses(String packageName) throws Exception {
File directory = null;
try {
ClassLoader cld = getClassLoader();
URL resource = getResource(packageName, cld);
directory = new File(resource.getFile());
} catch (NullPointerException ex) {
throw new ClassNotFoundException(packageName + " (" + directory + ") does not appear to be a valid package");
}
return collectClasses(packageName, directory);
}
private ClassLoader getClassLoader() throws ClassNotFoundException {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
return cld;
}
private URL getResource(String packageName, ClassLoader cld) throws ClassNotFoundException {
String path = packageName.replace('.', '/');
URL resource = cld.getResource(path);
if (resource == null) {
throw new ClassNotFoundException("No resource for " + path);
}
return resource;
}
private List<Class<?>> collectClasses(String packageName, File directory) throws ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
if (directory.exists()) {
String[] files = directory.list();
for (String file : files) {
if (file.endsWith(".class")) {
// removes the .class extension
classes.add(Class.forName(packageName + '.' + file.substring(0, file.length() - 6)));
}
}
} else {
throw new ClassNotFoundException(packageName + " is not a valid package");
}
return classes;
}
private void generate(String[] packagesName) throws Exception {
Map<String, String> settings = new HashMap<String, String>();
settings.put("hibernate.hbm2ddl.auto", "drop-create");
settings.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL94Dialect");
MetadataSources metadata = new MetadataSources(
new StandardServiceRegistryBuilder()
.applySettings(settings)
.build());
for (String packageName : packagesName) {
System.out.println("packageName: " + packageName);
for (Class<?> clazz : getClasses(packageName)) {
System.out.println("Class: " + clazz);
metadata.addAnnotatedClass(clazz);
}
}
SchemaExport export = new SchemaExport(
(MetadataImplementor) metadata.buildMetadata()
);
export.setDelimiter(";");
export.setOutputFile("db-schema.sql");
export.setFormat(true);
export.execute(true, false, false, false);
}
}