Engineering Full Stack Apps with Java and JavaScript
An entity class can refer to other entity classes or embed value types. An entity is a type on its own and has an existence of its own like a Course and its students. A value type is a type which doesn't have existence of its own, but belongs to an entity like an user and his address. Value types are always completely owned by their containing entity. Once you delete the entity, its associated value types are also deleted. But when you delete an entity, referenced entities still exist: when you delete a Course entity, you don't delete all its students. In this article, we will see embedding value types within an entity class.
The simplest implementation to embed a value type in an entity class is to treat the member variables of the embedded class (e.g. Address), the same way as we treat the member variables of the embedding class (e.g. User) by adding more columns to the entity table one column each for the value type class members. To embed a value type like this, we have to annotate the embedded class as @Embeddable and annotate the reference variable to the value type class in the embedding class as @Embedded. Even though @Embedded is optional when you have @Embeddable, it is better to have them both. There is a special case: @Embedded and @Id won’t work together. To get the combined behavior of @Embedded and @Id, we should use @EmbeddedId annotation.
If you have a collection/list of value types like a list of addresses, hibernate will create a different table for the included collection/list and link that table to the original table using a foreign key. For this you should use the @ElementCollection annotation over the collection/list declaration in the embeding entity class (instead of @Embedded).
...
@ElementCollection
private Set<Address> AddressList = new HashSet<Address>();
...
You can also use the optional @JoinTable in addition to @ElementCollection, for instance, to give a name for the join column created for the foreign key.
...
@ElementCollection
@JoinTable(name = "USE_ADDR_TABLE", joinColumns = @JoinColumn(name = "USER_ID_COL"))
private Set<Address> AddressList = new HashSet<Address>();
...
By default it will be parentclassname_referencefield. We can also use hibernate specific annotations such as @CollectionId, for instance, to define a primary key for the list table.
Example: We will see an example for the simple case here that embed an Address class inside a User class. Annotate the embedded Address class as @Embeddable and annotate the reference variable to Address class as @Embedded in the embedding class. Hibernate will treat the member variables of the embedded Address class, the same way as we treat the member variables of the embedding User class, and create additional columns in User table for each of the members of Address class. If you are not following the tutorials in order, refer to the article http://javajee.com/your-first-hibernate-program for the basic setup including hibernate configuration file. We have used MySQL database and if you are using a different one, change the configuration file accordingly.
We will create one more class called Address as below:
package com.javajee.hiberex.dto;
import javax.persistence.Embeddable;
@Embeddable
public class Address {
private int houseno;
private String street;
public int getHouseno() {
return houseno;
}
public void setHouseno(int houseno) {
this.houseno = houseno;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
We will now modify the class User.java as below:
package com.javajee.hiberex.dto;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class User {
@Id
int id;
private String name;
@Embedded
private Address address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
We will also modify the test class as below:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.javajee.hiberex.dto.Address;
import com.javajee.hiberex.dto.User;
public class UserTest {
public static void main(String[] args) {
User u = new User();
u.setId(1);
u.setName("Heartin");
Address addr = new Address();
addr.setHouseno(777);
addr.setStreet("MyStreet");
u.setAddress(addr);
Configuration configuration = new Configuration();
configuration.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties()).buildServiceRegistry();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
session.beginTransaction();
u = null;
session = sessionFactory.openSession();
session.beginTransaction();
u = (User) session.get(User.class, 1);
session.getTransaction().commit();
System.out.println("Details retrieved:" + u.getName() + " "
+ u.getAddress().getHouseno() + " " + u.getAddress().getStreet());
}
}
Run the program and verify the results. You can also verify the result by looking into the corresponding database and tables.