Spring/팀스파르타

13. JDBC란 무엇일까?

열심히 해 2024. 10. 28. 15:09
  • 우리는 애플리케이션 서버에서 요청을 받고, 해당 요청을 처리하기 위해 데이터베이스와 소통을 해야합니다.
  • 따라서 실제로 서버가 데이터베이스와 어떠한 방법을 통해 소통하고 있는지 알아보려고 합니다.

 

 

JDBC의 등장배경

 

  • 애플리케이션 서버에서 DB에 접근 하기 위해서는 여러가지 작업이 필요합니다.
    1. DB에 연결하기 위해 커넥션을 연결해야합니다.
    2. SQL을 작성한 후 커넥션을 통해 SQL을 요청합니다.
    3. 요청한 SQL에 대한 결과를 응답 받습니다.

 

 

  • 기존에 사용하던 MySQL 서버를 PostgreSQL 서버로 변경한다면 무슨일이 발생할까요?
  • MySQL과 PostgreSQL은 커넥션을 연결하는 방법, SQL을 전달하는 방법, 결과를 응답받는 방법 모두 다를 수 있습니다.
  • 따라서 애플리케이션 서버에서 작성했던 DB 연결 로직들을 전부 수정해야합니다.

 

 

  • 이러한 문제를 해결하기위해 JDBC 표준 인터페이스가 등장했습니다.
  • JDBC는 Java Database Connectivity로 DB에 접근할 수 있도록 Java에서 제공하는 API입니다.
  • JDBC에 연결해야하는 DB의 JDBC 드라이버를 제공하면 DB 연결 로직을 변경할 필요없이 DB 변경이 가능합니다.
    • DB 회사들은 자신들의 DB에 맞도록 JDBC 인터페이스를 구현한 후 라이브러리로 제공하는데 이를 JDBC 드라이버라 부릅니다.
  • 따라서, MySQL 드라이버를 사용해 DB에 연결을 하다 PostgreSQL 서버로 변경이 필요할 때 드라이버만 교체하면 손쉽게 DB 변경이 가능합니다.

 

 

 

 

  • JDBC의 등장으로 손쉽게 DB교체가 가능해졌지만 아직도 DB에 연결하기 위해 여러가지 작업 로직들을 직접 작성해야한다는 불편함이 남았습니다.
  • 이러한 불편함을 해결하기 위해 커넥션 연결, statement 준비 및 실행, 커넥션 종료 등의 반복적이고 중복되는 작업들을 대신 처리해주는 JdbcTemplate이 등장했습니다.

 

 

 

JdbcTemplate 사용방법

 

 

  • application.properties에 DB에 접근하기 위한 정보를 작성
spring.datasource.url=jdbc:mysql://localhost:3306/memo
spring.datasource.username={id}
spring.datasource.password={비밀번호}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

 

 

  • build.gradle에 JDBC, MySQL 의존성 추가
// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'

 

 

  • DB연결이 필요한 곳에서 JdbcTemplate을 주입받아와 사용합니다.
private final JdbcTemplate jdbctemplate;

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

 

 

 

@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(); // 기본 키를 반환받기 위한 객체
        // jdbcTemplate 의 update 메서드 : update, insert, delete 문
        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";

        // query 메서드 : select 문.
        return jdbcTemplate.query(sql, new RowMapper<MemoResponseDto>() {
            @Override
            public MemoResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                // SQL 의 결과로 받아온 Memo 데이터들을 MemoResponseDto 타입으로 변환해줄 메서드
                // get 뒤에 타입. ()에 컬럼명.
                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 > 팀스파르타' 카테고리의 다른 글

15. IoC와 DI  (1) 2024.10.30
14. 3 Layer Architecture  (1) 2024.10.28
12. Database란 무엇일까?  (0) 2024.10.27
11. DTO란 무엇일까?  (0) 2024.10.27
10. HTTP 데이터를 객체로 처리하는 방법  (1) 2024.10.27