I have entity with some fields marked as LAZY
. In order to load LAZY
fields one must just access a getter while session is opened so ORM proxy perform select
sub-queries.
But this is suboptimal in case when you need a fully resolved object, using the same strategy as "eager" relations fetched by join
.
How is it possible to mark some fields as EAGER
temporary only for one query?
Is that possible with JPA standard or does it require proprietary extension like Hibernate?
Hibernate has a feature called Fetch Profiles that solves this problem. It requires access to Hibernate Session
, but you can use unwrap()
to access it from EntityManager
.
If you want a pure JPA solution, you can use queries with join fetch
when loading objects in use cases that require eager fetching.
UPDATE: JPA 2.1 (implemented by Hibernate 4.3) supports a feature similar to fetch profiles - entity graphs.
Spring JpaRepository
allows marking queries (including custom) with org.springframework.data.jpa.repository.EntityGraph
:
@Entity Book {
@Id Long id;
@OneToMany List<Author> authors;
}
@EntityGraph(attributePaths = {"authors", "author.address"})
@Query("select b from Book b" +
" where b.id in (:ids)")
List<Book> loadAll(@Param("ids") List<Long> ids);
Due to fetch = FetchType.LAZY at the associated collection Faced problem like org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: could not initialize proxy-no session
Now changing fetch = FetchType.LAZY to fetch = FetchType.EGAR
is the worst solution as it opens to some other regerations
Then i thought what if there is a way to make fetch type lazy to egar at runtime for some operations...
Then i tried to understand the problem like why i faced LazyInitializationException
SO i realized that this problems occurs because we are doing db operation after the session is closed so we have to make all db operations within the session
So the solution is to wrap the method which has as db operation performed within a Transaction . Check below code snippet-->
@Transactional
public void fetchThroughLazy() {
List<UserEntity> userList = aeqplUserRepository.findByEmailEndingWith("@amazon.com");
userList.forEach(user->{
logger.info("user --->{}",user);
logger.info("user permission--->{}",user.getPermissions());
});
}
But again this is not a good solution because it will lead to Hibernate N+1 problem
Best Solution---> Either Use EntityGraph or Join Fetch
Check Below code snippet --->>
public void fetchThroughJPQLJoinFetch() {
List<UserEntity> userList = entityManager.createQuery("select distinct u from UserEntity u left join fetch u.permissions Where u.email like :endsWith",UserEntity.class)
.setParameter("endsWith", "%@google.com")
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
userList.forEach(user->{
logger.info("user --->{}",user);
logger.info("user permission--->{}",user.getPermissions());
});
}
public void fetchThroughJPQLEntityGraph() {
EntityGraph<UserEntity> userEntityGraph = em.createEntityGraph(UserEntity.class);
userEntityGraph.addSubgraph("permissions");
List<UserEntity> userList = em.createQuery("Select u From UserEntity u Where u.email Like :endsWith",UserEntity.class)
.setParameter("endsWith", "%@facebook.com")
.setHint(QueryHints.LOADGRAPH, userEntityGraph)
.getResultList();
userList.forEach(user->{
logger.info("user --->{}",user);
logger.info("user permission--->{}",user.getPermissions());
});
}
@Entity
@Table(name="user")
public class UserEntity {
@Id
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@OneToMany(mappedBy="userEntity",cascade = CascadeType.All,fetch= FetchType.LAZY)
private List<PermissionEntity> cmList =new ArrayList<>();
//setters and getters
}
@Entity
@Table(name="permission")
public class PermissionEntity {
@Id
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "type")
private String type;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name = "user_id")
private UserEntity userEntity;
//setters and getters
}
Note-> Avoid using Join Fetch more Than once otherwise it will throw MultipleBagFetchException
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