iBatis selectKey и транзакции

Я использую 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 второй подход безопаснее.


person Boris Kirzner    schedule 22.03.2011    source источник


Ответы (1)


Во-первых, я должен констатировать очевидное: вам следует серьезно постараться избежать собственного управления транзакциями внутри хранимой процедуры.

Предполагая, что это ваш единственный вариант, я бы сказал, что последнее решение было бы моим предпочтением, поскольку любому разработчику ясно, что идентификатор возвращается из транзакции.

person Rolf    schedule 05.07.2011