
JDBC(Java Database Connectivity)는 자바 프로그램이 데이터베이스와 통신할 수 있도록 해주는 표준 인터페이스(규약)이다. 쉽게 말해, 자바와 DB의 통역사 역할을 한다고 보면 된다.
키워드
DB 드러이버 등록 + Connection 생성 담당
connection 객체를 만들어 주는 클래스Class.forName("com.mysql.cj.jdbc.Driver");Class.forName() : 풀클래스명으로 클래스를 메모리에 올림(동적 로딩)Connection 얻기Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost/{DB이름}"{,
"username}",
"password}"
);DriverManager는 직접 new를 못하고 getConnection 만 사용한다.키워드
설정 파일로 DB configuration을 메인 파일과 분리한다.
jdbc-config.properties 예시
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost/{DB이름}
user={username}
password={password}
사용 코드
Properties prop = new Properties();
prop.load(new FileReader("jdbc-config.properties"));
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String password = prop.getProperty("password");
Class.forName(driver);
Connection con = DriverManager.getConnection(url, user, password);
키워드
DB와의 연결(세션) + Statement/PreparedStatement 생성
createConnection(), prepareStatement()로 쿼리 실행 객체 생성Connection con = DriverManager.getConnection(...);
Statement stmt = con.createStatement();
OR
PreparedStatement pstmt = con.prepareStatement(sql);if (con != null && !con.isClosed()) {
con.close();
}키워드
반복되는 코드들 모아서 재사용. Connection, close, commit/rollback 의 try/catch 문 등 작성하기 귀찮은 코드 여기에서 메소드화.
예시 코드
public class JDBCTemplate {
public static Connection getConnection(){
Properties prop = new Properties();
Connection con = null;
try {
prop.load(new FileReader(
"src/main/java/com/ohgiraffers/crud/config/jdbc-info.properties"
));
String url = prop.getProperty("url");
con = DriverManager.getConnection(url, prop);
// 자동 커밋 설정을 수동 커밋 설정으로 변경하여 서비스에서 트랜잭션을 컨트롤 할 수 있도록해준다.
con.setAutoCommit(false);
} catch (IOException | SQLException e) {
throw new RuntimeException(e);
}
return con;
}
/* Connection을 닫는 개념은 별도의 메소드로 분리하고 실제 담는 시점은 Service 계층에서 진행 */
public static void close(Connection con){
try {
if(con != null && !con.isClosed()) con.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void close(Statement stmt){
try {
if(stmt != null && !stmt.isClosed()) stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void close(ResultSet rset){
try {
if(rset != null && !rset.isClosed()) rset.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void commit(Connection con){
try {
if(con != null && !con.isClosed()) con.commit();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void rollback(Connection con){
try {
if(con != null && !con.isClosed()) con.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
핵심
setAutoCommit(false) 자동커밋(비활성화)commit() / rollback() 직접 호출키워드
완성된 SQL 문자열 실행. 정적 쿼리문 다루며, Injection 공격에 취약하다는 단점을 가짐
Statement stmt con.createStatement();String query = SELECT id, last_name FROM empdb";
ResultSet rset = stmt.executeQuery(query);
while(rset.next) {
System.out.prinln(rset.getString("id") + ", " + rset.getString("last_name"));
}executeUpdate)String query = "UPDATE empdb SET last_name = "KIM" WHERE id = '10000'";
int result = stmt.executeUpdate(query);키워드
미완성 쿼리(동적 쿼리) + ? placeholder. 빠르고 안전함
PreparedStatement도 Statement의 한 종류? 로 플레이스홀더 사용String sql = "INSERT INTO member VALUES(?, ?)";PreparedStatement pstmt = con.prepareStatement(sql);pstmt.setString(1, id);
pstmt.setString(2, password);executeQuery()executeUpdate()int result = pstmt.executeUpdate();Statement 사용(취약)
String empId = "200";
String empName = "' or 1=1 and emp_id = '200";
String query = "SELECT * FROM employee WHERE emp_id = '"
+ empId + "'and emp_name = '" + empName + "'";
PreparedStatement 사용
문자열 안에 ' 있어도 값으로 취급하여 공격 실패
키워드
SELECT 결과 집합
rset.next();↳ 다음 행으로 커서 이동, 있으면 true반환rset.getString("emp_id");↳ 현재 행의 컬럼 값rset.getInt("menu_price");↳ 자료형에 맞는 getXXX()while(rset.next()) {
System.out.println(
rset.getStirng("emp_id") + ", " +
rset.getString("emp_name")
);
}rset.close()로 자원 반납키워드
SELECT 는 ResultSet, 나머지는 int, commit/rollback
ResultSet rset = pstmt.executeQuery();int result = pstmt.executeUpdate();
// 성공한 행의 수 반환Connection con = getConnection();
int result = repoistory.insertMent(con, menu);
if (result > 0) {
commit(con);
} else {
rollback(con);
}
close(con);MenuMapper.xml) <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment/>
<entry key="insertMenu">
INSERT
INTO tbl_menu
(menu_name, menu_price, category_code, orderable_status)
VALUES
(?, ?, ?, ?)
</entry>
</properties>
Repository에서 위 xml 파일 꺼내와 사용
Properties prop = new Properties();
prop.loadFromXML(new FileInputStream(".../MenuMapper.xml"));
String sql = prop.getProperty("insertMenu");
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, menu.getMenuName());
pstmt.setInt(2, menu.getMenuPrice());
pstmt.setInt(3, menu.getMenuCode());
pstmt.setString(4, menu.getOrderableStatus());
int result = pstmt.executeUpdate();
키워드: 역할 분리
main, 콘솔 입력/출력)Connection 생성commit / rollbackPreparedStatement, ResultSet 사용드라이버 로딩 → Connection 획득 → Statement/PreparedStatement 생성 → SQL 실행 → ResultSet 처리 → 트랜잭션 처리(commit/rollback) → 자원 반납(close)