从Hibernate 3 / Grails 2.2.4迁移到Hiberate 5 / Grails 3.2.4

时间:2017-01-12 19:46:52

标签: hibernate grails gorm

我有一个使用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)
    }
}

1 个答案:

答案 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,以便在发生这些异常时记录有用的错误消息,而不是无声地吞下它们。

https://github.com/grails/grails-core/pull/10400