views:

47

answers:

2

I am trying to establish a relationship between 2 entities which would be zero-to-one. That is, the Parent can be saved without the associated Child entity and also along with the assoicated Child. Following are the 2 Entity classes...


Employee (Parent)

public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name="EMP_NAME")
    private String name;

    @PrimaryKeyJoinColumn
    @OneToOne(cascade = {CascadeType.ALL})
    private EmployeeInfo info;

    @Column(name="EMP_ENUM")
    private Integer enumId;

EmployeeInfo (Child)

public class EmployeeInfo {
        @Id
        @GeneratedValue(strategy=GenerationType.AUTO)
        private Long id;

        @Column(name="EMPLOYEE_EMAIL")
        private String email;

With such kind of a relation and id column of the only Parent (Employee) table set to AUTO INCREMENT in MySql DB, the problem is that while saving a Parent->Child object graph, I get the following exception

org.springframework.orm.hibernate3.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [insert into EMP_INFO 
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value

I tried setting the Child Table's Id property to AUTO INCREMENT in the DB , and the persistence of such a Parent->Child object graph is successful. However, the problem described here surfaces, because I have a scenario in which I would like to save the parent (Employee) object without the associated EmpInfo object, and hence do NOT want to have AUTO INCREMENT on the Child's id column.


One solution could be not use the PrimaryKeyJoinColumn, but use a particular JoinColumn, but that adds an unnecessary column to my existing Table.


Has anyone come across such a problem? If yes, any pointers would be much helpful.

A: 

I have a scenario in which I would like to save the parent (Employee) object without the associated EmpInfo object.

The optional attribute of a OneToOne is true by default, which is what you want.

However, you are somehow misusing the @PrimaryKeyJoinColumn here (well, it actually depends on what you really want to achieve but your current combination of annotations is not correct).

IF you want to map a OneToOne with a shared primary-key, use the @PrimaryKeyJoinColumn. But in that case, don't use a GeneratedValue on EmployeeInfo and set the id manually or, if you don't want to set it manually, use the Hibernate specific foreign generator that I already mentioned in your previous question. Check also the related question mentioned below.

And IF you do not want to use a shared primary key (like in your current code since you're trying to get the id generated by the database), then do not use the PrimaryKeyJoinColumn.

You have to make a choice.

References

  • JPA 1.0 specification:
    • 9.1.32 PrimaryKeyJoinColumn Annotation

Related question

Pascal Thivent
Hey Pascal, Thanks for your response as always :) As for your suggestion "don't use a GeneratedValue on EmployeeInfo and set it manually" - The problem would be that before saving the parent (Employee) I would not have the id associated with it, and hence I cannot it manually to the child object. As for "use the Hibernate specific foreign generator", I have tried that already but it gives me an NPE (I will paste the stack trace and what I did when I lay my hands on the code again, tomorrow).Btw, what makes you think that I do not want to use a shared primary key? Infact,I want that!
PaiS
Continuing my previous comment.. I want to use shared primary key, what in my code makes you feel that I do not want it? However, with the shared primary key join column, I also want 2 scenarios to be satisfied i) The parent saved alone ii) Parent along with the child saved - in which case the shared primary key would come into picture actually. I hope that makes the question more clear.
PaiS
Also, yes thanks for pointing me my previous post. It was the earlier approach that I was planning (that is to save the child entity separately by setting into it manually, the parent's id). I have now changed the approach, to make it less bug prone, by saving the entire object graph ( from parent -> child, even in the case when I want the child alone to be saved).
PaiS
@PaiS: `Btw, what makes you think that I do not want to use a shared primary key?` Because we saw yesterday that this can't work with `@GeneratedValue` without using the specific `foreign` generator, which is what you are trying to do in this question :)
Pascal Thivent
@PaiS: `However, with the shared primary key join column, I also want 2 scenarios to be satisfied i) The parent saved alone ii) Parent along with the child saved` I don't see the problem, having a `null` child should be possible. Feel free to update the question with accurate mappings and stack trace.
Pascal Thivent
@Pascal: " Then you can't use a shared primary-key (if there is no parent, there is no FK)" --> Just to reframe, (as already mentioned too), I want 2 scenarios to be satisfied 1) The parent saved alone 2) The parent along with the child saved (in which case the primary key Join column would come into picture ofcourse). I do want the child alone to be saved in any case/scenario.
PaiS
@Pascal - thanks for the suggestion about updating the question. I have highlighted a few things and added a line to indicate what I really want to achieve. Also, as an answer to your previous comment, the parent save with the null child is successful always and never carries a problem. The only time the problem occurs is when the scenario mentioned in the URL (of my original post) occurs.
PaiS
@PaiS: Note that I removed my comment about saving a child without the parent :) That was an incorrect interpretation.
Pascal Thivent
Pascal Thivent
@Pascal: Yep, thanks for that. I have changed the implementation to be bidirectional now, giving the @GenericGenerator on the child entity. I will "answer" the question and provide the implementation.
PaiS
A: 

Finally, I got it working thanks to Pascal and some googling from my side. Apparently, I cannot use the Native key generator for such relationships where the parent can exist without the child (optional = true). The thing that worked finally was the following, leaving me the downside of having to deal with Hibernate specific annotation (@GenericGenerator) and also having to make-do with bi-directional relationships instead of the unidirectional that I wanted.

Employee (Parent) class remains unchanged as above. It has AUTO INCREMENT on the Id column.

As for the child class (EmployeeInfo) it changed to the following, and again WITHOUT having the AUTO INCREMENT set on the Id column.

@Table(name="EMP_INFO")
@Entity
public class EmployeeInfo {
    @Id
    @GeneratedValue(generator="foreign")
    @GenericGenerator(name="foreign", strategy = "foreign", parameters={
    @Parameter(name="property", value="verifInfo")}) 
    private Long id;

    @OneToOne(optional=false)
    @JoinColumn (name="id")
    private Employee emp;

    @Column(name="EMPLOYEE_EMAIL")
    private String email;

This helped me achieve what I wanted but on the downside, GenericGenerator is not a JPA annotation, it is a hibernate annotation, and sadly I have to make do with that as of now because JPA does not currently support this(or any similar) annotation.

Anyway, it helps to get through such cases :-)

PaiS