views:

59

answers:

2

I though I understood lazy/eager loading, but obviously I don't:

I have a service that is marked @Transactional, yet when I try to manipulate the list I get (accessing its objects), I get "org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: tld.myproduct.data.entities.CategoryType.translatableText, no session or session was closed". Any suggestions to why I cannot access my under-objects?

UPDATE: The exception happens at the line "type.getTranslatableText().size()"

Cheers

Nik

// The service
@Service("categoryTypeService")
@Transactional("transactionManager")
public class CategoryTypeServiceImpl extends AbstractCRUDServiceImpl<CategoryType, Integer> implements CategoryTypeService {

    @SuppressWarnings("unchecked")
    @Override
    public List<CategoryType> getAll() {
        List<CategoryType> list = DAO.getSession().createCriteria(CategoryType.class).list();

        for(CategoryType type : list)   
            type.getTranslatableText().size();  // Access TranslatableText to demonstrate problem

        return list;
    }

}


// The entity
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
@Configurable(preConstruction=true)
public class CategoryType {

    @Id
    @Column(nullable = false)
    private Integer id;

    @Column(length = 50, nullable = true)
    private String description;

    @Column(name = "TranslatableDescriptionId", nullable = false)
    private Integer TranslatableDescriptionId;

    @OneToMany(fetch=FetchType.LAZY)
    @JoinColumn(name = "Id", referencedColumnName="TranslatableDescriptionId", insertable=false, updatable=false)
    private Set<TranslatableText> translatableText;

    /** getters and setters **/
}
A: 

Are you sure its something to do with @Transactional? To me, its just because your session is already closed. Once you are out of the method, that did the original query, lazy loading wouldn't work, unless you got something like open-session-in-view.

Further, you must present the actual calling code, I mean the code which tries to use the list returned by the method.

Adeel Ansari
I'm sorry I forgot to mention this important detail, the exception happens in the line "type.getTranslatableText().size()", which is inside the same transaction as getAll(), right? So the session shouldn't be closed?
niklassaers
I should also add that I do have open-session-in-view
niklassaers
@niklassaers: Weired indeed.
Adeel Ansari
+1  A: 

Here is a general discussion of Open Session in View issue. I recommend you to read the article.

To put is simple, you have 3 possible solutions:

1) don't do lazy loading of the collection (of course, if it's not critical, performance should be tested somehow):

@OneToMany(fetch=FetchType.EAGER)   //   <-- it's not lazy any more
@JoinColumn(name = "Id", referencedColumnName="TranslatableDescriptionId", insertable=false, updatable=false)
private Set<TranslatableText> translatableText;

2) you can use Open Session in View Filter (or OpenEntityManagerInViewFilter), but this will keep your session open until the page loading will be finished (maybe a few seconds).

It can cause performance issues, and more likely, you'll get the situation, when one user is changing objects while another user has an open hibernate session. I don't know exactly what happens then but you should find it out if you want to be sure that your application is robust.

3) when you need to load some collection from a DB just execute a JPA query and fetch all the necessary data with special DAO method (this means that field translatableText will disappear from CategoryType class). The most robust solution but you'll need to do some extra work to implement it, its more difficult then any of previous 2 options.

Roman
Suggestion #1 was revealing as it caused other errors. It seems Hibernate takes offence at how many places I use this translatableText, so I need to sort that out, and that's most likely the story behind this weird error. So suggestion #2 is probably involved as, with lazy loading, it's already been loaded other places before. I'm not a big fan of suggestion #3, though, as it seems to me that defeats much of the purpose of using JPA in the first place, or have I misunderstood you?
niklassaers