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
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.
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();
}
}
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>
What I found out is that the sessionFactory
holds 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.
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