我在我的Java应用程序中使用iBatis和MySQL 5.
我有一个持久的实体类
public class Entity {
private int id;
private Stirng property;
// setters and getters are omitted
}
按如下方式插入新实体:
<insert id="insert" parameterClass="MyEntity">
<selectKey resultClass="int" type="post" keyProperty="id" >
select LAST_INSERT_ID() as value
</selectKey>
{CALL insert_entity(#property#)}
</insert>
在存储过程中管理事务,如下所示:
CREATE DEFINER=`user`@`%` PROCEDURE `insert`(IN p_property VARCHAR(255))
BEGIN
START TRANSACTION;
INSERT INTO entities (property) VALUES (p_property);
-- Do more stuff that requires transaction: update more tables etc.
COMMIT;
END;
我想要实现的是将新插入的实体ID添加回我的Java代码。在没有并发数据库更新的情况下,上面的设置将完全符合我的要求。不清楚的部分是并发数据库更新会发生什么 - 即iBatis执行selectKey
语句的确切时间和上下文 - 我猜它不会在存储过程中定义的同一事务中执行,所以它可能返回的id不是我想要的实体的id。
我能想到的唯一可行解决方案是避免使用selectKey
:
<insert id="insert" parameterClass="MyEntity">
{CALL insert_entity(#property#, #id,mode=OUT#)}
</insert>
从存储过程返回最后一个插入的id:
CREATE DEFINER=`user`@`%` PROCEDURE `insert`(
IN p_property VARCHAR(255),
OUT p_id INTEGER(11),
)
BEGIN
START TRANSACTION;
INSERT INTO entities (property) VALUES (p_property);
SELECT LAST_INSERT_ID() INTO p_id;
-- Do more stuff that requires transaction: update more tables etc.
COMMIT;
END;
这个问题有更好的解决方案吗?
编辑: LAST_INSERT_ID
州的MySQL文档:
生成的ID基于每个连接在服务器中维护。这意味着函数返回给定客户端的值是为该客户端影响AUTO_INCREMENT列的最新语句生成的第一个AUTO_INCREMENT值。此值不受其他客户端的影响,即使它们生成自己的AUTO_INCREMENT值。此行为可确保每个客户端都可以检索自己的ID,而无需关心其他客户端的活动,也无需锁定或事务。
因此,selectKey
的原始解决方案似乎适用于所有情况。但是,对于具有多个INSERT
语句的复杂存储过程,第二种方法更安全。
答案 0 :(得分:0)
首先,我必须说明一点:您应该认真避免在存储过程中进行自己的事务管理。
假设这是你唯一的选择,我会说后一个解决方案是我的偏好,因为任何开发人员都清楚这个id是从事务中返回的。