프로젝트

[트러블 슈팅] 일대일 관계에서 save 순서 및 외래키 관리 org.hibernate.TransientObjectException: persistent instance references an unsaved transient instance of

매일_공부 2025. 1. 2. 16:23
반응형

쇼핑몰 프로젝트를 수행하다, 회원가입 하는 동안, 회원을 생성하고, 장바구니를 생성하고 매핑하는 과정에서 

오류가 발생했다.

//멤버 회원 가입
public Member joinMember(MemberRequestDto.JoinDto request) {
    //로그인 아이디 중복 여부
    if (isLoginIdDuplicated(request.getLoginId())) {
        throw new MemberHandler(ErrorStatus._DUPLICATED_LOGIN_ID);
    }
    //멤버 생성
    Member member = MemberConverter.toMember(request);

    //장바구니 생성
    Cart cart = CartConverter.toCart(member);

    //연관관계 매핑
    member.setCart(cart);

    memberRepository.save(member);
    cartRepository.save(cart);
    // 멤버 저장 후 반환
    return member;
}

 

처음에는 cart를 먼저 save 하고, member를 save하려고 했는데, 오류가 발생했다.

 

현재 연관관계는 이러하다.

package GoodPang.goodPang.domain.member;

import GoodPang.goodPang.domain.base.BaseEntity;
import GoodPang.goodPang.domain.cart.Cart;
import GoodPang.goodPang.domain.images.MemberImage;
import GoodPang.goodPang.domain.item.LikedItem;
import GoodPang.goodPang.domain.order.Orders;
import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter //@Getter 자동생성
@Builder // 빌더 패턴으로 자동으로 만들어주는 에노테이션
@NoArgsConstructor(access = AccessLevel.PROTECTED) //기본 생성자 자종 생성
@AllArgsConstructor // 모든 필드 값을 파라미터로 받는 새성자 생성
public class Member extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Embedded
    private Address address;


    @OneToOne
    @JoinColumn(name = "member_image_id")
    private MemberImage memberImage;


    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "cart_id")
    private Cart cart;

    private String loginId;
    private String password;

    @OneToMany(mappedBy = "member")
    private List<LikedItem> likedItems = new ArrayList<>();
    @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE)
    private List<Orders> orders = new ArrayList<>();

    public void setCart(Cart cart) {
        this.cart = cart;
        cart.setMember(this);
    }

    public Member editMember(String name, Address address) {
        this.name = name;
        this.address = address;
        return this;
    }
}

package GoodPang.goodPang.domain.cart;

import GoodPang.goodPang.domain.base.BaseEntity;
import GoodPang.goodPang.domain.member.Member;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
public class Cart extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @OneToOne(mappedBy = "cart", fetch = FetchType.LAZY)
    private Member member;

    @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, orphanRemoval = true)
    public List<CartItem> carts = new ArrayList<>();

    public void setMember(Member member) {
        this.member = member;
    }
}

 

위처럼 member에서 @JoinColumn으로 cart_id를 조인키로 들고 있다.

서비스 단에서 연관관계 매핑으로 member.setCart(cart)를 할때, 트렌젝션 안에서 

Cart가 저장되지 않은 상황(영속화 x)이기 때문에 오류가 발생한다.

 

따라서 이후, memberRepository.save(member)를 할때, member의 cart_id가 없게 되어 버린다.

 

따라서 Cart를 먼저 저장을하고, member를 저장하는 순서로 해야 한다.

 

++++

 

@Transaction을 붙여서, 한 트렌젝션에 수행되도록 하면, 순서는 중요하지 않아진다.

 

반응형