Sunday, May 4, 2014

@OneToOne with shared primary key, revisited :)

Long time ago, I wrote a post @OneToOne with shared primary key. Today I would like to return to this problem, with solution based on @MapsId annotation introduced in JPA 2.0

Again we have two entities: Primus and Secundus. Both entities have primary key using Long Java type. They are related 1-1, and Secundus should use the same primary key as Primus.
3 Years after my initial post they will look slightly different ;)
@Entity
@Table(name = "PRIMUS")
public class Primus {

    public static Primus newInstance() {
        Primus primus = new Primus();
        primus.secundus = new Secundus(primus);
        return primus;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "primus")
    private Secundus secundus;

    Primus() {
        super();
    }
    ...

}
Not much changed here, ;). Really important changes were made on the Secundus:
@Entity
@Table(name = "SECUNDUS")
public class Secundus {

    @Id
    private Long id;

    @JoinColumn(name = "ID")
    @OneToOne
    @MapsId
    private Primus primus;

    Secundus() {
        super();
    }

    public Secundus(Primus primus) {
        this();
        this.primus = primus;
    }
As you see, the @PrimaryKeyJoinColumn annotation is replaced with two annotations: @MapsId, which defines that Secundus identifier will be determined by Primus identifier, and @JoinColumn, specifying which column in SECUNDUS table will be used for joining. 

Nothing more is needed :) - JPA Provider should automatically ask Primus for its identifier, when persisting Secundus, as long as you take care of correct entities correlation, like in newInstance method of Primus.

Long live JPA 2.0+ ;) :) 

6 comments:

  1. Thanks for you post, it's extremely helpful. You mention towards the end in the second last paragraph that '@JoinColumn, specifying which column in SECUNDUS table will be used for joining.'. Should this not be '@JoinColumn, specifying which column in PRIMUS table will be used for joining.'?

    I thought the JoinColumn annotation takes the column name from the entity that you can joining to, in this case the Primus entity.

    ReplyDelete
  2. I was banging my head on the keyboard, trying to solve this exact problem, until I found this post.
    Thank You very, very much.

    ReplyDelete
  3. Thank you...fixed my issue after 4 hours of trial and error.

    ReplyDelete
  4. Thank you...fixed my issue after 4 hours of trial and error.

    ReplyDelete
  5. Thanks for helping me with the fantastic solution. I am getting a org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.example.model.ApplicantAddressDetails.index]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.example.model.ApplicantAddressDetails.index]
    How do i resolve this ?? I have exactly as you have explained in the blog.

    ReplyDelete
  6. Thank you very much for this simple and great solution!

    ReplyDelete