[Java/Spring] by 김영한님 @Inflearn 스프링 입문
JDBC => JdbcTemplate => myBatis / JPA
JPA = Java Persistance Api로 2015년 이후 사용이 크게 늘어나고 있다.
Jdbc Template 라이브러리를 사용함으로써 기존의 jdbc로 코딩을 해서 가져오는 부분을 간략화할 수 있다.
예컨대 save 메소드에서 jdbc로 구현하고자 한다면 다음과 같은 코드가 필요하다.
@Override
public Member save(Member member) {
String sql = "insert into member(name) values(?)";
Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, member.getName());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if (rs.next()) { member.setId(rs.getLong(1));
} else {
throw new SQLException("id 조회 실패");
}
return member;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
템플릿을 사용한다면 이와 같이 축약화 된다.
@Override
public Member save(Member member) {
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
Map<String, Object> parameters = new HashMap<>(); parameters.put("name", member.getName());
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
member.setId(key.longValue()); return member;
}
JPA를 사용해보자.
먼저 의존성을 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
그 다음으로는 Property에 추가해준다.
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
JPA는 객체 (여기서는 회원 객체)를 보고 테이블을 자동으로 만드는 기능이 있다.
현재는 이 기능을 none으로 설정
엔티티 맵핑이 필요하다.
JPA는 인터페이스로 존재하고 그 구현체로 Hibernate 등의 기술들이 구현된다.
JPA는 객체와 ORM 기술이다.
O = Object
R = Relational
M = Mapping
맵핑은 애노테이션으로 한다.
@Entity 애노테이션으로 JPA 관리 부분임을 선언할 수 있다.
pk를 생성하는 전략 중에 DB가 알아서 생성해주는 전략
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
이와 같은 애노테이션을 갖고 데이터베이스와 맵핑을 한다.
package com.inflearn.springintro.domain;
import javax.persistence.*;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Column(name="username")
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;
}
}
Repository에 Jpa를 생성한다.
package com.inflearn.springintro.repository;
import com.inflearn.springintro.domain.Member;
import java.util.List;
import java.util.Optional;
public class JpaMemberRepository implements MemberRepository{
@Override
public Member save(Member member) {
return null;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.empty();
}
@Override
public Optional<Member> findByName(String name) {
return Optional.empty();
}
@Override
public List<Member> findAll() {
return null;
}
}
스프링부트가 알아서 EntityManger라는 것을 만들어주고 거기서부터 맵핑을 시작하면 된다.
=> 데이터 소스를 기본적으로 들고 있어서 db 통신을 내부적으로 함
Entity Manger를 아래와 같이 주입받음.
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
JPA에서 사용되는 SQL 언어는 다음과 같다.
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
DB를 향해 쿼리를 날리는 게 아니라 객체를 대상으로 쿼리를 작성한다.
=> SQL로 번역이 됨.
Member 엔티티를 조회하는데 멤버 엔티티 자체를 셀렉트.
전체 문을 다음과 같이 작성.
package com.inflearn.springintro.repository;
import com.inflearn.springintro.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
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> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
@Override
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 List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
}
JPA를 쓰려면 주의해야 하는 점은 항상 Transaction이 있어주어야 한다.
스프링 데이터 JPA
스프링 부트와 JPA만 사용해도 인터페이스 만으로 개발을 완료할 수 있을 만큼 쉬워진다.
=> 개발 생산성 혁신 크게 올라감
데이터JPA 클래스를 인터페이스로 만든다.
package com.inflearn.springintro.repository;
import com.inflearn.springintro.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
@Override
Optional<Member> findByName(String name);
}
인터페이스만 있는 SpringDataJpaMemberRepository가 JpaRepository를 받고 있으면 구현체를 자동적으로 만들어준다.
스프링 빈에 자동으로 등록.
그것을 config에서 다음과 같이 가져올 수 있다.
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
스프링 컨테이너에서 MemberRepository를 찾는데 등록한 게 없는 상황에서 위에서 만든 스프링데이터JPA에서 만든 것을 보고
구현체를 자체적으로 만듬 => 스프링 빈에 등록을 하기 때문에
인젝션을 받을 수 있다.
JPA 라이브러리를 보면 기본적인 CRUD 등이 다 만들어져있다.
Member 엔티티에서
name으로 찾는 것은 공통화하기가 힘들기 때문에 name 부분은 작성해준다.
@Override
Optional<Member> findByName(String name);
==> SELECT M FROM MEMBER M WHERE M.NAME = ?
이와 같은 쿼리문을 짜서 찾아줄 수 있다.
'Programming > Java, Spring' 카테고리의 다른 글
한 메서드에 오직 한 단계의 들여쓰기만 - 우아한테크코스 우테코 클린코드 #2 (0) | 2022.10.30 |
---|---|
자바 코딩 컨벤션 - 우아한테크코스 우테코 클린코드 #1 (0) | 2022.10.30 |
[Java/Spring] 스프링 빈과 의존관계 설정 (0) | 2022.10.24 |
[Java ] 객체 지향에 대한 개념, 계산기 코드 OOP 리팩토링 (0) | 2022.10.23 |
[Java] 테스트 코드 작성시 ParameterizedTest + MethodSource 코드 작성 예시 (0) | 2022.10.23 |