콘텐츠로 이동

2021 06 28

2021-06-28

JPA 제이슨

  • 마음 한편에는,,, - Dao가 서로 의존해서 객체를 만들어야 할지 고민 - 부모 자식 관계가 삭제되면 DB에 자동 반영될 수는 없나?
  • 태초에,,, - 객체 지향 패러다임 - 시스템을 구성하는 객체들에게 적절한 책임 할당 - 상속 - 연관관계 - 자유롭게 객체 그래프를 탐색할 수 있어야 함 - 테이블 - 방향성이 서로 없어 - A JOIN B를 하던 - B JOIN A를 하던
  • SQL을 직접 작성하는 것의 문제점,,, - 반복 작업 - 새로운 필드 추가되면 SQL 다 추가해줘야해 - 신뢰성 - 개발자들이 Dao에서 조회해 온 엔티티 신뢰할 수 없다
  • JPA (ORM Framework) - hibernate 구현체 - 장점 - DB 스키마 자동생성 (내가 만든 객체를 기준으로!) - JPA가 보고 오? 이거 이런 테이블 만들면 되겠구나! - "spring.jpa.hibernate.ddl-auto=create" - create: 기존 테이블 삭제 후 생성 - create-drop: create와 같으나 종료시점에 테에블 DROP - update: 변경된 부분만 반영 - validate: entity와 table이 정상 매핑되었는지만 확인 - none: 사용하지 않음 - 실습 - 의존성 - implementation 'org.springframework.boot:spring-boot-starter-date-jpa' - 사용법 - 객체에 @Entity를 추가 - 파라미터 없는 생성자 만듦 (리플렉션과 비슷한 기술 사용함) - Entity에 PK를 등록해주기 + Auto-increment 설정 - @Id 어노테이션 붙여주기 - @GeneratedValue()를 통해 어떻게 Id값 관리할지 지정 - strategy = GeneratedType.IDENTITY - strategy = GeneratedType.SEQUENCE // PK 값 관리 테이블 별도 지금 - strategy = GeneratedType.AUTO // DB 벤더사에 의존적이라 위험함 - @Table(name = "station") 매핑할 테이블 이름 지정 - 안해주면 그냥 엔티티 이름이 테이블 이름 - @Column(nullable = false) - 해당 필드는 not null! - DB 스키마 상에서 null이 될 수 없다는겨 - @NotNull 이거랑은 달라
  • Spring Data JPA - save(), findBy~()와 같은 반복작업을 - CRUDRepository를 만들어뒀어 - JpaRepository // 객체와 ID의 형 - findByName() 과 같은 기능도 자동완성 해줄수 있어
    public interface StationRepository extends JpaRepository<Station, Long> {
        Optional<Station> findByName(String name);
        List<Station> findAllByName(String name);
    }
    

    - 테스트 코드 작성 - @DataJpaTest를 통해 필요한 설정 해둘 수 있음

  • 영속성 컨텍스트 - 엔티티를 영구 저장하는 환경 - 엔티티 매니저는 한 트랜잭션당 엔티티 매니저 생성함 - 엔티티 매니저 팩토리는 하나만 생성 - 엔티티 매니저로 엔티티 저장/조회 시 엔티티 매니저는 영속성 컨텍스트에 엔티티 보관/관리 - 1차 캐시 - 동일서 보장 - 트랜잭션 지원 쓰기 지연 - 변경 감지 - 지연 로딩 - id를 key값으로 Entity를 value로 - 캐시에 있으면 해당 Entity 반환 - 캐시에 없으면 DB에 Entity 조회해서 반환 - 엔티티의 생명주기 - 비영속: 방금 인스턴스 생성한거 (메모리엔 있는데 DB/영속성 컨텍스트는 모름) - 영속: 영속성 컨텐스트에 저장한 상태 - 준영속: 영속성 컨텍스트에 저장했다가 분리된 상태 (트랜잭션 벗어난 경우) - 삭제: 삭제된 상태 - - Transaction이 없으면 동작하지 않아!
    - EmbeddedTomcat 하나의 요청 당 하나의 쓰레드, 하나의 트랜잭션 - 하나의 요청에 대해 캐싱 동작 - @Transactional - 트랜잭션 커밋하는 순간 영속성 컨텍스트 ---> 데이터베이스 - commit & flush - 쓰기지연 SQL 저장소 - INSERT QUERY 등 - 여기에 쿼리를 쌓아 두었다가 commit() 되면 이거 촤르르 flush() - 이게 쓰기지연 - 변경 감지 - SNAPSHOT과 ENTITY를 비교하여 달라졌다면 update query 생성후 commit() - 마치 컬렉션을 다루듯이 DB를 사용하자
    @Test
    public void update() {
        final Station station = stations.save(new Station("잠실역"));
        station.changeName("잠실나루역");
        // 변경 감지하고 update 쿼리 커밋한다
        final Station actual = stations.findByName("잠실나루역");
        assertThat(actual).isNotNull();
        // 여기서 트랜잭션 커밋
    }
    
      - 참고로 1차 캐시론 조회 안댐
          - 1차 캐시 key값은 ID
          - ID를 기반으로 1차 캐시 찌르기 가능
          - ID 기반 아니면 바로 DB를 찔러
      - Id 기반으로 찌르지 않는 친구들을 "JPQL"이라고 함
          - 직접 쿼리를 생성하는 것
    

DB 관련 키워드

  • Transaction - 참고: https://mommoo.tistory.com/62 - DB의 상태를 변화시키기 위해 수행하는 "쪼개질 수 없는 작업의 단위" 뜻함 - 특징 - 원자성: DB에 모두 반영되던가, 전혀 반영되지 않던가 - 일관성: 작업처리가 항상 일관성 있어야 함 - 독립성: 어떤 하나의 트랜잭션이라도, 다른 트랜잭션의 연산에 끼어 들 수 없음 - 지속성: 결과는 영구적으로 반영되어야 함 - Commit: 하나의 트랜잭션 성공적으로 끝남. DB 일관성있는 상태에 있음 - Rollback: 하나의 트랜잭션 처리가 비정상적으로 끝나 원자성이 깨진 경우, 트랜잭션 재시작 or 연산 취소
  • Foreign Key - 참고: https://brunch.co.kr/@dan-kim/26 - 두 테이블의 데이터 간 연결을 설정하고 강제 적용하여 외래 키 테이블에 저장될 수 있는 데이터를 제어하는 데 사용 - 한 테이블의 기본 키 값을 가지고 있는 열을 다른 테이블의 열이 참조할 때 두 테이블간의 연결 생성 - 다른 테이블의 기본키를 참조 - 중복된 데이터를 효율적으로 표현하기 위함 - 주문 테이블에서 사용자 아이디 정보를 활용해 사용자 테이블의 정보를 찾을 수 있음 - 외래키는 두 테이블을 연결하는 연결 다리로, 참조하는 테이블에 무결성 (DB의 값이 정확함) 을 높여주는 역할을 함
  • CASCADE - 참고: https://brownbears.tistory.com/182 - 부모 테이블에서 해당 행이 업데이트/삭제 시, 참조 테이블에서도 해당 행이 업데이트/삭제
  • 실습
    create table parent(
        id bigint auto_increment not null, 
        name varchar(10) not null,
        primary key(id)
    );
    
    create table child(
        id bigint auto_increment not null, 
        name varchar(10) not null,
        parent_id bigint not null,
        primary key(id),
        foreign key(parent_id) references PARENT(id)
    );
    
    insert into parent(name) values ('parent1');
    insert into child(name, parent_id) values ('child1', 1);
    delete from parent where id = 1;
    
    >> 오류 발생!!!!!
    Referential integrity constraint violation: 
    "CONSTRAINT_3D: PUBLIC.CHILD FOREIGN KEY(PARENT_ID) REFERENCES PUBLIC.PARENT(ID) ('1')"; SQL statement:
    delete from parent where id = 1 
    
    create table parent(
        id bigint auto_increment not null, 
        name varchar(10) not null,
        primary key(id)
    );
    
    create table child(
        id bigint auto_increment not null, 
        name varchar(10) not null,
        parent_id bigint not null,
        primary key(id),
        foreign key(parent_id) references PARENT(id) on delete cascade
    );
    
    insert into parent(name) values ('parent1');
    insert into child(name, parent_id) values ('child1', 1);
    delete from parent where id = 1;
    
    >> 정상 처리
    데이터 삭제  문제 없이 뚝딱 삭제된다
    

STUB vs MOCK

  • STUB - 호출되면 미리 준비된 답변으로 응답 - 테스트 시에 프로그램 된 것 외에는 응답 X - 협력 객체의 특정 부분 힘들다면 stub을 사용해 수월한 테스트 가능 - 일반적으로 우리가 mock으로 잘못 알고 있음
  • MOCK - 행위 검증 사용을 추구 - 상태 검증: 메서드가 수행된 후 협력 객체의 "상태"를 살펴보고 올바로 작동했는지를 판단 - 행위 검증: 특정 메서드가 호출되었는지의 "행위"를 검사하여 올바로 작동했는지를 판단 - 행위를 기록하는 식의 로직이 들어있음