Spring 사용하기 (Coupon_check 백엔드(6) - DB만들기) - (문제있음2)

김태훈·2022년 12월 29일
0

Spring_CouponCheck

목록 보기
9/14

가벼운 데이터베이스인 h2 데이터베이스를 사용하였다.
먼저 h2 database에 간단한 테이블을 만들자.
나에게 필요한 db정보는
1. 회원 id(primary-key), 회원 name, 회원이 부여받은 쿠폰 번호
2. 남은 Coupon 갯수 -> 굳이 database에 저장시키지 않아도 된다. 이미 Domain 디렉토리에 해당 쿠폰 갯수가 하드 코딩 되어있기 때문이다.

Coupon은 무조건 데이터베이스로 해야하지 않을까..?-이걸 건드려서 해결해야할것같다.
아니다. 무조건 Domain에는 Coupon이 존재 해야만 함!!!

다음 테이블은 회원 정보와 관련된 테이블

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

부여받은 쿠폰번호는, 회원가입을 했으면 아직 없기 때문에 초기 값은 0으로 지정한다.



대충 name을 spring1으로 지정하고 table에 삽입한 결과를 select 문으로 확인해보았다.

그리고 해당 sql을 프로젝트안에 넣어서 버전컨트롤에 유의하게 하였다.

기존에는 MemoryRepository를 활용하여 메모리 구현체를 만들었다.
이는 Repository 라는 인터페이스를 활용하여 만들었다.
당초에 Repository 인터페이스를 만든 이유는, DB가 선정되지 않았기 때문이다.
따라서, 해당 인터페이스가 가지고 있는 다형성을 이용하여 DB를 연동한 Repository 구현체를 구현하면 되겠다.

1. jdbc 직접 활용 (막노동)

jdbc를 활용하기 위해서 이를 프로젝트와 직접 연동시켜야 한다.

1. build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가시킨다.

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

build.gradle에 대한 설명은 Spring 1편부터 보고 오면 될 것 같다. (management)

2. 스프링 부트에 데이터베이스 연결을 추가시키기.

resources/application.properties 파일에 해당 데이터베이스에 대한 정보를 추가시킨다.

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

처음에 h2.Driver에 빨간줄을 뜰 수도있다. build.gradle에 해당 파일을 추가시키고 refresh를 해준다.

3. JdbcRepository 구현체 구현하기

package Goat.CouponCheck.repository;
import Goat.CouponCheck.domain.Coupon;
import Goat.CouponCheck.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 JdbcRepository implements Repository {
    private final DataSource dataSource;
    public JdbcRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @Override
    public Member saveMember(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<Integer> saveCoupon(Coupon coupon) {
        coupon.setNum();
        Optional<Integer> returnVal = Optional.ofNullable(coupon.getNum());
        if (returnVal.isPresent()){
            return Optional.ofNullable(coupon.getNum());
        }
        else{
            System.err.println("쿠폰수가 0장입니다.");
            return Optional.ofNullable(coupon.getNum());
        }
    }


    @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"));
                member.setCouponNum(rs.getInt("coupon"));
                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"));
                member.setCouponNum(rs.getInt("coupon"));
                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"));
                member.setCouponNum(rs.getInt("coupon"));
                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);
    }
}
  1. DataSource란?
    DataSource는 2번에서 application.propeties에 DB정보를 추가했었던 그 DataSource를 말한다.
    해당 properties를 읽고, 스프링부트는 DataSource를 만들어 놓고, 이를 주입받는 과정이다.
  2. sql과 관련된 Connection, prepareStatement,ResultSet란?
    Connection : 데이터베이스와 관련된 socket이 열린다.
    prepareStatement : query를 보낼 sql문 작성 (반복적인 sql문에 유리)
    ResultSET : 말그대로 sql문 실행 결과이다.
  3. prepareStatement 속 RETURN_GENERATED_KEYS 란?
    DB에 insert를 한 후, id를 얻기 위해서 쓰는 것. 이는 이후에
prepareStatement.getGeneratedKeys()

와 연동된다. 이 후에, 해당 id를 이용하여 ResultSet을 얻는다.
4. 자원들 release는 필수다. 반드시 Connection, PrepareStatement, ResultSet을 역순으로 닫아주자.

2. Spring Configure 수정

지금까지 한 것으로 데이터베이스를 수정할 수 없다.
우리는 이 전까지 단순히 MemoryRepository를 이용했다.
이제는 JdbcRepository로 수정시켜야 한다.
해당 과정은 SpringConfig 파일에서 이루어졌다. 해당 내용 다시보기

package Goat.CouponCheck;

import Goat.CouponCheck.repository.JdbcRepository;
import Goat.CouponCheck.repository.Repository;
import Goat.CouponCheck.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(repository());
    }

    @Bean
    public Repository repository(){

//        return new MemoryRepository();
        return new JdbcRepository();
    }
}

앞에 존재하는 dataSource는,
스프링 자체적으로 우리가 앞서 설정했던 application.properties에서 db에 관해 적은 정보를 읽고, dataSource를 Bean에 등록한다.

이렇게 객체지향적인 방식으로, Repository를 바꿔치기 한다는 것이 Spring의 장점이다

profile
기록하고, 공유합시다

0개의 댓글