본문 바로가기

Spring

순수 Jdbc

스프링부트가 지원하는 인메모리 관계형 데이터베이스인 H2 db 연결하기

h2의 장점으로는 따로 설치가 필요없는 아주 가벼운 웹용 로컬 DB이다!

(설치 가이드는 쉽고 쉽게 설명된 다른 글들이 많으니 패스~)

 

 

H2 연결 성공!

insert into member(name) values("spring")라고 ""쓰면 에러.. 꼭 작은따옴표..

오라클할 때 배웠는데도 실수하는 저란 사람 ㅠㅠㅠ

 

자바는 db연동을 하려면 jdbc가 꼭 있어야 한다.

의존성 추가한다음에 꼭 우측 상단 코끼리 눌러서 업로드 해주기!

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

application.properties에서도 연결해주기

datasourrce url 은(h2데이터베이스 깔때 설정했던 url)

package com.example.spring.repository;

import com.example.spring.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;

import static org.springframework.jdbc.datasource.DataSourceUtils.getConnection;

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 (SQLException 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 (SQLException 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);
            }else{
                return Optional.empty();
            }
        } catch (SQLException 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);
        }
    }

    private Connection getConnection() {
        return DataSourceUtils.getConnection(dataSource);
    }

    private void close(Connection conn, PreparedStatement pstmt, ResultSet rs){
        try {
            if(rs != null) rs.close();
            if(conn != null) conn.close();
            if(pstmt != null) pstmt.close();

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

 

순수 JDBC로 작업한 어마어마한 코드량.. 예전에는 모두 이렇게 했다고..

근데 저도 학원에서 이렇게 배웠습니다만..ㅎ

 

*참고로 데이터베이스는 외부와 소통하는거긴 때문에 자원을 쓰고 난후에는 꼭 끊는 close()작업을 해야한다.!!

(안하다 쌓이면 나중에 대장애가 올수도 있다...)

 

package com.example.spring;

import com.example.spring.repository.JdbcMemberRepository;
import com.example.spring.repository.MemberRepository;
import com.example.spring.repository.MemoryMemberRepository;
import com.example.spring.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 {

    private DataSource dataSource;

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

 

자바의 큰 장점인 다형성의 활용!

인터페이스를 두고 구현체는 내 마음대로 바꿀수 있게 해준다.

기능을 완전히 변경하더라도 전체 수정을 하지 않는다.

조립하는 코드는 수정하더라도 실제 애플리케이션이 동작하는 코드는 수정이 필요없다!

 

SOLID

개방-폐쇄 원칙(OCP; Open-Closed Principle)

확장에는 열려있고, 수정(변경)에는 닫혀있다.

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