博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于spring jdbcTemplate取得LAST_INSERT_ID
阅读量:5764 次
发布时间:2019-06-18

本文共 9776 字,大约阅读时间需要 32 分钟。

hot3.png

spring的jdbctemplate提供的方案:

KeyHolder keyHolder = new GeneratedKeyHolder();int updatecount = jdbcTemplate.update(new PreparedStatementCreator() {	@Override	public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {		PreparedStatement ps = (PreparedStatement) connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);		return ps;	}}, keyHolder);long id = keyHolder.getKey().longValue();

 

jdbctemplate中执行的相关源码

 

 

public int update(PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)        throws DataAccessException    {        Assert.notNull(generatedKeyHolder, "KeyHolder must not be null");        logger.debug("Executing SQL update and returning generated keys");        Integer result = (Integer)execute(psc, new PreparedStatementCallback() {            public Object doInPreparedStatement(PreparedStatement ps)                throws SQLException            {//第一步:通过PreparedStatement对象先执行相关sql                int rows = ps.executeUpdate();                List generatedKeys = generatedKeyHolder.getKeyList();                generatedKeys.clear();//第二步:通过同一个PreparedStatement取得产生的auto-generated keys                ResultSet keys = ps.getGeneratedKeys();                if(keys != null)                    try                    {                        RowMapper rowMapper = getColumnMapRowMapper();                        RowMapperResultSetExtractor rse = new RowMapperResultSetExtractor(rowMapper, 1);//解析取得的ResultSet,放入到holder对象的,供我们的应用程序使用                        generatedKeys.addAll((List)rse.extractData(keys));                    }                    finally                    {                        JdbcUtils.closeResultSet(keys);                    }                if(JdbcTemplate.this.this$0.isDebugEnabled())                    JdbcTemplate.this.this$0.debug("SQL update affected " + rows + " rows and returned " + generatedKeys.size() + " keys");                return new Integer(rows);            }                     {                super();            }        });        return result.intValue();    }

查看相关PreparedStatement对象的具体执行源码

 

public int executeUpdate()        throws SQLException    {        return executeUpdate(true, false);    }    protected int executeUpdate(boolean clearBatchedGeneratedKeysAndWarnings, boolean isBatch)        throws SQLException    {        if(clearBatchedGeneratedKeysAndWarnings)        {            clearWarnings();            batchedGeneratedKeys = null;        }        return executeUpdate(parameterValues, parameterStreams, isStream, streamLengths, isNull, isBatch);    }    protected int executeUpdate(byte batchedParameterStrings[][], InputStream batchedParameterStreams[], boolean batchedIsStream[], int batchedStreamLengths[], boolean batchedIsNull[], boolean isReallyBatch)        throws SQLException    {        checkClosed();//这个connection就是我们具体使用的连接的对象,具体实现就是jdbc的connection对象        Connection locallyScopedConn = connection;        if(locallyScopedConn.isReadOnly())            throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") + Messages.getString("PreparedStatement.35"), "S1009");        if(firstCharOfStmt == 'S' && StringUtils.startsWithIgnoreCaseAndWs(originalSql, "SELECT"))            throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), "01S03");        if(results != null && !locallyScopedConn.getHoldResultsOpenOverStatementClose())            results.realClose(false);        com.mysql.jdbc.ResultSet rs = null;        synchronized(locallyScopedConn.getMutex())        {            Buffer sendPacket = fillSendPacket(batchedParameterStrings, batchedParameterStreams, batchedIsStream, batchedStreamLengths);            String oldCatalog = null;            if(!locallyScopedConn.getCatalog().equals(currentCatalog))            {                oldCatalog = locallyScopedConn.getCatalog();                locallyScopedConn.setCatalog(currentCatalog);            }            if(locallyScopedConn.useMaxRows())                locallyScopedConn.execSQL(this, "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, 1003, 1007, false, currentCatalog, true);            boolean oldInfoMsgState = false;            if(retrieveGeneratedKeys)            {                oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();                locallyScopedConn.setReadInfoMsgEnabled(true);            }//内部方法,执行得到ResultSet对象,executeInternal方法调用传入的connection对象的execSQL方法完成数据库操作并得到返回的ResultSet对象            rs = executeInternal(-1, sendPacket, false, false, true, null, isReallyBatch);            if(retrieveGeneratedKeys)            {                locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);                rs.setFirstCharOfQuery(firstCharOfStmt);            }            if(oldCatalog != null)                locallyScopedConn.setCatalog(oldCatalog);        }        results = rs;        updateCount = rs.getUpdateCount();        int truncatedUpdateCount = 0;        if(updateCount > 2147483647L)            truncatedUpdateCount = 2147483647;        else            truncatedUpdateCount = (int)updateCount;        lastInsertId = rs.getUpdateID();        return truncatedUpdateCount;    }

查看jdbc的Connection源码

 

com.mysql.jdbc.ResultSet execSQL(com.mysql.jdbc.Statement callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, boolean streamResults,             String catalog, boolean unpackFields, boolean isBatch)        throws SQLException{...//关键执行语句,this.io是一个jdbc的mysqlio对象return this.io.sqlQueryDirect(callingStatement, null, null, packet, maxRows, this, resultSetType, resultSetConcurrency, streamResults, catalog, unpackFields);

经过MysqlIo对象的处理,最后封装一个包含updateCount和updateID两个主要数据的ResultSet对象

 

private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(        Statement callingStatement, Buffer resultPacket)        throws SQLException {        long updateCount = -1;        long updateID = -1;        String info = null;        try {            if (this.useNewUpdateCounts) {                updateCount = resultPacket.newReadLength();                updateID = resultPacket.newReadLength();            } else {                updateCount = resultPacket.readLength();                updateID = resultPacket.readLength();            }            if (this.use41Extensions) {                this.serverStatus = resultPacket.readInt();                this.warningCount = resultPacket.readInt();                if (this.warningCount > 0) {                    this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()                }                resultPacket.readByte(); // advance pointer                if (this.profileSql) {                    this.queryNoIndexUsed = (this.serverStatus &                        SERVER_QUERY_NO_GOOD_INDEX_USED) != 0;                    this.queryBadIndexUsed = (this.serverStatus &                        SERVER_QUERY_NO_INDEX_USED) != 0;                }            }            if (this.connection.isReadInfoMsgEnabled()) {                info = resultPacket.readString();            }        } catch (Exception ex) {            throw SQLError.createSQLException(SQLError.get(                    SQLError.SQL_STATE_GENERAL_ERROR) + ": " //$NON-NLS-1$                 +ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);        }        ResultSet updateRs = new com.mysql.jdbc.ResultSet(updateCount,                updateID, this.connection, callingStatement);        if (info != null) {            updateRs.setServerInfo(info);        }        return updateRs;    }

回到PreparedStatement对象的executeUpdate方法

 

//把语句执行得到的结果集交给当前对象this.results = rs;//更新条数		this.updateCount = rs.getUpdateCount();		int truncatedUpdateCount = 0;		if (this.updateCount > Integer.MAX_VALUE) {			truncatedUpdateCount = Integer.MAX_VALUE;		} else {			truncatedUpdateCount = (int) this.updateCount;		}//LAST_INSERT_ID值		this.lastInsertId = rs.getUpdateID();		return truncatedUpdateCount;

再回到jdbctemplate源码的update方法,第一步:通过PreparedStatement对象先执行相关sql 【这部分执行已经完成,得到的ResultSet对象值已经交给ps对象】
然后通过ps对象ps.getGeneratedKeys()获得generateKeys的值,具体处理在PreparedStatement父类Statement对象中

 

public ResultSet getGeneratedKeys()        throws SQLException    {        if(batchedGeneratedKeys == null)        {            return getGeneratedKeysInternal();        } else        {            Field fields[] = new Field[1];            fields[0] = new Field("", "GENERATED_KEY", -5, 17);            fields[0].setConnection(connection);            return new com.mysql.jdbc.ResultSet(currentCatalog, fields, new RowDataStatic(batchedGeneratedKeys), connection, this);        }    }    protected ResultSet getGeneratedKeysInternal()        throws SQLException    {        Field fields[] = new Field[1];        fields[0] = new Field("", "GENERATED_KEY", -5, 17);        fields[0].setConnection(connection);        ArrayList rowSet = new ArrayList();        long beginAt = getLastInsertID();        int numKeys = getUpdateCount();        if(results != null)        {            String serverInfo = results.getServerInfo();            if(numKeys > 0 && results.getFirstCharOfQuery() == 'R' && serverInfo != null && serverInfo.length() > 0)                numKeys = getRecordCountFromInfo(serverInfo);            if(beginAt > 0L && numKeys > 0)            {//根据更新条数会累加LastInsertID的值,因为在插入多条的时候只会默认返回第一条执行后产生的ID                for(int i = 0; i < numKeys; i++)                {                    byte row[][] = new byte[1][];                    row[0] = Long.toString(beginAt++).getBytes();                    rowSet.add(row);                }            }        }        return new com.mysql.jdbc.ResultSet(currentCatalog, fields, new RowDataStatic(rowSet), connection, this);    }

转载于:https://my.oschina.net/dreamnight/blog/695044

你可能感兴趣的文章
[雪峰磁针石博客]虚拟现实与增强现实VR&AR工具书籍汇总下载(持续更新)
查看>>
【对讲机的那点事】手把手教你给摩托罗拉C1200数字对讲机写频
查看>>
JWT基本简介以及实例展示
查看>>
SourceTree 教程文档(进阶知识)
查看>>
Java并发编程之线程安全、线程通信
查看>>
WPF 异步加载高清大图
查看>>
基因组survey
查看>>
单链表C++
查看>>
WEBGL学习【十三】鼠标点击立方体改变颜色的原理与实现
查看>>
Node.js 11.14.0 发布,服务器端的 JavaScript 运行环境
查看>>
适配最新iPhone9、iPhoneXs提醒事项备忘录APP
查看>>
SOFABoot 3.1.2 发布,蚂蚁金服开源研发框架
查看>>
Signalling entropy: A novel network-theoretical fram
查看>>
几种常见的限流算法
查看>>
使用DataGrip连接SQL Server 2017数据库
查看>>
[剑指offer] 和为S的连续正数序列
查看>>
C#和windows系统哪些事
查看>>
Kubernetes 学习笔记(三)--- 资源清单定义入门
查看>>
Centos7主机名莫名其妙变成了bogon
查看>>
12.1、python内置函数——eval、exec、compile
查看>>