작성
·
123
0
안녕하세요 강의 잘 듣고 있습니다.
다름이 아니라 프로젝트를 업그레이드 하는 과정에서 오류가 생겨 질문 드립니다.
package ShopProject.myShopProject.Domain;
import ShopProject.myShopProject.Domain.Item.Item;
import ShopProject.myShopProject.Domain.Order.Order;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String name;
@Embedded
private Address address;
@OneToMany(mappedBy = "member",cascade = CascadeType.REMOVE)
private List<Order> orders = new ArrayList<>();
private String loginId;
private String password;
@OneToMany(mappedBy = "member")
private List<LikedItem> likedItems = new ArrayList<>();
}
package ShopProject.myShopProject.Domain;
import ShopProject.myShopProject.Domain.Item.Item;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
public class LikedItem {
@Id
@GeneratedValue
@Column(name = "likedItem_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
}
보시는 것처럼 멤버와 좋아요 상품은 Lazy 관계에 있습니다.
OneToMany는 기본이 Lazy이므로 명시하지 않았습니다.
따라서 좋아요 상품은 직접 조회될 때만 DB에서 조회되고, 다른 경우는 프록시 형태로 존재합니다.
//아이템 상세 정보
@GetMapping("item")
public String itemDetail(@RequestParam("itemId") Long itemId,
@SessionAttribute(name = "loginMember") Member loginMember,
Model model) {
log.info("item 호출");
Item item = itemService.findOne(itemId);
log.info(loginMember.getLoginId() + " " + loginMember.getName());
// 좋아요 여부 추가
model.addAttribute("isLiked", memberService.isliked(loginMember, item)); //이 부분입니다!!
model.addAttribute("item", item);
model.addAttribute("member", loginMember);
if (item.getCategory().equals("Book")) {
return "items/item/book";
}
if (item.getCategory().equals("Album")) {
return "items/item/album";
}
if (item.getCategory().equals("Movie")) {
return "items/item/movie";
}
return "items/item";
}
위 컨트롤러를 호출하면, isliked라는 매소드를 실행하게 됩니다. 좋아요 여부를 확인하는 메소드입니다.
//좋아요 아이템이 아니면 false 반환
@Transactional
public Boolean isliked(Member member, Item item) {
log.info("좋아요 확인 메서드 실행");
//
log.info("멤버 정보" + member.getName() + " " + member.getLoginId());
for (LikedItem likeditem: member.getLikedItems()) {
//여기에서 lazyinitializationError 발생
if (likeditem.getItem().getName().equals(item.getName())) {
log.info("좋아요인 경우");
return true;
}
}
log.info("좋아요 아닌 경우");
return false;
}
그런데 여기에서 맴버의 좋아요 목록을 불러오면 lazyinitialization 에러가 발생합니다...
정말 많이 원인도 구글링해보았는데, 영속성 컨텍스트가 종료 된 후 접근할 때 발생하는 것을 알았습니다.
영속성 컨텍스트의 생명주기는 트렌잭션과 거의 동일하다고 알고 있습니다.
만약 위 메소드에 트렌젝션 어노테이션이 없다면 member.getLikedItems() 의 결과를 초기화해주지 않으면 오류가 발생할 것입니다.
따라서 트렌젝션 어노테이션을 넣으므로서, member.getLikedItems()를 가져오고, 직접 DB에서 불러오는 과정을 한 트랜젝션 안에서 수행하도록 했습니다.
그런데 이 메서드에서 계속 LazyinitializatoinError가 발생합니다. 이 부분의 원인을 전혀 못 찾겠습니다..
해결책으로는 DTO를 사용하거나, fetch Join을 사용해서 멤버와 같이 조인하여 한번에 가져옴으로 해결할 수있을 거라 생각합니다.
하지만 해결책은 알았지만, 위 코드의 무슨 오류가 있는지 정말 궁금합니다.
따라서 이 코드의 오류를 알고 싶습니다. 또한 저의 이해에 오류가 있다면 그 부분도 알려주시면 감사하겠습니다.
답변 1
0
안녕하세요. ml505050님, 공식 서포터즈 y2gcoder입니다.
예외 트레이스를 전부 올려주시거나 코드를 올려주시면 더 정확한 답변이 될 것이라 생각하나, 일단 올려주신 코드와 상황만을 봤을 때는 member를 준영속 상태로 서비스단으로 넘겨주기 때문이라고 생각합니다.
서비스단에서 member의 id로 member를 다시 조회해옴으로써 영속화된 member를 찾아오셔서 다시 시도해보시거나,
아니면 세션에 로그인된 멤버 객체를 저장할 때 fetch join이든, 프록시 초기화를 사용하시든 하여 likedItems를 미리 초기화한 후 저장하시거나,
혹은(이건 다른 얘기일 수도 있으나 영한님도 권장하는 방법입니다) DTO를 만들어 필요한 정보만 넣어 세션에 저장 후 불러오시는 것을 추천합니다.
감사합니다.