Meetu Maltiar's Blog

Meetu's thoughts on technology and software development

Working With Spring Data JPA

leave a comment »


SpringSource have recently released the first milestone of Spring Data JPA project. What are its features? and how can we use them. Spring framework already provides support to build a JPA based data access layer. So, what does Spring Data adds to it. I used it for one small application and found significant reduction of code. In this post we will use an Employee entity and EmployeeRepository as an example to work with Spring Data JPA.

We have a domain Employee :

@Entity
public class Employee {

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

  private String name;

  // … methods omitted
}

We also have a EmployeeRepository handling the Employee entities. The interface and the implementation are given below.

package com.inphina.springdata.repository;

public interface EmployeeRepository {
	
	Employee save(Employee employee);
	
	List<Employee> findAll();
	
	Employee findById(Long id);
	
	List<Employee> findByName(String name, int page, int pageSize);

}

@Repository
@Transactional(readOnly = true)
public class EmployeeRepositoryImpl implements EmployeeRepository {
	
	@PersistenceContext
	private EntityManager entityManager;

	@Override
	@Transactional
	public Employee save(Employee employee) {
		if(employee.getId() == null) {
			entityManager.persist(employee);
		} else {
			entityManager.merge(employee);
		}
		return employee;
	}

	@Override
	public List<Employee> findAll() {
		return entityManager.createQuery("select e from Employee e", Employee.class).getResultList();
	}

	@Override
	public Employee findById(Long id) {
		return entityManager.find(Employee.class, id);
	}

	@Override
	public List<Employee> findByName(String name, int page, int pageSize) {
		TypedQuery<Employee> query = entityManager.createQuery("select e from Employee e where e.name = ?1", Employee.class);
		query.setParameter(1, name);
		query.setFirstResult(page * pageSize);
		query.setMaxResults(pageSize);
		return query.getResultList();
	}
}

the client need to decide whether to call merge(…) or persist(…) on the EntityManager. We use the id-field of the Employee to decide whether we consider an Employee object as new or not.

Spring Data JPA provides a repository programming model that starts with an interface per managed domain object. That means for the Employee domain there is an Interface such as EmployeeRepository .

@Transactional(readOnly = true)
public interface EmployeeRepository extends JpaRepository<Employee, Long> { ... }

by extending JpaRepository we get generic CRUD methods into our type that allows working with Employees. This also allow the Spring Data JPA repository infrastructure to scan the classpath for this interface and create a Spring bean for it.

To have Spring create a bean that implements this interface, we need to use the Spring JPA namespace and activate the repository support using the appropriate element:

<jpa:repositories base-package="com.inphina.springdata" />

This scans all the package below com.inphina.springdata for interfaces extending JpaRepository and creates a spring bean backed by an implementation of SimpleJpaRepository

Now lets use a bit of Spring Data help in reducing the EmployeeRepositoryImpl code. We change the interface EmployeeRepository to this:

public interface EmployeeRepository extends JpaRepository<Employee, Long>{
	Employee save(Employee employee);

	List<Employee> findByName(String name, int page, int pageSize);
}

The EmployeeRepositoryImpl can then implement findByName(String name, int page, int pageSize) and Employee save(Employee employee);
as shown in the code below.

@Transactional(readOnly = true)
public class EmployeeRepositoryImpl implements EmployeeRepository {
	
	@Autowired
	private EmployeeRepository repository;

      @Override
	public Employee save(Employee employee) {
		return repository.save(employee);
	}

      @Override
	public List<Employee> findByName(String name, int page, int pageSize) {
		return repository.findByName(name, page, pageSize);
	}
}

Here we simply delegate the call to save(…) to the repository. By default the repository implementation considers an entity new if its id property is null similar to the first code listing of EmployeeRepositoryImpl

Spring Data JPA provides an abstraction consisting of two interfaces: Pageable (to capture pagination request information) as well as Page (to capture the result as well as meta-information). We can add the findByName in the interface itself. The need for the interface implementation is not required now. This is how the interface will look after the changes.

@Transactional(readOnly = true)
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

	/**
	 * Returns a Page of {@link Employee}s with the given name.
	 * @param name
	 * @param page
	 * @param pageSize
	 * @return
	 */
	Page<Employee> findByName(String name, int page, int pageSize);
}

To summarize we have seen that there is significant reduction of the code. After refactoring what remains is one interface and a XML declaration. Spring Data JPA does looks similar to Hades where simple queries can be generated from method names. However we might come to see integration of QueryDsl in near future.

Written by Meetu Maltiar

March 15, 2011 at 17:22

Posted in Java, spring

Tagged with ,

Leave a comment