简体   繁体   中英

Hibernate cascade saving error (Cannot add or update a child row: a foreign key constraint fails)

I'm new to hibernate, after following some tutorials, i tried to make my self example of storing persisting data with hibernate. Using: MySQL 5.7 and Hibernate 5.0.3 with Eclipse IDE.

I looked for similar topics but no solution running.

My Java Classes are:

Dossier.java (Folder containing multiple files)

package com.testhibernate;
import java.util.ArrayList;
import java.util.List;

public class Dossier {

    private Long id;
    private String name;
    private List<Fichier> files;

    public Dossier()
    {
        setName("Folder");
        files = new ArrayList<Fichier>();
    }

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public List<Fichier> getFiles()
    {
        return files;
    }

    public void addFile(Fichier a_file)
    {
        a_file.setFolder(this);
        files.add(a_file);
    }
}

Fichier.java (File contained in a Folder) package com.testhibernate;

public class Fichier {
    private Long id;
    private String name;
    private Dossier folder;

    public Fichier()
    {
        setName("Fichier");
    }

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public void setFolder(Dossier a_folder) {
        folder = a_folder;
    }

    public Dossier getFolder() {
        return (folder);
    }
}

I also created a class to handle save requests

ManageDB.java (Contains starting main function)

package com.testhibernate.dbaccess;

import java.util.List;
import java.util.Iterator;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

import com.testhibernate.Dossier;
import com.testhibernate.Fichier;


public class ManageDB {

    private static SessionFactory factory;
    private static boolean configured = false;

    private static void log(String a_str)
    {
        System.out.println("[[MANAGEDB@: " + a_str + "]");
    }

    /**
     * @name setup
     * @brief configure the factory session
     */
    public static void setup()
    {
        log("Setup");
        ServiceRegistry serviceRegistry;
        if (!configured)
        {
            try{
                Configuration configuration = new Configuration();
                configuration.configure();
                configuration.addClass(Dossier.class);
                configuration.addClass(Fichier.class);

                serviceRegistry = new StandardServiceRegistryBuilder().applySettings(
                        configuration.getProperties()).build();
                factory = configuration.buildSessionFactory(serviceRegistry);
                 configured = true;
             }catch (Throwable ex) { 
                System.err.println("Failed to create sessionFactory object." + ex);
                throw new ExceptionInInitializerError(ex); 
             }
        }
    }

    /**
     * @name addFolder.
     * @param a_folder the folder to save
     * @return ID of the added call.
     */
    public static Long addFolder(Dossier a_folder)
    {
        log("addFolder");
        Session session = factory.openSession();
        Transaction tx = null;
        Long folderID = null;
        try {
            tx = session.beginTransaction();
            folderID = (Long) session.save(a_folder);
            tx.commit();
        }catch(HibernateException e)
        {
            if (tx != null)
                tx.rollback();
            log("Error");
            e.printStackTrace();
        }finally
        {
            session.close();
        }

        return(folderID);
    }

    public static Long addFile(Long folderID, Fichier a_file)
    {
        log("addFile");
        Session session = factory.openSession();
        //Load Dossier persistent object
        Dossier l_folder = (Dossier) session.load(Dossier.class, folderID);
        log("Folder@Name: " + l_folder.getName());
        log("Folder@FilesCount: " + l_folder.getFiles().size());
        //add the file
        l_folder.addFile(a_file);
        log("Folder@FilesCount: " + l_folder.getFiles().size());
        session.flush();
        return(a_file.getId());
    }

    public static void main(String arg[])
    {
        //Instantiate Dossier (a folder)
        Dossier folder = new Dossier();
        //Instantiate 4 Fichier objects (4 files)
        Fichier file1, file2, file3, file4;
        file1 = new Fichier();
        file2 = new Fichier();
        file3 = new Fichier();
        file4 = new Fichier();
        file1.setName("File1");
        file2.setName("File2");
        file3.setName("File3");
        file4.setName("File4");

        //Add files to the folder
        folder.addFile(file1);
        folder.addFile(file2);
        folder.addFile(file3);

        //Configure session factory
        ManageDB.setup();
        //Add folder to database
        ManageDB.addFolder(folder);
    }
}

Hibernate files (I used hibernate with XML, no annotations).

hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                                         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory name="HibernateTestSessionFactory">
  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  <property name="hibernate.connection.password">root</property>
  <property name="hibernate.connection.url">jdbc:mysql://localhost/test</property>
  <property name="hibernate.connection.username">root</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="hibernate.hbm2ddl.auto">create</property>
  <property name="show_sql">true</property>
  <property name="format_sql">true</property>
  <property name="use_sql_comments">true</property>
 </session-factory>
</hibernate-configuration>

Dossier.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 20 nov. 2015 03:16:28 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.testhibernate.Dossier" table="DOSSIER">
        <id name="id" type="java.lang.Long">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <list name="files" table="FICHIER" access="field" cascade="all">
            <key>
                <column name="ID" />
            </key>
            <list-index></list-index>
            <one-to-many class="com.testhibernate.Fichier" />
        </list>
    </class>
</hibernate-mapping>

Fichier.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 20 nov. 2015 03:16:28 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.testhibernate.Fichier" table="FICHIER">
        <id name="id" type="java.lang.Long">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <many-to-one name="folder" class="com.testhibernate.Dossier" fetch="join">
            <column name="FOLDER" />
        </many-to-one>
    </class>
</hibernate-mapping>

When I execute the program an error occurs:

ERROR Cannot add or update a child row: a foreign key constraint fails ( test . fichier , CONSTRAINT FKa0k27gcqnhentsb26vm3cwki0 FOREIGN KEY ( ID ) REFERENCES dossier ( ID ))

When there is only one element in the ''files'' list, everything works fine.

Database model generated automatically

DOSSIER Table

+---------+-------------------------------------------------------
| Table   | Create Table
+---------+-------------------------------------------------------
| dossier | CREATE TABLE `dossier` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
+---------+-------------------------------------------------------
1 row in set (0.00 sec)

FICHIER Table

-----------------------------------------------------------------------------------------+
| Table   | Create Table

                                                                                         |
+---------+-------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------+
| fichier | CREATE TABLE `fichier` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) DEFAULT NULL,
  `FOLDER` bigint(20) DEFAULT NULL,
  `files_ORDER` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `FKgc8774xohnq2sadsw2bum3kn7` (`FOLDER`),
  CONSTRAINT `FKa0k27gcqnhentsb26vm3cwki0` FOREIGN KEY (`ID`) REFERENCES `dossier` (`ID`),
  CONSTRAINT `FKgc8774xohnq2sadsw2bum3kn7` FOREIGN KEY (`FOLDER`) REFERENCES `dossier` (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
+---------+-------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------+
1 row in set (0.04 sec)

Thanks!

You are adding your Files before your Folder. But the File table has a foreign key pointing toward the Folder : it doesn't exist yet.

Just save your Folder before saving the Files.

This is not a Hibernate problem, this is a Database problem.

I finally figured out my problem.

Error was in Hibernate mapping, here's the correct version of Dossier and Folder mapping.

Dossier mapping file

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 20 nov. 2015 03:16:28 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.testhibernate.Dossier" table="DOSSIER">
        <id name="id" type="java.lang.Long">
            <column name="FOLDER_ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <list name="files" table="FICHIER" access="field">
            <key>
                <column name="FOLDER_ID" />
            </key>
            <list-index></list-index>
            <one-to-many class="com.testhibernate.Fichier" />
        </list>
    </class>
</hibernate-mapping>

Fichier mapping file

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 20 nov. 2015 03:16:28 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.testhibernate.Fichier" table="FICHIER">
        <id name="id" type="java.lang.Long">
            <column name="FILE_ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <many-to-one name="folder" class="com.testhibernate.Dossier" fetch="join">
            <column name="FOLDER_ID" />
        </many-to-one>
    </class>
</hibernate-mapping>

One to many ( Dossier -> Fichier )

The one to many relation specifies a key (FOLDER_ID).


Many to One ( Fichier -> Dossier )

The many to one relation need to know the ID of the other (FOLDER_ID).


The error in the first version of mapping was to name differently the columns ID ( ID in the one-to-many and FOLDER in the many-to-one) .

Reading Hibernate documentation helped me:

Hibernate Documentation goto section 23.3. Customer/Order/Product to see a simple example of how implement a one-to-many relation.

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