不正确的 SQL where 子句从 HQL 生成,因为 hibernate 6 使用 test on left join on where 子句

问题描述 投票:0回答:2

自从我迁移到 Hibernate 6(5.4.30.Final 到 6.2.0.Final)后,我的一些查询没有正确翻译。

这是基于本教程的最小复制项目:

实体(省略getters/setters):

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

我的主要方法的输出是:

找到的书:闪亮
找到的书:空间之外的颜色

但是《The Color Out of Space》不是斯蒂芬·金写的,也不是“Simon & Schuster”出版的,我插入数据的时候可以看到,所以这本书不应该通过这个查询得到。

问题是这个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
是否不为空,所以不是在 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 

此查询仅查找作者为“Stephen king”或出版社为“Simon & Schuster”的书籍

我的休眠版本没有这个问题< 6.0

sql hibernate hql jpql
2个回答
0
投票

我的意思是,如果我理解正确的话,您正在尝试使用

is not null
条件来模拟两个常规内部联接的效果。

为什么不首先使用内连接编写查询?

任一:

SELECT book
FROM Book book
   JOIN book.author author
       WITH author.id = :stephenKingId
   JOIN book.publishingHouse publishingHouse
       WITH publishingHouse.id = :simonSchusterId

甚至只是:

SELECT book
FROM Book book
   JOIN book.author author
   JOIN book.publishingHouse publishingHouse
WHERE author.id = :stephenKingId 
  AND publishingHouse.id = :simonSchusterId

我的意思是,我只是不确定为什么会在这里使用外部连接。


0
投票

我建议一个更简单的查询:

SELECT b.id, b.author.id, b.publishingHouse.id, b.title 
FROM Book b 
LEFT JOIN b.author a 
LEFT JOIN b.publishingHouse p 
WHERE a.id = :authorId OR p.id = :publishingHouseId

你可以使用 WITH:

SELECT b.id, b.author.id, b.publishingHouse.id, b.title
FROM Book b
LEFT JOIN b.author a WITH a.id = :authorId
LEFT JOIN b.publishingHouse p WITH p.id = :publishingHouseId
WHERE a.id IS NOT NULL OR p.id IS NOT NULL

或者,明确指定连接条件:

SELECT b.id, b.author.id, b.publishingHouse.id, b.title 
FROM Book b 
LEFT JOIN b.author a ON a.id = :authorId 
LEFT JOIN b.publishingHouse p ON p.id = :publishingHouseId 
WHERE a.id IS NOT NULL OR p.id IS NOT NULL
© www.soinside.com 2019 - 2024. All rights reserved.