
JDBC와 MySQL을 사용하기 위한 설정을 해준다.
당연히 MySQL이 깔려있어야하고 스키마 역시 생성되어있다.

JPA와 MySQL 사용하기 위함
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'mysql:mysql-connector-java:8.0.26'// JDBC 드라이버 (사용하는 데이터베이스 종류에 맞게 선택)

EntityManager em;: 엔티티 매니저를 주입받기 위한 필드이다.
public JpaMemberRepository(EntityManager em): 생성자(Constructor)에서 EntityManager를 주입받아 필드에 할당하는 역할을 한다.
Member save(Member member): 회원 정보를 데이터베이스에 저장하는 메서드이다. em.persist(member)를 호출하여 엔티티를 영속화한다.
Optional findByLoginId(String loginId): 로그인 아이디를 기반으로 회원을 조회하는 메서드이다. JPQL(Java Persistence Query Language)을 사용하여 쿼리를 작성하고, 파라미터로 넘어온 loginId를 바인딩하여 조회한다. 결과를 리스트로 가져온 뒤 첫 번째 요소를 Optional로 감싸서 반환한다. 결과는 단 하나밖에 없기 때문에 첫 번째 요소를 가져와도 무방하다.
Member findById(Long id): 주어진 고유 아이디를 사용하여 회원을 조회하는 메서드이다. EntityManager의 find 메서드를 사용하여 엔티티를 조회한다.
List findAll(): 모든 회원을 조회하는 메서드이다. JPQL을 사용하여 모든 Member 엔티티를 가져온다.
@Repository: 스프링에서 리포지토리(데이터 액세스 로직) 빈으로 등록되어 사용되는 것을 나타내는 어노테이션이다.
package com.shopingmall.seungjae.repository;
import com.shopingmall.seungjae.domain.Member;
import jakarta.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member save(Member member) {
// 엔티티를 영속화하여 데이터베이스에 저장하는 메서드입니다.
em.persist(member);
return member;
}
@Override
public Optional<Member> findByLoginId(String loginId) {
// 로그인 아이디(loginId)를 기반으로 회원을 조회하는 메서드입니다.
// JPQL을 사용하여 쿼리를 작성하고, 파라미터로 넘어온 loginId를 바인딩합니다.
// 조회 결과를 리스트로 가져온 뒤 첫 번째 요소를 Optional로 감싸서 반환합니다.
Optional<Member> member = em.createQuery("select m from Member m where m.loginId = :loginId", Member.class)
.setParameter("loginId", loginId)
.getResultList()
.stream().findFirst();
return member;
}
@Override
public Member findById(Long id) {
// 주어진 아이디(id)를 사용하여 회원을 조회하는 메서드입니다.
// EntityManager의 find 메서드를 사용하여 엔티티를 조회합니다.
Member member = em.find(Member.class, id);
return member;
}
@Override
public List<Member> findAll() {
// 모든 회원을 조회하는 메서드입니다.
// JPQL을 사용하여 모든 Member 엔티티를 가져옵니다.
List<Member> members = em.createQuery("select m from Member m", Member.class)
.getResultList();
return members;
}
}
org.springframework.transaction.annotation.Transactional 를 사용하자.
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을 커밋한다. 만약 런타임 예외가 발생하면 롤백한다.
JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
즉, 데이터 변경 도중에 발생하는 여러가지 예외 또는 에러들에 의해 데이터가 손상되지 않도록 트랜젝션을 사용해줘야 하는 것이다.
따라서 member를 저장하는 MemberService에 @Transactional를 붙여줘야 한다.
이 부분을 안해줘서 에러가 발생했었다! 이 부분 꼭 인지하자!!!
package com.shopingmall.seungjae.service;
import com.shopingmall.seungjae.domain.Member;
import com.shopingmall.seungjae.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor @Service @Transactional
public class MemberService {
private final MemberRepository memberRepository;
public String join(Member member) {
//같은 로그인 아이디가 있는 중복 회원은 안된다.
memberRepository.findByLoginId(member.getLoginId())
.ifPresent(m->{
throw new IllegalStateException();
});
memberRepository.save(member); //저장
return member.getLoginId();
}
}

기존에는 위의 사진과 같은 형태로 되어있었다. 이는 JPA를 사용하지 않을 때의 Config였다.
하지만 이제는 JPA를 사용하는 구현체로 바꾸어야 하기 때문에 아래와 같이 코드를 수정했다.

EntityManager 생성해준다.
EntityManager em;
public SpringConfig(EntityManager em){
this.em = em;
}
inset문이 실행된 것으로 값이 들어갔다는 것을 유추할 수 있다.

실제 DB에 query문을 작성하여 테이블을 보니 제대로 값이 들어간것을 확인할 수 있었다.

로그인까지 성공적으로 되는것을 확인했다.


마찬가지로 ItemRepositoryImpl를 JpaItemRepository로 바꿔줄 예정이다.