스프링 부트 & JPA 실전 사용 감 익히기

2023. 2. 3. 16:49JPA

2/3

 

* Entity를 만들때 객체를 타입으로 갖기 위해서는 @Embeddible 해줘야 한다.

 

@Embeddible

@Embeddied

@ManyToOne

@OneToMany

@JoinColumn (fk) 주인

mappedBy( 주인에서 정한 컬럼명)

@Enumrated

 

* 맵핑 연관관계 주인

다대일 관계에서 다에 fk

일대일에서는 어디에 fk를 둬도 상관 X (직접 조회하는 곳에다 둠)

다대다 상황에서 RDB는 객체끼리 연결이 불가능하기 때문에 중간에 징검다리 역할을 해줄 애를 놔둔다. (실무 X)

fk가 있는 쪽 -> 주인

 

*엔티티 설계시 주의점

모든 연관관계는 지연로딩으로 설정!

즉시로딩( EAGER )은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다.

특히 JPQL을 실행할 때 N+1 문제가 자주 발생한다.

실무에서 모든 연관관계는 지연로딩( LAZY )으로 설정해야 한다.

연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.

@XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 한다.

----> (fetch = FetchType.LAZY)

 

* 컬렉션은 필드에서 초기화 하자.

 

옵션

cascade = CascadeType.ALL

예를들어

원래는 

OrderItemA

OrderItemB

OrderItemC

Order

 

이런식으로 별도로 해야 되는걸 Order 로 줄여줌 , 알아서 영속성을 가지게해준다 ? 

 

 

연관관계 편의 메서드 (양방향 연관관계에서 컨트롤 하는 쪽)

//==연관관계 편의 메서드 ==//
public void setMember(Member member){
    this.member = member;
    member.getOrders().add(this);
}

public void addOrderItem(OrderItem orderItem) {
    orderItems.add(orderItem);
    orderItem.setOrder(this);
}

 

 

em.persist( ) ->영속성 컨테스트에 객체를 올려놓는다.

pk가 key가된다.

db에 들어간 시점이 아니어도 값을 채워줌 (Pk)

 

* 데이터를 가지고 있는 쪽이 비즈니스 로직을 가지고 있는게 좋다

 

 

 

@PostMapping("/members/new")
public String create(@Valid MemberForm form, BindingResult result){

    if (result.hasErrors()) {
        return "members/createMemberForm";
    }
 
 
2/9

@NotEmpty ("이름은 필수로 입력해주세요.")

private String name;

 

@Vaild

NotEmpty가 붙어있는 변수에 값이 잘 들어갔는지 확인해준다.

 

@PostMapping("/members/new")
public String create(@Valid MemberForm form, BindingResult result){
if (result.hasErrors()) {
    return "members/createMemberForm";
}

 

@Vaild 에서 오류가 있으면 튕겨버린다 원래.

하지만 BindingResult에 값이 들어왔으면 화면에 어떤 에러가 있는지 보여줄 수 있다.

==> front에서 에러를 받아서 화면을 처리할 수 있다.

 

* @Setter 제거하고 static으로 createBook을 만드는게 더 좋은 설계이다.

@PostMapping("/items/new")
public String create(BookForm form) {
    Book book = new Book();
    book.setStockQuantity(form.getStockQuantity());
    book.setName(form.getName());
    book.setPrice(form.getPrice());
    book.setIsbn(form.getIsbn());
    book.setAuthor(form.getAuthor());
    itemService.saveItem(book);
    return "redirect:/";
}

 

* 변경감지 = dirty checking

ex)

 

1. Book book = EntityManager.find(pk)를 통해 저장되어 있는 Entity를 반환

 

2. book.setName("어쩌고저쩌고")

 

--> JPA가 저장되어 있는 데이터의 값이 바뀐걸 보고 Transaction이 커밋되는 시점에 자동으로 수정해준다.

 

**** 준영속 엔티티 -> 영속성 컨텍스트가 더이상 관리하지 않는 엔티티****

준영속 엔티티는 JPA 영속성 컨텍스트가 관리하지 않기 때문에 값이 바뀌어도 자동으로 업데이트되지 않는다.

 

따라서

 

- 변경 감지 기능 사용

 

- 병합 (준영속 -> 영속) == EntityManager.Merge

@Transactional
public Item updateItem(Long itemId, Book param) {
    Item findItem = itemService.findOne(itemId);
    findItem.setPrice(param.getPrice());
    findItem.setName(param.getName());
    findItem.setPrice(param.getPrice());
    return findItem;
}

위 코드 동작이 merge 메서드의 기능과 동일

 

Item item = em.merge(itemparam)

item != itemparam

파라미터로 넘어온 Item객체와 merge 반환값 Item객체는 다른 객체이다.

 

///주의점

merge는 선택 속성만 변경하는 것이 아니라 전부 다 변경이 된다.

즉, 모든 값이 들어있는 객체를 파라미터로 넘기지 않고 빈 값이 있으면 null로 업데이트 된다.

 

컨트롤러에서 엔티티 생성 X

트랜잭션이 있는 서비스 계층에 식별자(PK)와 변경할 데이터를 명확히 전달하자.

트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 데이터를 변경하자