关于spring jdbcTemplate取得LAST_INSERT_ID

spring的jdbctemplate提供的方案: 
Java代码   收藏代码
  1. KeyHolder keyHolder = new GeneratedKeyHolder();  
  2. int updatecount = jdbcTemplate.update(new PreparedStatementCreator() {  
  3.     @Override  
  4.     public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {  
  5.         PreparedStatement ps = (PreparedStatement) connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);  
  6.         return ps;  
  7.     }  
  8. }, keyHolder);  
  9. long id = keyHolder.getKey().longValue();  


jdbctemplate中执行的相关源码 
Java代码   收藏代码
  1. public int update(PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)  
  2.         throws DataAccessException  
  3.     {  
  4.         Assert.notNull(generatedKeyHolder, "KeyHolder must not be null");  
  5.         logger.debug("Executing SQL update and returning generated keys");  
  6.         Integer result = (Integer)execute(psc, new PreparedStatementCallback() {  
  7.   
  8.             public Object doInPreparedStatement(PreparedStatement ps)  
  9.                 throws SQLException  
  10.             {  
  11. //第一步:通过PreparedStatement对象先执行相关sql  
  12.                 int rows = ps.executeUpdate();  
  13.                 List generatedKeys = generatedKeyHolder.getKeyList();  
  14.                 generatedKeys.clear();  
  15. //第二步:通过同一个PreparedStatement取得产生的auto-generated keys  
  16.                 ResultSet keys = ps.getGeneratedKeys();  
  17.                 if(keys != null)  
  18.                     try  
  19.                     {  
  20.                         RowMapper rowMapper = getColumnMapRowMapper();  
  21.                         RowMapperResultSetExtractor rse = new RowMapperResultSetExtractor(rowMapper, 1);  
  22. //解析取得的ResultSet,放入到holder对象的,供我们的应用程序使用  
  23.                         generatedKeys.addAll((List)rse.extractData(keys));  
  24.                     }  
  25.                     finally  
  26.                     {  
  27.                         JdbcUtils.closeResultSet(keys);  
  28.                     }  
  29.                 if(JdbcTemplate.this.this$0.isDebugEnabled())  
  30.                     JdbcTemplate.this.this$0.debug("SQL update affected " + rows + " rows and returned " + generatedKeys.size() + " keys");  
  31.                 return new Integer(rows);  
  32.             }           
  33.             {  
  34.                 super();  
  35.             }  
  36.         }  
  37. );  
  38.         return result.intValue();  
  39.     }  


查看相关PreparedStatement对象的具体执行源码 
Java代码   收藏代码
  1. public int executeUpdate()  
  2.         throws SQLException  
  3.     {  
  4.         return executeUpdate(truefalse);  
  5.     }  
  6.   
  7.     protected int executeUpdate(boolean clearBatchedGeneratedKeysAndWarnings, boolean isBatch)  
  8.         throws SQLException  
  9.     {  
  10.         if(clearBatchedGeneratedKeysAndWarnings)  
  11.         {  
  12.             clearWarnings();  
  13.             batchedGeneratedKeys = null;  
  14.         }  
  15.         return executeUpdate(parameterValues, parameterStreams, isStream, streamLengths, isNull, isBatch);  
  16.     }  
  17.   
  18.     protected int executeUpdate(byte batchedParameterStrings[][], InputStream batchedParameterStreams[], boolean batchedIsStream[], int batchedStreamLengths[], boolean batchedIsNull[], boolean isReallyBatch)  
  19.         throws SQLException  
  20.     {  
  21.         checkClosed();  
  22. //这个connection就是我们具体使用的连接的对象,具体实现就是jdbc的connection对象  
  23.         Connection locallyScopedConn = connection;  
  24.         if(locallyScopedConn.isReadOnly())  
  25.             throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") + Messages.getString("PreparedStatement.35"), "S1009");  
  26.         if(firstCharOfStmt == 'S' && StringUtils.startsWithIgnoreCaseAndWs(originalSql, "SELECT"))  
  27.             throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), "01S03");  
  28.         if(results != null && !locallyScopedConn.getHoldResultsOpenOverStatementClose())  
  29.             results.realClose(false);  
  30.         com.mysql.jdbc.ResultSet rs = null;  
  31.         synchronized(locallyScopedConn.getMutex())  
  32.         {  
  33.             Buffer sendPacket = fillSendPacket(batchedParameterStrings, batchedParameterStreams, batchedIsStream, batchedStreamLengths);  
  34.             String oldCatalog = null;  
  35.             if(!locallyScopedConn.getCatalog().equals(currentCatalog))  
  36.             {  
  37.                 oldCatalog = locallyScopedConn.getCatalog();  
  38.                 locallyScopedConn.setCatalog(currentCatalog);  
  39.             }  
  40.             if(locallyScopedConn.useMaxRows())  
  41.                 locallyScopedConn.execSQL(this"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1null10031007false, currentCatalog, true);  
  42.             boolean oldInfoMsgState = false;  
  43.             if(retrieveGeneratedKeys)  
  44.             {  
  45.                 oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();  
  46.                 locallyScopedConn.setReadInfoMsgEnabled(true);  
  47.             }  
  48. //内部方法,执行得到ResultSet对象,executeInternal方法调用传入的connection对象的execSQL方法完成数据库操作并得到返回的ResultSet对象  
  49.             rs = executeInternal(-1, sendPacket, falsefalsetruenull, isReallyBatch);  
  50.             if(retrieveGeneratedKeys)  
  51.             {  
  52.                 locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);  
  53.                 rs.setFirstCharOfQuery(firstCharOfStmt);  
  54.             }  
  55.             if(oldCatalog != null)  
  56.                 locallyScopedConn.setCatalog(oldCatalog);  
  57.         }  
  58.         results = rs;  
  59.         updateCount = rs.getUpdateCount();  
  60.         int truncatedUpdateCount = 0;  
  61.         if(updateCount > 2147483647L)  
  62.             truncatedUpdateCount = 2147483647;  
  63.         else  
  64.             truncatedUpdateCount = (int)updateCount;  
  65.         lastInsertId = rs.getUpdateID();  
  66.         return truncatedUpdateCount;  
  67.     }  


查看jdbc的Connection源码 
Java代码   收藏代码
  1. com.mysql.jdbc.ResultSet execSQL(com.mysql.jdbc.Statement callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, boolean streamResults,   
  2.             String catalog, boolean unpackFields, boolean isBatch)  
  3.         throws SQLException{  
  4. ...  
  5. //关键执行语句,this.io是一个jdbc的mysqlio对象  
  6. return this.io.sqlQueryDirect(callingStatement, nullnull, packet, maxRows, this, resultSetType, resultSetConcurrency, streamResults, catalog, unpackFields);  


经过MysqlIo对象的处理,最后封装一个包含updateCount和updateID两个主要数据的ResultSet对象 
Java代码   收藏代码
  1. private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(  
  2.         Statement callingStatement, Buffer resultPacket)  
  3.         throws SQLException {  
  4.         long updateCount = -1;  
  5.         long updateID = -1;  
  6.         String info = null;  
  7.   
  8.         try {  
  9.             if (this.useNewUpdateCounts) {  
  10.                 updateCount = resultPacket.newReadLength();  
  11.                 updateID = resultPacket.newReadLength();  
  12.             } else {  
  13.                 updateCount = resultPacket.readLength();  
  14.                 updateID = resultPacket.readLength();  
  15.             }  
  16.   
  17.             if (this.use41Extensions) {  
  18.                 this.serverStatus = resultPacket.readInt();  
  19.   
  20.                 this.warningCount = resultPacket.readInt();  
  21.   
  22.                 if (this.warningCount > 0) {  
  23.                     this.hadWarnings = true// this is a 'latch', it's reset by sendCommand()  
  24.                 }  
  25.   
  26.                 resultPacket.readByte(); // advance pointer  
  27.   
  28.                 if (this.profileSql) {  
  29.                     this.queryNoIndexUsed = (this.serverStatus &  
  30.                         SERVER_QUERY_NO_GOOD_INDEX_USED) != 0;  
  31.                     this.queryBadIndexUsed = (this.serverStatus &  
  32.                         SERVER_QUERY_NO_INDEX_USED) != 0;  
  33.                 }  
  34.             }  
  35.   
  36.             if (this.connection.isReadInfoMsgEnabled()) {  
  37.                 info = resultPacket.readString();  
  38.             }  
  39.         } catch (Exception ex) {  
  40.             throw SQLError.createSQLException(SQLError.get(  
  41.                     SQLError.SQL_STATE_GENERAL_ERROR) + ": " //$NON-NLS-1$  
  42.                  +ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);  
  43.         }  
  44.   
  45.         ResultSet updateRs = new com.mysql.jdbc.ResultSet(updateCount,  
  46.                 updateID, this.connection, callingStatement);  
  47.   
  48.         if (info != null) {  
  49.             updateRs.setServerInfo(info);  
  50.         }  
  51.   
  52.         return updateRs;  
  53.     }  


回到PreparedStatement对象的executeUpdate方法 
Java代码   收藏代码
  1. //把语句执行得到的结果集交给当前对象  
  2. this.results = rs;  
  3. //更新条数  
  4.         this.updateCount = rs.getUpdateCount();  
  5.         int truncatedUpdateCount = 0;  
  6.         if (this.updateCount > Integer.MAX_VALUE) {  
  7.             truncatedUpdateCount = Integer.MAX_VALUE;  
  8.         } else {  
  9.             truncatedUpdateCount = (intthis.updateCount;  
  10.         }  
  11. //LAST_INSERT_ID值  
  12.         this.lastInsertId = rs.getUpdateID();  
  13.         return truncatedUpdateCount;  


再回到jdbctemplate源码的update方法,第一步:通过PreparedStatement对象先执行相关sql 【这部分执行已经完成,得到的ResultSet对象值已经交给ps对象】 
然后通过ps对象ps.getGeneratedKeys()获得generateKeys的值,具体处理在PreparedStatement父类Statement对象中 
Java代码   收藏代码
  1. public ResultSet getGeneratedKeys()  
  2.         throws SQLException  
  3.     {  
  4.         if(batchedGeneratedKeys == null)  
  5.         {  
  6.             return getGeneratedKeysInternal();  
  7.         } else  
  8.         {  
  9.             Field fields[] = new Field[1];  
  10.             fields[0] = new Field("""GENERATED_KEY", -517);  
  11.             fields[0].setConnection(connection);  
  12.             return new com.mysql.jdbc.ResultSet(currentCatalog, fields, new RowDataStatic(batchedGeneratedKeys), connection, this);  
  13.         }  
  14.     }  
  15.   
  16.     protected ResultSet getGeneratedKeysInternal()  
  17.         throws SQLException  
  18.     {  
  19.         Field fields[] = new Field[1];  
  20.         fields[0] = new Field("""GENERATED_KEY", -517);  
  21.         fields[0].setConnection(connection);  
  22.         ArrayList rowSet = new ArrayList();  
  23.         long beginAt = getLastInsertID();  
  24.         int numKeys = getUpdateCount();  
  25.         if(results != null)  
  26.         {  
  27.             String serverInfo = results.getServerInfo();  
  28.             if(numKeys > 0 && results.getFirstCharOfQuery() == 'R' && serverInfo != null && serverInfo.length() > 0)  
  29.                 numKeys = getRecordCountFromInfo(serverInfo);  
  30.             if(beginAt > 0L && numKeys > 0)  
  31.             {  
  32. //根据更新条数会累加LastInsertID的值,因为在插入多条的时候只会默认返回第一条执行后产生的ID  
  33.                 for(int i = 0; i < numKeys; i++)  
  34.                 {  
  35.                     byte row[][] = new byte[1][];  
  36.                     row[0] = Long.toString(beginAt++).getBytes();  
  37.                     rowSet.add(row);  
  38.                 }  
  39.   
  40.             }  
  41.         }  
  42.         return new com.mysql.jdbc.ResultSet(currentCatalog, fields, new RowDataStatic(rowSet), connection, this);  
  43.     }  

转:http://fancyboy2050.iteye.com/blog/1455559
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值