새로운 강의는 늘 설레는 거 같다. 파이팅!!!!
복습하면서 정리
- 1.
@Embeddable
-valueClass 안에
@Enumerated(EnumType.STRING)
- valueClass 쓰는 곳에
private DeliveryStatus status; // READY, COMP
한쪽만 써도 된다.- 2.
Enum타입을 사용할 때 -@Enumerated(EnumType.STRING)
Ordinal 도 있는데, 수정될 때 장애가 난다.- 3.
N:M은 실무에서는 쓰면 안된다!@ManyToMany @JoinTable(name="category_item", //중간 테이블 매핑 joinColumns = @JoinColumn(name="category_id"), //중간 테이블에 있는 내 아이디 inverseJoinColumns = @JoinColumn(name="item_id")) //중간 테이블에 있는 상대방 아이디 private List<Item>items = new ArrayList<>();
Category.java
@ManyToMany(mappedBy = "items") private List<Catrgory>catrgories = new ArrayList<>();
Item.java
- 4.
자기참조@ManyToOne @JoinColumn(name="parent_id") private Catrgory parent; @OneToMany(mappedBy = "parent") private List<Catrgory>child = new ArrayList<>();
@Setter 를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들자.
JPA 스펙상 엔티티나 임베디드 타입( @Embeddable )은 자바 기본 생성자(default constructor)를 public 또는 protected 로 설정해야 한다.
public 으로 두는 것 보다는 protected 로 설정하는 것이 그나마 더 안전하다.
JPA가 이런 제약을 두는 이유는 JPA 구현 라이브러리가 객체를 생성할 때 리플랙션 같은 기술을 사용할 수 있도록 지원해야 하기 때문이다.
엔티티에는 가급적 Setter를 사용하지 말자
변경 포인트가 너무 많아서, 유지보수가 어렵다.
나중에 리펙토링으로 Setter를 제거하도록 하자.
모든 연관관계는 지연로딩으로 설정하자
즉시로딩( EAGER )은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다.
특히 JPQL을 실행할 때 N+1 문제가 자주 발생한다.
실무에서 모든 연관관계는 지연로딩( LAZY )으로 설정해야 한다.
연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.
@XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 한다.
컬렉션은 필드에서 초기화 하자.
컬렉션은 필드에서 바로 초기화 하는 것이 안전하다.
ex) private List<Order> orders = new ArrayList<>();
하이버네이트는 엔티티를 영속화 할 때, 컬랙션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다.
만약 getOrders() 처럼 임의의 메서드에서 컬력션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다.
따라서 필드레벨에서 생성하는 것이 가장 안전하고, 코드도 간결하다
@Transactional(readOnly = true)
- 읽기 단계에서 사용하면 최적화가 가능하다.TestCode 작성 중
@Runwith 과 @SpringBootTest가 있어야 테스트를 할 수 있다. JUnit4 버전은 그런듯??? 5를 사용했을 때에는 @SpringBootTest만 사용했었다.
또한 기본적으로 @Transactional 하면 Rollback이라 insert문이 나가지 않는다.
쿼리문을 보고싶다면 @Rollback(false) 해주기!!
@Test(expected = IllegalArgumentException.class)
이거 꿀팁인 거 같다. Illegal~~ 에러가 터지면 담기는 것. 꿀인 거 같다.
값 변경
public void addStock(int quantity){
this.stockQuantity += quantity;
}
이런식으로!!
@NoArgsConstructor(access = AccessLevel.PROTECTED)
protected 생성자 역할을 한다.
@PostMapping("/members/new")
public String create(@Valid MemberForm form , BindingResult result){
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
if(result.hasErrors()){
return "members/createMemberForm";
}
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.join(member);
return "redirect:/";
}
키워드 - @Valid, @NotEmpty, BindingResult, result.hasErrors()
참고 - https://jj-yi.tistory.com/23
영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다.
준영속 엔티티를 수정하는 2가지 방법
- 변경 감지 기능 사용
- 병합(merge) 사용
변경 감지
- 영속성 컨텍스트에서 엔티티를 다시 조회해서 영속성 컨텍스트에 올려놓은 후 데이터를 수정하는 것이다.
병합(merge)
- 준영속 엔티티의 식별자 값으로 1차 캐시에 먼저 조회를 한다.
없다면 DB에서 값을 가져와서 조회한 영속 엔티티에 DB에서 가져온 값을 채워 넣는다. 아래의 그림과 코드를 참고하면 이해하기가 쉽다.
@Transactional
public Item updateItem(Long itemId, Book param){
Item findItem = itemRepository.findOne(itemId);
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
return findItem;
}
BUT!!
merge
는 모든 속성이 변경된다.
즉 여러개의 필드 중 하나의 필드만 빼고 바꾼다고 가정하자.
그럼 그 하나는 null이 되고 db에도 null이 저장된다. 위험위험몬~
setter를 지양해야 하는 이유!
역추적이 불가능하다.
의미있는 메서드를 만들어 두면 역추적이 가능하다!!!
다음 강의는 jpa2가 아닌 spring data jpa를 수강할 예정이다!