比如有两个实体A, B是多对多关系,并且相互指定默认使用lazy加载
@Entity
public class EntityA implements Serializable {
...
@ManyToMany(fetch = FetchType.LAZY)
private List<EntityB> entityBList = new ArrayList<>();
...
}
@Entity
public class EntityB implements Serializable {
...
@ManyToMany(mappedBy = "entityBList", fetch= FetchType.LAZY)
private List<EntityA> entityAList = new ArrayList<>();
...
}
在单元测试的时候,如果通过A获取B的List,则会抛出LazyInitializationException
@Test
public void test() throws Exception {
EntityA a = dao.findOne(id);
List<EntityB> bList = a.getEntityBList();
bList.size(); // 这里抛出异常
}
当然,解决办法加一个@Transactional倒是可以、但如果单独把find方法提出来作为一个新方法又不行~
@Test
public void test() throws Exception {
this.getAwithLazyData();
}
@Transactional
public void getAwithLazyData(id) {
EntityA a = dao.findOne(id);
List<EntityB> bList = a.getEntityBList();
bList.size(); // 这里仍然抛出异常
}
当然,如果将fetch= FetchType.LAZY改为fetch= FetchType.EAGER是不会抛异常 不过每次查询就都会带出关联数据了。
如何在特定的时候带出关联数据:
(其实还是session过期了,数据没有取出来)之前调用的findOne方法,则查询结果中的entityBList为lazy数据,无法取出,解决办法就是如果调用findWithEntityBById方法,则查询结果中已经带出了entityBList,可以直接使用。
使用@NamedEntityGraph相关注解
@Entity
@NamedEntityGraph(name = "EntityA.lazy", attributeNodes = {@NamedAttributeNode("entityBList")})
public class EntityA implements Serializable {
...
@ManyToMany(fetch = FetchType.LAZY)
private List<EntityB> entityBList = new ArrayList<>();
...
}
~~对应Dao层
@Repository
public interface EntityADao extends SimpleJpaRepository<EntityA, Long> {
@EntityGraph(attributePaths = { "entityBList" })
EntityA findWithEntityBById(Long id);
}
或者也可以直接取出来~
@Query的sql语句使用join fetch
public interface PersonRepository extends JpaRepository<Person, Long> {
@Query("SELECT p FROM Person p JOIN FETCH p.roles WHERE p.id = (:id)")
public Person findByIdAndFetchRolesEagerly(@Param("id") Long id);
}
这样也就是把数据先初始化,防止session过期后再去取就报异常了