Сохранение/обновление коллекций с помощью mybatis, какова распространенная практика?

Я решил попробовать использовать mybatis для нового проекта. Я неплохо знаком с SQL, и недавно у меня был плохой опыт с hibernate, поэтому я ищу более низкоуровневый подход к DAO.

Кажется, это очень приятно, за исключением одного, и это обработка коллекций.

У меня есть два POJO, группа и пользователь, которые много-ко-многим. Я решил философию дизайна, что POJO, у которого есть коллекция, должен обновлять отношение M-M между таблицами при сохранении. Так, например, когда я сохраняю групповой объект с коллекцией пользователей, философия дизайна требует, чтобы пользователи уже были сохранены, и мне нужно сохранить отношение группы и group_user в базе данных.

поэтому для функции saveGroup в интерфейсе я сделал это сопоставление XML для mybatis:

    <insert id="saveGroup" keyColumn="id"
    parameterType="se.myapp.domain.Group">
    <choose>
        <when test="id == null">
        INSERT INTO myapp_group (name, description)
        VALUES
        (#{username}, #{password});
        </when>
        <otherwise>
        UPDATE myapp_group set name=#{name}, description=#{description}
        where id=#{id};
        </otherwise>
    </choose>

    <if test="users != null">
        create temporary table tmpnewgroups (group_id integer, user_id integer);

        insert into tmpnewgroups (group_id, user_id) values (
        <foreach collection="users" item="user" open="" close="" separator="),()">
             #{id},#{user.id}
        </foreach>
        );

        insert into myapp_user_group(group_id, user_id) 
        select tmp.group_id, tmp.user_id 
        from tmpnewgroups tmp 
        left outer join myapp_user_group ug 
            on ug.group_id = tmp.group_id and ug.user_id = tmp.user_id
        where ug.group_id is null;

        delete from myapp_user_group 
        where group_id = #{id} and user_id not in (select user_id from tmpnewgroups);
    </if>

</insert>

Это работает по назначению (вставляет/обновляет группу, сохраняет коллекцию пользователей как отношения в базе данных). Но я не чувствую, что это лучшая практика. Приложение сделано так, что я могу переключиться на спящий режим, если это необходимо, поэтому логика сохранения коллекции предпочтительно должна находиться в слое базы данных. Есть ли какая-то "магия" в mybatis, что я не знаю, что может оптимизировать такие операции?

Любые мысли о том, как улучшить это? Или я должен переосмыслить дизайн приложения и добавить обработку коллекций в модель?

Ответ 1

Вторая часть операции сопоставления данных saveGroup действительно является поводом для пересмотра дизайна вашего приложения. Сохранение вашей коллекции данных в памяти во временную таблицу, чтобы сравнить ее с сохраненной, чтобы вставлять и удалять дельта, является довольно тяжелой операцией, которая не нужна вообще, если просто нужно изменить имя или описание группы, т.е. когда нет дельт. Независимо от того, может ли это быть, может быть решен сервер базы данных, который является вашим текущим решением или клиентом базы данных, вашим приложением.

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

См., по сравнению с Hibernate MyBatis блаженно не знает о ваших объектах и ​​состоянии, которое они переносят после того, как MyBatis выполнил свое задание, которое является сопоставлением данных, а не сопоставлением отношений объектов. Hibernate может автоматически обнаруживать так называемое грязное состояние ваших объектов, MyBatis не может, так как это никогда не было частью описания работы. Таким образом, вы остаетесь на своих устройствах.

Супер простой подход состоял в том, чтобы сохранить хэш-код пользователя после выбора и проверить, изменился ли этот хэш-код с помощью метода isUserDirty(). Вы можете просто проверить это условие из вашего сопоставления, используя <if test="isUserDirty">. Это, конечно, не очень общий подход и зависит от достойной реализации hashCode(). Посмотрите на ответ leonbloy на аналогичный вопрос для более общего подхода. Конечно, это все равно может быть слишком простым, особенно потому, что мы говорим много со многими отношениями. Какой подход лучше всего зависит от вашего дела.

Теперь вы должны знать, что делать. Удачи!

PS вместо того, чтобы вставлять и удалять delta, я бы посоветовал простую перезапись: удалить все, а затем вставить все, в транзакцию. Ваша временная стратегия таблицы - это стратегия оптимизации, которая фактически не может улучшить производительность вашей базы данных вообще, на самом деле я предполагаю, что она, вероятно, еще хуже. Если вы правильно профилировали эту стратегию и знаете, что делаете, вы можете игнорировать эту постскриптум.