JPA Guide
ID Generation
- GenerationType.AUTO : It dependecy on data base mostly it used SEQUNCE
- GenerationType.Identity : It relies on auto-incremented database column
- GenerationType.SEQUNCE : It uses databse sequnce to generate
- GenerationType.TABLE : for pessimistic locks
@CreationTimestamp and @UpdateTimestamp
Query
Query Generation by Spring Data JPA
@Query
- We can write JPQL query #### @Query JPQL using index parameters
//JPQL Query using index params
@Query("select p from Product p where p.name =?1 or p.description=?2")
public Product findByNameorDescriptionJPQLIndexParam(String name, String description);
#### @Query JPQL using named parameters
//JPQL Query using named params
@Query("select p from Product p where p.name =:name or p.description=:desc")
public Product findByNameorDescriptionJPQLNamedParam(@Param("name") String name, @Param("desc") String description);
@Query for Native SQL Queries using index parameters
//Native SQl Query using index params
@Query(value = "select * from products p where p.name =?1 or p.description=?2" , nativeQuery = true)
public Product findByNameorDescriptionSQLIndexParam(String name, String description);
@Query for Native SQL Queries using named parameters
//Native SQL Query using named params
@Query(value = "select * from products p where p.name =:name or p.description=:desc" , nativeQuery = true)
public Product findByNameorDescriptionSQLNamedParam(@Param("name") String name, @Param("desc") String description);
Named Queries
@NamedQuery for JPQL named Query
//Entity
@NamedQueries(
{
@NamedQuery(
name = "Product.findAllProductsOrderByNameDesc",
query = "select p from Product p order by p.name desc"
),
@NamedQuery(
name = "Product.findByPrice",
query = "select p from Product p where p.price =:price"
)
}
)
// reposiroty
public List<Product> findAllProductsOrderByNameDesc();
public Product findByPrice(@Param("price") BigDecimal price);
@NamedNativeQuery for SQL Native named Query
@NamedNativeQueries(
{
@NamedNativeQuery(
name = "Product.findAllProductsOrderByNameAsc",
query = "select * from products p order by p.name asc",
resultClass = Product.class
),
@NamedNativeQuery(
name = "Product.findBySku",
query = "select * from products p where p.stock_keeping_unit = ?1",
resultClass = Product.class
)
}
)
//repository
@Query(nativeQuery = true)
public Product findBySku (String sku);
@Query(nativeQuery = true)
public List<Product> findAllProductsOrderByNameAsc();
Mapping
One-to-One Mapping
Unidirectional mapping
- Source entity has a relationship field that refers to the target entity and the source entity's table contains the foreign key
public class Order {
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "billing_address_id" , referencedColumnName = "id")
private Address billingAddress;
}
Bidirectional Mapping
- Each entity has a relationship field that refers to each other and the target entity table contain the foreign key. The source entity must use the mappedBy attributr to define the bidirectional one-to-one mapping
public class Order {
@OneToOne(cascade = CascadeType.ALL, mappedBy = "order", fetch = FetchType.LAZY)
private Address billingAddress;
}
public class Address {
@OneToOne(cascade = CascadeType.ALL )
@JoinColumn(name = "billing_address_id", referencedColumnName = "id")
private Order order;
}
One-to-Many Mapping
-
Child tables record reference the primary key or the parent table #### Unidirectional mapping :
-
Parent entity will have OnetoMany relationship , Foreign key column in child table which referes to primary key of the parent table
public class Order {
//Default fetch type for one to many is LAZY
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumnn(name = "order_id" , referenceColumnName = "id")
private Set<OrderItem> orderItemSets = new HashSet<>();
Bidirectional mapping :
- Parent entity will have OnetoMany and child entity will have ManyToOne relationship , Foreign key column in child table which referes to primary key of the parent table
public class Order {
//Default fetch type for one to many is LAZY
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "order")
private Set<OrderItem> orderItemSets = new HashSet<>();
}
public class OrderItem {
//Default fetch type of ManyToOne : EAGER
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", referencedColumnName = "id")
private Order order;
Many-to-Many Mapping
- It use a table to store association that join two entities.
#### Unidirectional mapping :
- Source entity has @ManyToMany annotation
public class User {
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(
name = "user_id", referencedColumnName = "id"
),
inverseJoinColumns = @JoinColumn(
name = "role_id", referencedColumnName = "id"
)
)
private Set<Role> roles = new HashSet<>();
}
Bidirectional mapping :
- Both entities have @ManyToMany annotation
public class User {
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(
name = "user_id", referencedColumnName = "id"
),
inverseJoinColumns = @JoinColumn(
name = "role_id", referencedColumnName = "id"
)
)
private Set<Role> roles = new HashSet<>();
}
public class Role {
@ManyToMany(mappedBy = "roles" , cascade = {CascadeType.PERSIST, CascadeType.MERGE} , fetch = FetchType.EAGER)
private Set<User> users = new HashSet<>();
}
JPA Inheritance
Mapped Super Class
-
Base entity example https://medium.com/codex/base-entity-and-audit-entity-with-jpa-fe413ad18bda
-
It will create tables for each sublcass with parent fields
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
//---
@Entity
public class OrderHeader extends BaseEntity {
private String customerName;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
}
Single Table
- It will create single table for all subclasses it will include all fields from parent and all child classes and it will have a discriminator column for identification of rows for child
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "vehicle_type")
public abstract class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
@Entity
@DiscriminatorValue("car")
public class Car extends Vehicle{
private String trimLevel;
public String getTrimLevel() {
return trimLevel;
}
public void setTrimLevel(String trimLevel) {
this.trimLevel = trimLevel;
}
}
@Entity
@DiscriminatorValue("truck")
public class Truck extends Vehicle{
private Integer payload;
public Integer getPayload() {
return payload;
}
public void setPayload(Integer payload) {
this.payload = payload;
}
}
Joined Table
- Each entity has its own table with shared attribute from parent will have foreign key to parent tables
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Instrument {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
@Entity
public class Piano extends Instrument {
private Integer numberOfKeys;
public Integer getNumberOfKeys() {
return numberOfKeys;
}
public void setNumberOfKeys(Integer numberOfKeys) {
this.numberOfKeys = numberOfKeys;
}
}
@Entity
public class ElectricGuitar extends Guitar{
private Integer numberOfPickups;
public Integer getNumberOfPickups() {
return numberOfPickups;
}
public void setNumberOfPickups(Integer numberOfPickups) {
this.numberOfPickups = numberOfPickups;
}
}
@Entity
public class Guitar extends Instrument {
private Integer numberOfStrings;
public Integer getNumberOfStrings() {
return numberOfStrings;
}
public void setNumberOfStrings(Integer numberOfStrings) {
this.numberOfStrings = numberOfStrings;
}
}
Tables per class
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Mammal {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private Integer bodyTemp;
private String species;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getBodyTemp() {
return bodyTemp;
}
public void setBodyTemp(Integer bodyTemp) {
this.bodyTemp = bodyTemp;
}
public String getSpecies() {
return species;
}s
public void setSpecies(String species) {
this.species = species;
}
}
@Entity
public class Dog extends Mammal {
private String breed;
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
}
@Entity
public class Dolphin extends Mammal {
private Boolean hasSpots;
public Boolean getHasSpots() {
return hasSpots;
}
public void setHasSpots(Boolean hasSpots) {
this.hasSpots = hasSpots;
}
}
- Each entity has its own table and There is no explicit foreign key relationship between the tables representing subclasses and the superclass.