Generate DDL with spring boot using a custom delimiter

前端 未结 5 1824
臣服心动
臣服心动 2020-12-31 20:27

I want generate create and drop ddl scripts using spring boot v1.4.3 with JPA - Hibernate 5.0.11.

Most answers I found use the javax.persistence.schema-generati

相关标签:
5条回答
  • 2020-12-31 21:00

    You might want to try setting the following Hibernate property:

    spring.jpa.properties.hibernate.hbm2ddl.delimiter=;
    
    #in addition to the other standard JPA properties you refered to, namely:
    spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
    spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=create.sql
    

    I found this to do the job in a Spring Boot 2.1.2.RELEASE + matching Hibernate version (5.3.7.Final) project where I needed the same feature.

    It might very well work on your not-so-different Hibernate environment.


    Slightly off-topic, but one issue I have remains: Hibernate appends to create.sql. I whish I found a way to have it replace the file contents.

    0 讨论(0)
  • 2020-12-31 21:05

    I have modified the solution from René to work in Spring Boot 2. Tested with version 2.0.4:

    @Configuration
    @AutoConfigureAfter({ HibernateJpaAutoConfiguration.class })
    public class HibernateMetadataBean {
    
        @ConditionalOnMissingBean({ Metadata.class })
        @Bean
        public Metadata getMetadata(StandardServiceRegistry standardServiceRegistry,
                                    PersistenceUnitInfo persistenceUnitInfo) {
            MetadataSources metadataSources = new MetadataSources(standardServiceRegistry);
    
            List<String> managedClassNames = persistenceUnitInfo.getManagedClassNames();
            for (String managedClassName : managedClassNames) {
                metadataSources.addAnnotatedClassName(managedClassName);
            }
    
            return metadataSources.buildMetadata();
        }
    
        @ConditionalOnMissingBean({ StandardServiceRegistry.class })
        @Bean
        public StandardServiceRegistry getStandardServiceRegistry(JpaProperties jpaProperties) {
            StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder();
    
            Map<String, String> properties = jpaProperties.getProperties();
            ssrb.applySettings(properties);
    
            return ssrb.build();
        }
    
        @ConditionalOnMissingBean({ PersistenceUnitInfo.class })
        @Bean
        public PersistenceUnitInfo getPersistenceUnitInfo(BeanFactory beanFactory) {
            List<String> packagesToScan = EntityScanPackages.get(beanFactory).getPackageNames();
            if (packagesToScan.isEmpty() && AutoConfigurationPackages.has(beanFactory)) {
                packagesToScan = AutoConfigurationPackages.get(beanFactory);
            }
    
            DefaultPersistenceUnitManager persistenceUnitManager = new DefaultPersistenceUnitManager();
    
            String[] packagesToScanArr = StringUtils.toStringArray(packagesToScan);
            persistenceUnitManager.setPackagesToScan(packagesToScanArr);
            persistenceUnitManager.afterPropertiesSet();
    
            return persistenceUnitManager.obtainDefaultPersistenceUnitInfo();
        }
    
    }
    
    0 讨论(0)
  • 2020-12-31 21:14

    It's maybe a workaround but in my case it's enough to add semicolons during the build of the project. You can do that using a maven plugin, e.g.:

    <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <version>1.6.0</version>
                    <executions>
                        <execution>
                            <id>add-semicolon-to-sql-file</id>
                            <phase>generate-resources</phase>
                            <goals>
                                <goal>exec</goal>
                            </goals>
                            <configuration>
                                <executable>sed</executable>
                                <arguments>
                                    <argument>-i</argument>
                                    <argument>/;$/!s/$/;/</argument>
                                    <argument>src/main/resources/db/migration/V1__init.sql</argument>
                                </arguments>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
    
    0 讨论(0)
  • 2020-12-31 21:17

    What I found out is that the sessionFactoryholds the schemaExport values like createSQL, dropSQL, outputFile and delimiter.

    The sessionFactory as a Bean can be created like that and is then available for autowiring:

    ....
    
    @Autowired
    private EntityManagerFactory entityManagerFactory;
    
    @Bean
    public SessionFactory sessionFactory() {
        if (entityManagerFactory.unwrap(SessionFactory.class) == null) {
            throw new NullPointerException("factory is not a hibernate factory");
        }
        return entityManagerFactory.unwrap(SessionFactory.class);
    }
    

    That is not a working solution but could help you to manually configure the schemaExport using sessionFactory. I dont find a solution for just using the properties and setting the delimiter just there. But could be a little helper for finding the working solution.

    If I find more useful infos I will update my answer.

    0 讨论(0)
  • 2020-12-31 21:18

    Finally after a lot of investigation I think I found an easy solution that uses public APIs. The solution I found uses hibernate 5.2 (more concrete 5.2.6.Final). But I think it can also be adapted to 5.0

    Here is my spring java configuration

    @Configuration
    @AutoConfigureAfter({ HibernateJpaAutoConfiguration.class })
    public class HibernateJavaConfig {
    
        @ConditionalOnMissingBean({ Metadata.class })
        @Bean
        public Metadata getMetadata(StandardServiceRegistry standardServiceRegistry,
                PersistenceUnitInfo persistenceUnitInfo) {
            MetadataSources metadataSources = new MetadataSources(standardServiceRegistry);
    
            List<String> managedClassNames = persistenceUnitInfo.getManagedClassNames();
            for (String managedClassName : managedClassNames) {
                metadataSources.addAnnotatedClassName(managedClassName);
            }
    
            Metadata metadata = metadataSources.buildMetadata();
            return metadata;
        }
    
        @ConditionalOnMissingBean({ StandardServiceRegistry.class })
        @Bean
        public StandardServiceRegistry getStandardServiceRegistry(JpaProperties jpaProperties) {
            StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder();
    
            Map<String, String> properties = jpaProperties.getProperties();
            ssrb.applySettings(properties);
    
            StandardServiceRegistry ssr = ssrb.build();
            return ssr;
        }
    
        @ConditionalOnMissingBean({ PersistenceUnitInfo.class })
        @Bean
        public PersistenceUnitInfo getPersistenceUnitInfo(EntityScanPackages entityScanPackages) {
            List<String> packagesToScan = entityScanPackages.getPackageNames();
    
            DefaultPersistenceUnitManager persistenceUnitManager = new DefaultPersistenceUnitManager();
    
            String[] packagesToScanArr = (String[]) packagesToScan.toArray(new String[packagesToScan.size()]);
            persistenceUnitManager.setPackagesToScan(packagesToScanArr);
            persistenceUnitManager.afterPropertiesSet();
    
            PersistenceUnitInfo persistenceUnitInfo = persistenceUnitManager.obtainDefaultPersistenceUnitInfo();
            return persistenceUnitInfo;
        }
    
    }
    

    The java configuration creates a Metadata bean. This bean can be used in hibernate 5.2 to execute a schema generation. E.g.

    @Component
    public class GenerateDDLApplicationRunner implements ApplicationRunner {
    
        private Metadata metadata;
    
        public GenerateDDLApplicationRunner(Metadata metadata) {
            this.metadata = metadata;
        }
    
        public void run(ApplicationArguments args) throws Exception {
            File dropAndCreateDdlFile = new File("drop-and-create.ddl");
            deleteFileIfExists(dropAndCreateDdlFile);
    
            SchemaExport schemaExport = new SchemaExport();
            schemaExport.setDelimiter(";");
            schemaExport.setFormat(false);
            schemaExport.setOutputFile(dropAndCreateDdlFile.getAbsolutePath());
    
            schemaExport.execute(EnumSet.of(TargetType.SCRIPT), Action.BOTH, metadata);
        }
    
        private void deleteFileIfExists(File dropAndCreateDdlFile) {
            if (dropAndCreateDdlFile.exists()) {
                if (!dropAndCreateDdlFile.isFile()) {
                    String msg = MessageFormat.format("File is not a normal file {0}", dropAndCreateDdlFile);
                    throw new IllegalStateException(msg);
                }
    
                if (!dropAndCreateDdlFile.delete()) {
                    String msg = MessageFormat.format("Unable to delete file {0}", dropAndCreateDdlFile);
                    throw new IllegalStateException(msg);
                }
            }
        }
    
    }
    

    The hibernate dialect is configured using the spring boot application.properties. In my case:

    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL57InnoDBDialect
    
    0 讨论(0)
提交回复
热议问题