Overview
Using @Embedded and @Embeddable in hibernate can help you manage entities and their value type efficiently.
The term embed signifies a has-a relationship. For example, in an online game platform, a player has a ranking. In a CRM, a customer has an address.
Take the Player and Ranking analogy for example, you can store the ranking as a String field in the Player entity (“Noob”, “Pro”, “God”… for example). However, your ranking system might evolve into something more complex such as Ranking now has a minimum score and a name…
In such case, using @Embeddable and @Embedded makes sense.
Let’s find out.
Show me the code
Enough talking, here is the code simulate the Player and Ranking situtation:
The Ranking class
@Embeddable public class Ranking { private String rankName; private int score; public Ranking() { } //getter, setters, other constructors are ommited for brevity }
The Player class
@Entity @Table(name = "player") public class Player { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @NotNull private String name; private int age; private Ranking ranking; @UpdateTimestamp @Temporal(TemporalType.TIME) private Time joinedDate; //getter, setters, other constructors are ommited for brevity }
By marking a class @Embeddable, you don’t need to use the @Embedded in the field inside the entity.
Let’s try inserting a player into the database:
try (Session session = factory.openSession()) { Transaction tx = session.beginTransaction(); var rank = new Ranking("Beginner", 0); Player player = new Player(); player.setName("Jane"); player.setAge(10000); player.setRank(rank); session.persist(player); tx.commit(); }
Here is the hibernate query log:
As you can see, all the fields of the embedded class are in the same table as the entity class.
If you prefer, you can mark the ranking field in the Player class with @Embedded and remove the @Embeddable annotation at the Ranking class. The effect is the same.
Naming conflict
As mentioned in the previous section, the fields of the embedded class are store in the same table with the entity. Thus, if I set the name of the ranking to “name” (instead of “rankName”), Hibernate will complain:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.datmt.hibernate.model.Player column: name (should be mapped with insert="false" update="false")
The obvious choice is to rename the field to something else to avoid this conflict.
If you don’t want to rename the field in the embedded class, you can use the @AttributeOverride annotation.
@Embedded @AttributeOverrides({ @AttributeOverride( name = "name", column = @Column(name = "rank_name")) }) private Ranking ranking;
By adding this annotation, Hibernate will create a column named “rank_name” to store the value of the name field of Ranking.
Nested embedded field
@Embeddable class can have other @Embeddable class as its property.
Let’s say your ranking system gets more sophisticated. The ranking name is not a String anymore but a class.
Now, the Ranking class has Rank as a field:
public class Rank { private String name; private int baseScore; }
public class Ranking { @Embedded private Rank rank; private int score; public Ranking() { } }
Conclusion
In this post, I’ve showed you how you can use @Embedded and @Embeddable to handle a has-a relationship in Hibernate.
I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.