1. 고객 - 장바구니 매핑하기 (일대일)
- Member
<hide/>
@OneToOne
@JoinColumn(name = "CART_ID") // 카트의 변수명과 달라도되나???
private Cart cart;
- Cart 엔티티 추가
<hide/>
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Cart {
@Id @GeneratedValue()
private Long id;
private String name;
@OneToOne(mappedBy = "cart")
@JoinColumn
private Member member; // mappedBy를 조인컬럼으로 바꾸면? mamberId 컬럼 생긴다.
}
- MemberServiceImpl - 이메일 인증 후
- 새로운 카트를 만들어서 추가해준다.
<hide/>
Cart cart = new Cart();
member.setCart(cart);
cartRepository.save(cart);
memberRepository.save(member);
Note) 실행 결과
- 회원 가입하면 다음과 같이 장바구니 번호가 생성된다.
2. 고객 - 쿠폰 매핑하기 (일대일)
- coupon 엔티티
<hide/>
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Coupon {
@Id @GeneratedValue
private Long id;
@OneToOne(mappedBy = "coupon")
@JoinColumn
private Member member;
// @Enumerated(EnumType.STRING)
// String으로 하면 SQL 오류가 난다. ==> 해결하기
private CouponType couponType; // 쿠폰 종류
private LocalDateTime couponRegDt; // 쿠폰 등록일
private LocalDateTime couponExpirationDt; // 쿠폰 만료일
}
- ServiceImpl
<hide/>
@Override
public boolean emailAuth(String uuid) { // 이메일 인증
Optional<Member> optionalMember = memberRepository.findByEmailAuthKey(uuid); // 있으면 Optional<Member> 가 리턴된다.
if(!optionalMember.isPresent()){
return false;
}
Member member = optionalMember.get();
// 이미 활성화됐기 때문에 또 활성화할 필요없다.
if(member.isEmailAuthYn()){
return false;
}
// 장바구니 추가
Cart cart = new Cart();
LocalDateTime now = LocalDateTime.now();
// 쿠폰 추가
Coupon coupon = Coupon.builder()
.couponType(CouponType.COUPON_A)
.couponRegDt(now)
.couponExpirationDt(now.plusMonths(3))
// .member(member) => TransientPropertyValueException 발생
.build();
// member에만 set()하고 반대편은 저장 x
member.setCart(cart);
member.setCoupon(coupon);
member.setUserStatus(MemberCode.MEMBER_STATUS_ING);
member.setEmailAuthYn(true);
member.setEmailAuthDt(LocalDateTime.now());
// couponRepository.save(coupon);
// cartRepository.save(cart);
memberRepository.save(member); // 연관관계의 주인만 저장해도 된다.
return true;
}
3. 회원 가입 후 - 이메일 인증
- ServiceImpl
- 이메일 인증
- 가입시 생성한 uuid로 회원을 찾는다.
<hide/>
@Override
public boolean emailAuth(String uuid) { // 이메일 인증
Optional<Member> optionalMember = memberRepository.findByEmailAuthKey(uuid); // 있으면 Optional<Member> 가 리턴된다.
if(!optionalMember.isPresent()){
return false;
}
Member member = optionalMember.get();
// 이미 활성화됐기 때문에 또 활성화할 필요없다.
if(member.isEmailAuthYn()){
return false;
}
member.setUserStatus(MemberCode.MEMBER_STATUS_ING);
member.setEmailAuthYn(true);
member.setEmailAuthDt(LocalDateTime.now());
memberRepository.save(member);
return true;
}
- 컨트롤러
- 회원 가입시 uuid를 생성해서 member의 이메일 인증키에 넣었다.
- 그러고나서 회원가입 이메일을 보낼 때 링크에 아래와 같은 링크를 넣었다,
- localhost:8080/member/email-auth?id= {uuid}
- 따라서, GetMapping이 실행되면 위 URL의 id인 uuid가 컨트롤러로 넘어온다.
- 인증을 받으면 result에 true를 저장해서 html로 result 값을 보낸다.
<hide/>
@GetMapping("/member/email-auth")
public String emailAuth(Model model, HttpServletRequest request){
String uuid = request.getParameter("id"); // 주소창에 보이는 변수
System.out.println(uuid);
boolean result = memberService.emailAuth(uuid);
model.addAttribute("result", result); // 현재 결과 result를 html로 result라는 변수로 넘긴다.
return "member/email-auth";
}
- html
<hide/>
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>회원 활성화</title>
</head>
<body>
<h1>회원 활성화</h1>
<!-- <div th:if="${result}">-->
<div th:if="${result}">
<p>회원님의 계정이 활성화 되었습니다.</p>
</div>
<div th:if="${result eq false}">
<p>회원님의 계정 활성화에 실패했습니다.</p>
</div>
</body>
</html>
Note) 실행 결과
- localhost:8080/member/email-auth?id = {}
- {인증을 위한 uuid}
오류) TransientPropertyValueException
object references an unsaved transient instance - save the transient instance before flushing : com.example.mall.entity.Coupon.member -> com.example.mall.entity.Member
- 오류: TransientPropertyValueException
- 원인: 저장되지 않은 객체를 참고했다.
- 해결: 연관 관계 매핑한 부분에 대해서 member 쪽에만 setCart(), setCoupon()을 해주고 반대는 설정하지 말야야 한다.
- 멤버 - 장바구니
- 멤버 - 쿠폰
<hide/>
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.example.mall.entity.Coupon.member -> com.example.mall.entity.Member
at org.hibernate.engine.spi.CascadingActions$8.noCascade(CascadingActions.java:379) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:169) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:159) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:149) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:82) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1407) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:489) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3290) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2425) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:449) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:40) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101) ~[hibernate-core-5.6.10.Final.jar:5.6.10.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:562) ~[spring-orm-5.3.22.jar:5.3.22]
오류)
- couponType을 넣는데 @EnumType을 String으로 정해도 데이터베이스에 쿠폰 타입이 들어가지 않는다.
- IllegalArgumentException 아니면 SQL 오류가 난다.
- 이메일 인증 하는 마지막 부분에 연관관계의 주인인 member만 repository에 저장하면 cart, coupon에 자동으로 id가 들어간다.
- string이 들어가도록 구현해야한다.
- EnumType.String을 넣었을 때 에러 화면 - 회원 가입 까지는 되는데 이메일의 링크를 누르면 다음과 같이 오류난다.
- 에러 코드
java.sql.SQLException: (conn=475) Incorrect integer value: 'COUPON_C' for column 'coupon_type' at row 1 - 원인: 데이터베이스에 타입이 int로 설정되어 있음
- 해결: 다음과 같이 varchar() 형태로 바꾼다.
'Spring Projcect > [갠플] Online-mall' 카테고리의 다른 글
[7일차] 관리자 API - 상품 조회 (0) | 2022.09.29 |
---|---|
[6일차] 관리자 로그인, 상품 등록 (0) | 2022.09.28 |
[4일차] 오류 해결, 로그인 히스토리 (0) | 2022.09.24 |
[3주차] 회원 가입, 로그인 (0) | 2022.09.23 |
[2주차] DB 연결 & 회원가입 구현 (0) | 2022.09.22 |