tags:

views:

824

answers:

1

I have two objects Mat and MatImage, parent and child resp. The primary key of MatImage is the id of Mat and they are join by one to one relationship.

If I understand bidirectional relation correctly, the child object would know about the parent if I do something like matImage.setMat(mat). I would think the primary key would be filled at this point, but it doesn't. I know this because sql throws an exception when it try to insert MatImage with #0 as matId.

Another question is with the n+1 problem. I would like to lazily load child object because not all mat has matimage. I can try changing @OneToOne to @ManyToOne but not sure how that can be done bidirectionally. Any help would be appreciated. Thank you.

Here are the entities:

// Mat

@Entity
@Table(name="mat")
public class Mat implements Serializable {
 @Id
 @GeneratedValue(generator="SeqMat")
 @SequenceGenerator(name="SeqMat", sequenceName="seq_mat_id")
    int id

    @OneToOne(mappedBy="mat", optional=true, fetch=FetchType.LAZY)
 @PrimaryKeyJoinColumn(name="id", referencedColumnName="matId")
 @Cascade([ALL, DELETE_ORPHAN])
 MatImage matImage

    int matTemplateId
    int number
    ... 
}


     // MatImage
    @Entity
    @Table(name="matimage")
    public class MatImage implements Serializable {
        @Id
        int matId

        @OneToOne(optional=true, fetch=FetchType.LAZY)
        @JoinColumn(name="matId", referencedColumnName="id")
        Mat mat

        @Column(name="img_eventid")
        int eventId

        ...
}
+1  A: 

Hi,

I am not sure about you really want. But you can use something like the following one to set up your bidirectional relationship.

@Entity
public class Mat implements Serializable {

    private MutableInt id = new Mutable(-1);

    private MatImage matImage;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO, generator="SeqMat")
    @SequenceGenerator(name="SeqMat", sequenceName="seq_mat_id")
    public Integer getId() {
        return this.id.intValue;
    }

    public void setId(Integer id) {
        this.id.setValue(id)
    }

    public void setIdAsMutableInt(MutableInt id) {
        this.id = id;
    }

    @OneToOne(fetch=FetchType.LAZY)
    @PrimaryKeyJoinColumn
    @Cascade({CascadeType.ALL, CascadeType.DELETE_ORPHAN})
    public MatImage getMatImage() {
        return this.matImage;
    }

    public void setMatImage(MatImage matImage) {
        this.matImage = matImage;

        this.matImage.setIdAsMutableInt(this.id);
    }

}

Now our MatImage

@Entity
public class MatImage implements Serializable {

    private MutableInt id = new MutableInt(-1);

    private Mat mat;

    @Id
    public Integer getId() {
        return this.id.intValue();
    }

    public void setId(Integer id) {
        this.id.setValue(id);
    }

    public void setIdAsMutableInt(MutableInt id) {
        this.id = id;
    }

    @OneToOne(fetch=FetchType.LAZY)
    @PrimaryKeyJoinColumn
    public Mat getMat() {
        return mat;
    }

    public void setMat(Mat mat) {
        this.mat = mat;

        this.mat.setIdAsMutableInt(this.id);
    }

}

A couple of advice

JPA specification does not include a standardized method to deal with the problem of shared primary key generation

It explains why i use a org.apache.commons.lang.mutable.MutableInt as a way both Mat and MatImage share the same object (id) in memory

Now you can use something like

Mat mat = new Mat();
MatImage matImage = new MatImage();

/**
  * Set up both sides
  * 
  * Or use some kind of add convenience method To take car of it
  */
mat.setImage(matImage);
matImage.setMat(matImage);

session.saveOrUpdate(mat);

Both will share the same generated id.

Advice: put annotation configuration in getter method instead of member field. Hibernate makes use of proxy objects. But proxy works fine when using annotation in getter method, not when using in member field.

regards,

Arthur Ronald F D Garcia