There are two JPA entities: User and Order with one-to-many relationship.
/**
* User DTO
*/
@Entity
@Table(name="user")
public class User implements Serializable {
private static final long serialVersionUID = 8372128484215085291L;
private Long id;
private Set<Order> orders;
public User() {}
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequenceUser")
public Long getId() {
return this.id;
}
private void setId(Long id) {
this.id = id;
}
@OneToMany(mappedBy="user", cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
@LazyCollection(LazyCollectionOption.EXTRA)
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
/**
* Order DTO
*/
@Entity
@Table(name="order")
public class Order implements Serializable {
private static final long serialVersionUID = 84504362507297200L;
private Long id;
private User user;
public Order() {
}
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequenceOrder")
public Long getId() {
return this.id;
}
private void setId(Long id) {
this.id = id;
}
@ManyToOne
@JoinColumn(name="user_id")
public User getUser(){
return user;
}
public void setUser(User user){
this.user = user;
}
}
I use these entities in my service layer classes where every method runs in transaction. Everything is fine except cases when methods of service layer classes must return these entities.
@Transactional(readOnly=true)
public Set<Order> getOrders() {
Set<Order> orders = user.getOrders();
return orders;
}
This method returns data well. But when I try access to received collection elements I catch exception: "org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: package.User.orders, no session or session was closed".
So, it was excepted. I thought that detaching result will solve my problem, but trick like this
@Transactional(readOnly=true)
public Set<Order> getOrders() {
Set<Order> orders = user.getOrders();
for(Order order: orders)
entityManager.detach(order);
return orders;
}
didn't change anything :(
It doesn't matter for me will info about users attend in set of orders or not. I just want to work with this set and not going to modify it.
Can anybody help me? :)
void detach( Object entity. Remove the given entity from the persistence context, causing a managed entity to become detached. Unflushed changes made to the entity if any (including removal of the entity), will not be synchronized to the database.
LAZY it is interpreted as a hint to the JPA provider that the loading of that field may be delayed until it is accessed for the first time: the property value in case of a @Basic annotation, the reference in case of a @ManyToOne or a @OneToOne annotation, or.
Implementing a Lazy-Initialized Property To implement a public property by using lazy initialization, define the backing field of the property as a Lazy<T>, and return the Value property from the get accessor of the property. The Value property is read-only; therefore, the property that exposes it has no set accessor.
LAZY tells Hibernate to only fetch the related entities from the database when you use the relationship. This is a good idea in general because there's no reason to select entities you don't need for your uses case. You can see an example of a lazily fetched relationship in the following code snippets.
This method returns data well. But when I try access to received collection elements I catch exception: "org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: package.User.orders, no session or session was closed".
The error is self explaining: you are trying to load a (lazy) loaded collection but there is no active session anymore because the User
instance is detached.
So, it was excepted. I thought that detaching result will solve my problem, but trick like this
This won't change anything. The EntityManager#detach(Object)
method doesn't load anything, it removes the passed entity from the persistence context, making it detached.
It doesn't matter for me will info about users attend in set of orders or not. I just want to work with this set and not going to modify it.
You need to either make the association EAGER (so that Orders will be loaded when retrieving a User) or to use a FETCH JOIN when retrieving a user in your service:
SELECT u
FROM User u LEFT JOIN FETCH u.orders
WHERE u.id = :id
Do I understand correctly that hibernate has no standard mechanism for force load all lazy associations of current object
Hibernate has a Hibernate.initialize
method, but this is obviously not standard JPA (prefer fetch join for portable code).
I have no way to make hibernate ignore lazy associations of current object (set them as null)
What? What do you mean by ignore? Why would you do that anyway?
The LazyInitialisationException occurs because data is requested outside a transaction. If you need that data, you must request it within a transaction - in your case, this would be within the service method.
You can request the data to be loaded in several ways. The easiest:
for(Order order: orders)
order.getX()
(where X is some property other than Id or version)
This however will load each order in a seperate query, which is slow if there are many orders. A faster approach would be to issue a query (JP-QL or Criteria) returning all orders of that user. (Edit: See Pascal's answer for a suitable query.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With