In this article, we are going to understand about one-to-one mapping in JPA with hibernate. In a relational database, different tables are connected using common columns like foreign keys or we can create separate tables like junction table/linking table (used mainly in case of many-to-many relationships) to connect two tables, It is one of the advantages of the relational database. In the case of an ORM framework like JPA, we can also create relationships/mapping between entities at the application level and then it will be stored as tables with relationships in DB. There are various relationships we can create between entities as provided by JPA like OneToOne, OneToMany, ManyToOne, and ManyToMany. here we will discuss one-to-one mapping.
OneToOne: it represents the relationship between two entities when one instance of an entity is associated with one and only one instance of another entity.
Example Employee and Department, one Employee can be part of only one Department.
- In OneToOne mapping, there are two ways we can create the mapping,
- Unidirectional
- Bidirectional
Unidirectional OneToOne Mapping: Only one side of the association/mapping/relationship has a reference to the other side.
Example: let's say we have an Employee and Department entity, and as one employee can be part of any one Department, so we will create OneToOne mapping between them.
But let's say we just want the employee just have the reference of the Department but the Department should not maintain the reference back to the Employee. this is what unidirectional OneToOne mapping looks like.
Here we have an Entity Employee and an Entity department, and if we want that Employee related to the department using OneToOne mapping, then we can use a field of type Department annotated with @OneToOne in Employee Entity (that will manage foreign key as well) and nothing requires in department entity. By default, the Department fetch type will be EAGER, in the Employee entity.
Bidirectional OneToOne Mapping: Both side of the association/mapping/relationship has reference to each other.
Examples: let's say we have two entities Employee and Passport, we want the Employee entity must have a Passport reference and the Passport entity must have an Employee reference. this is what bidirectional OneToOne mapping looks like.
Way 1 to handle bidirectional one-to-one mapping,
here, we created a field of Passport type in the Employee entity annotated with @OneToOne and a field of employee type in the Passport entity annotated with @OneToOne. this is the simplest way. but unnecessarily both tables need to maintain foreign keys of each other.
Way 2 to handle bidirectional one-to-one mapping,
Firstly, a foreign key is created in both tables but let's say we just want to create a foreign key in just one table. Then, in that case, we can create a mapping like above. This way foreign keys will be created in the tb_emp table only.
We used OneToOne annotations in previous examples, Now let's understand it in detail.
@OneToOne annotation we used to establish OneToOne mapping between two entities.
We have Different elements in the @OneToOne annotation, below are their details.
- targetEntity: used to specify the entity class that is the target of mapping/relationship/association.
- cascade: it ensures that changes made to the owning entity are propagated to the reference/target entity.
- Different cascade type: ALL, PERSIST, MERGE, REMOVE, REFRESH
- Cascading operations ensure that related entities are automatically merged, persisted, removed, or refreshed when corresponding operations are performed on the owning entity.
- fetch: used to specify Fetch strategies, different fetch strategies, FetchType.EAGER and FetchType.LAZY
- optional: used to specify whether the association is optional or mandatory.
- false: associated entity must always be present (foreign key column cannot be null).
- true: association is optional, (foreign key column can be null).
- mappedBy: it is mostly used in case of bidirectional mapping, it is used to specify the field of the target entity that owns the relationship (or acts as a foreign key)
- For example, In the case of one-to-one mapping, if we used mappedBy with the Passport entity to create an association with the Employee entity, then the Employee entity will manage the foreign key and will call as owning entity. whereas the Passport entity is not managing the foreign key so it's called a referenced/inverse/target entity.
- The owning entity is responsible for managing the relationship and the DB updates related to the relationship.
- orphanRemoval: it is used to specify whether orphan entities (entities that are no longer referenced by the owning entity), should be automatically removed. It ensures referential integrity.
- true: we can set it as true, which means JPA automatically deletes the associated entity from the database when its associated owning entity is removed.
- false: we can set it as false, which means JPA will not automatically delete the associated entity from the database when its associated owning entity is removed.
There is one more annotation we used, @JoinColumn
- It is used to define a foreign key column in the database table of the owning entity that references the primary key column of the referenced entity.
- Customize foreign key behavior, like name and if it's optional or not.
Fetching in one-to-one mapping
Let's understand fetch in the case of OneToOne mapping in detail. We have two Fetch Types
- EAGER: It loads all associated entities while loading the owning entity.
- LAZY: It loads associated entities when they require.
- By default, in bidirectional/unidirectional OneToOne mapping, the fetch type is EAGER.
- In the case of unidirectional OneToOne mappings, if you use fetch type LAZY instead of EAGER it will work.
In case you don't specify any fetch type then it will consider it as EAGER in one-to-one mapping. In this case, you can see even if we just fetch the Employee object (using entityManager.find(Employee.class,1)) from DB it will by default fetch the Department and passport as well.
hibernate will generate the query below to fetch data from the DB
let's make it lazy and let's see how it behaves.
hibernate will generate the below query to fetch data from the DB
- In the case of bidirectional mapping if we use LAZY at the owning side (Entity that holds foreign key) then it will work, but LAZY at the Reference/Target/Inverse side will not work. The reason is Hibernate needs to know whether it needs to initialize the associated entity as null or proxy, as there is no foreign key present in the Reference entity, due to this Hibernate queries in the associated side and that's why even if specify LAZY, the query executes and it behaves as EAGER.
But let's see the other side, that is in the Passport entity, Employee association is present if we put lazy on top of it let's see how it behaves.
Hibernate will generate the below query, and here we can observe clearly that LAZY not working and is fetching Employee as well.
- Let's understand this in more detail, why it is happening
- In the case of unidirectional OneToOne mappings, the column representing association, namely department_id in table Employee. From reading the row from the table Employee, Hibernate ORM can determine if the value of the department_id column is null or associated. this information enabled the entity to decide if the association mapping value department is null or proxy during creation, allowing for the possibility of lazy fetching.
- In the case of bidirectional OneToOne mapping, The table Passport does not represent the Employee association column directly. Instead, the association is mapped through the passport_id column in the Employee table. whenever an entity is created from a row in the table Passport, Hibernate ORM checks the Employee table to see if there is an association exists. depending on the result, it sets the value of Passport#employee to the associated employee or null. this means that lazy fetching is not possible as without checking the Employee table, it is impossible to determine if the association value is null or not.
- So, it means in the case of one-to-one, the entity which owns the foreign key of its associated entities, can use lazy loading with the associated entity and it will work. But for the entities that do not own the foreign key, if it uses lazy loading with the associated entities it will not work and act as eagerly loading.
Cascading in one-to-one mapping
Now let's understand about Cascading,
Cascading: its ability to propagate an operation from a parent entity to its child entities. Useful for managing the state of related entities. For if you delete a person and you want its associated address also delete as well, in such case we can use cascade.
- It's best practice to perform Cascading from parent to child instead of child to parent.
- Cascade Type, we have different cascade types as defined in javax.persistence.CascadeType enum.
- ALL, all operations like PERSIST, MERGE, REMOVE, REFRESH and DETACH will cascade from the Parent entity to the Child entity.
- PERSIST, If the parent entity persists, then all of its child entity also persists.
- MERGE, If the parent entity merged, then all of its child entities also merged.
- REMOVE, if the parent entity is removed, then all of its child entity is also removed.
- REFRESH, if the parent entity is refreshed, then all its related entities will also be refreshed.
- DETACH, if the parent entity is detached, then all its related entities will also detached.
- PERSIST, if we don't use then we may face errors like "Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing: com.pg.entity.Employee.department -> com.pg.entity.Department"
- MERGE cascading, if we don't mention it then only the entity we are merging, will only merge not its associated entities..
- REMOVE, if we don't mention it then only the entity we are removing, will only removed not its associated entities.
- DETACH, if we don't mention it then only the entity we are detaching, will only detach not its associated entities.

Comments
Post a Comment