2012-03-10 3 views
3

В Grails я пытаюсь найти экземпляр класса домена, который имеет точные записи в отношениях «один ко многим». Рассмотрим следующий пример:Соответствие hasMany детей с динамическими искателями 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» для получения желаемого поведения?

ответ

2

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

Позвольте мне получить это право. Вы хотите найти автора, который написал книги с заголовком (или id), то есть «Книга 1» и «Книга 2». Чтобы выполнить контрольную работу, вы должны дважды присоединиться к таблице книг, чтобы сравнить название книги, чтобы присоединиться к ней с «Книгой 1» и названием книги, чтобы присоединиться к книге «Книга 2».

Можно было бы предположить, что следующий тест должен работать:

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 это не может быть archieved с использованием критериев 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") 
} 
+0

Это похоже на решение, которое я придумал, хотя я использовал raw SQL, а не HQL. Поддержка любого количества книг делает формирование запроса несколько более сложным, и дополнительная проверка на Author.books.size() может использоваться для обеспечения точного соответствия коллекции коллекций. Я был разочарован тем, что первый подход с двумя закрытиями книг не работает. Спасибо за разумный ответ, подтверждающий, что классы домена Grails и запросы критериев не соответствуют задаче. – timbonicus

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") 
    } 
} 
+0

Это не совсем сработало, так как созданный запрос пытается присоединиться к books.id = 1 AND books.id = 2, что не дает результата. Мне нравится подход, я пытаюсь поддразнить строитель критериев для создания нескольких объединений. Я не думаю, что это обеспечит точное задание даже в тот момент, но это может привести меня к тому, что маленький код может сделать все остальное. – timbonicus

+0

Я отредактировал запрос так, чтобы он использовал OR вместо AND. – Gregg

+0

Проблема с тем, что он подтянет любого автора, соответствующего любой книге, а не только авторов, имеющих две книги. – timbonicus

0

Вам нужно добавить двунаправленную один-ко-многим отношение к вашим объектам домена. В вас книги домен добавить:

static belongsTo = [ author:Author ] 

Вы можете запрос:

Author a = Book.author 
Смежные вопросы