2016-01-05 8 views
19

Mit Spring 4 und Hibernate 4 konnte mich Reflexion verwenden, um das Hibernate Konfigurationsobjekt aus der aktuellen Umgebung zu bekommen, mit diesem Code:Programmatische Schema/SchemaUpdate mit Hibernate 5 und 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); 

Mit Hibernate 5, muss ich einige MetadataImplementor verwenden, die von keinem dieser Objekte verfügbar scheint. Ich habe auch versucht, MetadataSources mit der serviceRegistry zu verwenden. Aber es hat gesagt, dass es die falsche Art von ServiceRegistry ist.

Gibt es einen anderen Weg, um das zu erreichen?

+0

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

+0

Ich finde den persistence.xml-Parser aus diesem Beitrag ziemlich praktisch: http://stackoverflow.com/questions/23310617/how-to-use-schemaexporttool-with-jpa-and-hibernate-4-3 –

+0

Der ServiceRegistry-Fehler, dem Sie gegenüberstanden war aufgrund der Tatsache, dass MetadataSources 'StandardServiceRegistry' erwartet, während die ServiceRegistry von SessionFactoryImpl nicht vom obigen Typ ist. Schauen Sie in meine Antwort für die Details. – James

Antwort

7

ich auf Aviad Antwort addieren möchte zu beantworten scheint es wie pro OP Antrag zu vervollständigen .

Die Einbauten:

Um eine Instanz von MetadataImplementor zu bekommen, die Abhilfe ist, eine Instanz von SessionFactoryBuilderFactory durch Java ServiceLoader Anlage zu registrieren. Die Methode getSessionFactoryBuilder dieses registrierten Diensts wird dann von MetadataImplementor mit einer Instanz von sich aufgerufen, wenn Hibernate bootstrapped wird. Die Code-Referenzen sind unter:

  1. Service Loading
  2. Invocation of getSessionFactoryBuilder

also letztlich eine Instanz von MetadataImplementor zu bekommen, müssen Sie SessionFactoryBuilderFactory implementieren und registrieren, damit ServiceLoader diesen Service erkennen:

Eine Implementierung von 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; 
    } 
} 

Um die oben zu schaffen einfache Textdatei im folgenden Pfad zu registrieren (vorausgesetzt, es ist ein Maven-Projekt, schließlich müssen wir die ‚META-INF‘ -Ordner in dem Classpath verfügbar sein):

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

Und der Inhalt der Textdatei sollte eine einzelne Zeile sein (kann sogar mehrere Zeilen sein, wenn Sie mehrere Instanzen registrieren müssen) und den vollständig qualifizierten Klassenpfad Ihrer Implementierung von SessionFactoryBuilderFactory angeben. Wenn beispielsweise für die oben genannte Klasse Ihr Paketname "com.IhreFirma.prj" lautet, sollte der Inhalt der Datei der folgende Inhalt sein.

com.yourcompany.prj.MetadataProvider 

Und das ist es, wenn Sie Ihre Anwendung, Frühling App oder Standalone-Hibernate ausführen, werden Sie eine Instanz von MetadataImplementor zur Verfügung haben über eine statische Methode einmal überwintern bootstraped wird.

Update 1:

Es gibt keine Möglichkeit, kann es über Frühling injiziert werden.Ich habe in den Hibernate-Quellcode gegraben und das Metadaten-Objekt wird nirgendwo in SessionFactory gespeichert (was wir von Spring bekommen). Also, es ist nicht möglich, es zu injizieren. Aber es gibt zwei Möglichkeiten, wenn Sie es im Frühjahr Weg wollen:

  1. Klassen bestehende erweitern und anpassen den ganzen Weg von

LocalSessionFactoryBean -> MetadataSources -> MetadataBuilder

LocalSessionFactoryBean Dies ist, was Sie in Spring konfigurieren und es hat ein Objekt von MetadataSources. MetadataSources erstellt MetadataBuilder, der wiederum MetadataImplementor erstellt. Alle oben genannten Operationen speichern nichts, sie erstellen nur Objekte im laufenden Betrieb und kehren zurück. Wenn Sie eine Instanz von MetaData haben möchten, sollten Sie die obigen Klassen erweitern und modifizieren, damit sie eine lokale Kopie der entsprechenden Objekte speichern, bevor sie zurückkehren. Auf diese Weise können Sie einen Verweis auf MetadataImplementor haben. Aber ich würde das wirklich nicht empfehlen, es sei denn, es wird wirklich benötigt, weil sich die APIs im Laufe der Zeit ändern könnten.

  1. Auf der anderen Seite, wenn Sie nichts dagegen haben, ein MetaDataImplemetor von Session bauen, wird der folgende Code Ihnen helfen:

    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

Gibt es keine andere Möglichkeit, es über Spring zu injizieren?Ich mag diese Textdateien nicht, die ich beim Umbenennen oder Refactoring im Auge behalten muss. –

+0

Ich habe meine Antwort gerade mit einer Möglichkeit aktualisiert, sie im Frühling zu verwenden, genau wie Sie es zuvor mit Hibernate verwendet haben. 4 – James

+0

"new SchemaUpdate (serviceRegistry, metadata)" gibt mir einen Fehler, dass dieser Konstruktor nicht definiert ist, mit Hibernate 5.2. Ist das jetzt woanders hingezogen? –

1

Werfen Sie einen Blick auf diese:

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

Werfen Sie einen Blick auf This Thread, die Ihren Bedürfnissen

+0

Danke. Ich werde das später versuchen. Und melde dich zurück. –

+0

Wie integriere ich das mit Spring? Die Klasse wird nicht aufgerufen und getMeta() gibt null zurück. –

+0

Nun, das erfordert eine Änderung im Ordner METADATA. Warum brauchst du das? Was ist der Zweck? – Aviad

7

Grundidee für dieses Problem ist:

Implementierung von org.hibernate.integrator.spi.Integrator, die erforderlichen Daten zu einem Halter speichert. Registrieren Sie die Implementierung als Service und verwenden Sie sie dort, wo Sie sie benötigen.

Arbeit Beispiel finden Sie hier https://github.com/valery-barysok/spring4-hibernate5-stackoverflow-34612019


erstellen org.hibernate.integrator.api.integrator.Integrator Klasse

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

erstellen META-INF/services/org.hibernate.integrator.spi.Integrator Datei

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

Nun, mein unterwegs dazu:

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