인프런 커뮤니티 질문&답변

인프러너님의 프로필 이미지
인프러너

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

주문 기능 테스트

원하는대로 DB에 저장이 안됩니다.ㅠㅠ 어느 부분을 잘못한 것일까요?

작성

·

939

0

@Entity
@Getter
@Setter
@ToString
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "item_id")
private Long id; //아이템 고유번호

@Column(name = "sentence_info")
private String sentenceInfo; //문장 정보
    @ManyToOne(fetch = LAZY)
@JoinColumn(name = "member_id")
private Member member;
 public static Item createItem(Item itemList, Member member) {
Item item = new Item();
item.setMember(member);
item.setSentenceInfo(itemList.sentenceInfo);
return item;
}

}
@Entity
@Getter
@Setter
@ToString
public class Member {
@Id
// @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "member_id")
private String memberId; //회원 아이디

private String name; //회원이름

private String email; //이메일

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = LAZY)
private List<Item> itemList = new ArrayList<>();

//== 연관관계 편의 메서드==//
public void setItems(Item item) {
itemList.add(item);
item.setMember(this);
}

//==회원가입 ==//
public static Member createMember(MemberForm memberForm) {
Member member = new Member();
String password = passwordEncoder.encode(memberForm.getPassword());
member.setMemberId(memberForm.getMemberId());
member.setName(memberForm.getName());
member.setEmail(memberForm.getEmail());
return member;
}
}


@Getter
@Setter
@ToString
public class MemberForm {

@NotEmpty(message = "아이디는 필수 입력 값입니다.")
private String memberId;

@NotEmpty(message = "이름은 필수 값 입니다.")
private String name;

@NotEmpty(message = "이메일은 필수 입력 값입니다.")
@Email(message = "이메일 형식에 맞지 않습니다.")
private String email;

}

위와같이 설정을 하고

@Controller
@RequiredArgsConstructor
public class MemberController {

private final MemberService memberService;

/**
* 회원가입 폼 호출
*
* @param model
* @return
*/
@GetMapping("/members/new")
public String createForm(Model model) {
model.addAttribute("memberForm", new MemberForm());
return "members/createMemberForm";
}

/**
* 회원가입
*
* @param form
* @param result
* @return
*/
@PostMapping("/members/new")
public String create(@Valid MemberForm form, BindingResult result, Item item) {

if (result.hasErrors()) {
return "members/createMemberForm";
}
memberService.save(form, item);
return "redirect:/";
}

}
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {

private final MemberRepository memberRepository;
private final ItemRepository itemRepository;

/**
* 회원가입하기 테스트
*/
@Transactional
public void save(MemberForm form, Item item) {

//회원 저장
Member savedMember = Member.createMember(form);
//아이템 저장
Item savedItem = Item.createItem(item, savedMember);

memberRepository.save(savedMember);

System.out.println("SavedFinished");

}

/**
* 회원가입
* @param member
* @return
*/
@Transactional
public String join(Member member) {
validateDuplicateMember(member);//중복회원 검증
memberRepository.save(member);
return member.getMemberId();
}

private void validateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getMemberId());
//회원수를 카운트 해서 0보다 클 경우에 문제가 있다... 하면 최적화가 될 수 있다.
if (!findMembers.isEmpty()) {
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}

}
@Repository
@RequiredArgsConstructor
public class MemberRepository {

private final EntityManager em;

/**
* 회원 저장
* @param member
*/
public void save(Member member) {
em.persist(member);
}
}
@Repository
@RequiredArgsConstructor
public class ItemRepository {

private final EntityManager em;

/**
* 저장
* @param item
*/
public void save(Item item) {
em.persist(item);
}
}

위와같이 설정을 했는데 생각했던대로 동작이 안되서 도저히 이해가 안되서 문의드립니다.

원하는 결과는

1. 회원가입을 한다.
2. 회원가입을 할 때 Item 테이블에 있는 항목도 같이 입력을 한다.
3. 회원가입 완료 후 회원가입 테이블에 등록한 회원이 저장되고, 관련 Item테이블에도 FK가 설정이 되어있으니 정보가 저장된다.

Item테이블이 Many라 (회원 한 명당 Item을 여러개 등록할 수 있어서) ManyToOne을 Item에 넣었고, JoinColumn도 Item에 설정을 하고 PK를 member_id로 설정했습니다.

Member는 참조만 되는 테이블이라 OneToMany로 설정하고 mappedBy를 member로 설정했습니다.
그리고 연관관계 편의 메서드를 아이템을 저장할 때 회원정보도 저장하게 했습니다.

회원가입, 아이템 엔티티에 create~ 메소드를 만들어서 service단에서 저장을 할 수 있도록 설정을 했습니다.

제가 어느 부분을 잘못 설정을 했는지 MemberService에서 memberRepository.save~를 호출해서 저장을 하면 생성되는 쿼리에 insert문이 두개가 생성이 됩니다.

어?? 이상하다 해서 MemberService에서 createMember에서 저장한것은 반영이 안된다고 알고 있는데..이상하다고 생각을 해서 혹시나 해서 memberRepository.save~~를 주석처리하면 당연히 저장이 안되고 insert쿼리문도 출력이 안됩니다.(repository 안에 em.persist(~)를 save()에서 호출하기 때문에 save를 반드시 호출해야 저장이 되기때문에 안되는게 당연하겠죠.)

궁금한점이..
위와같이 소스를 작성할 때 어느부분이 잘못됐는지 DB에 원하는 정보들이 저장이 안되는것과

createMember가 왜 insert쿼리문이 생성이 되는지.. 아니면 제가 잘못 알고있는게 있는지 궁금합니다.(insert쿼리가 두개가 만들어지는게 이해가 안됩니다.)

답변 1

0

안녕하세요. 아버지님, 공식 서포터즈 David입니다.
.
log에 찍힌 insert 쿼리 캡쳐해서 올려주시겠어요?
.
감사합니다.

인프러너님의 프로필 이미지
인프러너
질문자

2021-12-09 16:30:49.654 DEBUG 11026 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
    insert 
    into
        member
        (create_date, email, name, password, phone, sns_key, sns_type, update_date, member_id) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        member
        (create_date, email, name, password, phone, sns_key, sns_type, update_date, member_id) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?)

 

이렇게 찍힙니다. 다른 필드값들은 사족같은 필드들이라 위의 소스에서는 제거하고 올렸었습니다.

확인이 늦어 죄송합니다.

혹시 insert문 외에도 쿼리들이 중복되어 로그에 찍히는지 확인해주실 수 있으실까요?

강의 자료와 같이 show_sql을 주석처리하시거나 false로 값을 주셨을까요?

해당 옵션을 사용중이시라면 올려주신 로그처럼 중복으로 찍혀 나옵니다.

인프러너님의 프로필 이미지
인프러너
질문자

네 설정부분은 똑같이 되어있어요.
그리고 쿼리문이 두개가 중복되서 나오는 부분은 해결을 했는데 위의 소수중 회원가입 테스트를 진행을 할 때 정보를 입력하면 회원가입은 되는데 Item쪽은 저장이 안됩니다.

그래서 혹시 제가 엔티티 연관관계를 잘못 설정을 했는지, 아니면 다른 부분에서 잘못된 부분이 있는지를 아직 찾지 못하고 있습니다.

해당 코드에서 Member쪽에 item을 넣어주는 코드가 추가되어야 하지 않을까요?

member.setItem()이 필요해보입니다.

 

혹시 member.setItem()을 추가했는데도 안 되신다면 

전체 프로젝트를 압축해서 구글 드라이브로 공유해서 링크를 남겨주세요.
구글 드라이브 업로드 방법은 다음을 참고해주세요.

https://bit.ly/3fX6ygx

주의: 업로드시 권한 문제 꼭 확인해주세요

인프러너님의 프로필 이미지
인프러너

작성한 질문수

질문하기