Java 如何在Spring控制器中获取与JPA和Hibernate的FetchType.LAZY关联
我有个人课:Java 如何在Spring控制器中获取与JPA和Hibernate的FetchType.LAZY关联,java,spring,hibernate,jpa,spring-data,Java,Spring,Hibernate,Jpa,Spring Data,我有个人课: @Entity public class Person { @Id @GeneratedValue private Long id; @ManyToMany(fetch = FetchType.LAZY) private List<Role> roles; // etc } PersonRepository就是这个代码,根据 公共接口PersonRepository扩展了JpaRepository{ } 然而,在这
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
// etc
}
PersonRepository就是这个代码,根据
公共接口PersonRepository扩展了JpaRepository{
}
然而,在这个控制器中,我实际上需要惰性数据。如何触发其加载?
尝试访问它将失败
未能延迟初始化角色集合:
no.Dasken.momus.model.Person.roles,无法初始化代理-否
会议
或其他例外情况,取决于我的尝试
我的,万一需要的话
谢谢。您必须对惰性集合进行显式调用才能初始化它(通常的做法是为此调用
.size()
)。在Hibernate中有专门的方法(Hibernate.initialize()
),但JPA没有类似的方法。当然,您必须确保在会话仍然可用时完成调用,因此请使用@Transactional
注释控制器方法。另一种方法是在控制器和存储库之间创建一个中间服务层,该层可以公开初始化惰性集合的方法
更新:
请注意,上述解决方案很简单,但会导致对数据库的两个不同查询(一个用于用户,另一个用于其角色)。如果希望获得更好的性能,请将以下方法添加到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);
}
公共接口PersonRepository扩展了JpaRepository{
@查询(“从Person p JOIN FETCH p.roles中选择p,其中p.id=(:id)”)
公共人物findbyiandfetchrolesegerly(@Param(“id”)Long id);
}
此方法将使用JPQL的子句在数据库的一次往返中急切地加载角色关联,因此将减轻上述解决方案中两个不同查询引起的性能损失。它只能在事务中延迟加载。因此,您可以访问存储库中的集合,它有一个事务,或者什么,我认为您需要在视图渲染期间保持会话打开(但这不是很好的做法)。您有一些选择
- 在存储库上编写一个方法,按照R.J的建议返回一个初始化的实体李>
- 使用OpenEntityManagerInViewFilter为整个请求保持会话打开
- 需要时,使用帮助器类初始化实体
@Transactional
public class RepositoryHelper {
@PersistenceContext
private EntityManager em;
public void intialize(Object entity, int depth) {
JpaUtils.initialize(em, entity, depth);
}
}
这是一个旧的帖子,请考虑使用@ NealDeNeTyGrand(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);
}
@RepositoryRestResource(collectionResourceRel=“Employee”,path=“Employee”)
公共接口IEEmployeeRepository扩展了分页和排序存储库{
@EntityGraph(value=“EmployeeAuthories”,type=EntityGraphType.LOAD)
EmployeeEntity getByUsername(字符串用户名);
}
您也可以这样做:
@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;
}
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();
}
}
只需在控制器中使用faqQuestions.getFaqAnswers().size(),如果惰性地初始化列表,您将获得该列表的大小,而无需获取列表本身。Spring DataJpaRepository
Spring DataJpaRepository
定义了以下两种方法:
,它返回一个适合设置getOne
或@ManyToOne
父关联的@OneToOne
,它在运行从关联表加载实体的SELECT语句后返回实体POJOfindById
getOne
也没有调用findById
:
Person person = personRepository.findOne(1L);
因此,我假设findOne
方法是您在PersonRepository
中定义的方法。但是,findOne
方法在您的案例中不是很有用。由于您需要获取Person
以及isroles
集合,因此最好使用findOneWithRoles
方法
自定义Spring数据方法
您可以定义PersonRepositoryCustom
接口,如下所示:
public interface PersonRepository
extends JpaRepository<Person, Long>, PersonRepositoryCustom {
}
public interface PersonRepositoryCustom {
Person findOneWithRoles(Long id);
}
就这样 您是否可以编写一个方法,在给定某个参数的情况下创建一个查询来获取
Person
对象?在Query
中,包括fetch
子句,并为个人加载角色。由于我的所有请求都是简单的REST调用,没有呈现等,因此事务基本上是我的整个请求。谢谢你的意见。第一个怎么办?我知道如何编写查询,但不知道如何执行您所说的。你能举个例子吗?扎吉在他的回答中提供了一个例子,谢谢你给我指出了正确的方向。我不知道你的班级会怎么称呼!未完成的解决方案浪费其他人的时间使用OpenEntityManagerViewFilter为整个请求保持会话打开。-坏主意。我将另外请求获取我的实体的所有集合。请注意,这是一个简单的解决方案,但会导致对数据库的两个不同查询(一个用于用户,另一个用于其角色)。如果您想获得更好的性能,请尝试编写一个专用方法,使用JPQL或其他人建议的Criteria API,在一个步骤中急切地获取用户及其相关角色。我现在要求提供一个Jose回答的示例,我必须承认我不完全理解。请检查所需问题的可能解决方案
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();
}
}