Mybatis 使用 “SELECT LAST_INSERT_ID 總是為1” 問題解決
問題
問題是發生在從 ibatis 升級到 mybatis 出現的,程式碼中使用SELECT LAST_INSERT_ID
獲取自增 id,原來在 ibatis 好用的程式碼,升級到了 mybatis 就總是返回 1 了。
linkedkeeper_sequence表結構如下:
CREATE TABLE `linkedkeeper_sequence` ( `seq_name` varchar(200) NOT NULL, `current_value` bigint(20) NOT NULL, `_increment` int(4) NOT NULL, PRIMARY KEY (`seq_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
ibatis 中獲取自增 id 的 xml:
<insert timeout="30" id="update" parameterClass="java.lang.String"> UPDATE linkedkeeper_sequence SET current_value = LAST_INSERT_ID(current_value + _increment) WHERE seq_name = #seqname# <selectKey resultClass="long"> <![CDATA[SELECT LAST_INSERT_ID() ]]> </selectKey> </insert>
Dao 的程式碼:
public long getSeq(final String seqName) throws Exception { return insert("Seq.update", seqName); }
第一次修改成 mybatis 的 xml,但總是返回 1。
<insert timeout="30" id="update" parameterType="java.lang.String"> UPDATE linkedkeeper_sequence SET current_value = LAST_INSERT_ID(current_value + _increment) WHERE seq_name = #{seqname} <selectKey resultClass="long"> <![CDATA[SELECT LAST_INSERT_ID() ]]> </selectKey> </insert>
原因
在 mybatis 中,insert 之後返回的是 1 是影響的行數,並不是自增 id。
mybatis 使用 insert 的原始碼(CallableStatementHandler),可以看到return rows
。
@Override public int update(Statement statement) throws SQLException { CallableStatement cs = (CallableStatement) statement; cs.execute(); int rows = cs.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject); resultSetHandler.handleOutputParameters(cs); return rows; }
ibatis 使用 insert 的原始碼(MappedStatement),可以看到是 object。
public Object insert(SessionScope sessionScope, String id, Object param) throws SQLException { Object generatedKey = null; ... if (selectKeyStatement != null && !selectKeyStatement.isRunAfterSQL()) { ... generatedKey = executeSelectKey(sessionScope, trans, ms, param); ... } ... return generatedKey; } private Object executeSelectKey(SessionScope sessionScope, Transaction trans, MappedStatement ms, Object param) throws SQLException { Object generatedKey = null; .... generatedKey = selectKeyStatement.executeQueryForObject(statementScope, trans, param, null); ... return generatedKey; } public Object executeQueryForObject(StatementScope statementScope, Transaction trans, Object parameterObject, Object resultObject) throws SQLException { try { Object object = null; DefaultRowHandler rowHandler = new DefaultRowHandler(); executeQueryWithCallback(statementScope, trans.getConnection(), parameterObject, resultObject, rowHandler, SqlExecutor.NO_SKIPPED_RESULTS, SqlExecutor.NO_MAXIMUM_RESULTS); List list = rowHandler.getList(); if (list.size() > 1) { throw new SQLException("Error: executeQueryForObject returned too many results."); } else if (list.size() > 0) { object = list.get(0); } return object; } catch (TransactionException e) { throw new NestedSQLException("Error getting Connection from Transaction.Cause: " + e, e); } }
解決
想要獲取自增 id,直接呼叫物件的 getId() 方法就可以了,因為在 mybatis 中指定自增主鍵 id 封裝到了物件的屬性中,所以我們需要在物件中來獲取。
建立 Seq 物件:
class Seq extends Serializable { private var id: Long = 0L private var seqName: String = null def getId: Long = { return id } def setId(id: Long) { this.id = id } def getSeqName: String = { return seqName } def setSeqName(seqName: String) { this.seqName = seqName } }
第二次修改 mybatis 中獲取自增 id 的 xml,可以正確獲取自增 id:
<insert timeout="30" id="update" parameterType="Seq"> UPDATE linkedkeeper_sequence SET current_value = LAST_INSERT_ID(current_value + _increment) WHERE seq_name = #{seqName} <selectKey resultType="long" keyProperty="id" order="AFTER"> <![CDATA[SELECT LAST_INSERT_ID() ]]> </selectKey> </insert>
對<selectKey>
進行說明:
-
SELECT LAST_INSERT_ID
:執行 sql 函式得到剛 insert 進去記錄的主鍵值,只適用於自增主鍵 -
keyProperty
:指定返回的 id 對映到 bean 中的哪個屬性(這裡是 id),這個 bean 對應的類的名稱就是上面 insert 標籤中的屬性 parameterType 的值 -
order="AFTER"
:表示這個 selectKey 語句的執行是在 insert 語句之後 -
resultType
:selectKey 語句返回值的型別,這裡是 long 型別
修改呼叫的程式碼:
Seq seq = new Seq(); seq.setSeqName(seqName); if (seqDao.update(seq) == 0) { throw new RuntimeException("seq update failure."); } return seq.getId();
Reference:
https://www.cnblogs.com/quan-coder/p/8728410.html
https://blog.csdn.net/weixin_42244235/article/details/82391810
https://www.cnblogs.com/caizhen/p/9186608.html
https://blog.csdn.net/abc997995674/article/details/80867401
https://www.cnblogs.com/xingyunblog/p/6243179.html
本文受原創保護,未經作者授權,禁止轉載。 linkedkeeper.com (文/張鬆然)