Я использую iBatis с MySQL 5 в своем приложении Java.
У меня есть постоянный класс сущностей
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;
Чего я пытаюсь добиться, так это вернуть вновь вставленный идентификатор объекта в мой код Java. При работе без одновременных обновлений БД описанная выше установка будет делать именно то, что я хочу. Непонятная часть заключается в том, что происходит с одновременными обновлениями БД, т.е. каковы точные сроки и контекст выполнения iBatis оператора selectKey
. Я предполагаю, что он не будет выполняться в той же транзакции, которая определена в хранимой процедуре, поэтому возможно, что идентификатор возвращенный не будет идентификатором сущности, которую я хочу.
Единственное возможное решение, о котором я могу думать, это избегать использования selectKey
:
<insert id="insert" parameterClass="MyEntity">
{CALL insert_entity(#property#, #id,mode=OUT#)}
</insert>
Возврат последнего вставленного идентификатора из хранимой процедуры:
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;
Есть ли лучшее решение этой проблемы?
Отредактировано: документация MySQL для LAST_INSERT_ID
гласит:
Созданный идентификатор сохраняется на сервере для каждого соединения. Это означает, что значение, возвращаемое функцией данному клиенту, является первым значением AUTO_INCREMENT, сгенерированным для самого последнего оператора, влияющего на столбец AUTO_INCREMENT этим клиентом. На это значение не могут повлиять другие клиенты, даже если они генерируют собственные значения AUTO_INCREMENT. Такое поведение гарантирует, что каждый клиент может получить свой собственный идентификатор, не беспокоясь о действиях других клиентов и без необходимости блокировок или транзакций.
Таким образом, похоже, что оригинальное решение с selectKey
будет работать во всех случаях. Однако для сложных хранимых процедур с несколькими операторами INSERT
второй подход безопаснее.