Hibernate -> LazyInitializationException с отношением n: m - PullRequest
1 голос
/ 12 октября 2010

У меня проблема с Hibernate и LazyInitializationException.Я искал и находил много ответов, но не могу использовать их для решения своей проблемы, потому что должен сказать, что я новичок в Hibernate.

Я запускаю тесты JUnit, в случае ошибки эта:

@Test
public void testAddPerson() {
    Set<Person> persons = service.getAllPersons();

    // create new person
    Person person = new Person();
    person.setEmail("john@doe.com");
    Project testProject = serviceProj.findProjectById(1);

    HashSet<Project> lister = new HashSet<Project>();
    lister.add(testProject);
    person.setProjects(lister);
    service.addPerson(person);

    testProject.getPersons().add(person);
    ...
}

Последняя показанная строка:

testProject.getPersons().add(person);

выдает эту ошибку:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.doe.john.domain.Project.persons, no session or session was closed

Персона и Проект являются двунаправленными n: m:

Person.java :

@ManyToMany(mappedBy="persons")
private Set<Project> projects = new HashSet<Project>();

Project.java :

@ManyToMany
@JoinTable(name = "Project_Person",
    joinColumns = {@JoinColumn(name="project_id", referencedColumnName="id")},
    inverseJoinColumns = {@JoinColumn(name="person_id", referencedColumnName="id")}
)
private Set<Person> persons = new HashSet<Person>();

Так в чем проблема?

Ответы [ 4 ]

1 голос
/ 12 октября 2010

Поместите свой код в транзакцию - это решит проблему для вас

1 голос
/ 12 октября 2010

Проблема в том, что по умолчанию коллекция загружается лениво.Это означает, что он не будет загружен из базы данных до тех пор, пока к нему не будет получен доступ.Чтобы загрузить его, вам потребуется активная сессия / транзакция.

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

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

public void addPersonTo(Project project, Person person)
{
  project = em.merge(project); //EntityManager, not sure what you are using but you get the idea hopefully
  project.addPerson(person);
}
0 голосов
/ 12 октября 2010

Из спящего режима ссылка:

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

Если вы установите hibernate.default_batch_fetch_size, Hibernate будет использовать оптимизацию пакетной выборки для отложенной выборки. Эту оптимизацию также можно включить на более детальном уровне.

Обратите внимание, что доступ к ленивым ассоциациям вне контекста открытого сеанса Hibernate приведет к исключению. Например:

s = sessions.openSession();
Transaction tx = s.beginTransaction();

User u = (User) s.createQuery("from User u where u.name=:userName")
.setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();

tx.commit();
s.close();

Integer accessLevel = (Integer) permissions.get("accounts");  // Error!

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

Кроме того, вы можете использовать не ленивую коллекцию или ассоциацию, указав lazy = "false" для отображения ассоциации. Однако предполагается, что ленивая инициализация будет использоваться почти для всех коллекций и ассоциаций. Если вы определите слишком много не ленивых ассоциаций в вашей объектной модели, Hibernate будет извлекать всю базу данных в память при каждой транзакции.

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

Таким образом, вы должны закрыть сеанс после доступа к коллекции!

Вместо:

service.addPerson(person);

testProject.getPersons().add(person);

Я думаю, вы должны иметь:

testProject.getPersons().add(person);
service.addPerson(person);
0 голосов
/ 12 октября 2010

Можете ли вы попробовать

person.getProjects().Add(testProject)

вместо

HashSet<Project> lister = new HashSet<Project>();
lister.add(testProject);
person.setProjects(lister);

Вы должны делать это, так как в противном случае вы удалили бы управляемую коллекцию в спящем режиме.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...