我试图在实体User
上得到一个简单的DTO投影,它有一个订单列表。投影应该只包含用户firstName、lastName和链接表中order
的数量。
用户
类:
@Entity
@Table(name = "user")
public class User {
@Column(name = "firstName")
private String firstName;
@Column(name = "lastName")
private String lastName;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Order> orders;
// many other fields here
}
订单
类:
@Entity
@Table(name = "order")
public class Order {
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
// many other fields here
}
然后我有DTO对象:
public class UserDetailOrderCountDto {
private String firstName;
private String lastName;
private int orderCount;
public UserDetailOrderCountDto(String firstName, String lastName, int orderCount) {
this.firstName = firstName;
this.lastName = lastName;
this.orderCount = orderCount;
}
// getters, setters, ...
}
最后是带有查询的存储库:
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select new a.b.c.UserDetailOrderCountDto(u.firstName, u.lastName, size(u.orders)) from User u group by u.firstName, u.lastName")
List<UserDetailOrderCountDto> findUsersAndOrderCount();
}
DB包含2个用户的2个订单。有许多用户没有任何订单(我仍然希望收到orderCount为0的订单)。存储库中的查询为2个用户返回2个DTO,每个用户有1个订单(正确),但没有订单的用户会被跳过(因为它不是左连接的)。Hibernate生成的查询如下:
选择user0_. firstName作为col_0_0_,user0_.lastName作为col_1_0_,count(orders1_.user_id)作为用户user0_的col_2_0_,订单orders1_其中user0_.id=orders1_。user_id分组user0_.firstName,user0_.lastName
如何强制Hibernate给我所有用户(又名左连接,但如果可能的话没有本地查询)?或者任何其他方法来获得我想要的解决方案?任何帮助都很感激。谢谢你。
更新1:如果我尝试强制Hibernate使用FetchMode. JOIN
连接表,它仍然使用内连接。
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@Fetch(FetchMode.JOIN)
private Set<Order> orders;
然后查询如下所示:
选择user0_. firstName作为col_0_0_,user0_.lastName作为col_1_0_,count(orders1_.user_id)作为用户user0_交叉连接顺序的col_2_0_orders1_其中user0_.id=orders1_.user_id分组user0_.firstName,user0_.lastName
JPA查询
您可以在下一个方法后指示左连接:
@Query("SELECT new com.your.package.dto.UserDetailOrderCountDto(u.firstName, u.lastName, COUNT(o)) "
+ "FROM User u LEFT JOIN u.orders o group by u.firstName, u.lastName")
List<UserDetailOrderCountDto> findUsersAndOrderCount();
只需确保将类属性orderCount
更改为long:
public class UserDetailOrderCountDto {
private String firstName;
private String lastName;
private long orderCount;
public UserDetail() {
}
public UserDetail(String firstName, String lastName, long orderCount) {
this.firstName = firstName;
this.lastName = lastName;
this.orderCount = orderCount;
}
// Getters and setters
}
请注意,这适用于您的User
类的以下配置:
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Order> orders;
使用本机查询
您可以改用本机查询,因此您可以定义左连接:
@Query(value = "select u.first_name as firstName, u.last_name as lastName, count(o.id) as orderCount from user u left join orders o on u.id = o.user_id group by u.first_name, u.last_name;"
, nativeQuery = true)
List<UserDetailOrderCountDto> findUsersAndOrderCount();
您只需要确保生成的列名与bean的属性名匹配。
此外,在最新版本的Spring上,您不需要创建bean,您可以改为定义一个接口并Spring创建一个继承自接口的bean:
public interface UserDetailOrderCountDto {
public String getFirstName();
public String getLastName();
public int getOrderCount();
}