JPA Hibernate Lazy many-to-one fetch proxy
Asked Answered
O

1

6

I'm using JPA 2.1 and Hibernate 4.3.7

I tried to tuned my app so I turn relationships to lazy and fetch only what I need them

I have a problem with the many-to-one relationships, when turn to lazy when I load the entity again Hibernate replace the entity by a proxy even if I fetch the entity and this proxy is not working in the view part (JSF) of the application. The problem disapear when the many-to-one is in eager mode but hibernate perform one select more for each many-to-one even if I don't need them

@Entity
public class Department {
    @Id
    private Integer id;

    //...
}

1/

@Entity
public class Employee {
    @Id
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JoinColumn(name = "id_department", referencedColumnName = "id")
    private Department department;

    //...
}

the JPQL query:

SELECT e FROM Employee e LEFT JOIN FETCH e.department WHERE e.id=:id

=> one select query => faster but department is of type Department_$$_jvst3ac_5f (employee.getDepartment().getClass().getCanonicalName()) and this proxy doesn't work in the view part of the application

2/

@Entity
public class Employee {
    @Id
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER, optional = true)
    @JoinColumn(name = "id_department", referencedColumnName = "id")
    private Department department;

    //...
}

the JPQL query:

SELECT e FROM Employee e WHERE e.id=:id

=> two selects => slower but department is loaded as Department and everything goes fine in the view part of the application

The relation is unidirectional, Department have no references of emplyees

Is this possible to have the department without proxy when using FETCH JOIN?


After the response of Luiggi I will precised that the data are fetched with lazy many-to-one + fetch join. When I do a employee.getDepartment().toString() I have Department{ id=11, ...} but the class of this department is still Department_$$_jvst3ac_5f. For reason I don't know, the JSF/PrimeFaces selectOneMenu component don't work properly whith HibernateProxy even if the data are fetched

I tried to use the Hibernate annotation @LazyToOne(LazyToOneOption.FALSE) in addition of @ManyToOne(fetch = FetchType.LAZY) but the result is similar of @ManyToOne(fetch = FetchType.EAGER) alone...

Ootid answered 31/3, 2015 at 16:1 Comment(1)
Hi, I know it's quite a time you had this problem, but did you find an elegant solution for this problem?Thrust
O
3

The problem is that when you use lazy loading you will obtain a proxy of the class (as you already stated) and this proxy can fetch the data from database only if the hibernatesession is still open. Seems like your session is being closed when returning the data to the view, so when trying to use the lazily-loaded field in the view you're getting the exception.

Possible solutions:

  • Keep the field as fetch eager and pay the overhead for each query against your entity (probably this isn't good and can affect performance, but is a solution).
  • Maintain your field as lazy and use the proper get method before the Hibernate session is closed in order to the proxy to retrieve the relevant data to be used after the session is closed.
Orthopsychiatry answered 31/3, 2015 at 16:7 Comment(6)
Yes, keeping the field as eager seems to be unfortunatly the only solution, to avoid multiple selects I add a LEFT JOIN FETCH Department in evry queries, even if don't need department (As I precised in my post using get is not a solution)Ootid
@Ootid it is a solution, the fact that you cannot use this approach is a different story.Orthopsychiatry
Is this not a bug in hibernate then? If I perform a query within its own transaction, and it has some lazy fields that aren't initialized, and then in a new transaction, I make a different query, and in that case the same entity for the field that was previously lazy loaded is now fetched eagerly from the db, shouldn't that field get updated with the new eagerly fetched entity?Zuniga
@Zuniga basic Java: they are different objects created at a different time, there's no way to synchronize them as the same reference.Orthopsychiatry
perhaps i have a different problem, i'm having this problem where hibernate is lazily loading a specific user record, and then im making a query for that user in another transaction, but the lazy record is still there, and then that's causing problemsZuniga
@Zuniga if those are different transactions then you won't have the same object. I suggest you to create a question with the relevant code to relocate the problem. Probably when you're creating the cider sample you realize how to solve your issue, if not somebody will reply youOrthopsychiatry

© 2022 - 2024 — McMap. All rights reserved.