因为我迁移到Hibernate 6(5.4.30分,最后到6分。2.0.Final),我的一些查询翻译不正确。
以下是基于this tutorial的最小复制项目:
实体(省略getter/setter):
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
}
@Entity
public class PublishingHouse {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
}
一本书有一个指向作者和出版社的外键:
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
@ManyToOne
private Author author;
@ManyToOne
private PublishingHouse publishingHouse;
}
我的主要方法是添加实体,然后尝试查找所有以“Stephen King”为作者或以“Simon & Schuster”为出版社的书籍:
public static void main(String[] args) {
Transaction transaction = null;
PublishingHouse phSimonSchuster = null;
Author stephenKing = null;
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
transaction = session.beginTransaction();
/**
* Add Publishing houses
*/
phSimonSchuster = new PublishingHouse();
phSimonSchuster.setName("Simon & Schuster");
session.persist(phSimonSchuster);
var phHarperCollins = new PublishingHouse();
phHarperCollins.setName("HarperCollins");
session.persist(phHarperCollins);
/**
* Add Authors
*/
stephenKing = new Author();
stephenKing.setName("Stephen King");
session.persist(stephenKing);
var hpLovecraft = new Author();
hpLovecraft.setName("Howard Phillips Lovecraft");
session.persist(hpLovecraft);
/**
* Add Books
*/
var bShining = new Book();
bShining.setTitle("Shining");
bShining.setAuthor(stephenKing);
bShining.setPublishingHouse(phSimonSchuster);
session.persist(bShining);
var bColorOutOfSpace = new Book();
bColorOutOfSpace.setTitle("The Colour Out of Space");
bColorOutOfSpace.setAuthor(hpLovecraft);
bColorOutOfSpace.setPublishingHouse(phHarperCollins);
session.persist(bColorOutOfSpace);
transaction.commit();
}
try (Session session = HibernateUtil.getSessionFactory().openSession()) {
var hql = "SELECT book "
+ "FROM Book book "
+ " LEFT JOIN book.author author "
+ " WITH author.id = :stephenKingId "
+ " LEFT JOIN book.publishingHouse publishingHouse "
+ " WITH publishingHouse.id = :simonSchusterId "
+ "WHERE author IS NOT NULL OR publishingHouse IS NOT NULL ";
List<Book> books = session.createQuery(hql, Book.class)
.setParameter("stephenKingId", stephenKing.getId())
.setParameter("simonSchusterId", phSimonSchuster.getId())
.list();
books.forEach(b -> {
System.out.println("Book found: " + b.getTitle());
});
}
}
我的main方法的输出是:
专辑中文名:Shining
首页〉外文书〉文学〉西洋文学〉The Colour Out of Space
但是,《太空中的颜色》不是斯蒂芬·金写的,也不是由“西蒙与舒斯特”出版的,正如你在我插入数据时看到的,所以这本书不应该通过这个查询得到。
问题是这个hql:
SELECT book
FROM Book book
LEFT JOIN book.author author
WITH author.id = :stephenKingId
LEFT JOIN book.publishingHouse publishingHouse
WITH publishingHouse.id = :simonSchusterId
WHERE author IS NOT NULL OR publishingHouse IS NOT NULL
翻译错误:
SELECT b1_0.id,
b1_0.author_id,
b1_0.publishinghouse_id,
b1_0.title
FROM book b1_0
LEFT JOIN author a1_0
ON a1_0.id = b1_0.author_id
AND b1_0.author_id = ?
LEFT JOIN publishinghouse p1_0
ON p1_0.id = b1_0.publishinghouse_id
AND b1_0.publishinghouse_id = ?
WHERE b1_0.author_id IS NOT NULL
OR b1_0.publishinghouse_id IS NOT NULL
为什么是错的?因为WHERE条件正在测试author_id
和publishinghouse_id
是否不为null,不是在LEFT JOIN上,而是在Book
实体上。当然,每本书都有一个author_id
和publishinghouse_id
,所以每本书都可以在查询中检索。但是在HQL查询中,测试是在LEFT JOIN上。还有一个问题:在HQL中,WITH子句正在测试LEFT JOIN别名的ID,因此:
FROM book book
LEFT JOIN book.author author
WITH author.id = :stephenKingId
应为:
FROM book b1_0
LEFT JOIN author a1_0
ON a1_0.id = b1_0.author_id
AND a1_0.id = ?
而不是:
FROM book b1_0
LEFT JOIN author a1_0
ON a1_0.id = b1_0.author_id
AND b1_0.author_id = ?
最后,正确的查询应该是:
SELECT b1_0.id,
b1_0.author_id,
b1_0.publishinghouse_id,
b1_0.title
FROM book b1_0
LEFT JOIN author a1_0
ON a1_0.id = b1_0.author_id
AND a1_0.id = ?
LEFT JOIN publishinghouse p1_0
ON p1_0.id = b1_0.publishinghouse_id
AND p1_0.id = ?
WHERE a1_0.id IS NOT NULL OR p1_0.id IS NOT NULL
此查询只查找以“斯蒂芬·金”为作者或以“西蒙与舒斯特”为出版社的书籍
我在Hibernate版本〈6时没有这个问题。0
2条答案
按热度按时间tf7tbtn21#
我建议一个更简单的查询:
您可以使用WITH:
或者,显式指定连接条件:
46qrfjad2#
我的意思是,如果我理解正确的话,您正在尝试使用
is not null
条件来模拟两个常规内部连接的效果。为什么不首先使用内部联接来编写查询呢?
或者:
甚至只是:
我的意思是,我只是不确定为什么会在这里使用外连接。
更新
我错过了限制中的
or
。在这种情况下,类似于:或者类似的东西
更新2
实际上,我已经完全迟钝了!此查询完全避免了连接:
(我 * 觉得 * 原始查询有什么东西很臭,但一开始我没能把手指放在上面。)