본문 바로가기

개발 일지/TIL

[ #29 ] TIL

✏️ 0527      


Spring 숙련 4주차 강의

[ 특강 ] 스프링 필터


Spring 숙련 4주차

 

지연 로딩

 

 

음식 테이블과 고객 테이블이 N : 1 양방향 관계일 때를 가정

@Test
@DisplayName("아보카도 피자 조회")
void test1() {
    Food food = foodRepository.findById(2L).orElseThrow(NullPointerException::new);

    System.out.println("food.getName() = " + food.getName());
    System.out.println("food.getPrice() = " + food.getPrice());

    System.out.println("아보카도 피자를 주문한 회원 정보 조회");
    System.out.println("food.getUser().getName() = " + food.getUser().getName());
}

 

 

 

자동으로 legt join 하면서 user 정보를 가져오고 있다

 

Food의 User가 @ManyToOne EAGER 방식이 적용되어 있기 때문에
연관되어 있는 그 Entity의 User 정보를 Join 해서 한번에 가져오게 되는것

 

 

JPA 에서는 연관관계가 설정된 Entity의 정보를 바로 가져올지 필요할 때 가져올지 정할 수 있다

즉, 가져오는 방법을 정할 수 있다


JPA 에서는 가져오는 방법을 FetchType 이라고 부른다

FetchType 에는 두가지 종류가 있는데
LAZY(지연로딩) 와 EAGER(즉시로딩) 있다

 

@OneToMany(mappedBy = "user")
private List<Food> foodList = new ArrayList<>();


@OneToMany 는 defaulte 가 LAZY 이다
필요한 시점에서 정보를 가져온다

 

@ManyToOne
@JoinColumn(name = "user_id")
private User user;


@ManyToOne 은 defaulte 가 EAGER 이다
조회할 때 연관된 모든 Entity 의 정보를 즉시 가져온다


다른 연관관계에도 default가 있다
구분하는 방법은 애노테이션 이름 뒤에 Many가 붙어있으면 설정된 해당 필드가 자바 컬렉션 타입

즉, 해당 Entity의 정보가 여러개 들어있을 수 있다는 것을 의미
따라서 효율적으로 조회하기 위해서 지연 로딩(LAZY)가 default로 설정되어 있다

반대로 One이 뒤에 붙어있는 경우 필드의 타입이 그냥 일반 객체 타입, 즉 정보가 하나만 들어온다

그렇기에 곧바로 정보를 가져와도 무리가 없기 때문에 즉시 정보(EAGER)가 default로 설정되어 있다

 

public interface UserRepository extends JpaRepository<User, Long> {
    User findByName(String robbie);
}


JpaRepository 에서 User 로 바로 반환하고 싶을 때
Optional 를 사용하지 말고 User 사용하면 된다

 

@ManyToOne(fetch=FetchType.LAZY)


수동으로 fetchType 을 바꿔줄 수 있다

 

=> 바꾼 상태로 실행하게 되면 오류가 잔뜩 일어남


지연 로딩도 영속성 컨텍스트 기능 중 하나
지연 로딩된 Entity 의 정보를 조회하려고 할 때는 반드시 영속성 컨텍스트가 존재해야 한다
=> Srping Container 환경에서는 트랜잭션이 적용되어 있어야 한다라는 의미와 동일


지연로딩된 Entity를 조회할 때는 트랜잭션이 필수로 적용되어 있어야 한다

 

 

영속성 전이

 

// 매번 저장 하는거 너무 귀찮다!!
// 이것도 코드 중복 아니냐!!
// 자동으로 영속화 해달라!!
// 하는 분들을 위한 JPA 에서 아주 간편하게 처리할 수 있도록
// 영속성 전이에 persist 옵션 제공
userRepository.save(user);
foodRepository.save(food);
foodRepository.save(food2);

 

Cascade의 persist
cascade 는 sql 배울 때 한번 본적이 있다
delete 할 때 연관된건 지울 수 없는 문제가 발생하는데
그래서 설정했던 foreign key 를 drop 하고 새로 Alter 하면서 FK 를 설정할떄
뒤에 on delete Cascade 옵션을 추가했었다
옵션 추가하면 연관된 데이터도 삭제가 가능한 것을 해본 적 있다

이 on delete Cascade 과 같은 기능을 JPA 에서 제공하는데 
이걸 바로 영속성 전이라고 한다

 

 

// cascade = CascadeType.PERSIST 영속성 전이 추가
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST)

 

그럼 영속성 전이란?
영속 상태의 Entity 에서 수행되는 작업들이 연관된 Entity 까지 전파되는 상황을 뜻한다
그래서 영속성 전이를 적용해서 해당 Entity 를 저장할 때 이 해당 Entity와 연관된 Entity 까지 자동으로 저장하기 위해서 
자동으로 저장하려고 하는 연관된 Entity 에 추가한 연관관계 애너테이션에 Cascade persist option을 설정하면 된다

 

 

// cascade = {CascadeType.PERSIST, CascadeType.REMOVE} 중복 설정 가능=> 중괄호 사용, 한번에 삭제 추가
@OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})

 

저장 뿐만 아니라 삭제 기능도 가능

 

 

 

고아 Entity 삭제

 

CASCADE의 REMOVE 옵션을 적용하면 해당 Entity 객체를 삭제 했을 때

연관된 Entity 객체들을 자동으로 삭제할 수 있었다
하지만 REMOVE 옵션 같은 경우 연관된 Entity와 관계를 제거했다고 해서 자동으로 해당 Entity가 삭제 되지는 않는다

 

Entity 객체와 연관관계를 제거했지만 Delete SQL이 수행되지 않는 것을 확인할 수 있다

 

JPA 는 제공한다
간편하게 처리할 수 있는 방법으로
⭐orphanRemoval 옵션 제에공⭐


orphanRemoval 옵션도 REMOVE 옵션과 마찬가지로 해당 Entity 즉,

Robbie Entity 객체를 삭제하면 연관된 음식 Entity들이 자동으로 삭제
연돤된 Entity 를 제거했을 때 delete 해주는 기능뿐만 아니라 CascadeType.REMOVE의 기능도 가지고있다


⚠️주의 ⚠️
orphanRemoval이나 REMOVE 옵션을 사용할 때 삭제하려고 하는

연관된 Entity를 다른 곳에서 참조하고 있는지 아닌지를 꼭 확인해야한다


A와 B에 참조되고 있던 C를 B를 삭제하면서 같이 삭제하게 되면

A는 참조하고 있던 C가 사라졌기 때문에 문제가 발생할 수 있다

따라서 orphanRemoval 같은 경우 @ManyToOne 같은 애너테이션에서는 사용할 수 없다


ManyToOne이 설정된 Entity는 해당 Entity 객체를 참조하는 다른 Entity 객체들이 있을 수 있기 때문에

속성으로 orphanRemoval를 가지고 있지 않는다

orphanRemoval 과  CascadeType.REMOVE 는 
이게 다른데 영향을 주는지 안주는지 한번 더 생각하고
고민을 많이 한 다음에 설정하는게 좋다

'개발 일지 > TIL' 카테고리의 다른 글

[ #31 ] TIL  (0) 2024.05.29
[ #30 ] TIL  (0) 2024.05.28
[ #28 ] TIL  (0) 2024.05.24
[ #27 ] TIL  (2) 2024.05.23
[ #26 ] TIL  (0) 2024.05.22