|

Hibernate Tips: How to Share the Primary Key in a One-to-One Association


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please leave a comment below.

Question:

I need to map a one-to-one association in which the primary key value of one entity is also used as the primary key value of the other entity. How can I do that with JPA or Hibernate?

Solution:

You can use JPA’s @MapsId annotation to tell Hibernate that it shall use the foreign key of an associated entity as the primary key.

Let’s take a look at a simple example.

Each Book has a Manuscript, and each Manuscript belongs to 1 Book. The foreign key of the Book is also the primary key of the Manuscript.


Mapping the Book entity

There is nothing special about the mapping of the Book entity. It defines the primary key attribute id and tells Hibernate to use a sequence to generate the primary key values. It also specifies the title attribute as a simple String and a one-to-one association to the Manuscript entity.

@Entity
public class Book {

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	@SequenceGenerator(name = "book_seq")
	private Long id;

	private String title;

	@OneToOne(mappedBy = "book")
	private Manuscript manuscript;
	
	...
}

Mapping the Manuscript entity

The mapping of the Manuscript entity is more complex but also not very complicated. It defines an id attribute as the primary key and a file attribute of type byte[].

The important part is the book attribute which defines the association between the Book and the Manuscript entity. The @OneToOne and the @JoinColumn annotations specify the association. The @MapsId annotation tells Hibernate to use the primary key value of the Book entity as the primary key value of the Manuscript entity.

@Entity
public class Manuscript {

	@Id
	private Long id;
	
	private byte[] file;
	
	@OneToOne
	@JoinColumn(name = "id")
	@MapsId
	private Book book;
	
	...
}

Persisting a new Manuscript

Let’s give the mapping a try and persist a Manuscript for an existing Book entity.

Book b = em.find(Book.class, 1L);
		
Manuscript m = new Manuscript();
m.setBook(b);

b.setManuscript(m);

em.persist(m);

As you can see in the log output, Hibernate writes a new record to the Manuscript table.

06:45:12,563 DEBUG [org.hibernate.SQL] - 
    select
        book0_.id as id1_0_0_,
        book0_.title as title2_0_0_,
        book0_.version as version3_0_0_,
        manuscript1_.id as id1_1_1_,
        manuscript1_.file as file2_1_1_ 
    from
        Book book0_ 
    left outer join
        Manuscript manuscript1_ 
            on book0_.id=manuscript1_.id 
    where
        book0_.id=?
06:45:12,645 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        Manuscript
        (file, id) 
    values
        (?, ?)

Learn more:

JPA and Hibernate also support several other association mappings. I explain them in more details in Ultimate Guide – Association Mappings with JPA and Hibernate.

And if you’re already familiar with the basic association mappings, you might be interested in the following posts:

Hibernate Tips Book

Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching, and statically and dynamically defined queries.

Get it now!

6 Comments

  1. Say both book and manuscriot tables are empty.
    How do you save a new record to the book entity ?

    1. Avatar photo Thorben Janssen says:

      Hi,
      persisting a new Book entity isn’t an issue. The primary key value gets generated during the persist operation. But you can’t persist a Manuscript entity without a Book.
      So, the code to persist a new Book would be:
      Book b = new Book();
      em.persist(b);

      Regards,
      Thorben

  2. Hello, using shared primary key, what if i want to delete a book but still keeping its manuscript ? i can’t find a way to do this by using shared primary key

    1. Avatar photo Thorben Janssen says:

      Hi Hamza,

      you can’t do that if the primary key value of the book is also the primary key value of the manuscript. If you want to do that, you need to remove the @MapsId annotation from the one-to-one association and model a separate primary key attribute.

      Regards,
      Thorben

  3. Avatar photo Oleg Marcenjuk says:

    on book0_.id=manuscript1_.id
    They really have same Ids?

    1. Avatar photo Thorben Janssen says:

      Yes, that’s the most efficient way to model a one-to-one association and it’s preferred by most DBAs.
      As you can see in the article, you can easily map it with Hibernate.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.