2022. 9. 28. 10:35ใ์ธํ๋ฐ/์ค์ ! ์คํ๋ง ๋ฐ์ดํฐ JPA
0๏ธโฃ ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ
- ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ
- NamedQuery
- @Query : ๋ฆฌํ์งํ ๋ฆฌ ๋ฉ์๋์ ์ฟผ๋ฆฌ ์ ์
- ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
- ๋ฐํ ํ์
- ํ์ด์ง๊ณผ ์ ๋ ฌ
- ๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ
- @EntityGraph
-> ์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ ์ถ๊ฐ๋ก ๋ ํธํ๊ฒ ์ฌ์ฉํ๋๋ก ์ ๊ณตํด์ฃผ๋ ๊ธฐ๋ฅ๋ค!!
- ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ 3๊ฐ์ง
- ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ
- ๋ฉ์๋ ์ด๋ฆ์ผ๋ก JPA NamedQuery ํธ์ถ
- @Query ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด์ ๋ฆฌํฌ์งํ ๋ฆฌ ์ธํฐํ์ด์ค์ ์ฟผ๋ฆฌ ์ง์ ์ ์
1๏ธโฃ ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ
: ๋ฉ์๋ ์ด๋ฆ์ ๋ถ์ํด์ JPQL ์ฟผ๋ฆฌ ์คํ
- ์์ JPA ๋ฆฌํฌ์งํ ๋ฆฌ
MemberJpaRepository
-> Member๊ฐ์ฒด์์ ์ด๋ฆ์ด ๊ฐ๊ณ , ๋์ด๊ฐ ์ฃผ์ด์ง ๋์ด๋ณด๋ค ๋ง์ ํ์ ์กฐํํ๋ ์ฟผ๋ฆฌ์ด๋ค.
์์ JPA ๋ฆฌํฌ์งํ ๋ฆฌ์์๋ ๊ทธ๋ฅ ์กฐ๊ฑด์ ๋ง๊ฒ JPQL ์ฟผ๋ฆฌ๋ฅผ ์งฐ๋ค.
MemberJpaRepositoryTest
jpql์ sql๋ก ๋ณํ๋์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์กํ๋ค.
- ์คํ๋ง ๋ฐ์ดํฐ JPA
MemberRepository
-> ์คํ๋ง ๋ฐ์ดํฐ JPA์์๋ ์์ JPA์๋ ๋ค๋ฅด๊ฒ ๊ทธ๋ฅ ๋ฉ์๋ ์ด๋ฆ์ ๋ถ์ํด์ JPQL์ ์์ฑํด์ ์คํํด์ค๋ค..!!!
(์์ ๋ง๋ฒ๊ฐ์.. ์งฑ์ ๊ธฐ)
MemberRepositoryTest
์์ MemberJpaRepositoryTest์ ๋์ผํ ํ ์คํธ์ธ๋ฐ ์ด๋ฒ์๋ ์คํ๋ง ๋ฐ์ดํฐ jpa๋ฅผ ์ฌ์ฉํด์ ํ ์คํธํ๋ ๊ฒ์ผ๋ก ๊ฒฐ๊ณผ๋ ์์์ ํ๋ ํ ์คํธ์ ๋์ผํ๋ค.
์ฟผ๋ฆฌ ๋ฉ์๋ ํํฐ ์กฐ๊ฑด
Spring Data JPA - Reference Documentation
Spring Data JPA - Reference Documentation
Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del
docs.spring.io
Keyword Sample JPQL snippet
Distinct | findDistinctByLastnameAndFirstname | select distinct …โ where x.lastname = ?1 and x.firstname = ?2 |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is, Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull, Null | findByAge(Is)Null | … where x.age is null |
IsNotNull, NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ ์ ๊ณตํ๋ ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ
- ์กฐํ: find...By, read...By, query..By, get...By
Spring Data JPA - Reference Documentation
Spring Data JPA - Reference Documentation
Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del
docs.spring.io
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
์) findHelloBy ์ฒ๋ผ ...์๋ ์๋ณํ๊ธฐ ์ํ ๋ด์ฉ(์ค๋ช )์ด ๋ค์ด๊ฐ๋ ๋๋ค.
COUNT: count...By ๋ฐํํ์ long
EXISTS: existst...By ๋ฐํํ์ boolean
์ญ์ : delete...By, remove...By ๋ฐํํ์ long
DISTINCT: findDistinct, findMemberDistinctBy
LIMIT: findFirst3, findFirst, findTop, findTop3
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/ #repositories.limit-query-result
Spring Data JPA - Reference Documentation
Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del
docs.spring.io
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
โ ์ฐธ๊ณ
์ด ๊ธฐ๋ฅ์ ์ํฐํฐ์ ํ๋๋ช ์ด ๋ณ๊ฒฝ๋๋ฉด ์ธํฐํ์ด์ค์ ์ ์ํ ๋ฉ์๋ ์ด๋ฆ๋ ๊ผญ ํจ๊ป ๋ณ๊ฒฝํด์ผ ํ๋ค!!
๊ทธ๋ ์ง ์์ผ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์์ํ๋ ์์ ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค!!
์ด๋ ๊ฒ ์ ํ๋ฆฌ์ผ์ด์ ๋ก๋ฉ ์์ ์ ์ค๋ฅ๋ฅผ ์ธ์งํ ์ ์๋ ๊ฒ์ด ์คํ๋ง ๋ฐ์ดํฐ JPA์ ๋งค์ฐ ํฐ ์ฅ์ ์ด๋ค.
2๏ธโฃ JPA NamedQuery
: Jpa์ NamedQuery๋ฅผ ํธ์ถํ ์ ์์.
Member
Member ๊ฐ์ฒด์ @NamedQuery ์ด๋ ธํ ์ด์ ์ผ๋ก Naemd ์ฟผ๋ฆฌ ์ ์ํ ์ ์๋ค.
๊ทธ๋ฌ๋ฉด ํด๋น select m from Member ~ ์ฟผ๋ฆฌ์ ์ด๋ฆ์ Member.findByUsername์ด๋ค.
- ์์ JPA๋ฅผ ์ฌ์ฉํด์ Named ์ฟผ๋ฆฌ ํธ์ถ
MemberJpaRepository
์ด๋ฐ์์ผ๋ก createNamedQuery ๋ฉ์๋ฅผ ์ฌ์ฉํด์ ์๊น Member์ ์ง์ ํ๋ NamedQeury ์ด๋ฆ์ ์ค์ ํด์ค๋ค.
- ์คํ๋ง ๋ฐ์ดํฐ JPA๋ก NamedQuery ์ฌ์ฉ
MemberRepository
@Query(name = "Member.findByUsername") ์ด๊ฑธ ์๋ตํด๋ ๋์ํ๋ค!!
๊ทธ๋ด ์ ์๋ ์ด์ ๊ฐ ์ฐ์ ํด๋น ๋ฉ์๋ ์ด๋ฆ์ ๊ฐ์ง๊ณ NamedQuery๋ฅผ Member๊ฐ์ฒด์์ ์ฐพ๋๋ฐ,
๊ฐ์ ์ด๋ฆ์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ NamedQuery๋ฅผ ์ฌ์ฉํ๋ค.
๊ทผ๋ฐ ๋ง์ฝ Member ๊ฐ์ฒด์ ๋์ผํ ๋ฉ์๋ ์ด๋ฆ์ ๊ฐ์ง NamedQuery๊ฐ ์๋ค๋ฉด ๊ทธ๋ ๋ฉ์๋ ์ด๋ฆ์ ๊ธฐ์ค์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๋ฐฉ์์ด๋ค.
โ ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ์ ์ธํ "๋๋ฉ์ธ ํด๋์ค + .(์ ) + ๋ฉ์๋ ์ด๋ฆ"์ผ๋ก Named ์ฟผ๋ฆฌ๋ฅผ ์ฐพ์์ ์คํ
๊ทธ๋ฆฌ๊ณ ๋ง์ฝ ์คํํ Named ์ฟผ๋ฆฌ๊ฐ ์์ผ๋ฉด ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ ์ ๋ต์ ์ฌ์ฉํ๋ค.
(ํ์ํ๋ฉด ์ ๋ต์ ๋ณ๊ฒฝํ ์๋ ์๋๋ฐ ๊ตณ์ด ๊ถ์ฅํ์ง๋ ์๋๋ค๊ณ ํ๋ค.)
3๏ธโฃ @Query, ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉ์๋์ ์ฟผ๋ฆฌ ์ ์ํ๊ธฐ
MemberRepository
: ๋ฉ์๋์ JPQL ์ฟผ๋ฆฌ ์์ฑ
@org.springframework.data.jpa.repository.Query ์ด๋ ธํ ์ด์ ์ฌ์ฉ
์คํํ ๋ฉ์๋์ ์ ์ ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ๋ฏ๋ก ์ด๋ฆ ์๋ Named ์ฟผ๋ฆฌ๋ผ๊ณ ํ ์ ์๋ค.
JPA Named ์ฟผ๋ฆฌ์ฒ๋ผ ์ ํ๋ฆฌ์ผ์ด์ ์คํ ์์ ์ ๋ฌธ๋ฒ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ ์ ์์ (์์ฒญ๋ ์ฅ์ !!)
-> ์คํ๋ง ๋ฐ์ดํฐ jpa๋ ๋จผ์ ์ ํ๋ฆฌ์ผ์ด์ ์คํ ์์ ์ ์ฟผ๋ฆฌ๋ค์ ๋ชจ๋ ํ์ฑํด์ sql๋ก ๋ณํํ๋ ์์ ์ ํ๋ค๊ณ ํ๋ค.
๊ทผ๋ฐ ํ์ฑํ๋ฉด์๋ ์ค๋ฅ๊ฐ ์๋์ง ํ์ธํ ์ ์๋ค!!!
โ ์ฐธ๊ณ
์ค๋ฌด์์๋ ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ ๊ธฐ๋ฅ์ ํ๋ผ๋ฏธํฐ๊ฐ ์ฆ๊ฐํ๋ฉด ๋ฉ์๋ ์ด๋ฆ์ด ๋งค์ฐ ์ง์ ๋ถํด์ง๋ค...! (์กฐ๊ฑด์ด ๋ง์์ง๋ฉด ๋ฉ์๋ ์ด๋ฆ์ด ๋๋ฌด ๊ธธ์ด์ง๋๊น..)
๋ฐ๋ผ์ @Query ๊ธฐ๋ฅ์ ํจ์ฌ ๋ง์ด ์ฌ์ฉํ๋ค๊ณ ํ๋ค~~
๐ @Query ๊ฐ DTO๋ก ์กฐํํ๊ธฐ
JPA ํ์ฉํธ์ ๋ฃ๋ค๋ณด๋ฉด ์ ์ ์๋ ๋ด์ฉ์ธ๋ฐ ์ํฐํฐ๋ฅผ ์ง์ ๋ฐํํด์ ์ํฐํฐ๋ฅผ ๋ ธ์ถํ๋ ๋ฐฉ๋ฒ์ ๊ถ์ฅํ๋ ๋ฐฉ๋ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ DTO๋ก ๋ณํํด์ ๋ฐํํ๋ผ๊ณ ํ๋ค.
๊ทธ๋ด๋ ์คํ๋ง ๋ฐ์ดํฐ JPA์์๋ JPA์ ๋์ผํ ๋ฐฉ์์ ์ ๊ณตํด์ค๋ค.
MemberDto
-> MemberDto์๋ id์ username, teamName ์ ๋๋ง ํ์ํ๋ค๊ณ ๊ฐ์ ํ๋ค.
MemberRepository
๊ทธ๋ฆฌ๊ณ @Query ์ด๋ ธํ ์ด์ ์์ Jpa ์ new ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํด์ DTO๋ก ์กฐํํ๊ณ ์ ํ๋ ๊ฐ๋ค์ ๋์ดํด์ฃผ๋ฉด ๋๋๋ฐ,
์ด๋ ํด๋น ๊ฐ๋ค์ด ๋ค์ด๊ฐ๋ ์์ฑ์๊ฐ ํ์ํ๋ค.
MemberRepositoryTest
4๏ธโฃ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
- ์์น ๊ธฐ๋ฐ
- ์ด๋ฆ ๊ธฐ๋ฐ
-> ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์๋ ์์น ๊ธฐ๋ฐ๊ณผ ์ด๋ฆ ๊ธฐ๋ฐ ๋๊ฐ๊ฐ ์๋๋ฐ ์์น ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉด ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ฉด ์ ๋ถ ๋ฐ๊ฟ์ค์ผ ํ๊ธฐ ๋๋ฌธ์ ์ ์ง๋ณด์์ฑ์ด ์์ข๊ธฐ ๋๋ฌธ์ ์ด๋ฆ ๊ธฐ๋ฐ์ผ๋ก ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค!
MemberRepository
-> :name์ด๋ฉด @Param("name") ํ๊ณ String username์ด๋ฉด ์ด์ username์ผ๋ก ๋ฐ์์จ ๊ฐ์ ์คํ๋ง ๋ฐ์ดํฐ jpa๊ฐ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉํด์ค๋ค.
์ปฌ๋ ์ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
: Collection ํ์ ์ผ๋ก in์ ์ง์
MemberRepositoryTest
in ์ ์ ์ฌ์ฉํด์ memberA์ memberB๋ฅผ ์กฐํํด์ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
5๏ธโฃ ๋ฐํ ํ์
: ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ์ ์ฐํ ๋ฐํ ํ์ ์ ์ง์ํ๋ค.
MemberRepository
https://docs.spring.io/spring-data/jpa/docs/current/reference/ html/#repository-query-return-types
MemberRepositoryTest
-> ์กฐํ ์ฟผ๋ฆฌ๋ ๋ชจ๋ ๋๊ฐ์๋ฐ ๋ฐํ ํ์ ๋ง ๋ค๋ฅด๊ฒ ์ด๋ฃจ์ด์ง๋ค.
โ ์กฐํ ๊ฒฐ๊ณผ๊ฐ ๋ง๊ฑฐ๋ ์๋ค๋ฉด..?
์ปฌ๋ ์ :
- ๊ฒฐ๊ณผ ์์: ๋น ์ปฌ๋ ์ ๋ฐํ
๋จ๊ฑด ์กฐํ:
- ๊ฒฐ๊ณผ ์์: null ๋ฐํ
- ๊ฒฐ๊ณผ๊ฐ 2๊ฑด ์ด์: javax.persistence.NonUniqueResultException ์์ธ ๋ฐ์
โ ์ฐธ๊ณ
๋จ๊ฑด์ผ๋ก ์ง์ ํ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ์คํ๋ง ๋ฐ์ดํฐ JPA๋ ๋ด๋ถ์์ JPQL์ Query.getSingleResult() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
์ด ๋ฉ์๋๋ฅผ ํธ์ถํ์ ๋, ์กฐํ ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด NoResultException ์์ธ๊ฐ ๋ฐ์ํ๋๋ฐ ๊ฐ๋ฐ์ ์ ์ฅ์์ ๋ค๋ฃจ๊ธฐ๊ฐ ์๋นํ ๋ถํธํ๋ค..
์คํ๋ง ๋ฐ์ดํฐ JPA๋ ๋จ๊ฑด์ ์กฐํํ์ ๋ ์ด ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ์์ธ๋ฅผ ๋ฌด์ํ๊ณ ๋์ ์ NULL ๊ฐ์ ๋ฐํํด์ค๋ค.
6๏ธโฃ ์์ JPA ํ์ด์ง๊ณผ ์ ๋ ฌ
- JPA ํ์ด์ง ๋ฆฌํฌ์งํ ๋ฆฌ ์ฝ๋
MemberJpaRepository
JPQL์์ ํ์ด์งํ ๋๋ setFirstResult๋ก ์ฒ์ ๊ฐ์ ธ์ฌ ๊ฐ์ ์ค์ ํด์ฃผ๊ณ , setMaxResult๋ฅผ ํตํด ๊ฐ์ ธ์ฌ ๋ฐ์ดํฐ ์๋ฅผ ์ง์ ํด์ค๋ค.
๊ทธ๋ฆฌ๊ณ ํ์ด์งํ ๋ totalCount๋ ํ์ํ ๊ฒฝ์ฐ๊ฐ ๋ง์๋ฐ ์ด๋ด ๋๋ฅผ ์ํด ๋ฐ๋ก ๋ฉ์๋๋ฅผ ํ๋ ๋ ๋ง๋ค์ด์ค๋ค.
count ๊ฐ์ ๊ตฌํ ๋๋ ๊ตณ์ด order by๋ฅผ ํด์ค ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด์ ์ ๋ ฌ์ ์๋ตํ๋ค.
MemberJpaRepositoryTest
-> limit์ ํตํด ํ์ด์ง์ ํ๋ ์ฟผ๋ฆฌ๋ฅผ ํ์ธํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ offset์ ์ด์ฐจํผ 0๋ถํฐ ์์์ด๋ผ ์๋ตํด์ค ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ count๊ฐ๋ ๊ตฌํด์จ ์ฟผ๋ฆฌ๋ฅผ ํ์ธํ ์ ์๋ค.
7๏ธโฃ ์คํ๋ง ๋ฐ์ดํฐ JPA ํ์ด์ง๊ณผ ์ ๋ ฌ
ํ์ด์ง๊ณผ ์ ๋ ฌ ํ๋ผ๋ฏธํฐ
-org.springframework.data.domain.Sort : ์ ๋ ฌ ๊ธฐ๋ฅ
-org.springframework.data.domain.Pageable : ํ์ด์ง ๊ธฐ๋ฅ( ๋ด๋ถ์ Sort ํฌํจ)
ํน๋ณํ ๋ฐํ ํ์
-org.springframework.data.domain.Page : ์ถ๊ฐ count ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํฌํจํ๋ ํ์ด์ง
-org.springframework.data.domain.Slice : ์ถ๊ฐ count ์ฟผ๋ฆฌ ์์ด ๋ค์ ํ์ด์ง๋ง ํ์ธ ๊ฐ๋ฅ
-> ๋ด๋ถ์ ์ผ๋ก limit + 1 ์กฐํ
- List(์๋ฐ ์ปฌ๋ ์ ) : ์ถ๊ฐ count ์ฟผ๋ฆฌ ์์ด ๊ฒฐ๊ณผ๋ง ๋ฐํ
MemberRepository
Page๋ count ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก count ์ฟผ๋ฆฌ๋ฅผ ๋ถ๋ฆฌํด์ ๋ค์๊ณผ ๊ฐ์ด ์ง์ ํด ์ค ์๋ ์๋ค.
๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ Pageable ์ ์ธํฐํ์ด์ค๋ก ์ค์ ์ฌ์ฉํ ๋๋ ํด๋น ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ org.springframework.data.domain.PageRequest ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค.
MemberRepositoryTest
PageRequest ์์ฑ์์ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ํ์ฌ ํ์ด์ง๋ฅผ, ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ์กฐํํ ๋ฐ์ดํฐ ์๋ฅผ ์ ๋ ฅํด์ค๋ค.
์ฌ๊ธฐ์ ์ด์ ์ถ๊ฐ๋ก ์ ๋ ฌ ์ ๋ณด๋ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉํ ์ ์๋ค.
(์ฐธ๊ณ ๋ก ํ์ด์ง๋ 0๋ถํฐ ์์ํ๋ค.)
โ ์ฃผ์: page๋ ์์์ด 1๋ถํฐ๊ฐ ์๋๋ผ 0๋ถํฐ์ด๋ค.
-> ๋ฐํํ์ ์ด Page ์ผ๋๋ count ์ฟผ๋ฆฌ๋ ํจ๊ป ๋๊ฐ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ฐํ ํ์ ์ Slice๋ก ๋ฐ๊ฟ์ ํ ์คํธ ์ฝ๋๋ฅผ ์คํํ๋ฉด ์ด๋ Slice์ผ ๋๋ ์ง์ํ์ง ์๋ ๋ฉ์๋๋ค์ด ์๊ธฐ ๋๋ฌธ์ ๊ทธ๊ฒ๋ค์ ์ฃผ์ ์ฒ๋ฆฌ๋ฅผ ํด์คฌ๋ค.
(๊ทธ๋ฆฌ๊ณ MemberRepository ์ธํฐํ์ด์ค์ findByAge ๋ฉ์๋ ๋ฐํํ์ ๋ Slice๋ก ๋ณ๊ฒฝํด์ค์ผ ํ๋ค!!)
-> ์ด๋๋ count ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ์ง ์๊ณ select ํ์ด์ง ์ฟผ๋ฆฌ๋ง ๋๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ Slice๋ก ํ๋ฉด limit์ด 3์ด ์๋๋ผ 4๊ฐ ๋๊ฐ๋๋ฐ ์ด์ ๋ limit+1์ ์กฐํํด์ ๋ค์ ํ์ด์ง ์ฌ๋ถ๋ฅผ ํ์ธํ๊ธฐ ์ํด์ +1 ํด์ ์กฐํํด์ค๋ค.
ํ์ด์ง๋ฅผ ์ ์งํ๋ฉด์ ์ํฐํฐ๋ฅผ dto๋ก ๋ณํํ๊ธฐ
Page์ map ๋ฉ์๋๋ฅผ ์ด์ฉํด์ Member ์ํฐํฐ๋ฅผ dto๋ก ๋ณํํ ์ ์๋ค.
8๏ธโฃ ๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ
- JPA๋ฅผ ์ฌ์ฉํ ๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ
MemberJpaRepository
-> ๊ทธ๋ฅ jpql๋ก update ์ฟผ๋ฆฌ๋ฅผ ์งฐ๋๋ฐ ์ด๋ m.age๊ฐ ํ๋ผ๋ฏธํฐ๋ก ์ฃผ์ด์ง age๋ณด๋ค ๊ฐ์ด ํฌ๋ฉด ํด๋น Member๊ฐ์ฒด์ age๋ฅผ +1์ฉ ์ฆ๊ฐํ๋ ๋ฒํฌ ์์ ์ฟผ๋ฆฌ์ด๋ค.
MemberJpaRepositoryTest
- ์คํ๋ง ๋ฐ์ดํฐ JPA๋ฅผ ์ฌ์ฉํ ๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ
MemberRepository
๋ฒํฌ์ฑ ์์ , ์ญ์ ์ฟผ๋ฆฌ์๋ @Modifying ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด์ผ ํ๋ค.
์ฌ์ฉํ์ง ์์ผ๋ฉด org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations ์์ธ ๋ฐ์ํ๋ค.
๐ ๋ฒํฌ์ฑ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ณ ๋์ ์์์ฑ ์ปจํ ์คํธ ์ด๊ธฐํ: @Modifying(clearAutomatically = true)
: ์ด ์ต์ ์์ด ํ์ธ์ f indById๋ก ๋ค์ ์กฐํํ๋ฉด ์์์ฑ ์ปจํ ์คํธ์ ๊ณผ๊ฑฐ ๊ฐ์ด ๋จ์์ ๋ฌธ์ ๊ฐ ๋ ์์๋ค.
๋ฐ๋ผ์ ๋ค์ ์กฐํํด์ผํ๋ค๋ฉด ๊ผญ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ด๊ธฐํํด์ผํ๋ค.
MemberRepositoryTest
์๋ ๋ฒํฌ ์ฐ์ฐ์ ์ํํ ๋ค์์๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ flush ํด์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋น์์ผ ๊ฐ์ ํธ๋์ญ์ ์์์ ๋ค๋ฅธ ๋ก์ง์ด ์์ ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ๋ฐ์๋๊ฒ์ ์กฐํํ๊ฑฐ๋ ํ ์ ์๋ค.
๊ทธ๋ฐ๋ฐ ์คํ๋ง ๋ฐ์ดํฐ jpa์์๋ ์ธํฐํ์ด์ค์์ @Modifing ์ด๋ ธํ ์ด์ ์ค clearAutomatically ์ต์ ์ true๋ก ์ค์ ํด์ฃผ๋ฉด ๋ฐ๋ก flush๋ฅผ ์ํด์ค๋ ์๋์ผ๋ก flush๋ฅผ ํด์ค๋ค!
โ ์ฐธ๊ณ
๋ฒํฌ ์ฐ์ฐ์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋ฌด์ํ๊ณ ์คํํ๊ธฐ ๋๋ฌธ์, ์์์ฑ ์ปจํ ์คํธ์ ์๋ ์ํฐํฐ์ ์ํ์ DB์ ์ํฐํฐ ์ํ๊ฐ ๋ฌ๋ผ์ง ์ ์๋ค.
๊ถ์ฅํ๋ ๋ฐฉ์
1. ์์์ฑ ์ปจํ ์คํธ์ ์ํฐํฐ๊ฐ ์๋ ์ํ์์ ๋ฒํฌ ์ฐ์ฐ์ ๋จผ์ ์คํํ๋ค.
2. ๋ถ๋์ดํ๊ฒ ์์์ฑ ์ปจํ ์คํธ์ ์ํฐํฐ๊ฐ ์์ผ๋ฉด ๋ฒํฌ ์ฐ์ฐ ์งํ์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ด๊ธฐํํ๋ค.
9๏ธโฃ @EntityGraph
: ์ฐ๊ด๋ ์ํฐํฐ๋ค์ SQL ํ๋ฒ์ ์กฐํํ๋ ๋ฐฉ๋ฒ
member -> team ์ ์ง์ฐ๋ก๋ฉ ๊ด๊ณ์ด๋ค.
๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด team์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋ ๋ง๋ค ์ฟผ๋ฆฌ๊ฐ ์คํ๋๋ N + 1 ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
MemberRepositoryTest
member.getTeam().getName()์ ํ๋ ์๊ฐ member์ ์๋งํผ select team์ ์กฐํํ ๊ฒ์ ํ์ธํ ์์๋ค.
์ด๋ฐ ๋ฌธ์ ๋ฅผ N+ 1 ๋ฌธ์ ๋ผ๊ณ ํ๋ค.
์ด N + 1 ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ํ๋ฒ์ ์กฐํํ๋ ค๋ฉด ํ์น ์กฐ์ธ์ด ํ์ํ๋ค.
MemberRepository
MemberRepositoryTest
-> ํ์น ์กฐ์ธ์ ์ฌ์ฉํ๋ ์ฝ๋๋ก ๋ฐ๊ฟ์ ํ ์คํธ๋ฅผ ์ํํ๋ left outer join์ ํตํด์ ํ๋ฒ์ ์กฐํํด ์ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
EntityGraph
: ์คํ๋ง ๋ฐ์ดํฐ JPA๋ JPA๊ฐ ์ ๊ณตํ๋ ์ํฐํฐ ๊ทธ๋ํ ๊ธฐ๋ฅ์ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ๊ฒ ๋์์ค๋ค.
์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด JPQL ์์ด ํ์น ์กฐ์ธ์ ์ฌ์ฉํ ์ ์๋ค. (JPQL + ์ํฐํฐ ๊ทธ๋ํ๋ ๊ฐ๋ฅ)
MemberRepository
-> findAll ๊ณตํต ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ ํด์ @EntityGraph๋ฅผ ์ถ๊ฐํด์คฌ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์ Test ์ฝ๋๋ฅผ ์ํํ๋ฉด
LEFT OUTER JOIN์ ์ฌ์ฉํ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๋ ๊ฒ ๋ณด๋ฉด ๊ทธ๋ฅ EntityGraph๋ ์ฌ์ค์ ํ์น ์กฐ์ธ์ ๊ฐํธ ์กฐ์ธ์ด๋ผ๊ณ ํ ์ ์๋ค.
๐ JPA Hint & Lock
JPA Hint
: JPA ์ฟผ๋ฆฌ ํํธ(SQL ํํธ๊ฐ ์๋๋ผ JPA ๊ตฌํ์ฒด์๊ฒ ์ ๊ณตํ๋ ํํธ)
MemberRepository
MemberRepositoryTest
-> ํ ์คํธ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํด๋ณด๋ฉด findReadOnlyByUsername์ readOnly๋ก ์ค์ ํด๋๊ธฐ ๋๋ฌธ์ setUsername์ ํตํด ์ด๋ฆ์ ๋ณ๊ฒฝํ๋๋ผ๋ ๋ณ๊ฒฝ๊ฐ์ง๊ฐ ์ผ์ด๋์ง ์๋๋ค. (๋ฌด์ํด๋ฒ๋ฆฐ๋ค๊ณ ํ๋ค?)
Lock
MemberRepository
-> org.springframework.data.jpa.repository.Lock ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ค.
MemberRepositoryTest
ํด๋น ํ ์คํธ๋ฅผ ์คํํ๋ฉด ์ด๋ฐ ์ฟผ๋ฆฌ๊ฐ ์คํ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
Lock์ ๋ํด์๋ ์ด๋ ๊ฒ๋ง ์๊ธฐํ๊ณ ๋นจ๋ฆฌ ๋์ด๊ฐ๋ฒ๋ ค์ ์ดํด๊ฐ ์ ์๋ผ์ ๋ ๊ฒ์ํด๋ดค๋ค.
SELECT FOR UPDATE=๋์์ฑ ์ ์ด๋ฅผ ์ํด ํน์ row์ ๋ฐฐํ์ LOCK์ ๊ฑฐ๋ ํ์
“๋ฐ์ดํฐ ์์ ํ๋ ค๊ณ ์ฐพ์ ๊ฒ์ด๋, ๋ค๋ฅธ๋ถ๋ค์ ๊ฑด๋๋ฆฌ์ง ๋ง์ธ์!”
PESSMISTIIC_WRITE ๋ ๋น๊ด์ ์ ๊ธ์ด๋ผ๊ณ ํ๋ค.
๋น๊ด์ ์ ๊ธ(Pessimistic Lock)
๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์์ ํ ๊ฐ๋ฅ์ฑ์ด ๋๋ค๋ ๋น๊ด์ ์ธ ์ ์ ๋ก ์ ๊ธ์ ๊ฑฐ๋ ๋ฐฉ์์ ๋๋ค. ์๋ฅผ ๋ค์ด ์ํ์ ์ฌ๊ณ ๋ ๋์์ ๊ฐ์ ์ํ์ ์ฌ๋ฌ๋ช ์ด ์ฃผ๋ฌธํ ์ ์์ผ๋ฏ๋ก ๋ฐ์ดํฐ ์์ ์ ์ํ ๊ฒฝํฉ์ด ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ๋๋ค๊ณ ๋น๊ด์ ์ผ๋ก ๋ณด๋ ๊ฒ์ ๋๋ค. ์ด ๊ฒฝ์ฐ ์ถฉ๋๊ฐ์ง๋ฅผ ํตํด์ ์ ๊ธ์ ๋ฐ์์ํค๋ฉด ์ถฉ๋๋ฐ์์ ์ํ ์์ธ๊ฐ ์์ฃผ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ด๊ฒฝ์ฐ ๋น๊ด์ ์ ๊ธ์ ํตํด์ ์์ธ๋ฅผ ๋ฐ์์ํค์ง ์๊ณ ์ ํฉ์ฑ์ ๋ณด์ฅํ๋ ๊ฒ์ด ๊ฐ๋ฅํฉ๋๋ค. ๋ค๋ง ์ฑ๋ฅ์ ์ธ ์ธก๋ฉด์ ์์ค์ ๊ฐ์ํด์ผ ํฉ๋๋ค. ์ฃผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ ๊ณตํ๋ ๋ฐฐํ์ ๊ธ(Exclusive Lock)์ ์ฌ์ฉํฉ๋๋ค.
์ถ์ฒ: https://reiphiel.tistory.com/entry/understanding-jpa-lock [๋ ์ดํผ์์ ๋ธ๋ก๊ทธ:ํฐ์คํ ๋ฆฌ]
๋ฐฐํ์ ์ ๊ธ(Exclusive Lock)์ ํ๋ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฅธ ํธ๋์ญ์ ์์ READ, UPDATE, DELETE ํ๋๊ฒ์ ๋ฐฉ์ง ํ ์ ์์ต๋๋ค.
'์ธํ๋ฐ > ์ค์ ! ์คํ๋ง ๋ฐ์ดํฐ JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์คํ๋ง ๋ฐ์ดํฐ JPA] 6. ์คํ๋ง ๋ฐ์ดํฐ JPA ๋ถ์ (0) | 2022.10.03 |
---|---|
[์คํ๋ง ๋ฐ์ดํฐ JPA] 5. ํ์ฅ ๊ธฐ๋ฅ (1) | 2022.09.29 |
[์คํ๋ง ๋ฐ์ดํฐ JPA] 3. ๊ณตํต ์ธํฐํ์ด์ค ๊ธฐ๋ฅ (0) | 2022.09.22 |
[์คํ๋ง ๋ฐ์ดํฐ JPA] 2. ์์ ๋๋ฉ์ธ ๋ชจ๋ธ (0) | 2022.09.22 |
[์คํ๋ง ๋ฐ์ดํฐ JPA] 1. ํ๋ก์ ํธ ํ๊ฒฝ์ค์ (0) | 2022.09.21 |