100% found this document useful (1 vote)
351 views44 pages

SpringBoot Data-JPA Guide

This document provides an overview and guide for using the Spring Data JPA module. It introduces Spring Data projects for accessing different types of data stores and describes features like repositories, dynamic queries, auditing. It also covers generating domain models from databases, customizing the generated models, configuring a Spring Boot project with JPA dependencies, creating repository interfaces, and examples of CRUD operations using the repositories.

Uploaded by

Bikram KC
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
100% found this document useful (1 vote)
351 views44 pages

SpringBoot Data-JPA Guide

This document provides an overview and guide for using the Spring Data JPA module. It introduces Spring Data projects for accessing different types of data stores and describes features like repositories, dynamic queries, auditing. It also covers generating domain models from databases, customizing the generated models, configuring a Spring Boot project with JPA dependencies, creating repository interfaces, and examples of CRUD operations using the repositories.

Uploaded by

Bikram KC
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 44

Spring Data JPA Module

Developer Guide - Detailed


( Beginner to Expert )
By Vikram Thakur
Sr. Software Architect

www.ezeontech.com
Spring Data Project - Introduction

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.

This is an umbrella project which contains many subprojects that are specific to a given
database.

Official Project Link :


https://spring.io/projects/spring-data
Spring Data Project - Features

1. Powerful repository interface


2. Dynamic query derivation from repository method names
3. Implementation domain base classes providing basic properties
4. Support for transparent auditing (created, last changed)
5. Possibility to integrate custom repository code
6. Easy Spring integration via JavaConfig and custom XML namespaces
7. Advanced integration with Spring MVC controllers
8. Experimental support for cross-store persistence
Official Reference Documentation: (Must Read)
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Database Schema
Intro
Reverse Engineering/
Domain Model Generation
Intro
For Reverse Engineering.
(Generate Model
Classes from
database tables)
Intro
Intro
Intro
3. Basic Customization in
Generated Domain Model Class
Customization Domain/Entity Model Class

1. Refactor camel-case class name like “Enquirycourse” → “EnquiryCourse”


2. Remove @NamedQueries
3. Remove One-To-Many association (delete annotation @OneToMany) and add
new annotations

@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private List<Enquiry> enquiryList;

4. Child class knows about its parent whereas parent does not know its childs
5. Add necessary constructors (parameterized)
Continue...

6. If a class has multiple FK from same entity then refactor the name of
generated properties. Example as below

class Contact { class Contact {

private Address address; private Address localAddress;

private Address address1; private Address permanentAddress;

} }
Customize Generated
Domain Model Class (Entity Class)....

8. Check hashCode() and equals() overridden methods are generated correctly

@Override

public int hashCode() {

int hash = 0;

hash += (contactId != null ? contactId.hashCode() : 0);

return hash;

}
Override Equals Method - Looks Like

@Override
public boolean equals(Object object) {
if (!(object instanceof Contact)) {
return false;
}
Contact other = (Contact) object;
if ((this.contactId == null && other.contactId != null) ||
(this.contactId != null && !this.contactId.equals(other.contactId))) {
return false;
}
return true;
}
Entity Class Sample
import java.io.*;
import java.util.*;
import javax.persistence.*; NOTE:
@Entity
1. Some properties are removed in
@Table(name = "enquiry") PPT
public class Enquiry implements Serializable { 2. Setter/getter and constructor is
private static final long serialVersionUID = 1L;
also removed for clarity of
@Id presentation
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "enquiryId")
private Long enquiryId;

@Column(name = "remark")
private String remark;

@Column(name = "committedFees")
private Double committedFees;

@JoinColumn(name = "contactId", referencedColumnName = "contactId")


@ManyToOne
private Contact contact; //FK
……
//TODO : getter setters and constructors

}
Final Generated - Entity/Domain Model Classes
NOTE

1. I have generated Entity/Domain Model Class and Done all corrections


2. I created a temp-project under NetBeans but my package name is same as my
actual package in main project. So that I can copy all generated classes directly in
main project
SpringBoot Project
Config Guide
Dependencies

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

Other dependencies as needed…..


application.properties

spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://localhost:3306/enquiry_poc_db
spring.datasource.username=root
spring.datasource.password=mysql

spring.jpa.hibernate.naming.implicit-strategy=
org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Project Directory Structure
Repository F
Create Repository Interface for all entities
JpaRepository
Repository Interface Reference

package net.ezeon.poc.repo;

import net.ezeon.poc.domain.Address;
import org.springframework.data.jpa.repository.JpaRepository;

public interface EnquiryRepository extends JpaRepository<Enquiry, Long> {


//customized query methods
} Primary Key
Entity Class Type

This interface will provide various reusable methods to perform operations on entity.
Example : save(), delete(), deleteAll(), deleteById(), deleteInBatch(), getOne(), findOne(),
exists(), existsById(), flush() etc..

Spring Data Project will supply implementation of this repository interface.

NOTE : NO DAO class required. Repository can handle more than DAO
All Repository Interfaces

1. AddressRepository
2. ContactRepository
3. CourseRepository
4. EnquiryCourseRepository
5. EnquiryRepository
6. EnquirySourceRepository
7. FollowupRepository
8. InstituteRepository
Use Cases - 1
Save Enquiry Source - CRUD
Enquiry Source - CRUD Operations
Controller Method - Endpoint

@RequestMapping("/save-institute")
public String saveInstitute(@ModelAttribute Institute inst) {
inst.setDoe(new Date());
inst.getContact().setName(inst.getName());
commonService.saveInstitute(inst);
return "redirect:institute-list";
}

Call Service Method To Save Data


in Database Using
Spring-Data-JPA
Use Cases - 1
Add/Register New Institute
Institute Registration Form
Controller Method - Endpoint

@RequestMapping("/save-institute")
public String saveInstitute(@ModelAttribute Institute inst) {
inst.setDoe(new Date());
inst.getContact().setName(inst.getName());
commonService.saveInstitute(inst);
return "redirect:institute-list";
}

Call Service Method To Save Data


in Database Using
Spring-Data-JPA
Save Institute - Business Method in Service Class

@Transactional Generated Query :


Hibernate:
public void saveInstitute(Institute inst) { insert
//save in address table into
addressRepository.save(inst.getContact().getPermanentAddress()); address
(city, country, detail, zip)
//save in contact table values
contactRepository.save(inst.getContact()); (?, ?, ?, ?)
//save in institute table Hibernate:
insert
instituteRepository.save(inst); into
} contact
(email, localAddress, name,
permanentAddress, phone)
values
(?, ?, ?, ?, ?)
Hibernate:
NOTE: save() method is a reusable method supplied by spring-data-jpa insert
framework implementation into
institute
(contactId, doe, name)
values
(?, ?, ?)
Use Cases - 2
Fetch Institute List
Institute List Page
Controller Method - Endpoint

@RequestMapping("/institute-list")
public String instituteList(Model m) {
m.addAttribute("instList", instituteRepository.findAll());
return "institute-list";
}

Call Repository method to fetch all


Institutes
Fetched Data in JSON Format

[
{"instituteId":4,"name":"EZEON Tech", "doe":"2019-08-22T17:23:29.000+0000",
"contact":{"contactId":6,"name":"Official","email":"contact@ezeon.net","phone":"9630130106","localAddress
":{"addressId":9,"detail":"63,
Zone-1","city":null,"country":null,"zip":null},"permanentAddress":{"addressId":10,"detail":"A-8,
Kolar","city":"Mumbai","country":"India","zip":777777}}},

{"instituteId":5,"name":"APT Academy",
"doe":"2019-08-23T07:36:21.000+0000","contact":{"contactId":7,"name":"Admin","email":"admin@apt.com","
phone":"9630130106","localAddress":{"addressId":11,"detail":"Addr 1,
Zone-1","city":null,"country":null,"zip":null},"permanentAddress":{"addressId":12,"detail":"Addr 2,
Kolar","city":"Mumbai","country":"India","zip":777777}}}
]
Internally 3 queries executed (Expensive Approach)

select select select


institute0_.instituteId as contact0_.contactId as contactI1_1_0_, contact0_.contactId as contactI1_1_0_,
institut1_7_, contact0_.email as email2_1_0_, contact0_.email as email2_1_0_,
institute0_.contactId as contact0_.localAddress as localAdd5_1_0_, contact0_.localAddress as localAdd5_1_0_,
contactI4_7_, contact0_.name as name3_1_0_, contact0_.name as name3_1_0_,
institute0_.doe as doe2_7_, contact0_.permanentAddress as permanen6_1_0_, contact0_.permanentAddress as permanen6_1_0_,
institute0_.name as contact0_.phone as phone4_1_0_, contact0_.phone as phone4_1_0_,
name3_7_ address1_.addressId as addressI1_0_1_, address1_.addressId as addressI1_0_1_,
from address1_.city as city2_0_1_, address1_.city as city2_0_1_,
institute institute0_ address1_.country as country3_0_1_, address1_.country as country3_0_1_,
address1_.detail as detail4_0_1_, address1_.detail as detail4_0_1_,
address1_.zip as zip5_0_1_, address1_.zip as zip5_0_1_,
address2_.addressId as addressI1_0_2_, address2_.addressId as addressI1_0_2_,
address2_.city as city2_0_2_, address2_.city as city2_0_2_,
address2_.country as country3_0_2_, address2_.country as country3_0_2_,
address2_.detail as detail4_0_2_, address2_.detail as detail4_0_2_,
address2_.zip as zip5_0_2_ address2_.zip as zip5_0_2_
from from
contact contact0_ contact contact0_
left outer join left outer join
address address1_ address address1_
on contact0_.localAddress=address1_.addressId on contact0_.localAddress=address1_.addressId
left outer join left outer join
address address2_ address address2_
on on contact0_.permanentAddress=address2_.addressId
contact0_.permanentAddress=address2_.addressId where
where contact0_.contactId=?
contact0_.contactId=?
Fetch Only Selected Fields Using - JPQL : @Query Annotation : Optimized Approach

Add Method in InstituteRepository Interface

@Query("SELECT i.instituteId AS instituteId, i.name AS name, i.contact.phone AS


phone, i.contact.email AS email, i.contact.permanentAddress.city AS
city FROM Institute AS i ")
public List<Map<String,Object>> getInstituteList();

Every Tuple(row) will be mapped to Map<String,Object>

Instead of calling findAll() in InstituteRepository we should call this method getInstituteList() method from Controller
Endpoint, Its more optimized and executing only one SQL query in database

JSON :
[
{ "name": "EZEON Tech", "phone": "9630130106", "city": "Mumbai", "instituteId": 4, "email": "contact@ezeon.net" },

{ "name": "APT Academy", "phone": "9630130106", "city": "Mumbai", "email": "admin@apt.com", "instituteId": 5 }
]
Generated SQL for JPQL

Hibernate:
select Only single sql is executed
institute0_.instituteId as col_0_0_, in databse to fetch
institute0_.name as col_1_0_, institute list
contact1_.phone as col_2_0_,
contact1_.email as col_3_0_,
address4_.city as col_4_0_
from
institute institute0_ cross
join
contact contact1_ cross
join
address address4_
where
institute0_.contactId=contact1_.contactId
and contact1_.permanentAddress=address4_.addressId
Projection: Mapping selected columns to “Projection Interface”

public interface InsitutetDTO {


Long getInstituteId();
String getName();
String getCity();
String getPhone();
String getEmail();
}

Institute Repository Method:


@Query("SELECT i.instituteId AS instituteId, i.name AS name, i.contact.phone AS phone, i.contact.email AS
email, i.contact.permanentAddress.city AS city FROM Institute AS i ")
public List<InsitutetDTO> getInstituteList();

Same JSON as Previous:


[
{ "name": "EZEON Tech", "instituteId": 4, "phone": "9630130106", "email": "contact@ezeon.net", "city": "Mumbai" },

{ "name": "APT Academy","instituteId": 5,"phone": "9630130106", "email": "admin@apt.com", "city": "Mumbai" }


]
Projection As Array: Mapping selected columns to “Object[ ]” - “AsArray”

Institute Repository Method:

@Query("SELECT i.instituteId AS instituteId, i.name AS name, i.contact.phone AS phone, i.contact.email AS


email, i.contact.permanentAddress.city AS city FROM Institute AS i ")
public List<Object[ ]> getInstituteListAsArray();

Tuple mapping to Object[ ].


Remember method name must contain
AsArray term
JSON LIST :

[
[4,"EZEON Tech","9630130106","contact@ezeon.net","Mumbai"],

[5,"APT Academy","9630130106","admin@apt.com","Mumbai"]
]
Final Optimized Approach I Used - projection interface

Controller Method: Endpoint


@RequestMapping("/institute-list")
public String instituteList(Model m) {
m.addAttribute("instList", instituteRepository.getInstituteList());
return "institute-list";
}

InstituteRepository Method:
@Query("SELECT i.instituteId AS instituteId, i.name AS name, i.contact.phone AS phone, i.contact.email AS email,
i.contact.permanentAddress.city AS city FROM Institute AS i ")
public List<InsitutetDTO> getInstituteList();

Thymeleaf Template : for each loop :


<tr th:each="dto,itr : ${instList}">
<td th:text="${itr.count}">SR</td>
<td th:text="${dto.instituteId}">ID</td>
<td th:text="${dto.name}">NAME</td>
<td th:text="${dto.phone}">PHONE</td>
<td th:text="${dto.email}">EMAIL</td>
<td th:text="${dto.city}">CITY</td>
<td><a href="TODO">Edit</a> | <a href="TODO">Delete</a></td>
</tr>

You might also like