0% found this document useful (0 votes)
36 views18 pages

Untitled

Uploaded by

imrit33
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
36 views18 pages

Untitled

Uploaded by

imrit33
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 18

Spring Data JPA:

Basic Concepts:

1. What is Persistence Store?

2. What are the Persistence Operations?

3. What are the Persistence Technologies available for Java?

4. What is Persistence Layer?

5. What are the languages we use for Persistence Layer?

SQL
HQL
JPQL

Best Practices to develop persistence logic:

1. Separate the JPQL with a persistence layer.

2. In persistence logic we should follow Table per Dao concept.

3. Every Dao performs the operation with that table only.

USER_MASTER UserMaster.java
UserMasterImpl.java

ROLE_MASTER RoleMaster.java
RoleMasterImpl.java

CSTMR_ACCNT_DTLS CstmrAccntDtls.java
CstmrAccntDtlsImpl.java

4. Table name should have max 30 characters. If it is crossing the limit, you have
to remove the vowels but not the initials.

5. For every table take a primary key.

6. For every table few extra columns are recommended:

CREATED_BY
CREATED_DATE
UPDATED_BY
UPDATED_DATE

7. For every primary key create dedicated sequence to generate primary key column
value.

8. Always maintain cache for static table data to avoid DB interactions.

Environment Setup:

1. Install Database
2. Add some dependencies:

a. spring-boot-starter
b. spring-boot-starter-data-jpa
c. Driver (situational)
d. Lombok (Optional)
Dynamic class creation:

Internally the generated class for UserRepository interface is: jdk.proxy2.$Proxy95

Responsible Technique: Dynamic Proxying or Proxy Pattern

Important Classes of Data JPA:

Repository

CrudRepository
ListCrudRepository
PagingAndSortingRepository
ListPagingAndSortingRepository

JpaRepository = ListCrudRepository + ListPagingAndSortingRepository +


QueryByExampleExecutor

*** Note: Your UserRepository must extend any of these 5 Repository classes. Then
only your UserRepository will be implemented dynamically.

CrudRepository

It is the pre-defined interface that defines the basic CRUD operations.

Usage:

1. use pre-defined method

2. use findBy method

3. use JPQL

CrudRepository methods:

C - Create / Insert
R - Read
U - Update
D - Delete

Save = Insert + Update:

a. save(E): It is insert operation when the primary key value is not available
in the database. (select + insert))
It is update operation when the primary key value is
available in the database. (select + update)

Student student = new Student();


student.setRollNo(100);
student.setName("Anya");
student.setMarks(8.91);
dao.save(student);

b. saveAll(): It is the repeated task of save() method.

Student student1 = new Student();


student1.setRollNo(101);
student1.setName("Robert");
student1.setMarks(8.31);

Student student2 = new Student();


student2.setRollNo(103);
student2.setName("Harisson");
student2.setMarks(8.11);

// List<Student> students = new ArrayList<>();


// students.add(student1);
// students.add(student2);

dao.saveAll(Arrays.asList(student1, student2));

Read / Retrieve / Find:

a. findById(id): Returns the optional object of the entity you requested for.

Optional<Student> student = dao.findById(999);


if (student.isPresent()) {
System.out.println(student.get());
} else {
System.out.println("NA");
}

b. findAll(): Returns all entities

Iterable<Student> students = dao.findAll();


// for (Student s : students) {
// System.out.println(s);
// }
students.forEach(System.out::println);

c. findAllById(): Returns collection of entities for the given collection of PKs

Iterable<Student> students = dao.findAllById(Arrays.asList(101, 103));


students.forEach(System.out::println);

d. existById(): Checks the given PK is available or not

System.out.println(dao.existsById(101));
System.out.println(dao.existsById(999));

e. count(): Returns the number of the rows in the table

System.out.println(dao.count());

Delete:

a. delete(): Deletes the given entity if available.

Student student = new Student();


student.setRollNo(100);
student.setName("David");
student.setMarks(9.94);
dao.delete(student);

b. deleteById(): Deletes the given PK if available.

dao.deleteById(103);

c. deleteAllById(): Deletes rows for the given collection of PKs

dao.deleteAllById(Arrays.asList(101, 102, 999));

d. deleteAll(Iterable<E>): It is the repeated task of delete() method.

Student student1 = new Student();


student1.setRollNo(101);
student1.setName("Robert");
student1.setMarks(8.31);

Student student2 = new Student();


student2.setRollNo(103);
student2.setName("Harisson");
student2.setMarks(8.11);

Student student3 = new Student();


student3.setRollNo(999);
student3.setName("Phillip");
student3.setMarks(7.54);

dao.deleteAll(Arrays.asList(student1, student2, student3));

e. deleteAll(): Truncates the table

// TRUNCATE TABLE student;


dao.deleteAll();

HW: Develop the L3 and implement all 12 methods. Build one console based switch-
case application.

a. Insert
b. Update
c. Read
d. Delete

findBy methods:

The findBy methods in Spring Data JPA's CrudRepository enable query derivation
based on method names. This means that you can define methods in your repository
interface, and Spring will automatically generate the necessary query logic based
on the method's name. These methods follow a specific naming convention to create
queries dynamically, allowing you to perform database operations without explicitly
writing query code. This is called DSL (Domain Specific Language).

Basic Structure of findBy Methods

The general syntax of a findBy method is:


findBy<Property>[Optional Keyword]

• findBy: This is the keyword indicating that you want to retrieve entities
from the database.
• <Property>: This refers to the field in your entity class that you want to
query by. The field name must match an attribute in your entity.
• [Optional Keyword]: You can add keywords like And, Or, Between, LessThan,
Like, etc., to build more complex queries.
Query Derivation Keywords

And

findByFirstNameAndLastName(String firstName, String lastName)

SELECT * FROM user WHERE first_name = ? AND last_name = ?

Or

findByFirstNameOrLastName(String firstName, String lastName)

SELECT * FROM user WHERE first_name = ? OR last_name = ?

Between

findByAgeBetween(int startAge, int endAge)

SELECT * FROM user WHERE age BETWEEN ? AND ?

LessThan

findByAgeLessThan(int age)

SELECT * FROM user WHERE age < ?

GreaterThan

findByAgeGreaterThan(int age)

SELECT * FROM user WHERE age > ?

Like

findByEmailLike(String email)

SELECT * FROM user WHERE email LIKE ?

OrderBy

findByLastNameOrderByFirstNameAsc(String lastName)

SELECT * FROM user WHERE last_name = ? ORDER BY first_name ASC


Not

findByLastNameNot(String lastName)

SELECT * FROM user WHERE last_name <> ?

IsNull

findByEmailIsNull()

SELECT * FROM user WHERE email IS NULL

IsNotNull

findByEmailIsNotNull()

SELECT * FROM user WHERE email IS NOT NULL

True/False

findByActiveTrue() / findByActiveFalse()

Custom Query:

1. Using SQL
2. Using JPQL

You need to specify the query using @Query annotation.

@Query: It is used to execute a custom query.

Note #1:
If you are executing native SQL you must provide nativeQuery = true.
If you are executing JPQL / HQL by default it is false.

Note #2:
By default it will call executeQuery() method. That's why if you are giving non-
select query, it will through you the Exception.

@Modifying:
This annotation works for @Query. It tells the @Query that the given query is non-
select.
So it will use executeUpdate() method. That's why it's return type is Integer.

@Transactional:
Our Data JPA uses Hibernate. In Hibernate, every update operation happens inside
the transaction. That's why here also every non-select query you must execute as a
transaction.
This annotation handles the given query as a transaction.

JPQL:
1. Select Query
2. Update/Delete Query
3. Parameters: It supports both positional and named parameter

SQL: SELECT * FROM book


JPQL: FROM Book

SQL: SELECT * FROM book WHERE book_price=2000


JPQL: FROM Book WHERE bookPrice=2000

SQL: SELECT * FROM book WHERE book_price>=2000 AND book_name='Spring'


JPQL: FROM Book WHERE bookPrice>=2000 AND bookName='Spring'

SQL: SELECT book_isbn FROM book


JPQL: SELECT bookIsbn FROM Book

Note: Maintain the Class - Object Relationship:

SQL: SELECT book_isbn FROM book


JPQL: SELECT b.bookIsbn FROM Book b

SQL: SELECT * FROM book WHERE book_name=?

Positional Placeholder:
JPQL: FROM Book b WHERE b.bookName=?1

Named Parameter:
JPQL: FROM Book b WHERE b.bookName=:name

Note: You can map the method argument with the JPQL parameter name using @Param
Note: In JPQL also, if you are trying to execute any non-select query, you have to
use @Modifying and @Transactional

SQL: SELECT age FROM user WHERE email LIKE '%example%'

JPQL: SELECT u.age FROM User u WHERE u.email LIKE %:emailPart%

Custom Return Type:

@Query("SELECT u.firstName, u.lastName FROM User u WHERE u.email LIKE %:emailPart


%")
public List<Object[]> m5(@Param("emailPart") String emailPart);

List<Object[]> userNames = dao.m5("example");


for (Object[] name : userNames) {
System.out.println("First Name: " + name[0] + " Last Name: " + name[1]);
}

JPQL vs SQL:

#1:
JPQL is DB independent
SQL is DB dependent

#2:
JPQL follows the Java Entity.
SQL follows DB Table.

#3:
Performance wise SQL is better.
Maintenance wise JPQL is better.

#4:
SQL can directly execute in DB
JPQL can't directly execute in DB

ListCrudRepository:

It is related to the CRUD operations of CrudRepository which returns collection


object.

1. saveAll()

2. findAll()

3. findAllById()
PagingAndSortingRepository:

::Sorting::

findAll(Sort sort)

1. Sort.by(properties[])

Sort sort = Sort.by("name", "sal");


findAll(sort.descending())

Order.by(property)
Order.asc(property)
Order.desc(property)

2. Sort.by(Order[])

3. Sort.by(List<Order>)

4. Sort.by(Direction.DESC, properties[])

::Paging::

If we are retrieving the records from the table as a page one by one and each page
contains a limited or pre-defined number of rows. The technique is called paging.

Page findAll(Pageable pageable)

--Page--
Iterable
Streamable
Slice
Page

get() => Stream


getContent() => List

--Pageable--

PageRequest.ofSize(int pageSize)

PageRequest.of(int pageNumber, int pageSize)


PageRequest.of(int pageNumber, int pageSize, Sort sort)
PageRequest.of(int pageNumber, int pageSize, Direction, properties[])

PageRequest.of(0, 2)
PageRequest.of(1, 5)

HW:

Create a Xyz table.


Map with Entity.
Use PagingAndSortingRepository
Set the page to 10.
Insert min 50 records in the table.
and design a console based application where if user is giving
input (1) you will go to next page.
input (0) you will go to previous page.

Problem:
Find All entities of user table where first_name is 'David'

Solution:
List<User> findAllByFirstName(String firstName)

Now, assume you have so many properties in your Entity class. In the same way you
want to define the methods like:
findAllByFirstName
findAllByLastName
findAllByEmail
findAllByAge
findAllByActive

findAllByFirstNameAndLastName
...

So, Total possible combinations: 5C1 + 5C2 + 5C3 + 5C4 + 5C5 = 31

For n non-primary key properties, you have to create: nC1 + nC2 + ... + nCn

QueryByExampleExecutor:

You can go for this approach when you want to generate the query dynamically
depending on the situation.

methods:

findOne(Example example)
findAll(Example example)
findAll(Example example, Sort sort)
findAll(Example example, Pageable pageable)

--Example--

of(probe)
of(probe, ExampleMatcher)

When you are getting the probe property dynamically.

--ExampleMatcher--

matching()
matchingAny() [Checks for any of the given property in
example is matching or not. So, it is OR operation]
matchingAll() ---> Default [Checks for all of the given property in example is
matching or not. So, it is AND operation]

JpaRepository:

1. Return type of the findAll(Example) is List type.


2. It supports batch operation.
3. It supports flushing. So, you can ensure immediate change.

getReferenceById():

It just return you the reference to the entity (row). It don't give the
understandable information.
When you are required to get a particular reference of any entity object (row) for
any other query.
You can also use this reference to get a small amount of data.
This small data must be related to your @Id property.

Annotations we have learnt:

@Entity
@Id
@Query
@Modifying
@Transactional
@Param
@NoReposioryBean

Problem: For MySQL why caseSensitive matching is not working by default?

Sol #1: Change the collation of table to utf8_bin

ALTER TABLE user MODIFY first_name VARCHAR(255) COLLATE utf8_bin;


Sol #2: Use Native Query

SELECT * FROM user WHERE BINARY first_name = 'DaViD'

Sol #3: Use JQPL

SELECT * FROM User u WHERE u.firstName LIKE BINARY :name

How to implement Projection without using Query?

Create a sub dto class of your entity class. make this class as return type.

Utility Annotations:

Set a different table name of your entity class:

@Entity
@Table(name = "table_name")

Set a different column name of a column of the table:

@Column(name = "column_name")

TimeStamping:

It is used to insert CREATED_DATE & UPDATED_DATE columns values for the record

@CreationTimeStamp : To populate record created date

@UpdateTimeStamp : To populate record updated date

To prevent updation of a property, we can use:


@Column(updatable = false)

Similarly, To prevent insertion of a property, we can use:


@Column(insertable = false)

Generators:

Oracle, PostgreSQL -> Sequence


MySQL, MSSQL -> AI

Enq_Id_Gen

next_val
0

select next_val+1 as next from Enq_Id_Gen;


insert into enquery (id) values (next);
update Enq_Id_Gen set next_val=next_val+1;
@GeneratedValue

+--------+
| enq_id |
+--------+
| 001 |
| 002 |
| 052 |
| 102 |
| 152 |
| 202 |
| 252 |
| 302 |
+--------+

update enquiry_seq set next_val= ? where next_val=?

generation strategies:

IDENTITY (AI)
SEQUENCE (Sequence)
TABLE (Separate Table)
AUTO

Custom Generators:

Example:

OD001
OD002
OD003

prefix + suffix

prefix = "OD"
suffix = AI

Use the following:

Interface: IdentifierGenerator
method: void generate()

-----------------------------------------------------------------------------------
----------------------------------------------------------------
public class EnquirySequenceGenerator implements IdentifierGenerator {

private static final long serialVersionUID = 1L;

private void init(SharedSessionContractImplementor session) {

// Create a table if not available


String query1 = "CREATE TABLE ENQ_SEQ(NEXTVAL INT)";

// set the default value of the NEXTVAL as 0 or 1


String query2 = "INSERT INTO ENQ_SEQ (NEXTVAL) VALUES (1)";

Connection con;
Statement st;
try {
con = session.getJdbcConnectionAccess().obtainConnection();
st = con.createStatement();
st.executeUpdate(query1);
st.executeUpdate(query2);
} catch (SQLException e) {
// ignore
}

@Override
public Serializable generate(SharedSessionContractImplementor session, Object
object) {

String prefix = "OD";


String suffix = null;

// Get the next seq value


try {

init(session);

String query1 = "SELECT NEXTVAL FROM ENQ_SEQ";


String query2 = "UPDATE ENQ_SEQ SET NEXTVAL=NEXTVAL+1"; // increment
the value

Connection con = session.getJdbcConnectionAccess().obtainConnection();


Statement st = con.createStatement();

ResultSet rs = st.executeQuery(query1);
if (rs.next()) {
suffix = String.format("%010d", rs.getInt(1));
}

st.executeUpdate(query2);

} catch (SQLException e) {
e.printStackTrace();
}

return prefix + suffix;


}

}
-----------------------------------------------------------------------------------
----------------------------------------------------------------

Making a property NOT NULL

@Basic(optional = false)
@Column(nullable = false)

Composite Primary Key

More than one primary keys in a single db table


create table enq_tbl (
date_created time(6),
date_updated time(6),
enq_status bit not null,
enq_id varchar(255) not null,
enq_name varchar(255) not null,
primary key (enq_id, enq_name)
)

@Embeddable: class level annotation.

@EmbeddedId: Alternate of @Id for @Embeddable.

HW:

Custom generator for Oracle DB.

JOIN Operations using Data JPA:

types w.r.t. RDBMS:

1. self join
2. inner join
3. left outer join
4. right outer join

types w.r.t. Data JPA:

1. One to One
2. One to Many
3. Many to Many

One to One:

In this relationship, one entity is related to exactly one other entity.

--------------------------------------------------Entity
1--------------------------------------------------
@Entity
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id", referencedColumnName = "id")
private Profile profile;

--------------------------------------------------Entity
2--------------------------------------------------
@Entity
public class Profile {

@Id
private Integer id;
private String name;
private Double age;

Note:

@JoinColumn is used to specify that the specific column is required to store the
foreign key. And it is not a simple column.
foreign key column default name: {@JoinColumn}_{@Id}

cascading:

• ALL = PERSIST + MERGE + REMOVE + REFRESH + DETACH


• PERSIST: If you save some entity to the parent entity, the child entity will
also be saved.
• MERGE: If you update (merge) some entity to the parent entity, the child
entity will also be updated.
• REMOVE: If you delete some entity to the parent entity, the child entity will
also be delete.
• REFRESH: If you refresh some entity to the parent entity, the child entity
will also be refresh.
• DETACH: If the parent entity is detached from persistence context, child
entity will also be detached.
--------------------------------------------------One-To-One-
Test--------------------------------------------------
@Autowired
private UserRepo repo;

public void test() {


User user = new User();
Profile profile = new Profile();

profile.setId(101);
profile.setName("David Jones");
profile.setAge(36.0);

user.setUsername("david@123");
user.setProfile(profile);

repo.save(user);

One to Many:

In this relationship, one entity is related to many other entities.

--------------------------------------------------Entity
1--------------------------------------------------
@Entity
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String name;

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn
private Department department;

@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}

--------------------------------------------------Entity
2--------------------------------------------------
@Entity
public class Department {

@Id
private Integer id;

private String name;

@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, fetch =


FetchType.EAGER)
private List<Employee> employees;

@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + ", employees=" +
employees + "]";
}

Note:

@OneToMany(mappedBy = "department"): By specifying this on Department class we are


saying the mapping logic is specified in the department field of the Employee
class. So, no need to write the join logic again.

--------------------------------------------------Many-To-One-
Test--------------------------------------------------
@Autowired
private EmployeeRepo repo;

public void test() {

Department department = new Department();


department.setId(101);
department.setName("SIT");

Employee employee1 = new Employee();


employee1.setId(101);
employee1.setName("Robert");
employee1.setDepartment(department);
Employee employee2 = new Employee();
employee2.setId(102);
employee2.setName("Harrison");
employee2.setDepartment(department);

repo.saveAll(List.of(employee1, employee2));

--------------------------------------------------One-To-Many-
Test--------------------------------------------------
@Autowired
private DepartmentRepo repo;

public void test() {

Optional<Department> dept = repo.findById(101);


if (dept.isPresent()) {
dept.get().getEmployees().forEach((emp) ->
System.out.println(emp.getName()));
}

}
Many to Many

In this relationship, one entity can relate to multiple entities and vice versa.
Example: Relation between Student & Course

--------------------------------------------------Entity
1--------------------------------------------------
@Entity
public class Student {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@ManyToMany
@JoinTable( // define a (bridge) new table for join
name = "student_course", // name of the join table
joinColumns = @JoinColumn(name = "student_id"), // Foreign key to student
table
inverseJoinColumns = @JoinColumn(name = "course_id") // Foreign key to
course table
)
private Set<Course> courses;

--------------------------------------------------Entity
2--------------------------------------------------
@Entity
public class Course {

@Id
private Long id;

private String name;


@ManyToMany(mappedBy = "courses")
private Set<Student> students;

Note:

By using @JoinTable we want to create a new bridge table for the two entities and
we can also specify the table configuration by providing the values.

--------------------------------------------------Many-To-Many-
Test--------------------------------------------------
Saving a Student and Course

@Autowired
private StudentRepo repo;

public void test() {


Course course1 = new Course();
course1.setId(1001L);
course1.setName("Core Java");

Course course2 = new Course();


course2.setId(1002L);
course2.setName("Advanced Java");

Student student = new Student();


student.setName("Anya");
student.setCourses(Set.of(course1, course2));

// course1.setStudents(Set.of(student));

repo.save(student);

Note: Cross Join can not be implemented in Data JPA. You can use native query.

Remaining Topic:

1. Auditing (Needs Spring Security)

Conclusion:

1. We are going to use Data JPA everyday instead of Java JDBC.

You might also like