2022년 4월 12일 TIL

yshjft·2022년 4월 12일
0

데브코스 TIL

목록 보기
17/45

JDBC

  • Java Database Connectivity
  • 데이터베이스 연결 + SQL 실행 인터페이스

JDBC API, JDBC DB Driver

  • JDBC API가 DB Driver와 Connection 생성
  • JDBC API가 DB Driver에게 Query 요청

의존성

mysql-connector-java 필요

connection

try (
       var connection = DriverManager.getConnection(URL, USER, PASSWORD);
       var statement = connection.prepareStatement(SELECT_ALL_SQL);
       var resultSet = statement.executeQuery();
) {
	...
} catch (SQLException e) {
    logger.error("Got error while closing connection", e);
}
  • try-with-resources
    • Connection은 resource를 많이 잡아 먹기 때문에 반드시 close() 해주어야만 한다.
    • try-with-resources를 사용하면 일일히 connection을 닫을 필요가 없다. try 블록을 닫으면 connection, statement, resultSet에 대해 알아서 close가 호출된다.

SQL INJECTION

var SELECT_SQL = "select * from customers where name  = '" + name +"'";

문자열을 이용한 query는 SQL injection에 취약하다. 즉 query로 인해 의도하지 않은 조회 및 수정이 일어날 수 있다. 이러한 문제를 해결하기 위해 prepared statement를 사용해야 한다.

PREPARED STATEMENT

private final String PREPARE_SELECT_SQL = "select * from customers where name = ?";
  • 전달된 쿼리는 첫번째 수행에만 분석 → 컴파일 → 실행 단계를 거친다. 이후에는 캐시를 사용한다.
  • 컴파일로 인해 쿼리문이 수정될 수 없다.

CRUD

Create

try (
       var connection = DriverManager.getConnection(URL, USER, PASSWORD);"root1234!");
       var statement = connection.prepareStatement(INSERT_SQL);
) {
     statement.setBytes(1, customerId.toString().getBytes());
     statement.setString(2, name);
     statement.setString(3, email);
     return statement.executeUpdate();
} catch (SQLException e) {
    logger.error("Got error while closing connection", e);
}

입력 인덱스는 1부터 시작한다.

Read

try (
       var connection = DriverManager.getConnection(URL, USER, PASSWORD);
       var statement = connection.prepareStatement(PREPARE_SELECT_SQL);
) {
    statement.setString(1, name);

    try(var resultSet = statement.executeQuery()) {
        while(resultSet.next()) {
            var customerName = resultSet.getString("name");
            var customerId  = UUID.nameUUIDFromBytes(resultSet.getBytes("customer_id"));
            var createdAt = resultSet.getTimestamp("created_at").toLocalDateTime(); 
       
            logger.info("customer id -> {}, name -> {}, createdAt -> {}", customerId, customerName, createdAt);

        }
    }
} catch (SQLException e) {
   logger.error("Got error while closing connection", e);
}

시간은 LocalDatetime을 사용하자. 다만 resultSet.getTimestamp("created_at")에서 NULL이 나올 수도 있으니 조심하자!

Update

try (
        var connection = DriverManager.getConnection(URL, USER, PASSWORD);
        var statement = connection.prepareStatement(UPDATE_NAME_BY_ID_SQL);
) {
    statement.setString(1, name);
    statement.setBytes(2, customerId.toString().getBytes());

    return statement.executeUpdate();
} catch (SQLException e) {
    logger.error("Got error while closing connection", e);
}

Delete

try (
        var connection = DriverManager.getConnection(URL, USER, PASSWORD);
        var statement = connection.prepareStatement(DELETE_ALL);
) {
    return statement.executeUpdate();
} catch (SQLException e) {
    logger.error("Got error while closing connection", e);
}

UUID에 대하여

입력된 UUID와 조회된 UUID가 일치하지 않는 문제가 발생하였다.

  • 이유
    UUID 생성 버전이 달라서 발생한 문제이다.

    • nameUUIDFromByte type 3
    • randomUUID type 4
  • 해결

    • 생성
    var customerId = UUID.randomUUID();
    • 조회
    var customerId = toUUID(resultSet.getBytes("customer_id"));
    
    ...
    
     static UUID toUUID(byte[] bytes) {
         var byteBuffer = ByteBuffer.wrap(bytes);
         return new UUID(byteBuffer.getLong(), byteBuffer.getLong());
      }
profile
꾸준히 나아가자 🐢

0개의 댓글