본문 바로가기
Programming/Java, Spring

[Java/Spring] 스프링 DB 매니지먼트 변천사 / JDBC, JPA, 스프링 데이터 JPA

by Renechoi 2022. 10. 25.

[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 = ? 

이와 같은 쿼리문을 짜서 찾아줄 수 있다. 

 

 

 

반응형