Hibernate - ManyToOne - 保存 - org.hibernate.TransientObjectException:

时间:2009-12-14 12:59:27

标签: hibernate jpa

当我尝试保存顶级实体(使用JPA)时,我是否需要从数据库中新获取ManyToOne映射实体并设置它或者我不能设置Id(manyToOne映射实体并保存顶级实体? 如果没有得到新的实体,它会抛出:org.hibernate.TransientObjectException:

我们正在使用的表结构:

DEPARTMENT(DEPARTMENT_ID BIGINT, NAME VARCHAR(128))

EMPLOYEE(EMPLOYEE_ID BIGINT, NAME VARCHAR(128), DEPARTMENT_ID BIGINT)

Entities:
class Department
{
   @Id
   Long departmentId;
   String name;
   @Version
   Long versionNumber;
}

class Employee
{
   @Id
   Long employeeId;
   String name;
   @ManyToOne
   Department department;
   @Version
   Long versionNumber
}

(两个类都有所有字段的setter和getter方法以及默认构造函数,构造函数将主键作为参数) 现在,如果我想用departmentId(比如100)保存员工,我是否需要先获取部门记录然后在员工中设置?

我不能直接创建Department实例(通过设置主键(departmentId))并在Employee中设置Department实例并保存Employee? 当我这样做时,它会抛出org.hibernate.TransientObjectException。

有关最佳做法的建议吗?

提前谢谢

3 个答案:

答案 0 :(得分:7)

如果您想将Employee实例与`部门实例相关联,可以设置an appropriate cascading behavior并让Hibernate处理整个事情:

class Employee {
  ...
  @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
  Department department;
}

请注意,您 NOT 想要使用CascadeType.ALL,因为删除员工不应删除部门。

如果,OTOH,您想要将Employee实例与现有`部门实例相关联,那么您的最佳方法确实是加载它。您可以使用session.load()方法,不会访问数据库

另一种解决方案是在Employee上使用session.merge()(如上所述级联)将传播到Department。但是,这可能会产生副作用。

答案 1 :(得分:1)

如果你想保存父节点而不保存每个子节点,你需要映射它(我不记得确切的语法)

@ManyToOne(CascadeType = Cascade.All)
Department department;
编辑:我错误地认为它是父母对孩子而不是孩子对父母。按照ChssPly76的例子。

答案 2 :(得分:1)

谢谢ChssPly76和Wysawyg。

其中一个解决方案可能是: 我们将更新员工POJO,如下所示

ManyToOne(fetch=FetchType.EAGER)
@**JoinColumn**(name = "DEPARTMENT_ID", referencedColumnName = "DEPARTMENT_ID", **insertable=false, updatable=false**)
private Department department;

@Column(name = "department_id")
private Long departmentId;

(department和departmentId都有setter和getter方法)

现在在这里(请注意,department和departmentId都映射到同一列(DEPARTMENT_ID) 我们仅使用部门来获取部门详细信息 和departmentId插入或更新员工

但我担心这是否是一种更好的方法。