기존 Jdbc Template의 특징은 물론, JPA 는 SQL마저 직접 작성해준다.
build.gradle에 더이상 'jdbc'는 없어도 된다.
jpa를 추가시키자.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
그 후,properties에도 설정을 추가한다.
spring.jpa.show-sql=true
이렇게 하면, JPA가 만든 SQL을 직접 볼 수가 있다.
추가로,
spring.jpa.hibernate.ddl-auto=none
JPA는 객체를 보고, 테이블을 다 만들어버린다. 하지만 우리는 이미 테이블을 작성한 상태이므로, 해당 기능을 꺼준다.
객체와 relation database table을 mapping 하는 것이 JPA이다.
이를 위해선 Mapping Annotation (@Entity)를 추가한다.
Member 클래스에 해당 어노테이션을 추가하자.
package Goat.CouponCheck.domain;
import jakarta.persistence.*;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //임의의 값, 데이터 구분을 위함.
@Column(name="Name")
private String name;
@Column(name="coupon")
private Integer couponNum;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getCouponNum() {
return couponNum;
}
public void setCouponNum(Integer couponNum) {
this.couponNum = couponNum;
}
}
PK인 ID는 자동으로 생성해주므로, @ID라는 어노테이션과, @GeneratedValue라는 어노테이션을 함께 id앞에 붙여준다.
마찬가지로 다른 DB들도 JPA가 자동으로 관리할 수 있게끔 명시하는 Annotation을 작성한다.
JPA는 EntityManager로 동작한다.
Spring Boot가 gradle에서 build 되어질 때, 자동으로 EntityManager를 생성하고, 우리는 이를 그저 가져다 쓰면 된다.(Injection)
package Goat.CouponCheck.repository;
import Goat.CouponCheck.domain.Coupon;
import Goat.CouponCheck.domain.Member;
import jakarta.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class JpaRepository implements Repository{
private final EntityManager em;
public JpaRepository(EntityManager em) {
this.em = em;
}
@Override
public Member saveMember(Member member) {
em.persist(member);
return member;
}
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
}
@Override
public Optional<Integer> saveCoupon(Coupon coupon) {
return Optional.empty();
}
}
객체 지향이라 불리우는 이유?
public List<Member> findAll() { return em.createQuery("select m from Member m", Member.class) .getResultList(); }
해당 코드를 보면 Member m에서 select m을 뽑는다.
원래 sql같은 경우에는 모든 column name들을 다 뽑아야 하겠지만, 이처럼 객체 자체를 뽑아온 다는 점에서 유용하다.
package Goat.CouponCheck;
import Goat.CouponCheck.repository.JpaRepository;
import Goat.CouponCheck.repository.Repository;
import Goat.CouponCheck.service.MemberService;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
// private DataSource dataSource;
private EntityManager em;
@Autowired
// public SpringConfig(DataSource dataSource) {
// this.dataSource = dataSource;
// }
public SpringConfig(EntityManager em){
this.em = em;
}
@Bean
public MemberService memberService(){
return new MemberService(repository());
}
@Bean
public Repository repository(){
// return new MemoryRepository();
// return new JdbcRepository(dataSource);
// return new JdbcTemplateRepository(dataSource);
return new JpaRepository(em)
}
}
이전 상황들과 별개로, Jpa는 DataSource가 아닌 EntityManager를 사용하므로 해당 객체를 가져와서 파라미터로 넘겨주자.