Objective
In this tutorial, we will study to use the right object-oriented Hibernate in our application. The object-oriented Hibernate differs from NON object-oriented Hibernate on the way the entity classes are mapped from the database tables.
Database
Assume that we have two tables in a 1-n relationship in a database of hotel:
- tblhotel: represents the attributes of hotels: id, name, address, starRate, description.
- tblroom: represents the attributes of rooms: id, name, type, price, description.
- a hotel may have many rooms; a room belongs to a hotel. So they have a relationship of 1(tblhotel) – n(tblroom). Therefore, in the tblroom, there is a foreign key idhotel to refer to the primary id in the tblhotel.

NON object-oriented mapping to entities (NOT recommended)
In the NON object-oriented mapping, these two tables are transformed to two entities whose attributes are the same as those of respective table.
Hotel.java
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "tblhotel")
public class Hotel implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "address")
private String address;
@Column(name = "star")
private int star;
@Column(name = "des")
private String des;
}
Room.java
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "tblroom")
public class Room implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "type")
private String type;
@Column(name = "price")
private float price;
@Column(name = "des")
private String des;
@Column(name = "idhotel")
private int idhotel;
}
In the class Room, there is an attribute of idhotel (lines 31-32), this is the same as that in the tblroom. However, this design already violated the object-oriented design spirit: idhotel is an attribute of the class Hotel, therefore, the fact that the class Room also has the same attribute idhotel, that is already belonged to other class, is not accepted. That is why this design is NOT recommended in object-oriented context.
Object-oriented mapping to entities (recommended)
The right object-oriented mapping way is create two respective entity classes as follow:
- Hotel: has all attributes of tblhotel and: a list of room that it has.
- Room: has all attributes of tblroom except the foreign keys, and a hotel to which it belongs

In this way, there is no incorrect attribute in any class (The attribute that violate the object-oriented rule of: only the owner class of the attribute has the right to control that attribute).
Hotel.java
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "tblhotel")
public class Hotel implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "address")
private String address;
@Column(name = "star")
private int star;
@Column(name = "des")
private String des;
@OneToMany(mappedBy = "hotel", cascade = CascadeType.ALL)
List<Room> listRoom;
public Hotel() {
super();
}
public Hotel(String name, String address, int star, String des) {
super();
this.name = name;
this.address = address;
this.star = star;
this.des = des;
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getStar() {
return star;
}
public void setStar(int star) {
this.star = star;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public List<Room> getListRoom() {
return listRoom;
}
public void setListRoom(List<Room> listRoom) {
this.listRoom = listRoom;
}
}
Room.java
package model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "tblroom")
public class Room implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "name")
private String name;
@Column(name = "type")
private String type;
@Column(name = "price")
private float price;
@Column(name = "des")
private String des;
@ManyToOne
@JoinColumn(name = "idhotel")
private Hotel hotel;
public Room() {
super();
}
public Room(String name, String type, float price, String des) {
super();
this.name = name;
this.type = type;
this.price = price;
this.des = des;
}
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 String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public Hotel getHotel() {
return hotel;
}
public void setHotel(Hotel hotel) {
this.hotel = hotel;
}
}
The relationship between tables is covered by using the @OneToMany tag in the Hotel class (lines 32-33) and the @ManyToOne tag in the Room class (lines 33-35).
Using createQuery()
This method enables us to manipulate the query on the Entity classes and their attributes (NOT on the tables and their attributes). For instance, the following function will query on the Room class instead of tblroom.
public ArrayList<Room> searchRoom(String key){
ArrayList<Room> result = (ArrayList<Room>)session.createQuery("from Room where name like '%"+key+"%'").list();
return result;
}
Using createNativeQuery()
This method enables us to manipulate the query on the tables and their attributes as the way we use query statement in classical way without Hibernate. This is usually used when the query statement is too complicate or the returned data in not stocked in any table in the database. For instance, the following statistical report function: report on the revenue and filled rate of rooms during a given period (startDate, endDate).
The full database of the hotel management application is follow:

The respective entity classes of the application is follow:

Noted that the RoomStat is a class that has not respective table in the database because all its attributes are secondary so they are omitted when designing the database. The following code will use the createNativeQuery() to retrieve the statistical data on the rooms
public ArrayList<RoomStat> getRoomStat(Date startDate, Date endDate){
ArrayList<RoomStat> result = new ArrayList<RoomStat>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sql = "SELECT a.id, a.name, a.type, a.des, "
+ "coalesce((SELECT SUM(DATEDIFF((CASE WHEN b.checkout > :endDate THEN :endDate ELSE b.checkout END), "
+ "(CASE WHEN b.checkin > :startDate THEN b.checkin ELSE :startDate END))) FROM tblBookedRoom b "
+ "WHERE b.idroom = a.id AND b.checkout > :startDate AND b.checkin < :endDate AND b.ischeckin=1 GROUP BY b.idroom), 0) as days,"
+ " coalesce((SELECT SUM(DATEDIFF((CASE WHEN b.checkout > :endDate THEN :endDate ELSE b.checkout END), "
+ "(CASE WHEN b.checkin > :startDate THEN b.checkin ELSE :startDate END))*b.price) FROM tblBookedRoom b "
+ "WHERE b.idroom = a.id AND b.checkout > :startDate AND b.checkin < :endDate AND b.ischeckin=1 GROUP BY b.idroom),0) as income "
+ " FROM tblRoom a ORDER BY income DESC, days DESC";
try {
Query query = session.createNativeQuery(sql);
query.setDate("startDate", startDate);
query.setDate("endDate", endDate);
List<Object[]> rs = query.list();
for( Object[] lo : rs){
RoomStat r = new RoomStat();
r.setId(Integer.parseInt(lo[0].toString()));
r.setName(lo[1].toString());
r.setType(lo[2].toString());
r.setDes(lo[3].toString());
r.setTotalDay(Integer.parseInt(lo[4].toString()));
r.setTotalIncome(Float.parseFloat(lo[5].toString()));
result.add(r);
}
}catch(Exception e) {
e.printStackTrace();
}
return result;
}