我正在从另一个开发堆栈切换到Java/Spring,我对Spring中的一些事情是如何完成的感到非常困惑。我很惊讶我找不到解决我的特定问题的方法。
考虑以下代码结构:
@Entity
class Parent {
}
@Entity
class Child {
private String name;
@ManyToOne
private Parent parent;
}
在我的 API 控制器中,我想用
Child
列表进行响应。每个 Child
项目应包含一些 Child
字段(例如 name
)和 Parent
ID(并且没有其他 Parent
数据):
// JSON Response example
{
"children": [
{
"name": "Child 1",
"parent_id": 1
},
{
"name": "Child 2",
"parent_id": 2
}
]
}
为了提供
parent_id
值 Spring+Hibernate+JPA 让我以这种方式访问 Parent
ID:
child.getParent().getId()
这不仅会导致不必要地从数据库中获取整个
Parent
记录及其所有字段,而且如果不使用任何 JOIN FETCH 或 EAGER LOAD 方法,在我的情况下还会导致 N + 1 问题。
但是
parent_id
是一个简单的Child
的专栏。
即使未加载关联,它也应该免费提供!
有什么最佳方法可以实现我的目标吗?
这很有趣,但是当我使用
@EntityGraph
注解标记查询方法而不指定要获取哪些属性(关联)时,它不会执行额外的 SQL 查询来获取关联的 Parent
并且不会修改原始 SQL查询仅获取 Child
的数据。但同时,它能够计算 Parent
的 ID
值。
参见示例:
public interface ChildRepository extends JpaRepository<Child, Integer> {
@Override
@EntityGraph // Adding this annotation does the trick.
List<Child> findAll();
}
现在当我以这种方式访问
Parent
ID时
List<Child> children = repository.findAll();
children.stream().map((child) -> child.getParent().getId());
它不会进行数据库查询来为每个
parent
获取 child
,并且不会急切加载(联接、获取)任何 parent
数据。同时它会工作并返回 parents
的 ID。
看起来使用
@EntityGraph
使得关联被急切加载(获取),因为 FETCH
是默认的 @EntityGraph
模式。但由于我没有指定要获取哪些关联,所以它没有获取任何内容。而且它没有延迟加载任何东西(没有进行额外的 SQL 查询),因为模式是 FETCH
。但是,它能够计算 child.getParent().getId()
,这是 目标 - 访问 parent
ID,而不获取 parent
记录。
此外,正如预期的那样,使用此设置无法计算除
id
之外的任何其他字段值。例如,执行 child.getParent().getName()
失败并出现 LazyInitializationException
异常。
但是,我认为这不是正确的解决方案。看来应该有另一种简单而明确的方法来解决这个问题。