使用自定义分隔符生成具有弹簧引导的DDL

时间:2017-01-24 15:36:57

标签: spring hibernate spring-boot spring-data-jpa ddl

我希望使用带有JPA - Hibernate 5.0.11的spring boot v1.4.3生成create和drop ddl脚本。

我发现的大多数答案都使用javax.persistence.schema-generation属性。例如。 https://stackoverflow.com/a/36966419/974186

这种方法的问题是它输出没有分隔符的sql语句。 E.g。

create table ... (...)
create table ... (...)

我希望它使用分隔符;

输出语句
create table ... (...);
create table ... (...);

但我找不到任何javax.persistence.schema-generation属性来配置它。

所以我想从hibernate中使用SchemaExport,因为你可以set the delimiter property。但要创建SchemaExport我需要MetadataImplementor(非删除api)。

我无法弄清楚如何从春季启动中获得MetadataImplementor

有没有人知道是否有

  • 用于定义分隔符的javax.persistence.schema-generation属性
  • 或如何创建SchemaExport(获取依赖项)
  • 还是有其他解决方案?

以下是您可以使用的一些代码

@SpringBootApplication
@ComponentScan(basePackageClasses = Application.class)
@EntityScan(basePackageClasses = User.class)
public class Application {

    @Bean
    public ApplicationRunner getApplicationRunner() {
        return new ApplicationRunner() {

            public void run(ApplicationArguments args) throws Exception {
                // MetadataImplementor metadataImplementor = ???;
                // new SchemaExport(metadataImplementor);
            }
        };
    }

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        application.run(args);
    }

}

实体类

@Entity
public class User {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

另一个实体类

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

application.properties

spring.jpa.properties.javax.persistence.schema-generation.create-source=metadata
spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=create.sql

maven依赖

<properties>
    <spring.boot.version>1.4.3.RELEASE</spring.boot.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring.boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
</dependencies>   

使用Hibernate 5.0

我刚用hibernate 5.0.11.Final尝试了上面的代码。你唯一必须改变的是

SchemaExport schemaExport = new SchemaExport((MetadataImplementor) metadata);
schemaExport.setDelimiter(";");
schemaExport.setFormat(false);
schemaExport.setOutputFile(dropAndCreateDdlFile.getAbsolutePath());

schemaExport.execute(true, false, false, false);

或者当然让java配置返回MetadataImplementor而不是Metadata并更改ApplicationRunner构造函数参数。

5 个答案:

答案 0 :(得分:4)

经过大量调查后,我认为我找到了一个使用公共API的简单解决方案。我找到的解决方案使用了休眠5.2(更具体5.2.6.Final)。但我认为它也可以适应5.0

这是我的spring java配置

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

}

java配置创建一个Metadata bean。这个bean可以在hibernate 5.2中用来执行模式生成。 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);
            }
        }
    }

}

使用spring boot application.properties配置hibernate方言。就我而言:

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL57InnoDBDialect

答案 1 :(得分:1)

我已经修改了René的解决方案,使其可以在Spring Boot 2中使用。经过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();
    }

}

答案 2 :(得分:0)

我发现sessionFactory包含schemaExportcreateSQL, dropSQL, outputFiledelimiter

enter image description here

作为Bean的sessionFactory可以像这样创建,然后可用于自动装配:

....

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

这不是一个有效的解决方案,但可以帮助您使用sessionFactory手动配置schemaExport。我没有找到只使用属性并在那里设置分隔符的解决方案。但是找到合适的解决方案可能是一个小帮手。

如果我找到更多有用的信息,我会更新我的答案。

答案 3 :(得分:0)

这可能是一种解决方法,但在我的情况下,它足以在项目构建期间添加分号。你可以使用maven插件来做到这一点,例如:

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

答案 4 :(得分:0)

您可能想尝试设置以下Hibernate属性:

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

我发现这是在 Spring Boot 2.1.2.RELEASE +匹配的Hibernate版本( 5.3.7.Final )项目中完成的,而我需要相同的项目特征。

在不太不同的Hibernate环境中,它可能会很好地工作。


稍微偏离主题,但我仍然有一个问题:Hibernate 追加到create.sql。我希望找到一种方法来替换文件内容。