[JPA] Spring Data JPA - ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ
by rlaehddnd0422Spring Data JPA๋ ์คํ๋ง ํ๋ ์์ํฌ์์ JPA๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ ํ๋ก์ ํธ์ ๋๋ค.
์ด ํ๋ก์ ํธ๋ ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ ๊ฐ๋ฐํ ๋ ์ง๋ฃจํ๊ฒ ๋ฐ๋ณต๋๋ CRUD๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๊ณตํต ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ฐ๋ผ์ ๊ตฌํ ํด๋์ค ์์ด ์ธํฐํ์ด์ค๋ง ์์ฑํด๋ ๋ฆฌํฌ์งํ ๋ฆฌ ๊ณ์ธต์ ๊ฐ๋ฐํ ์ ์์ต๋๋ค.
์ด ์ ํฌ์คํ ์์ ๋ค๋ค๋ ์คํ๋ง ๋ฐ์ดํฐ JPA์ ๊ธฐ๋ฅ์ ๊น๊ฒ ์์๋ด ์๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ๊ณตํต ์ธํฐํ์ด์ค ๊ธฐ๋ฅ๊ณผ ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ์ ์ง์ํ๋ Spring Data JPA์์ ์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ์ ์์ธํ ๋ค๋ค๋ด ์๋ค.
์ฟผ๋ฆฌ ๋ฉ์๋ ๊ธฐ๋ฅ
์ธํฐํ์ด์ค์ Spring Data JPA ๊ฐ ์ง์ํ๋ ํ์์ ๋ฉ์๋ ์ด๋ฆ์ ์ ์ด๋๋ฉด, ๋ฉ์๋ ์ด๋ฆ์ ๋ถ์ํด์ ์๋์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค๊ณ ์คํํด์ฃผ๋ ๊ธฐ๋ฅ.
๋๋ ๋ณด๋ฉด ํฌ๊ฒ ์ธ ๊ฐ์ง ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
- ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ
- ๋ฉ์๋ ์ด๋ฆ์ผ๋ก JPA NamedQuery ํธ์ถ.
- @Query ์ด๋ ธํ ์ด์ ์ผ๋ก Repository ์ธํฐํ์ด์ค์ ์ฟผ๋ฆฌ ์ง์ ์ ์
๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ
: ๋ฉ์๋ ์ด๋ฆ์ ๋ถ์ํด์ JPQL ์ฟผ๋ฆฌ ์์ฑ
Spring Data JPA๋ฅผ ์ฌ์ฉํ์ง ์์ ์์ JPA ๋ฆฌํฌ์งํ ๋ฆฌ ๊ณ์ธต
@Repository
public class MemberJpaRepository {
@PersistenceContext
private EntityManager em;
// C
public Member save(Member member) {
member.setName("KIM");
em.persist(member);
return member;
}
// R
public Member find(Long id) {
return em.find(Member.class, id);
}
// R
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
// R
public List<Member> findAll() {
return em.createQuery("select m from Member m",Member.class).getResultList();
}
public long count() {
return em.createQuery("select count(m) from Member m", Long.class).getSingleResult();
}
// D
public void delete(Member member) {
em.remove(member);
}
}
Spring Data JPA ๋ฆฌํฌ์งํ ๋ฆฌ
public interface MemberRepository extends JpaRepository<Member, Long> {
}
์คํ๋ง ๋ฐ์ดํฐ JPA๋ ๋ฉ์๋ ์ด๋ฆ์ ๋ถ์ํด์ JPQL์ ์์ฑํ๊ณ ์คํํฉ๋๋ค.
- ์กฐํ(R) : find..By, read..By, query..By, get..By / Return ํ์ Entity
- ์ญ์ (D) : delete..By, remove..By / Return ํ์ Long
์์ธํ ๊ณต์ ๋ฌธ์
์ด ๊ธฐ๋ฅ์ ์ํฐํฐ์ ํผ๋ฅป๋ช ์ด ๋ณ๊ฒฝ๋๋ฉด ์ธํฐํ์ด์ค์ ์ ์ํ ๋ฉ์๋ ์ด๋ฆ๋ ํจ๊ป ๋ณ๊ฒฝํด์ผ ํฉ๋๋ค.
๋ณ๊ฒฝํ์ง ์์ผ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ํ๋ ์์ ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ฐ, ๋ก๋ฉ ์์ ์ ์ฌ์ฉ์์๊ฒ ์ค๋ฅ๋ฅผ ์ธ์งํ ์ ์๋๋ก ํด์ฃผ๋๊ฒ ๋ํ Spring Data JPA์ ํฐ ์ฅ์ ์ ๋๋ค.
JPA Named Query
์คํ๋ง ๋ฐ์ดํฐ JPA๋ ๋ฉ์๋ ์ด๋ฆ์ผ๋ก JPA Named ์ฟผ๋ฆฌ๋ฅผ ํธ์ถํ๋ ๊ธฐ๋ฅ ๋ํ ์ ๊ณตํฉ๋๋ค.
๊ธฐ์กด ์์ JPA ์ ์ ๋ค์๋ ์ฟผ๋ฆฌ ๋ฐฉ์
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED) // ๊ธฐ๋ณธ ์์ฑ์ ๋ง๊ณ ์ถ์๋ฐ, JPA ์คํ์ protected๋ก ์ด์ด๋์ด์ผ ํจ
@Getter @Setter
@ToString(of = {"id","name","age"}) // @ToString ์ฌ์ฉ ์ ๊ฐ๊ธ์ ๋ด๋ถ ํ๋๋ง, ์ฐ๊ด๊ด๊ณ ํ๋๋ X
@NamedQuery(
name="Member.findByName",
query="select m from Member m where m.name =: name"
)
public class Member {
public Member findByName() {
return em.createNamedQuery("Member.findByName", Member.class).getSingleResult();
}
- ๋๋ฉ์ธ์ ๋ค์๋ ์ฟผ๋ฆฌ ์ ์ ๋ฐ ๋ฆฌํฌ์งํ ๋ฆฌ ๊ณ์ธต์์ ๋๋ฉ์ธ์ ์ค์ ํ ๋ค์๋ ์ฟผ๋ฆฌ ์ฌ์ฉ
Spring Data JPA๋ก NamedQuery ์ฌ์ฉ
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query(name="Member.findByName")
List<Member> findByName(@Param("name") String name);
}
- ์ธํฐํ์ด์ค ๋ฆฌํฌ์งํ ๋ฆฌ์ ์ด๋ ๊ฒ ๋ค์๋ ์ฟผ๋ฆฌ๋ฅผ @Query๋ก ์ ์ํ๊ณ ์ฟผ๋ฆฌ ๋ฉ์๋ ์ด๋ฆ ๊ธฐ๋ฅ์ผ๋ก ์ ์ธํ๊ธฐ๋ง ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
@Query๋ฅผ ์๋ตํ๊ณ ๋ฉ์๋ ์ด๋ฆ๋ง์ผ๋ก Named Query๋ฅผ ํธ์ถํ ์๋ ์์ต๋๋ค.
public interface MemberRepository extends JpaRepository<Member, Long> {
// ๊ธฐ๋ณธ์ ์ผ๋ก ์ฟผ๋ฆฌ ์ด๋ฆ ๋ฉ์๋ ์ ๋ต ์ฌ์ฉ
List<Member> findByName(@Param("name") String name);
}
์คํ๋ง ๋ฐ์ดํฐ JPA๋ ์ ์ธํ "๋๋ฉ์ธ ํด๋์ค + . + ๋ฉ์๋ ์ด๋ฆ"์ผ๋ก Named ์ฟผ๋ฆฌ๋ฅผ ์ฐพ์์ ์คํํฉ๋๋ค.
๋ง์ฝ ์คํํ Named ์ฟผ๋ฆฌ๊ฐ ์์ผ๋ฉด ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑ ์ ๋ต์ ์ฌ์ฉํฉ๋๋ค.
@Query์ ์ฟผ๋ฆฌ ์ง์ ์ ์
์ค๋ฌด์์ ๊ธฐ์กด ์์ JPA ๋ฐฉ์์ผ๋ก Named Query๋ฅผ ์ง์ ๋ฑ๋กํด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋๋ฌผ๊ณ , ๋๊ฐ ๋ค์๋ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค๊ณ ํ๋ฉด @Query๋ฅผ ์ฌ์ฉํด์ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉ์๋์ ์ง์ ์ฟผ๋ฆฌ๋ฅผ ์ ์ํฉ๋๋ค.
@Query์ JPQL ์ฟผ๋ฆฌ ์ง์ ์์ฑํ์ฌ ์ฟผ๋ฆฌ ๋ฉ์๋ ์ ๋ต์ ์ฌ์ฉํ์ง ์๊ณ ์ง์ ์ฌ์ฉ์๊ฐ ๋ง๋ ์ฟผ๋ฆฌ ๋ฑ๋ก๋ ๊ฐ๋ฅํฉ๋๋ค.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.name =: name and m.age = :age")
List<Member> findUsers(@Param("name") String name, @Param("age") int age);
}
์ค๋ฌด์์๋ ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑํ๋ ๊ธฐ๋ฅ์ ํ๋ผ๋ฏธํฐ๊ฐ ์ฆ๊ฐํ๊ฒ ๋๋ฉด ๋ฉ์๋ ์ด๋ฆ์ด ๋งค์ฐ ์ง์ ๋ถํด์ง๊ธฐ ๋๋ฌธ์ ์ด๋ ๊ฒ @Query์ ์ง์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ธฐ๋ ํฉ๋๋ค.
@Query๋ก ํ๋ ๊ฐ์ด๋ DTO ์กฐํ
@Query๋ก ์ํฐํฐ ์กฐํ ๋ฟ๋ง ์๋๋ผ ํ๋ ๊ฐ์ ์กฐํํ๊ฑฐ๋ DTO๋ก ์กฐํํ๋๋ก ์ค์ ๋ํ ๊ฐ๋ฅํฉ๋๋ค.
1. ํ๋ ๊ฐ ์กฐํ
@Query("select m.name from Member m")
List<String> findNameList();
2. DTO๋ก ์ง์ ์กฐํ
@Query("select new study.datajpa.dto.MemberDto(m.id, m.name, m.age, t.name) from Member m join m.team t")
List<MemberDto> findMembers();
+ MemberDto.java
@Data
public class MemberDto {
private Long id;
private String name;
private int age;
private String teamName;
public MemberDto(Long id, String name, int age, String teamName) {
this.id = id;
this.name = name;
this.age = age;
this.teamName = teamName;
}
}
@Query ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
๊ธฐ๋ณธ์ ์ผ๋ก ์์น๊ธฐ๋ฐ๋ณด๋ค ์ด๋ฆ๊ธฐ๋ฐ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์ ํ๋ ๊ฒ์ด ๋ฒ๊ทธ ์ก๊ธฐ ์ฝ์ต๋๋ค.
์์น๊ธฐ๋ฐ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ ์ฌ์ฉ์ ์ง์ํ๋๋ก ํฉ์๋ค.
์์น ๊ธฐ๋ฐ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
@Query("select m from Member m where m.name = ?1 and m.age = ?2")
List<Member> findUsers(@Param("name") String name, @Param("age") int age);
์ด๋ฆ ๊ธฐ๋ฐ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
@Query("select m from Member m where m.name =: name and m.age = :age")
List<Member> findUsers(@Param("name") String name, @Param("age") int age);
์ปฌ๋ ์ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์๋ in ์ ์ฌ์ฉ
@Query("select m from Member m where m.name in :names")
List<Member> findByNames(@Param("names") List<String> names);
๋ฐํ ํ์
Spring Data JPA๋ ์ ์ฐํ ๋ฆฌํด ํ์ ์ ์ง์ํฉ๋๋ค.
List<Member> findByName(String name); //์ปฌ๋ ์
Member findByName(String name); // ๋จ๊ฑด
Optional<Member> findByName(); // ๋จ๊ฑด Optional
- ๋จ๊ฑด ์ง์ ๋ฉ์๋ ํธ์ถ ์ Query.getSingleResult() ํธ์ถ ๋ฐ ๋จ๊ฑด ์ํฐํฐ ๋ฆฌํด.
- ์ปฌ๋ ์ ์ง์ ๋ฉ์๋ ํธ์ถ ์ Query.getResultList() ํธ์ถ ๋ฐ ์ํฐํฐ ์ปฌ๋ ์ ๋ฆฌํด
- Spring Data JPA๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์์ JPQL + JPA๋ฅผ ์ฌ์ฉํ์ ๋, ๋ง์ฝ ๋จ๊ฑด ์ง์ ๋ฉ์๋ ํธ์ถ ์ ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด NoResultException ์์ธ๊ฐ ๋ฐ์ํ์ง๋ง, Spring Data JPA๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ์์ธ๊ฐ ๋ฐ์ ์ ์์ธ๋ฅผ ๋ฌด์ํ๊ณ null์ ๋ฆฌํดํฉ๋๋ค.
Spring Data JPA์ ํ์ด์ง, ์ ๋ ฌ ๊ธฐ๋ฅ
์์ JPA๋ฅผ ์ฌ์ฉํ์ ๋๋ ํ์ด์ง์ ๋ฉ์๋๋ฅผ ํตํด, ์ ๋ ฌ์ JPQL์ ํตํด ์ค์ ํ์์ต๋๋ค.
์์ JPA๋ฅผ ์ฌ์ฉํ Repostiory ๊ณ์ธต์์ Paging ๋ฐ Sort ์ ์ฉํ JPQL
public List<Member> findByPage(int age, int offset, int limit) {
return em.createQuery("select m from Member m where m.age =: age order by m.name desc")
.setParameter("age", age)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
Spring Data JPA์ ํ์ด์ง๊ณผ ์ ๋ ฌ
- ์ ๋ ฌ ๊ธฐ๋ฅ๋ง ์ฌ์ฉํ๊ธฐ : Sort ์ฌ์ฉ
- ํ์ด์ง ๊ธฐ๋ฅ : Pageable ์ฌ์ฉ ( ๋ด๋ถ์ Sort ํฌํจ )
- ํน๋ณํ ๋ฆฌํด ํ์
- Page : ๊ฒ์๋ ์ ์ฒด ๋ฐ์ดํฐ ๊ฑด์๋ฅผ ์กฐํํ๋ count ์ฟผ๋ฆฌ๋ฅผ ์ถ๊ฐ๋ก ํธ์ถ์ง ํ๋ ํ์ด์ง
- Slice : ์ถ๊ฐ count ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์์ด ๋ค์ ํ์ด์ง๋ง ํ์ธ ๊ฐ๋ฅ ( ๋ด๋ถ์ ์ผ๋ก limit + 1 ์กฐํ )
- List : ์ถ๊ฐ count ์ฟผ๋ฆฌ ์์ด ๊ฒฐ๊ณผ๋ง ๋ฆฌํด
Page<Member> findByName(String name, Pageable pageable); // ์ถ๊ฐ count ์ฟผ๋ฆฌ ํธ์ถ
Slice<Member> findByName(String name, Pageable pageable); // ์ถ๊ฐ count ์ฟผ๋ฆฌ ํธ์ถ x
List<Member> findByUsername(String name, Pageable pageable); //count ์ฟผ๋ฆฌ ์ฌ์ฉ ์ํจ
List<Member> findByUsername(String name, Sort sort); // ์ ๋ ฌ ๊ธฐ๋ฅ๋ง ์ฌ์ฉ
ex) ๋์ด๊ฐ 10์ด์ธ ๋ฉค๋ฒ๋ง, ์ด๋ฆ์ผ๋ก ๋ด๋ฆผ์ฐจ์ํด์, ํ์ด์ง๋น 3๊ฑด ์กฐํ
Page<Member> findByAge(int age, Pageable pageable); // ์ถ๊ฐ count ์ฟผ๋ฆฌ ํธ์ถ
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "name"));
Page<Member> page = memberRepository.findByAge(10, pageRequest);
- PageRequest ์ ํ์ด์ง ์ฌ์ด์ฆ ์ค์ ๋ฐ ์ ๋ ฌ ๋ฐฉ์์ ์ค์ ํ ์ ์์ต๋๋ค.
- ์ค์ ํ PageRequest๋ฅผ Pageable ์ ํ ์ค
ํ์ด์ง ๊ด๋ จ ๋ฉ์๋
int getNumber(); // ํ์ฌ ํ์ด์ง
int getSize(); // ํ์ด์ง ํฌ๊ธฐ
int getNumberOfElements(); //
List<T> getContent(); // ์กฐํ๋ ๋ฐ์ดํฐ
boolean hasContent(); // ์กฐํ๋ ๋ฐ์ดํฐ ์กด์ฌ ์ฌ๋ถ
Sort getSort(); // ์ ๋ ฌ ์ ๋ณด
boolean isFirst(); // ํ์ฌ ํ์ด์ง๊ฐ ์ฒซ ํ์ด์ง์ธ์ง ์ฌ๋ถ
boolean isLast(); // ํ์ฌ ํ์ด์ง๊ฐ ๋ง์ง๋ง ํ์ด์ง์ธ์ง ์ฌ๋ถ
boolean hasNext(); // // ๋ค์ ํ์ด์ง ์ฌ๋ถ
boolean hasPrevious(); // ์ด์ ํ์ด์ง ์ฌ๋ถ
Pageable getPageable(); // ํ์ด์ง ์์ฒญ ์ ๋ณด
Pageable nextPageable(); // ๋ค์ ํ์ด์ง ๊ฐ์ฒด
Pageable previousPageable(); //์ด์ ํ์ด์ง ๊ฐ์ฒด
<U> Slice<U> map(Function<? super T, ? extends U> converter); // ๋ณํ๊ธฐ
Page๋ฅผ ์ฌ์ฉํ ๋ count ์ฟผ๋ฆฌ๋ฅผ ๋ฐ๋ก ๋ถ๋ฆฌํ ์ ์์ต๋๋ค.
@Query(value = “select m from Member m”,
countQuery = “select count(m.username) from Member m”)
Page<Member> findMemberAllCountBy(Pageable pageable);
- ์ ์ฒด count ํ๋ ์ฟผ๋ฆฌ๋ ๋งค์ฐ ๋ฌด๊ฒ๊ธฐ ๋๋ฌธ์ Page ์ฌ์ฉ ์ ์นด์ดํธ ์ฟผ๋ฆฌ๋ ์์ฒ๋ผ ๋ฐ๋ก ๋ถ๋ฆฌํด์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
TopN, FirstN
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);
- ์ฟผ๋ฆฌ ๋ฉ์๋ ์ด๋ฆ์ผ๋ก TopN, FirstN์ ์ฌ์ฉํด ์ ๋ ฌ ๊ฒฐ๊ณผ์์ N๊ฐ๋ฅผ ๋ฝ์์ ํ์ด์งํ๋๋ก ์ค์ ํ ์๋ ์์ต๋๋ค.
ํ์ด์ง ๊ฒฐ๊ณผ DTO๋ก ๋ฐํํ๊ธฐ
Page<Member> page = memberRepository.findByAge(10, pageRequest);
Page<MemberDto> dtoPage = page.map(m -> new MemberDto());
- ํ์ด์ง ๊ฒฐ๊ณผ์ map์ผ๋ก Dto ์์ฑ ๋ฐ ์ปฌ๋ ์ ์ผ๋ก ๋ฆฌํด
๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ
๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ๋ ํ ๋ฒ์ ์์ ์ผ๋ก ์ฌ๋ฌ ๊ฐ์ ๋ ์ฝ๋๋ฅผ ์์ ํ๋ ์ฟผ๋ฆฌ๋ฅผ ๋งํฉ๋๋ค.
์์ JPA๋ฅผ ์ฌ์ฉํ ๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ
public int bulkAgePlus(int age) {
return em.createQuery("update Member m set m.age = m.age + 1 where m.age >= :age")
.setParameter("age", age)
.executeUpdate();
}
Spring Data JPA๋ฅผ ์ฌ์ฉํ ๋ฒํฌ์ฑ ์์ ์ฟผ๋ฆฌ
@Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgeUp(@Param("age") int age);
- Spring Data JPA๋ฅผ ์ฌ์ฉํ๋ฉด @Modifying ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํด ๋ฒํฌ์ฑ ์์ , ์ญ์ ๋ฅผ ํ ์ ์์ต๋๋ค.
- Spring Data JPA์์ ๋ฒํฌ์ฑ ์ฟผ๋ฆฌ์ @Modifying ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์์ธ๊ฐ ๋ฐ์.
- ๋ฒํฌ์ฑ ์ฟผ๋ฆฌ๋ฅผ ์คํ ํ ์์์ฑ ์ปจํ
์คํธ ์ด๊ธฐํ ์ต์
: @Modifying(clearAutomatically = true)
-
์ด ์ต์ ์์ด ํ์์ findById๋ก ๋ค์ ์กฐํํ๋ฉด ์์์ฑ ์ปจํ ์คํธ์ ๊ณผ๊ฑฐ ๊ฐ์ด ๋จ์์ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค. ๋ง์ฝ ๋ค์ ์กฐํํด์ผ ํ๋ฉด ๊ผญ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ด๊ธฐํ ํ์.
-
๋ฒํฌ ์ฐ์ฐ์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋ฌด์ํ๊ณ ์คํํ๊ธฐ ๋๋ฌธ์,
์์์ฑ ์ปจํ ์คํธ์ ์๋ ์ํฐํฐ์ ์ํ์ DB์ ์ํฐํฐ ์ํ๊ฐ ๋ฌ๋ผ์ง ์ ์์.
๊ถ์ฅํ๋ ๋ฐฉ๋ฒ
- ์์์ฑ ์ปจํ ์คํธ์ ์ํฐํฐ๊ฐ ์๋ ์ํ์์ ๋ฒํฌ ์ฐ์ฐ์ ์คํํ๋๋ก ํฉ์๋ค.
- ๋ถ๋์ดํ๊ฒ ์์์ฑ ์ปจํ ์คํธ์ ์ํฐํฐ๊ฐ ์์ผ๋ฉด ๋ฒํฌ ์ฐ์ฐ ์งํ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ด๊ธฐํ ํฉ์๋ค.
@EntityGraph๋ฅผ ์ด์ฉํ ์ฐ๊ด ์ํฐํฐ ์กฐํ
JPA์์๋ JPQL์ ํ์น์กฐ์ธ์ผ๋ก ์ฌ์ฉํจ์ผ๋ก์จ N+1 ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์์ต๋๋ค.
Spring Data JPA์์๋ @EntityGraph๋ฅผ ์ฌ์ฉํด์ ๊ฐํธํ๊ฒ JPQL์ ํ์น์กฐ์ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Spring Data JPA๋ JPA๊ฐ ์ ๊ณตํ๋ ์ํฐํฐ ๊ทธ๋ํ ๊ธฐ๋ฅ์ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ๋๋ก ๋์์ค๋๋ค.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
//JPQL + ์ํฐํฐ ๊ทธ๋ํ
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
+ @NamedEntityGraph
- @EntityGraph์ ์ด๋ฆ์ ๋ถ์ฌ ์ ์ํ ์ ์์ต๋๋ค.
- attributeNodes ์๋ ํจ๊ป ํธ์ถํ ์ฐ๊ด๋ ์ํฐํฐ ์์ฑ.
@NamedEntityGraph(name = "Member.all", attributeNodes = @NamedAttributeNode("team"))
@Entity
public class Member {
private Team team;
...
}
@NamedEntityGraph ์ ์ฉ
@EntityGraph("Member.all")
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
JPA Hing & Lock
Jpa Hint ์ ์ฉ
@QueryHints ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํฉ๋๋ค.
@QueryHints(value = @QueryHint(name = "org.hibernate.readOnly", value = "true"))
Member findReadOnlyByUsername(String username);
Member member = memberRepository.findReadOnlyByUsername("member1");
member.setUsername("member2");
em.flush(); //Update Query ์คํX readOnly = true ์ค์
- @QueryHint์ readOnly ์ต์ ์ด true๋ก ์ค์ ๋์ด ์๊ธฐ ๋๋ฌธ์ flush() ํ๋๋ผ๋ update query๊ฐ ์คํ๋์ง ์์ต๋๋ค.
Jpa Lock
@Lock์ ์ผ๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์คํ ์์ ์ ๊ณต๋๋ ๊ธฐ๋ฅ์ผ๋ก, ๋ฐ์ดํฐ์ ๋์ ์ก์ธ์ค๋ฅผ ์ ์ดํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
์ด ๊ธฐ๋ฅ์ ํน์ ๋ฐ์ดํฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฆฌ์์ค์ ๋ํ ์ ๊ธ(lock)์ ์ค์ ํ๊ณ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
์ฌ๋ฌ ๊ฐ์ ํธ๋์ญ์ ์ด ๋์์ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ๊ฒฝ์ฐ, ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ ์งํ๊ณ ์ถฉ๋์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ ๊ธ์ด ํ์ํฉ๋๋ค.
์ฃผ ์ฌ์ฉ ๋ชฉ์
1. ๋์์ฑ ์ ์ด: ์ฌ๋ฌ ๊ฐ์ ํธ๋์ญ์ ์ด ๋์์ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ค๋ ๊ฒฝ์ฐ ์ถฉ๋์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
2. ๊ต์ฐฉ์ํ ๋ฐฉ์ง: ๊ต์ฐฉ์ํ(deadlock)๋ ๋ ๊ฐ ์ด์์ ํธ๋์ญ์ ์ด ์๋ก๊ฐ ๊ฐ์ง ๋ฆฌ์์ค๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉฐ ๋ฌดํํ ๋๊ธฐํ๋ ์ํฉ์ ๋งํฉ๋๋ค. @Lock์ ์ฌ์ฉํ์ฌ ํธ๋์ญ์ ์ด ํ์ํ ๋ฆฌ์์ค์ ๋ํ ์ ๊ธ์ ์ค์ ํ๋ฉด, ๊ต์ฐฉ์ํ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ ์ค์ผ ์ ์์ต๋๋ค.
3. ์ต์ ํ ๋ฐ ์ฑ๋ฅ ๊ฐ์ : @Lock์ ๋ฐ์ดํฐ ์ก์ธ์ค๋ฅผ ํต์ ํ๋ฏ๋ก, ๋์ ์ก์ธ์ค๋ก ์ธํ ์ฑ๋ฅ ์ ํ๋ฅผ ๋ฐฉ์งํ๊ณ ์ต์ ํ๋ ์คํ ๊ณํ์ ์๋ฆฝํ ์ ์์ต๋๋ค. ํนํ ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ด๋ ๋ฌด๊ฒฐ์ฑ์ด ์ค์ํ ๊ฒฝ์ฐ, ์ ๊ธ์ ์ฌ์ฉํ์ฌ ์ ํ์ฑ์ ๋ณด์ฅํ ์ ์์ต๋๋ค.
์ฟผ๋ฆฌ ์ ๋ฝ์ ๊ฑธ๋ ค๋ฉด @Lock ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findByUsername(String name);
<์ฐธ๊ณ ์๋ฃ>
'๐ Backend > Spring Data JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JPA] Spring Data JPA - Web ํ์ฅ ๊ธฐ๋ฅ (0) | 2023.05.22 |
---|---|
[JPA] Spring Data JPA - ํ์ฅ ๊ธฐ๋ฅ (0) | 2023.05.19 |
[JPA] JPQL ๋ฌธ๋ฒ 4 - ๋คํ์ฑ ์ฟผ๋ฆฌ, ์ํฐํฐ ์ง์ ์ฌ์ฉ, Named ์ฟผ๋ฆฌ (0) | 2023.05.10 |
[JPA] JPQL ๋ฌธ๋ฒ 3 - ๊ฒฝ๋ก ํํ์, ์๋ธ ์ฟผ๋ฆฌ, ์กฐ๊ฑด์ (0) | 2023.05.10 |
[JPA] JPQL ๋ฌธ๋ฒ 2 - ์งํฉ ๋ฐ ์ ๋ ฌ, ์กฐ์ธ, ํ์น ์กฐ์ธ (0) | 2023.05.09 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422