Spring Data JPA
Agenda
- Introduction
- Getting Required Dependencies
- Configuration
- CRUD
- Query Methods
- Creating Database Queries from Method Names
- @Query Annotation
- Named Queries
- JPA Criteria API
- Sorting
- Pagination
- Auditing
- Custom method in Repository
What is Spring Data JPA ?
Spring Data JPA is not a JPA provider. It is a library / framework that adds an extra layer of abstraction on the top of our JPA provider. If we decide to use Spring Data JPA, the repository layer of our application contains three layers that are described in the following:
- Spring Data JPA provides support for creating JPA repositories by extending the Spring Data repository interfaces.
- Spring Data Commons provides the infrastructure that is shared by the datastore specific Spring Data projects.
- The JPA Provider implements the Java Persistence API.
What components do we need ?
If we want to implement a persistence layer that uses Spring Data JPA, we need the following components:
- The JDBC driver provides a database specific implementation of the JDBC API.
- The JPA Provider implements the Java Persistence API. We use Hibernate because it is the most common JPA provider.
- Spring Data JPA hides the used JPA provider behind its repository abstraction.
build.gradle
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.hibernate:hibernate-entitymanager:4.1.7.Final'
compile 'mysql:mysql-connector-java:5.1.13'
compile 'org.springframework.data:spring-data-jpa:1.11.4.RELEASE'
}
Configuration
- We have to create configuration class that configures the persistence layer of our application:
@Configuration
class PersistenceContext {
//Configure the required beans here
}
- Configure the datasource bean.
@Bean
DataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/poc");
driverManagerDataSource.setUsername("root");
return driverManagerDataSource;
}
- Configure the entity manager factory bean.
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.springdata.entity");
Properties jpaProperties= new Properties();
jpaProperties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
jpaProperties.setProperty("hibernate.show_sql","true");
jpaProperties.setProperty("hibernate.hbm2ddl.auto","update");
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
- Configure the transaction manager bean.
@Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
- Enable Spring Data JPA by annotating the PersistenceContext class with the@EnableJpaRepositories annotation.
@Configuration
@EnableJpaRepositories("com.springdata.repositories")
class PersistenceContext {
//The beans are configured here
}
PersistenceContext.java
package com.springdata.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
@EnableJpaRepositories("com.springdata.repositories")
public class PersistenceContext {
@Bean
DataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/poc");
driverManagerDataSource.setUsername("root");
return driverManagerDataSource;
}
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.springdata.entity");
Properties jpaProperties= new Properties();
jpaProperties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQLDialect");
jpaProperties.setProperty("hibernate.show_sql","true");
jpaProperties.setProperty("hibernate.hbm2ddl.auto","update");
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
@Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
Creating an entity
We have to create an entity class that contains the information of a single Employee entry.
package com.springdata.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
@Entity
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private Integer age;
private Integer salary;
//getters and setters
}
We are now ready to create our first Spring Data JPA repository. We can create the repository that provides CRUD operations for Todo objects by using one of the following methods:
- Create an interface that extends the CrudRepository interface.
- Create an interface that extends the Repository interface and add the required methods to the created interface.
Creating a repository (Cont.)
Extending the CrudRepository Interface
If we create our repository by extending the CrudRepository interface, we have to provide two type parameters:
- The type of the entity that is managed by our repository.
- The type of the entity’s id field.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
}
Extending the Repository Interface
- Provide two type parameters managed entity (Employee) and the type of the entity’s id field (Integer).
- Add the required methods to the repository interface:
package com.springdata.repositories;
import java.util.List;
import java.util.Optional;
interface EmployeeRepository extends Repository<Employee, Integer> {
void delete(Employee deleted);
List<Employee> findAll();
Employee save(Employee persisted);
}
Exercise 1
- Create a Person entity with instance variables Firstname, Lastname, salary, age and Id.
- Implement CrudRepository for it.
- Perform all the methods inside CrudRepository for Person Class.
Query Methods
Query methods are methods that find information from the database and are declared on the repository interface.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
List<Employee> findByName(String name);
Employee findById(Integer id);
}
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
@Query("SELECT name from Employee where id=:id")
String findById(@Param("id") Integer id);
}
- Spring Data has pretty versatile support for different return values that we can leverage when we are adding query methods to our Spring Data JPA repositories.
- We can pass parameters to our database queries by using either position based parameter binding or named parameters.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface EmployeeRepository extends Repository<Employee, Integer> {
@Query("SELECT id,name from Employee where id=:id")
List<Object[]> findById(@Param("id") Integer id);
}
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
public interface EmployeeRepository extends Repository<Employee, Integer> {
@Query("SELECT e from Employee e where id=:id")
Employee findById(@Param("id") Integer id);
}
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
public interface EmployeeRepository extends Repository<Employee, Integer> {
@Query("SELECT Count(*) from Employee e where name=:name")
Integer countByName(@Param("name") String name);
}
Exercise 2
- For class Person find person declare methods in repository to find person by firstname, lastname and Id.
- Use the methods declared above to fetch the person.
- Use @Query to fetch firstname of the Person whose age is 25.
- Use @Query to fetch firstname and lastname of the Person whose age is 25.
- Get complete information of the Employee whose age is 25 using @Query.
- Count Person where name is "Peter" using @Query.
Creating Database queries from method name
- The name of our query method must start with one of the following prefixes: find…By, read…By, query…By, count…By, and get…By.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
int countById(Integer id);
}
- If we want to limit the number of returned query results, we can add the First or the Top keyword before the first By word
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
List<Employee> findFirstByName(String name);
List<Employee> findFirst3ByName(String name);
}
- If we want to select unique results, we have to add the Distinct keyword before the first By word. For example, findDistinctEmployeeByName means that we want to select all unique titles that are found from the database.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
Employee findDistinctByName(String name);
}
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByNameOrAge(String name, Integer age);
}
- Or Operation
- And Operation
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByNameAndAge(String name, Integer age);
}
- Between
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByAgeBetween(int min,int max);
}
- LessThan
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByAgeLessThan(int age);
}
GreaterThanEquals
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByAgeGreaterThanEqual(Integer age);
}
- Like
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByNameLike(String name);
}
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByNameNot(String name);
}
- Not
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByAgeIn(List<Integer> ageList);
}
- In
- IgnoreCase
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findByNameIgnoreCase(String name);
}
Exercise 3
Implement following methods for Person repository:
- count
- distinct
- or
- and
- between
- LessThan
- GreaterThan
- Like
- Not
- In
- IgnoreCase
Named Queries
If we want to create a SQL query, we must follow these steps:
- Annotate the entity with the @NamedNativeQuery annotation.
- Set the name of the named query (Employee.findByNameIs) as the value of the @NamedNativeQuery annotation's named attribute.
- Set the SQL Query as the value of @NamedNativeQuery annotation name attribute.
- Set the returned entity class as the value of @NamedNativeQuery resultClass attribute.
Named Queries
package com.springdata.entity;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@NamedNativeQuery(name = "Employee.findByNameIs",
query = "SELECT * from Employee where name = :name",
resultClass = Employee.class)
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private Integer age;
private Integer salary;
// getters and setters
}
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
List<Employee> findByNameIs(@Param("name") String name);
}
Named Queries
Exercise 4
Implement Named query to fetch an Employee Object for a firstname and lastname.
JPA Criteria API
We can create database queries with the JPA Criteria API by following these steps:
- Modify the repository interface to support queries that use the JPA Criteria API.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.Repository;
public interface EmployeeJPARepository extends Repository<Employee,Integer>
,JpaSpecificationExecutor<Employee>{
}
2. Specify the conditions of the invoked database query.
3. Invoke the database query.
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
EmployeeJPARepository employeeJPARepository = applicationContext.getBean(EmployeeJPARepository.class);
Specification<Employee> employeeSpecification = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("id");
Predicate predicate = cb.gt(path,5);
return predicate;
}
};
System.out.println(employeeJPARepository.findAll(employeeSpecification));
EmployeeJPARepository employeeJPARepository = applicationContext.getBean(EmployeeJPARepository.class);
Specification<Employee> employeeSpecification = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("id");
Path path1= root.get("name");
Predicate predicate = cb.and(cb.equal(path1,"Peter1"),cb.lt(path,7));
return predicate;
}
};
System.out.println(employeeJPARepository.findAll(employeeSpecification));
EmployeeJPARepository employeeJPARepository = applicationContext.getBean(EmployeeJPARepository.class);
Specification<Employee> employeeSpecification = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("id");
Path path1= root.get("name");
Predicate predicate = cb.between(path,1,5);
return predicate;
}
};
System.out.println(employeeJPARepository.findAll(employeeSpecification));
Exercise 5
Use JPA Criteria API for following operations for person
- equals
- gt
- lt
- and
- or
- between
Sorting
we can sort the query results of our database queries by using the OrderBy keyword. We can use the OrderBy keyword by following these steps:
- Append the OrderBy keyword to the method name of our query method.
- Append the name of the property to the method name of our query method and transform its first letter into uppercase. If we want to order our query results by using the title of a todo entry, we have to append the string: Title to the method name of our query method.
Sorting
- Describe the sort direction. If we want to sort the query results in ascending order, we have to append the keyword Asc to the method name of our query method. On the other hand, if we want to sort the query results in descending order, we have to append the keyword Desc to the method name of our query method.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
List<Employee> findByNameOrderByIdDesc(String name);
}
Sorting Query Results With the Sort Class
We can sort our query results by following these steps:
- Obtain the Sort object that describes the sorting options of the invoked database query.
- Pass the Sort object forward to the correct repository method as a method parameter.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
List<Employee> findAll(Sort sort);
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
EmployeeRepository employeeRepository=applicationContext.getBean(EmployeeRepository.class);
List<Employee> list = employeeRepository.findAll(new Sort(Sort.Direction.DESC,"age").and(
new Sort(Sort.Direction.ASC,"name")));
System.out.println(list);
Pagination
If we want create the Pageable object manually, the service class (or other component) that wants to paginate the query results, which are returned by a Spring Data JPA repository, must create the Pageable object and pass it forward to the invoked repository method.
package com.springdata.repositories;
import com.springdata.entity.Employee;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
Page<Employee> findAll(Pageable pageable);
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
EmployeeRepository employeeRepository=applicationContext.getBean(EmployeeRepository.class);
Page<Employee> employeePage= employeeRepository.findAll(new PageRequest(0,3,new Sort(Sort.Direction.DESC,"id")));
List<Employee> employeeList=employeePage.getContent();
System.out.println(employeeList);
Exercise 6
- Get the persons greater than age 25 and sort them in descending order according to id by method query.
- Do the question above using the Sort class.
- Apply Pagination on Person entities.
Auditing
The source code of the DateTimeService interface looks as follows:
package com.springdata.demo;
import java.time.ZonedDateTime;
public interface DateTimeService {
ZonedDateTime getCurrentDateAndTime();
}
The CurrentTimeDateTimeService class implements the DateTimeService interface. Its getCurrentDateAndTime() method simply returns the current date and time.
package com.springdata.demo;
import java.time.ZonedDateTime;
public class CurrentDateTimeService implements DateTimeService {
@Override
public ZonedDateTime getCurrentDateAndTime() {
return ZonedDateTime.now();
}
}
package com.springdata.demo;
import org.springframework.data.auditing.DateTimeProvider;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class AuditingDateTimeProvider implements DateTimeProvider {
private final DateTimeService dateTimeService;
public AuditingDateTimeProvider(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
@Override
public Calendar getNow() {
return GregorianCalendar.from(dateTimeService.getCurrentDateAndTime());
}
}
Integrating Our Service With the Auditing Infrastructure of Spring Data JPA
@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")
@EnableJpaRepositories("com.springdata.repositories")
public class PersistenceContext {
// Other beans
@Bean
DateTimeService currentTimeDateTimeService() {
return new CurrentDateTimeService();
}
@Bean
DateTimeProvider dateTimeProvider(DateTimeService dateTimeService) {
return new AuditingDateTimeProvider(dateTimeService);
}
}
we need to create the DateTimeProvider bean and enable the auditing support of Spring Data. We can do this by making the following changes to the configuration class that configures the persistence layer of our example application:
Employee Entity
package com.springdata.entity;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.Date;
@Entity
@NamedNativeQuery(name = "Employee.findByNameIs",
query = "SELECT * from Employee where name = :name",
resultClass = Employee.class)
@EntityListeners(AuditingEntityListener.class)
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private Integer age;
private Integer salary;
@CreatedDate
private Date creationTime;
@LastModifiedDate
private Date modificationTime;
//getters and setters
}
Exercise 7
Implement dateCreated and lastUpdated auditing for Person.
Creating a custom repository
package com.springdata.demo;
public class EmployeeSearchResultDTO {
private String name;
private Integer age;
public EmployeeSearchResultDTO() {}
//getters and setters
}
package com.springdata.demo;
import java.util.List;
public interface CustomEmployeeRepository {
List<EmployeeSearchResultDTO> searchTerm(String searchTerm);
}
@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")
@ComponentScan("com.springdata.component")
@EnableJpaRepositories(value = "com.springdata.repositories",
repositoryImplementationPostfix="Impl")
public class PersistenceContext {
//Other beans
@Bean
NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
}
package com.springdata.component;
import com.springdata.demo.CustomEmployeeRepository;
import com.springdata.demo.EmployeeSearchResultDTO;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class EmployeeRepositoryImpl implements CustomEmployeeRepository ,InitializingBean{
private static final String SEARCH_TODO_ENTRIES = "SELECT name, age FROM Employee t WHERE " +
"LOWER(t.name) LIKE LOWER(CONCAT('%',:searchTerm, '%')) " +
"ORDER BY t.id ASC";
private final NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
public EmployeeRepositoryImpl(NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Transactional(readOnly = true)
@Override
public List<EmployeeSearchResultDTO> searchTerm(String searchTerm) {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("searchTerm", searchTerm);
List<EmployeeSearchResultDTO> searchResults = jdbcTemplate.query(SEARCH_TODO_ENTRIES,
queryParams,
new BeanPropertyRowMapper<>(EmployeeSearchResultDTO.class)
);
return searchResults;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println(">>>>>>>>>>>>>>>>setting" );
}
}
package com.springdata.repositories;
import com.springdata.demo.CustomEmployeeRepository;
import com.springdata.entity.Employee;
import org.springframework.data.repository.Repository;
public interface EmployeeRepository extends Repository<Employee, Integer>,
CustomEmployeeRepository {
Integer count();
}
Exercise 8
Add a custom repository for person entity get firstname and lastname of the personswhose name starts with a specific character.
Spring Data JPA
By Pulkit Pushkarna
Spring Data JPA
- 2,283