SpringBoot Data-JPA Guide
SpringBoot Data-JPA Guide
www.ezeontech.com
Spring Data Project - Introduction
This is an umbrella project which contains many subprojects that are specific to a given
database.
@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
} }
Customize Generated
Domain Model Class (Entity Class)....
@Override
int hash = 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;
}
Final Generated - Entity/Domain Model Classes
NOTE
<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>
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;
This interface will provide various reusable methods to perform operations on entity.
Example : save(), delete(), deleteAll(), deleteById(), deleteInBatch(), getOne(), findOne(),
exists(), existsById(), flush() etc..
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";
}
@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";
}
@RequestMapping("/institute-list")
public String instituteList(Model m) {
m.addAttribute("instList", instituteRepository.findAll());
return "institute-list";
}
[
{"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)
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”
[
[4,"EZEON Tech","9630130106","contact@ezeon.net","Mumbai"],
[5,"APT Academy","9630130106","admin@apt.com","Mumbai"]
]
Final Optimized Approach I Used - projection interface
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();