Hibernate criteria for select into actual typesafe java objects

Hi friends,

If you know Hibernate Query Language(HQL), you will be familiar with following type of queries.

select new Family(mother, mate, offspr) from DomesticCat as mother join mother.mate as ate left join mother.kittens as offspr

It’s very useful when we retrieve some fields/properties from our entity class. The above query with “new” keyword can return a list of type “Family”. If we do not use such a keyword and specify the fields directly, a list of type Object [ ] is retrieved.

The major advantages with these type of queries are, we can easily iterate over with for-each loop, easy to display on GUI etc.

So, what my objective is that, just to write a Criteria equivalent to these type of HQL.

Here, I gonna write it in a more generic way. My example contains few number of classes, two or more enums etc. Also I include a custom comparator, which we have already discussed here.

Okay, let’s start. This is the Employee table.

db

It contains three columns namely id, fname and status. Id stands for employee id, fname for First Name of employee and status for mentioning whether the particular employee still exists or is obsolete.

Status 1 stands for LIVE employees and 0 for obsolete employees.

This is the Employee entity, Employee.java


import test.FetchType;
import java.io.Serializable;
import java.sql.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "employees", catalog = "hba")
@MappedEntity(className = "crit.pojo.Employee")
public class Employee implements Serializable {

    private long id;
    private Date dob;
    private String fname;
    private String lname;
    private int age;
    private String desgn;
    private FetchType status;

    @Column(name = "age")
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Column(name = "desgn", length = 25)
    public String getDesgn() {
        return desgn;
    }

    public void setDesgn(String desgn) {
        this.desgn = desgn;
    }

    @Column(name = "dob")
    public Date getDob() {
        return dob;
    }

    public void setDob(Date dob) {
        this.dob = dob;
    }

    @Column(name = "fname", length = 25)
    public String getFname() {
        return fname;
    }

    public void setFname(String fname) {
        this.fname = fname;
    }

    @Id
    @GeneratedValue
    @Column(name = "id")
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "lname", length = 25)
    public String getLname() {
        return lname;
    }

    public void setLname(String lname) {
        this.lname = lname;
    }

    @Enumerated(EnumType.ORDINAL)
    public FetchType getStatus() {
        return status;
    }

    public void setStatus(FetchType status) {
        this.status = status;
    }
}

This is how the Employee entity is being mapped with MySql database, hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hba</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">password</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <mapping class="crit.pojo.Employee"/>
    </session-factory>
</hibernate-configuration>

Here, I gonna make a try to retrieve a list of employees as per some searching and sorting conditions. I remind you that the following code is not destined for production, but it’s being given out just for an example.

The following example runs properly with JDK 5 or above and Hibernate 3.

This is the common Dao Class, DAO.java, where hibernate session is created.

import hba.util.HibernateUtil;
import org.hibernate.FlushMode;
import org.hibernate.Session;

public class DAO {

    private static final ThreadLocal THREAD = new ThreadLocal();

    protected DAO() {
    }

    public static Session getSession() {
        Session session = (Session) DAO.THREAD.get();
        if (session == null) {
            session = HibernateUtil.getSessionFactory().openSession();
            DAO.THREAD.set(session);
            getSession().setFlushMode(FlushMode.COMMIT);
        }
        return session;
    }

    protected static void begin() {
        getSession().beginTransaction();
    }

    protected static void commit() {
        getSession().getTransaction().commit();
    }

    protected static void rollback() {
        getSession().getTransaction().rollback();
        getSession().close();
        DAO.THREAD.set(null);
    }

    protected static void flush() {
        getSession().flush();
    }

    protected static void close() {
        getSession().close();
        DAO.THREAD.set(null);
    }
}

This is the BaseDao class, BaseDao.java, which extends above Dao class.


import crit.pojo.Employee;
import crit.pojo.UtilPojo;
import hba.dao.DAO;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;

public class BaseDao extends DAO {

    public static <S> Collection<UtilPojo> load(Class<S> modelClazz, String key, String value, FetchType fetchType, SortBy sortBy, SortOrder sortOrder) {
        try {
            ProjectionList projectionList = Projections.projectionList().add(Projections.property(key).as("id")).add(Projections.property(value).as("name"));
            ResultTransformer resultTransformer = Transformers.aliasToBean(UtilPojo.class);
            Criteria criteria = getSession().createCriteria(modelClazz);
            if (fetchType == FetchType.LIVE) {
                SimpleExpression simpleExpression = Restrictions.eq(Employee.class.getDeclaredField("status").getName(), FetchType.LIVE);
                criteria.add(simpleExpression);
            }
            criteria.setProjection(projectionList);
            criteria.setResultTransformer(resultTransformer);
            List<UtilPojo> list = criteria.list();
            return CustomComparator.sort(list, sortBy, sortOrder);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return new ArrayList<UtilPojo>();
        }
    }
}

The above class contains a static utility method load(), which accepts 6 parameters, an entity class, first field of the class to be retrieved (must be of type Integer), second field of the class to be retrieved (must be of type String), the fetch type(LIVE employees/ALL employees), sortBy (ID,NAME), sortOrder (ASCENDING/DESCENDING). The method fetches those two fields from the entity class and casts those fields to UtilPojo fields/properties.

This is the UtilPojo.java


public class UtilPojo {

    private String name;
    private long id;

    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 UtilPojo(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public UtilPojo() {
    }
}

The first enum, FetchType.java, which decides whether ALL or LIVE employees are to be retrieved.


public enum FetchType {

    ALL,
    LIVE
}

The second enum, SortBy.java, which tells by which property of entity class, the list is getting sorted. Here I give ID and NAME properties only.


public enum SortBy {

    ID("id"),
    NAME("name");
    private String value;

    public String getValue() {
        return value;
    }

    private SortBy(String value) {
        this.value = value;
    }
}

This is the third and final enum, SortOrder.java, which determines in which order (Ascending or Descending) the list is to be sorted.

public enum SortOrder {

    ASCENDING,
    DESCENDING
}

In the load() method, a list of model class, which is passed as a parameter (here the model class is Employee.java), is created, then casts the list values to a common pojo, ie. UtilPojo, then the entire is sent to the custom comparator for sorting purpose, and returns a sorted list (of type UtilPojo) to the calling method.

Here’s the CustomComparator.java, which have already been discussed in a previous post.


import crit.pojo.UtilPojo;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CustomComparator {

    private static Comparator<UtilPojo> idComparator = new Comparator<UtilPojo>() {

        public int compare(final UtilPojo pojo1, final UtilPojo pojo2) {
            return Long.valueOf(pojo1.getId()).compareTo(Long.valueOf(pojo2.getId()));
        }
    };
    private static Comparator<UtilPojo> nameComparator = new Comparator<UtilPojo>() {

        public int compare(final UtilPojo pojo1, final UtilPojo pojo2) {
            return pojo1.getName().compareTo(pojo2.getName());
        }
    };

    public static List<UtilPojo> sort(final List<UtilPojo> list, final SortBy sortBy, final SortOrder sortOrder) throws Exception {
        try {
            final Comparator<UtilPojo> comparator;
            if (sortBy == SortBy.ID) {
                comparator = idComparator;
            } else if (sortBy == SortBy.NAME) {
                comparator = nameComparator;
            } else {
                throw new IllegalArgumentException("Comparator not found for the filed, " + sortBy.getValue());
            }
            if (sortOrder == SortOrder.ASCENDING) {
                Collections.sort(list, comparator);
            } else if (sortOrder == SortOrder.DESCENDING) {
                Collections.sort(list, Collections.reverseOrder(comparator));
            } else {
                throw new IllegalArgumentException("Invalid Sort Order.");
            }
            return list;
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }
}

And finally, the last class, ListDao.java, from where a call to the load() method is emerged.


import crit.pojo.Employee;
import crit.pojo.UtilPojo;
import java.util.Collection;

public class ListDao extends BaseDao {

    public static void main(String[] args) {
        try {
            Collection<UtilPojo> collection = load(Employee.class, "id", "fname", FetchType.ALL, SortBy.ID, SortOrder.DESCENDING);
            for (UtilPojo pojo : collection) {
                System.out.println(pojo.getId() + " - " + pojo.getName());
            }
        } catch (Exception e) {
            System.out.println("Exception in List.main." + e);
        }
    }
}

Here, I tell the load() method to fetch “id” and “fname” of all employees from Employee entity, which is to be sorted by “id” in descending order.

The output will be.

 
Hibernate: select this_.id as y0_, this_.fname as y1_ from hba.employees this_
53 - Prem
41 - Tom
33 - Ram
27 - Robin
15 - Micheal
8 - Louis
5 - Kevin
4 - Deepesh
1 - Praveen

If we need a list of existing employees only, which is to be sorted by “name” in ascending order, we just want to change ListDao.java as follows.

import crit.pojo.Employee;
import crit.pojo.UtilPojo;
import java.util.Collection;

public class ListDao extends BaseDao {

    public static void main(String[] args) {
        try {
            Collection<UtilPojo> collection = load(Employee.class, "id", "fname", FetchType.LIVE, SortBy.NAME, SortOrder.ASCENDING);
            for (UtilPojo pojo : collection) {
                System.out.println(pojo.getId() + " - " + pojo.getName());
            }
        } catch (Exception e) {
            System.out.println("Exception in List.main." + e);
        }
    }
}

And the output will be.


Hibernate: select this_.id as y0_, this_.fname as y1_ from hba.employees this_ where this_.status=?
5 - Kevin
8 - Louis
1 - Praveen
53 - Prem
33 - Ram
41 - Tom

Thanks.