使用Spring Data repository时,仅需面对N+1查询问题即可。
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: select todo0_.id as col_0_0_ from todos todo0_ where todo0_.user_id=? ]
还有N个类似的查询
Hibernate: select todo0_.id as id1_0_0_, todo0_.description as descript2_0_0_, todo0_.target_date as target_d3_0_0_, todo0_.user_id as user_id4_0_0_, user1_.id as id1_1_1_, user1_.password as password2_1_1_, user1_.username as username3_1_1_ from todos todo0_ left outer join users user1_ on todo0_.user_id=user1_.id where todo0_.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.
}
更新:通过该查询可能解决问题,但为什么使用接受实体作为参数的构造函数时它不起作用呢?
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);
}
@OneToMany(mappedBy = "user" ...)
,这使得关系是双向的。你需要在User
类中映射这个关系吗?从User
中移除todos
可能会避免这样的问题。 - Karol Dowbecki