List
is an interface provided by Java and accessed from java.util.List
that has the capability to store a sequence of elements, allow duplicate elements, and contain elements of the same type. Some classes that implement the List
interface are java.util.ArrayList
, java.util.LinkedList
, and so on. Now, let's look at how to use List
while using hibernate.
Here, we consider a new table structure for employee
, and each employee has multiple e-mail addresses. So, we create an Employee
class that has the List<String>
field e-mails called list of e-mail addresses. Here, we use the List
class for this recipe. To achieve this, we need to create classes and tables; so, first of all, let's meet the basic prerequisites.
Use the following script to create the tables, unless you are using hbm2dll=create|update
to dynamically create the table using hibernate.
Use the following code to create the employee
table:
CREATE TABLE `employee` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) );
Use the following code to create the email
table:
CREATE TABLE `email` ( `Employee_id` BIGINT(20) NOT NULL, `emails` VARCHAR(255) DEFAULT NULL, `email_index` INT(11) NOT NULL, PRIMARY KEY (`Employee_id`,`email_index`), KEY `FK5C24B9C37808516` (`Employee_id`), CONSTRAINT `FK5C24B9C37808516` FOREIGN KEY (`Employee_id`) REFERENCES `employee` (`id`) );
Use the following code to create a class:
Source file: Employee.java
@Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue @Column(name = "id") private long id; @Column(name = "name") private String name; @ElementCollection(fetch=FetchType.LAZY) @CollectionTable(name = "email") @IndexColumn(name="email_index") private List<String> emails; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<String> getEmails() { return emails; } public void setEmails(List<String> emails) { this.emails = emails; } @Override public String toString() { return "Employee" + " Id:" + this.id + " Name:" + this.name + " Emails:" + this.emails; } }
In this section, we will take a look at how to insert, retrieve, delete, and update List
, step by step.
The following code is used to insert a record into the database. Here, we will try to insert the record of an employee with three e-mail addresses:
SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); Session session = sessionFactory.openSession(); Employee employee = new Employee(); employee.setName("yogesh"); List<String> emails = new ArrayList<String>(); emails.add("[email protected]"); emails.add("[email protected]"); emails.add("[email protected]"); employee.setEmails(emails); session.getTransaction().begin(); session.save(employee); session.getTransaction().commit();
Hibernate: insert into employee (name) values (?) Hibernate: insert into email (Employee_id, email_index, emails) values (?,?,?) Hibernate: insert into email (Employee_id, email_index, emails) values (?,?,?) Hibernate: insert into email (Employee_id, email_index, emails) values (?,?,?) Employee Id: 1 Name: yogesh Emails: [[email protected], [email protected], [email protected]]
When this code is executed, it inserts one record into the employee
table and three into the email
table. It also sets a primary key value for the employee
record in each record of the email
table as a reference.
Here, we know that the record is inserted with id
1
. So, we will try to get only this record and understand how List
works.
Use the following code to retrieve the records of Employee#1
:
Employee employee = (Employee) session.get(Employee.class, 1l); System.out.println(employee.toString());
Hibernate: select employee0_.id as id0_0_, employee0_.name as name0_0_ from employee employee0_ where employee0_.id=? Hibernate: select emails0_.Employee_id as Employee1_0_0_, emails0_.emails as emails0_, emails0_.email_index as email3_0_ from email emails0_ where emails0_.Employee_id=? Employee Id: 1 Name: yogesh Emails: [[email protected], [email protected], [email protected]]
Here, we notice that hibernate executes two different queries: one is to load the employee
object and the other to load all the e-mail addresses referenced to that particular employee.
Here, we will try to add one more e-mail address to the list of e-mail IDs for Employee#1
, which means that we will update the list of e-mails. Use the following code to do so:
Employee employee = (Employee) session.get(Employee.class, 1l); List<String> emails = employee.getEmails(); emails.add("[email protected]"); session.getTransaction().begin(); session.saveOrUpdate(employee); session.getTransaction().commit(); System.out.println(employee.toString());
Hibernate: select employee0_.id as id0_0_, employee0_.name as name0_0_ from employee employee0_ where employee0_.id=? Hibernate: select emails0_.Employee_id as Employee1_0_0_, emails0_.emails as emails0_, emails0_.email_index as email3_0_ from email emails0_ where emails0_.Employee_id=? Hibernate: insert into email (Employee_id, email_index, emails) values (?,?,?) Employee Id: 1 Name: yogesh Emails: [[email protected], [email protected], [email protected], [email protected]]
Here, we can see that we have two e-mail addresses with the same value, [email protected]
. This happens because here we used List
, and it allows the existence of duplicate elements.
When we use hbm2dll=auto|update
, hibernate will create two tables for us: one is employee
and the other is email
:
An important point that needs to be highlighted here is as follows:
@ElementCollection(fetch=FetchType.LAZY) @CollectionTable(name = "email") @IndexColumn(name="email_index") private List<String> emails;
Let's take a look at the preceding code in detail:
@CollectionTable
: This annotation indicates that the current field is of the Collection
type, and hibernate creates a separate table for it. It also creates a reference between them. In this case, hibernate creates a table named email
with email
and employee_id
. The employee_id
column is made by joining the persisted class name and the primary key column of the employee
class with an underscore (_
).@ElementCollection
: This annotation is used to define the relationship with the embedded or basic type.Here, we also use the fetch=FetchType.LAZY
attribute, which means that hibernate will load a child or referenced record on demand only. In our Retrieving a record example, it will execute the employee and e-mail queries separately. Hibernate uses FetchType.LAZY
if no attribute is defined for fetch
.
Another possible value with the fetch
attribute is FetchType.EAGER
, which means that hibernate will load all the child records at the time of the retrieval of the parent record. In other words, it eagerly loads all the records. If we use FetchType.EAGER
, hibernate uses the following query:
Hibernate: SELECT employee0_.id AS id0_0_, employee0_.name AS name0_0_, emails1_.Employee_id AS Employee1_0_2_, emails1_.emails AS emails2_, emails1_.email_index AS email3_2_ FROM employee employee0_ LEFT OUTER JOIN email emails1_ ON employee0_.id = emails1_.Employee_id WHERE employee0_.id = ?
The preceding code uses left outer join to get the child records, the reason being that when we use FetchType.LAZY
, hibernate executes a separate query to load the child or referenced record.
If we use FetchType.EAGER
, hibernate will use the JOIN
query to get the child records.
Here, hibernate uses the left outer join to get child records, because FetchType.EAGER
is used in the code.
@IndexColumn(name="email_index")
This annotation is used to hold the indexes of the collection's elements. This means that Hibernate creates a separate column in the child table (here, email
) with the value given in the attribute name (here, email_index
). This column preserves the sequence of the collection objects and helps while retrieving the records.
In the preceding example, we dealt with List
of the basic data type, which means that we used List
of String
(List<String>
). Now, let's consider that we have a Degree
class, which contains the degree name and passing year and want to map it with Employee
. For this, we deal with List<Object>
instead of List<String>
. In our case, it will be List<Degree>
. Let's take a look at how to do it.
Use the following code to create the classes:
Source file: Degree.java
@Entity @Table(name="degree") public class Degree { @Id @GeneratedValue private long id; @Column(name="degreename") private String degreeName; @Column(name="passingyear") private int passingYear; public Degree() { } public Degree(String degreeName, int passingYear) { this.degreeName = degreeName; this.passingYear = passingYear; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getDegreeName() { return degreeName; } public void setDegreeName(String degreeName) { this.degreeName = degreeName; } public int getPassingYear() { return passingYear; } public void setPassingYear(int passingYear) { this.passingYear = passingYear; } @Override public String toString() { return " Degree " + " Id:" + this.id + " Name:" + this.degreeName + " Passing year:" + this.passingYear; } }
@Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue @Column(name = "id") private long id; @Column(name = "name") private String name; @OneToMany(cascade={CascadeType.ALL}) private List<Degree> degrees; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Degree> getDegrees() { return degrees; } public void setDegrees(List<Degree> degrees) { this.degrees = degrees; } @Override public String toString() { return "Employee " + " Id: " + this.id + " Name: " + this.name + " Degrees: " + this.degrees; } }
When we create SessionFactory
with the hbm2dll=create|update
option, hibernate will create the employee
, degree
and employee_degree
tables for us; otherwise, you can use the following table script:
Use the following script to create tables if you are not using hbm2dll=create|update
. This script is for the tables that are generated by hibernate.
Use the following code to create the degree
table:
CREATE TABLE `degree` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`degreename` varchar(255) DEFAULT NULL,`passingyear` int(11) DEFAULT NULL,PRIMARY KEY (`id`));
Use the following code to create the employee
table:
CREATE TABLE `employee` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`));
Use the following code to create the employee_degree
table:
CREATE TABLE `employee_degree` (`employee_id` bigint(20) NOT NULL,`degrees_id` bigint(20) NOT NULL,UNIQUE KEY `degrees_id` (`degrees_id`),KEY `FK9CF457D5DB631AF` (`degrees_id`),KEY `FK9CF457D699100AA` (`employee_id`),CONSTRAINT `FK9CF457D699100AA` FOREIGN KEY (`employee_id`) REFERENCES `employee` (`id`),CONSTRAINT `FK9CF457D5DB631AF` FOREIGN KEY (`degrees_id`) REFERENCES `degree` (`id`));
Here, we used @OneToMany(cascade={CascadeType.ALL})
to map degree
with employee
.
Refer to the following code that shows how to insert/display records.
Here, we insert one employee
record with the two degrees associated with this employee.
Use the following code to insert an employee
record with degrees:
Employee employee = new Employee(); employee.setName("yogesh"); List<Degree> degrees = new ArrayList<Degree>(); degrees.add(new Degree("B.E.", 2008)); degrees.add(new Degree("M.S.", 2011)); employee.setDegrees(degrees); session.getTransaction().begin(); session.save(employee); session.getTransaction().commit();
Hibernate: insert into employee (name) values (?) Hibernate: insert into degree (degreename, passingyear) values (?, ?) Hibernate: insert into degree (degreename, passingyear) values (?, ?) Hibernate: insert into employee_degree (employee_id, degrees_id) values (?, ?) Hibernate: insert into employee_degree (employee_id, degrees_id) values (?, ?)
Here, from the SQL statements, we understand that the first SQL statement inserts one record in to the employee
table, the next two statements create two records in the degree
table, and the last two statements create the mapping records in the employee_degree
table using the inserted values of both the employee
and degree
tables.
Here, we will fetch the record of Employee#1
from the database. Use the following code to do so:
Employee employee = (Employee) session.get(Employee.class, 1l); System.out.println(employee.toString());
Hibernate: select employee0_.id as id0_0_, employee0_.name as name0_0_ from employee employee0_ where employee0_.id=? Hibernate: select degrees0_.employee_id as employee1_0_1_, degrees0_.degrees_id as degrees2_1_, degree1_.id as id1_0_, degree1_.degreename as degreename1_0_, degree1_.passingyear as passingy3_1_0_ from employee_degree degrees0_ inner join degree degree1_ on degrees0_.degrees_id=degree1_.id where degrees0_.employee_id=? Employee Id: 1 Name: yogesh Degrees: [ Degree Id: 1 Name: B.E. Passing year: 2008, Degree Id: 2 Name: M.S. Passing year: 2011]