Spring and Hibernate with Annotations

Data Access Objects

When using JPA and Hibernate, accessing databases becomes much easier.  In this section we are going to set up the most common Data Access Object (DAO) and extend it for use with Employee and Department.

As you may have guessed from our model, the lowest level DAO in our application is called MappedModelDao.  Through the use of Java’s Generics syntax, we’ll make it robust to handle (almost) all normal calls to add, edit, and delete records in the database.

/src/main/java/com/technologicaloddity/departments/dao/MappedModelDao.java

package com.technologicaloddity.departments.dao;

import java.util.*;

import org.hibernate.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.transaction.annotation.*;

import com.technologicaloddity.departments.model.*;

@Repository
@Transactional
public abstract class MappedModelDao<T extends MappedModel> {

    @Autowired
    protected SessionFactory sessionFactory;

    public abstract Class<T> getActualClass();

    @SuppressWarnings("unchecked")
    public T findById(String id) {
        Session session = sessionFactory.getCurrentSession();
        return (T)session.get(getActualClass(), id);
    }

    @SuppressWarnings("unchecked")
    public List<T> findAll() {
        Session session = sessionFactory.getCurrentSession();
        Criteria criteria = session.createCriteria(getActualClass());
        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return criteria.list();
    }

    public void saveOrUpdate(T entity) {
        Session session = sessionFactory.getCurrentSession();
        session.saveOrUpdate(entity);
    }

    public void delete(T entity) {
        Session session = sessionFactory.getCurrentSession();
        session.delete(entity);
    }
}

As always, here is the line-by-line:

  • Line 1: Standard Java package statement.
  • Lines 3-10: Java imports.  Worth noting here: org.hiberate.* (for SessionFactory) and various types of Spring annotations (bean, transaction, and stereotype).
  • Line 12: The Spring @Repository stereotype is basically a synonym for DAO.  This tells Spring (among other things) that it may see Data Exceptions from here.
  • Line 13: The Spring @Transactional annotation tells Spring to handle transactions for this class.  If not other rules are given (and we didn’t give any in this application), the default transaction strategy is to rollback a transaction on any runtime exception.
  • Line 14: Start Java class definition.  Note that MappedModelDao is parameterized class with “<T extends MappedModel>”.    The parameter is the class to be managed by the DAO.  This means that when we define EmployeeDao later, it will actually be EmployeeDao<Employee>, and Department will be DepartmentDao<Department>.
  • Lines 16-17: Here we define our Hibernate SessionFactory, which is autowired with the SessionFactory we defined before in our Spring configuration file (dispatch-servlet.xml).  While the SessionFactory does way too much to explain here, for this application you can think of it as a factory for object brokers, which in turn keep track of which objects have been requested or modified and if they need saving to the database.
  • Line 19: The abstract function getActualClass() allows us to retrieve the underlying class which is managed.  In other words, in some cases, we want to know that we are managing Employee.class, not just an extension of MappedModel.class.  The core of the problem is that you can’t say “T.class” since T isn’t a Class, and Java will not allow this design time.  There are other ways to get the actual class name that don’t involve abstract functions (namely, using java.reflect) but they tend to be a bit complicated.  To keep the code simple, we’ll just force DAOs to tell us what kind of object they are managing.  We’ll see the extremely simple implementation of this function in EmployeeDao and DepartmentDao.
  • Lines 21-25: Here we define our first workhorse function, findById.  First you’ll see a SuppressWarnings(“unchecked”) annotation.  While not strictly required, it is good practice to note that the return value has not been strongly checked to be the type that is declared.  This is not a major problem for us, as we will trust Hibernate to return the correct type.  Next, the definition: public T findById(String id).  Note that “T” is the same as the parameterized type of <T extends MappedModel> above.  Therefore, if T is later set as Employee (e.g. “public class EmployeDao extends MappedModelDao<Employee>”), this method will return an Employee (since Employee == T in that case).  The id field is, of course, a String, just as we defined in MappedModel.  Next, we get a Hibernate session through SessionFactory.getCurrentSession().  How does it determine the “current” session?  Spring handles all these details for you, since it manages the SessionFactory. (And quite honestly, I don’t really know!).  Once we have a Session object, it is easy to find an element by ID:  the Session.get(Class, id) method returns an object of type Class with the given id.  Note that for the Class parameter, we are using our getActualClass() method and casting the result to type T.  (If you are bit confused, we’ll step through this in the EmployeeDao with a concrete example later).
  • Lines 27-33: The next workhorse function: findAll.  This method will return all instances of a given Class that are in the database.  It is basically the SQL equivalent of “select * from table_name”.  Again, we will start by getting a Hibernate session with getCurrentSession.  Next we will create a org.hiberate.Criteria to select all the objects.  Explaining all the things that Criteria can do is beyond this tutorial, but for this case, ours is pretty simple: we are creating a Criteria that matches an object that is of type “getActualClass()”.  Also on our Criteria, we want to set a ResultTransformer (a type of limiter for the actual criteria) called DISTINCT_ROOT_ENTITY, a predefined hibernate transformer.  Basically, DISTINCT_ROOT_ENTITY is the SQL equivalent of “distinct”.  It returns each object once and only once, regardless of the rest of the criteria.  Finally, we execute the Criteria with Criteria.list(), which returns a list of objects.  Since the Criteria is for getActualClass(), it should return a List<getActualClass()> or the more Java friendly List<T>  (there’s that T again!).
  • Lines 35-38: Hibernate sessions keep track of which objects are in the current session.  Therefore, as long as the object we are using was retrieved with Hibernate, we can make changes to the object in plain-old Java (using the setter functions, mostly) and then ask Hibernate to persist the changes.  The method that does this is Session.saveOrUpdate(object).  We’ll wrap this function in our DAO here with our own saveOrUpdate method.  Basically, it grabs a Hibernate Session object, then calls the underlying saveOrUpdate method.  This method will persist any changes to the object into the database.
  • Lines 40-43: Like saveOrUpdate, org.hibernate.Session also provides a delete(object) function.  As you might expect, delete removes an object from the database and from the current Hibernate session.  Also like saveOrUpdate, we’ll wrap delete with our own delete(T) method so that we can get the session and call session.delete(object).  This method will delete rows from the database!
  • Line 44: End of class definition.

OK, that’s a bit to digest for an abstract class.  But the payoff comes now, when we look at EmployeeDao.  EmployeeDao provides the methods to save, delete, and find (all and by id) the Employees in a database.

/src/main/java/com/technologicaloddity/departments/dao/EmployeeDao.java

package com.technologicaloddity.departments.dao;

import org.springframework.stereotype.*;

import com.technologicaloddity.departments.model.*;

@Component
public class EmployeeDao extends MappedModelDao<Employee> {

    @Override
    public Class<Employee> getActualClass() {
        return Employee.class;
    }
}

That’s it.  The whole thing.  Here’s the line-by-line.

  • Lines 1-5: Package statement and imports.  Nothing fancy here.
  • Line 7: The Spring @Component stereotype annotation makes this class available for auto-wiring into other classes (like Web Controllers, which we will do later).
  • Line 8: Notice that the class extends the parameterized MappedModelDao with the parameter <Employee>.  This means this DAO will manage Employee objects, and in MappedModelDao, it will set the placeholder “T” to Employee.
  • Line 10-13: This is our implementation of MappedModelDao’s abstract getActualClass method.  Note that it simply returns Employee.class.  That’s all that is needed.  Do note however that the parameter to the class, and the result from getActualClass must be the same.  In other words T == getActualClass().
  • Line 14: End of the class definition.

Since T in MappedModelDao is now equal to Employee, findById is now logically this:

Logical view of EmployeeDao.getById(String) [not source code]

public Employee findById(String id) {
    Session session = sessionFactory.getCurrentSession();
    return (Employee)session.get(Employee.class, id);
}

Now you can see why we used the MappedModel and MappedModelDao classes.  They greatly reduce the work to add new classes that are managed by Hibernate.  We don’t have to write save, delete, findById or findAll functions for any class that extends MappedModel (and has a MappedModelDao).  Cool, eh?

The DepartmentDao is almost as easy.  We do need one additional step to handle the “owned” side of our ManyToMany relationship on a delete.  But that is good, as it gives us a chance to discuss more about lazy initialization.

/src/main/java/com/technologicaloddity/departments/dao/DepartmentDao.java

package com.technologicaloddity.departments.dao;

import java.util.*;

import org.hibernate.*;
import org.springframework.stereotype.*;

import com.technologicaloddity.departments.model.*;

@Component
public class DepartmentDao extends MappedModelDao<Department> {

    @Override
    public Class<Department> getActualClass() {
        return Department.class;
    }

    @Override
    public void delete(Department entity) {
        removeEmployeesFromDepartment(entity);
        super.delete(entity);
    }

    private void removeEmployeesFromDepartment(Department department) {
        Session session = sessionFactory.getCurrentSession();

        Set<Employee> employees = department.getEmployees();
        if(!Hibernate.isInitialized(employees)) {
            session.lock(department, LockMode.NONE);
            Hibernate.initialize(employees);
        }

        if(employees != null) {
            for(Employee employee : employees) {
                Set<Department> departments = employee.getDepartments();
                departments.remove(department);
                employee.setDepartments(departments);
                session.saveOrUpdate(employee);
            }
        }

    }

}

Here’s the line-by-line for DepartmentDao:

  • Lines 1-8: Package statement and imports.  Note that unlike EmployeeDao, we are going to need org.hibernate.* this time, since we will be using a Session object later in the delete method.
  • Lines 10-11: Again we declare this DAO as a @Component, so that it can be auto-wired into other classes.  Also, DepartmentDao extends MappedModelDao<Department>, making T == Department in this example.
  • Lines 13-16: Just like EmployeeDao, we need to implement getActualClass() and return Department.class to tell the DAO which kinds of objects it is managing.  Remember, getActualClass() == T == Department for this DAO.
  • Lines 18-22: Unlike EmployeeDao, we are going to override MappedModelDAO’s delete function here.  Why? Since we are using JPA annotations to define a ManyToMany relationship between Employee and Department, JPA will manage referential integrity on the “owner” side only (that’s just the way it works).  The “owned” side has to handle referential integrity itself.  If you remember from the model section, Employee is the “owner” side, so we didn’t have to do anything in the EmployeeDao.  The “owned” side is Department, so we have to handle it here.  What does this mean in plain English? We have to make sure to remove all Employees from a Department before we delete it.  This is a two-step process: we have to remove the Department from any Employees that have it, then we have to empty the Employees Set for the Department we are trying to delete.  The removeEmployeesFromDepartment method will handle modifying the Employee objects for us (we’ll look at it next).  Removing the Department from each Employee will remove the link in the JoinTable employee_department (defined in Employee, remember?) and thus preserve referential integrity.  After this is done, we can delete the Department as normal, calling super.delete().  If all this is confusing, I suggest you try removing the delete method in DepartmentDao and run the webapp (once we have finished).  Create a department, assign an employee to the department, then try to delete the department.  It will fail, and the error message will tell you it is due to referential integrity.  Remember that JPA will only manage the references of the “owner” side of ManyToMany relationship!
  • Line 24: Here is some meaty stuff.  The removeEmployeesFromDepartment method needs to remove the given Department from all Employees’ Set<Department> property.  Should be easy right?  Just call getEmployee() on Department, iterate over the Employees and remove the Department from each one.  BZZZZZT!  What’s the problem?  The employees property of Department is lazy initialized (remember?  from Department).  That means that it isn’t really there in the Hibernate session, and therefore can’t be used inside a loop (or you will see a LazyInitialization exception). So how do we initialize something like employees after the fact?  Read on….
  • Line 25: Since we need to get the Set<Employee> into the current session, we naturally will need to call getCurrentSession to retrieve the current session.
  • Line 27: Huh?  Didn’t I just say that getEmployees() would fail with an exception?  Well, this isn’t 100% true.  getEmployees() itself can be called, but it won’t return what you expect.  Instead of a Set of Employee objects, what you will get back is a Hibernate Proxy object.  The Proxy object basically says, “Yeah, OK, we know that getEmployees exists, but we didn’t load the actual Employees, because it was lazy”.  If you try something like looping with getEmployees, such as “for(Employee employee: department.getEmployees())”, it will fail, and you will see the bane of all Hibernate users, the LazyInitialization exception.  So, remember that you can get a Proxy object for lazy loaded fields, but don’t try to use their value until it is initialized in the current session.  This is an important concept, so don’t gloss over it, or lazy initialization will make your Hibernate life torturous.
  • Line 28: So, how do we know that some other piece of code didn’t initialize the Set<Employee> already?  Well, we can check here.  The Hibernate object has a static function called isInitialized that, as you would expect, returns true or false depending on if the object passed in (in this case, the result of getEmployees()) has been initialized.  If employees has already been initialized (presumably by some other piece of code somewhere), that we can skip that step.
  • Line 29: When an object like Department is in a Hibernate Session, it may be “locked” by the Session.  This can happen, for example, with a pending transaction (like saveOrUpdate).  To be on the safe side, we are going to tell Hibernate to change the locking mode to NONE and associate the object with the current session.  Using LockMode.NONE will work in most cases, unless your application is highly transaction based.  But for our application, LockMode.NONE works just fine.  Note that the lock is on the Department object not on the employees property.
  • Line 30: Aha.. here is where we actually initialize the employees property.  Hibernate sees that employees is a Proxy object, and when we request an initialize(), it actual fills in the Set with real Employee objects so that we can actually use them as Employees.  Initialize is the real trick to using lazy initialization.  You’ll likely see it implemented 1000 ways, but somewhere deep down in the code is an initialize statement.
  • Line 33-40: This is a fairly standard Java loop over the Set<Employee>.  For each Employee, we get the Set<Department> (they aren’t lazy on the Employee side, remember?), remove the current Department (the one we eventually want to delete), and then persist the Employee back to the database with a saveOrUpdate().  Note that when saveOrUpdate completes, the row in the JoinTable (“employee_department”) will be removed to the Department we want to delete.  After we do this for all Employees that have the Department, we return and the Department is deleted the normal way in delete().

20 thoughts on “Spring and Hibernate with Annotations”

  1. Excellent tutorial! Very high quality, it all works beautifully, and very well explained. Thanks for putting it all together. I’ve tried some other similar tutorials before this but none worked. I was using Spring STS as my dev env, and it was fine, with a little change in the initial project setup.

    Only very minor comment is that jsp directory is not usually inside WEB-INF, but alonside it in webapp directory. As I said, this is very minor, viewed against a small mountain of very well designed/executed code.

    All the best
    Noel

    1. Noel,

      Thanks for the kind comments.

      On the matter of the location of the JSP directory, the developers of Spring use the WEB-INF/jsp location themselves (see http://static.springsource.org/spring/docs/2.5.x/reference/mvc.html#mvc-viewresolver ).

      The reason is for security. End users cannot directly reference any file under the WEB-INF directory (with something like http://yoursite.com/webapp/WEB-INF/foo.jsp ) as this produces an error. However, if you use only /jsp, they can reference http://yoursite.com/webapp/jsp/foo.jsp , in the same way they can access http://yoursite.com/webapp/images/logo.gif .

      Using WEB-INF/jsp allows you to maintain complete control of which JSP is shown when, and keeps the script-kiddies from poking around so much.

      Bob

      1. Hi Bob,
        your explanation about why you put the jsp directory into the WEB-INF directory sounds entirely reasonable to me – thanks for that.

        Noel

  2. Wow! Great Tutorial. I’m just in the process of absorbing everything now. The “step by step” code explanation is great for beginners like me. This has got to be the best maven+spring+hibernate tutorial I have come across yet (and I must have seen them all)

    1. Also, it wasn’t working for me at first. I was getting errors like:

      annotations are not supported in -source 1.3
      (use -source 5 or higher to enable annotations)

      I found that Maven’s default java compiler version is 1.3, and even if you don’t have it installed, you’ll still get the above errors unless you explicitly change this by adding the following code within in pom.xml:

      org.apache.maven.plugins
      maven-compiler-plugin
      2.3.2

      1.5
      1.5

        1. Deepak,

          You are 100% correct. You need to specify source and target 1.5 as you have shown above. I have updated the tutorial and source files to reflect this.

          Good catch, thanks!

          Bob

  3. Great tutorial and I learned a lot from this

    I made some changes (Oracle / combined jdbc.properties into the Spring config file etc) and it all works fine

    Then I tried to add some unit tests, but changed the MappedModelDao to implement an interface (extracted through Eclipse) – as explained in
    http://zenoconsulting.wikidot.com/blog:8#toc9

    As soon as I did this, and rebuilt the war, it failed to start in Tomcat with the following exception:

    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘deletionController’: Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.technologicaloddity.departments.dao.DepartmentDao com.technologicaloddity.departments.controller.DeletionController.departmentDao; nested exception is java.lang.IllegalArgumentException: Can not set com.technologicaloddity.departments.dao.DepartmentDao field com.technologicaloddity.departments.controller.DeletionController.departmentDao to $Proxy21
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:283)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1055)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:289)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:286)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:188)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:558)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416)
    at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:261)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:192)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4701)
    at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5204)
    at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5199)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.technologicaloddity.departments.dao.DepartmentDao com.technologicaloddity.departments.controller.DeletionController.departmentDao; nested exception is java.lang.IllegalArgumentException: Can not set com.technologicaloddity.departments.dao.DepartmentDao field com.technologicaloddity.departments.controller.DeletionController.departmentDao to $Proxy21
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:504)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:280)
    … 21 more
    Caused by: java.lang.IllegalArgumentException: Can not set com.technologicaloddity.departments.dao.DepartmentDao field com.technologicaloddity.departments.controller.DeletionController.departmentDao to $Proxy21
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63)
    at java.lang.reflect.Field.set(Field.java:657)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:500)
    … 23 more

    Also, I got a similar exception when trying to run a simple JUnit test in Eclipse
    My Spring knowledge is limited, so any help as to what the problem might be would be appreciated
    Thanks

  4. it works with postgres with quite minimal changes, just add the dependency for the jdbc connector for postgres to pom.xml and change jdbc driver class(dispatch-servlet.xml) to postgres(org.postgresql.Driver) and the db url(to something like jdbc:postgresql://localhost:5432/database ).

    great tutorial, better than most other spring and hibernate tutorials on the net! simple working approach that doesn’t need table creation by hand(so simpler to adapt to postgresql).

  5. Hi,

    This is probably one of the best tutorial that I came across.

    Just one thing, why there is no service layer added in the code or shown. Most of the enterprise applications do have a Service Layer for interacting with DB, I guess.

    Thanks & Regards
    Sunil

  6. One more thing, I have to include the servlet dependency in pom file otherwise the project was not getting run and giving error as “Cannot find class file for javax/servlet/ServletContext”.

    Regards
    Sunil

  7. U truly created a number of outstanding stuff within ur article, “Spring and Hibernate with Annotations | Technological
    Oddity”. I may possibly be coming to ur site soon.
    Thanks a lot ,Steve

  8. Now that I’ve just devoted six hours on your website reading your posts, I’m hooked on your blog.
    I bookmarked it as well so that I can keep up with it on a regular basis.
    Have a look at my web site too and tell me what you think.

  9. Heya i am for the first time here. I came across this board and I find It really useful & it helped me out a lot.
    I hope to give something back and aid others like you helped
    me.

  10. This is very interesting, You are a very skilled blogger. I have joined your
    rss feed and look forward to seeking more of your fantastic
    post. Also, I’ve shared your sitee in my social networks!

    1. Lakhan,

      I’m using a MySQL database. The database is automatically created by Hibernate, since “hibernate.hbm2ddl.auto = update” is in the hibernate.properties. So there is no SQL to show. If you have other questions, feel free to ask.

Leave a Reply

Your email address will not be published. Required fields are marked *