【问题标题】:How to intercept and modify JPA generated SQL statement?如何拦截和修改JPA生成的SQL语句?
【发布时间】:2021-03-16 07:14:37
【问题描述】:

我想知道是否有一种方法可以优化生成 JPA 查询 (JPQL) 的过程,而不是使用原生的。

为简单起见,我的模型如下所示:

@Entity
@Table(name = "b_books")
public class Book {
    
    @Id
    @Column(name = "book_id", updatable = false, nullable = false, unique = true)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="book_generator")   
    @SequenceGenerator(name="book_generator", sequenceName = "books_seq", allocationSize=1)
    private Integer id;
    
    @Column(name = "book_titl_tx")
    private String title;
    
    private int yearOfPublication;
    
    @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
    @JoinTable(name = "b_book_authors",
               joinColumns = @JoinColumn(name = "book_id"), 
               inverseJoinColumns = @JoinColumn(name = "auth_id"))
    private List<Author> authors = new ArrayList<Author>();

    //getter, setters, etc
}


@Entity
@Table(name = "b_authors")
public class Author {

    @Id
    @Column(name = "auth_id", nullable = false, unique = true)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="authors_generator")
    @SequenceGenerator(name="authors_generator", sequenceName = "authors_seq", allocationSize=1)
    private Integer id;

    @Column(name = "auth_last_name_tx")
    private String lastName;

    @ManyToMany(mappedBy = "authors")
    private List<Book> books = new ArrayList<Book>();

    //getters, setters, etc
}

在数据库方面,我有三个表:

  • B_BOOKS
  • B_AUTHORS
  • B_BOOK_AUTHORS 持有多对多关系

我想在数据库中查询每位作者的图书数量

public interface AuthorRepository extends JpaRepository<Author, Integer>{

@Query(value = "SELECT a.lastName AS lastName, " + 
               "       COUNT(a) AS booksCount" +
               "  FROM Author AS a " +
               "  LEFT JOIN a.books AS b" +
               " GROUP BY a.lastName")
List<IAuthorInfo> getAuthorInfo();
}

我正在使用基于界面的投影

public interface IAuthorInfo {

    String getLastName();
    Integer getBooksCount();
}

现在主要问题是:使用这种模型 JPA 生成的查询有一个冗余的 JOIN,如何摆脱它?

实际:

SELECT
    author0_.auth_last_name_tx   AS col_2_0_,
    COUNT(author0_.auth_id) AS col_3_0_
FROM
    admin.b_authors        author0_
    LEFT OUTER JOIN admin.b_book_authors   books1_ ON author0_.auth_id = books1_.auth_id
    LEFT OUTER JOIN admin.b_books          book2_ ON books1_.book_id = book2_.book_id
GROUP BY
    author0_.auth_last_name_tx

预期

SELECT
    author0_.auth_last_name_tx   AS col_2_0_,
    COUNT(author0_.auth_id) AS col_3_0_
FROM
    admin.b_authors        author0_
    LEFT OUTER JOIN admin.b_book_authors   books1_ ON author0_.auth_id = books1_.auth_id
GROUP BY
    author0_.auth_last_name_tx

【问题讨论】:

    标签: jpa spring-data-jpa jpql


    【解决方案1】:

    你可以使用size()函数:

    SELECT a.lastName AS lastName, size(a.books) AS booksCount FROM Author AS a GROUP BY a.lastName
    

    我没有测试过,但我想从您的查询中删除 LEFT JOIN a.books AS b 可以解决这个问题。

    【讨论】:

      【解决方案2】:

      谢谢,它确实删除了这个LEFT JOIN a.books AS b

      现在它会生成 SQL:

      SELECT author0_.auth_last_name_tx AS col_2_0_,
             (SELECT COUNT(books1_.auth_id)
                FROM b_book_authors books1_
               WHERE author0_.auth_id = books1_.auth_id) AS col_3_0_
        FROM b_authors author0_
       GROUP BY author0_.auth_last_name_tx;
      

      这令人惊讶地适用于 H2 (mem) 数据库以进行单元测试 但对于 Oracle 需要将 Author.ID 添加到 JPQL

      SELECT author0_.auth_id,
             author0_.auth_last_name_tx AS col_2_0_,
             (SELECT COUNT(books1_.auth_id)
                FROM b_book_authors books1_
               WHERE author0_.auth_id = books1_.auth_id) AS col_3_0_
        FROM b_authors author0_
       GROUP BY author0_.auth_id,
             author0_.auth_last_name_tx;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-12
        • 2018-06-07
        • 2018-10-10
        相关资源
        最近更新 更多