waveofmymind
기록하는 습관
waveofmymind
전체 방문자
오늘
어제
  • 분류 전체보기 (124)
    • 📝 정리 (5)
    • 🌊TIL (9)
    • 💻CS (1)
      • 자료구조 (1)
    • 📙Language (9)
      • ☕Java (6)
      • 🤖Kotlin (3)
    • 🍃Spring (28)
    • 👨🏻‍💻알고리즘 (67)
      • 프로그래머스 (59)
      • 백준 (3)
    • 👷DevOps (4)
      • 🐳Docker (2)
      • 🤵Jenkins (1)

블로그 메뉴

  • 홈
  • Spring
  • Java
  • 알고리즘

공지사항

인기 글

태그

  • 스프링
  • CORS
  • chat GPT
  • 챗GPT
  • LeetCode
  • spring
  • til
  • sql
  • 힙
  • 코틀린
  • spring boot
  • 트랜잭션
  • 스택
  • JDBC
  • AOP
  • Connection
  • Open AI
  • kotlin
  • 통합테스트
  • 다이나믹 프로그래밍
  • 완전탐색
  • 스프링 부트
  • 스프링 시큐리티
  • Spring Security
  • 트랜잭션 전파
  • mybatis
  • kotest
  • resultset
  • BFS
  • SpringAOP

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
waveofmymind

기록하는 습관

[JDBC] 동적으로 파라미터 바인딩 하기
📙Language/☕Java

[JDBC] 동적으로 파라미터 바인딩 하기

2023. 4. 26. 09:54

SQL 매퍼를 구현 중 파라미터 수가 정해지지 않고 동적으로 입력을 받을 때, SQL문에 파라미터를 바인딩하는 경험을 적어보려고 한다.

 

우선 그전에 바인딩할 인자가 없을 때, 즉 SQL문을 바로 실행시켜도 괜찮을 때는 아래와 같은 함수를 사용했다.

바인딩이 필요 없는 쿼리 실행

public void run(String sql) {
        try (Connection conn = DriverManager.getConnection(url, username, password);
             Statement stmt = conn.createStatement()) {

            if (devMode) {
                System.out.println(sql);
            }

            stmt.executeUpdate(sql);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

실행 순서는 아래와 같다.

  1. DB 정보로 커넥션을 생성한다.
  2. 생성한 커넥션에 쿼리를 실행시키기 위해 Statement를 생성한다.
  3. 만약 devMode가 true이면 인자로 받은 sql을 화면에 출력한다.
  4. 쿼리를 실행한다.

위에서 try-with-resources문을 사용했기 때문에 자원이 자동적으로 종료되므로, 일일이 close()는 하지 않아도 된다.

바인딩이 필요한 쿼리 실행

나는 쿼리를 받을 때, append를 사용했는데, 이는 SQL을 String으로, 바인딩 값이 필요하다면 매개변수 배열을 인자로 받는 메서드이다.

public Sql append(String sqlPart, Object... params) {
        sqlBuilder.append(" ").append(sqlPart);
        parameters.addAll(Arrays.asList(params));
        return this;
    }

위에서 인자로 받은 SQL과 params를 Sql 클래스에 저장해 둔다.

@Data
public class Sql {
    private final StringBuilder sqlBuilder;
    private final List<Object> parameters;
    private final SimpleDb simpleDb;
    
    //...
    }

또한, append로 SQL문을 넘길 때는 SQL Injection을 막기 위해, 동적 쿼리를 실행할 때에는, 값을 바로 쿼리문에 포함해서 넘기지 않고, 

PreparedStatement에는 바인딩 타깃을?로 넘기고, set타입()을 통해 값을 바인딩해 준다.

 

이때 바인딩하는 인덱스 값이 1부터 시작이라는 것을 주의해야 한다.

public long insert() {
        try (Connection conn = DriverManager.getConnection(simpleDb.getUrl(), simpleDb.getUsername(), simpleDb.getPassword());
             PreparedStatement pstmt = conn.prepareStatement(getSql(), Statement.RETURN_GENERATED_KEYS)) {

            if (getSimpleDb().isDevMode()) {
                System.out.println(getSql());
            }

            Object[] parameters = getParameters();
            for (int i = 0; i < parameters.length; i++) {
                pstmt.setObject(i + 1, parameters[i]);
            }

            pstmt.executeUpdate();
            ResultSet rs = pstmt.getGeneratedKeys();

            if (rs.next()) {
                return rs.getLong(1);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return -1;
    }

동작 순서는 아래와 같다.

  1. 커넥션을 생성한다.
  2. 쿼리를 실행하기 위해 prepaedStatement()를 실행한다.
  3. getParameters()로 바인딩할 파라미터를 받고, 파라미터의 수만큼 set타입을 해준다.
  4. 쿼리를 실행
  5. 아까 preparedStatement()를 실행할 때 두 번째 인자로 RETURN_GENERATED_KEYS를 넘겼는데, 이는 1만큼 증가한 키 값을 
    ResultSet 타입으로 반환해 준다.

 

'📙Language > ☕Java' 카테고리의 다른 글

[Java] url 변수 동적으로 받도록 개선하기  (0) 2023.05.18
[트러블 슈팅] url 패턴이 같을 경우에 Controller 메서드 구별하기  (0) 2023.05.17
[JDBC] Connection과 PreparedStatement, 그리고 ResultSet  (0) 2023.04.24
[Java] 스트림(Stream) 잘 사용하기  (0) 2023.03.15
[Java] Jackson Databind 이용하기  (0) 2023.03.02
    '📙Language/☕Java' 카테고리의 다른 글
    • [Java] url 변수 동적으로 받도록 개선하기
    • [트러블 슈팅] url 패턴이 같을 경우에 Controller 메서드 구별하기
    • [JDBC] Connection과 PreparedStatement, 그리고 ResultSet
    • [Java] 스트림(Stream) 잘 사용하기
    waveofmymind
    waveofmymind

    티스토리툴바