# [JPA] Spring Data JPA - ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ
Study Repository

[JPA] Spring Data JPA - ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ

by rlaehddnd0422
 

[Spring Data JPA] ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA

Spring Data JPA๋ž€? ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ์ฃผ ๊ธฐ๋Šฅ - CRUD ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณต - Repository ๊ณ„์ธต์— ๊ตฌํ˜„ ํด๋ž˜์Šค ์—†์ด ์ธํ„ฐํŽ˜

rlaehddnd0422.tistory.com

Spring Data JPA๋Š” ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

์ด ํ”„๋กœ์ ํŠธ๋Š” ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์ง€๋ฃจํ•˜๊ฒŒ ๋ฐ˜๋ณต๋˜๋Š” CRUD๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ตฌํ˜„ ํด๋ž˜์Šค ์—†์ด ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ž‘์„ฑํ•ด๋„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ณ„์ธต์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด ์ „ ํฌ์ŠคํŒ…์—์„œ ๋‹ค๋ค˜๋˜ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์˜ ๊ธฐ๋Šฅ์„ ๊นŠ๊ฒŒ ์•Œ์•„๋ด…์‹œ๋‹ค.

 

๊ธฐ๋ณธ์ ์œผ๋กœ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋Šฅ๊ณผ ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š” Spring Data JPA์—์„œ ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ์„ ์ž์„ธํžˆ ๋‹ค๋ค„๋ด…์‹œ๋‹ค.


์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ

์ธํ„ฐํŽ˜์ด์Šค์— Spring Data JPA ๊ฐ€ ์ง€์›ํ•˜๋Š” ํ˜•์‹์˜ ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์„ ์ ์–ด๋‘๋ฉด, ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์„ ๋ถ„์„ํ•ด์„œ ์ž๋™์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹คํ–‰ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ. 

 

๋‚˜๋ˆ ๋ณด๋ฉด ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1.  ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ฟผ๋ฆฌ ์ƒ์„ฑ
  2.  ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ JPA NamedQuery ํ˜ธ์ถœ.
  3. @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 - Reference Documentation

Example 119. 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

์ด ๊ธฐ๋Šฅ์€ ์—”ํ‹ฐํ‹ฐ์˜ ํ”ผ๋ฅป๋ช…์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜ํ•œ ๋ฉ”์†Œ๋“œ ์ด๋ฆ„๋„ ํ•จ๊ป˜ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ์ž‘ํ•˜๋Š” ์‹œ์ ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ, ๋กœ๋”ฉ ์‹œ์ ์— ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ค๋ฅ˜๋ฅผ ์ธ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š”๊ฒƒ ๋˜ํ•œ Spring Data JPA์˜ ํฐ ์žฅ์ ์ž…๋‹ˆ๋‹ค.

JPA Named Query

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ JPA Named ์ฟผ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ธฐ๋Šฅ ๋˜ํ•œ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

๊ธฐ์กด ์ˆœ์ˆ˜ JPA ์ •์  ๋„ค์ž„๋“œ ์ฟผ๋ฆฌ ๋ฐฉ์‹ 

 

[JPA] JPQL ๋ฌธ๋ฒ• 4 - ๋‹คํ˜•์„ฑ ์ฟผ๋ฆฌ, ์—”ํ‹ฐํ‹ฐ ์ง์ ‘์‚ฌ์šฉ, Named ์ฟผ๋ฆฌ

๋‹คํ˜•์„ฑ ์ฟผ๋ฆฌ JPQL์œผ๋กœ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ชจ๋“  ์ž์‹ ์—”ํ‹ฐํ‹ฐ๊นŒ์ง€ ํ•จ๊ป˜ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "DTYPE") public abstract class Item {

rlaehddnd0422.tistory.com

@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);

<์ฐธ๊ณ ์ž๋ฃŒ>

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ