【问题标题】:"java.sql.SQLException: Field 'id_parent' doesn't have a default value" with one-to-many relationship mapping具有一对多关系映射的“java.sql.SQLException:字段'id_parent'没有默认值”
【发布时间】:2015-09-13 21:34:06
【问题描述】:

是的,类似标题的问题在 SO 上出现过多次。然而,到目前为止,这些解决方案都没有奏效。我试图重新创建表并更改 /etc/my.cnf 文件。

/etc/my.cnf:

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

以下程序应该创建父对象,将其存储在数据库中,检索它,将子对象附加到它并使用 session.save(parent) 隐式存储父对象及其子对象。崩溃点在源代码 (Main.java) 中显示为注释。

数据库构造脚本:

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

父.java:

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

子.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 日志文件:

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

输出:

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
            (?)

【问题讨论】:

  • 出于好奇,你有没有搞定这个?
  • @PéturValdimarsson 不,我遇到了另一个错误。 “非空属性引用空值或瞬态值”
  • 我决定实现向后导航,即在子类中创建 并在这个类中添加带有 getter 和 setter 的“父”字段。

标签: java mysql sql hibernate


【解决方案1】:

在子表中,您将“id_parent”定义为非空,没有默认值。

`id_parent` INT UNSIGNED NOT NULL,

正如您从最后的休眠日志中注意到的那样,尝试插入只有一列的子表。

Hibernate: 
    into children (attribute3)  values (?)

id_parent 在这种情况下将为空,这是不允许的。 可能的解决方案是删除 NOT NULL 限制,或者在 insertChildren 中正确分配父 id:

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

再一次,因为这是一个双向关系,所以最好把它映射成这样:

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

然后,您在 Child 中定义一个带有适当 setter/getter 的字段 Parent(可能还有一个构造函数,因为它始终是必需的):

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

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

最后回到主线:

Child child = new Child(parent);

您现在可以删除

c.setIdParent(parent.getId());

在我上面提到的insertChild()中,应该都是桃子。

More on Hibernate 3 collections

作为旁注,我建议使用注释,因为更容易获得在同一位置具有字段和映射的 bean 映射的概述 :)

【讨论】:

  • org.hibernate.PropertyValueException: 非空属性引用空值或瞬态值:com.mycompany.dvnfsscce.Child.parent 使用第二种解决方案。
  • 啊忘了在main里面设置父级,你的答案是正确的。
  • Sweet :) 希望你能适应这个。休眠可以很有趣。请记住,这里有一个轻微的范式转变,因为您可以将实体视为数据库的扩展。一旦您更改了一个对象,就认为它在 DB 中发生了更改(嗯,这并不完全正确,但从一个好的心理形象开始)。需要一段时间才能习惯。
【解决方案2】:

Parent 是关联的拥有方(它是 only 方,因此它必须是拥有方)。由于 join 列不是关联所有者实体的一部分,当 Hibernate 看到 Parentchildren 中关联 ParentChild 时,将生成一条更新语句来关联它们收藏。

这就是为什么在没有连接列的值的情况下插入Child(数据库抱怨因为它不可为空)。

您应该使关联成为双向并将many 方声明为关联的所有者(通过将one 方设为inverse)。这样您就可以避免尝试为连接列存储 null,并且您可以摆脱额外的更新语句。

或者,要保持当前映射不变,只需将连接列设为可为空(但您会放松约束并执行不必要的更新语句)。

【讨论】:

    猜你喜欢
    • 2015-07-07
    • 2017-11-15
    • 2014-12-07
    • 2015-07-19
    • 2019-02-09
    • 2020-09-30
    • 1970-01-01
    • 2023-01-03
    • 1970-01-01
    相关资源
    最近更新 更多