본문으로 바로가기

[Spring] 스프링 데이터 JPA - 1

category Spring 2021. 7. 22. 21:56

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-jpa

 

스프링 데이터 JPA - 인프런 | 강의

JPA(Java Persistence API)를 보다 쉽게 사용할 수 있도록 여러 기능을 제공하는 스프링 데이터 JPA에 대해 학습합니다., 스프링 데이터 JPA JPA(Java Persistence API)를 보다 쉽게 사용할 수 있도록 여러 기능을

www.inflearn.com

 

ㅁ 관계형 데이터베이스와 자바

-  Hibernate는 JPA의 구현체이다.

- docker 기반 postgresql 사용

 # docker run -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=defian -e POSTGRES_DB=springdata --name postgres_boot -d postgres

 

- 문제 발생 및 해결  (psql: error: FATAL:  role "postgres" does not exist)

root@452fe3e3c1de:/# su - postgres
postgres@452fe3e3c1de:~$ psql springdata
psql: error: FATAL:  role "postgres" does not exist
postgres@452fe3e3c1de:~$ 
postgres@452fe3e3c1de:~$ psql --username defian --dbname springdata
psql (13.3 (Debian 13.3-1.pgdg100+1))
Type "help" for help.

springdata=#

 

- jdbc는 자바에 기본적으로 포함되어 있음

- DDL : 스키마 조작   / DML : 데이터 조작

- statement와 connection은 try로 감싸서 자원 반환하기 용이하게 함 (try with resource 문이라 부름 - 자바 8부터 가능)

- 커넥션을 만드는 일이 코스트가 많이 드는 일임 (오래걸림)

 > 커넥션 풀을 만들어 사용하는게 유용함 (오픈소스가 많음 - spring은 hikari 라는 오픈소스 사용 )

- SQL이 DB마다 다르고 반복적인 소스코드가 많음

 

ㅁ ORM : Object-Relation Mapping

- ORM은 애플리케이션의 클래스와 SQL 데이터 베이스의 테이블 사이의 맵핑 정보를 기술한 메타데이터를 사용하여, 자바 애플리케이션의 객체를 SQL 데이터베이스의 테이블에 자동으로 ( 또 깨끗하게 ) 영속화 해주는 기술입니다.

- ORM, JPA, Hibernate 에서는 DB와 코드 사이에 캐싱이 있어서, 객체의 변화를 확인하여 실제 변경해야될 경우에만 쿼리를 발생 시킴

- Hibernate, ORM은 러닝커브가 높다.

 

ㅁ ORM 패러다임 불일치

- 밀도 문제 : 커스텀한 타입에 대한 관계 문제를 ORM이 해결해줌

- 서브타입 문제 : interface에 대해 2개의 구현체가 있을때, 이것들의 interface 를 값으로 같는 객체를 어떻게 테이블로 구현할 것인가?

- 식별성 문제 : 객체는 ==, equals 로 비교, 테이블은 primary key로 비교

- 관계 문제 : 객체는 다대다 관계가 가능하나, 테이블은 어려움

- 데이터 네비게이션 문제 : 쿼리를 만들고 실행하는 비용은 비싸다. join을 많이 발생시키는것도 좋은것은 아님

 > n+1 문제 : lazyloading 시 발생, 전체 조회 + 한줄씩 조회

 

ㅁ JPA 프로그래밍

- JPA와 Hibernate API 를 직접 사용하는 일은 거의 없다. 

 > Spring data JPA 사용

- EntityManager가 핵심적인 객체이다

    @PersistenceContext
    EntityManager entityManager;
    
    
    entityManager.persist(account);

- session을 통해서도 가능

 Session session = entityManager.unwrap(Session.class);
 session.save(account);

- @Entity (name="myAccount") 로 엔티티 이름 생성

   @Table에서 @Entity의 이름과 동일하게 테이블 생성 (없으면 class 네임씀 )

 > user와 같이 db 에 존재하는 설정 테이블일 경우, class 이름 = user , @entity 로 생성 불가능

  => entity 이름을 바꾸면 됨

- @Id의 경우, premitive와 reference 타입 모두 가능

 -> 보통 refernece를 씀 ( null 로 구분 지을 수 있음)

- @GenerateValue로 생성되는 id 값의 전략 설정 가능 (DB 마다 다름 )

- 모든 entity에 들어있는 멤버변수는 @Column이 생략되어있음

 > columnDefinition을 써서 매핑도 가능

  > columnDefinition(nullable=false, unique = true) 로 설정 

- @Temporal로 date와 calendar 지원 (jpa 2.1)

- @Transient는 맵핑하지 않게함

- getter setter는 필요없음

 

- Compositive 한 value 타입 만들기

 > 객체 (address)  생성 후, @Embeddable 추가

 >아래와 같이 추가

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name="street", column=@Column(name="home_street"))
    })
    private Address homeAddress;

 >> 테이블에는 address 객체가 가진 column이 들어감

 

- @ManyToOne 이 헷갈릴경우,

  이 어노테이션 단 곳이 one인지 many인지 생각

 > DB에 foreignkey 칼럼이 새로 생김

 

- 양방향 관계일때는 @OneToMany에 mapped by로 반대쪽 관계를 설정해준다.

 > 양쪽에 서로를 등록시켜주는게 객체지향 및 방향성에 맞는 코딩

- 1차 캐시 : 이미 있는건 select 하지 않음

   dirty checking : 객체의 변경사항을 계속 감시

   write behind: 최대한 늦게 객체를 업데이트함

  => 캐싱까지 하여, 여러번 객체가 바뀌더라도 한번만 update , insert 진행

- repository에서 service로 넘기면 detached 상태가 되어 더이상 관리하지 않음

 (transaction과 관련있음 )

- Cascade는 부모 엔티티가 persistent 면 child 엔티티도 persistent 상태로 만들음

 > 자동으로 JPA가 관리하게 해줌 (부모가 삭제되면 child도 삭제되는 등..  post 와 comment 관계 )

- Fetch : 연관된 엔티티를 어떻게 가져올 것이냐 (지금 : eager / 나중에 : lazy)

 > eager를 통해 한번 호출하여, 두번 호출하지 않게함

 > 성능향상에 중요함

- session은 hibernate에 있는 api이고, EntityManager를 통해 unwrap하여  jpa가 감싸고 있는 hibernate api에 접속해서 썼던것 

 > entitymanager의 persist 등을 통해 비슷한 기능을 하게 할 수 있음

- 쿼리 하는 방법은?

 Java Persistence Query Language / Hibernate Query Language

 > entityManager.createQuery("SELECT p FROM post as p);

 

ㅁ 스프링 데이터 JPA 원리

- 아래 코드는 오래된 repository 생성 코드

import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import java.util.List;

@Repository
@Transactional
public class PostRepository {

    @PersistenceContext
    EntityManager entityManager;

    public Post add(Post post){
        entityManager.persist(post);
        return post;
    }

    public void delete(Post post){
        entityManager.remove(post);
    }

    public List<Post> findAll(){
        return entityManager.createQuery("SELECT p FROM Post AS p").getResultList();
    }
}

- 가장 진보적인 repository 생성 코드

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post,Long> {
}

- JpaRepository의 첫번째는 Entity 타입, 두번째는 id 타입

- 스캐닝이나 등록 필요가 없음 ( 부트가 자동으로 해줌 )

- @EnableJpaRepositories 에서 위 repository를 등록이 시작됨

 > ImportBeanDefinitionRegister 인터페이스

 

 

반드시 Hibernate가 발생시키는 Query를 확인하는 습관을 기르자. ( 이게 성능의 핵심 )