Lecture 2 - Spring Data and Spring Data Rest
Lecture 2 - Spring Data and Spring Data Rest
Data Rest
1 / 47
Spring Data framework
Spring Data: https://spring.io/projects/spring-data
Spring Data’s mission is to provide a familiar and consistent,
spring-based programming model for data access while still
retaining the special traits of the underlying data store.
It makes it easy to use data access technologies, relational and
non-relational databases, map-reduce frameworks, and cloud-
based data services.
2 / 47
Features
Dynamic query derivation from repository method names
Support for transparent auditing (created, last changed)
Data Pagination
3 / 47
Most common Spring Data projects
Spring Data JPA - Spring Data repository support for JPA.
Spring Data MongoDB - Spring based, object-document support
and repositories for MongoDB.
Spring Data REST - Exports Spring Data repositories as
hypermedia-driven RESTful resources.
Spring Data Elasticsearch - Spring Data module for Elasticsearch.
4 / 47
Spring Data JPA
5 / 47
Features
Ready made CRUD provided functionality
Dynamic query derivation from repository method names
Manual queries @Query annotated queries
Support for transparent auditing (created, last changed)
Support for Querydsl predicates and thus type-safe JPA queries
Pagination support, dynamic query execution, ability to integrate
custom data access code
6 / 47
Development time
“
7 / 47
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'gr.rongasa'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starte
implementation 'org.springframework.boot:spring-boot-starte
implementation 'org.postgresql:postgresql'
implementation 'org.springframework.boot:spring-boot-starte
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-st
}
8 / 47
Application Configuration
(Postgresql Connection and JPA)
server:
port: 9090 # Set the server's port
servlet:
context-path: /jpa # Set context path of application serve
...
9 / 47
...
spring:
application:
name: e-shop # Set the application name.
data:
jpa:
repositories:
enabled: true
datasource: # Setup datasource
hikari:
connection-timeout: 20000
maximum-pool-size: 5
url: jdbc:postgresql://localhost:5432/eshop
username: eshop
password: eshop@@@
jpa: #JPA properties
hibernate:
ddl-auto: update
show-sql: true
database: postgresql
database-platform: org.hibernate.dialect.PostgreSQL95Dialec
open-in-view: false # Significant attribute. Dont open tr
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
10 / 47
The Domain object
package gr.rongasa.eshop.domain;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
@Entity
@Table
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Setter
public class Inventory {
@Id
@GeneratedValue
private Long id;
private String name;
private Long amount;
private String description;
private String type;
private BigDecimal cost;
} 11 / 47
Repository
import gr.rongasa.eshop.domain.Inventory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
12 / 47
Database Configuration
package gr.rongasa.eshop.configuration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpa
@Configuration
@EnableJpaRepositories(basePackages = {"gr.rongasa.eshop.reposi
@Slf4j
public class DatabaseConfiguration {
@Bean
public CommandLineRunner stupidBean(){
return args -> log.info("JPA java code started");
}
}
13 / 47
Result
The conversion from Spring Data elasticSearch to spring data JPA
is done. This is what is meant with familiar and consistent model
for data layer access. Consider how few changes were made to
move from two completely different database technologies.
We could allow the concurrent existence of elasticSearch and JPA
connection
We changed domain id from String to Long. Elastic search
performs better/easier with String id while
Be careful with the @Id and from which path this is imported from.
javax.persistence.Id; is for JPA and
org.springframework.data.annotation.Id; is for non
relational databases.
14 / 47
Means of Database Queries
The repository proxy has two ways to derive a store-specific query
from the method name:
15 / 47
Query from method name
findFirst/ findTop
findAllBy or findBy
distinct
existsBy
delete
count
orderBy
asc, desc
e.g.
16 / 47
Supported method Names
keywords
Keyword Keyword Keyword Keyword
Page<Inventory> findByTypeAndAmountGreaterThanOrderByCost(P
Optional<Inventory> findFirstByNameAndTypeOrderByCost(Strin
Page<Inventory> findDistinctTop2ByAmountLessThanOrderByAmou
Page<Inventory> findDistinctTop2ByAmountLessThanOrderByAmou
Page<Inventory> findByNameContaining(Pageable pageable, Str
Page<Inventory> findByNameEndingWith(Pageable pageable, Str17 / 47
Query
JPQL or native queries are supported in Spring data repositories
e.g.
18 / 47
Query
Propose to avoid Queries and even more native queries. However,
although spring data provides practically almost everything,
sometimes JPA query may be the only way.
19 / 47
Entity Graph
*ToMany relationships are lazy fetch. This means you cannot get
i.e. the members without a transaction
Repository methods open a transaction inside method call and
close it after method exit.
EntityGraph is a performant way of eagerly fetching inner entities
after repository method returns
@Entity
public class GroupInfo {
@Repository
public interface GroupRepository extends CrudRepository<GroupIn
@EntityGraph(attributePaths = { "members" })
GroupInfo getByGroupName(String name);
} 20 / 47
Named Entity Graph
Instead of constructing and using EntityGraph inside repository one
create named entity graphs and then use these inside the repository
@Entity
@NamedEntityGraph(
name = "members-graph",
attributeNodes = {
@NamedAttributeNode("members")
}
)
public class GroupInfo {
// default fetch mode is lazy.
@ManyToMany
List<GroupMember> members = new ArrayList<GroupMember>();
…
}
@Repository
public interface GroupRepository extends CrudRepository<GroupIn
@EntityGraph(value = "members-graph")
GroupInfo getByGroupName(String name);
}
21 / 47
Common Mistakes/Improvements
JPA prerequisite information
Attention:
23 / 47
Spring Data Rest
24 / 47
Spring Data Rest is an 'extension' of Spring Data that exposes over
REST the spring data methods that exist in spring data repositories.
25 / 47
Spring Data Rest
Add in gradle the following dependency
implementation 'org.springframework.boot:spring-boot-starter-da
Add in all custom repository methods that you wish to expose over
rest and have method parameters the @Param annotation.
data:
rest:
base-path: api
26 / 47
Spring Data Rest - HATEOAS
curl -X GET http://localhost:9090/jpa/api
{
"_links": {
"groupInfoes": {
"href": "http://localhost:9090/jpa/api/groupInfoes{
"templated": true
},
"groupMembers": {
"href": "http://localhost:9090/jpa/api/groupMembers
"templated": true
},
"inventories": {
"href": "http://localhost:9090/jpa/api/inventories{
"templated": true
},
"profile": {
"href": "http://localhost:9090/jpa/api/profile"
}
}
} 27 / 47
HATEOAS/HAL
“
28 / 47
Spring Data REST
"inventories": {
"href": "http://localhost:9090/jpa/api/inventories{?pag
"templated": true
},
tempalated means that page, size, sort are part of query template.
i.e. http://localhost:9090/jpa/api/inventories?
page=1&size=10&sort=id,asc
29 / 47
{
"_embedded": { "inventories": [] },
"_links": {
"first": {
"href": "http://localhost:9090/jpa/api/inventories?
},
"prev": {
"href": "http://localhost:9090/jpa/api/inventories?
},
"self": {
"href": "http://localhost:9090/jpa/api/inventories"
},
"last": {
"href": "http://localhost:9090/jpa/api/inventories?
},
"profile": {
"href": "http://localhost:9090/jpa/api/profile/inve
},
"search": {
"href": "http://localhost:9090/jpa/api/inventories/
}
},
"page": {
"size": 10,
"totalElements": 0,
"totalPages": 0,
"number": 1
}
} 30 / 47
Create new object
curl -X POST http://localhost:9090/jpa/api/inventories -H 'Cont
-d '{
"name": "book",
"amount": 10,
"description": "custom rwritten book",
"type": "book",
"cost": 10.45
}'
31 / 47
Result
{
"name": "book",
"amount": 10,
"description": "custom rwritten book",
"type": "book",
"cost": 10.45,
"_links": {
"self": {
"href": "http://localhost:9090/jpa/api/inventories/
},
"inventory": {
"href": "http://localhost:9090/jpa/api/inventories/
}
}
}
32 / 47
Update new Object
curl -X PUT \
http://localhost:9090/jpa/api/inventories/3 \
-H 'Content-Type: application/json' \
-d '{
"name": "book",
"amount": 10,
"description": "custom rwritten book",
"type": "book",
"cost": 10.45
}'
33 / 47
{
"name": "book",
"amount": 10,
"description": "custom rwritten book",
"type": "book",
"cost": 10.45,
"_links": {
"self": {
"href": "http://localhost:9090/jpa/api/inventories/
},
"inventory": {
"href": "http://localhost:9090/jpa/api/inventories/
}
}
}
34 / 47
Search Hypermedia Link
Ensure you have added in spring data custom repository method
@Param annotation at method parameters
After creating an Inventory object locate hypermedia link named
search and make a GET request. i.e.
http://localhost:9090/jpa/api/inventories/search
35 / 47
All custom methods created are exposed and can be used
{
"_links": {
"updateMediaType": {
"href": "http://localhost:9090/jpa/api/inventories/
"templated": true
},
"findByAmountIsBetween": {
"href": "http://localhost:9090/jpa/api/inventories/
"templated": true
},
"findByTypeAndAmountGreaterThanOrderByCost": {
"href": "http://localhost:9090/jpa/api/inventories/
"templated": true
},
...
"self": {
"href": "http://localhost:9090/jpa/api/inventories/
}
}
}
36 / 47
Spring Data REST Tips
And Tricks
37 / 47
You can annotate as @RestResource(exported = false) a
Repository (method or class) to define it as not exposed over web
You can configure Repositoriies (i.e. expose id) by using
RepositoryRestConfigurer
@Configuration
public class RestConfiguration {
@Bean
public RepositoryRestConfigurer repositoryRestConfigurer()
return new RepositoryRestConfigurer() {
@Override
public void configureRepositoryRestConfiguration(Re
config.exposeIdsFor(Inventory.class);
}
};
}
}
38 / 47
You can enable HAL Browser to assist with spring data rest
experience:
implementation 'org.springframework.data:spring-data-rest-hal-b
Now open the Spring Data Rest base url via browser and
check the result.
39 / 47
Spring Data Tips And
Tricks
40 / 47
You can load initial data via CommandLineRunner
@Component
@RequiredArgsConstructor
public class DatabaseDataInitialization implements CommandLineR
private final InventoryRepository inventoryRepository;
@Override
public void run(String... args) {
if (inventoryRepository.countByType("book")==0){
inventoryRepository.saveAll(Stream.of("Spring Boot"
.collect(Collectors.toList()));
}
}
}
41 / 47
Spring Data MongoDB
42 / 47
Lets switch this project now to NoSQL/MongoDB.
Then we have seen the same project in ElasticSearch (1st
lecture), in JPA (Postgresql) and MongoDB.
43 / 47
Prepare Dependencies
In gradle remove 'org.springframework.boot:spring-boot-starter-
data-jpa' and 'org.postgresql:postgresql' from dependencies
In gradle add 'org.springframework.boot:spring-boot-starter-data-
mongodb' as dependency
44 / 47
Prepare Repositories
In repositories replace JpaRepository interface with
MongoRepository
In repositories remove Modifying queries
In Repositories native queries are not considered (all queries are
native)
In Repositories modify jpa queries to match mongodb query
language
In database configuration replace EnableJpaRepositories with
EnableMongoRepositories
45 / 47
Exercise 1
Purpose: Gain experience with Spring Data and Spring Data Rest.
46 / 47
Description
You need to extend the simple Library Management System (exercise
of first lecture) and add the same information added into elasticsearch
into postgresql database. This means that it has been decided to
support two databases in parallel.
Postgresql will not be exposed over web using Spring Data Rest.
Elasticsearch database will be exposed over REST.
One extra method supported over web will be the post method
''/synch' which will synchronize all data from Postgresql to elastic
search.
From elasticSearch repositories it has been decided to expose
over rest all the get methods but hide/disable the database
modifying methods and allow modification only via the existing
controller.
It is decided to use flyway as a migration tool for JPA database.
47 / 47