【问题标题】:Generate DDL with spring boot using a custom delimiter使用自定义分隔符通过 Spring Boot 生成 DDL
【发布时间】:2017-06-09 11:42:21
【问题描述】:

我想使用 Spring Boot v1.4.3 和 JPA - Hibernate 5.0.11 生成创建和删除 ddl 脚本。

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

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

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

我希望它输出带有分隔符;的语句

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

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

所以我想使用hibernate的SchemaExport,因为你可以set the delimiter property。但是要创建一个SchemaExport,我需要一个MetadataImplementor(未弃用的api)。

我不知道如何从 spring boot 中获取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>   

使用休眠 5.0

我刚刚用休眠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 构造函数参数。

【问题讨论】:

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


    【解决方案1】:

    我发现sessionFactory包含schemaExport 值,例如createSQL, dropSQL, outputFiledelimiter

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

    ....
    
    @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。我没有找到仅使用属性并在那里设置分隔符的解决方案。但可能是寻找有效解决方案的小帮手。

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

    【讨论】:

      【解决方案2】:

      最后经过大量调查,我想我找到了一个使用公共 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 中用于执行模式生成。例如

      @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 配置休眠方言。就我而言:

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

      【讨论】:

      • 使用 Spring 2.0 设置,上下文中没有 EntityScanPackages bean,我错过了什么?
      【解决方案3】:

      这可能是一种解决方法,但在我的情况下,在项目构建期间添加分号就足够了。您可以使用 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】:

        我已经修改了 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();
            }
        
        }
        

        【讨论】:

          【解决方案5】:

          您可能想尝试设置以下 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。我希望找到一种方法来替换文件内容。

          【讨论】:

          • 跟进追加/替换问题:已解决 pull request HHH-11817 已合并到 Hibernate 主分支 (2021-06-21),并随 Hibernate 5.5 一起发布.3.决赛(2021 年 6 月 22 日)。 => 设置hibernate.hbm2ddl.schema-generation.script.append=false 完全替换create-target SQL/DDL 文件的内容。
          猜你喜欢
          • 2014-02-02
          • 1970-01-01
          • 2013-10-12
          • 1970-01-01
          • 1970-01-01
          • 2019-09-13
          • 2020-04-16
          • 2021-03-09
          • 1970-01-01
          相关资源
          最近更新 更多