Spring Data JPA | InvalidDataAccessApiUsageException: "detached entity passed to
☑️Sping Data JPA 학습 중에 다음과 같은 에러를 마주쳤다.

InvalidDataAccessApiUsageException: "detached entity passed to persist"
☑️ Entity 연관 관계


orders와 coffee는 N:M 관계이다.
두 Entity의 다대다 매핑을 위해서, 참조 객체(order_coffee: 매핑 테이블)를 사이에 넣어 1:N / N:1 관계로 만들었다.
Order Entity를 보면 OrderCoffees 필드에 (cascade = CascadeType.PERSIST)라는 속성이 부여되어있다.
=> Order Entity에 대해 persist를 수행할 때 OrderCoffee Entity도 persist가 수행된다.
☑️ Exception 원인


엉뚱한 필드에 id를 부여해주고 있었다.
OrderCoffee.orderCoffeeId는 OrderCoffee Entity의 Id 필드로, Id 생성 전략을 IDENTITY로 설정해두었는데
거기에 직접 값을 부여해버린 것이다.
(원래 의도는 오른쪽 사진처럼 Coffee.coffeeId를 세팅하는 것이었다.)
* 디버깅

OrderCoffees > Coffee > coffeeId가 설정되어있어야 하는데
null값이 들어갔고,
자동생성 되어야 할 OrderCoffees > orderCoffeeId가
1이라는 값을 이미 가져버렸다.
Entity에 @GeneratedValue를 사용해서 자동생성하겠다고 선언을 했는데 Id를 직접 부여해버리면,
JPA는 아직 영속화되지 않은 New가 아닌
영속화 되었다가 현재는 영속성 컨텍스트로 관리되지 않는 Detached로 인식해버린다.
☑️ 근본적 원인

detach된 객체를 다시 영속 상태로 만들기 위해서는 persist()가 아닌 merge()를 사용한다.
하지만 Entity 작성 시 OrderCoffee에 대한 cascade 속성을 PERSIST로 설정해놓았기 때문에,
Order가 persist 되면서 OrderCoffee도 persist 되는데,
detached 객체로 인식된 OrderCoffee는 persist할 수 없어서 에러를 뱉은 것으로 보인다!
결론 1. IDENTITY 전략으로 Id를 생성하면서 이미 Id를 가진 객체는 New(비영속 객체)가 아닌 Detached(영속화 된 적 있는 객체)로 여겨진다.
결론 2. ==> 그렇기 때문에 persist가 불가능한 상태인 것
결론 3. ==> 그런데 Order가 persist 되면서 OrderCoffee까지 persist하려다가 에러 발생
☑️ 참고해본 JPA 로직

이건 JpaRepository의 구현체인 SimpleJpaRepository의 save() 로직을 가져와본 것이다.
New인지 확인을 해서 true면 persist를, 아니라면 merge를 진행하는데
Order가 New 객체라 OrderCoffee(Detached)까지 persist 진행,, => persist 실패
단순히 매핑 과정에서 값을 잘못 설정해주었다고만 생각했는데
왜 이런 에러가 나왔는지에 대해 생각하면서 JPA에 대해 한층 깊은 곳까지 공부할 수 있었다.
생각할 기회를 주시고 함께 토론해주신 엔지니어 정식님 감사합니다...🥹
* 일부 주관적인 내용이라 틀린 곳이 있을 수도 있습니다!