Mapping 1-1 relationship in hibernate
Context
- Hibernate uses
@OneToOne
annotation to model 1-1 relationship in RDBMS. When we want to travel through entity relationship, we model these relationships as bidirectional ones. But withone-to-one
relationship,hibernate
will trigger additional query to fetch child entity if we map the relationship like others relationship. - In hibernate context, an object will be identified by
id
andentity type
(aka class) [1].
Solution
When we model relationship as regular bidirectional one-to-one
relationship like two entity below:
public class QuizEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = LAZY, mappedBy = "quiz", optional = false)
private QuizResultEntity result;
// other fields
}
public class QuizResultEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = LAZY, optional = false)
@JoinColumn(name = "quiz_id")
private QuizEntity quiz;
// other fields
}
and fetch an entity of QuizEntity type
, hibernate will trigger an additional query to check whether the associated QuizResultEntity object
existing or not to provide appropriate strategy to initialize relationship. If QuizResultEntity object exists, hibernate will create
a proxy, otherwise this field is set to null
.
So if we add optional = false
properties to @OneToOne
annotation, will hibernate trigger additional query?
Unfortunately, based on [1], hibernate will still trigger additional query because the identifier of QuizResultEntity is unknown.
Query log:
select q1_0.id from quiz q1_0 where q1_0.id=?
select q1_0.id,q1_0.quiz_id from quiz_result q1_0 where q1_0.quiz_id=?
-> Use @OneToOne
mapping with @MapIds
at QuizResultEntity
side can solve this problem. This will work because at QuizEntity
side, hibernate knows the ID
of associated QuizResult
entity when it queries data.
public class QuizResultEntity {
@Id
// Note: we don't use auto generate strategy here.
// @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = LAZY)
@MapsId
@JoinColumn(name = "quiz_id")
private QuizEntity quiz;
// other fields
}
Query log:
select q1_0.id from quiz q1_0 where q1_0.id=?
Example source code: https://github.com/dntam00/learning-spring/tree/master/one-to-one