当前位置: 首页 > 知识库问答 >
问题:

不应使用Hibernate投影进行N 1查询

鞠嘉誉
2023-03-14

只需面对N 1查询问题与这样的Spring数据存储库

public interface ToDoRepository extends CrudRepository<ToDo, Long> {

    @Query("select new com.package.repr.ToDoRepr(t) from ToDo t " +
            "where t.user.id = :userId")
    List<ToDoRepr> findToDosByUserId(@Param("userId") Long userId);
}

我在日志中看到这样一个查询

Hibernate:选择todo0\u0。id为col_0_0_从todos todo0_其中todo0_。用户标识=?]

和N个这样的查询

Hibernate:选择todo0\u0。id为id1\u 0\u 0\u,todo0\u。描述为描述2\u 0\u 0\u,todo0\u。目标日期为目标日期第3天、第0天、第0天。用户标识为用户标识4、用户标识0、用户标识1。id为id1_1_1_,user1_。密码为password2\u 1\u 1\u,user1\u。用户名为username3\u 1\u 1\u来自todos todo0\u左外部加入用户todo0\u上的user1。用户id=用户1。id todo0\u0的位置。id=?

ToDoRepr是一个简单的POJO。使用接受ToDo实体作为参数的构造函数。

下面是我在这个查询中使用的两个JPA实体

@Entity
@Table(name = "todos")
public class ToDo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String description;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @Column
    private LocalDate targetDate;

    // geters, setters, etc.
}
@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @OneToMany(
            mappedBy = "user",
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private List<ToDo> todos;

    // geters, setters, etc.
}

UPD。通过该查询可以解决问题,但为什么它不使用接受实体作为参数的构造函数呢?

public interface ToDoRepository extends CrudRepository<ToDo, Long> {

    @Query("select new com.package.repr.ToDoRepr(t.id, t.description, t.user.username, t.targetDate) " +
            "from ToDo t " +
            "where t.user.id = :userId")
    List<ToDoRepr> findToDosByUserId(@Param("userId") Long userId);
}

共有2个答案

常献
2023-03-14

我想在这里收集一些关于这个我自己的问题的解决方法。有一个没有显式JPQL查询的简单解决方案。SpringDataJPA可以将任何具有适当getter和setter的POJO视为投影。

这对我来说很合适

public interface ToDoRepository extends CrudRepository<ToDo, Long> {

    List<ToDoRepr> findToDosByUser_Id(Long userId);
}
谯和煦
2023-03-14

这是一个非常常见的问题,因此我创建了一篇文章,详细介绍了解决方案

Hibernate的最佳实践是将所有关联定义为惰性,以避免在不需要时获取它。更多原因,请查看Vlad Mihalcea的文章https://vladmihalcea.com/eager-fetching-is-a-code-smell/

为了解决您的问题,在类ToDo中,您应该将manyTone定义为Lazy:

@Entity
@Table(name = "todos")
public class ToDo {

    ...

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    ...

    // geters, setters, etc.
}

如果您需要访问ToDoRepr中的用户,默认情况下不会加载该用户,因此您需要将其添加到查询中:

  • JPQL,使用JOIN FETCH:
public interface ToDoRepository extends CrudRepository<ToDo, Long> {

    @Query("select new com.package.repr.ToDoRepr(t) " +
            "from ToDo t " +
            "inner join fetch t.user " +
            "where t.user.id = :userId")
    List<ToDoRepr> findToDosByUserId(@Param("userId") Long userId);
}
  • JPA,使用EntityGraph:
public interface ToDoRepository extends CrudRepository<ToDo, Long> {

    @EntityGraph(attributePaths = {"user"})
    List<ToDoRepr> findToDosByUser_Id(Long userId);
}
 类似资料:
  • 问题内容: 我对Hibernate的 预测 和 标准 感到困惑。何时使用投影以及何时使用标准? 问题答案: 它们不是互斥的,您可以同时使用两者。投影通常在某些条件下使用。 简而言之,使用Hibernate Projections来仅查询要使用Criteria查询的一个实体或一组实体的属性的子集。您还可以使用预测指定条款和聚合函数一样,等等。这就像指 其 数据你抓取。就像修改SQL查询中的子句一样。

  • 本文向大家介绍使用HTML5画布进行投影,包括了使用HTML5画布进行投影的使用技巧和注意事项,需要的朋友参考一下 HTML5 canvas提供了在图纸周围创建漂亮阴影的功能。所有绘图操作均受四个全局阴影属性影响。 序号 属性和说明 1 shadowColor [=值] 此属性返回当前阴影颜色,可以对其进行设置以更改阴影颜色。 2 shadowOffsetX [=值] 此属性返回当前阴影偏移量X,

  • 嗨,我想写一个查询使用条件:以下查询必须使用条件创建: “从S2中选择不同的(s2Taxper),其中s2Tc='601'和s2Txcd!=”” 提前谢谢

  • 问题内容: 您好,我想按查询排除某些字段。我正在使用nodejs 但在结果集中,我一直在获取密码字段。 问题答案: 投影不适用于新的nodejs mongodb驱动程序…相反,您将不得不在 此处使用游标方法

  • 问题内容: 简而言之:hibernate状态不支持投影和示例查询吗?我发现了这篇文章: 代码是这样的: 就像其他张贴者所说的那样,生成的sql始终具有一个where类,仅 引用y0_ =? 而不是this_.city。 我已经尝试了几种方法,并搜索了问题跟踪器,但对此一无所获。 我什至尝试使用Projection别名和Transformers,但是它不起作用: 有没有人通过示例使用投影和查询? 问

  • 首先,我将列出我在查询中使用的三个模型 产品实体: 合作伙伴实体: 和StockProductInfoEntity: 我想从数据库产品中获取所有股票的合作伙伴计算计数。为了方便起见,我创建了一个简单的DTO: 并在JPA存储库中编写JPQL查询: 但我的应用程序甚至没有启动,因为查询验证有问题。我收到以下消息: 原因:组织。冬眠QueryException:查询指定了联接提取,但提取的关联的所有者