Matjaz
Matjaz

Reputation: 484

hibernate lazy error but I don't want those data

I have POJO with couple of strings/numbers/... and I want to get only this pojo, without any sub-POJOs in lists/maps/sets that could be attached by hibernate. If I execute query to get first pojo, exception is thrown when someone touches those subobjects.

This is normal behaviour and I agree with this, but is there a way, not to call this:

object.setSomeMap(null);

but to set it for default behaviour? I don't see setting each property like this as best solution as I have quite big model for everything.

Thanks!

Upvotes: 0

Views: 318

Answers (1)

JamesENL
JamesENL

Reputation: 6540

Set your FetchType to FetchType.LAZY and they won't be loaded when you load your class from the database. I'll give you an example:

@Entity
@Table(name="FOO")
public class Foo{

    @Id
    @GeneratedValue
    @Column
    private int id;

    @Column
    private String prop1;

    @Column
    private String prop2;

    //You don't have to set fetch type like this, as lazy is the default for collections,
    //but it doesn't really hurt anything
    @OneToMany(fetch=FetchType.LAZY)
    private List<Bar> listOfBars;
}

So when you load up your Foo class, id, prop1 and prop2 will be loaded and set from the database, but the listOfBars objects will not be populated.

Whenever you try to access the listOfBars objects, Hibernate detects it and tries to populate the objects before you can get it. AFAIK if you try and set listOfBars to null, Hibernate will detect this and try to delete your foreign keys, effectively removing all the members of listOfBars if you update the object. Eg:

@Transactional
public Foo doStuffWithFoo(int id){
    Foo foo = fooDao.find(id);
    //Set listOfBars to null so we can't call it.
    //This is bad and will cause disaster when we update Foo.
    foo.setListOfBars(null);

    //Do some stuff with Foo that changes values.

    fooDao.saveOrUpdate(foo);
    return foo;
}

Because you set your listOfBars object to null here, AFAIK Hibernate will then delete all your foreign keys because your list doesn't exist any more. Hibernate knows the difference between a list that is empty because it didn't populate it when it created the object and a list that you have set to null/emptied. If the case of the former, it doesn't do anything except perhaps cascade the updated status if it needs to. In the case of the latter, it will clear your associations, possibly deleting Bar objects, if you have orphanRemoval set to true.

However if you do try to access your listOfBars once you have returned Foo out of the method you get another problem. This problem occurs because the @Transactional session you originally used to fetch your Foo class has ended before you try to access your listOfBars. You have 3 options at this point, each with their own problems.

The first option (which is what most people try, and get frustrated because it doesn't work) is you can open a new session, and try to populate your listOfBars object. Hibernate assumes that both your Foo object and all the Bar objects in your listOfBars could have changed since the transaction was ended (which is perfectly true), so it throws an exception.

Another option is to fetch the entire Foo object again and populate the listOfBars object in your new transaction. The problem with this is that its quite inefficient to be doing multiple selects like this.

Your final option is to never try and access your listOfBars object if you know that its not there. You can't even perform a null check because Hibernate will detect your call to getListOfBars() and try and populate the object before the null check actually happens.

I have written a Hibernate tutorial for creating object relationships here that you might find helpful. I also discuss things like Lazy vs Eager object fetching for collections as well as a few other Hibernate things that can be helpful when designing classes for use with Hibernate.

Best of luck

Upvotes: 1

Related Questions