Hibernate / JPA:在更新DB Schema之前检查生成的sql(如.NET EF迁移)

时间:2015-06-14 18:53:45

标签: java hibernate jpa

所以我正在尝试学习Hibernate / JPA,我想知道是否有类似于我可以使用的.NET的实体框架migrations

我喜欢Code First(Class - > Schema)方法,但是自动生成的sql查询可能会对数据库执行奇怪的(和危险的)事情。我想验证生成的SQL查询,然后决定是否要更新数据库架构。

我启用了show_sql属性。我以调试模式运行项目以检查生成的查询,然后停止/继续执行。

是否有更优雅(正确?)的方式来做我想要的事情?

修改:还有一个icremental架构更新功能吗?例如,如果我重命名我的Model类的字段,那么Hibernate / JPA会做以下事情:

  • 如果hbm2ddl.auto=create-drop那么它将丢弃表格 重新创建它(数据丢失)。
  • 如果hbm2ddl.auto=update则会添加一个 新名称的新colunmn。

我想要的是改变现有的桌子。

1 个答案:

答案 0 :(得分:0)

是的,有一个架构生成器类。

org.hibernate.tool.hbm2ddl.SchemaExport

以下是我如何使用它的示例代码(请注意,此处的帖子非常受启发)

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