wasup

Spring) JDBC / jdbcTemplate / JPA / spring data JPA 본문

IT/Java

Spring) JDBC / jdbcTemplate / JPA / spring data JPA

wasupup 2021. 8. 8. 19:42
반응형

순수 JDBC : 데이터를 DB에 저장하고 관리하기 위해 서버와 디비를 연결!

JdbcTemplate : 순수 JDBC보다 소스 중복내용이 줄어들지만 쿼리를 모두 직접 작성해야함

JPA : JdbcTemplate보다 기본적인 CRUD를 지원한다.

스프링 데이터 JPA : 아예 구현 클래스 필요없이 인터페이스로 해결.


순수 JDBC

데이터를 데이터베이스에 저장하고 관리하기위해 애플리케이션 서버와 디비를 연결

 

예제)

Repository.java

public class JdbcMemberRepository implements MemberRepository {
    private final DataSource dataSource;
    public JdbcMemberRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @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,//sql넣기
                    Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, member.getName());//sql문의 '?'와 매칭
            pstmt.executeUpdate();//db에 실제 쿼리 날리기
            rs = pstmt.getGeneratedKeys();//RETURN_GENERATED_KEYS와 매칭하여 키를 반환
            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 Optional<Member> findById(Long id) {
        String sql = "select * from member where id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setLong(1, id);
            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            } else {
                return Optional.empty();
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public List<Member> findAll() {
        String sql = "select * from member";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            List<Member> members = new ArrayList<>();
            while(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                members.add(member);
            }
            return members;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public Optional<Member> findByName(String name) {
        String sql = "select * from member where name = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            }
            return Optional.empty();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    private Connection getConnection() {
        return DataSourceUtils.getConnection(dataSource);
    }
    private void close(Connection conn, PreparedStatement pstmt, ResultSet rs)
    {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (pstmt != null) {
                pstmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                close(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    private void close(Connection conn) throws SQLException {
        DataSourceUtils.releaseConnection(conn, dataSource);
    }
}

SpringConfig.java

@Configuration
public class SpringConfig {

    private DataSource dataSource;

    @Autowired
    public SpringConfig(DataSource dataSource){
        this.dataSource = dataSource;
    }

    @Bean
    public MemberRepository memberRepository(){
        return new JdbcMemberRepository(dataSource);
    }
}

스프링 jdbcTemplate

순수 JDBC와 동일한 환경설정 필요

스프링 jdbcTemplate, MyBatis같은 라이브러리는 

JDBC API에서 반복 코드를 대부분 제거해주지만 SQL은 직접 작성해야 한다.

 

예제)

public class JdbcTemplateMemberRepository implements MemberRepository {

    private final JdbcTemplate jdbcTemplate;

    //@Autowired //생성자가 하나면 @Autowired 생략 가능
    public JdbcTemplateMemberRepository(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @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;
    }

    @Override
    public Optional<Member> findById(Long id) {
        List<Member> result = jdbcTemplate.query("select * from member where id=?", memberRowMapper(), id);
        return result.stream().findAny();
    }

    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = jdbcTemplate.query("select * from member where name =? ", memberRowMapper(), name);
        return result.stream().findAny();
    }

    @Override
    public List<Member> findAll() {
        return jdbcTemplate.query("select * from member", memberRowMapper());
    }

    private RowMapper<Member> memberRowMapper(){
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }
}

 

@Configuration
public class SpringConfig {

    private DataSource dataSource;

    @Autowired
    public SpringConfig(DataSource dataSource){
        this.dataSource = dataSource;
    }

    @Bean
    public MemberRepository memberRepository(){
        return new JdbcTemplateMemberRepository(dataSource);
    }
}

JPA

기본적인 sql을 직접 만들어서 실행해준다.

객체를 쿼리없이 바로 DB에 저장하고 관리할 수 있음.

* JPA: 인터페이스 제공, hibernate : 구현체

* JPA는 객체와 ORM(Object Relational Mapping:객체 관계 매핑)기술이다.

* JPA를 사용하려면 트랜젝션(@Transactional)이 꼭 있어야 함.

 

비교해보기

build.gradle

dependencies {
	//implementation 'org.springframework.boot:spring-boot-starter-jdbc'//java는 db를쓰려면 jdbc가 꼭 있어야함.
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'//jdbc를 포함하는 jpa!
	runtimeOnly 'com.h2database:h2'//db와 연결할 때 database가 제공하는 클라이언트가 필요.
}

application.properties

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
//spring.jpa.hibernate.ddl-auto=create //create로 하면 테이블까지 생성해줌.

 

Member.java

//@Entity : JPA가 관리하는 Entity
@Entity
public class Member {
    //@Id : pk매핑
    //@GeneratedValue : 주키의 값을 위한 자동 생성 전략 명시.
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;
    private String name;

    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.java

public class JpaMemberRepository implements MemberRepository{

    //JPA는 EntityManager로 모듈이 동작함.
    private final EntityManager em;

    public JpaMemberRepository(EntityManager em) {
        this.em = em;
    }

    @Override
    public Member save(Member member) {
        //persist : 저장
        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() {
        //List<Member> result = em.createQuery("select m from Member m", Member.class).getResultList();
        //return result;
        //->
        return em.createQuery("select m from Member m", Member.class).getResultList();
    }
}

Service.java

@Transactional
public class MemberService {

}

SpringConfig.java

@Configuration
public class SpringConfig {

    /*JPA를 사용하려면 기존에 받았던 dataSource 대신

        private DataSource dataSource;

        @Autowired
        public SpringConfig(DataSource dataSource){
            this.dataSource = dataSource;
        }
    */

    //스팩에서는 @PersistenceContext 를 사용해줘야함.
    //스프링에서는 없어도 사용가능.
    private EntityManager em;

    public SpringConfig(EntityManager em) {
        this.em = em;
    }

    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new JpaMemberRepository(em);
    }
}

스프링 데이터 JPA

기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공.

* JPA를 먼저 공부한 다음에 스프링데이터JPA를 사용해야함.

* JPA와 스프링 데이터 JPA를 기본으로 사용하고 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용한다.

* Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성하고 동적 쿼리도 편리하게 작성할 수 있다.

* 이런 조합으로도 해결하기 어려운 쿼리는 jdbcTemplate를 사용한다.

 

package com.was.waspj.repository;

import com.was.waspj.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);
}

: SpringDataJpa가 JpaRepository를 받고있으면 스프링빈을 자동으로 등록해서 구현체를 알아서 만들어준다.

 


[출처]

 

인프런 강의 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

 

참고링크1 : ( https://jsaver.tistory.com/entry/Id%EC%99%80-GeneratedValue-%EC%95%A0%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98 )


 

반응형
Comments