Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

블로그 이름 뭐하지

[Spring] JDBC 본문

Spring

[Spring] JDBC

가는말이고우면오는말은come 2024. 9. 30. 20:05

어플리케이션 서버에서 DB로 접근하기 위해서는 여러 작업이 필요하다.

1) DB에 연결하기 위해 커넥션을 연결한다.

2) SQL을 작성 한 후 커넥션을 통해 SQL을 요청한다.

3) 요청한 SQL에 대한 결과를 응답받는다.

 

하지만 이 경우, 기존에 사용하던 MySQL 서버에서 PostgreSQL 서버로 변경할 시

DB 연결 로직들을 모두 수정해야하는 문제가 생긴다.

이런 문제를 해결하기 위해 등장한 것이 JDBC이다.

Java Database Connectivity의 약자로 DB에 접근할 수 있도록 자바에서 제공하는 API이다.

JDBC에 연결하는 DB의 JDBC드라이버를 제공하면 연결로직을 변경할 필요 없이 DB변경이 가능하다.

즉 드라이버만 교체하면 DB 변경이 가능하다.

 

JDBC로 손쉽게 DB 연결이 가능해졌지만 여전히 DB에 연결하기 위해 로직을 직접 작성해야했다.

이를 해결하기 위해 커넥션 연결, statement 준비 및 실행, 커넥션 종료 등의

반복적인 작업을 대신 처리해주는 JDBCTemplate가 등장했다.

 

JDBCTemplate 사용방법

1.application.properties 에 DB에 접근하기 위한 정보 작성

spring.datasource.url=jdbc:mysql://localhost:3306/memo
spring.datasource.username=root
spring.datasource.password={비밀번호}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

 

2. build.gradle에 JDBC 라이브러리와 MySQL 등록

// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'

 

3. intelliJ DB 연동

 

1) CREATE DATABASE memo;

 

2) Database 탭 선택 후 + 버튼

3) Data Source > MySQL

4) User, Password, Database 정보 추가 후 Ok 하면 완료된다.

 

DB 연결이 필요한 곳에서 JDBCTemplete를 주입받아와 사용한다.

private final JdbcTemplate jdbctemplate;

public MemoRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
}

 

생성자의 파라미터를 통해 JDBCTemplate 객체가 자동으로 넘어와 JDBCTemplate 변수에 저장된다.

 

INSERT

String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
jdbcTemplate.update(sql, "Robbie", "오늘 하루도 화이팅!");

Insert 쿼리를 작성하여 String 변수에 저장한다.

넣고자 하는 데이터에 ?를 사용하면 유동적으로 데이터를 넣어줄 수 있다.

jdbcTemplate.update() 메서드는 insert, update, delete와 함께 생성, 수정 삭제에 사용된다.

첫번째 파라미터로 SQL문을 받고 그 이후에는 ?에 들어갈 값을 받는다.

 

UPDATE

String sql = "UPDATE memo SET username = ? WHERE id = ?";
jdbcTemplate.update(sql, "Robbert", 1);

Update 쿼리를 작성하여 String 변수에 저장 후, update() 메서드 첫번째 파라미터에 넣어준다.

 

DELETE

String sql = "DELETE FROM memo WHERE id = ?";
jdbcTemplate.update(sql, 1);

Delete 쿼리를 작성해 String 변수에 저장한 후 update() 메서드 첫번째 파라미터에 넣어준다.

 

SELECT

String sql = "SELECT * FROM memo";
return jdbcTemplate.query(sql, new RowMapper<MemoResponseDto>() {
    @Override
    public MemoResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
        // SQL 의 결과로 받아온 Memo 데이터들을 MemoResponseDto 타입으로 변환해줄 메서드
        Long id = rs.getLong("id");
        String username = rs.getString("username");
        String contents = rs.getString("contents");
        return new MemoResponseDto(id, username, contents);
    }
});

select 쿼리를 작성해 String 변수에 저장한 후 query() 메서드 첫번째 파라미터에 넣어준다.

select의 경우 결과가 여러 줄로 넘어오기 때문에 RowMapper를 사용해 한 줄 씩 처리한다.

RowMapper는 인터페이스 이므로 익명클래스를 구현하여 처리한다.

오버라이딩 된 mapRow 메서드는 제네릭에 선언한 MemoResponseDTO 타입으로

데이터 한 줄을 변환하는 작업을 시행한다.

 

JDBC를 적용한 MemoController

package com.sparta.memo.controller;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.web.bind.annotation.*;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

@RestController
@RequestMapping("/api")
public class MemoController {

    private final JdbcTemplate jdbcTemplate;

    public MemoController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);

        // DB 저장
        KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체

        String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
        jdbcTemplate.update( con -> {
                    PreparedStatement preparedStatement = con.prepareStatement(sql,
                            Statement.RETURN_GENERATED_KEYS);

                    preparedStatement.setString(1, memo.getUsername());
                    preparedStatement.setString(2, memo.getContents());
                    return preparedStatement;
                },
                keyHolder);

        // DB Insert 후 받아온 기본키 확인
        Long id = keyHolder.getKey().longValue();
        memo.setId(id);

        // Entity -> ResponseDto
        MemoResponseDto memoResponseDto = new MemoResponseDto(memo);

        return memoResponseDto;
    }

    @GetMapping("/memos")
    public List<MemoResponseDto> getMemos() {
        // DB 조회
        String sql = "SELECT * FROM memo";

        return jdbcTemplate.query(sql, new RowMapper<MemoResponseDto>() {
            @Override
            public MemoResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                // SQL 의 결과로 받아온 Memo 데이터들을 MemoResponseDto 타입으로 변환해줄 메서드
                Long id = rs.getLong("id");
                String username = rs.getString("username");
                String contents = rs.getString("contents");
                return new MemoResponseDto(id, username, contents);
            }
        });
    }

    @PutMapping("/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = findById(id);
        if(memo != null) {
            // memo 내용 수정
            String sql = "UPDATE memo SET username = ?, contents = ? WHERE id = ?";
            jdbcTemplate.update(sql, requestDto.getUsername(), requestDto.getContents(), id);

            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }

    @DeleteMapping("/memos/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = findById(id);
        if(memo != null) {
						// memo 삭제
            String sql = "DELETE FROM memo WHERE id = ?";
            jdbcTemplate.update(sql, id);

            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }

    private Memo findById(Long id) {
        // DB 조회
        String sql = "SELECT * FROM memo WHERE id = ?";

        return jdbcTemplate.query(sql, resultSet -> {
            if(resultSet.next()) {
                Memo memo = new Memo();
                memo.setUsername(resultSet.getString("username"));
                memo.setContents(resultSet.getString("contents"));
                return memo;
            } else {
                return null;
            }
        }, id);
    }
}

'Spring' 카테고리의 다른 글

[Spring] IOC(제어의 역전)와 DI(의존성 주입)  (0) 2024.10.10
[Spring] 3 Layer Architecture  (0) 2024.10.10
[Spring] Spring MVC  (0) 2024.09.30
[Spring] application.properties  (0) 2024.09.29
[Spring] Lombok  (0) 2024.09.29