带有Hibernate 5和Spring 4的编程式SchemaExport / SchemaUpdate [英] Programmatic SchemaExport / SchemaUpdate with Hibernate 5 and Spring 4

查看:101
本文介绍了带有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();
配置cfg = null;

尝试{
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 的错误类型。



是否有其他方法可以使其工作?

解决方案

我想加入Aviad的答案,以便按照OP的要求完成。



内部消息:为了获取MetadataImplementor的实例,解决方法是注册一个实例 SessionFactoryBuilderFactory 通过Java的服务加载器工具。此注册服务的> getSessionFactoryBuilder 方法然后由 MetadataImplementor 与自己的一个实例,当休眠被引导时。代码参考如下:


  1. 载入服务

  2. < a href =https://github.com/hibernate/hibernate-orm/blob/2964ecbf1b74003fcc337b0574487c724638fe94/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java#L137-L152>调用getSessionFactoryBuilder

因此,最终要获得MetadataImplementor的实例,您必须实现SessionFactoryBuilderFactory并注册,以便ServiceLoader可以识别这个服务:

SessionFactoryBuilderFactory的一个实现:

  public class MetadataProvider实现SessionFactoryBuilderFactory {

private static MetadataImplementor metadata;

@Override
public SessionFactoryBuilder getSessionFactoryBuilder(MetadataImplementor元数据,SessionFactoryBuilderImplementor或defaultBuilder){
this.metadata =元数据;
返回defaultBuilder; //只要返回参数本身提供的那个。我们关心的是元数据:)
}

public static MetadataImplementor getMetadata(){
return metadata;




$ b为了注册上面的内容,创建一个简单的文本文件在下面的路径中(假设它是一个Maven项目,最终我们需要'META-INF'文件夹在类路径中可用):

  src / main / resources / META-INF / services / org.hibernate.boot.spi.SessionFactoryBuilderFactory 

文本文件的内容应该是单行(如果需要注册多个实例,甚至可以是多行),说明SessionFactoryBuilderFactory实现的完全限定类路径。例如,对于上面的类,如果你的包名是'com.yourcompany.prj',那么下面应该是文件的内容。

  com.yourcompany.prj.MetadataProvider 

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

/ p>

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


  1. 扩展现有类并自定义


  2. $ block $ b $ LocalSessionFactoryBean MetadataSource MetadataBuilder
    $ / $

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


    1. 另一方面,如果您不介意从SessionFactory构建MetaDataImplemetor,则以下代码将帮助您:

        EntityManagerFactoryImpl电动势=(EntityManagerFactoryImpl)lcemfb.getNativeEntityManagerFactory(); 
      SessionFactoryImpl sf = emf.getSessionFactory();
      StandardServiceRegistry serviceRegistry = sf.getSessionFactoryOptions()。getServiceRegistry();
      MetadataSources metadataSources = new MetadataSources(new BootstrapServiceRegistryBuilder()。build());
      元数据元数据= metadataSources.buildMetadata(serviceRegistry);
      SchemaUpdate update = new SchemaUpdate(serviceRegistry,metadata); //创建SchemaUpdate

      //你可以从上面的细节创建SchemaExport,或者你可以像下面这样得到现有的:
      try {
      Field field = SessionFactoryImpl。 class.getDeclaredField( SchemaExport工具);
      field.setAccessible(true);
      SchemaExport schemaExport =(SchemaExport)field.get(serviceRegistry);
      } catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e){
      e.printStackTrace();
      }



    With Spring 4 and Hibernate 4, I was able to use Reflection to get the Hibernate Configuration object from the current environment, using this code:

    @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);
    

    With Hibernate 5, I must use some MetadataImplementor, which doesn't seems to be available from any of those objects. I also tried to use MetadataSources with the serviceRegistry. But it did say that it's the wrong kind of ServiceRegistry.

    Is there any other way to get this working?

    解决方案

    I would like to add up on Aviad's answer to make it complete as per OP's request.

    The internals:

    In order to get an instance of MetadataImplementor, the workaround is to register an instance of SessionFactoryBuilderFactory through Java's ServiceLoader facility. This registered service's getSessionFactoryBuilder method is then invoked by MetadataImplementor with an instance of itself, when hibernate is bootstrapped. The code references are below:

    1. Service Loading
    2. Invocation of getSessionFactoryBuilder

    So, ultimately to get an instance of MetadataImplementor, you have to implement SessionFactoryBuilderFactory and register so ServiceLoader can recognize this service:

    An implementation of 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;
        }
    }
    

    In order to register the above, create simple text file in the following path(assuming it's a maven project, ultimately we need the 'META-INF' folder to be available in the classpath):

    src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory
    

    And the content of the text file should be a single line(can even be multiple lines if you need to register multiple instances) stating the fully qualified class path of your implementation of SessionFactoryBuilderFactory. For example, for the above class, if your package name is 'com.yourcompany.prj', the following should be the content of the file.

    com.yourcompany.prj.MetadataProvider
    

    And that's it, if you run your application, spring app or standalone hibernate, you will have an instance of MetadataImplementor available through a static method once hibernate is bootstraped.

    Update 1:

    There is no way it can be injected via Spring. I digged into Hibernate's source code and the metadata object is not stored anywhere in SessionFactory(which is what we get from Spring). So, it's not possible to inject it. But there are two options if you want it in Spring's way:

    1. Extend existing classes and customize all the way from

    LocalSessionFactoryBean -> MetadataSources -> MetadataBuilder

    LocalSessionFactoryBean is what you configure in Spring and it has an object of MetadataSources. MetadataSources creates MetadataBuilder which in turn creates MetadataImplementor. All the above operations don't store anything, they just create object on the fly and return. If you want to have an instance of MetaData, you should extend and modify the above classes so that they store a local copy of respective objects before they return. That way you can have a reference to MetadataImplementor. But I wouldn't really recommend this unless it's really needed, because the APIs might change over time.

    1. On the other hand, if you don't mind building a MetaDataImplemetor from SessionFactory, the following code will help you:

      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();
      }
      

    这篇关于带有Hibernate 5和Spring 4的编程式SchemaExport / SchemaUpdate的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆