본문으로 바로가기
반응형

 

자바 프로젝트에서 생성한 데이터들을 DB와 연동하고자 한다.

 

spring에서 제공하는 JPA 방식을 사용하면 간편하게 연동이 가능하지만,

스프링을 활용하지 않고 사용하는 JDBC 방법부터 알아보고자 한다.

 

 

 

DB 연동은, 같은 동작을 수행하는 코드를

총 4단계에 거쳐서 기술하고자 한다.

 

 

 

그 첫 번째는 순수 JDBC를 활용하는 방법이다.

 

 

 

 

 

기존 코드(Service, Repository, Domain 등)는 아래 링크에서 

순차적으로 볼 수 있다

 

https://healthdevelop.tistory.com/entry/spring12

 

[Spring boot] 스프링 - 회원 가입, 회원 조회

간단한 회원관리 예제를 구현하고자 한다. 순서는 아래를 참조하면 된다. ● 회원관리 예제 - 백엔드 개발  1. 회원 도메인과 리포지토리 만들기  2. 회원 리포지토리 테스트 케이스 작성  3. 회

healthdevelop.tistory.com

 

 

 

 

 

 

 

 

 

 

 


1. build.gradle 세팅

 

 

 

 

build.gradle에 dependencies에서

밑줄 친 코드를 삽입해준다. (spring에서 jdbc를 사용하는 dependency)

 

 

 

 

 

 

 

 

 

 

 

2. DB 접속 정보 작성

 

 

jdbc 설정을 했다면 이제 db의 접속 정보가 필요하다.

(oracle의 properties와 동일)

 

 

 

 

application.properties에 가서

아래 코드를 삽입해준다.

 

 

 

 

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

 

 

이로써 db 접속에 대한 정보 설정은 끝났다.

 

 

 

 

 

 

 

 

 

 

 

3. 코드(쿼리문) 작성

 

 

 

 

기존에 작성했던 memberRepository를 활용하여

회원 가입, 회원 조회, 전체 회원 조회 코드를 작성하고자 한다.

 

 

 

 

 

 

 

 

JdbcMemberRepository.java

package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.jdbc.datasource.DataSourceUtils;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class JdbcMemberRepository implements MemberRepository {

    private final DataSource dataSource; // db에 붙기위한 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(); // db 연결을 가져옴
            pstmt = conn.prepareStatement(sql,
                    Statement.RETURN_GENERATED_KEYS); // 자동 생성되는 id 값 얻기(시퀀스)

            pstmt.setString(1, member.getName()); // sql문 ?에 getname이 들어감

            pstmt.executeUpdate(); // db에 실제 쿼리 날라감
            rs = pstmt.getGeneratedKeys(); // 위에 생성된 generated_keys(id) 순차 반환

            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); // 자원들 release 해줌
        }
    }
    @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);
    }
}

 

코드의 자세한 내용은 주석을 참고하면 된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

4. Config 작성

 

 

 

위에 작성한 코드들을 작성했던 memberRepository interface로 넘겨주어야 한다.

 

그것은 이전에 작성했던 config 파일을 수정해서 할 수 있다.

 

 

 

package hello.hellospring;
import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.JdbcTemplateMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
public class SpringConfig {

 	private final DataSource dataSource;  // 추가
    
 	public SpringConfig(DataSource dataSource) {
 		this.dataSource = dataSource;
 	}
 	@Bean
 		public MemberService memberService() {
 		return new MemberService(memberRepository());
 	}
 	@Bean
 	public MemberRepository memberRepository() {
		// return new MemoryMemberRepository();
		return new JdbcMemberRepository(dataSource); // 추가
 	}

 

 

memberRepository 빈의 return을 

새로 작성한 JdbcMemberRepository로 반환해주는 것이다.

 

그리고 매개변수로 datasource를 넘겨주었다.

 

이것은 spring이 application properties에 설정 파일(db 연결)을 보고

datasource 스프링 빈을 만들어준다.

 

 

 

 

 


 

 

 

이제 데이터 연동은 마쳤고,

실제로 회원 가입이 잘 되는지 확인해 보자.

 

 

 

 

 

 

 


● 서버(브라우저)에서 확인

 

 

 

 

 

서버를 실행시키고,,

 

 

 

회원 가입을 페이지에 접속을 요청하고

 

 

 

 

 

 

 

 

 

 

새로운 회원 "jpa"를 등록시켜준다음

회원 조회 페이지로 가보면,,

 

 

 

 

 

 

 

 

 

 

새로운 회원이 "jpa"가 등록되었다.

 

 

 

 

 

 

 

 

 

h2 데이터베이스도 확인을 해보면

 

 

데이터가 잘 저장되었다.

 

 

 

 


 

 

 

우리(개발자)는 기존의 MemoryMemberRepository를 수정하지 않고

SpringConfig만 수정함으로써 db 저장소를 바꾸었다.

 

 

이것은 객체 지향 설계 5원칙 중 하나인 OCP(개방-폐쇄 원칙)을 잘 준수한 것이다.

 

 

기존에 있는 상태에서 확장은 하였지만, 기존 코드는 전혀 손을 대지 않았다.

 

 

 

이것이 스프링의 장점이다.

 

 

 

 

반응형