JPA (Java Persistence API)
- JPA는 데이터베이스에 직접적인 쿼리를 실행하지 않으며 Entity 테이블에 매핑하여 사용한다.
장점
- Native Query나 JPQL을 사용해 쿼리를 직접 작성하는 것보다 개발자가 사용하기 쉽도록 자동으로 쿼리를 만들어준다.
- 기본(Hibernate)으로 쿼리를 자동 생성한다.
Entity 테이블에 매핑하는 것은 아래의 설명을 보면 알겠지만 @Entity 어노테이션이 달린 User Entity 클래스의 각 인스턴스 변수들을 users 테이블의 열로 정의 하는 것이다.
import javax.persistence.*;
@Entity
@Table(name = "users") // 쿼리에서 user는 예약어기 때문에 users로 변경.
public class User {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Column
private String username;
@Column
private String email;
}
위의 Entity 클래스 각 인스턴스는 테이블의 행을 나타내며 변수는 각 행의 열이 된다.
id | username | |
---|---|---|
1 | username1 | user1@email.com |
2 | username2 | user2@email.com |
단점
- 쿼리 작성에 유연하지 않다
- 매핑하여 사용하는 만큼 DB에 직접적으로 쿼리를 실행하는 NativeQuery보다 속도적인 부분에서 차이가 있다.
1번과 같은 상황은 여러 테이블에대해 join해야하는 상황이나 보다 복잡한 쿼리를 작성해야 할 때, Entity에서 @OneToOne, @OneToMany, @ManyToOne 또는 @ManyToMany와 같은 어노테이션을 사용해 연관을 지어줘야 하고, JPQL을 사용해야 한다.
JPQL (Java Persistence Query Language)
- JPQL은 도메인 모델과 일치하는 방식으로 엔터티를 쿼리하도록 설계되었다고 한다.
- JPQL은 데이터베이스에 직접적인 쿼리를 실행하지 않으며 Entity 테이블에 매핑하여 사용한다.
- Query, TypedQuery 두 가지 유형을 사용할 수 있다.
장점
- 객체 지향적인 쿼리로 DB 테이블 조인이 아닌 java에서 엔티티관계 및 상속 개념으로 봐야한다.
- 반환 유형을 지정할 수 있다.
- 특정 유형의 DB(MySQL, Oracle)에 연결된 것이 아니기에 데이터베이스 변경 시에도 유지, 보수 부분에서 유연하며 쿼리를 다시 작성할 필요가 없다.
- 코드 실행 단계가 아닌 코드를 작성하는 시점에서 빠르게 오류를 발견할 수 있다.
Query 사용 예시
import javax.persistence.*; private final EntityManager em; Query query = em.createQuery("SELECT u FROM User u"); List<User> users = (List<User>) query.getResultList(); for (User user : users) { System.out.println(user.getUsername()); }
TypedQuery 사용 예시
import javax.persistence.*; private final EntityManager em; TypedQuery<User> query = em.createQuery("SELECT u FROM User u", User.class); List<User> users = query.getResultList(); for (User user : users) { System.out.println(user.getUsername()); }
TypedQuery의 경우, 반환 유형을 정해놓고 진행하기에 조금 더 가독성 부분에서 좋다. 그리고 JPQL은 도메인 모델과 일치하는 방식으로 엔터티를 쿼리하도록 설계되었다고 했는데 DTO를 구성할 때도 사용할 수 있다.
SELECT 뒤로 해당 클래스의 전체적인 경로를 적어주면 사용 가능하다.
단점
- 모든 SQL 기능을 지원하지 않아 쿼리 작성에 유연하지 않다
- 여러 조인이나 복잡한 쿼리를 작성해야 할 때 어렵다.
- 매핑하여 사용하는 만큼 DB에 직접적으로 쿼리를 실행하는 NativeQuery보다 속도적인 부분에서 차이가 있다.
Native Query
- 데이터베이스에 직접적인 쿼리를 실행한다.
장점
- DB에 직접적으로 쿼리를 실행시켜 속도가 빠르다.
- SQL 언어를 전체적으로 사용 가능하다.
- 여러 조인이나 복잡한 쿼리를 작성할 때 직관적이며 이해하기 쉽다.
아래는 회원 탈퇴를 위한 user의 연관 테이블(bookmark, terms, application)을 지우는 쿼리다.
@Modifying
@Query(value = "update users u " +
"left join bookmark b on u.id = b.user_id " +
"left join terms t on u.id = t.user_id " +
"left join application a on u.id = a.user_id " +
"set u.deleted_at = now(), " +
"b.deleted_at = now(), " +
"t.deleted_at = now(), " +
"a.deleted_at = now() " +
"where u.id = :userId", nativeQuery = true)
void deleteUserAndData(@Param("userId") Long userId);
이처럼 Join을 사용할 때 생각해야할 부분도 있다.
예를 들어 inner join을 사용했을 때 join 테이블에대해 전체 조회를 거쳐야 하며 두 테이블의 일치한 값만 조회한다. left join의 경우 오른쪽 테이블에 값이 없더라도 기존 테이블의 값을 가져와 유지할 수 있다.
위의 쿼리에서 left는 user이고 join하는 두 테이블의 일치값이 없을 때 null을 반환하는 것은 오른쪽 테이블인 bookmark, terms, application중 하나이다.
회원 탈퇴를 해야했기에 bookmark, terms, application이 없는 user도 그대로 불러와 delete할 수 있도록 left를 사용했다.
단점
- 특정 DB에대해 쿼리를 작성하기에 종속성이 높아질 수 있고 DB가 수정되면 쿼리를 다시 만들어야 한다.. (MySQL에서는 LIMIT / Oracle에서는 ROWNUM 등)
- 결과를 Object 리스트로 반환 하기에 JPQL의 TypedQuery보다 안정성 부분에서 미흡하다.
- 주의할 점으로 사용자에게 입력된 값을 매개변수로 바인딩하거나 따로 유효성 검사가 필요할 수 있다.
'TIL' 카테고리의 다른 글
[JAVA] 전역 예외 처리 (0) | 2023.05.15 |
---|---|
[회고] 프로젝트를 마치며.. (0) | 2023.05.13 |
쿠키, 캐시, 세션 (0) | 2023.05.06 |
[자료구조] 스택 & 큐 (0) | 2023.05.05 |
데이터 정규화 (0) | 2023.05.04 |