使用Hibernate 5和Spring 4的编程SchemaExport / SchemaUpdate

使用Spring 4和Hibernate 4,我能够使用Reflection从当前环境中获取Hibernate Configuration对象,使用以下代码:

@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 ,它们似乎不能从任何这些对象中获得。 我还尝试将MetadataSourcesserviceRegistry一起使用。 但它确实说它是错误的ServiceRegistry

有没有其他方法让这个工作?

我想补充一下Aviad的答案,根据OP的要求完成它。

内部:

为了获得MetadataImplementor的实例,解决方法是通过Java的ServiceLoader工具注册SessionFactoryBuilderFactory的实例。 然后,当hibernate被引导时, MetadataImplementor会调用此注册服务的getSessionFactoryBuilder方法及其自身实例。 代码参考如下:

  1. 服务加载
  2. 调用getSessionFactoryBuilder

因此,最终要获得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实现的完全限定类路径。 例如,对于上面的类,如果您的包名称是“com.yourcompany.prj”,则以下内容应该是该文件的内容。

 com.yourcompany.prj.MetadataProvider 

就是这样,如果您运行应用程序,弹簧应用程序或独立hibernate,一旦hibernate被引导,您将通过静态方法获得MetadataImplementor的实例。

更新1:

它无法通过Spring注入。 我深入研究了Hibernate的源代码,并且元数据对象没有存储在SessionFactory中的任何地方(这是我们从Spring获得的)。 所以,它不可能注入它。 但是如果你想以Spring的方式使用它,有两种选择:

  1. 扩展现有类并从中自定义

LocalSessionFactoryBean – > MetadataSources – > MetadataBuilder

LocalSessionFactoryBean是您在Spring中配置的,它具有MetadataSources的对象。 MetadataSources创建MetadataBuilder,后者又创建MetadataImplementor。 以上所有操作都不存储任何内容,它们只是动态创建对象并返回。 如果您想拥有MetaData实例,则应扩展并修改上述类,以便它们在返回之前存储相应对象的本地副本。 这样您就可以引用MetadataImplementor。 但除非真的需要,否则我不会真的推荐它,因为API可能会随着时间而改变。

  1. 另一方面,如果您不介意从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 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> 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> collectClasses(String packageName, File directory) throws ClassNotFoundException { List> 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 settings = new HashMap(); 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); } }