Spring

스프링 DB 접근 기술 1 ( H2 데이터베이스 설치 | 순수 JDBC )

보배 진 2022. 1. 26. 03:43

(스프링 DB 접근 기술을 몇 차례에 나누어 진행하도록 하겠습니다)

 

 

지금까지는 따로 회원 정보를 DB에 저장해두지 않아 껐다 키면 서버가 날아가 회원 목록이 전부 사라졌습니다

데이터가 날아가지 않도록 H2 데이터베이스 설치를 하도록 하겠습니다

 

그리고 설치한 DB를 JDBC로 연결하여 사용해보도록 하겠습니다

 

 

 

 

 

 

 

 

H2 데이터베이스 설치

: 개발이나 테스트 용도로 가볍고 편리한 DB, 웹 화면 제공

 

 https://www.h2database.com 에 접속하여 다운로드 및 설치

h2 데이터베이스 버전은 스프링 부트 버전에 맞춘다

권한 주기 : chmod 755 h2.sh <- 윈도우는 안해도 됨

실행 : ./h2.sh

데이터 베이스 파일 생성 방법

jdbc:h2:~/test (최초 한 번)

이후 부터는 jdbc:h2:tcp://localhost/~/test 이렇게 접속

 

 

 

 

다운로드를 받음 : 강의와는 다른 버전

우선 사이트에 접속하여 설치파일을 다운로드 합니다

 

 

 

처음에는 다운을 받고 ./h2.sh 로 실행을 시켜봤지만 실행이 되지 않았습니다

압축을 풀고 h2w.bat 파일을 클릭하면 H2 서버가 구동되어 자동으로 

 

 

이렇게 화면이 나타났습니다!!! 

 

 

 

 

 

 

 

우선 최초에는 데이터베이스 파일이라는 것을 만들어야 합니다

JDBC URL 오른쪽은 나의 test 파일의 경로를 나타낸다

 

 

 

 

 

 

 

[ H2 데이터베이스에 접근해서 member 테이블 생성 ]

create table member
(
id bigint generated by default as identity,
name varchar(255),
primary key (id)
);

 

 

 

1. 자신의 파일에 test.mv.db가 있는지 확인을 한다

2. JDBC URL을 바꾼다 -> jdbc:h2:tcp://localhost/~/test

3. 실행을 누른 뒤 테이블을 만든다

 

4. table을 하나 만들어주었다

왼쪽에 노랑 형광펜으로 칠한 MEMBER 테이블이 만들어졌다면 성공!

 

 

자바에서 id는 long이었지만 DB에서는 bigint로 사용
generated by default as identity 는 만약 값을 주지 않고 insert하면 DB가 자동으로 값을 채워준다
그리고 pk는 id로 주었다

 

테스트

 

 

 

 

 

 

한 번 insert 해보도록 하겠습니다 ^^

insert into member(name) values('spring')

 

이렇게 insert하고 select로 조회를 하고 나면 출력이 됩니다

실행을 두 번 했더니 두 번 들어감

 

 

 

 

 

 

 

참고로 ./h2.sh 로 실행을 시킨 DB를 끄면 H2 database가 실행되지 않습니다

웹 콘솔을 통해 DB에 접속을 해보았고 이제 앱플리케이션을 통해 DB에 삽입을 해보도록 하겠습니다


 

 

 

 

 

 

 

 

 

순수 JDBC

 

 

 

 

 

 

환경설정

 

bulid.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가

implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'

 

 

 

 

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

application.properties에 위 내용을 추가하고

 

 

 

다시 bulid.gradle파일에 가서 

노란 동그라미를 클릭하면 드라이브 세팅 완료.

(이제 DB에 접근할 수 있습니다)

 

 

 

 

 

 

 

 

 

 

JdbcMemberRepository를 추가합니다

 

 

 

 

 

JdbcMemberRepository에 들어가는 내용입니다

package com.ujin.ujinspring.repository;

import com.ujin.ujinspring.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;

    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, 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 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);
    }
}

save하기 위한 쿼리문, 데이터베이스를 가져오기 위한 conn 등을 사용하여 DB에 쿼리문을 날려준다

 

안에 내용을 추가한 뒤

SpringConfig에 가서 이렇게 변경해줍니다

package com.ujin.ujinspring.service;

import com.ujin.ujinspring.repository.JdbcMemberRepository;
import com.ujin.ujinspring.repository.MemberRepository;
import com.ujin.ujinspring.repository.MemoryMemberRepository;
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;

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


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

 

DataSource는 데이터베이스 커넥션을 획득할 때 사용하는 객체다. 스프링 부트는 데이터베이스 커넥션 정보를 바탕으로 DataSource를 생성하고 스프링 빈으로 만들어둔다. 그래서 DI를 받을 수 있다

 

 

 

 

 

 

 

 

 

개방.패쇄 원칙(,OCP, Open-Closed Principle)

- 확장에는 열려있고, 수정에는 닫혀있다

스프링의 DI(Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있다

회원을 등록하고 DB에 결과가 잘 입력되는지 확인,

데이터를 DB에 저장하므로 스프링 서버를 다시 실행해보 데이터가 안전하게 저장됨