匹配有许多孩子与Grails动态查找器

时间:2012-03-10 00:28:39

标签: hibernate grails groovy

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

class Author {
    String name
    List<Book> books

    static hasMany = [books:Book]
}

class Book {
    String title

    static belongsTo = Author
}

我的数据库然后显示为:

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
----------------------------------------

我希望能够在作者身上使用动态查找器。我正在搜索hasMany关系的完全匹配,以匹配此行为:

Author.findByBooks([1]) => null
Author.findByBooks([1, 2]) => author(id:1)
Author.findByBooks([1, 3]) => null
Author.findByBooks([3, 4]) => author(id:2)

尝试这会导致一个丑陋的Hibernate错误:

hibernate.util.JDBCExceptionReporter No value specified for parameter 1.

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

3 个答案:

答案 0 :(得分:2)

如果Book属于作者,则不清楚您的域模型。如果是这样,您应该将这一事实添加到您的域模型和查询,如Tom Metz所说。

让我做对了。您想要找到已经使用标题(或ID)写入书籍的作者,即“书籍1”和“书籍2”。 要进行检查工作,您必须两次加入Book表,以便将书籍标题从加入1与'Book 1'进行比较,将书籍标题从加入2与'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")
}

但事实证明,结果集是空的。 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=?)

ASFAIK使用grails标准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")
}

答案 1 :(得分:1)

我不是百分之百确定你真的可以让它工作(除非我在某处遗漏了一些文档)。但要获得所需,您需要使用标准:

class AuthorIntegrationTests {

  @Before
  void setUp() {
    def author = new Author(name: "Ted Jones").save(flush: true)
    def author2 = new Author(name:  "Beth Peters").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)
  }

  @After
  void tearDown() {
  }

  @Test
  void testAuthorCrit() {
    def result = Author.withCriteria(uniqueResult: true) {
      books {
        inList("id", [1.toLong(), 3.toLong()])
      }
    }
    assertTrue(result.name == "Ted Jones")
  }
}

答案 2 :(得分:0)

您需要为域对象添加双向一对多关系。进入你的书域添加:

static belongsTo = [ author:Author ]

然后您可以查询:

Author a = Book.author