Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate 5. Generate SQL DDL into file

Tags:

java

hibernate

I tried using this class:

Hibernate/JPA: Check generated sql before updating DB Schema (like .NET EF migrations)

I have the following code:

package com.mypackage.jpa.util;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class SchemaGenerator {

    private Configuration cfg;

    public static void main(String[] args) throws Exception {

        File f = new File(".");
        String directory = f.getAbsoluteFile() + "/src/main/resources/ddl/generated/";

        String packageName[] = { "com.mypackage.jpa", "com.mypackage.jpa.legacy", "com.mypackage.jpa.local",
                "com.mypackage.jpa.local.impl" };

        SchemaGenerator gen = new SchemaGenerator(packageName);
        gen.generate(Dialect.MYSQL, directory);

    }

    @SuppressWarnings("rawtypes")
    public SchemaGenerator(String[] packagesName) throws Exception {
        cfg = new Configuration();
        cfg.setProperty("hibernate.hbm2ddl.auto", "create");

        for (String packageName : packagesName) {
            for (Class clazz : getClasses(packageName)) {
                cfg.addAnnotatedClass(clazz);
            }
        }
    }

    @SuppressWarnings("rawtypes")
    private List<Class> getClasses(String packageName) throws Exception {
        File directory = null;
        try {
            ClassLoader cld = getClassLoader();
            URL resource = getResource(packageName, cld);
            directory = new File(resource.getFile());
        } catch (NullPointerException ex) {
            throw new ClassNotFoundException(packageName + " (" + directory + ") does not appear to be a valid package");
        }
        return collectClasses(packageName, directory);
    }

    private ClassLoader getClassLoader() throws ClassNotFoundException {
        ClassLoader cld = Thread.currentThread().getContextClassLoader();
        if (cld == null) {
            throw new ClassNotFoundException("Can't get class loader.");
        }
        return cld;
    }

    private URL getResource(String packageName, ClassLoader cld) throws ClassNotFoundException {
        String path = packageName.replace('.', '/');
        URL resource = cld.getResource(path);
        if (resource == null) {
            throw new ClassNotFoundException("No resource for " + path);
        }
        return resource;
    }

    @SuppressWarnings("rawtypes")
    private List<Class> collectClasses(String packageName, File directory) throws ClassNotFoundException {
        List<Class> classes = new ArrayList<>();
        if (directory.exists()) {
            String[] files = directory.list();
            for (String file : files) {
                if (file.endsWith(".class")) {
                    // removes the .class extension
                    classes.add(Class.forName(packageName + '.' + file.substring(0, file.length() - 6)));
                }
            }
        } else {
            throw new ClassNotFoundException(packageName + " is not a valid package");
        }
        return classes;
    }

    private void generate(Dialect dialect, String directory) {
        cfg.setProperty("hibernate.dialect", dialect.getDialectClass());
        SchemaExport export = new SchemaExport(cfg);
        export.setDelimiter(";");
        export.setOutputFile(directory + "ddl_" + dialect.name().toLowerCase() + ".sql");
        export.setFormat(true);
        export.execute(true, false, false, false);
    }

    private static enum Dialect {
        ORACLE("org.hibernate.dialect.Oracle10gDialect"), MYSQL("org.hibernate.dialect.MySQLDialect"), HSQL(
                "org.hibernate.dialect.HSQLDialect"), H2("org.hibernate.dialect.H2Dialect");

        private String dialectClass;

        private Dialect(String dialectClass) {
            this.dialectClass = dialectClass;
        }

        public String getDialectClass() {
            return dialectClass;
        }
    }
}

And I get the following error:

Exception in thread "main" java.lang.UnsupportedOperationException: Attempt to use unsupported SchemaExport constructor accepting org.hibernate.cfg.Configuration; one of the forms accepting org.hibernate.boot.spi.MetadataImplementor should be used instead at org.hibernate.tool.hbm2ddl.SchemaExport.(SchemaExport.java:250) at cu.x.util.SchemaGenerator.generate(SchemaGenerator.java:116) at cu.x.util.SchemaGenerator.main(SchemaGenerator.java:32) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

I change my code with the following:

ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(cfg.getProperties()).build();
MetadataImplementor metadataImplementor = (MetadataImplementor)
        new MetadataSources(serviceRegistry).buildMetadata();
SchemaExport export = new SchemaExport(metadataImplementor);

But it generates an empty .sql file. I'm using annotated classes. What could be happening?

like image 351
felix Avatar asked Nov 13 '15 19:11

felix


4 Answers

Apparently the Configuration class can not be used. We must use the MetadataSources class to add the annotated classes.

private void generate(Dialect dialect, String directory, String[] packagesName) throws Exception {

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

    for (String packageName : packagesName) {
        log.info("packageName: " + packageName);
        for (Class clazz : getClasses(packageName)) {
            log.info("Class: " + clazz);
            metadata.addAnnotatedClass(clazz);
        }
    }

    SchemaExport export = new SchemaExport(
            (MetadataImplementor) metadata.buildMetadata()
    );

    export.setDelimiter(";");
    export.setOutputFile(directory + "ddl_" + dialect.name().toLowerCase() + ".sql");
    export.setFormat(true);
    export.execute(true, false, false, false);
}
like image 104
felix Avatar answered Oct 23 '22 00:10

felix


The solution of felix don't work anymore on hibernate 5.2 here is a version which is compatible

private void generate(Class dialect, String directory, String... packagesName) throws Exception {

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

    for (String packageName : packagesName) {
        LOG.info("packageName: " + packageName);
        for (Class clazz : getClasses(packageName)) {
            LOG.info("Class: " + clazz);
            metadata.addAnnotatedClass(clazz);
        }
    }

    MetadataImplementor metadataImplementor = (MetadataImplementor) metadata.buildMetadata();
    SchemaExport export = new SchemaExport();

    export.setDelimiter(";");
    String filename = directory + "ddl_" + dialect.getSimpleName().toLowerCase() + ".sql";
    export.setOutputFile(filename);
    export.setFormat(true);

    //can change the output here
    EnumSet<TargetType> enumSet = EnumSet.of(TargetType.STDOUT);
    export.execute(enumSet, SchemaExport.Action.CREATE, metadataImplementor);
}
like image 31
Bertrand Cedric Avatar answered Oct 23 '22 01:10

Bertrand Cedric


Normally tools that dumps the JPA schema are based on SchemaExport tool, which reads only the static metadata.

There is a Maven/Gradle plugin https://github.com/Devskiller/jpa2ddl which generates the JPA schema. In includes all properties, namings strategies, user types, etc.

You can also use it to generate automated schema migrations for Flyway.

like image 3
Jakub Kubrynski Avatar answered Oct 23 '22 01:10

Jakub Kubrynski


I wrote a class for generating sql script depends on packages with entites.

Used version of hibernate is '5.3.7.Final'

public final class HibernateExporter {

    private static final Logger LOG = LoggerFactory.getLogger(HibernateExporter.class);
    private static final String OUTPUT_FILE = "schema.sql";
    private static final String DIALECT = "org.hibernate.dialect.H2Dialect";

    private List<String> entityPackages;

    private HibernateExporter(List<String> entityPackages) {
        this.entityPackages = entityPackages;
    }

    public static void main(String[] args) {
        final List<String> entityPackages = Collections.singletonList("pakage.with.entites");

        HibernateExporter exporter = new HibernateExporter(entityPackages);
        exporter.export();
    }

    private void export() {
        SchemaExport export = new SchemaExport();
        export.setOutputFile(OUTPUT_FILE);
        export.setFormat(true);
        export.setDelimiter(";");
        EnumSet<TargetType> types = EnumSet.of(TargetType.SCRIPT);
        Metadata metadata = createMetadataSources().buildMetadata();
        export.execute(types, Action.CREATE, metadata);
    }

    private MetadataSources createMetadataSources() {
        MetadataSources metadata = new MetadataSources(
                new StandardServiceRegistryBuilder()
                        .applySetting("hibernate.dialect", DIALECT)
                        .build());

        for (String entityPackage : entityPackages) {
            final Reflections reflections = new Reflections(entityPackage);
            for (Class<?> cl : reflections.getTypesAnnotatedWith(MappedSuperclass.class)) {
                metadata.addAnnotatedClass(cl);
                LOG.info(String.format("Mapped = %s", cl.getName()));
            }
            for (Class<?> cl : reflections.getTypesAnnotatedWith(Entity.class)) {
                metadata.addAnnotatedClass(cl);
                LOG.info(String.format("Mapped = %s", cl.getName()));
            }
        }
        return metadata;
    }
}
like image 2
Vladimir Avatar answered Oct 23 '22 02:10

Vladimir