2015-03-17 3 views
0

У меня есть эти отношения:Grails- Назначение связанный домен объектов в список против ArrayList

User{ 
... 
hasMany = [tags: Tag] 

} 

Tag{ 
... 
} 

Некоторые, где в моей службы у меня есть этот код:

List<Tag> tags = user.tags 

Но это не работает, я получаю эту ошибку:


org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[com.app.ext.Tag : 1]' with class 
'org.hibernate.collection.PersistentSet' to class 'java.util.List' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: java.util.List(com.app.ext.Tag) 
    at ConsoleScript15.run(ConsoleScript15:6) 

Если я изменю свой код на:

ArrayList<Tag> tags = user.tags 

Работает так, как ожидалось! хотя ArrayList является подклассом List. Любые объяснения? версия

Grails: 2.3.0

+0

'org.hibernate.collection.PersistentSet' - это' Set', а не '' List'. Поэтому понятно, почему он не может бросить кулак, но не имеет объяснения, почему его можно использовать для 'ArrayList'. Может быть, интерфейс против разницы в классе? –

ответ

2

Взгляните на Groovy, casting rules.

Когда значение, которое вы пытаетесь сделать, - это коллекция, вызывается конструктор типа, который вы хотите использовать. Так, ArrayList неявно вызывает:

ArrayList<Tag> tags = new ArrayList(users.tags) 

Для List это было бы что-то вроде new List(users.tags) что неверно, так как List является интерфейс.

Фрагмент ниже показывает ту же проблему:

Set<String> mySet = new HashSet<>(['A', 'B']) 
ArrayList<String> myList = mySet; // works okay 
List<String> myList = mySet; // fails with GroovyCastException 

Но вы можете явно привести к ArrayList:

List<String> myList = mySet as ArrayList; 
+0

заранее. – dsharew

+0

Нечетный ответ - вы прочитали сообщение об ошибке? –

+0

@BurtBeckwith Мой ответ - это объяснение того, почему неявное преобразование из 'Set' (' PersistentSet' в этом случае) в 'List' завершается с ошибкой. Оба ответа (ваш и мой) полезны imho, я объясняю основной источник проблемы (исключение, я прочитал), и ваш ответ более «широк». Я не уверен, что это хорошая причина для downvoting ... –

2

При определении hasMany свойства, формат карты. Ваш псевдокод неверен, используя {} вместо []. Реальный код, вероятно, больше похож

static hasMany = [tags: Tag] 

Tag определяет тип элемента коллекции как класс домена Tag и tags может быть любое юридическое имя переменной. Обычно это строчная и множественная форма имени класса, но это просто соглашение.

Устройство AST преобразования использует эту информацию для компиляции в класс Set свойства с именем tags, по существу, это:

Set<Tag> tags 

и если вы использовали другое имя, например

static hasMany = [puppies: Tag] 

, то вы бы

Set<Tag> puppies 

Set не List и наоборот - наборы гарантируют уникальность и списки гарантируют порядок.

Вы можете конвертировать один в другой очень легко в Groovy, и если это то, что вы пытались сделать, тогда другой ответ должен быть полезен. Но учитывая, что исходный тип - Set, добавление предметов в List бессмысленно, потому что заказ будет случайным.

Вы можете объявить тип коллекции как список, а Hibernate добавит дополнительный столбец для хранения индексов элементов, чтобы гарантировать сохранение порядка в списке в базе данных. Чтобы сделать это, явно добавить в List декларации

List tags 
static hasMany = [tags: Tag] 
+0

Спасибо, я обновил свой вопрос до '[tags: Tag]', также это действительно полезно. – dsharew

+0

Было бы также желательно проверить руководство по гравиям по адресу http://grails.github.io/grails-doc/3.0.x/guide/GORM.html#domainClasses – JohnTheBeloved

0

Было бы также целесообразно для вас, чтобы проверить руководство Grails, это быть указано там, что hasMany отношения является карта по умолчанию http://grails.github.io/grails-doc/3.0.x/guide/GORM.html#domainClasses, если вы хотите в противном случае, вы явно должны объявить переменную как Set

public class Car{ 
     Set<Tyre> tyres 

     static has many = [tyres:Tyre, seats:Seat] 
} 

в этом случае уникальность будет обеспечена для шин, потому что это набор в то время как это было бы не на места, потому что это карта по умолчанию

Надеюсь, что это тоже помогает

+0

Только декларация 'hasMany' представляет собой карту, но это _completely_ не имеет отношения к Hibernate и GORM. Это именно то, как вы заявляете конфигурационную информацию. Это может быть аннотация или внешняя конфигурация в XML, но в Grails это статическое свойство hasMany. Но результирующая коллекция является «Set» по умолчанию (ваш 'Set tyres' избыточен, поскольку AST добавляет это для вас, что вы можете видеть при декомпиляции) и может быть« List », если вы добавите« List tyres' , –

Смежные вопросы