无法删除或更新父行:Hibernate create-drop上的外键约束失败

时间:2015-11-21 06:26:38

标签: java mysql spring hibernate spring-boot

我希望我的春季启动应用程序重新创建表格,并在每次运行我的应用程序时填写其内容。我正在使用带有create-drop选项的Hibernate。我能够创建它并成功插入内容,但丢弃时会出现问题。我收到以下错误:

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成功删除整个表?

这是我的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;
}

2 个答案:

答案 0 :(得分:1)

我解决了,但我的设置不同。我只在验证模式下使用hibernate并运行脚本来手动更新数据库。这些脚本由hibernate org.hibernate.tool.hbm2ddl.SchemaExport生成。然后我获取生成的文件,在开头添加set foreign_key_checks = 0;并设置foreign_key_checks = 1;在末尾。然后我注释掉了(在开头添加- - 每一行都匹配模式alter table *. drop foreign key *.;

这是我的旧架构生成器,基于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

答案 1 :(得分:-2)

这是最简单的方法,但它不是一个很好的实践

SET FOREIGN_KEY_CHECKS=0
DROP TABLE IF EXISTS  YourTableName
SET FOREIGN_KEY_CHECKS=1

这是你需要发送的MySql命令(我不知道你如何向MySql发送命令)