How to use Hibernate in a Custom Component

by Ombretta Malinverno
3,835 views Published on Jan 12, 2018
Applies to: 7.2 or higher
Table of contents

Introduction

Implementing a custom component sometimes means querying the database in the component service class. This article wants to give you an introduction on how to retrieve the hibernate session in the component service and how to use Hibernate efficiently to query the database.

"Hibernate is an Object/Relational Mapping tool for Java environments. The term Object/Relational Mapping (ORM) refers to the technique of mapping a data representation from an object model to a relational domain model with a SQL-based schema. Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities and can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC." (source Wikipedia https://en.wikipedia.org/wiki/Hibernate_(framework))

Hibernate is integrated in WebRatio Platform, so that anytime you generate the Domain Model of your Web Project, the tool creates all the Hibernate files needed to handle the connection to the database and its querying.

Prerequisites

So, the first thing to do when you are going to use Hibernate in a component service class is to check that the tables you want to query are mapped into the Domain Model. If not, WebRatio Platform cannot generate the hibernate configuration file and the bean classes for these tables, so you cannot use Hibernate to do the query but have to use a standard SQL query instead.

You can use Hibernate in the component service, placing in the Components Project all of the hibernate configuration files and bean classes related to the Domain Model. You have to generate at least the Domain Model and copy all of these file in a new package that you can name whatever you wish. It is important that you remember to overwrite these files any time you make changes to the Domain Model and generate it.

At this point, you are ready to write the component service.

How to retrieve the Hibernate Session

First of all, you have to correctly declare the component service, depending on the component type.

  1. For a View Component, the service must extend the AbstractDBService class and implement the RTXCachedContentUnitService interface.
  2. For an Operation, the service must extend the AbstracDBService class and implement the RTXOperationUnitService and the RTXTransactionAware interfaces.

In both cases, you have to declare a private attribute "hbService" of type HibernateService. In the component service constructor, you have to retrieve the Hibernate service. There are two different ways to do this. If the component descriptor has the "Database" node, you can get this node and then retrieve the service using the following code:

String dbId = DescriptorHelper.getChildValue(descr, "Database", true, this);

this.hbService = (HibernateService) mgr.getService(dbId + ".hibernate", HibernateService.class);

Otherwise, you can use only the second line of code using as the first parameter of the getService method, the string representing the name of the hibernate configuration file that specifies the connection properties.

Now, you have to retrieve the Hibernate session in order to make queries. This is also different depending on the component type.

  1. Retrieve the Hibernate Session for a View Component. You have to write this piece of code, which opens a new session and closes it after you made all your queries, for a view component.
Session hbSession = null;
 
try {
  hbSession = openSession(hbService, pageContext, sessionContext);
  /* executes the query */ 
  return bean;
} catch (Throwable e) {
  throw new RTXException(e);
} finally {
  commitAndClose(hbSession, true);
}
  • Line 1. This line defines a variable with "Session" type.
  • Line 4. This line opens a Hibernate session.
  • Line 10. This line commits and releases a Hibernate session.
  1. Retrieve the Hibernate Session for an Operation. Use the following code, which retrieves the connection from the transaction (if any) in which the component is placed, for an operation.
Session hbSession = null;
boolean inTransaction = false;

try {
  hbSession = locateTransactionSession(hbService, operationContext);
  if (hbSession != null) {
    inTransaction = true;
  } else {
     hbSession = openSession(hbService, operationContext, sessionContext);
  }
  /* executes the query */
  return bean;
} finally {
  commitAndClose(hbSession, !inTransaction);
}
  • Line 1. This line defines a variable with "Session" type.
  • Line 2. This line defines a boolean variable used to check if the operation is placed in a transaction.
  • Line 5. This line obtains a Hibernate session.
  • Lines 6-10. In this piece of code is checked if the session exists. If it exists a Hibernate session is opened; otherwise it means that the operation is placed in a transaction, and then the inTransaction variable is set to true.
  • Line 14. This line commits and releases a Hibernate session.

Moreover, due to the fact that the operation service implements the RTXTransactionAware interface, you must define the "inTransaction" method, which you can copy from the following code:

public void inTransaction(Map operationContext, Map sessionContext) throws RTXException {

  DBTransaction tr = locateDBTransaction(hbService, operationContext);
  if (tr == null) {
    createAndStoreDBTransaction(hbService, operationContext);
  }
  
}
  • Line 3. This line locates the db transaction.
  • Line 5. This line creates and store to the operation context a new DBTransaction.

How to construct the query

Hibernate provides different methods to query the database. In this section, you will see which one is better to be used and in which situation.

Retrieve a single record with unique identifier

The simplest query you may want to do is to retrieve a single record given its unique identifier. There are two different methods that you can use to retrieve the record.

User aUser = (User) hbSession.load(User.class, new Integer("1"));

User aDifferentUser = (User) hbSession.get(User.class, new Integer("1"));

The first one is the "load" method, which has to be used only if you are sure that a row with the given identifier exists. This restriction is due to the fact that if the record does not exist, this method throws an ObjectNotFoundException. If you are not sure of the record existence, you have to use the "get" method instead. Here is the syntax for both of them.

Both methods request two parameters:

  1. The first one is the class file of the entity object you want to retrieve.
  2. The second one is its identifier. The identifier must be of the same Java type declared in the hibernate entity configuration file.

Retrieve a single record with composite key

If your entity uses a composite key, than you have to construct an object that contains all the primary keys and then use that object as the identifier. For example, if you have the "User" class with two primary keys, the name and the surname, you can use this piece of code:

User userPK = new User();
userPK.setName("aName");
userPK.setSurname("aSurname");
 
User aUser = (User) hbSession.load(User.class, userPK);
 
User aDifferentUser = (User) hbSession.get(User.class, userPK);

Use complex queries

Hibernate allows you to construct more complex queries using the Criteria interface. Using this interface, you can specify conditions and ordering clauses for the query. You can create a Criteria for the entity using the "createCriteria" method.

Criteria firstCriteria = hbSession.createCriteria(User.class);

Once you create a Criteria, you can add conditions to the query using the "add" method. This method has one parameter that is a criteria. Here are some examples showing how to add conditions to the query.

User aUser = (User) hbSession.createCriteria(User.class).add(Restrictions.eq("username","aUsername")).uniqueResult();
  
User aUser = (User) hbSession.createCriteria(User.class).add(Restrictions.and(Restrictions.eq("username","aUsername"),Restrictions.eq("password","aPassword"))).uniqueResult();

User aUser = (User) hbSession.createCriteria(User.class).add(Restrictions.or(Restrictions.eq("username","aUsername"),Restrictions.eq("username","aDifferentUsername"))).uniqueResult();
 
List users = (List) hbSession.createCriteria(User.class).createCriteria("user2Group").add(Restrictions.idEq(1)).list();
  • Line 1. This line retrieves the user when you have a specific username.
  • Line 3. This line retrieves the user when you have specific username and password.
  • Line 5. This line retrieves the user when you have one of the given usernames.
  • Line 7. This line retrieves the list of users belonging to the group with "1" as identifier.

The Restriction class allows you to specify different conditions using different methods for different predicates.

For other examples, please look at the official Hibernate documentation.

How to use the HQL query language

You can also use the HQL query language to construct the query. In this case, you have to use the createQuery(..) method of the Session object, which returns an Hibernate Query object. The Hibernate Query permits you to set query parameters using wildcards.

Query query = hbSession.createQuery("from Product p where p.price > :price");
query.setParameter("price", new Float(1000));
List products = query.list();

for (Iterator iter = products.iterator(); iter.hasNext();) {
  Product aProduct = (Product) iter.next();
  String name = aProduct.getName();
  Double price = aProduct.getPrice();
}

Otherwise you can use the traditional SQL query language in this way:

SQLQuery query = hbSession.createSQLQuery("select p.name, p.price from PRODUCTTABLE as p where p.price > :price");
query.setParameter("price", new Double(1000));
List result = query.list();

for (Iterator iter = result.iterator(); iter.hasNext();) {
  Object[] row = (Object[]) iter.next();
  String name = (String) row[0];
  Float price = (Float) row[1];
}

Manage the query result

The result of the query is different according to the number of records extracted by the query. If the result is a single instance, then, casting the result into the object class of your choice, you can use all the get methods defined for that class to retrieve information. Here is an example.

String username = aUser.getUsername();
String password = aUser.getPassword();
 
Group group = (Group) aUser.getUser2Group();
 
String groupName = group.getGroupName();
  • Line 1. This line retrieves a single property of the loaded object.
  • Line 4. This line retrieves the user group navigating the one-to-many relationship.
  • Line 6. This line retrieves the group name using the get method of the related class.

If the result is a List of objects, you have to check whether the list is null or empty, and then you can iterate on the results, cast each result into the object class of your need and use the get methods as shown in the previous paragraph. Here is a sample code.

List users = (List) hbSession.createCriteria(User.class).createCriteria("user2Group").add(Restrictions.idEq(1)).list();

for (Iterator iter = users.iterator(); iter.hasNext();) {
  User aUser = (User) iter.next();
  String username = aUser.getUsername();
  String password = aUser.getPassword();      
}

You can also make changes to an object retrieved by the query. You can obtain this, setting the object properties using the set methods of the object class and saving the changes.

User aUser = (User) hbSession.createCriteria(User.class).add(Restrictions.eq("username","aUsername")).uniqueResult();
aUser.setPassword("aPassword");
 
hbSession.save(aUser);
hbSession.flush();