由于内部联接而不是左联接,Spring DTO Projection查询不会返回所有结果

时间:2019-03-09 18:47:50

标签: java spring hibernate jpa spring-data-jpa

我正在尝试对具有订单列表的实体User进行简单的DTO投影。投影应仅包含用户firstName,lastName和链接表中Order的数量。

User类:

@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

}

Order类:

@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();

}

数据库包含2个用户的2个订单。有许多用户没有任何订单(我仍然希望orderOrder为0的订单)。存储库中的查询为2个用户返回2个DTO,每个用户有1个订单(正确),但是跳过了没有订单的用户(因为它不是左联接的)。 Hibernate生成的查询如下:

select user0_.firstName as col_0_0_, user0_.lastName as col_1_0_, count(orders1_.user_id) as col_2_0_ from user user0_, orders orders1_ where user0_.id=orders1_.user_id group by user0_.firstName , user0_.lastName

如何强制Hibernate给我所有用户(又名左联接,但如果可能的话不进行本机查询)?或任何其他方法来获得我想要的解决方案?任何帮助表示赞赏。谢谢。

更新1: 如果我尝试强制Hibernate使用FetchMode.JOIN联接表,它仍然使用内部联接。

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@Fetch(FetchMode.JOIN)
private Set<Order> orders;

查询如下:

select user0_.firstName as col_0_0_, user0_.lastName as col_1_0_, count(orders1_.user_id) as col_2_0_ from user user0_ cross join orders orders1_ where user0_.id=orders1_.user_id group by user0_.firstName , user0_.lastName

1 个答案:

答案 0 :(得分:1)

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();
}