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();
}
}
실행 순서는 아래와 같다.
- DB 정보로 커넥션을 생성한다.
- 생성한 커넥션에 쿼리를 실행시키기 위해 Statement를 생성한다.
- 만약 devMode가 true이면 인자로 받은 sql을 화면에 출력한다.
- 쿼리를 실행한다.
위에서 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;
}
동작 순서는 아래와 같다.
- 커넥션을 생성한다.
- 쿼리를 실행하기 위해 prepaedStatement()를 실행한다.
- getParameters()로 바인딩할 파라미터를 받고, 파라미터의 수만큼 set타입을 해준다.
- 쿼리를 실행
- 아까 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 |