Spring Data JPA
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
CAS Java Microservice Development
Simon Martinelli, [email protected], v2018.11
Spring Data JPA Stack
JDBC DataSource
HikariCP (Connection Pool)
Hibernate
JPA
Repositories
3
Repository
4
<<Interface>>
JpaRepository<T, ID>
<<Interface>>
PagingAndSortingRepository<T, ID>
<<Interface>>
QueryByExampleExecutor<T, ID>
<<Interface>>
CrudRepository<T, ID>
<<Interface>>
Repository<T, ID>
SimpleJpaRepository<T, ID>
<<Interface>>
JpaSpecificationExecutor<T>
This is the default implementation
Custom Repository
5
<<Interface>>
JpaRepository<T, ID>
<<Interface>>
CustomEmployeeRepository
CustomRepositoryImpl<<Interface>>
EmployeeRepository<Employee, Integer>
Impl is a mandatory naming conventioin
▶ Transactions are set to @Transactional(readOnly = true) on class level
▶ Transactional are all modifying methods:
▶ delete*
▶ save*
▶ flush()
▶ Caution:
This has a different semantic than JPA that doesn’t know a save method
Transaction Handling
6
public interface UserRepository extends Repository<User, Long> {
List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}
results in
select u from User u where u.emailAddress = ?1 and u.lastname = ?2
Spring Data JPA does a property check and traverses nested properties
Query Generation
7
▶ find* can have many return types
▶ Void, Primitives, Wrapper types, T, Iterator<T>, Collection<T>, List<T>,
Optional<T>, Stream<T>, Future<T>, CompletableFuture<T>,
ListenableFuture, Slice, Page<T>, GeoResult<T>, GeoResults<T>,
GeoPage<T>, Mono<T>, Flux<T>, Single<T>, Maybe<T>, Flowable<T>
▶ Repository CRUD methods that return an individual aggregate instance use
Java 8 Optional to indicate the potential absence of a value
Query Return Types
8
▶ We can define our own queries using @Query annotation
@Query("select e from Employee e order by e.name")
List<Employee> findAllEmployeesOrderByName();
@Query
9
▶ Using the constructor expression to create DTOs
@Query("select new jmdhs18.hr.model.DepartmentDTO(d.name, count(e)) " +
"from Department d join d.employees e group by d.name")
List<DepartmentDTO> findAllDepartmentDTO();
public class DepartmentDTO {
private final String name;
private final Long numberOfEmployees;
public DepartmentDTO(String name, Long numberOfEmployees) {
this.name = name;
this.numberOfEmployees = numberOfEmployees;
}
}
DTO-based Projection
10
▶ Use Interfaces
@Query("select d.name from Department d join d.employees e group by d.name")
List<DepartmentNames> findAllDepartmentNames();
public interface DepartmentNames {
String getName();
}
Interface-based Projection
11
▶ SQL can be used by defining the Query as nativeQuery:
@Query("select d.* from Department d", nativeQuery = true)
List<Department> findAllDepartmentNames();
Using SQL
12
▶ Parameters are positional by default
@Query("select u from User u where u.firstname = ?1 or u.lastname = ?2")
User findByLastnameOrFirstname(@Param("lastname") String lastname,
@Param("firstname") String firstname);
▶ But you can use named parameters too
@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(String lastname, String firstname);
Parameters
13
public interface PagingAndSortingRepository<T, ID
extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
Page<User> users = repository.findAll(new PageRequest(1, 20, Sort.by("name"));
Paging and Sorting
14
▶ By extending JpaSpecificationExecutor we get find* methods that take a
Specification as a parameter.
▶ The Specification method toPredicate provides all objects to create a Criteria API
Predicate
List<Employee> list = employeeRepository.findAll(new Specification() {
@Override
public Predicate toPredicate(Root employee, CriteriaQuery query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(employee.get(Employee_.name), name);
}
});
Specifications
15
▶ You can use Insert, Update and Delete but you have to tell Spring Data that
these are modifying queries
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);
Modifying Queries
16
Database Management
17
▶ spring.jpa.generate-ddl
▶ Switches the feature on and off and is vendor independent
▶ spring.jpa.hibernate.ddl-auto
▶ Hibernate feature that controls the behavior in a more fine-grained way
▶ Values are none, validate, update, create, and create-drop
JPA and Hibernate DDL Generation
18
▶ Turn initialization on
▶ spring.datasource.initialization-mode=always
▶ SQL Files
▶ schema.sql → Creates the schema
▶ data.sql → Loads data
▶ Can be database platform dependent
▶ schema-${platform}.sql
▶ data-${platform}.sql
▶ Can also be mixed by omitting schema.sql and let Hibernate create the schema
Database Initialization
19
▶ Spring Boot supports Flyway and Liquibase
▶ Spring Boot autoconfigures Flyway and Liquibase when on the classpath
▶ To use Flyway https://flywaydb.org/
1. Add org.flywaydb:flyway-core dependency to pom.xml
2. Add migration files to classpath:db/migration
Database Migration
20
Flyway Migrations
21
Testing
22
▶ Unit Test
▶ Check if an individual small piece of code is doing what it is supposed to do
▶ Integration Test
▶ Check if different pieces of the modules work together as a whole
▶ Maven support
▶ *Test run with Surefire Plugin
▶ Runs during test phase
▶ https://maven.apache.org/surefire/maven-surefire-plugin/
▶ *IT run Failsafe Plugin
▶ Must be configured. Usually runs in verify phase
▶ https://maven.apache.org/surefire/maven-failsafe-plugin/
Unit vs Integration Tests
23
▶ Spring Boot’s auto-configuration system works well for applications but can
sometimes be a little too much for tests
▶ It often helps to load only the parts of the configuration that are required to test
a “slice” of your application
▶ For example
▶ Test that Spring MVC controllers are mapping URLs correctly, and you do not
want to involve database calls in those tests
▶ Test JPA entities, and you are not interested in the web layer when those
tests run
Spring Boot Test Slices
24
▶ Use the @DataJpaTest annotation to test JPA applications
▶ By default
▶ it configures an in-memory embedded database
▶ scans for @Entity classes
▶ configures Spring Data JPA repositories
▶ Regular @Component beans are not loaded into the ApplicationContext
▶ You can use a “regular” database instead of the in-memory database
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@RunWith(SpringRunner.class)
public class CriteriaTest {
DataJpaTest
25
▶ Runs Spring Boot based tests
▶ Provides the following features
▶ Uses “standard” database
▶ Provides support for different webEnvironment modes, including the ability
to start a fully running web server listening on a defined or random port
@SpringBootTest
@RunWith(SpringRunner.class)
public class PessimisticLockIT {
SpringBootTest
26