我有一个使用grails 2.2.4和hibernate 3的项目。我正在将它迁移到grails 3.2.4,它使用hibernate 5.我开始从头开始创建一个grails 3.2.4项目,现在我是慢慢地从旧项目中复制一小部分代码并使其正常工作。起初,我只复制了我的User,UserRole和Role类,并使我的整个Spring安全登录流程正常工作。然后,我复制了剩余的域对象,并尝试启动我的项目。我的域对象使用GORM映射,而不是JPA注释。该项目现在甚至不会启动并在启动时获得以下异常(我链接到公共要点,因为内容对于堆栈溢出允许的内容太长)。
https://gist.github.com/schmickie/10522d3a2b8a66b6fb79f76e2af0fd72
我在网上搜索了一些错误,我发现的一切都说明它与设置复合主键的问题有关。但是,我没有使用任何复合主键。任何人都有任何想法出了什么问题?错误,Api和Method中引用的域对象如下所示。
class Api implements Serializable {
String name
boolean enabled
String apiVersion
String swaggerVersion
String resourcePath
String jsonData
boolean https = true
String regionSource
String contractName
String contractBasePath
Date dateCreated
Date lastUpdated
Date deprecationDate
static hasMany = [methods: Method, models: Model, apiServers: ApiServer]
static mapping = {
cache true
}
static constraints = {
name(nullable: false, blank: false)
enabled()
apiVersion(nullable: true, blank: true)
resourcePath(nullable: true, blank: true)
swaggerVersion(nullable: true, blank: true)
jsonData(type: 'text', nullable: true, blank: true)
dateCreated()
lastUpdated()
deprecationDate(nullable: true)
regionSource(nullable: true)
contractName(nullable: true)
contractBasePath(nullable: true)
}
public Method getMethod(String name) {
for (Method method : methods) {
if (method.name.equals(name)) {
return method
}
}
return null
}
}
class Method implements Serializable, Comparable<Method> {
String name
boolean enabled
String edgePath
HttpMethodEnum edgeHttpMethod
String servicePath
HttpMethodEnum serviceHttpMethod
String summary
String notes
boolean deprecatedMethod
String responseClass
SortedSet<EdgeParameter> edgeParameters
SortedSet<ServiceParameter> serviceParameters
Date dateCreated
Date lastUpdated
String publicResponseTransformScript
String baseResponseTransformScript
String regionFieldName
String platformFieldName
String apiKeyFieldName
String uriTransformScript
long cacheExpiry
static belongsTo = [api: Api]
static hasMany = [edgeParameters: EdgeParameter, serviceParameters: ServiceParameter, errorResponses: ApiErrorResponse]
static mapping = {
cache true
errorResponses sort: 'code', order: "asc"
}
static constraints = {
name(blank: false)
enabled()
edgePath(nullable: false, blank: false)
edgeHttpMethod(nullable: false, blank: false)
servicePath(nullable: false, blank: false)
serviceHttpMethod(nullable: false, blank: false)
summary(nullable: false, blank: false)
notes(nullable: true, blank: true)
deprecatedMethod()
responseClass(nullable: true, blank: true)
publicResponseTransformScript(nullable: true)
baseResponseTransformScript(nullable: true)
regionFieldName(nullable: true)
platformFieldName(nullable: true)
apiKeyFieldName(nullable: true)
uriTransformScript(nullable: true)
cacheExpiry(nullable: false, blank: true)
}
}
答案 0 :(得分:0)
好吧,我明白了。我做了很多步骤通过hibernate / GORM初始化代码。我发现问题实际上是在同一个类中使用无关的映射。在Method类中,上面显示了另一个名为errorResponses
的映射。当调用方法DefaultGrailsDomainClass.establishRelationshipForCollection
来建立Api和Method类之间的关系时,它会调用另一个方法GrailsClassUtils.getPropertiesOfType
。此类迭代Method类的所有属性,并尝试查找其类型与api
关联的类型匹配的任何属性。以下是方法实现:
public static PropertyDescriptor[] getPropertiesOfType(Class<?> clazz, Class<?> propertyType) {
if (clazz == null || propertyType == null) {
return new PropertyDescriptor[0];
}
Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();
try {
for (PropertyDescriptor descriptor : BeanUtils.getPropertyDescriptors(clazz)) {
Class<?> currentPropertyType = descriptor.getPropertyType();
if (isTypeInstanceOfPropertyType(propertyType, currentPropertyType)) {
properties.add(descriptor);
}
}
}
catch (Exception e) {
// if there are any errors in instantiating just return null for the moment
return new PropertyDescriptor[0];
}
return properties.toArray(new PropertyDescriptor[properties.size()]);
}
它找到的第一个属性是api
属性,它通过isTypeInstanceOfPropertyType
检查并添加到properties
集。由于名为getApiErrorResponse
的命名较差的辅助方法,对BeanUtils.getPropertyDescriptors(clazz)
的调用在返回的集合中包含一个名为apiErrorResponse
的字段。但是,我的Method类中没有这样的字段(有一个名为errorResponses
的映射关联,但没有一个叫apiErrorResponse
)。因此,此不存在字段的propertyType
的{{1}}为空。然后,当使用PropertyDescriptor
的{{1}}值调用isTypeInstanceOfPropertyType
时,它会抛出NullPointerException。这会导致null
方法返回currentPropertyType
,如getPropertiesOfType
子句中所示。稍后,无法返回映射到new PropertyDescriptor[0]
的属性描述符会导致为关联生成不正确的OneToOne映射类型,并且该映射也缺少映射字段。
基本上这是一个grails代码没有提供关于什么是真正出错的足够信息的情况。它默默地抑制了在另一个字段上发生的NullPointerException,并且该另一个字段上的错误导致catch
字段的初始化不正确。这意味着后来的错误信息是不正当的抱怨,因为信息/配置被假定为缺失,导致工程师非常困惑。
我的最终解决方法是将getApiErrorResponse帮助器方法移出Method类并移入MethodService。请注意,在我的域对象中使用这个帮助器方法在我的旧Grails项目(带有Hiberate 3的Grails 2.2.4)中运行正常,因此它可能是最新版本的Grails / GORM / Spring / Groovy中的新行为假设所有get *方法都映射到字段。我不知道哪些是罪魁祸首,但它们都是比旧项目更新的版本。
我还向grails-core提交了一个PR,以便在发生这些异常时记录有用的错误消息,而不是无声地吞下它们。