[스프링 부트 쇼핑몰 프로젝트 with JPA] 2장 정리

엔티티 매니저를 통해 영속성 컨텍스트에 접근하여 데이터베이스 작업을 수행하며, 이는 1차 캐시, 동일성 보장, 쓰기 지연, 변경 감지 등의 이점을 제공한다. Spring Data JPA를 사용하면 Repository 인터페이스를 통해 엔티티를 관리할 수 있으며, 복잡한 쿼리는 @Query 어노테이션과 JPQL을 사용하거나 Querydsl을 사용하여 처리할 수 있다. 또한, 쇼핑몰 프로젝트를 생성하고 상품 엔티티를 설계하는 방법, 데이터베이스 초기화 전략, 엔티티 매칭 관련 어노테이션, 기본키 생성 전략 등에 대해 설명하였다.
DriedPollack's avatar
Mar 29, 2024
[스프링 부트 쇼핑몰 프로젝트 with JPA] 2장 정리

🌼JPA

💡핵심 키워드

  • JPA(Java Persistence API)는 자바 ORM 기술에 대한 API 표준이다. ORM이란 Object Relational Mapping의 약자로 객체와 관계형 데이터베이스를 매핑해주는 것을 말한다.

JPA란?

  • 데이터를 관계형 데이터베이스에서 관리하기 위해 우리는 SQL문을 사용한다.
    • SQL 중심 개발의 문제점은 개발자가 CRUD문을 작성해서 객체를 관계형 데이터베이스에 넣어주고 가져오는 작업을 하는 것이다.
    • 즉 자바 객체를 SQL을 통해 데이터베이스에 관리하게 하고 데이터베이스에 저장된 데이터를 자바 어플리케이션에서 사용하려면 SQL을 통해 다시 자바 객체로 변환하는 반복적인 작업을 해야 한다.
    • 또한 객체와 관계형 데이터베이스의 패러다임의 불일치가 발생한다.
  • JPA 사용시 장점
    • 특정 데이터베이스에 종속되지 않는다.
    • 객체지향적 프로그래밍이 가능하다.
    • 생산성을 향상시킬 수 있다.
  • JPA 사용시 단점
    • 복잡한 쿼리를 사용할 경우 SQL문을 사용하는 게 나을 수 있다.
    • 성능 저하의 위험이 존재한다.
    • 학습 시간이 오래 걸린다.

JPA 동작 방식

  • 엔티티란 데이터베이스의 테이블에 대응하는 클래스다.
    • @Entity가 붙은 클래스는 JPA에서 관리하는 엔티티가 된다.
  • 엔티티 매니저 팩토리는 엔티티 매니저 인스턴스를 관리하는 주체이다.
    • 애플리케이션 실행 시 한 개만 만들어지며 사용자로부터 요청이 오면 엔티티 매니저 팩토리로부터 엔티티 매니저를 생성한다.
  • 엔티티 매니저는 영속성 컨텍스트에 접근하여 엔티티에 대한 데이터베이스 작업을 제공한다.
    • 내부적으로 데이터베이스 커넥션을 사용해서 데이터베이스에 접근한다.
    • find() : 영속성 컨텍스트에서 엔티티를 검색하고 영속성 컨텍스트에 없을 경우 데이터베이스에서 데이터를 찾아 영속성 컨텍스트에 저장한다.
    • persist() : 엔티티를 영속성 컨텍스트에 저장한다.
    • remove() : 엔티티 클래스를 영속성 컨텍스트에서 삭제한다.
    • flush() :영속성 컨텍스트에 저장된 내용을 데이터베이스에 반영한다.
  • 영속성 컨텍스트는 엔티티를 영구 저장하는 환경으로 엔티티 매니저를 통해 접근한다.
    • 생명주기
      내용
      비영속(new)
      new 키워드를 통해 생성된 상태로 영속성 컨텍스트와 관련이 없는 상태
      영속(managed)
      -엔티티가 영속성 컨텍스트에 저장된 상태로 영속성 컨텍스트에 의해 관리되는 상태 -영속 상태에서 데이터베이스에 저장되지 않으며, 트랜잭션 커밋 시점에 데이터베이스에 반영
      준영속 상태(detached)
      영속성 컨텍스트에 엔티티가 저장되었다가 분리된 상태
      삭제 상태(removed)
      영속성 컨텍스트와 데이터베이스에서 삭제된 상태
  • 영속성 컨텍스트 사용시 이점은 다음과 같다.
    • 1차 캐시
      • 영속성 컨텍스트에는 1차 캐시가 존재하며 Map<KEY, VALUE>로 저장된다.
      • entityManager.find() 메소드 호출시 영속성 컨텍스트의 1차 캐시를 조회해서 엔티티가 존재할 경우 반환하고, 없으면 데이터베이스에서 조회 후 1차 캐시에 저장 및 리턴한다.
    • 동일성 보장
      • 하나의 트랜잭션에서 같은 키값으로 영속성 컨텍스트에 저장된 엔티티 조회 시 1차 캐시에 저장된 엔티티를 조회하므로 같은 엔티티 조회를 보장한다.
    • 트랜잭션을 지원하는 쓰기 지연
      • 영속성 컨텍스트에는 쓰기 지연 SQL 저장소가 존재한다.
      • entityManager.persist()를 호출하면 1차 캐시에 저장되는 것과 동시에 쓰기 지연 SQL 저장소에 SQL문이 저장된다.
      • 이렇게 SQL문을 쌓아두고 트랜잭션을 커밋하는 시점에 저장된 SQL문들이 flush되면서 데이터베이스에 반영된다. 따라서 성능에서 이점을 볼 수 있다.
    • 변경 감지
      • JPA는 1차 캐시에 데이터베이스에서 처음 불러온 엔티티의 스냅샷 값을 갖고 있다.
      • 그리고 1차 캐시에 저장된 엔티티와 스냅샷을 비교 후 변경 내용이 있다면 UPDATE SQL문을 쓰기 지연 SQL 저장소에 담아둔다.
      • 그리고 데이터베이스에 커밋 시점에 변경 내용을 자동으로 반영한다. 즉, 따로 update문을 호출할 필요가 없다.
 

🌼쇼핑몰 스포젝트 생성하기

💡핵심 키워드

프로젝트 생성하기

  • Thymeleaf : 서버에서 가공한 데이터를 뷰에 보여주기 위한 템플릿 엔진이다.
  • Spring Data JPA : JPA를 쉽세 구현할 수 있도록 도와주는 모듈이다.
  • MySQL Driver : MySQL 데이터베이스를 사용하기 위한 의존성이다.
  • H2 Dataabase : 자바 기반의 관계형 데이터베이스로 매우 가볍고 빠른 데이터베이스다. 메모리 내에 데이터를 저장하는 메모리 데이터베이스 기능을 지원한다.

데이터베이스 초기화 전략

  • application.properties에 추가한 jpa 옵션 중 주의 깊게 봐야 할 설정은 DDL AUTO 옵션이다.
    • spring.jpa.hibernate.ddl-auto 옵션을 통해 애플리케이션 구동 시 JPA의 데이터베이스 초기화 전략을 설정할 수 있다.
      • none : 사용하지 않음
      • create : 기존 테이블 삭제 후 테이블 생성
      • create-drop : 기존 테이블 삭제 후 테이블 생성, 종료 시점에 테이블 삭제
      • update : 변경된 스키마 적용
      • validate : 엔티티와 테이블 정상 매핑 확인
    • 스테이징, 운영환경에서는 절대로 create ,create-drop, update 를 사용하면 안된다.
 

🌼상품 엔티티 설계하기

💡핵심 키워드

  • 쇼핑몰을 만들기 위해서는 상품 등록 및 조회, 수정, 삭제가 가능해야 한다.

상품 엔티티 설계하기

  • 엔티티란 데이터베이스의 테이블에 대응하는 클래스다.
    • @Entity가 붙은 클래스는 JPA에서 관리하며 엔티티라고 한다.
  • Lombok의 어노테이션을 이용한다면 getter, setter, toString등을 자동으로 만들어주기 때문에 코드를 깔끔하게 짤 수 있다.
  • 엔티티 매칭 관련 어노테이션
    • 어노테이션
      설명
      @Entity
      클래스를 엔티티로 선언
      @Table
      엔티티와 매핑할 테이블을 지정
      @id
      테이블의 기본키에 사용할 속성을 지정
      @GeneratedValue
      키 값을 생성하는 전략 명시
      @Column
      필드와 컬럼 매핑
      @Lob
      BLOB, CLOB 타입 매핑
      @CreationTimestamp
      insert 시 시간 자동 저장
      @UpdateTimeStamp
      update 시 시간 자동 저장
      @Enumeratierd
      enum 타입 매핑
      @Transient
      해당 필드 데이터베이스 매핑 무시
      @Temporal
      날짜 타입 매핑
      @CreateDate
      엔티티가 생성되어 저장될 때 시간 자동 저장
      @LastModifiedDate
      조회한 엔티티의 값을 변경할 떄 시간 자동 저장
  • @Column 어노테이션 추가 속성
    • 속성
      설명
      기본값
      name
      필드와 매핑할 컬럼의 이름 설정
      객체의 필드 이름
      unique(DDL)
      유니크 제약 조건
      insertable
      insert 가능 여부
      true
      updatable
      update 가능 여부
      true
      length
      String 타입의 문자 길이 제약조건 설정
      255
      nullable(DDL)
      null 값의 허용 어부 설정, false 설정 시 DDL 생성 시에 not null 제약조건 추가
      columnDefinition
      데이터베이스 컬럼 정보 직접 기술 예) @Column(columnDefinition = “varchar(5) default’10’ not null”)
      precision, scale(DDL)
      BigDecimal 타입에서 사용(BigInteger 가능) precision은 소수점을 포함한 전체 자리수이고, scale은 소수점 자리수. Double 타입과 float 타입에는 적용되지 않음
  • @GeneratedValue 어노테이션을 통한 기본키를 생성하는 전략
    • 생성 전략
      설명
      GenerationType.AUTO(default)
      JPA 구현체가 자동으로 생성 전략 결정
      GenerationType.IDENTITY
      기본키 생성을 데이터베이스에 위임 예) MySql 데이터베이스의 경우 AUTO_INCREMENT를 사용하여 기본키 생성
      GenerationType.SEQUENCE
      데이터베이스 시퀀스 오브젝트를 이용한 기본키 생성 @SequenceGenerator를 사용하여 시퀀스 등록 필요
      GenerationType.TABLE
      키 생성용 테이블 사용, @TableGenerator 필요
 

🌼Repository 설계하기

💡핵심 키워드

  • Spring Data JPA에서는 엔티티 매니저를 직접 이용해 코드를 작성하지 않아도 된다.
    • 그 대신 Data Access Object의 역할을 하는 Repository 인터페이스를 설계한 후 사용하는 것만으로 충분하다.
  • JpaRepository에서 지원하는 메소드 예시
    • 메소드
      기능
      <S extends T> save(S entity)
      엔티티 저장 및 수정
      boid delete(T entity)
      엔티티 삭제
      count()
      엔티티 총 개수 반환
      Iterable<T> findAll()
      모든 엔티티 조회
  • 테스트 환경의 경우 h2 데이터베이스를 사용하도록 resources 아래에 테스트 환경을 위한 별도의 application-test.properties 파일을 만든다.
 

🌼쿼리 메소드

💡핵심 키워드

  • 쿼리 메소드는 스프링 데이터 JPA에서 제공하는 핵심 기능 중 하나로 Repository 인터페이스에 간단한 네이밍 룰을 이용하여 메소드를 작성하면 원하는 쿼리를 실행할 수 있다.
  • 쿼리 메소드를 이용할 떄 가장 많이 사용하는 문법으로 find를 사용한다. 엔티티의 이름은 생략이 가능하며, By 뒤에는 검색할 때 사용할 변수의 이름을 적어준다.
    • find + (엔티티 이름) + By + 변수이름
  • 쿼리 메소드 Sample 및 JPQL snippet
    • Keyword
      Sample
      JPQL snippet
      And
      findByLastnameAndFirstname
      … where x.lastname = ?1 and x.firstname = 2?
      Or
      findByLastnameOrFirstname
      … where x.lastname = ?1 or x.firstname = 2?
      Is, Equals
      findByFirstname findByFirstnameIS findByFirstnameEquals
      … where x.lastname = ?1
      Between
      findByStartDateBetween
      … where x.startDate between ?1 and ?2
      LessThan
      findByAgeLessThan
      … where x.age < ?1
      LessThanEqual
      findByAgeLessThanEqual
      … where x.age <= ?1
      GraterThan
      findByAgeGraterThan
      … where x.age > ?1
      GraterThanEqual
      findByAgeGraterThanEqual
      … where x.age >= ?1
      After
      findByStartDateAfter
      … where x.startDate > ?1
      Before
      findByStartDateBefore
      … where x.startDate < ?1
      IsNull, Null IsNotNull
      findByAge(Is)Null
      … where x.age is null
      NotNull
      findByAge(Is)NotNull
      … where x.age not null
      Like
      FindByFirstnameLike
      … where x.firstname like ?1
      NotLike
      FindByFirstnameNotLike
      … where x.firstname not like ?1
      StartingWith
      FindByFirstnameStartingWith
      … where x.firstname like ?1 (parameter bound with appended %)
      EndingWith
      FindByFirstnameEndingWIth
      … where x.firstname like ?1 (parameter bound with prepended %)
      Containing
      FindByFirstnameContaining
      … where x.firstname like ?1 (parameter bound with wrapped in %)
      OrderBy
      findByAgeOrderByLastnameDesc
      … where x.age = ?1 order by x.lastname desc
      Not
      findByLastnameNot
      … where x.lastname <> ?1
      In
      findByAgeIn(Collection<Age> ages)
      … where x.age in ?1
      NotIn
      findByAgeNotIn(Collection<Age> ages)
      … where x.age not in ?1
      True
      findByActiveTrue()
      … where x.active = true
      False
      findByActiveFalse()
      … where x.active = false
      IgnoreCase
      findByFirstnameIgnoreCase
      … where UPPER(x.firstname) = UPPER(?1)
 

🌼Repository 설계하기

💡핵심 키워드

  • Spring Data JPA에서는 엔티티 매니저를 직접 이용해 코드를 작성하지 않아도 된다.
    • 그 대신 Data Access Object의 역할을 하는 Repository 인터페이스를 설계한 후 사용하는 것만으로 충분하다.
  • JpaRepository에서 지원하는 메소드 예시
    • 메소드
      기능
      <S extends T> save(S entity)
      엔티티 저장 및 수정
      boid delete(T entity)
      엔티티 삭제
      count()
      엔티티 총 개수 반환
      Iterable<T> findAll()
      모든 엔티티 조회
  • 테스트 환경의 경우 h2 데이터베이스를 사용하도록 resources 아래에 테스트 환경을 위한 별도의 application-test.properties 파일을 만든다.
 

🌼Spring DATA JPA @Query 어노테이션

💡핵심 키워드

  • 조건이 많아질 때 쿼리 메소드를 선언하면 이름이 너무 길어진다. 따라서 쿼리 메소드는 복잡한 쿼리를 다루기에 적합하지 않다.
  • Spring Data JPA에서 제공하는 @Query 어노테이션을 이용하면 SQL과 유사한 JPQL이라는 객체지향 쿼리 언어를 통해 복잡한 쿼리도 처리가 가능하다.
    • JPQL은 SQL을 추상화해서 사용하기 떄문에 특정 데이터베이스 SQL에 의존하지 않는다.
  • @Param 어노테이션을 이용하여 변수를 JPQL에 전달하는 대신 파라미터의 순서를 이용해 전달해줄 수 있다.
    • 이 경우 :파라미터 이름 대신 철 번째 파라미터를 전달하겠다는 ?1 이라는 표현을 사용하면 된다.
 

🌼Spring DATA JPA Querydsl

💡핵심 키워드

  • @Query 어노테이션 안에 JPQL 문법으로 문자열을 입력할 때 잘못 입력하면 컴파일 시점에 에러를 발견할 수 없다.
    • 이를 보완할 수 있는 방법으로 Querydsl이 있다.
  • Querydsl 장점
    • 고정된 SQL문이 아닌 조건에 맞게 동적으로 쿼리를 생성할 수 있다.
    • 비슷한 쿼리를 재사용할 수 있으며 제약 조건 조립 및 가독성을 향상시킬 수 있다.
    • 문자열이 아닌 자바 소스코드로 작성하기 때문에 컴파일 시점에 오류를 발견할 수 있다.
    • IDE의 도움을 받아서 자동 완성 기능을 이용할 수 있기 때문에 생산성을 향상시킬 수 있다.
  • Querydsl을 사용하기 위해서는 pom.xml 파일의 <dependencies> </dependencies> 사이에 querydsl-jpa 의존성과 querydsl-apt 의존성을 추가해야 한다. 이 경우 version은 5.0.0 이상이어야 한다.
  • 추가한 의존성을 받아오기 위해 인텔리제이의 오른쪽에 있는 Maven에서 Reload All Maven Projects 버튼을 클릭한다.
    • 이후 maven compile을 통해 QItem 클래스를 생성할 수 있다.
  • JPAQuery 데이터 반환 메소드
    • 메소드
      기능
      List<T> fetch()
      조회 결과 리스트 반환
      T fetchOne
      조회 대상이 1건인 경우 제네릭으로 지정한 타입 반환
      T fetchFirst()
      조회 대상 중 1건만 반환
      Long fetchCount()
      조회 대상 개수 반환
      QueryResult<T> fetchResults()
      조회한 리스트와 전체 개수를 포함한 QueryResults 반환
  • QueryDslPredicateExecutor 인터페이스 정의 메소드
    • 메소드
      기능
      long count(Predicate)
      조건에 맞는 데이터의 총 개수 반환
      boolean exists(Predicate)
      조건에 맞는 데이터 존재 여부 반환
      Iterable findAll(Predicate)
      조건에 맞는 모든 데이터 반환
      Page<T> findAll(Predicate, Pageable)
      조건에 맞는 페이지 데이터 반환
      Iterable findAll(Predicate, Sort)
      조건에 맞는 정렬된 데이터 반환
      T findOne(Predicate)
      조건에 맞는 데이터 1개 반환
 

🏁결론

해당 내용을 정리하면서 JPA의 등장 배경 및 특징, JPA의 구조와 동작 방식, SpringData JPA를 이용하여 데이터를 처리하는 방법을 이해할 수 있었다.
Share article

More articles

See more posts

👨🏻‍💻DriedPollack's Blog