Spring @Transactional - использование коллекций после сохранения / слияния - PullRequest
1 голос
/ 19 ноября 2010

Я использую Spring и JPA в своем приложении, но в последнее время я немного запутался с использованием коллекций из DomainObjects.

Например:

Представьте, что у нас есть две таблицы: родительская и детская. Родитель может иметь несколько детей (@OneToMany), а у ребенка может быть только один родитель (@ManyToOne). Я использую Spring для обработки моего контекста транзакции в моем ServiceLayer (не в шаблоне Dao - Unit of Work), который настроен в context-application.xml с помощью aop: config. Детские коллекции помечены как LazyLoading Collection. До сих пор все работало нормально.

Проблема теперь заключается в следующем:

Мне нужен метод в моей службе, который создает новый родительский элемент и вызывает другой метод в той же транзакции, чтобы выполнить некоторые вычисления с вновь созданным родительским элементом. В рамках этих расчетов я должен вызвать parent.getChildren (). Очевидно, это должно вернуть пустой набор, так как новый родительский объект не может иметь дочерних элементов во время создания, но по какой-то причине я возвращаю 'null' вместо пустого набора, что приводит к исключению NullpointerException

Чтобы создать родителя, я попробовал следующие методы EntityManager:

  1. с использованием entityManager.persist (parent) - не сработало, коллекция равна нулю после создания.
  2. Использование entityManager.merge (parent) и возврат нового управляемого экземпляра parent - имели тот же результат.

Оба привели к нулевому набору. Но я почти уверен, что эти вызовы находятся внутри одной и той же транзакции, потому что за пределами транзакции я получил бы исключение LazyLoadingException (вместо NullPointer), которое (afaik) является правильным в данный момент. С другой стороны, использование уже существующего родителя с entityManager.findById () привело к существующей коллекции (что означает, что она работает так, как ожидалось). Чтобы решить эту проблему, я попробовал несколько подходов, пока не нашел работающий:

entityManager.refresh (parent) после слияния / сохранения завершен, так как коллекции инициализируются при вызове refresh.

Но в уроках я никогда не видел, чтобы этот метод использовался. В JavaDoc EntityManager сказано, что слияние возвращает новые управляемые bean-компоненты, в то время как persist делает саму ссылку управляемой. Я догадался, что управляемый будет означать, что я уже могу использовать эти Коллекции в транзакции. Почему коллекции не инициализируются во время создания? Должен ли я инициализировать их самостоятельно в конструкторе Parent, например?

Интересно, сделал ли я что-то не так, или это правильный способ вызвать refresh () после того, как я создал новый Instance of Parent? Если да, то мне просто нужно разделить между постоянным и обновленным, как это рекомендуется в следующей статье: Реализации JPA и обновить вновь созданный объект после сохранения? Или я должен просто обновить Объект, когда мне действительно нужно использовать Коллекцию?

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

Большое спасибо,
ymene

Ответы [ 2 ]

4 голосов
/ 19 ноября 2010

Вы инициализируете свои поля коллекции?

@OneToMany
private Set<Foo> foos = new HashSet<Foo>();

, а не просто

@OneToMany
private Set<Foo> foos;

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

0 голосов
/ 19 ноября 2010

Попробуйте использовать CascadeType.ALL в родительском классе.

@OneToMany(cascade = CascadeType.ALL)
public List<Child> getChildren() {
  return children;
}

Во-вторых, используйте OpenSessionInViewFilter. Это гарантирует, что вы можете вызывать parent.getChildren () даже из представлений, и сеанс гибернации остается открытым, пока ответ не будет отправлен обратно. Ниже описано, как вы можете настроить его в своем файле web.xml

<filter>
 <filter-name>OpenSessionInViewFilter</filter-name>
 <filter-class>com.isavera.hibernate.OpenSessionFilter</filter-class>
   <init-param>
     <param-name>singleSession</param-name>
 <param-value>false</param-value>
   </init-param>
 </filter>

<filter-mapping>
 <filter-name>OpenSessionInViewFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

Надеюсь, это поможет

...