用 Grails 动态查找器匹配 hasMany children

tim*_*cus 5 grails groovy hibernate

在 Grails 中,我试图找到一个域类的实例,该实例在一对多关系中具有精确的条目。考虑这个例子:

class Author {
    String name
    List<Book> books

    static hasMany = [books:Book]
}

class Book {
    String title

    static belongsTo = Author
}
Run Code Online (Sandbox Code Playgroud)

我的数据库然后显示如下:

author                     book
-------------------------    ------------------------
| id | name             |    | id | title           |
|----|------------------|    ------------------------
| 1  | John Steinbeck   |    | 1  | Grapes of Wrath |
| 2  | Michael Crichton |    | 2  | East of Eden    |
-------------------------    | 3  | Timeline        |
                             | 4  | Jurassic Park   |
                             ------------------------

author_book
----------------------------------------
| author_books_id | book_id | book_idx |
----------------------------------------
| 1               | 1       | 0        | // John Steinbeck - Grapes of Wrath
| 1               | 2       | 1        | // John Steinbeck - East of Eden
| 2               | 3       | 0        | // Michael Crichton - Timeline
| 2               | 4       | 1        | // Michael Crichton - Jurassic Park
----------------------------------------
Run Code Online (Sandbox Code Playgroud)

我希望能够做的是对作者使用动态查找器。我正在寻找 hasMany 关系的精确匹配,以匹配此行为:

Author.findByBooks([1]) => null
Author.findByBooks([1, 2]) => author(id:1)
Author.findByBooks([1, 3]) => null
Author.findByBooks([3, 4]) => author(id:2)
Run Code Online (Sandbox Code Playgroud)

尝试这样做会导致丑陋的 Hibernate 错误:

hibernate.util.JDBCExceptionReporter No value specified for parameter 1.
Run Code Online (Sandbox Code Playgroud)

有没有人让动态查找器处理域类的 hasMany 关系?获得所需行为的最“Grails-y”解决方案是什么?

Pet*_*ter 3

从您的域模型中不清楚是否Book属于作者。如果是这样,您应该将该事实添加到您的域模型中并像 Tom Metz 所说的那样进行查询。

让我把这件事搞清楚。您想要查找撰写标题(或 ID)为“Book 1”和“Book 2”的书籍的作者。要进行检查,您必须连接 Book 表两次,以便将连接一中的书名与“Book 1”进行比较,以及将连接二中的书名与“Book2”进行比较。

人们可以假设以下测试应该有效:

void setUp() {
    def author = new Author(name: "Ted Jones").save(flush: true)
    def author2 = new Author(name:  "Beth Peters").save(flush: true)
    def author3 = new Author(name:  "Foo Bar").save(flush: true)
    def book1 = new Book(title: 'Book 1').save(flush: true)
    def book2 = new Book(title: 'Book 2').save(flush: true)
    def book3 = new Book(title: 'Book 3').save(flush: true)
    def book4 = new Book(title: 'Book 4').save(flush: true)
    author.addToBooks(book1)
    author.addToBooks(book3)

    author2.addToBooks(book2)
    author2.addToBooks(book4)

    author3.addToBooks(book1)
    author3.addToBooks(book2)
}

void testAuthorCrit() {
    def result = Author.withCriteria() {
        books {
            eq("title", "Book 1")
        }
        books {
            eq("title", "Book 3")
        }
    }
    assert 1 == result.size()
    assertTrue(result.first().name == "Ted Jones")
}
Run Code Online (Sandbox Code Playgroud)

但事实证明,结果集是空的。Grails 将每一本书闭包中的语句合并到一个连接中。

这是查询结果:

 select this_.id as id1_1_, this_.version as version1_1_, this_.name as name1_1_, books3_.author_books_id as author1_1_, books_alia1_.id as book2_, books3_.books_idx as books3_, books_alia1_.id as id0_0_, books_alia1_.version as version0_0_, books_alia1_.title as title0_0_ from author this_ inner join author_book books3_ on this_.id=books3_.author_books_id inner join book books_alia1_ on books3_.book_id=books_alia1_.id where (books_alia1_.title=?) and (books_alia1_.title=?)
Run Code Online (Sandbox Code Playgroud)

ASFAIK 这不能使用 grails criteria api 来实现。但你可以使用 hql 代替。以下测试有效:

void testAuthorHql() {
    def result = Author.executeQuery("select a from Author a join a.books bookOne join a.books bookTwo where bookOne.title=? and bookTwo.title=?", ['Book 1', 'Book 3'])
    assert 1 == result.size()
    assertTrue(result.first().name == "Ted Jones")
}
Run Code Online (Sandbox Code Playgroud)