技術前提:我們使用iBatis作為持久層方案
技術場景:
假設我們有兩張表,一張主表Main,一張子表Sub,並且主表的主鍵是由數據庫維護的自增長的主鍵,子表中有一個字段引用這個主鍵,那么當我們插入主表數據后,就需要馬上返回這個自增長的主鍵。
解決方案:
可以在insert時通過iBatis的selectKey返回那個主鍵。
selectKey有3個主要的屬性:
1)resultClass:返回的主鍵的數據類型,跟sqlMap中的數據類型一致;
2)type:表示主鍵在insert之前或之后生成(取決於數據庫的主鍵生成策略),取值分別為[pre|post],非必須,未填寫時如果在insert之前表示pre,否則表示post;
2)keyProperty:返回值保存到的屬性,非必須(作用參見Oracle配置);
實際配置:
<!-- Oracle SEQUENCE -->
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">
<selectKey resultClass="int" keyProperty="id" type="pre">
<![CDATA[
SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
]]>
</selectKey>
<![CDATA[
insert into PRODUCT(PRD_ID,PRD_DESCRIPTION) values(#id#,#description#)
<!-- 此處體現了keyProperty的作用 -->
]]>
</insert>
<!-- Microsoft SQL Server IDENTITY Column -->
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
<![CDATA[
insert into PRODUCT (PRD_DESCRIPTION) values(#description#)
]]>
<selectKey resultClass="int" keyProperty="id" type="post">
<![CDATA[
SELECT @@IDENTITY AS ID
]]>
<!-- 該方法不安全 應當用SCOPE_IDENTITY() 但這個函數屬於域函數,需要在一個語句塊中執行。 -->
</selectKey>
</insert>
上述MS SQL Server配置隨是官網提供的配置,但實際上卻恰恰隱患重重!按下述配置,確保獲得有效主鍵。
<!-- Microsoft SQL Server IDENTITY Column improvement-->
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
<selectKey resultClass="int" keyProperty="id">
<![CDATA[
insert into PRODUCT (PRD_DESCRIPTION) values(#description#)
SELECT SCOPE_IDENTITY() AS ID
]]>
</selectKey>
</insert>
<!-- MySQL Last Insert Id -->
<insert id="insertProduct-Mysql" parameterClass="com.domain.Product">
insert into PRODUCT(PRD_DESCRIPTION) values(#description#)
<selectKey resultClass="int" keyProperty="id">
SELECT LAST_INSERT_ID() AS ID
</selectKey>
</insert>
<!-- 該方法LAST_INSERT_ID()與數據庫連接綁定,同屬統一會話級別,不會發生上述MS SQL Server的函數問題。 -->
<!-- DB2 Identity Val Local -->
<insert id="insertProduct-DB2" parameterClass="com.domain.Product">
insert into PRODUCT(PRD_DESCRIPTION) values(#description#)
<selectKey resultClass="int" type="post">
VALUES IDENTITY_VAL_LOCAL()
</selectKey>
</insert>
<!-- Sqlite sqlite_sequence -->
<insert id="insertProduct-Sqlite" parameterClass="com.domain.Product">
insert into PRODUCT(PRD_DESCRIPTION) values(#description#)
<selectKey resultClass="string" type="post">
SELECT seq FROM sqlite_sequence WHERE name = 'PRD_DESCRIPTION'
</selectKey>
</insert>
<!-- 主鍵字段一定要加下INTEGER PRIMARY KEY AUTOINCREMENT,否則會報找不到 sqlite_sequence 表。 -->
通過以上方式,可以最大程度上確保插入數據的時候獲得當前自增主鍵。