首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JPA (Hibernate) / QueryDSL左连接条件不起作用

JPA (Hibernate) / QueryDSL左连接条件不起作用
EN

Stack Overflow用户
提问于 2018-06-25 20:27:03
回答 2查看 3.8K关注 0票数 3

我有两张桌子- userbooking。每个用户可能有许多预订(一对多的关系)。

代码语言:javascript
复制
user:                          booking:

    id | name    |               id | country    | user_id | price   |
    -------------|               ------------------------------------|
     1 | Alice   |                1 | Italy      | 1       | 2000    |
     2 | Bob     |                2 | France     | 1       | 2500    |
                                  3 | Spain      | 1       | 3000    |

我想选择所有的用户和所有预订的价格大于2000使用Query DSL。如果用户没有任何预订或预订不匹配的条件,我仍然想选择这个用户。

首先,让我们看看使用简单的查询会是什么样子:

代码语言:javascript
复制
SELECT u.*, b.* FROM user u LEFT JOIN booking b ON u.id = b.user_id AND b.price > 2000

上述查询应提供以下结果:

代码语言:javascript
复制
    id | name    | id    | country    | user_id | price   |
    -------------|----------------------------------------|
     1 | Alice   | 2     | France     | 1       | 2500    |
     1 | Alice   | 3     | Spain      | 1       | 3000    |
     2 | Bob     | null  | null       | null    | null    |

现在我想使用JPAQuery DSL来实现它。

JPA相关资料:

代码语言:javascript
复制
@Entity
public class User {

    @Id
    private Long id;
    private String name;

    @OneToMany(cascade = ALL, fetch = EAGER, orphanRemoval = true, mappedBy = "user")
    private List<Booking> bookings;

    // getters and setters
}

@Entity
public class Booking {

    @Id
    private Long id;
    private String name;
    private Integer price;

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

    // getters and setters
}

查询DSL:

代码语言:javascript
复制
public List<User> getUsersAndBookings() {
    QUser user = QUser.user;
    QBooking booking = QBooking.booking;

    JPAQuery<User> jpaQuery = new JPAQuery(entityManager);

    List<User> result = jpaQuery.from(user).leftJoin(user.bookings, booking).on(booking.price.gt(2000)).fetchJoin().fetch();

    return result;
}

实际上,这段代码不起作用,我得到了以下例外:

代码语言:javascript
复制
org.hibernate.hql.internal.ast.QuerySyntaxException: with-clause not allowed on fetched associations; use filters [select user from com.example.demo.entity.User user left join fetch user.bookings as booking with booking.price > ?1]

问题是条件子句是在on方法- on(booking.price.gt(2000))中指定的。

经过一些研究后,我发现这个条件应该在where方法中指定,应该如下所示:

代码语言:javascript
复制
List<User> result = jpaQuery.from(user).leftJoin(user.bookings, booking).where(booking.price.gt(2000)).fetchJoin().fetch();

这是可行的,但我不希望它工作,因为它不返回所有的用户,它只返回一个用户(Alice),它有一些预订,匹配条件子句。基本上,它只过滤合并的表(左联接操作后的结果表),这不是我要找的。

我想检索所有用户,如果没有任何特定用户的预订,那么只需要null,而不是这个用户的预订列表。

请帮帮忙,挣扎了几个小时都没有成功。

使用的版本:

  • 弹簧启动2.0.2
  • 春季数据JPA 2.0.7
  • 冬眠5.2.16.结束
  • QueryDSL 4.1.4
EN

回答 2

Stack Overflow用户

发布于 2018-06-26 11:44:32

可以在where子句中使用isNull表达式来获取具有空值的行。

您的查询应该如下所示:

代码语言:javascript
复制
jpaQuery.from(user)
    .leftJoin(user.bookings, booking)
    .fetchJoin()
    .where(booking.price.gt(2000).or(booking.id.isNull())).fetch();

Hibernate生成的查询:

代码语言:javascript
复制
select
user0_.id as id1_1_0_,
bookings1_.id as id1_0_1_,
user0_.name as name2_1_0_,
bookings1_.country as country2_0_1_,
bookings1_.price as price3_0_1_,
bookings1_.user_id as user_id4_0_1_,
bookings1_.user_id as user_id4_0_0__,
bookings1_.id as id1_0_0__
from
user user0_
left outer join
booking bookings1_
on user0_.id=bookings1_.user_id
where
bookings1_.id is null
or bookings1_.price>?
票数 0
EN

Stack Overflow用户

发布于 2021-10-03 03:59:16

似乎没有JPA的方法来解决这个问题。但是我用Hibernate的方式修复了它,使用了过滤器org.hibernate.annotations.Filter

代码语言:javascript
复制
@Entity
@FilterDef(name = "anyName", parameters = {
    @ParamDef(name = "price", type = "integer")
})
public class User {    
    @Id
    private Long id;
    private String name;

    @OneToMany(cascade = ALL, fetch = EAGER, orphanRemoval = true, mappedBy = "user")
    @Filter(name = "anyName", condition = "price > :inputPrice")
    private List<Booking> bookings;
}

在查询数据库之前,必须启用此筛选器。

代码语言:javascript
复制
Session session = enityManager.unwrap(Session.class);
session.enableFilter("anyName").setParameter("inputPrice", 2000);

// fetch using hql or criteria; but don't use booking.price.gt(2000) or similar condition there

session.disableFilter("anyName");

现在的结果将是一个User,即使他的所有预订价格低于2000,bookings列表将如预期的那样为空。

注意到condition中的单词price应该与db列名完全相同,而不是作为模型属性名。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51031350

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档