Spring boot - DB 제작(1)

범수·2024년 2월 20일

목차

  1. 프로젝트 생성하기
  2. View 환경설정
  3. 스프링 웹 개발 기초
  4. 회원 관리 예제
  5. 스프링 빈과 의존 관계
  6. 웹 MVC 제작
  7. DB 제작(1)
  8. DB 제작(2)
  9. AOP

1. H2 데이터베이스

H2 데이터베이스 설치

H2 데이터베이스 초기 설정

h2.jar File로 접속 h2접속(localhost:8082)
test.mv.DB 생성 확인test.db 화면
  1. Install 후 prgramFile > H2 > bin > h2.Jar File로 접속
  2. 페이지 로드 후 연결(img2)
    • 페이지 주소명 앞 부분 localhost:8082로 변경
  3. Users에 test.mv.DB파일이 생성된 것을 확인할 수 있다.
    • test파일 생성한 후에는 img2의 URL을 jdbc:h2:tcp://localhost/~/test 변경 후 접속
  4. 정상적으로 testDB를 생성한 것을 확인 가능

H2 데이터베이스 사용해보기

  • Member 테이블을 생성된 것을 확인할 수 있다.
    • bigint는 Java에서 Long 자료형을 의미
    • 칼럼 값을 자동으로 증가를 위한 generated by default as identify 설정
  • Member 테이블에 홍길동 추가
    • generated by default as identify를 설정했기 때문에 pk없이 추가 가능
  • (1, 홍길동)이라는 값이 제대로 들어있는 것을 조회해볼 수 있다.

2. 순수JDBC

IntelliJ 환경 설정

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

//build.gradle에 라이브러리 추가
  • build.gradle에 spring-boot-starter-jdbc 추가
    • Spring에 Jdbc를 연결하기 위해 추가
  • com.h2database:h2 라이브러리 추가
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

//resources > application.properties에 추가
  • 스프링에 H2를 연결했기 때문에 접속을 위한 설정 추가
    • H2 DB생성 및 연결 시 사용하는 값과 동일
    • 스프링부트 2.4부터는 spring.datasource.username=sa를 꼭 추가

JDBC 리포지토리 구현

public class JdbcMemberRepository implements MemberRepository{

	//javax.sql.DataSource 사용
    private final DataSource dataSource;
	
    public JdbcMemberRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }
  • JdbcMemberRepository 생성
  • spring.datasource는 DB 연결에 관한 예약된 key
    • driver-class-name, url, username, password 속성 필요
  • this.dataSource = dataSource를 통해 스프링 부트의 DBSource 주입
    • application.properties에서 세팅한 값을 통해 스프링 부트 DBSource 제작
@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(); // DB 연결
            // sql 전송
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, member.getName()); // sql의 첫번째 '?'에 대입
            pstmt.executeUpdate(); // DB에 데이터 전송
            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);
        }
    }
 // JdbcMemberRepositiory > Save 기능
  • pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);의 Statement.RETURN_GENERATED_KEYS는 기존 DB내에서 ID값을 자동으로 생성되도록 설정했으므로 Spring에서 값을 받기 위해 사용한 옵션
    • 즉 rs = pstmt.getGeneratedKeys();의 값을 받기 위해 사용
    • pstmt.executeUpdate();는 값을 받아올 수 없음
  • 연결한 기능을 모두 사용한 후 항상 close로 마무리
    • 계속 연결되어 있으면 오류 및 장애 발생
@Override
    public List<Member> findAll() {
        String sql = "select * from member";

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = getConnection(); // DB 연결
            pstmt = conn.prepareStatement(sql); // 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); // 연결 종료
        }
    }
 // JdbcMemberRepository > 조회(findAll) 기능
  • executeQuery();는 excuteUpdate()와 달리 쿼리 값을 받음

SpringConfig 설정

@Bean
public MemberRepository memberRepository(){
    //return new MemoryMemberRepository();
    return new JdbcMemberRepository(dataSource);
}
//SpringConfig.java
  • 컨테이너 안의 MemoryMemberRepository를 JdbcMemberRepository로 변경해 쉽게 전환 가능
    • 서비스-메모리(리)의 연결을 서비스-Jdbc(리)로 변경

돌려보기

  • H2 DB에서 넣어놨던 Member 테이블 값이 조회되는 것을 확인 가능
    • Spring과 DB가 잘 연결되어 있음을 확인 가능
  • 데이터 삽입 또한 잘되는 것을 확인할 수 있다.

3. 스프링 통합 테스트

코드 작성

 @SpringBootTest
 @Transactional
 class MemberServiceIntegrationTest {
    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;
    @Test
 	public void 회원가입() throws Exception {
 		//Given
 		Member member = new Member();
        member.setName("hello");
 		//When
 		Long saveId = memberService.join(member);
		//Then
 		Member findMember = memberRepository.findById(saveId).get();
 		assertEquals(member.getName(), findMember.getName());
    }
    @Test
 	public void 중복_회원_예외() throws Exception {
 		//Given
 		Member member1 = new Member();
        member1.setName("spring");
 		Member member2 = new Member();
        member2.setName("spring");
 		//When
        memberService.join(member1);
 		IllegalStateException e = assertThrows(IllegalStateException.class,
                () -> memberService.join(member2));//예외가 발생해야 한다.
 		assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
    }
 }
 
 // MemberServiceIntegrationTest.java
회원가입테스트 확인DB에 커밋 X
@Commit 후 실행DB에 커밋 O
  • @SpringBootTest는 스프링 컨테이너와 테스트 실행
    • JDBC를 이용하기 위해선 스프링 컨테이너 필요!
  • @Transactional는 독립적인 DB테스트 실행 가능
    • 테스트 시작 전에 트렌젝션을 시작하고 테스트 완료 후 항상 롤백
    • @Commit을 통해 커밋도 가능
profile
범수의 개발 놀이터😋

0개의 댓글