简体   繁体   中英

“java.sql.SQLException: Field 'id_parent' doesn't have a default value” with one-to-many relationship mapping

Yes, question with such title appeared several times on SO. However, none of these solutions worked so far. I tried to recreate table and change /etc/my.cnf file.

/etc/my.cnf:

datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
sql_mode=NO_ENGINE_SUBSTITUTION

The following program should create the parent object, store it in database, retrieve it, attach child to it and store parent with it's child implicitly with session.save(parent). The crashpoint is shown as comment in the source code(Main.java).

Database constructor script:

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

CREATE SCHEMA IF NOT EXISTS `dvsscce` ;
USE `dvsscce` ;

CREATE TABLE IF NOT EXISTS `dvsscce`.`parents` (
  `id_parent` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `attribute1` VARCHAR(45) NULL,
  `attribute2` VARCHAR(45) NULL,
  PRIMARY KEY (`id_parent`))
ENGINE = InnoDB
AUTO_INCREMENT = 1;

CREATE TABLE IF NOT EXISTS `dvsscce`.`children` (
  `id_child` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `id_parent` INT UNSIGNED NOT NULL,
  `attribute3` VARCHAR(45) NULL,
  PRIMARY KEY (`id_child`, `id_parent`),
  INDEX `fk_children_parent_idx` (`id_parent` ASC),
  CONSTRAINT `fk_children_parent`
    FOREIGN KEY (`id_parent`)
    REFERENCES `dvsscce`.`parents` (`id_parent`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB
AUTO_INCREMENT = 1;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

Main.java:

public class Main {

    public static void insertParent(Parent p)
    {
        Session session = SessionContainer.factory.openSession();
        Transaction tx = null;

        try
        {
            tx = session.beginTransaction();
            session.saveOrUpdate(p);
            tx.commit();
        }
        catch(HibernateException he)
        {
            if (tx != null) tx.rollback();
            he.printStackTrace();
        }
        finally
        {
            session.close();
        }
    }

    public static void insertChild(Parent p, Child c)
    {
        Session session = SessionContainer.factory.openSession();
        Transaction tx = null;

        try
        {
            tx = session.beginTransaction();
            Query query = session.createQuery("FROM com.mycompany.dvnfsscce.Parent p WHERE p.id = :id_parent");

            query.setParameter("id_parent", p.getId());

            List results = query.list();
            Parent parent = (Parent)results.get(0);

            List<Child> finalList = parent.getChildren();
            finalList.add(c);
            parent.setChildren(finalList);

            session.update(parent);

            tx.commit(); //crashpoint
        }
        catch(HibernateException he)
        {
            if (tx != null) tx.rollback();
            he.printStackTrace();
        }
        finally
        {
            session.close();
        }
    }

    public static void main(String[] args)
    {
        Parent parent = new Parent();
        parent.setAttribute1("foo");
        parent.setAttribute2("bar");

        insertParent(parent);

        Child child = new Child();
        child.setAttribute3(("Quick brown fox"));

        insertChild(parent,child);
    }
}

Parent.java:

public class Parent {
    private int id;
    private String attribute1;
    private String attribute2;
    private List<Child> children = new ArrayList<>();
    // Getters and setters not pasted...
}

Child.java

public class Child {
    private int id;
    private String attribute3;
    // Getters and setters not pasted...
}

child.hbm.xml

<hibernate-mapping package="com.mycompany.dvnfsscce">
  <class name="Child" table="children">
      <id name="id" type="int">
          <column name="id_child"/>
          <generator class="native"/>
      </id>
      <property name="attribute3" column="attribute3" type="string"/>
  </class>
</hibernate-mapping>

parent.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mycompany.dvnfsscce">
  <class name="Parent" table="parents">
      <id name="id" type="int">
          <column name="id_parent" not-null="true"/>
          <generator class="native"/>
      </id>
      <property name="attribute1" column="attribute1" type="string"/>
      <property name="attribute2" column="attribute2" type="string"/>
      <bag name="children" cascade="all" table="children">
          <key>
              <column name="id_parent" not-null="true" />
          </key>
          <one-to-many class="Child"/>
      </bag>
  </class>
</hibernate-mapping>

log4j log file:

21:59:24,791 TRACE TypeFactory:72 - Scoping types to session factory org.hibernate.internal.SessionFactoryImpl@4b41dd5c
21:59:36,934 TRACE BasicBinder:81 - binding parameter [1] as [VARCHAR] - [foo]
21:59:36,934 TRACE BasicBinder:81 - binding parameter [2] as [VARCHAR] - [bar]
22:00:22,540 TRACE BasicBinder:81 - binding parameter [1] as [INTEGER] - [1]
22:00:22,634 TRACE BasicExtractor:78 - extracted value ([id_paren1_1_] : [INTEGER]) - [1]
22:00:22,649 TRACE BasicExtractor:78 - extracted value ([attribut2_1_] : [VARCHAR]) - [foo]
22:00:22,649 TRACE BasicExtractor:78 - extracted value ([attribut3_1_] : [VARCHAR]) - [bar]
22:00:22,665 TRACE CollectionType:783 - Created collection wrapper: [com.mycompany.dvnfsscce.Parent.children#1]
22:00:27,462 TRACE BasicBinder:81 - binding parameter [1] as [INTEGER] - [1]
22:06:42,595 TRACE BasicBinder:81 - binding parameter [1] as [VARCHAR] - [Quick brown fox]
22:06:42,781  WARN SqlExceptionHelper:144 - SQL Error: 1364, SQLState: HY000
22:06:42,782 ERROR SqlExceptionHelper:146 - Field 'id_parent' doesn't have a default value

Output:

Hibernate: 
    /* insert com.mycompany.dvnfsscce.Parent
        */ insert 
        into
            parents
            (attribute1, attribute2) 
        values
            (?, ?)
Hibernate: 
    /* 
FROM
    com.mycompany.dvnfsscce.Parent p 
WHERE
    p.id = :id_parent */ select
        parent0_.id_parent as id_paren1_1_,
        parent0_.attribute1 as attribut2_1_,
        parent0_.attribute2 as attribut3_1_ 
    from
        parents parent0_ 
    where
        parent0_.id_parent=?
Hibernate: 
    select
        children0_.id_parent as id_paren3_1_0_,
        children0_.id_child as id_child1_0_0_,
        children0_.id_child as id_child1_0_1_,
        children0_.attribute3 as attribut2_0_1_ 
    from
        children children0_ 
    where
        children0_.id_parent=?
Hibernate: 
    /* insert com.mycompany.dvnfsscce.Child
        */ insert 
        into
            children
            (attribute3) 
        values
            (?)

In table children you define "id_parent" as not null, without a default value.

`id_parent` INT UNSIGNED NOT NULL,

As you can note from the hibernate log from the end, there is an attempt to insert into the child table with but one column.

Hibernate: 
    into children (attribute3)  values (?)

id_parent will be null in this case, which is not allowed. Possible solutions is either to drop the NOT NULL restriction, or to properly assign the parent id in insertChildren:

public static void insertChild(Parent p, Child c) {
    Session session = SessionContainer.factory.openSession();
    Transaction tx = null;

    try {
        tx = session.beginTransaction();
        Query query = session.createQuery("FROM com.mycompany.dvnfsscce.Parent p WHERE p.id = :id_parent");

        query.setParameter("id_parent", p.getId());

        List results = query.list();
        Parent parent = (Parent)results.get(0);

        // Here we set the id.
        c.setIdParent(parent.getId());

        List<Child> finalList = parent.getChildren();
        finalList.add(c);
        parent.setChildren(finalList);

        session.update(parent);

        tx.commit(); //crashpoint
    } catch(HibernateException he) {
        if (tx != null) tx.rollback();
        he.printStackTrace();
    } finally {
        session.close();
    }
}

Then again, since this is a bi-directional relationship, it would be nice to map it as such:

<class name="Parent" table="parents">
  <id name="id" type="int">
      <column name="id_parent" not-null="true"/>
      <generator class="native"/>
  </id>
  <property name="attribute1" column="attribute1" type="string"/>
  <property name="attribute2" column="attribute2" type="string"/>

  <set name="children" inverse="true" cascade="all">
    <key column="id_parent" not-null="true" />
    <one-to-many class="Child"/>
  </set>
</class>

<class name="Child" table="children">
  <id name="id" type="int">
      <column name="id_child"/>
      <generator class="native"/>
  </id>
  <property name="attribute3" column="attribute3" type="string"/>

  <many-to-one name="parent" class="Parent" column="id_parent" not-null="true"/>
</class>

You then define a field Parent with appropriate setters/getters (and possibly a constructor taking in parent since it is ALWAYS required) in Child:

public class Child {
    private int id;
    private String attribute3;
    private Parent parent;
    // Getters and setters not pasted...

    public Child(Parent parent) {
        this.setParent(parent);
    }
}

And finally back to the main:

Child child = new Child(parent);

You can now remove the

c.setIdParent(parent.getId());

in insertChild() that I mentioned above and all should be peaches.

More on Hibernate 3 collections

As a sidenote I'd recommend using annotations since it's easier to get an overview of the bean mappings having fields and mappings in the same place :)

Parent is the owning side of the association (it's the only side, so it has to be the owning one). Since the join column is not part of the entity which is the owner of the association, Hibernate will generate an update statement to associate Parent and Child when it sees that they are associated in the Parent 's children collection.

That's why the insertion of the Child happens without the value for the join column (and the database complains because it is not nullable).

You should make the association bidirectional and declare the many side to be the owner of the association (by making the one side inverse ). This way you avoid attempting to store null for the join column and you get rid of the additional update statement.

Or, to keep your current mappings as they are, just make the join column nullable (but you loose the constraint and execute an unnecessary update statement).

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