我想通过在父级上调用save来将Parent实体和子实体一起保存到MySQL数据库中。父实体和子实体之间存在一对一的映射。父ID是自动生成的,我们需要在子项中使用它作为子项的pk。
我正在使用Spring Data JPA 2.0(JPA提供程序是Hibernate)和Spring MVC框架。 当尝试插入实体时,我收到以下错误。
根本原因
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:207)
这是我的数据库架构:
Parent Table:
CREATE TABLE `parent` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`parent_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`pid`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Child Table:
CREATE TABLE `child` (
`cid` int(11) NOT NULL,
`child_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`cid`),
CONSTRAINT `child_f1` FOREIGN KEY (`cid`) REFERENCES `parent` (`pid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
这是我的Java实体 父实体:
@Entity(name="parent")
@NamedQuery(name="Parent.findAll", query="SELECT p FROM parent p")
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private int pid;
@Column(name="parent_name")
private String parentName;
//bi-directional one-to-one association to Child
@OneToOne(mappedBy="parent",cascade=CascadeType.ALL)
private Child child;
//getter, setters
}
Child Enity:
@Entity(name="child")
@NamedQuery(name="Child.findAll", query="SELECT c FROM child c")
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private int cid;
@Column(name="child_name")
private String childName;
//bi-directional one-to-one association to Parent
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="cid")
@MapsId("cid")
private Parent parent;
//getter, setters
}
这是我的主要方法
AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ParentRepository parentResp = context.getBean(ParentRepository.class);
Parent parent = new Parent();
parent.setParentName("Parent1");
Child child = new Child();
child.setChildName("Child1");
parent.setChild(child);
parentResp.save(parent);
答案 0 :(得分:3)
您应该在两个对象中使用级联。试试这个:
@Entity(name="parent")
public class Parent implements Serializable {
//...
@OneToOne(mappedBy="parent",cascade=CascadeType.ALL)
private Child child;
//...
}
@Entity(name="child")
public class Child implements Serializable {
//...
@OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
@JoinColumn(name="cid", referencedColumnName = "id")
private Parent parent;
//...
}
答案 1 :(得分:2)
例外
not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: com.serro.cbmapi.model.Child.parent
表示parent
对象中的Child
属性为null
。
所以要解决这个问题,你需要添加以下代码:
child.setParent(parent);
同样根据JPA Docs:
指定ManyToOne或OneToOne关系属性 提供EmbeddedId主键(属性)的映射 在EmbeddedId主键中,或者是一个简单的主键 父实体。 value元素指定a中的属性 关系属性对应的复合键。 如果 实体的主键与主键具有相同的Java类型 在关系引用的实体中,value属性是 未指定。
parent
类中的Child
字段应声明为value
的{{1}}属性:
@MapsId