ORM
ORM은 Object Relational Mapping의 줄임말로 객체 관계 매핑을 의미한다.
자바와 같은 객체지향 언어에서 의미하는 객체와 RDB의 테이블을 자동으로 매핑하는 방법입니다.
클래스는 데이터베이스의 테이블과 매핑하기 위해 만들어진 것이 아니기 때문에 RDB 테이블과 어쩔 수 없는 불일치가 존재하는데 ORM은 이 둘의 불일치와 제약사항을 해결하는 역할이다.
ORM의 장점
1. 데이터베이스 쿼리를 객체지향적으로 조작할 수 있다.
- 쿼리문을 작성하는 양이 현저히 줄어 개발 비용이 줄어든다
- 객체지향적으로 DB에 접근할 수 있어 코드의 가독성이 높아진다
2. 재사용 및 유지보수가 편리하다
- ORM을 통해 매핑된 객체는 모두 독립적으로 작성되어 있어 재사용이 용이하다
- 객체들은 각 클래스로 나뉘어 있어 유지보수가 수월하다
3. 데이터베이스에 대한 종속성이 줄어든다
- ORM을 통해 자동 생성된 SQL문은 객체를 기반으로 DB 테이블을 관리하기 때문에 데이터베이스에 종속적이지 않다
- DB를 교체하는 상황에서도 비교적 적은 리스크를 부담한다
ORM의 단점
1. ORM만으로 온전한 서비스를 구현하기에는 한계가 있다
- 복잡한 서비스의 경우 직접 쿼리를 구현하지 않고 코드로 구현하기 어렵다
- 복잡한 쿼리를 정확한 설계 없이 ORM만으로 구성하게 되면 속도 저하 등의 성능 문제가 발생할 수 있다.
2. 애플리케이션의 객체 관점과 데이터베이스의 관계 관점의 불일치가 발생한다.
JPA
JPA는 자바 진영의 ORM 기술 표준으로 채택되 인터페이스의 모음이다.
JPA 매커니즘을 보면 내부적으로 JDBC를 사용하고 개발자가 직접 JDBC를 구현하면 SQL에 의존하게 되는 문제가 있어 효율성이 떨어지는데 JPA는 이 같은 문제점을 보완해서 개발자가 대신 적절한 SQL을 생성하고 DB를 조자개서 객체를 자동 매핑하는 역할을 수행한다.
하이버네이트
하이버네이트는 자바 ORM의 프레임워크로, JPA가 정의하는 인터페이스를 구현하고 있는 JPA 구현체중 하나이며 가장 많이 사용되는 구현체이다.
영속성 컨텍스트
영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에서 엔티티와 레코드의 괴리를 해소하는 기능과 객체를 보관하는 기능을 수행한다. 엔티티 객체가 영속성 컨텍스트에 들어오면 JPA는 엔티티 객체의 매핑 정보를 데이터베이스에 반영하는 작업을 수행한다. 이처럼 엔티티 객체가 영속성 컨텍스트에 들어와 JPA의 관리 대상이 되는 시점부터는 해당 객체를 영속 객체라고 부른다.
엔티티의 생명주기
엔티티 객체는 영속 컨텍스트에서 4가지의 상태로 구분된다.
비영속(new)
영속성 컨텍스트에 추가되지 않은 엔티티 객체의 상태
영속(managed)
영속성 컨텍스트에 의해 엔티티 객체가 관리되는 상태
준영속(detached)
영속성 컨텍스트에 의해 관리되던 엔티티 객체가 컨텍스트와 분리된 상태
삭제(removed)
데이터베이스에서 레코드를 삭제하기 위해 영속성 컨텍스트에 삭제 요청을 한 상태
엔티티 설계
Spring Data JPA를 사용하면 DB에 테이블을 생성하기 위해 직접 쿼리를 작성할 필요가 없다. 이 기능을 가능케 하는 것이 엔티티이다. JPA에서 엔티티는 DB의 테이블에 대응하는 클래스이며 엔티티에는 DB에 쓰일 테이블과 칼럼을 정의한다.
리포지토리 인터페이스 설계
Spring Data JPA는 JpaRepository를 기반으로 더욱 쉽게 DB를 사용할 수 있는 아키텍처를 제공한다. 스프링 부트로 JpaRepository를 상속하는 인터페이스를 생성하면 다양한 메서드를 사용 가능하다.
리포지토리 인터페이스 생성
여기서 말하는 리포지토리는 Spring Data JPA가 제공하는 인터페이스이다. 엔티티를 DB의 테이블과 구조를 생성하는 데 사용했다면 리포지토리는 엔티티가 생성한 DB에 접근하는 데 사용된다.
public interface ProductRepository extends JpaRepository<Product, Long> {
}
JpaRepository를 상속받을 때는 대상 엔티티와 기본키의 타입을 지정해야 한다. 엔티티는 Product이고 Product의 기본키의 타입은 Long이기 때문에 <Product, Long>를 지정해주면 된다.
리포지토리 메서드의 생성 규칙
메서드에 이름을 붙일 때는 첫 단어를 제외한 이후 단어들의 첫 글자를 대문자로 설정해야 JPA에서 정상적으로 인식하고 쿼리를 자동으로 만들어준다.
FindBy : SQL문의 where 절 역할을 수행하는 구문이다. findBy 뒤에 엔티티의 필드값을 입력해서 사용
ex) findByName(String name)
AND, OR : 조건을 여러 개 설정하기 위해 사용한다.
ex) findByNameAndEmail(String name, String email)
Like/NoLike : SQL문의 like와 동일한 기능을 수행하며, 특정 문자를 포함하는지 여부를 조건으로 추가한다.
StartsWith/StartingWith : 특정 키워드로 시작하는 문자열 조건을 설정
EndsWith/EndingWith : 특정 키워드로 끝나는 문자열 조건 설정
IsNull/IsNotNull : 레코드 값이 Null이거나 Null이 아닌 값을 검색
True/False : Boolean 타입의 레코드를 검색할 때 사용
Before/After : 시간을 기준으로 값을 검색
LessThan/GreaterThan: 특정 값을 기준으로 대소 비교를 할 때 사용
Between : 두 값 사이의 데이터 조회
OrderBy : SQL 문에서 order by와 동일한 기능.
ex) List<Product> findByNameOrderByPriceAsc(String name)
countBy : SQL문의 count와 동일한 기능 수행하며, 결과값으 갯수를 추출.
DAO 설계
DAO는 DB에 접근하기 위한 로직을 관리하기 위한 객체.
비지니스 로직의 동작 과정에서 데이터를 조작하는 기능은 DAO 객체가 수행한다.
하지만 스프링 데이터 JPA에서 DAO의 개념은 리포지토리가 대체하고 있다.
DB와 밀접한 관련이 있는 데이터 액세스 레이어까지는 엔티티 객체를 사용하고, 클라이언트와 가까워지는 다른 레이어에서는 데이터를 교환하는 데 DTO 객체를 사용하는 것이 일반적이다.