Spring Data Query Methods & Custom Repository Implementation

When you’re developing a Spring Data Repository, you’ll have three options:

  1. Using of a CRUD operations that implemented by the Spring Data infrastructure
  2. Defining of a query methods and
  3. Manually implementing your own custom repositories.

In our previous tutorials, we have explained the first option, in that, a vast amount of tutorials had used the infrastructure repositories. We have not explored any samples for the query methods and custom repository in our previous tutorials. In this tutorial, you would see how could we implement a Query methods and how to implement a custom repositories in spring data.

Spring Data provides the developers ability to define their own custom queries by declaring a method. The method declaration will be inspected by the infrastructure and parsed, and a store-specific query was derived eventually. However, as the queries become more complex, the method names would become awkwardly, until you’ve reached into level the keywords supported by the method parser wouldn’t even suffice. Thus, Spring Data provides you a special annotation @Query that would take a query as a string that formed in the store-specific query language. In this tutorial, we would use the MySQL as a persistent store, thus, JPA (Java Persistence API) & JPQL for defining the mapping and queries.

1. Query Methods

The query derivation mechanism built into the Spring Data repository infrastructure is useful to build constraining queries over entities of the repository. The below points represent the major points for creating a query method in your own repository.

  • Start the method name with one of the prefixes findBy, readBy and getBy.
  • Complete the method name by adding one or more of the entity’s properties and concatenate them with And and Or.
  • Operators like Between, LessThan, GreaterThan & Like are also applicable.
  • Define a nested properties is applicable (Employees have addresses).
  • Once you’ve finished writing your own query methods, a compilation error time could be raised if your query couldn’t be parsed.

Find below EmployeeRepository that we’ve defined in our previous tutorials. This time the repository has a set of query methods which derived from the Employee entity.

At the same time, a not valid query methods cause you will find an error like below:

Spring Data - Not Valid Query Methods

2. Spring Data Custom Repository

Until now, you’ve created repositories that exposed a CRUD methods or a query methods. Both types are implemented by the Spring Data infrastructure, either by a backing implementation (Repository proxy) or the query execution engine (Query Derivation). These two cases will probably cover a broad range of data access operations you’ll face when building applications. Often, it is necessary to provide a custom implementation for a certain repository methods.

Spring Data provides you an intelligent way to integrate your custom repository code and integrate it with the generic CRUD abstraction and query method functionality.

To enrich a repository with custom functionality you first define an interface and an implementation for the custom functionality. Use the repository interface you provided to extend the custom interface.

EmployeeRepositoryCustom.java

package net.javabeat.springdata.repo;

import java.util.List;

import net.javabeat.springdata.jpa.data.Employee;

public interface EmployeeRepositoryCustom {

	public List<Employee> readByEmployeeName(String name);

}

EmployeeRepositoryImpl.java

package net.javabeat.springdata.repo;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import net.javabeat.springdata.jpa.data.Employee;

public class EmployeeRepositoryImpl implements EmployeeRepositoryCustom{
	@PersistenceContext
	private EntityManager entityManager;

	@SuppressWarnings("unchecked")
	@Override
	public List<Employee> readByEmployeeName(String name) {
		return this.entityManager.
				createQuery("select e from Employee e where e.employeeName like '"+name+"'").
					getResultList();
	}

}

Your custom repository as you’ve seen doesn’t depend on the Spring Data, it’s like a normal Spring Bean. So it’s possible to contain an injection aspect or any other aspect you want supported by Spring framework. As you’ve noted the entityManager has been injected into our custom implementation.

After finished your custom repository and its implementation, it’s the time for you to upgrade your abstract repository (EmployeeRepository) so that it contains the custom behaviors. Just makes it extends another interface as below.

EmployeeRepository.java

package net.javabeat.springdata.repo;

import java.util.List;

import net.javabeat.springdata.jpa.data.Employee;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>,EmployeeRepositoryCustom{

	public List<Employee> findByEmployeeName(String name);
	public List<Employee> getByEmployeeId(Integer id);
	public List<Employee> findByEmployeeNameAndEmployeeId(String name, Integer id);
	public List<Employee> findByEmployeeIdGreaterThan(Integer id);
	public List<Employee> findByEmployeeIdBetween(Integer lowerValue, Integer upperValue);
	public List<Employee> findByAddressAddressCountry(String country);
	public List<Employee> findAll();
}

For now, you’ve created your own custom repository and it’s implementation and added your custom behaviors into your original repository. But a big question here is, how could your custom repository been detected by the Spring Data specifically, we’ve not used any type of annotations related to neither inside the interface nor inside the implementation,  except that responsible for injecting the persistence context.

That’s what Spring Data called the configuration of the custom repositories, in that, if you use namespace configuration (XML Spring Bean Definition), the repository infrastructure tries to auto detect custom implementations by scanning for classes below the package we found a repository in. These classes need to follow the naming convention of appending the namespace element’s attribute repository-impl-postfix to the found repository interface name. This postfix defaults to Impl and for that our custom repository named as EmployeeRepositoryImpl.

3. Spring Configurations

I would provide you a full of Spring namespace configuration, meanwhile i’ll provide you a fragment of how the form of repository-impl-postfix attribute could be.

SpringContext.xml

<?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:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

	<!-- For consider the using of annotations foe defining Spring Bean -->
	<context:annotation-config />

	<!-- For defining Spring Bean -->
	<context:component-scan base-package="net.javabeat.springdata.beans" />

	<!-- For bootstrapping the Spring Repository -->
	<jpa:repositories base-package="net.javabeat.springdata.repo" />

	<!-- Necessary to get the entity manager injected into the factory bean -->
	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

	<!-- Define EclipseLink JPA Vendor Adapter -->
	<bean id="jpaVendorAdapter"
		class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
		<property name="databasePlatform"
			value="org.eclipse.persistence.platform.database.MySQLPlatform" />
		<property name="generateDdl" value="false" />
		<property name="showSql" value="true" />
	</bean>

	<!-- Entity Manager Factory -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="SpringData"></property>
		<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
	</bean>

	<!-- Transaction Manager -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<!-- Enable Transactional Manner -->
	<tx:annotation-driven transaction-manager="transactionManager" />

</beans>

SpringContext.xml (Non defaulted postfix)

	<!-- For bootstrapping the Spring Repository -->
	<jpa:repositories base-package="net.javabeat.springdata.repo" repository-impl-postfix="DetectPostfixToken"/>

In case, you’ve provided the repository-impl-postfix attribute, you should comply when defining your custom repositories.

4. Persistence Entities

These are the entities used in this tutorial.

Address.java

package net.javabeat.springdata.jpa.data;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;

@Entity(name = "address")
public class Address {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer addressId;
	private String addressCountry = "";
	private String addressCity = "";

	@OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
	private Employee employee;

	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public Integer getAddressId() {
		return addressId;
	}

	public void setAddressId(Integer addressId) {
		this.addressId = addressId;
	}

	public String getAddressCountry() {
		return addressCountry;
	}

	public void setAddressCountry(String addressCountry) {
		this.addressCountry = addressCountry;
	}

	public String getAddressCity() {
		return addressCity;
	}

	public void setAddressCity(String addressCity) {
		this.addressCity = addressCity;
	}
}

Employee.java

package net.javabeat.springdata.jpa.data;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

@Entity
public class Employee {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer employeeId;

	@Basic(optional = false)
	private String employeeName;

	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "Address")
	private Address address = new Address();

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public Integer getEmployeeId() {
		return employeeId;
	}

	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}

	public String getEmployeeName() {
		return employeeName;
	}

	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}
}

5. Spring Service

It’s just a bean used for referencing both of Spring Data default repository and that custom one. All of those methods contained in the custom repository are also contained in the Spring Data default repository, but the service below presents the concept of autowiring those custom repositories.

RegistrationService.java

package net.javabeat.springdata.beans;

import net.javabeat.springdata.repo.EmployeeRepository;
import net.javabeat.springdata.repo.EmployeeRepositoryImpl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RegistrationService {
	@Autowired
	private EmployeeRepositoryImpl employeeRepositoryImpl;

	@Autowired
	private EmployeeRepository employeeRepository;

	public EmployeeRepositoryImpl getEmployeeRepositoryImpl() {
		return employeeRepositoryImpl;
	}

	public void setEmployeeRepositoryImpl(EmployeeRepositoryImpl employeeRepositoryImpl) {
		this.employeeRepositoryImpl = employeeRepositoryImpl;
	}

	public EmployeeRepository getEmployeeRepository() {
		return employeeRepository;
	}

	public void setEmployeeRepository(EmployeeRepository employeeRepository) {
		this.employeeRepository = employeeRepository;
	}

}

6. Primefaces / JSF Managed Bean

It’s a managed bean for handling the presentation logic.

RegistrationManagedBean.java

package net.javabeat.primefaces.managedbeans;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;

import net.javabeat.springdata.beans.RegistrationService;
import net.javabeat.springdata.jpa.data.Employee;

import com.google.common.collect.Lists;

@ManagedBean
@SessionScoped
public class RegistrationManagedBean {

	private Employee employee = new Employee();

	private List<Employee> employees = new ArrayList<Employee>();

	public RegistrationManagedBean(){
	}

	@PostConstruct
	public void fetchEmployees(){
		this.employees = Lists.newArrayList(this.service.getEmployeeRepository().findAll());
	}

	@ManagedProperty(value="#{registrationService}")
	private RegistrationService service;

	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public List<Employee> getEmployees() {
		return employees;
	}

	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}

	public RegistrationService getService() {
		return service;
	}

	public void setService(RegistrationService service) {
		this.service = service;
	}

	public String register(){
		this.service.getEmployeeRepository().save(this.employee);
		this.employees = this.service.getEmployeeRepository().findAll();
		this.employee = new Employee();
		return "";
	}

	public void findByEmployeeName(){
		this.employees = Lists.newArrayList(this.service.getEmployeeRepository().readByEmployeeName(this.employee.getEmployeeName()));
	}

	public void findByEmployeeAddressAddressCountry(){
		this.employees = Lists.newArrayList(this.service.getEmployeeRepository().findByAddressAddressCountry(this.employee.getAddress().getAddressCountry()));

	}

}

7. JSF Configurations

It’s faces configuration file that used to configure the JSF framework.

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
	version="2.2">
<application>
	<resource-bundle>
		<base-name>net.javabeat.jsf.application</base-name>
		<var>msg</var>
	</resource-bundle>
	<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</faces-config>

8. Maven Build

It’s a maven pom file that maintaining dependencies.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>next.javabeat.jsf</groupId>
	<artifactId>JavaBeat-Primefaces-SpringData-QueryMethods-ManualRepository</artifactId>
	<packaging>war</packaging>
	<version>1.0</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.7</java.version>
		<junit.version>4.9</junit.version>
		<slf4j.version>1.6.4</slf4j.version>
		<logback.version>1.0.1</logback.version>
		<log4j.version>1.2.14</log4j.version>

		<servlet.version>2.5</servlet.version>
		<jsp.version>2.1</jsp.version>
		<jstl.version>1.2</jstl.version>
		<taglibs-standard.version>1.1.2</taglibs-standard.version>

		<maven.compiler.plugin>2.3.2</maven.compiler.plugin>
		<maven.failsafe.plugin>2.4.3-alpha-1</maven.failsafe.plugin>

	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>prime-repo</id>
			<name>PrimeFaces Maven Repository</name>
			<url>http://repository.primefaces.org</url>
			<layout>default</layout>
		</repository>
	</repositories>

	<dependencies>
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>${servlet.version}</version>
			<scope>provided</scope>
		</dependency>
		<!-- Faces Implementation -->
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-impl</artifactId>
			<version>2.2.4</version>
		</dependency>
		<!-- Faces Library -->
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-api</artifactId>
			<version>2.2.4</version>
		</dependency>
		<!-- Primefaces Version 5 -->
		<dependency>
			<groupId>org.primefaces</groupId>
			<artifactId>primefaces</artifactId>
			<version>5.0.RC2</version>
		</dependency>
		<!-- JSP Library -->
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
		</dependency>
		<!-- JSTL Library -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.1.2</version>
		</dependency>
		<!-- Spring Core -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.0.3.RELEASE</version>
		</dependency>
		<!-- Spring Web -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.0.3.RELEASE</version>
		</dependency>
		<!-- Google List Library -->
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>r09</version>
		</dependency>
		<!-- Dependencies for Eclipse JPA Persistence API -->
		<dependency>
			<groupId>org.eclipse.persistence</groupId>
			<artifactId>eclipselink</artifactId>
			<version>2.5.0-RC1</version>
			<exclusions>
				<exclusion>
					<groupId>org.eclipse.persistence</groupId>
					<artifactId>commonj.sdo</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- Spring Data Dependency -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>1.5.2.RELEASE</version>
		</dependency> <!-- Dependency for MySql Java connector -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.30</version>
		</dependency>
	</dependencies>

</project>

9. Spring Data + Custom Repository + Query Methods Demo

Spring Data - Custom Repository - Query Derivation - Demo

Spring Data - Custom Repository - Query Derivation - Demo II

Spring Data - Custom Repository - Query Derivation - Demo III

10. Database Tables Queries

CREATE TABLE `address` (
  `addressId` bigint(20) NOT NULL AUTO_INCREMENT,
  `addressCountry` varchar(45) DEFAULT NULL,
  `addressCity` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`addressId`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
SELECT * FROM javabeat.employee;

CREATE TABLE `employee` (
  `employeeId` bigint(20) NOT NULL AUTO_INCREMENT,
  `employeeName` varchar(20) DEFAULT NULL,
  `address` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`employeeId`),
  KEY `FK_EMP_ADD` (`address`),
  CONSTRAINT `FK_EMP_ADD` FOREIGN KEY (`address`) REFERENCES `address` (`addressId`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

11. Summary

This tutorial explained how to use the query methods and custom repository in Spring Data. This is useful when you want to define your own repository for your application. By looking at the above explanation, implementing custom repository is simple for the spring data developers. If you have any questions, please write it in the comments section.

Download Source Code

Comments

comments

About Amr Mohammed

Speak Your Mind

*