JPA (Java Persistece Api)

18232 단어 JavaORM MappingJPAJPA

1. JPA(Java Persistece Api)란

  • ORM을 사용하기 위한 인터페이스를 모아둔 것
  • 인터페이스, 메서드 추가만으로 기능 구현 가능 (직접 구현체 구현 할 필요 X)
  • 어플리케이션 실행 시점에 DB 테이블을 자동으로 생성해 줌
  • 신규 개발, 컬럼 추가 시 반복적으로 CRUD sql 작성을 피하기 위해서, 쿼리를 자동 생성해주는 JPA 사용
  • 구현체 ex : Hibernate

[ 장점 ]

  • 생산성 good. 유지보수 용이
    • 객체 지향적인 코드 => 비즈니스 로직에 더 집중 가능
    • sql을 직접 작성하지 않고, 객체를 사용해 동작 => 재사용성 증가. 유지보수 간결해짐
    • DB 컬럼이 추가될 때마다 테이블 수정, SQL 수정하는 과정 줄어듦
  • DBMS에 대한 종속성 줄어듦
    • DBMS가 변경되더라도 소스, 쿼리, 구현 방법등을 변경할 필요 X

[ 통신 과정 ]



2. JPA 구현

1) Entity 클래스

  • Entity : 실제 DB 테이블과 매칭될 클래스

@Entity(name="member") 
@Getter
@Builder
@AllArgsConstructor 
@NoArgsConstructor(access = AccessLevel.PROTECTED) 
@IdClass
public class Member{
      @Id 
      @GeneratedValue(strategy = GenerationType.IDENTITY) 
      private int mbrNo; 
      private String id; 
      private String name;
}

[ 어노테이션 설명 ]

(1) @Entity

  • 테이블에 대응되는 클래스 (매핑 할 클래스)

(2) @Builder

  • Builder 객체 자동 생성

(3) @Table(schema, name)

  • entity와 매핑할 테이블 지정.
  • 생략시 매핑한 entity 이름을 테이블 이름으로 사용

(4) @GeneratedValue(strategy)

  • 기본키 생성을 DB에 위임
  • 기본키 자동 생성 (auto_increment)

(5) @IdClass(class)

  • 복합키 매핑을 위해 사용
    - @IdClass 없이, 2개 이상의 속성에 @Id를 붙이면 에러 발생
  • 'class'에 PK가 있는 클래스 이름 명시 (DTO)

💡 Builder Pattern

객체 설정 시 인자가 많을 경우, 코드 길어짐 + 가독성 떨어짐
이를 해결하고자 Builder Pattern 등장
필요한 객체를 직접 생성하는 대신, 필수 인자 값들을 전달해 Builder 객체를 만듦

💡 DTO와 Entity를 분리하는 이유?

DTO는 View layer을 위한 클래스. Entity는 DB layer을 위한 클래스.
테이블과 매핑되는 Entity 클래스가 변경되면 여러 클래스에 영향을 끼침. View와 통신하는 DTO는 자주 변경되므로, 분리하는게 적절.

따라서 View layer과 DB layer 역할을 분리하기 위해서 따로 둔다


2) Repository 인터페이스

  • Entity의 기본적인 CRUD가 가능하게 함
    - save(), findOne(), findAll(), count(), delete() 함수 기본 제공
  • JpaRepository<Entity, Type>을 상속받아 사용
    • Type은 id 필드 타입
    • 기본 타입이 아닌, wrapper class를 지정해야 함
  • 쿼리 메서드 기능
    (1) 메서드 이름으로 쿼리 생성
    (2) @Query어노테이션 사용해서 직접 쿼리 작성

[ 쿼리 메서드 기능 ]

(1) 메서드 명으로 쿼리 생성

  • 메서드 이름으로 쿼리가 자동 생성되게 함

a. 메서드 명명 규칙

  • find ~ By
    : 단일 객체 조회
  • findAll ~ By
    : 다수의 객체 조회
  • count ~ By
    : 레코드 수 조회
  • limit
    : 레코드 수 제한
    ex) findTop1, findFirst
public interface PostRepository extends JpaRepository<Post, Long> {
    List<Post> findAllByTitleLike(String title);

    List<Post> findAllByCreatedDateGreaterThanEqualOrderByIdDesc(LocalDateTime localDateTime);

    List<Post> findAllByTagListInOrderByCreatedDateAsc(List<Tag> tagList);
}

b. 메소드 키워드

  • And
  • Or
  • Between
  • Like
  • IsNull, IsNotNull
  • In, NotIn
  • Is, Equals
  • OrderBy

(2) @Query 사용

  • 실행할 메서드 위에 정적 쿼리 작성
    - JPQL 사용
  • 개발자가 원하는 쿼리 작성 가능
  • @Query, @Param 같이 사용 => 파라미터 바인딩
  • 어플리케이션 로딩 시점에 문법오류 감지

[ 파라미터 바인딩 ]

  • ':' 사용
    • ex) :{파라미터이름}
  • @Param("")으로 들어오는 파라미터를 JPQL에서 사용

a. 단수 파라미터 바인딩

  • '=' 사용
@Query("select m from Member m where m.username = :username and m.age = :age")
List<Member> findUser(@Param("username") String username, @Param("age") int age);

b. 복수 파라미터 바인딩

  • '=' 대신 SQL IN절 사용
@Query("select m from Member m where m.username in :usernames)
List<Member> findUser(@Param("usernames") List<String> usernames);

💡 JPQL (Java Persistence Query Language) ?

JPA에서 관계형 데이터베이스 엔티티에 대한 쿼리를 만들기 위해 사용
SQL문과 문법은 비슷.
차이점은 데이터베이스 테이블에 직접 연결되는 것이 아니라, JPA 엔티티에 대해 동작한다는 점!
따라서 테이블이 아닌, 엔티티명과 엔티티 컬럼의 이름을 사용


3) Service 클래스

@Service 
@RequiredArgsConstructor
public class MemberService { 
    private final MemberRepository memberRepository; 

    public List<MemberDTO> findAll() { 
        List<MemberDTO> members = new ArrayList<>();
        memberRepository.findAll().forEach(e -> members.add(e)); 
        return members; 
    } 

    public Optional<MemberDTO> findById(Long mbrNo) { 
        Optional<MemberDTO> member = memberRepository.findById(mbrNo); 
        return member; 
    } 

    public void deleteById(Long mbrNo) { 
        memberRepository.deleteById(mbrNo); 
    } 
    
    public MemberVo save(MemberDTO member) { 
        memberRepository.save(member); 
        return member; 
    } 

    public void updateById(Long mbrNo, MemberDTO member) { 
        Optional<MemberDTO> e = memberRepository.findById(mbrNo); 
        if (e.isPresent()) { 
            e.get().setMbrNo(member.getMbrNo());
            e.get().setId(member.getId()); 
            e.get().setName(member.getName()); 
            memberRepository.save(member); 
         } 
    } 
}

4) Controller 클래스



참고 블로그 :

좋은 웹페이지 즐겨찾기