我有一个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> {
}
然而,在这个控制器中 其实我需要懒惰数据。我如何才能触发它的加载?
试图访问它将失败
懒惰初始化角色集合失败:no.dusken.momus.model.Person.role,无法初始化代理 - 没有Session
或其他例外情况,取决于我的尝试。
我的 xml-描述,以备不时之需。
谢谢,我有一个人物类:
你必须对懒惰集合进行显式调用以初始化它(通常的做法是调用 .size()
用于此目的)。) 在Hibernate中,有一个专门的方法来实现这个目的(Hibernate.initialize()
),但JPA没有与之对应的方法。当然,你必须确保在会话仍然可用的情况下完成调用,所以在你的控制器方法中注解了 @Transactional
. 另一种选择是在Controller和Repository之间创建一个中间服务层,它可以暴露初始化懒惰集合的方法。
请注意,上面的解决方案很简单,但会导致对数据库进行两个不同的查询(一个是用户,另一个是角色)。如果你想获得更好的性能,请在你的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的 领取加入 子句来急切地在一次往返数据库的过程中加载角色关联,因此将减轻上述解决方案中两个不同查询所产生的性能惩罚。
虽然这是一个老帖子,但请考虑使用@NamedEntityGraph(Javax Persistence)和@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);
}
你有一些选择
更多的工作,最好的性能。
更少的工作,通常在web环境下可以接受。
工作量较少,当OEMIV不在选项中时很有用,例如在Swing应用程序中,但在存储库实现中可能也很有用,可以一次性初始化任何实体。
对于最后一个选项,我写了一个实用类。JpaUtils 来初始化某个deph的实体。
例如,它只能在事务中被懒惰地加载。
@Transactional
public class RepositoryHelper {
@PersistenceContext
private EntityManager em;
public void intialize(Object entity, int depth) {
JpaUtils.initialize(em, entity, depth);
}
}
它只能在事务中被懒惰地加载。所以你可以访问你的仓库中的集合,它有一个事务--或者说是 我通常做的是一个 get with association
或将fetchmode设置为eager。
我想你需要 OpenSessionInViewFilter 在视图渲染过程中保持会话打开(但这不是太好的做法)。
JpaRepository
春天的数据 JpaRepository
定义了以下两个方法。
getOne
,它返回一个 实体代理 适宜于设置一个 @ManyToOne
或 @OneToOne
母会 当持久化一个子实体时.findById
在运行从关联表加载实体的SELECT语句后,返回实体POJO。然而,在你的情况下,你没有打电话给任何一个。getOne
或 findById
:
Person person = personRepository.findOne(1L);
所以,我假设 findOne
方法是你在 PersonRepository
. 然而, findOne
方法在你的情况下不是很有用。因为你需要获取 Person
随着是 roles
集合,最好使用 findOneWithRoles
方法代替。
您可以定义一个 PersonRepositoryCustom
接口,如下。
public interface PersonRepository
extends JpaRepository<Person, Long>, PersonRepositoryCustom {
}
public interface PersonRepositoryCustom {
Person findOneWithRoles(Long id);
}
然后像这样定义它的实现
public class PersonRepositoryImpl implements PersonRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public Person findOneWithRoles(Long id)() {
return entityManager.createQuery("""
select p
from Person p
left join fetch p.roles
where p.id = :id
""", Person.class)
.setParameter("id", id)
.getSingleResult();
}
}
就是这样!
你也可以像这样做。
@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()nin你就能得到懒惰初始化列表的大小,而不用获取列表本身。