Соответствие имеет много детей с динамическими искателями Грааля - PullRequest
3 голосов
/ 10 марта 2012

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

2 голосов
/ 14 марта 2012

Это не ясно из вашей модели домена, если Book принадлежит автору. Если это так, вам следует добавить этот факт в модель вашего домена и выполнить запрос, как сказал Том Метц.

Позволь мне понять это правильно. Вы хотите найти авторов, которые написали Книги с названием (или идентификатором), то есть «Книга 1» и «Книга 2». Чтобы проверка работала, вам нужно присоединиться к таблице Book два раза, чтобы сравнить заголовок книги из join one с «Book 1» и заголовок книги из join two с «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 это не может быть заархивировано с помощью API Grails. Но вы можете использовать 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 голос
/ 11 марта 2012

Я не уверен на 100%, что вы действительно можете заставить это работать (если я не пропускаю некоторые документы).Но чтобы получить то, что вам нужно, вы должны использовать критерии:

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")
  }
}
0 голосов
/ 12 марта 2012

Вам необходимо добавить двунаправленное отношение «один ко многим» к объектам вашего домена.В вашу книгу добавить домен:

static belongsTo = [ author:Author ]

Затем вы можете запросить:

Author a = Book.author
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...