Today we are going to understand how to persist a collection object based on a sorted map(TreeMap) collection within the database using Hibernate.
By default, TreeMap stores key-value pairs in a sorted ascending order(based on the key), hence Hibernate will store the elements mapped from a TreeMap in an ascending order.
We will implement the Comparator interface to store the elements in a descending order.
Why do we have to add a collection object in an Entity class.
Let's suppose we have a class Companies containing the information such as - id, name and a Set collection of String object(representing the first name and last name of all the employees of a particular company).
That's why we are going to need a collection object in Companies class, representing multiple employee names in a company.
Let's put this example to work and make it easier for you to understand the whole concept.
Note :
In the upcoming example, We are going to create a Java Entity class which will be mapped to a table in our database(Oracle Express Edition 10g).
To perform this, we are not going to use Hibernate Annotations, we will instead create the mapping-resource file with extension hbm.xml.
And yes, one more thing, we are going to create this program in an Eclipse Integrated Development Environment (IDE),
so you may use any IDE of your choice.
Creating a POJO/Entity class
We are going to create a class that will contain all the Java code files
representing our Model class(whose objects needs to be persisted)
and the class that calls the Hibernate API and create an object-relational mapping(ORM) and perform object persistence.
This is a simple Java class whose objects(containing the Map object) needs to be persisted/saved, these objects are also known as Plain Old Java Objects(POJO) or Entity class.
Some may even refer to such class whose objects needs to be persisted as the Model class.
This Entity class will implement the Comparator interface to sort the collection of
of employee names stored in a Map, in an ascending order.
Companies.java
package decodejava;
import java.util.Map;
public class Companies implements Comparator<Object>
{
public int compare(Object obj1, Object obj2) //overriding Comparator's compare() method to give an ascending ordering
{
return(((String)obj2).compareTo((String)obj1)); //String's compareTo() method compares two String objects.
}
private int id;
private String companyName;
private Map<String,String> employeeNames;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getCompanyName()
{
return companyName;
}
public void setCompanyName(String companyName)
{
this.companyName = companyName;
}
public Map<String, String> getEmployeeNames()
{
return employeeNames;
}
public void setEmployeeNames(Map<String, String> employeeNames)
{
this.employeeNames = employeeNames;
}
}
Advertisement
Creating the class that calls the Hibernate API - Utility class
This class will create objects of Companies Entity class , which will be persisted using the Hibernate API and an
object-relational mapping(ORM) in the database is performed.
The Hiber class creates a Configuration object, used to configure the Hibernate. This is the first object we use when using the Hibernate.
This object is used to specify the location of a configuration file and
mapping document used by Hibernate. Using Configuration object we can create a SessionFactory object, which is eventually used to create a Session
object to perform the object persistence operations.
Hiber.java
package decodejava;
import java.util.Set;
import java.util.Map;
import java.util.TreeMap;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Hiber
{
public static void main(String... ar)
{
Configuration config = new Configuration();
SessionFactory sf = config.configure().buildSessionFactory();
Session session = sf.openSession();
//Creating the first company
Companies company1 = new Companies();
company1.setCompanyName("Company1");
//Creating the second company
Companies company2 = new Companies();
company2.setCompanyName("Company2");
Map<String,String> collection1 = new TreeMap<String,String>();
collection1.put("Hugh","Grant");
collection1.put("Anuj","Sobti");
collection1.put("James", "Stewart");
//Adding a duplicate value to the Set, but this won't be added
//collection.add(mob2);
//Setting the collection of employee names to the first company
company1.setEmployeeNames(collection1);
Map<String,String> collection2 = new TreeMap<String,String>();
collection2.put("Rakesh","Khanna");
collection2.put("Sean","Webber");
collection2.put("Adam", "Shot");
//Setting the collection of employee names to the second company
company2.setEmployeeNames(collection2);
session.beginTransaction();
session.save(company1); //Saving the object of first Company
session.save(company2); //Saving the object of second Company
session.getTransaction().commit();
//Creating a new Session to retrieve the stored objects.
session= sf.openSession();
session.beginTransaction();
//Retrieve and displaying the employee details
company1 = (Companies)session.get(Companies.class,1);
company2 = (Companies)session.get(Companies.class,2);
collection1 = company1.getEmployeeNames();
Set<Map.Entry<String,String>> set1 = collection1.entrySet();
for(Map.Entry<String,String> mapE : set1)
{
System.out.print("First Name : " + mapE.getKey());
System.out.println(", Second Name : "+ mapE.getValue());
}
collection2 = company2.getEmployeeNames();
Set<Map.Entry<String,String>> set2 = collection2.entrySet();
for(Map.Entry<String,String> mapE : set2)
{
System.out.print("First Name : " + mapE.getKey());
System.out.println(", Second Name : "+ mapE.getValue());
}
session.getTransaction().commit();
session.close();
}
}
Adding JARs
We are going to add some JARs files to the build path of our Java project.
These JARs make the Hibernate work with our database using a specific JDBC Driver for our particular
database.
All these JARs are included in the folder named required(within our Hibernate installation folder).
So, we need to add all the JARs in the required to our build path of our Java project.
Finally, we are going to add one more JAR file.
This is a specific JDBC JAR file(ojdbc14.jar) required by Hibernate to connect to our database(Oracle Express Edition 10g) and perform
object-relational mapping and object persistence.
Following the same steps using which we created and added the configuration file in our project, we are going to create and add a mapping-resource file within our package decodejava.
This file allows Hibernate to map the Model/Entity class and Value Type class to a table in the database and perform the object-relational mapping.
This mapping document ends with an extension .hbm.xml, hence, we have named it companies.hbm.xml.
This mapping document ends with an extension .hbm.xml,
hence, we have named it employeedetails.hbm.xml.
This mapping document tells Hibernate -
The name of the class to be mapped to a database table.
The name of the database table to be created.
The specific field, which is to be made the primary key in this table.
The data-type of each property in the class, which is going to mapped to a column in a table.
employeedetails.hbm.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "decodejava.Companies" table = "COMPANIES">
<id name = "id" column = "Company_ID" type = "int">
<generator class="increment">
</id>
<property name = "companyName" column = "Company_Name" type = "string"/>
<map name ="employeeNames" table="COMPANY_EMPLOYEES" cascade="all" sort="decodejava.Companies">
<key><column name="COMPANY_ID"></column></key>
<index column="First_Name" type="string"/>
<element column="Second_Name" type="string"/>
</map>
</class>
</hibernate-mapping>
This mapping document has <hibernate-mapping> as the root element and its child element <class>, containing all the class elements.
The name attribute within the <class> element defines the name of the class to be mapped to a database table.
The table attribute within the <class> element defines the name of database table to be created for the class.
The id element maps a property within the class to the primary key of the database table.
the name child attribute refers to the name of a property in the class.
The column child attribute refers to name of a column in the database table.
The type child attribute specifies the data type of the property within the class, which will be mapped to SQL data type.
The property element maps a property within the class to a column in the database table.
the name child attribute refers to the name of a property in the class.
The column child attribute refers to the column name in the database table.
The map element maps a Map object to be saved in the another database table).
the name child attribute refers to the name of a property to map.
The table child attribute refers to the name of the table in which this Set object will be saved.
The sort child attribute refers to the name of the class which has implemented Comparator interface for sorting the objects to be stored in a Map.
The index child attribute refers to the column name for index part of a Map entry.
The element child attribute refers to the column namefor the value part of a Map entry and its type.
Note :
If we don't use sort attribute within the set element in our mapping resource file, the retrieved collection of
employee names of a company will not be in any order, despite using the TreeMapto store them, because Hibernate uses the functionality of Map interface when
a set of any kind(sorted - TreeMap or unsorted - HashMap)is pulled out of database.
Adding a configuration file
Next, we are going to add a configuration file to our project.
This configuration document ends with an extension .cfg.xml,
and it is named as hibernate.cfg.xml.
This configuration file is an xml file and it allows us to specify the following features -
A database to which Hibernate connects to perform object-relational mapping and object persistence.
The JDBC driver class for our specific database, used by the Hibernate to connect to the database.
The username and password of database, using which the Hibernate connects to the database.
An SQL Dialect, used by the Hibernate to instruct the database.
A command to echo all the SQL commands which are executed by the Hibernate on stdout.
The mapping resource file used by Hibernate to correctly map the parent and subclasses to a database table.
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:XE</property>
<property name="connection.username">scott</property>
<property name="connection.password">tiger</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<!--Naming the mapping resource file -->
<mapping resource ="companies.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Directory Structure of Hibernate Project
The picture above depicts how and where to arrange POJO/Entity(.java) file and the other the class(that calls the hibernate into action)
in a specific directory structure.
Project Folder - MappingTreeMapWithXML is the name of our Project and it is a top-level directory.
This project folder contains the main package of our project i.e. decodejava,
which contains POJO/Entity class file i.e. Companies.java and the class that calls the hibernate into action i.e. Hiber.java.
Besides this, the project folder also contains the hibernate configuration file i.e. hibernate.cfg.xml, and
as we have not used Hibernate Annotations in this project, hence, we have created and used a mapping-resourcecompanies.hbm.xml .
The project folder also contains all the needed Hibernate JARs present in the required folder of Hibernate
installation and the JDBC JAR file(ojdbc14.jar) required by Hibernate to connect to our database.
Execution
Finally, after executing Hiber class, two separate tables will be created in the database -
COMPANIES, which stores the Entity objects.
COMPANY_EMPLOYEES, which stores the Set collection object.
And you will get the following output, which shows the collection of employee names retrieved in a natural ascending order.
Hibernate: create table COMPANIES (COMPANY_ID number(10,0) not null, Company_Name varchar2(255 char), primary key (COMPANY_ID))
Hibernate: create table COMPANY_EMPLOYEES (COMPANY_ID number(10,0) not null, First_Name varchar2(255 char) not null, Second_Name varchar2(255 char), primary key (COMPANY_ID, First_Name))
Hibernate: alter table COMPANY_EMPLOYEES add constraint FKf3ir1cup274vb9nd2jrwj3hea foreign key (COMPANY_ID) references COMPANIES
Hibernate: select max(COMPANY_ID) from COMPANIES
Hibernate: insert into COMPANIES (Company_Name, COMPANY_ID) values (?, ?)
Hibernate: insert into COMPANIES (Company_Name, COMPANY_ID) values (?, ?)
Hibernate: insert into COMPANY_EMPLOYEES (COMPANY_ID, First_Name, Second_Name) values (?, ?, ?)
Hibernate: insert into COMPANY_EMPLOYEES (COMPANY_ID, First_Name, Second_Name) values (?, ?, ?)
Hibernate: insert into COMPANY_EMPLOYEES (COMPANY_ID, First_Name, Second_Name) values (?, ?, ?)
Hibernate: insert into COMPANY_EMPLOYEES (COMPANY_ID, First_Name, Second_Name) values (?, ?, ?)
Hibernate: insert into COMPANY_EMPLOYEES (COMPANY_ID, First_Name, Second_Name) values (?, ?, ?)
Hibernate: insert into COMPANY_EMPLOYEES (COMPANY_ID, First_Name, Second_Name) values (?, ?, ?)
Hibernate: select companies0_.COMPANY_ID as COMPANY_ID1_0_0_, companies0_.Company_Name as Company_Name2_0_0_ from COMPANIES companies0_ where companies0_.COMPANY_ID=?
Hibernate: select companies0_.COMPANY_ID as COMPANY_ID1_0_0_, companies0_.Company_Name as Company_Name2_0_0_ from COMPANIES companies0_ where companies0_.COMPANY_ID=?
Hibernate: select employeena0_.COMPANY_ID as COMPANY_ID1_1_0_, employeena0_.Second_Name as Second_Name3_1_0_, employeena0_.First_Name as First_Name2_0_ from COMPANY_EMPLOYEES employeena0_ where employeena0_.COMPANY_ID=?
First Name : James, Second Name : Stewart
First Name : Hugh, Second Name : Grant
First Name : Anuj, Second Name : Sobti
Hibernate: select employeena0_.COMPANY_ID as COMPANY_ID1_1_0_, employeena0_.Second_Name as Second_Name3_1_0_, employeena0_.First_Name as First_Name2_0_ from COMPANY_EMPLOYEES employeena0_ where employeena0_.COMPANY_ID=?
First Name : Sean, Second Name : Webber
First Name : Rakesh, Second Name : Khanna
First Name : Adam, Second Name : Shot
Let's see how the two tables and their contents look like within the database
select * from COMPANIES;
you will get the following output displaying the objects of COMPANIES Entity class stored in COMPANIES table, as shown below.
And on executing the second query in the database -
select * from COMPANY_EMPLOYEES;
you will get the following output displaying the collection object of type String, stored in COMPANY_EMPLOYEES table as shown below.
This output shows that each object of COMPANY_EMPLOYEES and all the employee names are sorted in an descending order, according to the first name of each employee,
because the Comparator interface is implemented in the Entity class i.e. Companies.