Hibernate / JPA:在更新DB Schema之前检查生成的SQL(如.NET EF迁移) [英] Hibernate/JPA: Check generated sql before updating DB Schema (like .NET EF migrations)

查看:115
本文介绍了Hibernate / JPA:在更新DB Schema之前检查生成的SQL(如.NET EF迁移)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我想学习Hibernate / JPA,我想知道是否有类似于.NET的实体框架迁移,我可以使用。

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

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



有没有一种更优雅的方法可以做到这一点?我想要吗?



编辑:还有一个icremental模式更新功能吗?例如,如果我重命名模型类的字段,那么Hibernate / JPA将执行以下操作:


  • 如果如果 hbm2ddl.auto = create-drop 那么它将删除表并且
    重新创建它(数据丢失)。
  • hbm2ddl.auto = update 然后它将添加一个
    新的colunmn与新名称。



我想要的是改变现有的表。

解决方案

是的,有一个模式生成器类。 b
$ b

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;

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

公共类SchemaGenerator {

私有配置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);


$ b @SuppressWarnings(rawtypes)
public Sc​​hemaGenerator(String [] packagesName)抛出Exception {
cfg = new Configuration();
cfg.setProperty(hibernate.hbm2ddl.auto,create); (String packageName:packagesName){
for(Class clazz:getClasses(packageName)){
cfg.addAnnotatedClass(clazz);





$ b $ @SuppressWarnings(rawtypes)
private List< Class> getClasses(String packageName)抛出异常{
File directory = null;
尝试{
ClassLoader cld = getClassLoader();
URL resource = getResource(packageName,cld);
directory = new File(resource.getFile());
} catch(NullPointerException ex){
throw new ClassNotFoundException(packageName +(+ directory +)似乎不是一个有效的包);
}
return collectClasses(packageName,directory);


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

$ b $ 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);
}
返回资源;


@SuppressWarnings(rawtypes)
private List< Class> collectClasses(String packageName,File directory)引发ClassNotFoundException {
List< Class> classes = new ArrayList<>();
if(directory.exists()){
String [] files = directory.list();
for(String file:files){
if(file.endsWith(。class)){
//移除.class扩展名
classes.add(Class。 forName(packageName +'。'+ file.substring(0,file.length() - 6)));
}
}
} else {
抛出新的ClassNotFoundException(packageName +不是有效的包);
}
返回类;

$ b private void generate(dialect dialect,String directory){
cfg.setProperty(hibernate.dialect,dialect.getDialectClass());
SchemaExport export =新的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;

私人方言(String dialectClass){
this.dialectClass = dialectClass;
}

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


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

这篇关于Hibernate / JPA:在更新DB Schema之前检查生成的SQL(如.NET EF迁移)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆