현재 하고있는 대외활동인 큐시즘에서 개발팀끼리 스터디를 진행한다.
Java 언어를 위한 ORM 프레임워크이다. Hibernate는 SQL을 직접 사용하지 않고, 메서드 호출만으로 쿼리가 수행된다. 즉, SQL 반복 작업을 하지 않음으로 생산성이 높아진다. 이 말은 SLQ 중심이 아닌 객체 중심의 개발이 가능하다고도 할 수 있다.
Spring framework에서 JPA를 편리하게 사용할 수 있도록 지원하는 모듈이다. Spring Data JPA의 목적은 JPA를 사용할 때 필수적으로 생성해야하나, 예상가능하고 반복적인 코드들을 대신 작성해줘서 코드를 줄여주는 것이다.
Spring framework 를 사용하여 프로젝트를 진행한다면 JPA를 사용하고 있다. SQL을 사용할수도 있지만 SQL을 사용하게 되면 단순 반복 작업을 해야하는게 싫어서 JPA를 사용중이다. JPA 를 사용하면 베이스 모델링에만 집중할 수 있고 내 입장에서는 객체지향적으로 프로그래밍을 하고 JPA가 관계형 DB에 맞게 SQL 을 대신 생성해서 실행해주니 너무 편하다.
데이터를 생성한 프로그램이 종료되어도 사라지지 않는 데이터의 특성을 말한다. 영속성을 갖지 않으면 데이터는 메모리에서만 존재하게 되고 프로그램이 종료되면 해당 데이터는 모두 사라지게 된다. 그래서 데이터를 파일이나 DB에 영구 저장함으로써 데이터에 영속성을 부여한다.
영속성 컨텍스트(persistence context) 는 ‘엔티티를 영구 저장하는 환경' 이라는 뜻이다. EntityManager 를 이용해 Entity 를 저장하거나 조회할 떄 EntityManager 는 영속성 컨텍스트에 Entity 를 보관하고 관리한다.
트랜잭션(Transaction)이란, 데이터베이스의 상태를 변화시키기 위해 수행하는 작업으의 단위를 뜻한다. 간단하게 말해서 아래의 질의어(SQL)를 이용하여 데이터베이스를 접근 하는 것을 의미한다.
착각하지 말하야 할 것은, 작업의 단위는 질의어 한문장이 아니라는 점이다. 작업단위는 ㅁ낳은 질의어 명령문들이 사람이 정하는 기준에 따라 정하는 것을 의미한다.
게시판을 예로 들어보자. 게시판 사용자는 게시글을 작성하고, 올리기 버튼을 누른다. 그 후에 다시 게시판에 돌아왔을때, 게시판은 자신의 글이 포함된 업데이트된 게시판을 보게 된다.
이러한 상황을 데이터베이스 작업으로 옮기면, 사용자가 올리기 버튼을 눌렀을 시, Insert 문을 사용하여 사용자가 입력한 게시글의 데이터를 옮긴다. 그 후에, 게시판을 구성할 데이터를 다시 Select 하여 최신 정보로 유지한다. 여기서 작업의 단위는 insert문과 select문 둘 다 합친것이다. 이러한 작업단위를 하나의 트랜잭션이라고 한다.
관리자나 개발자가 하나의 트랜잭션 설계를 잘 하는것이 데이터를 다루는 것에 많은 이점이 있다고 한다.
원자성은 트랜잭션이 DB에 모두 반영되거나, 전혀 반영되지 않거나를 뜻한다. All or Nothing 이다.
일관성은 트랜잭션 작업 처리의 결과가 항상 일관되어야 한다를 뜻한다. 즉, 데이터 타입이 반환 후와 전이 항상 동일해야 한다.
독립성은 하나의 트랜잭션은 다른 트랜잭션에 끼어들 수 없고 마찬가지로 독립적임을 의미한다. 즉, 각각의 트랜잭션은 독립적이라 서로 간섭이 불가능하다.
지속성은 트랜잭션이 성공적으로 완료되면 영구적으로 결과에 반영되어야 함을 뜻한다. 보통 commit 이 된다면 지속성은 만족할 수 있다.
QueryDSL 은 Hibernate Query Language 쿼리를 타입에 안전하게 생성 및 관리할 수 있게 해주는 프레임워크다. QueryDSL 은 자바 코드를 기반으로 쿼리를 작성하게 해준다라고 생각해도 좋을 것 같다. 또한 Spring Data JPA로 해결하지 못하는 복잡한 쿼리/동적 쿼리를 해결할 수 있다. 그리고 자바 쿼드로 작성하기 떄문에 문법 오류를 컴파일 시점에 잡아낼 수 있다.
JPA를 사용한다고 가정해보자. 간단한 쿼리라면 인터페이스에 메서드 명세만 잘 정의해 주면 별다른 문제 없이 사용할 수 있을 것이다. 예를 들면 아래처럼 “제목에 특정 문자열이 포함된 기사를 조회"하는 메서드처럼.
Arthicle findByTitleContains(String title);
조금 더 복잡한 쿼리가 필요한 경우는 어떨까? 앞서 살펴본 것처럼 단순히 특정 문자열이 포함된 기사를 조회하는 것이 아니라, 기사를 작성한 사용자의 레벨을 기준으로 조회하는 것이다.
이런 경우에는 JPA 자체 제공 메서드만으로 해결하기 어렵기 때문에 네이티브 쿼리를 고려해볼 수 있다. 다음은 레벨이 특정 기준 이상인 사용자가 작성한 기사들을 조회하는 메서드다.
@Query(value = "SELECT id, title, user_id FROM article WHERE user_id IN (SELECT id FROM article WHERE user_id IN (SELECT id FROM user WHERE level > :level)", nativeQuery = true)
List<Article> findByLevel(String level);
위에서 정의한 네이티브 쿼리를 다시 살펴보자. 가독성을 감안하더라도 문자열을 이어 붙여가며 직접 작성하기 때문에 오타가 발생하기 아주 좋다.
그렇다면 이 코드를 QueryDSL로 변경하면 어떻게 될까? 다음은 위에서 살펴본 네이티브 쿼리와 동일한 쿼리를 수행하는 QueryDSL 예시다.
public List<Article> findByUserLevel(String level) {
QArticle article = QArticle.article;
QUser user = QUser.user;
return queryFactory.selectFrom(article)
.where(
article.userId.in(
JPAExpressions
.select(user.id)
.from(user)
.where(user.level.gt(level))
)
)
.fetch();
}
코드의 절대적인 양은 늘었지만 앞에서 살펴본 네이티브 쿼리보다 훨씬 가독성이 좋다.
또한 메서드 타입에 맞지 않는 파라미터를 넘기는 경우 친절하게 컴파일 오류를 발생시켜 잠재적인 버그를 방지해준다. 즉, 실행 시점 이전에 잘못된 쿼리 파라미터 타입까지 확인할 수 있는 장점이 있다.