управление ассоциациями по нескольким запросам в приложении Grails - PullRequest
0 голосов
/ 21 января 2010

В настоящее время я внедряю веб-приложение grails с несколькими сложными формами, в которых изменения в графе ассоциаций должны управляться «в памяти» (то есть в сеансе http) до тех пор, пока объект или домен верхнего уровня объект не сохранен.

, например

сверху вниз: Документ -> Категории -> Подкатегории ...

требование: изменения в документе / категориях / подкатегориях должны сохраняться только при сохранении документа и ни при каких других обстоятельствах.

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

// update some abstract association
for (def Iterator it = documentInstance.association.iterator(); it.hasNext();)  {
  if (!session.association.contains(it.next().someEntity.id))  {
    it.remove()
  }
}

for (def roleTypeId in session.association)  {
  // add/update association
  ... 
}

код подсказки становится еще хуже, когда дело доходит до фактического изменения данных, например категории, что означает, что измененный объект категории должен быть отсоединен / повторно присоединен / объединен при сохранении сущности верхнего уровня.

Мне было бы очень интересно, чтобы вы подумали о такой длительной единице работ.

Ответы [ 2 ]

2 голосов
/ 27 января 2010

Вы можете использовать шаблон сеанса для разговора a.k.a. "длинные разговоры". Попробуйте подключаемый модуль Grails для веб-потока, который работает таким образом, или, если вы считаете, что веб-поток не подходит для ваших нужд, самостоятельно внедрите сеанс для разговора.

Основная предпосылка - в начале разговора вы открываете новый сеанс гибернации (с режимом сброса = ручной) и сохраняете его в сеансе http пользователя. В начале каждого последующего http-запроса необходимо убедиться, что sessionFactory.getCurrentSession возвращает сеанс гибернации диалога, и не забудьте отключить этот сеанс в конце каждого запроса, чтобы закрыть соединение jdbc между запросами. Когда вы достигаете конца разговора, вы сбрасываете сеанс, чтобы сохранить все изменения, или закрываете, не сбрасывая, чтобы отменить их.

Веб-сайт hibernate / Java Persistence с книгой Hibernate содержит некоторые действительно полезные сведения о том, как это сделать, но кроме веб-потока в Grails отсутствует встроенная поддержка. Я нахожусь в процессе написания плагина SessionPerConversation, но это очень рано. Мой подход состоял в том, чтобы посмотреть на исходный код Grails 1.2.0 и скопировать, как они реализовали .withNewSession, а затем украсить мои контроллеры методами .withConversation, .endConversation и .discardConversation. Когда я продвинусь немного дальше, я, вероятно, опубликую код на State Your Bizness

Готы, с которыми я сталкивался до сих пор ...

  1. Если пользователь никогда не заканчивает свой диалог, сеанс гибернации будет оставаться открытым (хотя и не соединение jdbc), пока время его сеанса http не истечет. Если вы поддерживаете несколько разговоров, то у каждого пользователя может быть несколько сессий hiberate, а для сайта с высокой нагрузкой могут возникнуть проблемы с памятью

  2. Вы должны следить за автоматической очисткой сеанса. Это может произойти, когда вы создаете новые объекты в зависимости от того, какую стратегию вы используете для генерации идентификатора, или если вы вызываете транзакционные услуги .

0 голосов
/ 21 января 2010

Некоторые мысли:

Вырвите свой код из контроллера и поместите его в сервис.

Установите для статического свойства транзакции службы значение false и установите контроль над транзакцией. Это может выглядеть примерно так:

class DocumentService {
    // take control from spring
    static transactional = false

    void updateMethod() {
        Document.withTransaction { transact ->
            // handle your business

            // problems? - you can always rollback without breaking anything
            transact.setRollbackOnly()
        }
    }
}

Это позволит вам внедрить сервис в ваш контроллер с помощью строки 'def documentService'. Вы можете обрабатывать всю свою логику в сервисе и проверять все более тщательно.

...