Spring/스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

[ 스프링 DB 접근 기술 ] 순수 JDBC

alsruds 2023. 9. 12. 23:45

🙂강의🙂

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

 

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., 스프링 학습 첫 길잡이! 개발 공부의 길을 잃지 않도록 도와드립니다. 📣 확인해주세

www.inflearn.com

 

  • 고대의 방식이라고 한다 😑

 

▨ 목차 ▨

- 환경 설정
    - build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가
    - 스프링 부트 데이터베이스 연결 설정 추가 (resources/application.properties)
- Jdbc 리포지토리 구현
    - Jdbc 회원 리포지토리
    - 스프링 설정 변경

 

[ 환경 설정 ]

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

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

 

  • 추가 화면

 

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

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

 

  • 추가 화면

주의 : 스프링 부트 2.4 부터는 spring.datasource.username=sa 를 꼭 추가해야 한다. 추가하지 않을 시 Wrong user name or password 오류가 발생한다. 마지막에 공백이 들어가도 같은 오류가 발생한다.
참고 : IntelliJ 커뮤니티 (무료) 버전의 경우 application.properties 파일의 왼쪽이 다음 그림과 같이 회색으로 나오지만 실제 동작하는 데는 아무런 문제가 없다. 엔터프라이즈 (유료) 버전에서 제공하는 스프링의 소스 코드를 연결하는 편의 기능이 빠진 것이다.

 

[ Jdbc 리포지토리 구현 ]

주의 : 이렇게 JDBC API 로 직접 코딩하는 것은 20년 전 이야기로, 참고만 하고 넘어가기

✅ Jdbc 회원 리포지토리 생성하기

  • src/main/java/hello.hellospring/repository 에 JdbcMemberRepository class 생성
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);
    }
}

 

✅ 스프링 설정 변경하기

  • src/main/java/hello.hellospring/service/SpringConfig
package hello.hellospring;

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

➡️ MemoryMemberRepository 대신 JdbcMemberRepository

 

  • 스프링 설정 이미지

→ 개방 - 폐쇄 원칙 ( OCP, Open - Closed Principle ) : 확장에는 열려 있고, 수정/변경에는 닫혀 있다

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

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

 

  • 실행 화면

데이터베이스에 저장했던 데이터가 잘 들어와 있다 ~