无法删除或更新父行:外键约束在Hibernate create-drop上失败 [英] Cannot delete or update a parent row: a foreign key constraint fails on Hibernate create-drop

查看:100
本文介绍了无法删除或更新父行:外键约束在Hibernate create-drop上失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望我的spring boot应用程序在每次运行我的应用程序时重新创建表并填充其内容.我正在使用带有create-drop选项的Hibernate.我可以创建它并成功插入内容,但是删除时会出现问题.我收到以下错误:

I want my spring boot app to re-create the tables and fill up its contents each time I run my app. I am using Hibernate with create-drop option. I am able to create it and insert the content successfully, but the problem occurs when dropping. I get the following error:

2015-11-21 14:17:42.694 ERROR 7028 --- [ost-startStop-1] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000389: Unsuccessful: drop table if exists gender
2015-11-21 14:17:42.694 ERROR 7028 --- [ost-startStop-1] org.hibernate.tool.hbm2ddl.SchemaExport  : Cannot delete or update a parent row: a foreign key constraint fails
2015-11-21 14:17:42.757 ERROR 7028 --- [ost-startStop-1] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000389: Unsuccessful: drop table if exists profile
2015-11-21 14:17:42.757 ERROR 7028 --- [ost-startStop-1] org.hibernate.tool.hbm2ddl.SchemaExport  : Cannot delete or update a parent row: a foreign key constraint fails

我知道发生这种情况是因为我在这两个表(genderprofile)之间有关系.我应该怎么做才能使用create-drop成功删除整个表?

I know that happened because I have a relation between those two table (gender and profile). What should I do to drop my entire tables successfully using create-drop?

这是我的Gender实体:

@Entity
public class Gender {

    @Id
    @Column(name = "gender_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "gender_name")
    private String name;

}

这是我的Profile实体:

@Entity
public class Profile {

    @Id
    @Column(name = "profile_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @ManyToOne(optional = false)
    @JoinColumn(name = "gender_id", referencedColumnName = "gender_id")
    private Gender gender;
}

推荐答案

我解决了它,但是设置不同.我仅在验证模式下使用休眠模式,然后运行脚本以手动更新数据库.脚本由休眠org.hibernate.tool.hbm2ddl.SchemaExport生成.然后,我将生成的文件放在开头,并添加set foreign_key_checks = 0;并设置foreign_key_checks = 1;在最后.然后我注释掉与模式alter table *. drop foreign key *.;

I solved it, but my setup is different. I use hibernate only in validation mode and run the scripts to update the database by hand. The scripts are generated by hibernate org.hibernate.tool.hbm2ddl.SchemaExport. Then I take the generated file, added set foreign_key_checks = 0; in the beginning and set foreign_key_checks = 1; at the end. Then I commented out (add -- at the beginning) every line that match the pattern alter table *. drop foreign key *.;

这是我基于SchemaExport的旧模式生成器:

This is my old Schema Generator, based on SchemaExport:

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

import javax.persistence.Entity;

import org.apache.commons.io.FileUtils;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.reflections.Reflections;

public class SchemaGenerator {

    /** The configuration. */
    private final Configuration cfg;


    /**
     * @param packageNames the package namesused
     */
    public SchemaGenerator(final List<String> packageNames) throws Exception {


        if (beanValidationInClasspath()) {
            this.cfg = new ConfigurationWithBeanValidation();
        } else {
            this.cfg = new Configuration();
        }

        this.cfg.setProperty(AvailableSettings.HBM2DDL_AUTO, "create");
        this.cfg.setProperty("javax.persistence.validation.mode", "ddl");

        List<Class<?>> classes = getClasses(packageNames);
        Collections.sort(classes, ClassComparator.INSTANCE);
        for (Class<?> clazz : classes) {
            this.cfg.addAnnotatedClass(clazz);
        }

    }

    /**
     * Method that actually creates the file.
     */
    public void generate(final String fileName) {
        this.cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");

        SchemaExport export = new SchemaExport(this.cfg);
        export.setDelimiter(";");
        export.setOutputFile(fileName);
        export.execute(true, false, false, false);

        try {
            addAdditinalStatements(fileName);
        } catch (IOException e) {
            throw new RuntimeException("error will extending dll with addtional statement", e);
        }

        try {
            addCommentDropConstraintStatements(fileName);
        } catch (IOException e) {
            throw new RuntimeException("error will extending dll by escaping drop foraign key relation ships", e);
        }
    }


    private void addAdditinalStatements(final String fileName) throws IOException {
        File outputFile = new File(fileName);
        String original = FileUtils.readFileToString(outputFile, "utf-8");
        String extended = "set foreign_key_checks = 1;\n" 
                + original 
                + "set foreign_key_checks = 0;\n";
        FileUtils.writeStringToFile(outputFile, extended);    }


    void addCommentDropConstraintStatements(final String fileName) throws IOException {
        File outputFile = new File(fileName);
        List<String> original = FileUtils.readLines(outputFile, "utf-8");
        String withComment = addCommentDropConstraintStatements(original);
        FileUtils.writeStringToFile(outputFile, withComment);
    }

    private Pattern dropKeyStatementPattern = Pattern.compile("alter table .* drop foreign key [^;]*;");

    String addCommentDropConstraintStatements(final List<String> original) {
        StringBuilder shirnked = new StringBuilder();

        for (int i = 0; i < original.size(); i++) {
            if ((i + 2) < original.size()) {
                String combined3Lines = original.get(i).trim() + " " + original.get(i + 1).trim() + " "
                        + original.get(i + 2).trim();

                if (dropKeyStatementPattern.matcher(combined3Lines).matches()) {
                    shirnked.append("-- " + combined3Lines + "\n");
                    i += 2; //skip the next two lines
                } else {
                    shirnked.append(original.get(i) + "\n");
                }
            } else {
                shirnked.append(original.get(i) + "\n");
            }

        }
        return shirnked.toString();
    }

    /**
     * Utility method used to fetch Class list based on a package name.
     *
     * @param packageNames (should be the package containing your annotated beans.
     *
     * @return the classes
     *
     * @throws Exception the exception
     */
    private List<Class<?>> getClasses(final List<String> packageNames) throws Exception {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (String packageName : packageNames) {
            System.out.println("scan:" + packageName);

            Reflections reflections = new Reflections(packageName);
            classes.addAll(reflections.getTypesAnnotatedWith(Entity.class));

        }
        return classes;
    }

    /**
     * This filter accepts only java class files.
     */
    public static class ClassFileFilter implements FileFilter {

        /**
         * The holy instance of class file filter.
         */
        public static final ClassFileFilter INSTANCE = new ClassFileFilter();

        @Override
        public boolean accept(final File pathname) {
            return pathname.isFile() && pathname.getName().endsWith(".class");
        }
    };

    /**
     * This filter accepts only java class files.
     */
    public static class PackageDirectoryFilter implements FileFilter {

        /**
         * The holy instance of normal directory filter.
         */
        public static final PackageDirectoryFilter INSTANCE = new PackageDirectoryFilter();

        @Override
        public boolean accept(final File pathname) {
            return pathname.isDirectory() && !pathname.isHidden() && !pathname.getName().equalsIgnoreCase(".svn")
                    && !pathname.getName().contains(".");
        }
    };

    /**
     * Check if Bean validation is in classpath.
     *
     * @return true, if NotNull class is found.
     */
    private boolean beanValidationInClasspath() {
        try {
            Class.forName("javax.validation.constraints.NotNull");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

和错误修正类ConfigurationWithBeanValidation

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Properties;

import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.MySQLInnoDBDialect;

/**
 * Problem: Hibernate 4.x Schema Export tool does not pay attention to
 * jsr303 annotations for ddl generation.
 * 
 * This class fixes that problem. (use it instead of {@link Configuration}).
 * 
 * This integration is usually performed by BeanValidationIntegrator.
 * Unfortunately, that integration will only be activated upon
 * initialization of the ServiceRegistry, which initializes
 * DatasourceConnectionProviderImpl, which looks up the datasource,
 * which requires a JNDI context ...
 * We therefore reimplement the relevant parts of BeanValidatorIntegrator.
 * Since that must occur after secondPassCompile(), which is invoked by
 * Configuration.generateSchemaCreationScript, which is invoked by
 * SchemaExport, some fancy subclassing is needed to invoke the integration
 * at the right time.
 * 
 * https://forum.hibernate.org/viewtopic.php?f=1&t=1014535
 */
public class ConfigurationWithBeanValidation extends Configuration {

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = -6277290406810542021L;

    @Override
    protected void secondPassCompile() throws MappingException {
        super.secondPassCompile();

        try {
            // thank you, hibernate folks, for making this useful class package private ...                
            Method applyDDL = Class.forName("org.hibernate.cfg.beanvalidation.TypeSafeActivator").getMethod("applyDDL",
                    Collection.class,
                    Properties.class,
                    MySQLInnoDBDialect.class);
            applyDDL.setAccessible(true);
            applyDDL.invoke(null, classes.values(), getProperties(), MySQLInnoDBDialect.class);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }

    }
}

用法:

new SchemaGenerator(Arrays.asList("myPackage.com")).generate("ddl.sql");

org.reflections.Reflections来自https://code.google.com/p/reflections/库(同时移至https://github.com/ronmamo/reflections)

这篇关于无法删除或更新父行:外键约束在Hibernate create-drop上失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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