Skip to main content

The best way to map a @OneToOne relationship with JPA and Hibernate

One-To-One Shared Primary Key Relationship

One to one relationship refers to the relationship between two entities/tables A and B in which one item/row of A may be linked with only one item/row of B, and vice versa.
In this example, book and book_detail tables have a one-to-one relationship. A book has only one book detail, and a book detail belong to only one book.
book_detail.book_id is a foreign key references to book.idbook_detail also uses its foreign key book_id as primary key so-called shared primary key.

@Id declares the entity identifier.
@Column maps the entity's field with the table's column. If @Column is omitted, the field name of the entity will be used as column name by default.
@OneToOne defines a one-to-one relationship between 2 entities.
@JoinColumn defines foreign key column and indicates the owner of the relationship.
mappedBy indicates the inverse of the relationship.
unique = true enforces the unique constraint, 1 address belongs to only 1 library.
@MapsId defines embedded primary key, book_detail.book_id is embedded from book.id

Domain Model

For the following examples, I’m going to use the following Post and PostDetails classes:
OneToOne
The Post entity is the parent, while the PostDetails is the child association because the Foreign Key is located in the post_details database table.

Typical mapping

Most often, this relationship is mapped as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {
    @Id
    @GeneratedValue
    private Long id;
    @Column(name = "created_on")
    private Date createdOn;
    @Column(name = "created_by")
    private String createdBy;
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id")
    private Post post;
    public PostDetails() {}
    public PostDetails(String createdBy) {
        createdOn = new Date();
        this.createdBy = createdBy;
    }
    //Getters and setters omitted for brevity
}
More, even the Post entity can have a PostDetails mapping as well:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Entity(name = "Post")
@Table(name = "post")
public class Post {
    @Id
    @GeneratedValue
    private Long id;
    private String title;
    @OneToOne(mappedBy = "post", cascade = CascadeType.ALL,
              fetch = FetchType.LAZY, optional = false)
    private PostDetails details;
    //Getters and setters omitted for brevity
    public void setDetails(PostDetails details) {
        if (details == null) {
            if (this.details != null) {
                this.details.setPost(null);
            }
        }
        else {
            details.setPost(this);
        }
        this.details = details;
    }
}
However, this mapping is not the most efficient, as further demonstrated.
The post_details table contains a Primary Key (PK) column (e.g. id) and a Foreign Key (FK) column (e.g. post_id).
one-to-one
However, there can be only one post_details row associated with a post, so it makes more sense to have the post_details PK mirroring the post PK.
one-to-one-shared-pk
This way, the post_details Primary Key is also a Foreign Key, and the two tables are sharing their PKs as well.
PK and FK columns are most often indexed, so sharing the PK can reduce the index footprint by half, which is desirable since you want to store all your indexes into memory to speed up index scanning.
While the unidirectional @OneToOne association can be fetched lazily, the parent-side of a bidirectional @OneToOne association is not. Even when specifying that the association is not optional and we have the FetchType.LAZY, the parent-side association behaves like a FetchType.EAGER relationship. And EAGER fetching is bad.
This can be easily demonstrated by simply fetching the Post entity:
1
Post post = entityManager.find(Post.class, 1L);
Hibernate fetches the child entity as well, so, instead of only one query, Hibernate requires two select statements:
1
2
3
4
5
6
7
8
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_
FROM   post p
WHERE  p.id = 1
SELECT pd.post_id AS post_id3_1_0_, pd.created_by AS created_1_1_0_,
       pd.created_on AS created_2_1_0_
FROM   post_details pd
WHERE  pd.post_id = 1
Even if the FK is NOT NULL and the parent-side is aware about its non-nullability through the optional attribute (e.g. @OneToOne(mappedBy = "post", fetch = FetchType.LAZY, optional = false)), Hibernate still generates a secondary select statement.
For every managed entity, the Persistence Context requires both the entity type and the identifier,
so the child identifier must be known when loading the parent entity, and the only way to find the associated post_details primary key is to execute a secondary query.
Bytecode enhancement is the only viable workaround. However, it only works if the parent side is annotated with @LazyToOne(LazyToOneOption.NO_PROXY) and the child side is not using @MapsId.

The most efficient mapping

The best way to map a @OneToOne relationship is to use @MapsId. This way, you don’t even need a bidirectional association since you can always fetch the PostDetails entity by using the Post entity identifier.
The mapping looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {
    @Id
    private Long id;
    @Column(name = "created_on")
    private Date createdOn;
    @Column(name = "created_by")
    private String createdBy;
    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Post post;
    public PostDetails() {}
    public PostDetails(String createdBy) {
        createdOn = new Date();
        this.createdBy = createdBy;
    }
    //Getters and setters omitted for brevity
}
This way, the id column serves as both Primary Key and FK. You’ll notice that the @Id column no longer uses a @GeneratedValue annotation since the identifier is populated with the identifier of the post association.
The PostDetails entity can be persisted as follows:
1
2
3
4
5
6
doInJPA(entityManager -> {
    Post post = entityManager.find(Post.class, 1L);
    PostDetails details = new PostDetails("John Doe");
    details.setPost(post);
    entityManager.persist(details);
});
And we can even fetch the PostDetails using the Post entity identifier, so there is no need for a bidirectional association:
1
2
3
4
PostDetails details = entityManager.find(
    PostDetails.class,
    post.getId()
);


copied from : https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/


Note : Special Cases and Tips : 

Tip 1 : Suppose We want unidirectional Relation (Like Every Employe has address) then here we have to make 2 beans employee bean and address bean. 
now in employee bean we have to declare ref of address bean class in employee bean class. 
like 
now we have to annotate this address variable as @OneToOne
// public Address address.
// setter and getter 
now employee class holds single ref of address class.
(Keep in mind address class will create its own table so we have to declare @Entity and @Table annotation and we have also declare and id to it by defining a variable like address.id)
Keep in mind while saving your parent data if you want to save child data as well as then you have to use cascade type. if you don't define cascade type then manually we have to save parent entity and child entity seperatly. 
by doing this undirection away we are trying to maintain fk key of address entity in employee entity class. 
If we don't specify fk name then hibernate will pickup automatically as entity class (address) and its pk id like (address(under-score "_")address_pk_id) as column name. If we want to define our custom cloumn name for this column then we have to use @joinColumn(name = "fk column name that you want for this column") annotation.
Now when you fetch parent entity then it will automatically fetch child record as well as. 
Disadvantage. suppose we want fetch the record based on child entity like address id then we cann't

Bidirectional @OnetoOne relationship Tips

suppose we have one to one relation between our entities like employee and address. suppose we are not using cascade type in @OneToOne annotation and while saving parent entity we will get exception from hibernate. so either we have to save both entity separately or we have to use cascade type.
if we have separately then again we have to 2 case 
case : 1) Suppose we saved employee entity first (Parent entity that consist of fk id) and then we saved address entity. 

case : 2) suppose i saved child entity first and then i saved parent entity 

so actually what happend here if we go for 1st case then hibernate will fire 3 sql query 2 for insert and one for update of fk. 

but if we go for first case then it will fire only 2 select query because while inserting parent entity hibernate knows the fk id. thats the difference. 

Note: you can use cascade type but keep in mind if you use cascade type ALL. then we have to use save method. we can also use cascade type = PERSIST but for that we have to use persist method.

Note: If we want to achieve bidirectional relationship then we have to declare parent entity reference in child entity as well as. 

like :
@OneToOne(mappedBy="its ref ownership in parent table like address ref variable name")
// public Employee emp;

if we don't define mappedBy then hibernate will add one more column in address table.

Comments

Popular posts from this blog

Search Engine Optimization (SEO) Tricks

6 Key Search Engine Optimization (SEO) tricksStressed and frustrated with your Website's lower rankings on the Search Engines?The tricks and tactics listed below can tremendously change the entire scenario of your website's ranking and rating on Search Engine Results Pages (SERPs) and can take your website to a whole new level, with which increase in relevant traffic on the website is easily observable.http://www.boolment.com The tricks discussed here needs to be implemented in the most desirable way possible. Paginate Your Content: The content on your website is the main facial asset with which you can either impress your audience if the content is paginated well, or you can confuse the hell out of them if the content is unpaginated. So, make sure to arrange everything in a well-ordered manner. Organize CSS For Spiders: Organizing CSS for spiders is a crucial factor that must be considered in order to let the Search Engines know what you are up to and what exactly are you trying…

Android & iOS Application Development For Dummies

App Development ProcessApp Development Process explained in the most simplified way This tutorial will guide all the beginners how an app is developed from scratch and will help in letting the beginners know how the process works.Phase-1 (Conceptualization): Even a concept or an idea starts with an approach to introduce something after acknowledging the inkling of amiss or what's missing in the existing market. Developing the aftermath ideas involves critical questioning and rational thinking towards the wire-framing stage. Phase-2 (Design): After analyzing the idea thoroughly and looking through all possible dimensions, comes the design part, where a feasible layout of the model is planned to move ahead for the further development processes. Phase-3 (Coding): Development process begins with designing a user-friendly UI. Prototypes of the model are prepared with the combined agreement of developers and designers to increase the efficiency and capitalization. The expedience checking r…

Digitization of Restaurant Chains

How Restaurant Businesses Are Transforming With Mobile Apps The Interaction Between Apps and Customers: Customers interact with mobile phones more often than we can imagine and this interaction can be efficiently utilized when it comes to ordering food, which in turn makes it hassle-free for the customers to order food. Just by looking at the online menu on their mobile phones, customers can effortlessly feed themselves with your service and at the same time, your sales graph can jump vertically upwards. While a customer easily gets all information regarding your restaurant, your restaurant gets famous at the same time. In that sense, mobile apps have restructured the way customers and restaurants interact with each other. Business Profits With Mobile Apps:Mobile apps for restaurants benefits them through multiple dimensions. From push notifications to personalized offers, the customer engagement becomes more thrifty with your catering. To explain it with an example, consider the restau…