Part 2 – 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>

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.
02_uml.png

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.

02_select_namespaces.png

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.

02_beans_dependencies.png

<?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)

In the next part of this tutorial, we will implement DAOs and services and write unit tests for them.

Back to main tutorial page

23 thoughts on “Part 2 – Persistence layer – writing entities and configuring Hibernate

  1. Pingback: Part 1 – Designing the domain model and the service layer | vrtoonjava

  2. Pingback: JavaPins

  3. Thanks, great tutorial!

    class=”org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean”

    it doesn’t work with Hibernate4, you can use:

    class=”org.springframework.orm.hibernate4.LocalSessionFactoryBean”

  4. First of all: thanks for this great tutorial. I am following to learn more Spring.
    I just got one question about this. I understand what you say when you refer to the plain text as not “refactor friendly”. So, what would be a way to avoid this?

    • There is a way of configuring JPA2 (with Hibernate for example) that will read entity names from @Entity annotations on your classes If you’re interested about that try to do some googling and you’ll be able to find some XML-free tutorials πŸ™‚

      With good IDE though (like IntelliJ) you can rename class and IDE will scan for usages of that class and should be smart enough to refactor its name in XML file as well.

      • Thanks for your quick reply.
        I did some searching and I found tutorials about Java Spring Configuration. Is that it?
        I can see that XML is more straightforward and an easy introduction, whereas annotations are a bit to complex to be taught to beginners.

      • Well there are two things going on here:

        1. There is a way to configure JPA to automatically scan for classes annotated with @Entity annotation. You do not need Spring for that.

        2. There is a way of configuring Spring with annotations only, no XML needed.

        While the former one might be more popular in real-world projects today, the latter one is getting more attention for newer Spring-based projects. Essentialy it is just a matter of taste. I personaly do not consider Java to be very good language for configurations (unlike Groovy or Ruby).

        Hope this helps a bit!

  5. Hello Sir, thank you for your tutorial, Where Can I download source code for this tutorial. please give me link
    List of chapters:

    Part 1 – Designing the domain model
    Part 2 – Persistence layer with Hibernate
    Part 3 – DAO and Service layer
    Part 4 – Spring MVC part 1
    Part 5 – Spring MVC part 2
    Part 6 – AOP support

    Please help sir

    iam waiting for your reply

    • Hey there, thanks! We did code-first approach here, so database was generated. Just create a “timesheet” schema in MySQL, build app (and run the tests) and tables are gonna get generated for you!

  6. Hi,
    I’m a beginner and i’m referring this to build my understanding.
    Could you please explain how to generate the tables and how is the database connected.
    Thanks.

Leave a reply to vrtoonjava Cancel reply