2010년 12월 25일 토요일

SQL이 아닌 함수(getGeneratedKeys())를 이용한 AutoIncrement 키값 가져오기

통상적인 RDBMS는 Sequence 또는, AutoIncrement 형태의 시리얼한 id 채번 기능을 제공하고 있다.
또한, 이러한 Sequence 또는 AutoIncrement 로 부터 값이 추출되면, 그 세션에서 최종으로 사용된 id값을
가져올 수 있는 방법들을 제공하고 있다. (Oracle의 SEQUENCE.nextval 또는 MySQL의 LAST_INSERT_ID() 등)
많은 프로그램들에서 이러한 최종 채번된 id값을 가져오기 위해서 “SELECT LAST_INSERT_ID()” 쿼리 문장을
이용하여 조회하고 있는 것으로 보인다.
하지만, 이 방식은 또 한번의 서버 쿼리(Network round-trip)를 발생시키는 방식이며, JDBC 3.0 이상에서는
데이터베이스 서버까지 조회하지 않고 그냥 값을 가져오는 API를 제공하고 있다.

Network 비용이 그리 비싼 건 아니지만,
사용하는 JDBC Driver의 버전이 3.0 이상이라면 (물론 JDK 1.4 이상에서),
아래와 같이 Statement.getGeneratedKeys() 라는 함수를 이용하여 또 한번의 네트워크 통신 없이 바로 가져올 수 있다.
이 방식을 사용하기 위해서는 아래와 같이 Statement.executeUpdate() 나 PreparedStatement.prepareStatement()
함수 호출 시 별도의 설정 항목이 필요합니다.

l   Statement 객체 이용할 경우
int affectedRowCount = stmt.executeUpdate(
       "insert into tb_ai (fdpk, fddata) values (NULL, 'test')",Statement.RETURN_GENERATED_KEYS);
 ResultSet rs = stmt.getGeneratedKeys();
String autoInsertedKey = (rs.next()) ? rs.getString(1) : null;

l   PreparedStatement 객체 이용할 경우
PreparedStatement pstmt = conn.prepareStatement(
       "insert into tb_ai (fdpk, fddata) values (NULL, ?)", Statement.RETURN_GENERATED_KEYS);
 ResultSet rs = pstmt.getGeneratedKeys();
String autoInsertedKey = (rs.next()) ? rs.getString(1) : null;

이러한 방식은 DBMS 의존적인 부분이 아니라(물론 Vendor에서 지원하지 않으면 안되겠지만),
JDBC Driver Version 3.0 의 Spec이기 때문에 기본적인 DBMS(Oracle, MySQL, MSSQL, …)에서는
모두 지원되는 기능이므로 DB Framework에서 지원되지 않는다면, 기능 보완 요청을 통해서 해결이 가능할 듯 함

MySQL JDBC ConnectorConnector-J 3.0.17 부터 JDBC 3.0 지원하고 있습니다 

---------------------------------------------------------------------------------------
/**
 * create table tb_ai(
 *       fdpk bigint not null auto_increment,
 *       fddata varchar(100),
 *       primary key(fdpk)
 * );
 *
 * Get Auto generated insert key
 *     // Every version of JDBC driver
 *     1. rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
 *     // Over JDBC driver version 3.0
 *     2. rs = pstmt.getGeneratedKeys();
 */
public class GetAutoIncrementKeyTester {
         public static void main(String[] args) throws Exception{
                   GetAutoIncrementKeyTester tester = new GetAutoIncrementKeyTester();

                   String autoInsertedKey = tester.insertWithLiteralStatement();
                   System.out.println(">> Auto inserted key : " + autoInsertedKey);

                   autoInsertedKey = tester.insertWithLiteralStatement();
                   System.out.println(">> Auto inserted key : " + autoInsertedKey);
         }

         protected String insertWithLiteralStatement() throws Exception{
                   Connection conn = getMasterConnection();
                   Statement stmt = conn.createStatement();

                   stmt.executeUpdate(
                       "insert into tb_ai (fdpk, fddata) values (NULL, 'test')",Statement.RETURN_GENERATED_KEYS);
                   ResultSet rs = stmt.getGeneratedKeys();
                   String autoInsertedKey = (rs.next()) ? rs.getString(1) : null;
                   rs.close();stmt.close();conn.close();

                   return autoInsertedKey;
         }

         protected String insertWithPreparedStatement() throws Exception{
                   Connection conn = getMasterConnection();
                   PreparedStatement pstmt = conn.prepareStatement(
                       "insert into tb_ai (fdpk, fddata) values (NULL, ?)",Statement.RETURN_GENERATED_KEYS);

                   pstmt.setString(1, "data");
                   pstmt.executeUpdate();

                   ResultSet rs = pstmt.getGeneratedKeys();
                   String autoInsertedKey = (rs.next()) ? rs.getString(1) : null;
                   rs.close();pstmt.close();conn.close();

                   return autoInsertedKey;
         }

         protected Connection getMasterConnection() throws Exception{
                   String driver = "com.mysql.jdbc.Driver";
                   String url = "jdbc:mysql://test_db_host_ip:20306/test_db_name";
                   String uid = "userid";
                   String pwd = "password";

                   Class.forName(driver).newInstance();

                   Connection conn = DriverManager.getConnection(url , uid, pwd);
                   conn.setAutoCommit(false);

                   return conn;
         }
}

댓글 없음:

댓글 쓰기