1. Introduction
In this tutorial, we’ll discuss embeddable types in JPA.
An embeddable type is a composition of values. If you have a class which naturally is a part of another class then this class can be made embedded. For example, consider a class Address
, which has properties Country
, State
and City
. This Address
class is a part of another class Employee
. The Addres
s class can be created as embeddable.
Generally embeddable types are used to group multiple basic type mappings and reuse them across several entities. The composed values are mapped to the same table as the parent table.
Following are some important points about embeddable types:
- An embeddable type is another form of a value type.
- An embeddable type’s lifecycle is bound to a parent entity type.
- Embeddable types can be made up of basic values as well as associations.
- Embeddable type when used as collection elements, cannot define collections themselves.
- An Embeddable type shares the identity of the entity.
Following is an example of embeddable type Address
.
@Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "name") private String name; @Embedded Address address; // constructor, getter and setter removed for brevity }
@Embeddable public class Address { @Column(name = "country") private String country; @Column(name = "state") private String state; @Column(name = "city") private String city; // constructor, getter and setter removed for brevity }
2. @Embeddable and @Embedded annotations
2.1 @Embeddable
This annotation specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity. Each of the persistent properties or fields of the embedded object is mapped to the database table for the entity. In our example, @Embeddable
annotation is used to specify the Address
class will be used as a component. Address
does not have its own primary key and shares the primary key of the enclosing Employee
class. @Embeddable
is used to describe the mapping type itself.
2.1 @Embedded
This annotation specifies a persistent field or property of an entity whose value is an instance of an embeddable class. The embeddable class must be annotated as @Embeddable
. @Embedded
is for referencing a given embeddable type.
Note that in our example, Address
field in Employee
is annotated with @Embedded
.
3. Example
We have already mentioned an embeddable type Address
earlier in this tutorial. Lets try to save an Employee
instance. A typical client code looks like the following:
Session session = HibernateUtil.getSessionFactory().openSession(); transaction = session.beginTransaction(); Address address = new Address("Dummy Country", "Dummy State", "Dummy city"); Employee employee = new Employee(); employee.setName("Dummy name"); employee.setAddress(address); session.save(employee); transaction.commit();
Hibernate generates the following insert statement:
Hibernate: insert into Employee (city, country, state, name) values (?, ?, ?, ?) BasicBinder:64 - binding parameter [1] as [VARCHAR] - [Dummy city] BasicBinder:64 - binding parameter [2] as [VARCHAR] - [Dummy Country] BasicBinder:64 - binding parameter [3] as [VARCHAR] - [Dummy State] BasicBinder:64 - binding parameter [4] as [VARCHAR] - [Dummy name]
4. Multiple embeddable types and Overriding Embeddable types
It is possible to include the same embeddable type multiple times in the same parent entity type, but we have to set the associated column names explicitly. This is because JPA expects a database column having the same name with its associated object property.
If an embeddable type is used multiple times in some entity, you need to use the @AttributeOverride
and @AssociationOverride
annotations to override the default column names defined by the Embeddable.
Lets change our Employee
class to include two instances of Address
. We can then use @AttributeOverride
annotation to specify the mapping.
@Entity @AttributeOverrides({ @AttributeOverride(name = "currentAddress.country", column = @Column(name = "current_address_country")), @AttributeOverride(name = "currentAddress.state", column = @Column(name = "current_address_state")), @AttributeOverride(name = "currentAddress.city", column = @Column(name = "current_address_city")), @AttributeOverride(name = "permanentAddress.country", column = @Column(name = "permanent_address_country")), @AttributeOverride(name = "permanentAddress.state", column = @Column(name = "permanent_address_state")), @AttributeOverride(name = "permanentAddress.city", column = @Column(name = "permanent_address_city")), }) public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "name") private String name; Address currentAddress; Address permanentAddress; }
Hibernate will generate the following SQL statements:
Hibernate: insert into Employee (current_address_city, current_address_country, current_address_state, name, permanent_address_city, permanent_address_country, permanent_address_state) values (?, ?, ?, ?, ?, ?, ?) BasicBinder:64 - binding parameter [1] as [VARCHAR] - [Current city] BasicBinder:64 - binding parameter [2] as [VARCHAR] - [Current Country] BasicBinder:64 - binding parameter [3] as [VARCHAR] - [Current State] BasicBinder:64 - binding parameter [4] as [VARCHAR] - [Dummy name] BasicBinder:64 - binding parameter [5] as [VARCHAR] - [Permanent city] BasicBinder:64 - binding parameter [6] as [VARCHAR] - [Permanent Country] BasicBinder:64 - binding parameter [7] as [VARCHAR] - [Permanent State]