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

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…

Amazon EC2 Server Setup & Installing JDK 8 and Tomcat 8, Running on Port 80 & 443 and Redirect Request from port 80 to 8080 and 443 to 8443

Amazon EC2 Server Setup & Installing JDK 8 and Tomcat 8, Running on Port 80 & 443 and Redirect Request from port 80 to 8080 and 443 to 8443.Step 1 : Log in to youraws account by following this link then click on my account and choose option aws management console.
Note: I am assuming you created your account with aws and you are ready with you account if you haven’t done then you can check out on google you will get many and it's a straight forward steps if you have still problem while creating an account you can comment in comment box i will also provide tutorial for that.
Once you logged in aws management console you are able to see window like this one


Note : Before we go ahead we have to select proper reason from right and side.I choose ohio region for this example.
Step 2 : Now you have to choose EC2 Server from Services tab on left side top corner then choose EC2 Services from “Compute option”

You will get window like this one and right now i have one instance is running for…

Digital Marketing

What actually is Digital Marketing?
This post will help you understand the insights of Digital MarketingWhat is Digital Marketing?Digital Marketing is an integral part of the overall marketing strategies of any business. It basically covers the advertisement of products/services/business/brand via digital channels. The digital channels could be of any type like websites, search engines, social media, emails, SMS, and MMS. In case if you're using all these digital channels for the marketing, make sure to have all the statistics & workflow of your campaigns via marketing automation. What are the types of digital marketing?Well, there are 6 core digital marketing types: Search Engine Optimization (SEO): Search Engine Optimization is nothing but a long-term process of improving your website rankings on search engine results pages (SERPs), which in turn has a wide range of tactics & strategies to implement. Although there is no specific method or a  specific formula with which an …