Connect to us ( @twitter | @facebook )
In this tutorial i am going to explain how to use query methods and custom repository implementation in Spring Data JPA framework. I have used Primfaces and JSF to develop the front end to access data using Spring Data infrastructure.
When you’re developing a Spring Data Repository, you’ll have three options:
- Using of a CRUD operations that implemented by the Spring Data infrastructure
- Defining of a query methods and
- Manually implementing your own custom repositories.
The following are the sections covered in this tutorial.
- Introduction
- Query Methods
- Spring Data Custom Repository
- Spring Configurations
- Persistence Entities
- Spring Service
- Primefaces / JSF Managed Bean
- JSF Configurations
- Maven Build
- Spring Data + Custom Repository + Query Methods Demo
- Database Table Queries
- Summary
- Download Source Code
1. Introduction
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.
2. 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:
3. 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.
4. Spring Configurations
Here is the complete 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.
5. 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; } }
6. 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; } }
7. 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())); } }
8. JSF Configurations
It’s faces configuration file that used for configuring the JSF framework in this tutorial.
- Also Read : Introduction to JSF
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>
9. Maven Build
Here is the complete maven build file for downloading dependencies used for this tutorial.
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>
10. Spring Data + Custom Repository + Query Methods Demo
11. Database Tables Queries
Here is the database table creation and data population scripts used for populating sample data for this tutorial.
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;
12. Summary
In this tutorial I have 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. Please download the complete source code in the next section.