开发者

Hibernate -> LazyInitializationException with n:m relation

I have a problem with Hibernate and the LazyInitializationException. I searched and find a lot of answers, but I can not use them to solve my problem, because I have to say, I am new to Hibernate.

I run JUnit tests, in case of the error this one:

@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);
    ...
}

The last shown line:

testProject.getPersons().add(person);

throws this error:

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

Person and Project are bidirectional 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", referenc开发者_如何学GoedColumnName="id")},
    inverseJoinColumns = {@JoinColumn(name="person_id", referencedColumnName="id")}
)
private Set<Person> persons = new HashSet<Person>();

So what's the problem?


The problem is that by default the collection is lazily loaded. This means that it wont actually be loaded from the database until it is being accessed. In order to load it you will need a active session/transaction.

The easy way out is to change it to FethType.EAGER which makes sure that the collection is populated straight away.

--update--

I've recently had the very same problem and I ended up modifying my actual service to deal with this sort of thing. Declare a addPerson method in your ProjectService class.

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);
}


Put your code inside a transaction - this will solve the problem for you


Can you try

person.getProjects().Add(testProject)

instead of

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

You should be doing this since otherwise you would be blowing away a hibernate managed collection.


From hibernate reference:

By default, Hibernate3 uses lazy select fetching for collections and lazy proxy fetching for single-valued associations. These defaults make sense for most associations in the majority of applications.

If you set hibernate.default_batch_fetch_size, Hibernate will use the batch fetch optimization for lazy fetching. This optimization can also be enabled at a more granular level.

Please be aware that access to a lazy association outside of the context of an open Hibernate session will result in an exception. For example:

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!

Since the permissions collection was not initialized when the Session was closed, the collection will not be able to load its state. Hibernate does not support lazy initialization for detached objects. This can be fixed by moving the code that reads from the collection to just before the transaction is committed.

Alternatively, you can use a non-lazy collection or association, by specifying lazy="false" for the association mapping. However, it is intended that lazy initialization be used for almost all collections and associations. If you define too many non-lazy associations in your object model, Hibernate will fetch the entire database into memory in every transaction.

On the other hand, you can use join fetching, which is non-lazy by nature, instead of select fetching in a particular transaction. We will now explain how to customize the fetching strategy. In Hibernate3, the mechanisms for choosing a fetch strategy are identical for single-valued associations and collections.

So you have to close the session after accessing the collection!

Instead of :

service.addPerson(person);

testProject.getPersons().add(person);

I think you should have:

testProject.getPersons().add(person);
service.addPerson(person);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜