本文转载自微信公众号《肌肉码农》,作者邹雪。转载本文请联系肌农公众号。简介:mybatis等ORM将对象插入数据库后,会将数据库中的自增主键值赋值给对象的id。这个功能给我们的开发带来了很多方便,那么它是如何实现的呢?源码分析:使用mybatis实现这个功能非常简单。网上有很多资料。今天我们主要看看它是如何实现的。这个类可以通过断点插入来跟踪:PreparedStatementHandler.java的update方法。publicintupdate(Statementstatement)throwsSQLException{PreparedStatementps=(PreparedStatement)statement;//执行插入操作ps.execute();//获取执行的行数introws=ps.getUpdateCount();ObjectparameterObject=boundSql.getParameterObject();//getidKeyGeneratorkeyGenerator=mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor,mappedStatement,ps,parameterObject);returnrows;}进一步追查getKeyGenerator()获取id的方法,会进入Jdbc3KeyGenerator类的processBatch方法,如下:publicvoidprocessBatch(MappedStatementms,Statementsstmt,Objectparameter){finalString[]keyProperties=ms.getKeyProperties();if(keyProperties==null||keyProperties.length==0){return;}//使用getGeneratedKeys()方法语句try(ResultSetrs=stmt.getGeneratedKeys()){finalResultSetMetaDatarsmd=rs.getMetaData();finalConfigurationconfiguration=ms.getConfiguration();if(rsmd.getColumnCount()rowSet=newArrayList();//获取最后一次插入后的idlongbeginAt=getLastInsertID();if(beginAt<0){//lookingatanUNSIGNEDBIGINTthathasoverflowedfields[0].setUnsigned();}if(this.results!=null){StringserverInfo=this.results.getServerInfo();////Onlyparseserverinfomessagesfor'REPLACE'queries//if((numKeys>0)&&(this.results.getFirstCharOfQuery()=='R')&&(serverInfo!=null)&&(serverInfo.length()>0)){//计算有多少行数据numKeys=getRecordCountFromInfo(serverInfo);}//生成批量idif((beginAt!=0/*BIGINTUNSIGNEDcanwraptheprotocolrepresentation*/)&&(numKeys>0)){for(inti=0;i0){row[0]=StringUtils.getBytes(Long.toString(beginAt));}else{byte[]asBytes=newbyte[8];asBytes[7]=(字节)(beginAt&0xff);asBytes[6]=(字节)(beginAt>>>8);asBytes[5]=(byte)(beginAt>>>16);asBytes[4]=(byte)(beginAt>>>24);asBytes[3]=(byte)(beginAt>>>32);asBytes[2]=(byte)(beginAt>>>40);asBytes[1]=(byte)(beginAt>>>48);asBytes[0]=(byte)(beginAt>>>56);BigIntegerval=newBigInteger(1,asBytes);行[0]=val.toString().getBytes();}rowSet.add(newByteArrayRow(row,getExceptionInterceptor()));beginAt+=this.connection.getAutoIncrementIncrement();}}}com.mysql.jdbc.ResultSetImplgkRs=com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog,字段,newRowDataStatic(rowSet),this.connection,this,false);returngkRs;}}代码流程如下:获取上次插入后的id,然后计算本次插入的行数,最后生成自己分批。也就是说,jdbc是不会去数据库逐行查询id的。那我们看看它是如何获取最后一次insert后的Id的?/**支持自动递增主键*getLastInsertID在*executeQuery()或excute()调用后返回auto_incremented键的值。***这解决了“selectLAST_INSERT_ID()”的非线程安全行为,该行为与创建此语句的连接相关联,因此可能*在一个人有机会调用“selectLAST_INSERT”之前执行过许多INSERT()".*
**@returnthelastupdateID.*/publiclonggetLastInsertID(){try{synchronized(checkClosed().getConnectionMutex()){returnthis.lastInsertId;}}catch(SQLExceptione){thrownewRuntimeException(e);//evolveinterfacetothrowSQLException}}光看上面的代码注释就可以理解它的逻辑了。在session中通过selectLAST_INSERT_ID()获取insert后的Id,只支持自增主键mysql客户端获取id