๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“š ์‹œ๋ฆฌ์ฆˆ/- ๋ฐฐ์›Œ๋ณด์ž Spring Data JPA

[๋ฐฐ์›Œ๋ณด์ž Spring Data JPA] JPA์˜ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค, JpaRepository ์˜ ๊ธฐ๋Šฅ๊ณผ ๊ตฌ์กฐ

by Wonit 2021. 4. 9.

ํ•ด๋‹น ๊ธ€์€ ๋ฐฐ์›Œ๋ณด์ž Spring Data JPA ์‹œ๋ฆฌ์ฆˆ ์ž…๋‹ˆ๋‹ค.
ํ•ด๋‹น ์‹œ๋ฆฌ์ฆˆ์˜ ๋‚ด์šฉ์ด ์ด์–ด์ง€๋Š” ํ˜•ํƒœ์ด๋ฏ€๋กœ ๊ธ€์˜ ๋‚ด์šฉ ์ค‘์— ์ƒ๋žต๋˜๋Š” ๋ง๋“ค์ด ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ, ์ž์„ธํ•œ ์‚ฌํ•ญ์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”!


์ˆœ์ˆ˜ JPA๋ฅผ ์ด์šฉํ•œ JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ํ•œ๊ณ„

์ž! ์šฐ๋ฆฌ๋Š” ์ด์ œ JPA์˜ ๊ธฐ๋ณธ ์–ด๋…ธํ…Œ์ด์…˜๋“ค์— ๋Œ€ํ•ด์„œ ์ด์ œ ๋ฐฐ์› ๋‹ค.

 

๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ์ˆœ์ˆ˜ JPA๋ฅผ ์ด์šฉํ•˜์—ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

 

JPA๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŠธ๋žœ์žญ์…˜์„ ์„ ์–ธํ•ด์ค˜์•ผ ํ•œ๋‹ค.

 

์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ๊ฒ๋จน์ง€ ๋ง์ž!
์ด๋Ÿฐ ๋ฌด์„œ์šด ์ฝ”๋“œ๋“ค์—๊ฒŒ ์“ธ ์‹ ๊ฒฝ์„ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์“ฐ๊ธฐ ์œ„ํ•ด์„œ Spring Data JPA๊ฐ€ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด๋‹ˆ ์•„๋ž˜์— ๋‚˜์˜ฌ ์ฝ”๋“œ๋“ค์€ ๋‹จ์ง€ JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๋ชฉ์ ์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

public static void main(String[] args) {
  EntityManagerFactory factory = Persistence.createEntityManagerFactory("Factory");

  // db connection
  EntityManager manager = factory.createEntityManager();

  EntityTransaction transaction = manager.getTransaction();
  // ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
  transaction.begin();

  try {
    Member member = new Member();

    member.setUsername("Steven Gerrard");
    manager.persist(member); // db ์ €์žฅ

    Member findMember = manager.find(Member.class, member.getId()); // select ์ฟผ๋ฆฌ

    System.out.println(findTeam.getName());

    transaction.commit();
  } catch (Exception e) {
      transaction.rollback();
  } finally {
      manager.close();
      factory.close();
  }
}

์ด๋Ÿฐ ํ˜•ํƒœ์˜ ๊ฐœ๋ฐœ์„ ํ•ด์•ผ ํ•œ๋‹ค.

 

์ด๊ฑธ Spring ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 

์Œ.. ํ•˜๋ ค๋ฉด ํ•˜๊ฒ ์ง€๋งŒ ๋ณดํ†ต์€ ์ˆœ์ˆ˜ JPA๋ฅผ ์ด์šฉํ•œ ๊ฒฝ์šฐ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด Repository๋ฅผ ์„ ์–ธํ•ด์ฃผ๊ณ  ํŠน์ • ๊ธฐ๋Šฅ์„ Repository ์—์„œ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.

 

Repository

์šฐ๋ฆฌ๊ฐ€ CRUD๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์ธ Repository ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

 

์œ„์—์„œ๋Š” member์˜ ๊ธฐ๋ณธ ํ‚ค์ธ id๋กœ ์กฐํšŒ๋ฅผ ํ•˜๋ฏ€๋กœ em.find ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์‚ฌ์šฉ์ž ์ด๋ฆ„์œผ๋กœ ์กฐํšŒํ•œ๋‹ค๊ณ  ํ•˜๋ฉด ์šฐ๋ฆฌ๋Š” ์ƒˆ๋กœ์šด ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

 

@Repository
public class MemberRepository {
  @PersistenceContext;
  EntityManager em;

  public void save(Member member) {
    em.persist(member);
  }

  public void findByName(String name) {
    return em.createQuery("SELECT m FROM Member m WHERE m.name = :name", Member.class)
            .setParameter("name", name)
            .getResult();
  }
}

 

๊ทธ๋Ÿผ ์ค‘๋ณต ํšŒ์› ๊ฒ€์‚ฌ๊ฐ€ ์ถ”๊ฐ€๋˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?

 

@Repository
public class MemberRepository {
  @PersistenceContext;
  EntityManager em;

  public void save(Member member) {
    em.persist(member);
  }

  public void findByUsername(String name) {
    return em.createQuery("SELECT m FROM Member m WHERE m.name = :name", Member.class)
            .setParameter("name", name)
            .getResult();
  }

  public void validateUsername(Member member) {
    List<Member> findMembers = memberRepository.findByUsername(member.getUsername());

    if(!findMembers.isEmpty()) {
      throw new IllegalStateException("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํšŒ์›");
    }
  }
}

์ด๋Ÿฐ ํ˜•ํƒœ๊ฐ€ ๋œ๋‹ค.

 

๋ฌผ๋ก  JpaRepository๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ํŠน์ˆ˜ํ•œ ๊ธฐ๋Šฅ์„ ์œ„ํ•ด์„œ ์ถ”๊ฐ€์ ์ธ ์ž‘์—…์„ ํ•ด์•ผ ํ•˜๋‹ˆ ๋ฉ”์„œ๋“œ๊ฐ€ ๋” ๋งŽ์•„์งˆ ๊ฒƒ์ธ๋ฐ, ์œ„์˜ ์ฝ”๋“œ๋Š” ์–ด์ฉŒ๋ฉด ๊ฐ€๋…์„ฑ์ด ์•ˆ ์ข‹์•„ ๋ณด์ด๊ธฐ๋„ ํ•œ๋‹ค.

 

๋ญ์ง€? ๋„ํ†ต ์ฒ˜์Œ ๋ณด๋Š” ๋ฌธ์ž๋“ค์ด ์žˆ๋‹ค.

 

SQL๋„ ์•„๋‹Œ๊ฒƒ ๊ฐ™์€๋ฐ ํ˜•ํƒœ๋Š” SQL ์ด๊ณ .. ์ •ํ™•ํžˆ๋Š” JPQL ์ด๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์‚ฌ์šฉ์ž ์ •์˜ ์ฟผ๋ฆฌ๋ฅผ ์จ์•ผํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋Š” ๋‹ค์Œ์— ๋ฐฐ์šฐ๋‹ˆ ๊ฑฑ์ •๋ง์ž.

 

๋ณธ๋ก ์œผ๋กœ ๋Œ์•„์™€์„œ.

 

์•ž์„œ ๊ณ„์† ์–ธ๊ธ‰ํ•˜์˜€๋“ฏ, JPA๋Š” Spring์— ์ข…์†์ ์ธ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์•ž์„œ ๋ณธ ์ฝ”๋“œ๋“ค์—๋Š” Spring๊ณผ JPA ์‚ฌ์ด์˜ ์ฐจ์ด๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ๋“ฑ์žฅํ•œ ๊ฒƒ์ด ๋ฐ”๋กœ Spring Data JPA ์ธ ๊ฒƒ์ด๋‹ค.

 

Spring Data JPA์˜ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค

Spring Data JPA ๋Š” ๊ฐ„๋‹จํ•œ CRUD ๊ธฐ๋Šฅ์„ ๊ณตํ†ต์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

์ด๋ฅผ JPA ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์ •๋ง ์ •๋ง ๊ฐ„ํŽธํ•˜๊ณ , Jpa์™€ Spring Data JPA๋ฅผ ๊ตฌ๋ถ„์ง“๋Š” ์ฒซ ๊ฑธ์Œ์ด๋ผ ํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

 

public interface UserRepository extends JpaRepository<User, Long>{
}

 

์ด๋Ÿฐ ํ˜•ํƒœ๋กœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†!๋งŒ ๋ฐ›์œผ๋ฉด ์–ด๋–ค ์ผ์ด ์ƒ๊ธธ๊นŒ?

 

findByUsername, save, update ๋“ฑ๊ณผ ๊ฐ™์ด ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๋‹จ์ˆœ ๋ฐ˜๋ณต ์ž‘์—…๋“ค์„ Spring Data JPA ๊ตฌํ˜„์ฒด์ธ Hibernate๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ์ ์— ๋™์ ์œผ๋กœ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ฟผ๋ฆฌ ์ง‘ํ•ฉ์„ ๋งŒ๋“ค์–ด ์šฐ๋ฆฌ์˜ UserRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์ค€๋‹ค.

๊ทธ๋Ÿผ ์šฐ๋ฆฌ๊ฐ€ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” CRUD๋ฅผ ๊ตณ์ด JPQL๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š๋”๋ผ๋„ ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋‚˜๋งŒ ์ƒ์†๋ฐ›์œผ๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

 

๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์˜ JPA ์ธํ„ฐํŽ˜์ด์Šค

JpaRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์† ๋ฐ›์œผ๋ฉด ์–ด๋–ค ๊ธฐ๋Šฅ๋“ค์„ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ๋“ค์ด ์กด์žฌํ•œ๋‹ค.

  • findAll() : ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ ํ…Œ์ด๋ธ”์— ์žˆ๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • save() : ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ๋ฅผ DB์— ์ €์žฅํ•œ๋‹ค.
  • saveAll() : Iterable ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•œ๋‹ค.
  • delete() : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

์ด์™ธ์—๋„ ๋” ๋งŽ์€ ๊ธฐ๋Šฅ๋“ค์ด ์กด์žฌํ•˜๋Š”๋ฐ, JpaRepository ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์ด ์ •์˜๋œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์† ๋ฐ›๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ์ธํ„ฐํŽ˜์ด์Šค๋“ค ๋•๋ถ„์— ์šฐ๋ฆฌ๋Š” ๋‹จ์ˆœ CRUD ์™ธ์— ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ(Paging, Sorting)์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

JpaRepository ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ณ„์ธต ๊ตฌ์กฐ

JpaRepository ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

์ด์ œ JpaRepository๊ฐ€ ๋Œ€์ถฉ ๋ฌด์—‡์ธ์ง€ ์•Œ์•˜์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ณ , ์ง€๋‚œ ์‹œ๊ฐ„๊ณผ ์œ„์—์„œ ๋‚˜์˜จ Repository์˜ ์ƒ์†์— ๋Œ€ํ•ด์„œ ์ž ์‹œ ๋ด๋ณด์ž.

 

public interface UserRepository extends JpaRepository<User, Long>{
}

์ €๊ธฐ JpaRepository์— ์žˆ๋Š” <>๋Š” ์ œ๋„ค๋ฆญ ์ด๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์ œ๋„ค๋ฆญ์— ๋Œ€ํ•ด์„œ ๋ชจ๋ฅธ๋‹ค๋ฉด Generic์„ ํ•ต์‹ฌ๋งŒ ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜์ž๋ฅผ ํ™•์ธํ•˜๊ธธ ๋ฐ”๋ž€๋‹ค.

์ž.

 

JpaRepository ์—๋Š” ์ œ๋„ค๋ฆญ์œผ๋กœ ํƒ€์ž…์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

  1. ์—”ํ‹ฐํ‹ฐ
  2. ์—”ํ‹ฐํ‹ฐ์—์„œ ์‚ฌ์šฉํ•  PK ๋ฐ์ดํ„ฐ ํƒ€์ž…

UserRepository ๋ฅผ ์šฐ๋ฆฌ๋Š” User ์—”ํ‹ฐํ‹ฐ์— ์‹ค์ œ๋กœ ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•˜๋‹ˆ User๊ณผ User ํ…Œ์ด๋ธ”, User ์—”ํ‹ฐํ‹ฐ์˜ PK ๋ฐ์ดํ„ฐ ํƒ€์ž…์ธ Long์„ ๋„ฃ์–ด์ค€ ๊ฒƒ์ด๋‹ค.

 

์‚ฌ์šฉํ•ด๋ณด์ž!

์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์•ž์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์ž์ฃผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ์กฐํšŒ๋ฅผ ํ•ด๋ณด์ž.

 

  • User.class
    • ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค
  • UserRepository.interface
    • ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์ธ Repository ์ธํ„ฐํŽ˜์ด์Šค
    • SpringDataJpa ์˜ ํž˜์„ ๋นŒ๋ฆฌ๊ธฐ ์œ„ํ•ด JpaRepository๋ฅผ ์ƒ์† ๋ฐ›๋Š”๋‹ค.
  • UserRepositoryTest.class
    • ์กฐํšŒ ์ฟผ๋ฆฌ๋ฅผ ํ…Œ์ŠคํŠธํ•  ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค

User

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
}

๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ๋กฌ๋ณต์˜ ์–ด๋…ธํ…Œ์ด์…˜๋“ค์€ ์ œ๊ฑฐํ–ˆ๋‹ค.

 

๋กฌ๋ณต ์–ด๋…ธํ…Œ์ด์…˜์„ ์“ฐ๋˜ setter/ getter, consructor๋ฅผ ๋งŒ๋“ค๋˜ ๊ฐœ์ธ ์ทจํ–ฅ๊ฒƒ!

 

UserRepository

public interface UserRepository extends JpaRepository<User, Long{}

 

์œ„์—์„œ ๊ณ„์† ๋งํ•œ JpaRepository ๋ฅผ ์ƒ์† ๋ฐ›์ž.

 

UserRepositoryTest

@SpringBootTest
@Transactional
@Rollback(false)
class UserRepositoryTest {

    @Autowired
    UserRepository userRepository;

    @Test
    void querySingleRowTest() {
        // given
        List<User> generatedUsers = generateUsersForTest();
        userRepository.saveAll(generatedUsers);

        // when
        Optional<User> selectedUser = userRepository.findById(1L);

        // then
        assertEquals(selectedUser.get().getId(), 1L);
    }

    @Test
    void queriesMultipleRowTest() {

        // given
        List<User> generatedUsers = generateUsersForTest();
        userRepository.saveAll(generatedUsers);

        // when
        List<User> selectedAllUsers = userRepository.findAll();

        // then
        selectedAllUsers.forEach(selectedUser -> {
            System.out.println(selectedUser.toString());
        });

        // then ํ˜น์€
        assertAll(
                () -> assertEquals(selectedAllUsers.get(0).getId(), 1L),
                () -> assertEquals(selectedAllUsers.get(1).getId(), 2L),
                () -> assertEquals(selectedAllUsers.get(10).getId(), 11L)
        );
    }

    List<User> generateUsersForTest() {

        List<User> users = new ArrayList<>();

        for (long i = 1; i < 40; i++) {
            User user = User.builder().id(i).username("์‚ฌ์šฉ์ž " + i).build();
            users.add(user);
        }
        return users;
    }
}

์ด์™€ ๊ฐ™์ด ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

 

๋Œ“๊ธ€