Java / Spring

[회고] 네이티브 쿼리(Native Query) 사용기 본문

DB/MySQL

[회고] 네이티브 쿼리(Native Query) 사용기

밍구밍구밍 2024. 12. 23. 17:40

개인 프로젝트에서 처음 네이티브 쿼리에 대해 알고 사용하게 되었다.

 

개인적으로 네이티브 쿼리의 경우 데이터베이스 종속적인 특성을 가지고 있기 때문에 사용을 지양하려고 했고, 프로젝트 내의 대부분의 쿼리는 ORM 기술을 사용하고 활용하는데 초점을 두고 있었다. 

 

네이티브 쿼리의 장점

  • 복잡한 조인이나 서브쿼리를 작성할 때 유용
  • 데이터베이스에 최적화된 쿼리 작성 (개발자 의도대로 성능 최적화 및 튜닝 가능)

 

네이티브 쿼리의 단점 

  • 코드 가독성이 너무 떨어진다
  • 데이터베이스 종속적 (유지보수성 ↓)
  • 오타가 많이 발생한다
  • MySql 에서 읽지 못하는 타입은 강제로 형변환을 해주야 한다. - 형변환을 하면서 로직이 더 복잡해진다

(LocalDateTime -> TimeStamp)

 

 

* 프로젝트에서 사용자에게 메일을 전송했을 때 보낸 메일함에 아래와 같이 표시되도록 구현 하는것이 목표였다.

 

JPA를 사용하여 구현하려고 시도 해봤으나 DB 에서 사용하는 GROUOP_CONCAT() 과 같이 특정 데이터들을 하나의 필드에서 조회하는 방법을 찾지 못했다.

 

네이티브 쿼리를 사용하여 위와 같은 데이터를 조회한 결과는 아래와 같다

 

사용자가 보낸 메일을 조회하고 해당 메일에 여러명의 사용자가 하나의 필드로 포함되기 위해 조회 쿼리를 작성 하였다.

 

1) 보낸 메일함 group_concat() 내부에 여러명의 사용자 데이터를 저장하고 mysql 데이터타입인 char 로 타입캐스팅을 진행 하였다.

 

@Query(value = "select " +
        "mb.id, " +
        "mb.mail_Title, " +
        "mb.sender_emp_id, " +
        "group_concat(cast(e.emp_name as char)), " +  // CONCAT을 사용하여 이름들을 연결
        "mb.mail_date " +
        "from mailbox mb " +
        "join mailtrans mt on mb.id = mt.mailBox_id " +
        "join employee e on e.id = mt.emp_id " +
        "where mb.sender_emp_id = :senderId and mb.mail_status = 'SENDED' " +
        "group by mb.id, mb.mail_title, mb.mail_content, mb.sender_emp_id",
        countQuery = "select count(mb.id) " +
                "from mailbox mb " +
                "join mailtrans mt on mb.id = mt.mailbox_id " +
                "join employee e on e.id = mt.emp_id " +
                "where mb.sender_emp_id = :senderId and mb.mail_status = 'SENDED'",
        nativeQuery = true)
Page<Object[]> findMailboxesWithReceiveTypeBySend(@Param("senderId") Integer senderId, Pageable pageable);
@Data
@NoArgsConstructor
public class MailBoxDTO {
public MailBoxDTO(Integer mailBoxId, String mailTitle,
                  Integer senderEmployeeId, String senderName,
                  LocalDateTime senderDate) {
    this.mailBoxId = mailBoxId;
    this.mailTitle = mailTitle;
    this.senderEmployeeId = senderEmployeeId;
    this.senderName = senderName;
    this.senderDate = senderDate;
}

 

Dto 에  생성자를 만들고 서비스 레이어에서 아래와 같이 LocalDateTime -> TimeStamp 로 변환하였다

(TimeStamp 로 변환한 데이터를 mySQL 에서 처리할 수 있음)

public Page<MailBoxDTO> findReceiveTypeByDraft(MailBoxDTO mailBoxDto, Pageable pageable) {

    Page<Object[]> result = mailRepository.findMailBoxesWithReceiveTypeByDraft(mailBoxDto.getSenderEmployeeId(), pageable);

    return result.map(row -> new MailBoxDTO(
            (Integer) row[0],
            (String) row[1], // 메일 저장 시 여러명의 사용자의 메일 주소 또는 이름을 (,)로 구분하여 저장
            (Integer) row[2],
            (String) row[3],
            ((Timestamp) row[4]).toLocalDateTime()
    ));
}

 

MySql 의 TimeStamp 와 Java 의 LocalDateTime 이 매칭 되지 않는 이유

1)  TimeStamp(MySql):  UTC 로 저장하며 클라이언트 또는 서버의 타임존에 따라 값을 자동으로 변환한다. 즉, TimpStamp 는 타임존 정보를 포함하고 있기 때문에 데이터를 삽입하거나 조회할 때 MySql 서버의 타임존을 기준으로 변환한다.

 

2) LocalDateTime(Java) : 타임존을 포함하지 않고 단순히 날짜와 시간만을 저장한다. 

LocalDateTime 은 타임존 정보가 없기 떄문에 DB 에 값을 저장할 때 매핑 오류가 발생한다.

 

느낀점 : 프로젝트를 진행하면서  JPA 기술에 익숙해지려고 최대한 JPA 를 사용하고 동적인 쿼리는 QueryDsl 을 사용하였지만 네이티브 쿼리도 사용 해보니 좋았던 점은 평소 DB 에서 조회했던 복잡한 연산의 쿼리들을 ORM 으로 변환하는 것에 익숙하지 않아서 고군분투 하는 시간이 길었는데 앞으로는 JPA 와 네이티브 쿼리를 적절히 혼용하여 사용하는 것도 괜찮을 것 같다는 생각이 든다.  

'DB > MySQL' 카테고리의 다른 글

자주 사용하는 쿼리 (MySQL)  (0) 2024.09.03