Configuration.generateSchemaCreationScript() 在 Hibernate 5 中去了哪里

Posted

技术标签:

【中文标题】Configuration.generateSchemaCreationScript() 在 Hibernate 5 中去了哪里【英文标题】:Where did Configuration.generateSchemaCreationScript() go in Hibernate 5 【发布时间】:2015-11-17 15:00:17 【问题描述】:

在 Hibernate 4.x 中,我曾经生成和导出带注释实体中定义的模式,如下所示(使用 Spring 在类路径上查找带注释的实体):

Connection connection = 
    DriverManager.getConnection("jdbc:h2:mem:jooq-meta-extensions", "sa", "");

Configuration configuration = new Configuration()
    .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");

// [...] adding annotated classes to Configuration here...

configuration.generateSchemaCreationScript(
    Dialect.getDialect(configuration.getProperties()));
SchemaExport export = new SchemaExport(configuration, connection);
export.create(true, true);

这在 Hibernate 5.0 中不再有效:

Configuration.generateSchemaCreationScript() 不再存在 SchemaExport(configuration, connection) 构造函数现已弃用

除了:

相当多的方法已从配置中删除

使用 Hibernate 5.0 基于一组带注释的实体在现有 JDBC 连接上生成和导出数据库的正确方法是什么? (纯基于 JPA 的解决方案也可以)

(请注意,仅删除对 generateSchemaCreationScript() 的调用似乎可行,但我希望确保正确处理)

【问题讨论】:

感谢您的编辑,@NeilStockton。如果 Hibernate 实现的纯基于 JPA 的解决方案是可能的,我也会把它作为答案。 您的意思是为 persistence.xml 中的类所需的模式创建一个 DDL 文件? (因为我不使用 Hibernate,所以不知道这些方法是什么)。这不是 javax.persistence.schema-generation.scripts.action 和 javax.persistence.schema-generation.scripts.create-target 的属性吗? 问题实际上是“使用 Hibernate 5.0 基于一组带注释的实体生成导出数据库的正确方法是什么?(纯基于 JPA 的解决方案也可以)" 【参考方案1】:

感谢Vlad 和Gunnar 的回答,我已经设法通过新的配置API 来生成具有以下内容的等效导出逻辑。当然,历史表明这个 API 会再次崩溃,所以一定要选择合适的版本:

休眠 5.2:

MetadataSources metadata = new MetadataSources(
    new StandardServiceRegistryBuilder()
        .applySetting("hibernate.dialect", "org.hibernate.dialect.H2Dialect")
        .applySetting("javax.persistence.schema-generation-connection", connection)
        .build());

// [...] adding annotated classes to metadata here...
metadata.addAnnotatedClass(...);

SchemaExport export = new SchemaExport();
export.create(EnumSet.of(TargetType.DATABASE), metadata.buildMetadata());

休眠 5.2(无警告):

上面会产生一些讨厌的警告,可以忽略:

2016 年 10 月 20 日下午 2:57:16 org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator 启动服务 警告:HHH000181:没有遇到适当的连接提供程序,假设应用程序将提供连接 2016 年 10 月 20 日下午 2:57:16 org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator 启动服务 警告:HHH000342:无法获得查询元数据的连接:应用程序必须提供 JDBC 连接

...或者您通过将以下ConnectionProvider 破解到设置中来解决它们(我认为它不应该是必需的)

        .applySetting(AvailableSettings.CONNECTION_PROVIDER, new ConnectionProvider() 
            @Override
            public boolean isUnwrappableAs(Class unwrapType) 
                return false;
            
            @Override
            public <T> T unwrap(Class<T> unwrapType) 
                return null;
            
            @Override
            public Connection getConnection() 
                return connection; // Interesting part here
            
            @Override
            public void closeConnection(Connection conn) throws SQLException 

            @Override
            public boolean supportsAggressiveRelease() 
                return true;
            
        )

休眠 5.0:

MetadataSources metadata = new MetadataSources(
    new StandardServiceRegistryBuilder()
        .applySetting("hibernate.dialect", "org.hibernate.dialect.H2Dialect")
        .build());

// [...] adding annotated classes to metadata here...
metadata.addAnnotatedClass(...);

SchemaExport export = new SchemaExport(
    (MetadataImplementor) metadata.buildMetadata(),
    connection // pre-configured Connection here
);
export.create(true, true);

休眠 4:

提醒一下,这是在 Hibernate 4 中的工作方式:

Configuration configuration = new Configuration()
    .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");

// [...] adding annotated classes to metadata here...
configuration.addAnnotatedClass(...);

configuration.generateSchemaCreationScript(
    Dialect.getDialect(configuration.getProperties()));
SchemaExport export = new SchemaExport(configuration, connection);
export.create(true, true);

【讨论】:

为什么休眠需要连接?在测试设置中,根据已知的良好版本检查生成的架构时不需要它。 @gkephorus:我不确定你的意思。在我的例子中,模式是由一些 DDL 语句编写的,而 Hibernate 用于生成实体类(另见问题)。当然,还有其他设置,但这个问题是关于这个特定用例的。 我的立场是正确的,我应该提出一个新问题。对不起。 (这个问题的答案是正确的) 卢卡斯,感谢您的解决方案。但这似乎不适用于 HIbernate 5.2,因为我无法将任何内容传递给 Schemaexport。你遇到过这个问题吗? —— @FreakyThommi:好的,我已经对他们的最新版本进行了逆向工程。现在似乎又可以工作了……答案已更新【参考方案2】:

在SchemaExportTask 中可以找到新的SchemaExport 初始化示例:

final BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build();

final MetadataSources metadataSources = new MetadataSources( bsr );
final StandardServiceRegistryBuilder s-s-rBuilder = new StandardServiceRegistryBuilder( bsr );

if ( configurationFile != null ) 
    s-s-rBuilder.configure( configurationFile );

if ( propertiesFile != null ) 
    s-s-rBuilder.loadProperties( propertiesFile );

s-s-rBuilder.applySettings( getProject().getProperties() );

for ( String fileName : getFiles() ) 
    if ( fileName.endsWith(".jar") ) 
        metadataSources.addJar( new File( fileName ) );
    
    else 
        metadataSources.addFile( fileName );
    



final StandardServiceRegistryImpl s-s-r = (StandardServiceRegistryImpl) s-s-rBuilder.build();
final MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder( s-s-r );

ClassLoaderService classLoaderService = bsr.getService( ClassLoaderService.class );
if ( implicitNamingStrategy != null ) 
    metadataBuilder.applyImplicitNamingStrategy(
            (ImplicitNamingStrategy) classLoaderService.classForName( implicitNamingStrategy ).newInstance()
    );

if ( physicalNamingStrategy != null ) 
    metadataBuilder.applyPhysicalNamingStrategy(
            (PhysicalNamingStrategy) classLoaderService.classForName( physicalNamingStrategy ).newInstance()
    );


return new SchemaExport( (MetadataImplementor) metadataBuilder.build() )
    .setHaltOnError( haltOnError )
    .setOutputFile( outputFile.getPath() )
    .setDelimiter( delimiter );

当然,你可以根据自己的需要来定制。

【讨论】:

好点。这当然可行,但在我看来,创建了许多不必要的样板类型。我想知道是否真的需要所有这些...... 看来是逃不过MetadataImplementor对象构造,确实笨重。反正这么多间接层很容易迷路,这里只是为了配置。 那就需要一个 Jira 问题。【参考方案3】:

新的引导 API 允许进行许多自定义,但假设您不需要这些,最短的调用应该是这样的,为服务注册表和所有设置应用默认值:

Metadata metadata = new MetadataSources()
    .addAnnotatedClass( MyEntity.class )
    .build();

new SchemaExport( (MetadataImplementor) metadata )
    .setOutputFile( "my-statements.ddl" )
    .create( Target.NONE );

更新:提供应用配置属性的示例

有几种方法可以为连接 URL、方言等注入属性。例如你可以提供一个文件hibernate.properties,或者你使用一个定制了所需设置的服务注册表:

StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
    .applySetting( "hibernate.connection.url", "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1" )
    .build();

Metadata metadata = new MetadataSources( registry )
    .build();

【讨论】:

假设我想要Target.BOTH,我需要connectionhibernate.dialect 配置(如问题所示)。那些会去哪里? 更新了答案以包含指定配置属性的示例 如果我显得挑剔,我很抱歉 :) 但我已经更新了问题中的一个重要细节 - 我有一个独立的 JDBC Connection 来执行 DDL,这一点很重要(对我来说)。我不希望 Hibernate 管理连接,在这种特殊情况下...... 然后调用构造函数SchemaExport(MetadataImplementor, Connection) 感谢您的更新。我真的必须指定方言or I get this exception。我想知道如果它提供给SchemaExport,Hibernate 是否无法从 JDBC 连接中猜出方言?我现在已经记录了我正在寻找的具体解决方案in an additional answer【参考方案4】:

我用 hibernate 5.4.9.Final 以这种方式导出它:

import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;

import java.util.EnumSet;

public class ExportSchema 
    public static void main(String[] args) 
        final StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
                .applySetting("hibernate.dialect", "org.hibernate.dialect.H2Dialect")
                .build();
        final Metadata metadata = new MetadataSources(serviceRegistry)
                .addAnnotatedClass(...)
                .buildMetadata();
        new SchemaExport()
                .setFormat(true)
                .setDelimiter(";")
                .setOutputFile("schema.sql")
                .execute(EnumSet.of(TargetType.SCRIPT), SchemaExport.Action.CREATE, metadata);
    

【讨论】:

【参考方案5】:

如果使用 JPA 2.1+ - 有一个非常简单的内置可能性来生成 ddl。只需设置以下 jpa 属性,就会创建 ddl 文件。使用 Spring Boot,可以使用这些特定的配置选项编写一个单独的主类。

JPA 2.1+

javax.persistence.schema-generation.scripts.action=drop-and-create
javax.persistence.schema-generation.scripts.create-target=create.ddl
javax.persistence.schema-generation.scripts.drop-target=drop.ddl

使用 JPA 2.1+ 的 Spring Boot

schemagenerator.properties(放入资源文件夹):

spring.jpa.properties.javax.persistence.schema-generation.scripts.action=drop-and-create
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=create.ddl
spring.jpa.properties.javax.persistence.schema-generation.scripts.drop-target=drop.ddl
flyway.enabled=false // in case you use flyway for db maintenance

Spring Boot SchemaGenerator:

public class SchemaGenerator 
    public static void main(String[] args) throws Exception 
        SpringApplication.run(Application.class, new String[]"--spring.config.name=schemagenerator").close();
    

【讨论】:

谢谢,但是在这个问题中,我明确地在寻找“在现有 JDBC 连接上生成和导出数据库的正确方法是什么”的解决方案跨度>

以上是关于Configuration.generateSchemaCreationScript() 在 Hibernate 5 中去了哪里的主要内容,如果未能解决你的问题,请参考以下文章