Grails-将关联的域对象分配到List vs ArrayList

时间:2015-03-17 06:53:15

标签: hibernate grails gorm

我有这种关系:

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

按预期工作!虽然ArrayListList的子类。 任何解释?

Grails版本: 2.3.0

3 个答案:

答案 0 :(得分:3)

看一下Groovy的casting rules

当您尝试强制转换的值是集合时,将调用要强制转换为的类型的构造函数。因此,对于ArrayList,它隐式调用:

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

对于Listnew 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; 

答案 1 :(得分:2)

定义hasMany属性时,格式为Map。您的伪代码不正确,使用{}而不是[]。真正的代码看起来更像是

static hasMany = [tags: Tag]

Tag将集合成员类型指定为Tag域类,tags可以是任何合法变量名称。通常它是类名的小写和复数形式,但这仅仅是一种惯例。

AST转换使用此信息将名为Set的{​​{1}}属性编译到您的类中,基本上是这样的:

tags

如果您使用了其他名称,例如

Set<Tag> tags

然后你有

static hasMany = [puppies: Tag]

Set<Tag> puppies 不是Set,反之亦然 - 设置保证唯一性,并列出保证订单。

你可以在Groovy中轻松地将一个转换为另一个,如果那是你想要做的,那么另一个答案应该是有帮助的。但鉴于原始类型为List,将项目添加到Set没有任何意义,因为订单将是随机的。

您可以将集合的类型声明为列表,Hibernate将添加一个额外的列来存储元素索引,以确保列表中的顺序保留在数据库中。为此,请明确添加List声明

List

答案 2 :(得分:0)

建议你查看grails指南,在那里声明 hasMany 关系是默认情况下的Map 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]
 }

在这种情况下,轮胎的唯一性将得到保证,因为它是座椅而不是座椅,因为它默认为地图

希望这也有帮助