[JPA] JPA Study

Simple·2022년 3월 6일
0

JPA

목록 보기
1/6
  • 초기 셋팅할 때 팁!

    • Preference에서 annotation processors누르고, Enable annotation processing 체크
    • Build and run using, Run tests using IntelliJ IDEA 로 변경하면 더 빠르게 빌드된다
  • Command + Shift + T → 테스트 클래스 자동 생성

  • 테스트코드에 @Transactional 안붙일경우? 아래와 같은 에러

💡 org.springframework.dao.InvalidDataAccessApiUsageException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call

기본적으로 JPA는 Transactional을 기반으로 작동,

transaction 단위에 따라 1차캐시영역에 있는 객체들이 db에 flush되어 영속화되기 때문이다.

하지만 그러한 영속작업을 하는 persist() 메소드에 객체가 들어갔으나 가능한 transaction이 존재하지 않았기에 저런 에러를 낸것이다. (참고 - https://sas-study.tistory.com/348)

  • 참고: 모든 로그 출력은 가급적 로거를 통해 남겨야 한다.
    • show_sql : 옵션은 System.out 에 하이버네이트 실행 SQL을 남긴다.
    • org.hibernate.SQL : 옵션은 logger를 통해 하이버네이트 실행 SQL을 남긴다.
  • 다대다 관계,외래 키가 있는 곳을 연관관계의 주인으로 정해라.(활용편에서 살짝 이해안갔던 부분이다!, 기본편 들을 때 체킹해)
  • 실무에서는 가급적 Getter는 열어두고, Setter는 꼭 필요한 경우에만 사용하는 것을 추천
    • 실무에서 엔티티의 데이터는 조회할 일이 너무 많으므로, Getter의 경우 모두 열어두는 것이 편리하다. Getter는 아무리 호출해도 호출 하는 것 만으로 어떤 일이 발생하지는 않는다. 하지만 Setter는 문제가 다르다. Setter를 호출하면 데이터가 변한다. Setter를 막 열어두면 가까운 미래에 엔티티에가 도대체 왜 변경되는지 추적하기 점점 힘들어진다. 그래서 엔티티를 변경할 때는 Setter 대신에 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.
  • mappedBy?
@OneToMany(mappedBy = "member")
privateList<Order> orders =newArrayList<>(); //Member 테이블의 orders

orders테이블에서 member를 변경하면 같이 변경되겠다는 뜻
  • self로 양방향 연관관계 넣는법 ( 기본편에서 학습하기 )
@ManyToOne
@JoinColumn(name = "parent_id")
privateCategory parent;

@OneToMany(mappedBy = "parent")
privateList<Category> child =newArrayList<>();
  • foreign key를 걸어야되는지 말아야되는지는 시스템에 따라 다르다.
    • 실시간 트래픽이 엄청많고, 유연함이 중요하면 foreign key없이가 좋을 수도,
    • 돈과 관련된 엄청 중요한 로직이 실행되는거면 있는게 좋다.
  • 참고: 값 타입은 변경 불가능하게 설계해야 한다.
    @Setter 를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들자. JPA 스펙상 엔티
    티나 임베디드 타입( @Embeddable )은 자바 기본 생성자(default constructor)를 public 또는
    protected 로 설정해야 한다. public 으로 두는 것 보다는 protected 로 설정하는 것이 그나마 더 안전
    하다. 
    
    JPA가 이런 제약을 두는 이유는 JPA 구현 라이브러리가 객체를 생성할 때 리플랙션 같은 기술을 사용할 수
    있도록 지원해야 하기 때문이다.
  • 모든 연관관계는 지연로딩으로 설정! 즉시로딩( EAGER )은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다. 특히 JPQL을 실행할 때 N+1
    문제가 자주 발생한다. 실무에서 모든 연관관계는 지연로딩( LAZY )으로 설정해야 한다.
    연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.
    @XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 한
    다. @XTOMany(OneToMany, ManyToMany)는 기본이 LAZY설정이다.
  • 양방향 연관관계
    //==연관관계 메서드==//
        public void setMember(Member member){
            this.member = member;
            member.getOrders().add(this);
        }
    
        public void addOrderItem(OrderItem orderItem){
            orderItems.add(orderItem);
            orderItem.setOrder(this);
        }
    
        public void setDelivery(Delivery delivery){
            this.delivery = delivery;
            delivery.setOrder(this);
        }
  • 생성자 주입방식이 권장됨 > set주입은 중간에 데이터를 메소드를 통해 변경할 수도 있음 > 필드주입은 더 쉽게 변경되겠지?
    • 생성자 1개만 있을 때는 @Autowired가 자동 적용
    • 생성자 주입방식의 장점
      1. 순환참조방식
        1. final 선언 가능(객체 불변성 보장)
      2. 테스트 코드 작성 용이
  • @RequiredArgsConstructor는 final필드만 생성자를 생성한다.
  • Spring Data JPA 가 `private final EntityManager em;` 에 @PersistentContext가 아니라 @Autowired붙여도 주입되게끔 지원한다.
//==비즈니스 로직==// setter로 가지고 변경하는게 아니라 메소드로 이렇게해야 가장 객체지향 적인것
    /*
    stock 증가
     */
    public void addStock(int quantity){
        this.stockQuantity = quantity;
    }

    public void removeStock(int quantity){
        int restStock = this.stockQuantity-quantity;
        if(restStock<0){
            throw new NotEnoughStockException("need more stock");
        }
        this.stockQuantity = restStock;
    }
  • @NoargsConstructor(AccessLevel.PROTECTED)를 왜 사용하는가?

    Entity나 DTO를 사용할때 @NoargsConstructor(AccessLevel.PROTECTED) 어노테이션을 많이 사용한다. 이는 기본 생성자의 접근 제어를 PROTECTED로 설정해놓게 되면 무분별한 객체 생성에 대해 한번 더 체크할 수 있는 수단이 되기 때문입니다.

  • javax.validation 예외 처리 할 수 있는 어노테이션 많음, BindingResult를 통해 에러메세지 뱉게하는 것도 좋음

  • API를 만들 때는 절대로 엔티티를 반환하면 안된다.

    • 요구사항이 정말 단순할 때는 폼 객체( MemberForm ) 없이 엔티티( Member )를 직접 등록과 수정 화면에서 사용해도 된다. 하지만 화면 요구사항이 복잡해지기 시작하면, 엔티티에 화면을 처리하기 위한 기능이 점점 증가한다. 결과적으로 엔티티는 점점 화면에 종속적으로 변하고, 이렇게 화면 기능 때문에 지저분해진 엔티티는 결국 유지보수하기 어려워진다.
    • 실무에서 엔티티는 핵심 비즈니스 로직만 가지고 있고, 화면을 위한 로직은 없어야 한다. 화면이나 API에 맞는 폼 객체나 DTO를 사용하자. 그래서 화면이나 API 요구사항을 이것들로 처리하고, 엔티티는 최대한 순수하게 유지하자.
  • Form 객체 vs DTO 차이점은?(궁금해서 찾아본 결과)

    • 사실 Form이나 DTO나 모두 단순히 계층간에 데이터를 전달할 때 사용한다. 그래서 둘의 역할은 같다.
      다만 이 프로젝트에서 form이라는 것은 제약을 더 두어서 명확하게 컨트롤러 까지만 사용해야 한다는 의미를 강하게 두었다.(form이라는 것 자체가 웹 기술에 종속적인 단어이므로)
      DTO는 이름 그대로 데이터 전송 객체인데, 더 범용적으로 사용되는 단어라 생각하면 될 것같다. DTO는 어디에 정의해두는가에 따라 다르겠지만, 서비스에서도 사용할 수 있고, 리포지토리에서도 사용할 수 있다.
  • 변경감지와 병합

    • 주의: 변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만, 병합을 사용하면 모든 속성이 변경된다. 병합시 값이 없으면 null 로 업데이트 할 위험도 있다. (병합은 모든 필드를 교체한다.)
    • 실무에서 병합은 매우 위험, 변경 감지로 원하는 필드만 수정할 수 있게하는 것이 좋다.
profile
몰입하는 개발자

0개의 댓글