Автоматическое создание схемы данных из аннотированных классов сущностей JPA 2.1 без подключения к базе данных

Два года назад я работал над проектом, используя:

  • весна 4.0.3.RELEASE
  • jpa 2.0
  • спящий режим 4.2.7.Финал
  • java 1.6.X

В этом проекте есть maven-задача hibernate3-maven-plugin, которая позволяет нам создавать схему базы данных без какого-либо подключения к базе данных (MySQL).

Сейчас мы обновляем этот проект с помощью:

  • Java 1.8
  • JPA 2.1
  • весна 4.2.4.РЕЛИЗ
  • спящий режим 5.0.6.Final

Я понимаю, что hibernate3-maven-plugin не работает на JPA 2.1 и hibernate> 4.3.

Все найденные мной решения требуют подключения к базе данных.

Например: Автоматическое создание схемы данных из классов сущностей с аннотациями JPA .

Кто-нибудь знает, как сгенерировать схему базы данных в автономном режиме? Все, что у меня есть, - это persistence.xml со всеми перечисленными классами Entity.


person David Montpellier    schedule 20.01.2016    source источник
comment
Здесь нет никакого отношения к весне, пожалуйста, удалите тег   -  person chokdee    schedule 20.01.2016


Ответы (3)


Я следую вашей идее использования h2 с диалектом Mysql, но с использованием JPA Persistence.generateSchema(...).

Он работает, за исключением того, что все команды не разделены полуколонкой ...

Как это можно сделать с помощью 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:
 * http://stackoverflow.com/questions/27314165/generate-ddl-script-at-maven-build-with-hibernate4-jpa-2-1/27314166#27314166
 * @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); 
        }
    }
person David Montpellier    schedule 20.01.2016
comment
Отсутствие точки с запятой - известная ошибка: hibernate.atlassian.net/browse/HHH-10278 - person David Montpellier; 21.01.2016
comment
Странное приложение. не существует без System.exit(0) - person Peter Šály; 22.12.2016
comment
Самый простой обходной путь заключается в следующем: // генерируем sql с точкой с запятой - обходной путь для HHH-10278 String systemLineSeparator = System.getProperty (line.separator); System.setProperty (line.separator, ';' + systemLineSeparator); Persistence.generateSchema (persistenceUnitName, persistenceProperties); - person blacky; 15.02.2017

В качестве другого вопроса вы можете использовать hbm2ddl и встроенную базу данных для обеспечения соединения.

Например, с использованием базы данных H2 (требуется h2, scannotation, hibernate и common-io):

package com.stackoverflow;
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/stackoverflow/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));
                }
            }
        }
}
}

вы можете сделать это в модульном тесте или создать обработчик аннотаций.

person JEY    schedule 20.01.2016
comment
Я наткнулся на это решение, но не знал, соответствует ли сгенерированный SQL на 100ù Mysql. Вы знаете, где я могу найти эту информацию? - person David Montpellier; 20.01.2016
comment
Это будет зависеть от того, насколько хорошо реализован класс Mysql Dialect. Я использую его для postgresql, и он отлично работает. - person JEY; 20.01.2016

Мне удалось смешать ваше решение Hibernate с JPA2.1:

Теперь я могу добавить классы сущностей из файла persistence.xml.

Таким образом я могу сгенерировать файл SQl вне банки, в которой расположены объекты.

Это временное решение, пока спящий режим не исправит эту ошибку.

Спасибо за вашу помощь.

/**
 * 
 */
package com.stackoverflow.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();
    }

}
person David Montpellier    schedule 22.01.2016