我有这种关系:
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 :(得分:3)
看一下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;
答案 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]
}
在这种情况下,轮胎的唯一性将得到保证,因为它是座椅而不是座椅,因为它默认为地图
希望这也有帮助