Enterprise Java

Spring – Persistence layer – writing entities and configuring Hibernate

Welcome to the second part of this tutorial. Don’t freak out when you see how long this article is – I promise you it’s mostly easy POJOs and some generated code.

Before we start, we need to update our Maven dependencies, because we will be using Hibernate and Spring now. Add following dependencies to your pom.xml:

  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>3.6.8.Final</version>
  </dependency>
                <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.6</version>
  </dependency>
  
  <!-- spring framework -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
                <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
                <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>

If you’re new to Maven you might be wondering now – how do you know these? Where can I get them? Well, just go to http://mvnrepository.com/ and type what your ale looking for. You will get complete code for maven dependencies. If you have ever tried to assemble Spring or Hibernate application yourself without using Maven, you probably know how painful it was. With Maven things are so much easier.

Also note, that we have included dependency to MySQL’s connector. If you’ve decided to use other database, don’t forget to change this.

With Hibernate we have 2 options how to turn our POJOs to entites. Either we use XML and create mapping files, or we will put some meta information to our code (java annotations). Some people are afraid of those and consider this as coupling with framework. It is true that you will need javax.persistence annotations at your classpath, but we won’t be implementing interfaces or extending framework classes. We will just add some meta information to our code and POJOs will still be simply POJOs with some extra information.

We will turn our POJOs to entities now. We will need to do following changes:

  • Add default no-args constructor for Hibernate
  • Create getters and setters for fields
  • add equals and hashCode methods.
  • Add persistence annotations. Note that we also use @Table annotation to distinguish between Java and SQL naming conventions.
  • Add id fields. These will be primary keys in our relational database.

This is quite lot of boilerplate code, so let your IDE help you. Most modern IDEs will generate construcors, getters, setters, equals and hashCode for you.

package org.timesheet.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = 'employee')
public class Employee {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 private String name;
 private String department;
 
 public Employee() {
 }

 public Employee(String name, String department) {
  this.name = name;
  this.department = department;
 }
 
 public String getName() {
  return name;
 }
 
 public String getDepartment() {
  return department;
 }
 
 public Long getId() {
  return id;
 }
 
 public void setId(Long id) {
  this.id = id;
 }

 public void setName(String name) {
  this.name = name;
 }

 public void setDepartment(String department) {
  this.department = department;
 }

 @Override
 public String toString() {
  return 'Employee [id=' + id + ', name=' + name + ', department='
    + department + ']';
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result
    + ((department == null) ? 0 : department.hashCode());
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
   return true;
  }
  if (obj == null) {
   return false;
  }
  if (!(obj instanceof Employee)) {
   return false;
  }
  Employee other = (Employee) obj;
  if (department == null) {
   if (other.department != null) {
    return false;
   }
  } else if (!department.equals(other.department)) {
   return false;
  }
  if (id == null) {
   if (other.id != null) {
    return false;
   }
  } else if (!id.equals(other.id)) {
   return false;
  }
  if (name == null) {
   if (other.name != null) {
    return false;
   }
  } else if (!name.equals(other.name)) {
   return false;
  }
  return true;
 }
 
}
package org.timesheet.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = 'manager')
public class Manager {
 
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 private String name;
 
 public Manager() {
 }

 public Manager(String name) {
  this.name = name;
 }
 
 public String getName() {
  return name;
 }

 public Long getId() {
  return id;
 }

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

 public void setName(String name) {
  this.name = name;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
   return true;
  }
  if (obj == null) {
   return false;
  }
  if (!(obj instanceof Manager)) {
   return false;
  }
  Manager other = (Manager) obj;
  if (id == null) {
   if (other.id != null) {
    return false;
   }
  } else if (!id.equals(other.id)) {
   return false;
  }
  if (name == null) {
   if (other.name != null) {
    return false;
   }
  } else if (!name.equals(other.name)) {
   return false;
  }
  return true;
 }

 @Override
 public String toString() {
  return 'Manager [id=' + id + ', name=' + name + ']';
 }
 
}
package org.timesheet.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name='timesheet')
public class Timesheet {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;
 
 @OneToOne
 @JoinColumn(name = 'employee_id')
 private Employee who;
 
 @OneToOne
 @JoinColumn(name = 'task_id')
 private Task task;
 private Integer hours;
 
 public Timesheet() {
 }
 
 public Timesheet(Employee who, Task task, Integer hours) {
  this.who = who;
  this.task = task;
  this.hours = hours;
 }

 public Employee getWho() {
  return who;
 }

 public Task getTask() {
  return task;
 }
 
 public Integer getHours() {
  return hours;
 }
 
 public Long getId() {
  return id;
 }

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

 public void setWho(Employee who) {
  this.who = who;
 }

 public void setTask(Task task) {
  this.task = task;
 }

 public void setHours(Integer hours) {
  this.hours = hours;
 }

 /**
  * Manager can alter hours before closing task
  * @param hours New amount of hours
  */
 public void alterHours(Integer hours) {
  this.hours = hours;
 }

 @Override
 public String toString() {
  return 'Timesheet [id=' + id + ', who=' + who + ', task=' + task
    + ', hours=' + hours + ']';
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((hours == null) ? 0 : hours.hashCode());
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((task == null) ? 0 : task.hashCode());
  result = prime * result + ((who == null) ? 0 : who.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
   return true;
  }
  if (obj == null) {
   return false;
  }
  if (!(obj instanceof Timesheet)) {
   return false;
  }
  Timesheet other = (Timesheet) obj;
  if (hours == null) {
   if (other.hours != null) {
    return false;
   }
  } else if (!hours.equals(other.hours)) {
   return false;
  }
  if (id == null) {
   if (other.id != null) {
    return false;
   }
  } else if (!id.equals(other.id)) {
   return false;
  }
  if (task == null) {
   if (other.task != null) {
    return false;
   }
  } else if (!task.equals(other.task)) {
   return false;
  }
  if (who == null) {
   if (other.who != null) {
    return false;
   }
  } else if (!who.equals(other.who)) {
   return false;
  }
  return true;
 }
}

And finally, here’s Task entity when we needed to use also @ManyToMany mapping. That’s because one employee can work on multiple tasks and one task can have assigned multiple employees. We’ve defined how our m:n will look like, using @JoinTable and @JoinColumn annotations.

package org.timesheet.domain;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Entity
@Table(name = 'task')
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = 'task_employee',
            joinColumns = {@JoinColumn(name = 'task_id')},
            inverseJoinColumns = {@JoinColumn(name = 'employee_id')}
    )
    private List<Employee> assignedEmployees = new ArrayList<Employee>();

    @OneToOne
    @JoinColumn(name = 'manager_id')
    private Manager manager;

    private String description;
    boolean completed;

    public Task() {
    }

    public Task(String description, Manager manager, Employee... employees) {
        this.description = description;
        this.manager = manager;
        assignedEmployees.addAll(Arrays.asList(employees));
        completed = false;
    }

    public Manager getManager() {
        return manager;
    }

    public List<Employee> getAssignedEmployees() {
        return assignedEmployees;
    }

    public void addEmployee(Employee e) {
        assignedEmployees.add(e);
    }

    public void removeEmployee(Employee e) {
        assignedEmployees.remove(e);
    }

    public Long getId() {
        return id;
    }

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

    public boolean isCompleted() {
        return completed;
    }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }

    public void setAssignedEmployees(List<Employee> assignedEmployees) {
        this.assignedEmployees = assignedEmployees;
    }

    public void setManager(Manager manager) {
        this.manager = manager;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Task task = (Task) o;

        if (completed != task.completed) {
            return false;
        }
        if (description != null ? !description.equals(task.description) : task.description != null) {
            return false;
        }
        if (id != null ? !id.equals(task.id) : task.id != null) {
            return false;
        }
        if (manager != null ? !manager.equals(task.manager) : task.manager != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (manager != null ? manager.hashCode() : 0);
        result = 31 * result + (description != null ? description.hashCode() : 0);
        result = 31 * result + (completed ? 1 : 0);
        return result;
    }

    @Override
    public String toString() {
        return 'Task{' +
                'id=' + id +
                ', assignedEmployees=' + assignedEmployees +
                ', manager=' + manager +
                ', description='' + description + '\'' +
                ', completed=' + completed +
                '}';
    }
}

So we didn’t really do anything special to model. If you fancy some UML look at the following picture, relations are same as before.

Okay we have entites, now let’s create database. Pick some tool for database management (even plain terminal is fine) and create timesheet database like so (by default mysql will install to /usr/local/mysql/bin/mysql at Mac OS X):

$ mysql -u root
mysql > create database timesheet;

If you’ve ever configured Hibernate before you probably know, that you need quite many files and boilerplate code when dealing for example with SessionFactory. These things are much simpler with Spring.

We will now create our first Spring Bean Configuration File – it’s file where we register beans for Spring container. If I had to explain what’s this file to someone who doesn’t know what Spring is at all – it’s kind of magic bag where Spring container can find objects.

Modern IDEs will help you get all the XML namespaces right, for example you can see pictures from STS wizard. NetBeans has something similar and IntelliJ resolves namespaces on the fly.

Name configuration file persistence-beans.xml and we will put it under src/main/resources folder.

So setting up Hibernate, transactions, annotation config and so on is as simple as creating few beans in XML file. Alternatively, we can use Java Config for Spring, but XML configs are still used much more, so we’ll stick to those. I don’t want to discourage you from using Java Config though! XML config is much much more popular at this moment, but I can’t guarantee that for the next few years.
I’ve commented every bean to make sure you understand what we did there before proceeding. If you want to get some visual grasp of connections between beans you can again use some tooling – in STS it’s called Bean Graph, in IntelliJ it’s Dependencies. You can see sample of dependencies on the picture below.

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns='http://www.springframework.org/schema/beans'
 xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
 xmlns:context='http://www.springframework.org/schema/context'
 xmlns:tx='http://www.springframework.org/schema/tx'
 xsi:schemaLocation='http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.1.xsd
  http://www.springframework.org/schema/tx 
  http://www.springframework.org/schema/tx/spring-tx-3.1.xsd'>
 
 <!-- we can use annotations -->
 <context:annotation-config />  
 
 <!-- package to look for annotated classes -->
 <context:component-scan base-package='org.timesheet.service.impl' />
 
 <!-- we will manage transactions with annotations -->
 <tx:annotation-driven />

 <!-- data source for our database -->
 <bean id='dataSource' 
  class='org.springframework.jdbc.datasource.DriverManagerDataSource'>
  <property name='driverClassName' 
   value='com.mysql.jdbc.jdbc2.optional.MysqlDataSource' />
  <property name='url' value='jdbc:mysql://localhost/timesheet' />
  <property name='username' value='root' />
  <property name='password' value='' />
 </bean>
 
 <!-- configure hibernate session factory -->
 <bean id='sessionFactory'
  class='org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean'>
  <property name='dataSource' ref='dataSource' />
  <property name='annotatedClasses' >
   <list>
    <value>org.timesheet.domain.Employee</value>
    <value>org.timesheet.domain.Manager</value>
    <value>org.timesheet.domain.Task</value>
    <value>org.timesheet.domain.Timesheet</value>
   </list>
  </property>
  <property name='hibernateProperties'>
   <props>
    <prop key='dialect'>org.hibernate.dialect.MySQL5InnoDBDialect</prop>
    <prop key='hibernate.show_sql'>true</prop>
    <prop key='hibernate.hbm2ddl.auto'>update</prop>
   </props>
  </property> 
 </bean>
 
</beans>

Okay, that was quite lot of configuration, he? What’s not so good is that we’ve placed names of our entities to XML as plain text, so it isn’t refactoring friendly. But I think for this tutorial it’s acceptable :) Let’s write integration test for Hibernate so we know that everything is set up properly.

package org.timesheet.integration;

import static org.junit.Assert.*;

import org.hibernate.SessionFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

@ContextConfiguration(locations = '/persistence-beans.xml')
public class HibernateConfigurationTest extends AbstractJUnit4SpringContextTests {
 
 @Autowired
 private SessionFactory sessionFactory;

 @Test
 public void testHibernateConfiguration() {
  // Spring IOC container instantiated and prepared sessionFactory
  assertNotNull (sessionFactory); 
 }

}

I want you to note 2 things here. First, we extend AbstractJUnit4SpringContextTests class. We tell it where it should look for actual XML config with spring beans. Otherwise we would have to create Spring container by ourselves, which means more boilerplate code.

Second, we use @Autowired annotation. That means we don’t create instance of SessionFactory by hand using new operator, but we will have it Autowired (Injected) by Spring container! That’s one of the most important purposes of Spring container – depend on interfaces and have implementations injected instead of creating them by hand.
Everything should work now and I think that’s enough for this part.

If you like you can check plain SQL, and see tables are here, do it like so:

mysql> use timesheet;
mysql> show tables;
+---------------------+
| Tables_in_timesheet |
+---------------------+
| employee            |
| manager             |
| task                |
| task_employee       |
| timesheet           |
+---------------------+
5 rows in set (0.00 sec)

Reference: Part 2 – Persistence layer – writing entities and configuring Hibernate from our JCG partner Michal Vrtiak at the vrtoonjava blog.

Michal Vrtiak

Michal is a freelancer currently located in Prague, Czech Republic with huge passion for Java platform. He is very enthusiastic about Dependency Injection, IntelliJ IDEA and loves to use both Spring and Java EE.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Eran
Eran
12 years ago

Can you please post a link to part 1 of this tutorial

“Welcome to the second part of this tutorial …”

10x

Byron Kiourtzoglou
12 years ago
Reply to  Eran

Hello Eran, you can find all parts of the tutorial at our archive section down at the bottom of our site. We usually post all the parts of a multi-part tutorial at once so you can find them at the archived tree grouped together.

BRs

Oleksiy Rezchykov
Oleksiy Rezchykov
12 years ago

Hello Nikos, just two short comments. You have used Fetch.EAGER with @ManyToMany. I think this shouldn’t be done unless it is really needed. I think this could be explained in more details, especially for beginners. My second point is about extending AbstractJUnit4SpringContextTests. Why can’t you use @RunWith annotation for the same purpose?

Back to top button