如何从我的控制器中加载Hibernate / JPA中的延迟获取项目
我有一个Person类:
@Entity public class Person { @Id @GeneratedValue private Long id; @ManyToMany(fetch = FetchType.LAZY) private List<Role> roles; // etc }
有一个懒惰的多对多的关系。
在我的控制器中,我有
@Controller @RequestMapping("/person") public class PersonController { @Autowired PersonRepository personRepository; @RequestMapping("/get") public @ResponseBody Person getPerson() { Person person = personRepository.findOne(1L); return person; } }
而PersonRepository就是这个代码,按照这个指南写的
public interface PersonRepository extends JpaRepository<Person, Long> { }
但是,在这个控制器中我实际上需要懒惰数据。 我怎样才能触发它的加载?
试图访问它将失败
未能懒惰地初始化一个angular色集合:no.dusken.momus.model.Person.roles,无法初始化代理 – 没有会话
或其他例外取决于我的尝试。
我的xml描述 ,在需要的情况下。
谢谢。
你将不得不对lazy集合进行明确的调用来初始化它(通常的做法是为此调用.size()
)。 在Hibernate中有一个专用的方法( Hibernate.initialize()
),但是JPA没有这个方法。 当然,你必须确保调用完成,当会话仍然可用时,用@Transactional
注释你的控制器方法。 另一种方法是在Controller和Repository之间创build一个中间服务层,以便公开初始化懒惰集合的方法。
更新:
请注意,上面的解决scheme很简单,但会导致对数据库的两个不同的查询(一个用于用户,另一个用于angular色)。 如果您想要获得更好的性能,请将以下方法添加到您的Spring Data JPA存储库接口中:
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); }
这个方法将使用JPQL的fetch连接子句来在数据库中一次往返加载angular色关联,从而减轻上述解决scheme中两个不同查询产生的性能损失。
虽然这是一个旧的post,请考虑使用@NamedEntityGraph(Javax持久性)和@EntityGraph(Spring Data JPA)。 组合起作用。
例
@Entity @Table(name = "Employee", schema = "dbo", catalog = "ARCHO") @NamedEntityGraph(name = "employeeAuthorities", attributeNodes = @NamedAttributeNode("employeeGroups")) public class EmployeeEntity implements Serializable, UserDetails { // your props }
然后如下所示的春季回购
@RepositoryRestResource(collectionResourceRel = "Employee", path = "Employee") public interface IEmployeeRepository extends PagingAndSortingRepository<EmployeeEntity, String> { @EntityGraph(value = "employeeAuthorities", type = EntityGraphType.LOAD) EmployeeEntity getByUsername(String userName); }
你有一些select
- 在存储库中写一个方法,按照RJ的build议返回一个初始化的实体。
更多的工作,最好的performance。
- 使用OpenEntityManagerInViewFilter保持会话打开整个请求。
较less的工作,通常可以在networking环境中使用。
- 需要时使用助手类来初始化实体。
当OEMIV没有select时,例如在Swing应用程序中,工作量较less,有用,但是在存储库实现中也可以用于一次初始化任何实体。
对于最后一个选项,我写了一个实用程序类JpaUtils来初始化某些实体的实体。
例如:
@Transactional public class RepositoryHelper { @PersistenceContext private EntityManager em; public void intialize(Object entity, int depth) { JpaUtils.initialize(em, entity, depth); } }
它只能在交易过程中被延迟加载。 所以你可以访问你的仓库中的集合,这个集合有一个事务 – 或者我通常做的是一个get with association
,或者设置fetchmode为渴望。
我认为你需要OpenSessionInViewFilter来保持你的会话在视图渲染过程中打开(但这不是太好的做法)。
你可以这样做:
@Override public FaqQuestions getFaqQuestionById(Long questionId) { session = sessionFactory.openSession(); tx = session.beginTransaction(); FaqQuestions faqQuestions = null; try { faqQuestions = (FaqQuestions) session.get(FaqQuestions.class, questionId); Hibernate.initialize(faqQuestions.getFaqAnswers()); tx.commit(); faqQuestions.getFaqAnswers().size(); } finally { session.close(); } return faqQuestions; }
只要在你的控制器中使用faqQuestions.getFaqAnswers()。size(),你将得到大小,如果懒惰的初始化列表,而没有获取列表本身。