简体   繁体   中英

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

So i am trying to learn Hibernate/JPA and i was wondering if there is something similar to .NET's Entity Framework migrations that i can use.

I like Code First (Class -> Schema) approach, but the auto generated sql queries may do strange (and dangerous) things to a database. I want to verify the generated sql query and then decide if i want to update the database schema.

I have enabled the show_sql property. I run the project in debug mode in order to check the generated query and then stop / continue the execution.

Is there a more elegant (proper?) way to do what i want?

Edit : also is there an icremental schema update feature? For instance if i rename a field of my Model's Class, then Hibernate/JPA does the following thing:

  • If hbm2ddl.auto=create-drop then it will drop the table and recreate it (data loss).
  • If hbm2ddl.auto=update then it will add a new colunmn with the new name.

What i want is to alter the existing table.

Yes, there is a schema generator class.

org.hibernate.tool.hbm2ddl.SchemaExport

Here's a sample code on how I use it (note that this was very highly inspired from a post here)

    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;
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM