2016-01-05 1 views
19

avec ressort 4 et Hibernate 4, j'ai pu utiliser la réflexion pour obtenir l'objet Configuration Hibernate de l'environnement actuel, en utilisant ce code:Programmatic SchemaExport/SchemaUpdate avec Hibernate 5 et Spring 4

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

Avec Hibernate 5, je dois utiliser un MetadataImplementor, qui ne semble pas être disponible à partir de ces objets. J'ai également essayé d'utiliser MetadataSources avec le serviceRegistry. Mais il a dit que c'est le mauvais type de ServiceRegistry.

Existe-t-il un autre moyen d'obtenir ce fonctionnement?

+0

connexes: http://stackoverflow.com/questions/32178041/where-did-configuration-generateschemacreationscript-go-in-hibernate-5 –

+0

Je trouve le parseur persistence.xml de ce post assez pratique: http://stackoverflow.com/questions/23310617/how-to-use-schemaexporttool-with-jpa-and-hibernate-4-3 –

+0

L'erreur ServiceRegistry que vous avez rencontrée était dû au fait que MetadataSources s'attend à 'StandardServiceRegistry' alors que le serviceRegistry de SessionFactoryImpl n'est pas du type ci-dessus. Regardez dans ma réponse pour les détails. – James

Répondre

7

Je voudrais ajouter sur la réponse de Aviad pour le rendre complet selon la demande OP .

Les internes:

Afin d'obtenir une instance de MetadataImplementor, la solution de contournement consiste à enregistrer une instance de SessionFactoryBuilderFactory par installation Java ServiceLoader. La méthode getSessionFactoryBuilder de ce service enregistré est ensuite appelée par MetadataImplementor avec une instance de lui-même, lorsque hibernate est amorcée. Les références de code sont ci-dessous:

  1. Service Loading
  2. Invocation of getSessionFactoryBuilder

Alors, en fin de compte pour obtenir une instance de MetadataImplementor, vous devez mettre en œuvre SessionFactoryBuilderFactory et vous inscrire si ServiceLoader peut reconnaître ce service:

Une implémentation de 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; 
    } 
} 

Pour vous inscrire ci-dessus, créez simple fichier texte dans le chemin suivant (en supposant qu'il est un projet Maven, nous avons besoin en fin de compte le dossier « META-INF » soit disponible dans le classpath):

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

Et le contenu du fichier texte doit être une seule ligne (peut même être plusieurs lignes si vous avez besoin d'enregistrer plusieurs instances) indiquant le chemin de classe complet de votre implémentation de SessionFactoryBuilderFactory. Par exemple, pour la classe ci-dessus, si le nom de votre package est "com.yourcompany.prj", le contenu du fichier doit être le suivant.

com.yourcompany.prj.MetadataProvider 

Et c'est, si vous exécutez votre application, l'application de printemps ou mise en veille prolongée autonome, vous aurez une instance de MetadataImplementor disponible par le biais d'une méthode statique mise en veille prolongée est une fois bootstraped.

Mise à jour 1:

Il n'y a aucun moyen peut être injecté par ressort.J'ai creusé dans le code source de Hibernate et l'objet de métadonnées n'est stocké nulle part dans SessionFactory (ce que nous obtenons de Spring). Donc, il n'est pas possible de l'injecter. Mais il y a deux options si vous le voulez sur le chemin de printemps:

  1. étendre les classes existantes et personnaliser tout le chemin de

LocalSessionFactoryBean -> MetadataSources -> MetadataBuilder

LocalSessionFactoryBean est ce que vous configurez dans Spring et il a un objet de MetadataSources. MetadataSources crée MetadataBuilder qui à son tour crée MetadataImplementor. Toutes les opérations ci-dessus ne stockent rien, elles créent juste l'objet à la volée et retournent. Si vous souhaitez avoir une instance de MetaData, vous devez étendre et modifier les classes ci-dessus afin qu'elles stockent une copie locale des objets respectifs avant leur retour. De cette façon, vous pouvez avoir une référence à MetadataImplementor. Mais je ne recommanderais pas vraiment ceci à moins que ce soit vraiment nécessaire, parce que les API pourraient changer avec le temps.

  1. D'autre part, si vous ne me dérange pas la construction d'un MetaDataImplemetor de SessionFactory, le code suivant vous aidera:

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

N'y a-t-il pas d'autre moyen de l'injecter via Spring?Je n'aime pas ces fichiers texte, que je dois garder en mémoire lors du renommage ou du refactoring. –

+0

Je viens de mettre à jour ma réponse avec un moyen de l'utiliser dans Spring, tout comme Hibernate 4 – James

+0

"new SchemaUpdate (serviceRegistry, metadata)" me donne une erreur que ce constructeur n'est pas défini, en utilisant Hibernate 5.2. Est-ce que cela a déménagé ailleurs maintenant? –

1

Jetez un oeil sur celui-ci:

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

Jetez un coup d'oeil sur This Thread qui semble répondre à vos besoins

+0

Merci. Je vais essayer ça plus tard. Et rapport. –

+0

Comment intégrer cela avec Spring? La classe ne sera pas appelée et getMeta() renvoie null. –

+0

Eh bien, cela nécessite une modification dans le dossier METADATA. Pourquoi avez-vous besoin de cela? Quel est le but? – Aviad

7

idée de base pour ce problème est:

mise en œuvre de org.hibernate.integrator.spi.Integrator qui stocke les données requises à un titulaire. Enregistrez l'implémentation en tant que service et utilisez-la là où vous en avez besoin.

exemple de travail que vous pouvez trouver ici https://github.com/valery-barysok/spring4-hibernate5-stackoverflow-34612019


créer org.hibernate.integrator.api.integrator.Integrator classe

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) { 
    } 
} 

créer META-INF/services/org.hibernate.integrator.spi.Integrator fichier

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

Eh bien, mon aller à ce:

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