在没有数据库连接的情况下从 JPA 2.1 注释实体 类 自动生成数据模式
Auto generate data schema from JPA 2.1 annotated entity classes without a database connection
两年前我在做一个项目使用:
- spring4.0.3.RELEASE
- jpa 2.0
- 休眠 4.2.7.Final
- java1.6.X
这个项目有一个 maven 任务 hibernate3-maven-plugin,它允许我们生成数据库模式而无需与数据库建立任何连接 (MySQL)。
现在我们正在升级这个项目:
- java 1.8
- jpa 2.1
- spring 4.2.4.RELEASE
- 休眠 5.0.6.Final
我知道 hibernate3-maven-plugin 不适用于 JPA 2.1 和 hibernate > 4.3。
我找到的所有解决方案都需要连接到数据库。
例如:Auto generate data schema from JPA annotated entity classes.
有谁知道如何离线生成数据库模式?
我只有一个 persistence.xml,其中列出了所有实体 类。
作为另一个问题,您可以使用 hbm2ddl 和嵌入式数据库来提供连接。
例如使用 H2 数据库(需要 h2、scannotation、hibernate 和 common-io):
package com.Whosebug;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.net.URL;
import java.util.Set;
import javax.persistence.Entity;
import org.apache.commons.io.IOUtils;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.connection.DriverManagerConnectionProvider;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.scannotation.AnnotationDB;
public class ExportShema {
private static final String OUTPUT_SQL_FILE = "target/database.sql";
private static final String INIT_FILE = "init.sql";
private static final String DB_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
private static final String DB_USERNAME = "sa";
private static final String DB_PASSWORD = "";
private static final File HBM_DIRECTORY = new File("src/main/resources/com/Whosebug/domain/");
public static void main(final String[] args) throws Exception {
final Configuration cfg = new Configuration();
cfg.setProperty(Environment.CONNECTION_PROVIDER, DriverManagerConnectionProvider.class.getName());
//for postgrest schema
cfg.setProperty(Environment.DIALECT, PostgreSQLDialect.class.getName());
cfg.setProperty(Environment.URL, DB_URL);
cfg.setProperty(Environment.USER, DB_USERNAME);
cfg.setProperty(Environment.PASS, DB_PASSWORD);
//If you have HBM + annotated class
cfg.addDirectory(HBM_DIRECTORY);
final AnnotationDB db = new AnnotationDB();
db.scanArchives(new URL("file:target/classes/"));
final Set<String> clazzNames = db.getAnnotationIndex().get(Entity.class.getName());
for (final String clazzName : clazzNames) {
cfg.addAnnotatedClass(Class.forName(clazzName));
}
final SchemaExport exporter = new SchemaExport(cfg);
exporter.setOutputFile(OUTPUT_SQL_FILE);
exporter.setDelimiter(";");
exporter.setFormat(true);
exporter.create(false, true);
try (final InputStream init_file = ExportShema.class.getResourceAsStream(INIT_FILE)) {
if (init_file != null) {
final File output = new File(OUTPUT_SQL_FILE);
try (final FileWriter fw = new FileWriter(output, true)) {
final String eol = System.getProperty("line.separator");
fw.append(eol + eol);
fw.append(IOUtils.toString(init_file));
}
}
}
}
}
您可以在单元测试中执行此操作或创建注释处理器。
我遵循你的想法,将 h2 与 Mysql 方言一起使用,但使用 JPA Persistence.generateSchema(...)
。
它确实有效,除了所有命令都没有被 semi-column...
分隔
如何使用 JPA 2.1 完成此操作?
否则我会切换到你的解决方案。
import java.util.Properties;
import javax.persistence.Persistence;
import javax.persistence.PersistenceException;
import org.hibernate.jpa.AvailableSettings;
/**
* Generate DDL with hibernate 4+/5:
*
* @author dmary
*
*/
public class Jpa21SchemaExport {
/**
*
*/
public Jpa21SchemaExport() {
// TODO Auto-generated constructor stub
}
/**
* @param args
*/
public static void main(String[] args) {
execute(args[0], args[1]);
System.exit(0);
}
public static void execute(String persistenceUnitName, String destination) {
System.out.println("Generating DDL create script to : " + destination);
final Properties persistenceProperties = new Properties();
// XXX force persistence properties : remove database target
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "none");
// XXX force persistence properties : define create script target from metadata to destination
// persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, "true");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION, "create");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SOURCE, "metadata");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET, destination);
persistenceProperties.setProperty(AvailableSettings.JDBC_DRIVER,"org.h2.Driver");
persistenceProperties.setProperty(AvailableSettings.JDBC_URL, "jdbc:h2:mem:export");
persistenceProperties.setProperty(AvailableSettings.JDBC_USER, "sa");
persistenceProperties.setProperty(AvailableSettings.JDBC_PASSWORD, "");
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.DIALECT, "com.wiztivi.sdk.persistence.MySQL5InnoDBUTF8Dialect");
try
{
Persistence.generateSchema(persistenceUnitName, persistenceProperties);
} catch (PersistenceException pe)
{
System.err.println("DDL generation failed: ");
pe.printStackTrace(System.err);
}
}
我能够将您的 Hibernate 解决方案与 JPA2.1 混合使用:
我现在可以从 persistence.xml
添加实体 类
这样我就可以在实体所在的 jar 之外生成 SQl 文件。
这是一个临时解决方案,直到 hibernate 修复此问题 bug
感谢您的帮助。
/**
*
*/
package com.whosebug.common.util.schema;
import java.io.IOException;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.dialect.MySQL5InnoDBDialect;
/**
*
*/
public class JPA21Hibernate5ExportSchema {
private static final String JDBC_DRIVER = "org.h2.Driver";
private static final String JDBC_URL = "jdbc:h2:mem:export;DB_CLOSE_DELAY=-1";
private static final String JDBC_USERNAME = "sa";
private static final String JDBC_PASSWORD = "";
/**
*
*/
public JPA21Hibernate5ExportSchema() {
}
public static void main(String[] args) {
try {
JPA21Hibernate5ExportSchema hes = new JPA21Hibernate5ExportSchema();
hes.export(args[0], args[1]);
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public void export(String persistenceUnitName, String sqlFile) throws IOException, ClassNotFoundException {
final BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build();
final MetadataSources metadataSources = new MetadataSources(bsr);
final StandardServiceRegistryBuilder srrBuilder = new StandardServiceRegistryBuilder(bsr)
.applySetting(Environment.CONNECTION_PROVIDER, DriverManagerConnectionProviderImpl.class.getName())
.applySetting(Environment.DIALECT, MySQL5InnoDBDialect.class.getName())
.applySetting(Environment.URL, JDBC_URL).applySetting(Environment.USER, JDBC_USERNAME)
.applySetting(Environment.PASS, JDBC_PASSWORD);
// Use the persistence metamodel to retrieve the Entities classes
Metamodel metamodel = this.getMetamodel(persistenceUnitName);
for (final ManagedType<?> managedType : metamodel.getManagedTypes()) {
metadataSources.addAnnotatedClass(managedType.getJavaType());
}
final StandardServiceRegistry ssr = (StandardServiceRegistry) srrBuilder.build();
final MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder(ssr);
final SchemaExport exporter = new SchemaExport((MetadataImplementor) metadataBuilder.build());
exporter.setOutputFile(sqlFile);
exporter.setDelimiter(";");
exporter.setFormat(true);
exporter.create(false, true);
}
/**
* Retrieve the JPA metamodel from the persistence unit name
*
* @param persistenceUnitName
* @return
*/
private Metamodel getMetamodel(String persistenceUnitName) {
final Properties persistenceProperties = new Properties();
persistenceProperties.setProperty(AvailableSettings.JDBC_DRIVER, JDBC_DRIVER);
persistenceProperties.setProperty(AvailableSettings.JDBC_URL, JDBC_URL);
persistenceProperties.setProperty(AvailableSettings.JDBC_USER, "sa");
persistenceProperties.setProperty(AvailableSettings.JDBC_PASSWORD, "");
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.DIALECT,
MySQL5InnoDBDialect.class.getName());
final EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName,
persistenceProperties);
return emf.getMetamodel();
}
}
两年前我在做一个项目使用:
- spring4.0.3.RELEASE
- jpa 2.0
- 休眠 4.2.7.Final
- java1.6.X
这个项目有一个 maven 任务 hibernate3-maven-plugin,它允许我们生成数据库模式而无需与数据库建立任何连接 (MySQL)。
现在我们正在升级这个项目:
- java 1.8
- jpa 2.1
- spring 4.2.4.RELEASE
- 休眠 5.0.6.Final
我知道 hibernate3-maven-plugin 不适用于 JPA 2.1 和 hibernate > 4.3。
我找到的所有解决方案都需要连接到数据库。
例如:Auto generate data schema from JPA annotated entity classes.
有谁知道如何离线生成数据库模式? 我只有一个 persistence.xml,其中列出了所有实体 类。
作为另一个问题,您可以使用 hbm2ddl 和嵌入式数据库来提供连接。
例如使用 H2 数据库(需要 h2、scannotation、hibernate 和 common-io):
package com.Whosebug;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.net.URL;
import java.util.Set;
import javax.persistence.Entity;
import org.apache.commons.io.IOUtils;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.connection.DriverManagerConnectionProvider;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.scannotation.AnnotationDB;
public class ExportShema {
private static final String OUTPUT_SQL_FILE = "target/database.sql";
private static final String INIT_FILE = "init.sql";
private static final String DB_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
private static final String DB_USERNAME = "sa";
private static final String DB_PASSWORD = "";
private static final File HBM_DIRECTORY = new File("src/main/resources/com/Whosebug/domain/");
public static void main(final String[] args) throws Exception {
final Configuration cfg = new Configuration();
cfg.setProperty(Environment.CONNECTION_PROVIDER, DriverManagerConnectionProvider.class.getName());
//for postgrest schema
cfg.setProperty(Environment.DIALECT, PostgreSQLDialect.class.getName());
cfg.setProperty(Environment.URL, DB_URL);
cfg.setProperty(Environment.USER, DB_USERNAME);
cfg.setProperty(Environment.PASS, DB_PASSWORD);
//If you have HBM + annotated class
cfg.addDirectory(HBM_DIRECTORY);
final AnnotationDB db = new AnnotationDB();
db.scanArchives(new URL("file:target/classes/"));
final Set<String> clazzNames = db.getAnnotationIndex().get(Entity.class.getName());
for (final String clazzName : clazzNames) {
cfg.addAnnotatedClass(Class.forName(clazzName));
}
final SchemaExport exporter = new SchemaExport(cfg);
exporter.setOutputFile(OUTPUT_SQL_FILE);
exporter.setDelimiter(";");
exporter.setFormat(true);
exporter.create(false, true);
try (final InputStream init_file = ExportShema.class.getResourceAsStream(INIT_FILE)) {
if (init_file != null) {
final File output = new File(OUTPUT_SQL_FILE);
try (final FileWriter fw = new FileWriter(output, true)) {
final String eol = System.getProperty("line.separator");
fw.append(eol + eol);
fw.append(IOUtils.toString(init_file));
}
}
}
}
}
您可以在单元测试中执行此操作或创建注释处理器。
我遵循你的想法,将 h2 与 Mysql 方言一起使用,但使用 JPA Persistence.generateSchema(...)
。
它确实有效,除了所有命令都没有被 semi-column...
分隔如何使用 JPA 2.1 完成此操作?
否则我会切换到你的解决方案。
import java.util.Properties;
import javax.persistence.Persistence;
import javax.persistence.PersistenceException;
import org.hibernate.jpa.AvailableSettings;
/**
* Generate DDL with hibernate 4+/5:
*
* @author dmary
*
*/
public class Jpa21SchemaExport {
/**
*
*/
public Jpa21SchemaExport() {
// TODO Auto-generated constructor stub
}
/**
* @param args
*/
public static void main(String[] args) {
execute(args[0], args[1]);
System.exit(0);
}
public static void execute(String persistenceUnitName, String destination) {
System.out.println("Generating DDL create script to : " + destination);
final Properties persistenceProperties = new Properties();
// XXX force persistence properties : remove database target
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "none");
// XXX force persistence properties : define create script target from metadata to destination
// persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, "true");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION, "create");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SOURCE, "metadata");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET, destination);
persistenceProperties.setProperty(AvailableSettings.JDBC_DRIVER,"org.h2.Driver");
persistenceProperties.setProperty(AvailableSettings.JDBC_URL, "jdbc:h2:mem:export");
persistenceProperties.setProperty(AvailableSettings.JDBC_USER, "sa");
persistenceProperties.setProperty(AvailableSettings.JDBC_PASSWORD, "");
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.DIALECT, "com.wiztivi.sdk.persistence.MySQL5InnoDBUTF8Dialect");
try
{
Persistence.generateSchema(persistenceUnitName, persistenceProperties);
} catch (PersistenceException pe)
{
System.err.println("DDL generation failed: ");
pe.printStackTrace(System.err);
}
}
我能够将您的 Hibernate 解决方案与 JPA2.1 混合使用:
我现在可以从 persistence.xml
添加实体 类这样我就可以在实体所在的 jar 之外生成 SQl 文件。
这是一个临时解决方案,直到 hibernate 修复此问题 bug
感谢您的帮助。
/**
*
*/
package com.whosebug.common.util.schema;
import java.io.IOException;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.dialect.MySQL5InnoDBDialect;
/**
*
*/
public class JPA21Hibernate5ExportSchema {
private static final String JDBC_DRIVER = "org.h2.Driver";
private static final String JDBC_URL = "jdbc:h2:mem:export;DB_CLOSE_DELAY=-1";
private static final String JDBC_USERNAME = "sa";
private static final String JDBC_PASSWORD = "";
/**
*
*/
public JPA21Hibernate5ExportSchema() {
}
public static void main(String[] args) {
try {
JPA21Hibernate5ExportSchema hes = new JPA21Hibernate5ExportSchema();
hes.export(args[0], args[1]);
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public void export(String persistenceUnitName, String sqlFile) throws IOException, ClassNotFoundException {
final BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build();
final MetadataSources metadataSources = new MetadataSources(bsr);
final StandardServiceRegistryBuilder srrBuilder = new StandardServiceRegistryBuilder(bsr)
.applySetting(Environment.CONNECTION_PROVIDER, DriverManagerConnectionProviderImpl.class.getName())
.applySetting(Environment.DIALECT, MySQL5InnoDBDialect.class.getName())
.applySetting(Environment.URL, JDBC_URL).applySetting(Environment.USER, JDBC_USERNAME)
.applySetting(Environment.PASS, JDBC_PASSWORD);
// Use the persistence metamodel to retrieve the Entities classes
Metamodel metamodel = this.getMetamodel(persistenceUnitName);
for (final ManagedType<?> managedType : metamodel.getManagedTypes()) {
metadataSources.addAnnotatedClass(managedType.getJavaType());
}
final StandardServiceRegistry ssr = (StandardServiceRegistry) srrBuilder.build();
final MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder(ssr);
final SchemaExport exporter = new SchemaExport((MetadataImplementor) metadataBuilder.build());
exporter.setOutputFile(sqlFile);
exporter.setDelimiter(";");
exporter.setFormat(true);
exporter.create(false, true);
}
/**
* Retrieve the JPA metamodel from the persistence unit name
*
* @param persistenceUnitName
* @return
*/
private Metamodel getMetamodel(String persistenceUnitName) {
final Properties persistenceProperties = new Properties();
persistenceProperties.setProperty(AvailableSettings.JDBC_DRIVER, JDBC_DRIVER);
persistenceProperties.setProperty(AvailableSettings.JDBC_URL, JDBC_URL);
persistenceProperties.setProperty(AvailableSettings.JDBC_USER, "sa");
persistenceProperties.setProperty(AvailableSettings.JDBC_PASSWORD, "");
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.DIALECT,
MySQL5InnoDBDialect.class.getName());
final EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName,
persistenceProperties);
return emf.getMetamodel();
}
}