Grails-将关联的域对象分配给 List 与 ArrayList
Grails- Assigning associated domain objects into List vs 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
看看 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;
定义hasMany
属性时,格式为Map。您的伪代码不正确,使用 { } 而不是 [ ]。真正的代码可能看起来更像
static hasMany = [tags: Tag]
Tag
指定集合成员类型为Tag
域class,tags
可以是任何合法的变量名。通常它是 class 名称的小写和复数形式,但这只是一种约定。
AST 转换使用此信息编译成您的 class 一个名为 tags
的 Set
属性,本质上是这样的:
Set<Tag> tags
如果您使用了不同的名称,例如
static hasMany = [puppies: Tag]
那么你会
Set<Tag> puppies
Set
不是 List
,反之亦然 - 设置保证唯一性,并列出保证顺序。
您可以在 Groovy 中非常轻松地将一个转换为另一个,如果这就是您想要做的,那么另一个答案应该会有所帮助。但是考虑到原始类型是 Set
,将项目添加到 List
是没有意义的,因为顺序是随机的。
你可以将集合的类型声明为列表,Hibernate会增加一个额外的列来存储元素索引,以确保列表中的顺序在数据库中得以保留。为此,明确添加 List
声明
List tags
static hasMany = [tags: Tag]
还建议您查看 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]
}
在这种情况下,轮胎可以确保唯一性,因为它是一个集合,而座椅则不能,因为它默认是一个地图
希望这也有帮助
我有这样的关系:
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
看看 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;
定义hasMany
属性时,格式为Map。您的伪代码不正确,使用 { } 而不是 [ ]。真正的代码可能看起来更像
static hasMany = [tags: Tag]
Tag
指定集合成员类型为Tag
域class,tags
可以是任何合法的变量名。通常它是 class 名称的小写和复数形式,但这只是一种约定。
AST 转换使用此信息编译成您的 class 一个名为 tags
的 Set
属性,本质上是这样的:
Set<Tag> tags
如果您使用了不同的名称,例如
static hasMany = [puppies: Tag]
那么你会
Set<Tag> puppies
Set
不是 List
,反之亦然 - 设置保证唯一性,并列出保证顺序。
您可以在 Groovy 中非常轻松地将一个转换为另一个,如果这就是您想要做的,那么另一个答案应该会有所帮助。但是考虑到原始类型是 Set
,将项目添加到 List
是没有意义的,因为顺序是随机的。
你可以将集合的类型声明为列表,Hibernate会增加一个额外的列来存储元素索引,以确保列表中的顺序在数据库中得以保留。为此,明确添加 List
声明
List tags
static hasMany = [tags: Tag]
还建议您查看 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]
}
在这种情况下,轮胎可以确保唯一性,因为它是一个集合,而座椅则不能,因为它默认是一个地图
希望这也有帮助