使用自定义定界符通过 spring 引导生成 DDL
Generate DDL with spring boot using a custom delimiter
我想使用 spring 引导 v1.4.3 和 JPA - Hibernate 5.0.11 生成创建和删除 ddl 脚本。
我找到的大多数答案都使用 javax.persistence.schema-generation
属性。例如。
这种方法的问题在于它输出的 sql 语句没有分隔符。例如
create table ... (...)
create table ... (...)
我希望它输出带有分隔符 ;
的语句
create table ... (...);
create table ... (...);
但我找不到任何 javax.persistence.schema-generation
属性 来配置它。
所以我想使用SchemaExport
from hibernate, because you can set the delimiter property。但是要创建一个 SchemaExport
,我需要一个 MetadataImplementor
(未弃用 api)。
我不知道如何从 spring 引导中获取 MetadataImplementor
。
有谁知道有没有
- a
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);
}
}
实体Class
@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;
}
}
另一个实体class
@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
我刚刚在休眠模式下尝试了上面的代码 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 配置 return 一个 MetadataImplementor
而不是 Metadata
并更改 ApplicationRunner
构造函数参数。
我发现 sessionFactory
包含 schemaExport
值,例如 createSQL, dropSQL, outputFile
和 delimiter
。
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。我找不到仅使用属性并在那里设置分隔符的解决方案。但可能会成为寻找工作解决方案的小帮手。
如果我找到更多有用的信息,我会更新我的答案。
经过大量调查,我认为我终于找到了一个使用 public API 的简单解决方案。我找到的解决方案使用休眠5.2
(更具体5.2.6.Final
)。不过我觉得也可以适配5.0
这是我的springjava配置
@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 引导 application.properties
配置的。就我而言:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL57InnoDBDialect
这可能是一种解决方法,但就我而言,在项目构建期间添加分号就足够了。您可以使用 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>
我修改了 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();
}
}
您可能想尝试设置以下 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。我希望我找到了一种方法来 替换 文件内容。
我想使用 spring 引导 v1.4.3 和 JPA - Hibernate 5.0.11 生成创建和删除 ddl 脚本。
我找到的大多数答案都使用 javax.persistence.schema-generation
属性。例如。
这种方法的问题在于它输出的 sql 语句没有分隔符。例如
create table ... (...)
create table ... (...)
我希望它输出带有分隔符 ;
create table ... (...);
create table ... (...);
但我找不到任何 javax.persistence.schema-generation
属性 来配置它。
所以我想使用SchemaExport
from hibernate, because you can set the delimiter property。但是要创建一个 SchemaExport
,我需要一个 MetadataImplementor
(未弃用 api)。
我不知道如何从 spring 引导中获取 MetadataImplementor
。
有谁知道有没有
- a
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);
}
}
实体Class
@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;
}
}
另一个实体class
@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
我刚刚在休眠模式下尝试了上面的代码 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 配置 return 一个 MetadataImplementor
而不是 Metadata
并更改 ApplicationRunner
构造函数参数。
我发现 sessionFactory
包含 schemaExport
值,例如 createSQL, dropSQL, outputFile
和 delimiter
。
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。我找不到仅使用属性并在那里设置分隔符的解决方案。但可能会成为寻找工作解决方案的小帮手。
如果我找到更多有用的信息,我会更新我的答案。
经过大量调查,我认为我终于找到了一个使用 public API 的简单解决方案。我找到的解决方案使用休眠5.2
(更具体5.2.6.Final
)。不过我觉得也可以适配5.0
这是我的springjava配置
@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 引导 application.properties
配置的。就我而言:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL57InnoDBDialect
这可能是一种解决方法,但就我而言,在项目构建期间添加分号就足够了。您可以使用 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>
我修改了 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();
}
}
您可能想尝试设置以下 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。我希望我找到了一种方法来 替换 文件内容。