fernando1979
fernando1979

Reputation: 1947

Why JPA entities are treated like this outside a session?

Hy,

I am having a "Solve failed to lazily initialize a collection of role.... exception" in jpa. I understand that outside a session, when you want to retrieve a lazy collection if there is not session bound you will get this error, fine. But what I dont understand is if I have this spring controller(the code is not 100% correct, is just to explain my case):

@Controller

@Autowired
EnterpriseService enterpriseService ; 

public List<Enterprise> getAll(){


    List<Enterprise> enterprises = enterpriseService.getAll();      

    for(Enterprise enterprise:enterprises){

        enterprise.getEmployees();

    }
    return enterprises;

}

When I call "enterprise.getEmployees()", I know there is not anymore a session but why when I try to do "enterprise.getEmployees()", why enterprise is treated like a jpa entity and not just like a normal bean?, I mean; for what I understand a jpa entity is treated like this inside a session but outside like in this case it should be treated like a normal java bean so the calling to enterprise.getEmployees() should be treated like the calling of a get method of a java bean and should not throw any lazy exception....

Maybe is because spring controller treats the enterprise objects like jpa entities and not just only like java beans? this behaviour is specific to spring controllers?

Thanks

Upvotes: 0

Views: 159

Answers (2)

Tobias Liefke
Tobias Liefke

Reputation: 9022

An entity returned from an EntityManager is not necessarily an instance of your entity class, but a proxy class which extends your class. In many cases the same is true for the persistent properties of such entities (especially for those annotated with (One/Many)To(One/Many)).

For example if you are using field based access:

@Entity
public class Enterprise {
    @OneToMany
    private List<Employee> employees = new ArrayList<>();

    public List<Employee> getEmployees() {
        return employees;
    }
}

Here the JPA provider will create a proxy class that extends Enterprise and remembers the previous state from the DB somehow. In addition it will change the employees list to its own implementation of List (which does not need to extend ArrayList).

Because the proxy class can "overwrite" your methods, it could know when you are calling getEmployees() and check for the session. But I don't think that this would happen here, as the method is not annotated with any JPA specific annotation.

In addition some frameworks like Hibernate do support byte code enhancement or byte code instrumentation. That changes the implementation of your class (in byte code) and replaces every access to employees with some provider specific code. I don't know if Spring JPA provides this, but this could lead to check for a session.

Otherwise any call to enterprise.getEmployees() should just return the current value of employees - without any check for the session and without the LazyInitializationException.

But calling enterprise.getEmployees().size() will try to initialize the list and check for the session - this could lead to the mentioned exception.

If you are using property based access things are a little bit different:

@Entity
public class Enterprise {
    private List<Employee> employees = new ArrayList<>();

    @OneToMany
    public List<Employee> getEmployees() {
        return employees;
    }
}

Here the proxy class will not delegate to your implementation, but overwrite the getEmployees() method and return its own List implementation, without changing employees. Thus here it is possible to get a LazyInitalizationException for enterprise.getEmployees().

Remark: This describes how most JPA implementations are working - but as this is implementation specific some unusual frameworks could do things differently.

Upvotes: 1

JB Nizet
JB Nizet

Reputation: 691735

It can't do anything else. The only alternative would be to return an empty collection of employees, which would be much much worse: you would incorrectly assume that the enterprise has 0 employee, which is a valid, but completely incorrect result.

To realize how much worse it would be to do that, let's imagine a HospitalAnalysis entity, having a collection of DetectedDisease entities. And let's say you try to display the result of the analysis but forget to initialize the collection. The page would tell you that you're perfectly healthy and that you can safely go home, whereas in reality, you have a cancer, and the program has a bug. I'd much prefer the program to crash with an exception and be fixed rather than not starting my treatment.

Trying to access employees without having initialized the collection, and thus without knowing the actual collection of employees, is just a bug. This bug is signalled by throwing a runtime exception.

Upvotes: 0

Related Questions