앞선 예제에서 구축하고 연결한 JDBC에서 SQL 구문을 작성해 DB를 조작하는 것이 가능한데요.
이번 섹션에는 다음 강의를 토대로 데이터를 저장하는 방법을 좀 더 심도 있게 알아볼까 합니다.
앞서 존재하는 테이블에 값을 입력하려면 다음과 같은 sql 구문으로 작성할 수 있는데요.
INSERT INTO users (userid, username, userpassword, userage, useremail) VALUES ('winter', '한겨울', '12345', 25, 'winter@mycompany.com')
이걸 JDBC에서 작성하면 다음과 같습니다.
String sql = new Stringbuilder() .append("INSERT INTO users (userid, username, userpassword, userage, useremail)") .append("VALUES ('winter', '한겨울', '12345', 25, 'winter@mycompany.com')") .toString();
이때 Stringbuilder() 생성자가 사용된 이유는 불변 객체인 String에 비해 가변이 가능한 효율적인 빌더 클래스이므로 해당 생성자를 사용했으며 toString으로 해당 객체(버퍼)의 값을 직접 참조하는 방식이 아닌 String 타입으로 변환하도록 합니다. (어차피 할당 변수도 String이니...)
아무튼 위와 같이 작성이 가능한데, 문제는 저렇게 VALUES 영역의 코드를 직접 작성한다면 나중에 코드를 재활용 할 수 없겠죠? 그래서 쿼리로 대체해 줍니다.
"VALUES (?, ?, ?, ?, ?)"
그 다음 해당 문장을 실행해줄 준비를 하는데요. PreparedStatement 인터페이스를 다음과 같이 호출해주면 됩니다.
PreparedStatement pstmt = conn.prepareStatement(sql);
이제 쿼리 값들(VALUES(?,?,?,?,?))을 직접 넣어주면 됩니다. 이때 지정 될 수 없는 타입들은 SEQ_BNO(일렬 번호), NEXTVAL(자동 증답값), SYSDATE(시스템 날째)인데, 즉 사용자가 값을 넣을 수 없는 것들을 제외한 데이터 타입은 값으로 지정할 수 있다는 의미인 셈이죠.
또한 지정되어 있는 속성들의 순서는 위 sql 별수에서 넣어줬던 테이블 칼럼순 대로 작성해야 합니다. (원본 테이블과 순서가 달라도 작성한 스트링 쿼리문 순서로 작성해야 합니다.)
// 첫번째 속성(1)에 문자 형식의 값("winter") 을 문자열로서(setString) 삽입하겠음을 의미. (이하 동문) pstmt.setString(1, "real_re_go"); pstmt.setString(2, "박종민"); pstmt.setString(3, "1010201"); pstmt.setInt(4, 30); pstmt.setString(5, "www.re_go_tube.com");
해당 sql문의 실행이 끝나면 close() 메서드로 연결된 메모리를 해제합니다.
pstmt.close();
해당 코드를 조합해보면 완성 되는 데이터 입력문은 다음과 같습니다.
package thisisjava; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class ConnectionExample { public static void main(String[] args) { Connection conn = null; try { Class.forName("oracle.jdbc.OracleDriver"); conn = DriverManager.getConnection( "jdbc:oracle:thin:@localhost:1521/orcl", "java", "oracle" ); System.out.println("연결 성공"); String sql = new StringBuilder().append("INSERT INTO users (userid, username, userpassword, userage, useremail)").append("VALUES (?, ?, ?, ?, ?)").toString(); PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, "real_re_go"); pstmt.setString(2, "박종민"); pstmt.setString(3, "1010201"); pstmt.setInt(4, 30); pstmt.setString(5, "www.re_go_tube.com"); pstmt.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { if(conn != null) { try { conn.close(); System.out.println("연결 끊기"); } catch (SQLException e) {} } } } }
마지막으로 실제 실행 된 쿼리문을 반영하기 위해 다음 명령어를 실행하면 되는데요.
int reflectInsert = pstmt.executeUpdate(); System.out.println("생성된 라인은" + reflectInsert + "줄입니다.");
excuteUpdate() 메서드는 해당 쿼리문이 DML (SELECT, INSERT, UPDATE, DELETE)일 경우 사용되는데, 반환값은 반영된 라인 (테이블의 행)만큼의 수를 반영하고, 반영된(에러가 아닌) 라인이 없다면 0을 반환합니다.
데이터를 조회하는 법, 즉 DB의 테이블 중 특정 칼럼의 정보를 가져오는 법을 알아보겠습니다.
우선 데이터를 가져올 테이블의 컬럼 타입들을 정의해 줍니다.
// Data 어노테이션을 임포트 하기 위한 임포트문 import lombok.Data; // Data 어노테이션은 데이터 객체를 만들 때 getter,setter,toString 메서드를 자동 생성시켜주도록 하는 인터페이스 입니다. @Data public class User{ private String userId; private String userName; private String userPassword; private int userAge; private String userEmail; }
참고로 위에서 주석으로 설명드린 lombok을 사용하는 방법은, 기존 프로젝트를 Maven 프로젝트로 전환한 후 lombok.jar 파일을 다운 받아 lib 폴더에 저장한 후 디펜던시를 추가해주고 롬복을 설치한 뒤 사용하면 됩니다.
다시 코드로 돌아와서, 그 다음 userId를 기반으로 가져올 사용자의 정보를 SELECT문으로 작성합니다.
- SQL문
SELECT userid, username, userpasssword, userage, useremail FROM users WHERE userid = 'real_re_go';
- Java문
String selectSql = "SELECT userid, username, userpassword, userage, useremail " + "FROM users " + "WHERE userid = ?";
그 다음 해당 작성된 sql문을 PreparedStatement 메서드로 해당 쿼리문(selectSql)을 연결하여(conn) 실행할 준비를 하고 테이블 밸류에 값을 지정해 줍니다.
stmt = conn.prepareStatement(selectSql); pstmt.setString(1, "real_re_go");
그 다음으로 ResultSet 인터페이스를 선언하여 쿼리를 실행한 결과값 (executeQuery)을 저장하는데요. 이때 사용되는 ResultSet은 쿼리문을 실행한 결과값을 받아오는 역할을 수행합니다.
ResultSet rs = pstmt.executeQuery();
이제 받아온 객체를 테이블 형태로 보관 중인 rs 변수에 next() 메서드를 호출하여 해당 테이블을 순회하도록 하는데요. 1개의 칼럼 정보를 읽어올때는 if를 사용하고, 1개 초과(n개 이상)의 정보를 읽어올때는 while를 사용합니다.
if(rs.next()){ // 앞서 생성한 User 클래스의 인스턴스 생성 User user = new User(); // User 클래스의 필드에 불러온 DB 정보들을 하나씩 저장 (setter는 @Data 어노테이션에 의해 자동생성 되었으므로 메서드를 호출하기만 하면 됨) user.setUserId(rs.getString("userId")); user.setUserName(rs.getString("userName")); user.setUserPassword(rs.getString("userPassword")); user.setUserAge(rs.getInt(4)); user.setUserEmail(rs.getString(5)); // 저장한 사용자 정보를 출력 System.out.println("사용자 정보:"); System.out.println("ID: " + user.getUserId()); System.out.println("이름: " + user.getUserName()); System.out.println("비밀번호: " + user.getUserPassword()); System.out.println("나이: " + user.getUserAge()); System.out.println("이메일: " + user.getUserEmail()); }else{ // else문은 정보를 찾지 못했을 때, 즉 사용자 정보를 찾지 못했을 때 실행할 코드를 작성해주면 됩니다. System.out.println("사용자가 존재하지 않습니다.") }
이때 위의 코드를 보면 rs.getString의 매개변수로 문자열과 숫자를 넣어줬는데요. 문자열로 넣게 되면 해당 문자열과 일치하는 테이블 칼럼의 값을 가져오고, 숫자(정수) 로 전달할 경우 순번의 칼럼과 일치하는 테이블 카럶의 값을 가져오게 됩니다.
ResultSet
위의 예제문에서 특정 테이블 요소의 특정 속성을 수정하는 방법을 sql 문법으로 알아보자면
UPDATE users SET username = '수정된 사용자 이름', userpassword = '새로운암호', userage = 30, useremail = 'new_email@mycompany.com' WHERE userid = 'real_re_go';
이를 JDBC에서 작성할 때는 다음과 같이 작성하면 됩니다.
String updateSql = "UPDATE users SET username = ?, userpassword = ?, userage = ? WHERE userid = ?"; pstmt = conn.prepareStatement(updateSql); pstmt.setString(1, "comes_new_challanger"); pstmt.setString(2, "123-4567-8910"); pstmt.setInt(3, 19); pstmt.setString(4, "winter"); System.out.println("수정 완료"); int reflectUpdate = pstmt.executeUpdate(); System.out.println("수정된 라인은" + reflectUpdate + "줄입니다.");
그리고 사용된 메모리를 때마다 해제 해주기 위해 pstmt.close();
코드를 실행할수도 있으나, try-catch문 안에 작성된 해당 JDBC 코드들은 try-catch문이 종료되면 알아서 메모리가 해제되기 때문에
try-catch문 외에 있는 영역의 특정 메모리를 해제하고 싶다면 close() 메서드를 호출하면 됩니다. (애초에 JDBC가 try-catch문에 작성되는 경우가 많아서 굳이 명시하지 않아도 됩니다.)
위의 예제문에서 특정 테이블 요소를 특정 속성을 수정하는 방법을 sql 문법으로 알아보자면
DELETE FROM users WHERE userid = "winter";
이걸 JDBC에 작성하면 다음과 같이 작성할 수 있는데요.
String deleteSql = "DELETE FROM users " + "WHERE userid = ?"; pstmt = conn.prepareStatement(deleteSql); pstmt.setString(1, "winter"); int reflectDelete = pstmt.executeUpdate(); System.out.println("삭제된 행 수는 " + reflectDelete + "행입니다."));
다음 코드는 앞서 소개한 입력,조회, 수정, 삭제를 합친 코드문은 다음과 같습니다. (참고용)
package thisisjava; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import lombok.Data; public class ConnectionExample { @Data static class User{ private String userId; private String userName; private String userPassword; private int userAge; private String userEmail; } public static void main(String[] args) { Connection conn = null; try { Class.forName("oracle.jdbc.OracleDriver"); conn = DriverManager.getConnection( "jdbc:oracle:thin:@localhost:1521/orcl", "java", "oracle" ); System.out.println("연결 성공"); String insertSql = new StringBuilder().append("INSERT INTO users (userid, username, userpassword, userage, useremail)").append("VALUES (?, ?, ?, ?, ?)").toString(); PreparedStatement pstmt = conn.prepareStatement(insertSql); pstmt.setString(1, "real_re_go"); pstmt.setString(2, "박종민"); pstmt.setString(3, "1010201"); pstmt.setInt(4, 30); pstmt.setString(5, "www.re_go_tube.com"); System.out.println("입력 완료"); String updateSql = "UPDATE users SET username = ?, userpassword = ?, userage = ? WHERE userid = ?"; pstmt = conn.prepareStatement(updateSql); pstmt.setString(1, "comes_new_challanger"); pstmt.setString(2, "123-4567-8910"); pstmt.setInt(3, 19); pstmt.setString(4, "winter"); System.out.println("수정 완료"); int reflectUpdate = pstmt.executeUpdate(); System.out.println("수정된 라인은" + reflectUpdate + "줄입니다."); String deleteSql = "DELETE FROM users " + "WHERE userid = ?"; pstmt = conn.prepareStatement(deleteSql); pstmt.setString(1, "winter"); System.out.println("삭제 완료"); int reflectDelete = pstmt.executeUpdate(); System.out.println("삭제된 행 수는 " + reflectDelete + "행입니다."); String selectSql = "SELECT userid, username, userpassword, userage, useremail " + "FROM users " + "WHERE userid = ?"; pstmt = conn.prepareStatement(selectSql); pstmt.setString(1, "real_re_go"); ResultSet rs = pstmt.executeQuery(); if(rs.next()){ User user = new User(); user.setUserId(rs.getString("userId")); user.setUserName(rs.getString("userName")); user.setUserPassword(rs.getString("userPassword")); user.setUserAge(rs.getInt(4)); user.setUserEmail(rs.getString(5)); System.out.println("사용자 정보:"); System.out.println("ID: " + user.getUserId()); System.out.println("이름: " + user.getUserName()); System.out.println("비밀번호: " + user.getUserPassword()); System.out.println("나이: " + user.getUserAge()); System.out.println("이메일: " + user.getUserEmail()); }else{ System.out.println("사용자가 존재하지 않습니다."); } pstmt.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { if(conn != null) { try { conn.close(); System.out.println("연결 끊기"); } catch (SQLException e) {} } } } }