Подтвердить что ты не робот

Programmatic SchemaExport/SchemaUpdate с Hibernate 5 и Spring 4

С 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.

Есть ли другой способ заставить это работать?

4b9b3361

Ответ 1

Я хотел бы добавить ответ на Aviad, чтобы завершить его в соответствии с запросом OP.

Внутренние элементы:

Чтобы получить экземпляр MetadataImplementor, обходным путем является регистрация экземпляра SessionFactoryBuilderFactory через Java ServiceLoader. Этот зарегистрированный метод 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. Например, для вышеуказанного класса, если ваше имя пакета "com.yourcompany.prj", должно быть содержимое файла.

com.yourcompany.prj.MetadataProvider

И если он запустит ваше приложение, spring приложение или автономный спящий режим, у вас будет экземпляр MetadataImplementor, доступный через статический метод, когда hibernate загружается.

Обновление 1:

Невозможно ввести его через Spring. Я выкопал исходный код Hibernate, и объект метаданных не хранится нигде в SessionFactory (это то, что мы получаем от Spring). Таким образом, его невозможно вводить. Но есть два варианта, если вы хотите его в spring образом:

  • Расширение существующих классов и настройка полностью из

LocalSessionFactoryBean → MetadataSources → MetadataBuilder

LocalSessionFactoryBean - это то, что вы настраиваете в spring, и оно имеет объект MetadataSources. MetadataSources создает MetadataBuilder, который, в свою очередь, создает MetadataImplementor. Все вышеперечисленные операции ничего не хранят, они просто создают объект "на лету" и возвращаются. Если вы хотите иметь экземпляр MetaData, вы должны расширить и изменить вышеуказанные классы, чтобы они хранили локальную копию соответствующих объектов до их возвращения. Таким образом, вы можете иметь ссылку на MetadataImplementor. Но я бы не рекомендовал это, если это действительно было необходимо, потому что API могли бы меняться со временем.

  1. С другой стороны, если вы не возражаете создать MetaDataImplemetor из SessionFactory, следующий код поможет вам:

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

Ответ 2

Основная идея этой проблемы:

реализация 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);
    }
}

Ответ 3

Взгляните на это:

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

Взгляните на Этот раздел, который, кажется, отвечает вашим потребностям

Ответ 4

Что ж, пойдите по этому поводу:

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