Prepared Statement 란?
SQL Query 를 실행하기 전에 미리 컴파일하고
실행 시에만 파라미터를 전달하는 방식의 SQL 문
<Prepared Statement 의 이점들>
성능 향상
처음 실행될 때 DB 에 의해 컴파일되고
서버 측에 캐싱하여 같은 Query 가 재사용될 때
재컴파일 하지 않고 이미 컴파일된 Query 를 재활용하므로
반복된 Query 실행 시 컴파일 시간이 단축된다
SQL Injection 방지
파라미터를 '?' 로 표시하고 실행 시점에 값을 바인딩하는 특성상
문자열의 일부로 해석되지 않고 별도의 값으로 처리되기에
사용자의 입력이 쿼리의 논리를 변경할 수 없으므로
SQL Injection 을 차단하는데 효과적이다
입력 데이터의 자동 변환
지정된 데이터 타입에 따라 자동으로
입력값을 변환하는 특성을 가지고 있다
예시로 setInt() 나 setString() 메소드 등을 이용하여
적절한 데이터 타입으로 값을 설정할 수 있어 간편하다
가독성 및 유지 보수성 향상
Query 와 Java 코드가 분리되어 있으므로
둘 사이의 상호작용을 명확하게 이해할 수 있고
Query 수정이 필요한 경우 SQL 구문만 수정하면 되기에
유지보수 측면에서 용이하며 코드를 읽고 이해하기 수월하다
<Prepared Statement 사용 방법>
#1 쿼리 준비
Query 를 작성할 때 나중에 실제 값이
들어갈 위치에 플레이스홀더('?')를 넣어준다
#2 PreparedStatement 생성
DB Connection 을 사용하여
Prepared Statement 객체를 생성한다
#3 파라미터 생성
'setXXX()' 메소드를 사용하여
각 플레이스홀더에 해당하는 값을 설정한다
(ex: setInt(), setString(), etc...)
#4 Query 실행
executeQuery 나 excecuteUpdate 을 사용하여 Query 를 실행한다
executeQuery: ResultSet 객체를 반환하며, 해당 객체를 통해 쿼리의 결과 데이터에 접근할 수 있다. 주로 SELECT 에 사용
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM table_name WHERE column = ?");
pstmt.setString(1, "value");
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
// 후처리 로직
}
excecuteUpdate: int 값을 반환하며, 쿼리에 의해 영향을 받은 행의 수를 받는다. 주로 INSERT, UPDATE, DELETE 에 사용
PreparedStatement pstmt = conn.prepareStatement("UPDATE table_name SET column1 = ? WHERE column2 = ?");
pstmt.setString(1, "valueOne");
pstmt.setString(2, "valueTwo");
int affectedRows = pstmt.executeUpdate(); // 2
#5 결과 처리
Query 실행 후 후처리 로직을 수행한다
(SELECT 의 경우, ResultSet 을 통해 결과를 처리)
#6 자원 해제
사용이 끝난 후에는 자원을 닫고 해제한다
<Prepared Statement 코드 예시>
PreparedStatement + JDBC 사용 예시
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:yourDBMS://yourUrl", "username", "password");
String sql = "SELECT * FROM users WHERE id = ? AND username = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1); // 첫 번째 플레이스홀더에 값을 1 로 설정
pstmt.setString(2, "Peter"); // 두 번째 플레이스홀더에 값을 Peter 로 설정
rs = pstmt.executeQuery();
while (rs.next()) {
String name = rs.getString("name");
// 다른 칼럼 및 후처리 로직...
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) try { rs.close(); } catch (SQLException e) {}
if (pstmt != null) try { pstmt.close(); } catch (SQLException e) {}
if (conn != null) try { conn.close(); } catch (SQLException e) {}
}
PreparedStatement + mybatis 사용 예시
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUser(1, "Peter");
// 후처리 로직
} finally {
sqlSession.close();
}
// mybatis
<mapper namespace="example.mapper.UserMapper">
<select id="selectUser" resultType="example.User">
SELECT * FROM users WHERE id = #{id} AND username = #{username}
</select>
</mapper>
<주의사항>
예외 처리
SQL 실행 중 발생할 수 있는 예외를 적절히 처리해야 한다
일반적으로 try-catch 블록을 사용하여 SQLException 을 처리한다
자원 해제
PreparedStatement, ResultSet, Connection 등을
사용 후에는 반드시 자원을 닫고 해제해야 한다
자원 해제는 finally 에서 닫고 해제하는 것이 일반적이다
'Computing and DB 🖥 > Database' 카테고리의 다른 글
SQL Injection (0) | 2024.02.02 |
---|---|
DB LOCK 에 대해서 알아보자 (0) | 2024.01.23 |
NoSql 이 뭐야? (0) | 2023.12.12 |
대용량을 대비한 DB... 어떻게 설정해? (feat.PostgreSQL) (33) | 2023.11.14 |