복붙노트

[SPRING] Hibernate 5 및 Spring 4를 사용하여 프로그래밍 방식의 스키마 내보내기 / SchemaUpdate

SPRING

Hibernate 5 및 Spring 4를 사용하여 프로그래밍 방식의 스키마 내보내기 / 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를 사용해야한다.이 MetadataImplementor는 이러한 객체들 중 하나에서 사용할 수없는 것 같다. 나는 또한 serviceRegistry와 함께 MetadataSources를 사용하려고 시도했다. 그러나 그것은 잘못된 종류의 ServiceRegistry라고 말합니다.

이 방법을 사용할 수있는 다른 방법이 있습니까?

해결법

  1. ==============================

    1.OP의 요청에 따라 Aviad의 답변을 추가하고 싶습니다.

    OP의 요청에 따라 Aviad의 답변을 추가하고 싶습니다.

    내부 :

    MetadataImplementor의 인스턴스를 얻으려면 Java의 ServiceLoader 기능을 통해 SessionFactoryBuilderFactory의 인스턴스를 등록해야합니다. 그런 다음이 등록 된 서비스의 getSessionFactoryBuilder 메소드는 hibernate가 부트 스트랩 될 때 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
    

    응용 프로그램, 스프링 응용 프로그램 또는 독립 실행 형 최대 절전 모드를 실행하면 최대 절전 모드가 부트 스트랩 된 후 정적 메서드를 통해 MetadataImplementor 인스턴스를 사용할 수 있습니다.

    업데이트 1 :

    스프링을 통해 주입 할 수있는 방법은 없습니다. 나는 Hibernate의 소스 코드를 파고 들었고 메타 데이터 객체는 SessionFactory (Spring에서 얻은 것)에 저장되지 않았다. 그래서 주사는 불가능합니다. 그러나 Spring의 방식으로 원하면 두 가지 옵션이 있습니다.

    LocalSessionFactoryBean은 Spring에서 설정하는 것으로, MetadataSources 객체를 가지고 있습니다. MetadataSources는 MetadataImplementor를 생성하는 MetadataBuilder를 만듭니다. 위의 모든 작업은 아무 것도 저장하지 않고 즉시 개체를 만들어 반환합니다. MetaData의 인스턴스를 가지려면 위의 클래스를 확장하고 수정하여 반환하기 전에 각 객체의 로컬 복사본을 저장해야합니다. 그렇게하면 MetadataImplementor에 대한 참조를 얻을 수 있습니다. 하지만 API가 시간이 지남에 따라 변경 될 수 있기 때문에 실제로 필요하지 않는 한이 기능을 권장하지는 않습니다.

  2. ==============================

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

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

    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);
        }
    }
    
  5. from https://stackoverflow.com/questions/34612019/programmatic-schemaexport-schemaupdate-with-hibernate-5-and-spring-4 by cc-by-sa and MIT license