我有两张桌子- user和booking。每个用户可能有许多预订(一对多的关系)。
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。如果用户没有任何预订或预订不匹配的条件,我仍然想选择这个用户。
首先,让我们看看使用简单的查询会是什么样子:
SELECT u.*, b.* FROM user u LEFT JOIN booking b ON u.id = b.user_id AND b.price > 2000上述查询应提供以下结果:
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 |现在我想使用JPA和Query DSL来实现它。
JPA相关资料:
@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:
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;
}实际上,这段代码不起作用,我得到了以下例外:
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方法中指定,应该如下所示:
List<User> result = jpaQuery.from(user).leftJoin(user.bookings, booking).where(booking.price.gt(2000)).fetchJoin().fetch();这是可行的,但我不希望它工作,因为它不返回所有的用户,它只返回一个用户(Alice),它有一些预订,匹配条件子句。基本上,它只过滤合并的表(左联接操作后的结果表),这不是我要找的。
我想检索所有用户,如果没有任何特定用户的预订,那么只需要null,而不是这个用户的预订列表。
请帮帮忙,挣扎了几个小时都没有成功。
使用的版本:
发布于 2018-06-26 11:44:32
可以在where子句中使用isNull表达式来获取具有空值的行。
您的查询应该如下所示:
jpaQuery.from(user)
.leftJoin(user.bookings, booking)
.fetchJoin()
.where(booking.price.gt(2000).or(booking.id.isNull())).fetch();Hibernate生成的查询:
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>?发布于 2021-10-03 03:59:16
似乎没有JPA的方法来解决这个问题。但是我用Hibernate的方式修复了它,使用了过滤器org.hibernate.annotations.Filter。
@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;
}在查询数据库之前,必须启用此筛选器。
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列名完全相同,而不是作为模型属性名。
https://stackoverflow.com/questions/51031350
复制相似问题