views:

235

answers:

1

I have a one-to-one relationship using PrimaryKeyJoinColumn annotated on the parent side. And now I want to save the child entity by itself.

For example, I have Employee and EmpInfo as the child entity, I need to save EmpInfo (of course after setting the id property of the parent to it). However, when such an arrangement is used, I get an exception listed below...

org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist

Any ideas why hibernate does not allow this? To be more clear, the code I have is below...

ParentEntity:

public class Employee {
    private Long id;
    private String name;
    private EmployeeInfo info;
    private Integer enumId;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    public Long getId() {
        return id;
    }

    @Column(name="EMP_NAME")
    public String getName() {
        return name;
    }

    @PrimaryKeyJoinColumn
    @OneToOne(cascade = CascadeType.REMOVE)
    public EmployeeInfo getInfo() {
        return info;
    }
  }

ChildEntity:

@Table(name="EMP_INFO")
@Entity
public class EmployeeInfo {
    private Long id;
    private String email;

    @Column(name="EMPLOYEE_EMAIL")
    public String getEmail() {
        return email;
    }

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name = "emp_id", nullable = false)
    public Long getId() {
        return id;
    }
}

The way I try to save it is...

Employee emp = new Employee();
emp.setEnumId(SimpleEnum.COMPLETE);
emp.setName("Shreyas");
EmployeeInfo info = new EmployeeInfo();
info.setEmail("Sh@gmail");
concreteDAO.save(emp);   // This uses the JPATemplate provided by Spring JpaDaoSupport


info.setId(emp.getId());
concreteDAO.saveEmpInfo(info);

Any pointers would be much appreciated, as to how can I try to save the child entity?

+2  A: 

The problem here is that the @Id of EmployeeInfo is declared as being auto-generated and you're thus not supposed to set it manually (Hibernate looks at the Entity passed to persist and assumes it is already in the database because the @Id field is populated).

In other words, remove the @GeneratedValue on EmployeeInfo if you want to set the PK manually.

Note that Hibernate provides support for OneToOne association using a shared primary key in JPA 1.0 through a custom extension. See:

In JPA 2.0, derived identifiers are well supported and you can annotate OneToOne and ManyToOne associations with @Id. See:

Pascal Thivent
Great thanks a lot for the eye-opener, I did the same last night (before I saw this post) and it resolved the problem.However, am unsure, whether this is a good practice (because it can lead to data corruption on account of the service user setting the incorrect id into EmployeeInfo) or shall I ask the service user to pass on the whole object graph (the parent as well as the child) to save...
PaiS