Spring H2 데이터베이스 설치 | 순수 JDBC

심삼진·2023년 5월 2일
0

Spring

목록 보기
7/12
post-thumbnail

이번 시간에는 가볍고 심플한 H2 데이터베이스를 설치하여 서버와 DB를 연결하겠습니다.


H2 데이터베이스 설치

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


h2 데이터베이스는 꼭 다음 링크에 들어가서 1.4.200 버전을 설치해주세요.
최근에 나온 2.0.206 버전을 설치하면 일부 기능이 정상 동작하지 않습니다.

https://www.h2database.com/html/download-archive.html

저는 윈도우 사용자이기 때문에 Windows Installer를 다운로드 했습니다.


다운로드 후 설치까지 진행하면 H2 콘솔로 이동할 수 있습니다.

데이터베이스 최초 파일을 생성하기 위해 그대로 연결을 한 번 시켜줍니다.
에러가 나신 분들은 IP를 localhost로 변경하시면 생성이 됩니다.

하지만 저는 생성이 되지 않아서 다음 링크를 보고 참고하여 직접 test.mv.db 파일을 생성하여 진행했습니다.

https://kmhan.tistory.com/581

한 번 최초 실행을 했으면 그 후로부터는 jdbc:h2:tcp://localhost/~/test 로 연결해줍니다.



성공적으로 연결되면 이런 화면이 나타납니다.





H2 데이터베이스에 접근하여 테이블 생성하기

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

id bigint generated by default as identity : id 값이 null로 들어왔을 때 자동으로 채워줍니다.


sql문을 작성한 후 '실행'을 누르면 테이블이 생성됩니다.


이제 데이터를 삽입해 봅시다.

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

삽입 후 다시 조회해보면 잘 삽입한 것을 확인할 수 있습니다.





순수 JDBC

이제 회원 가입 웹과 연결을 해보겠습니다.


1. 환경 설정

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

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

각각 JDBC 드라이버와 데이터베이스 클라이언트입니다.


[ 스프링 부트 데이터베이스 연결 설정 추가 ( resources/application.properties ) ]

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

추가한 후, 다시 gradle 파일로 가서

코끼리 아이콘을 눌러줍니다.

😺 : 스프링부트 2.4부터는 spring.datasource.username=sa 를 꼭 추가해주어야 합니다.

인텔리J 커뮤니티(무료) 버전의 경우 application.properties 파일의 왼쪽이 다음 그림과 같이 회색으로 나옵니다.

실제로 동작하는 데 문제 없습니다.


2. Jdbc 리포지토리 구현


[ Jdbc 회원 리포지토리 ]

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;

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

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



[ SpringConfig 수정 ]

package hello.hellospring;

import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
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 {

    @Autowired
    DataSource dataSource;

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

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

}



[ 구현 클래스 이미지 ]


[ 스프링 설정 이미지 ]

개방-폐쇄 원칙(OCP, Open-Closed Principle) : 확장에는 열려있고, 수정, 변경에는 닫혀있다.
스프링의 DI (Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있습니다.



이제 회원을 등록하고 DB에 결과가 잘 입력되는지 확인하겠습니다.

😺 : 꼭 H2 콘솔 열어둔 상태로 실행하셔야 합니다!



[ 결과 ]

아까 넣었던 spring이 잘 출력되어 있습니다.

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





학습중인 스프링 강의 : https://inf.run/pcut
스프링 실습 코드 저장소 : https://github.com/0pyaq0/Spring_Study.git

본 글은 2022년도에 작성한 기존 티스토리 블로그 글을 재업로드한 글입니다
기존 티스토리 블로그 : https://develop-about-leejin.tistory.com/

profile
주니어 백엔드 개발자

0개의 댓글