一個 GORM 定義錯誤,關於集合屬性的排序設置

當一個 Domain Class 有一個集合屬性時,我們可以指定集合的排序方法。
示例代碼如下:

class Airport {
    ...
    static hasMany = [flights: Flight]

    static mapping = {
        flights sort: 'number', order: 'desc'
    }
}

上面的代碼就可以讓 機場(Airport) 的 航班(Flight) 按照 number 倒序排列。

這裏需要注意,number 屬性是 Flight 類的一個屬性而不是 Airport 的屬性,如果設置了錯誤的屬性,將導致程序在做集成測試時啓動失敗。

今天就遇到了這個錯誤,在執行集成測試時報告錯誤:

java.lang.IllegalStateException: Failed to load ApplicationContext

檢查異常堆棧日誌

...
...省略大量關係不大的異常信息...
...
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.orm.hibernate.HibernateDatastore]: Constructor threw exception; nested exception is java.lang.NullPointerException
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:184)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:117)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:300)
	... 85 common frames omitted
Caused by: java.lang.NullPointerException: null
	at org.grails.orm.hibernate.cfg.GrailsDomainBinder.bindCollectionSecondPass(GrailsDomainBinder.java:368)
	at org.grails.orm.hibernate.cfg.GrailsDomainBinder.bindListSecondPass(GrailsDomainBinder.java:265)
	at org.grails.orm.hibernate.cfg.GrailsDomainBinder$ListSecondPass.doSecondPass(GrailsDomainBinder.java:3426)
	at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1684)
	at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1652)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:84)
	at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:474)
	at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:85)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)
	at org.grails.orm.hibernate.cfg.HibernateMappingContextConfiguration.buildSessionFactory(HibernateMappingContextConfiguration.java:287)
	at org.grails.orm.hibernate.connections.HibernateConnectionSourceFactory.create(HibernateConnectionSourceFactory.java:86)
	at org.grails.orm.hibernate.connections.AbstractHibernateConnectionSourceFactory.create(AbstractHibernateConnectionSourceFactory.java:39)
	at org.grails.orm.hibernate.connections.AbstractHibernateConnectionSourceFactory.create(AbstractHibernateConnectionSourceFactory.java:23)
	at org.grails.datastore.mapping.core.connections.AbstractConnectionSourceFactory.create(AbstractConnectionSourceFactory.java:64)
	at org.grails.datastore.mapping.core.connections.AbstractConnectionSourceFactory.create(AbstractConnectionSourceFactory.java:52)
	at org.grails.datastore.mapping.core.connections.ConnectionSourcesInitializer.create(ConnectionSourcesInitializer.groovy:24)
	at org.grails.orm.hibernate.HibernateDatastore.<init>(HibernateDatastore.java:200)
	at sun.reflect.GeneratedConstructorAccessor73.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:172)
	... 87 common frames omitted

發現是NPE

at org.grails.orm.hibernate.cfg.GrailsDomainBinder.bindCollectionSecondPass(GrailsDomainBinder.java:368)

查看導致NPE的代碼片段

protected void bindCollectionSecondPass(ToMany property, InFlightMetadataCollector mappings,
                                        Map<?, ?> persistentClasses, Collection collection, 
                                        String sessionFactoryBeanName) {
......
	if (associatedClass != null) {
	    collection.setOrderBy(buildOrderByClause(propertyToSortBy.getName(), associatedClass, collection.getRole(),
	            propConfig.getOrder() != null ? propConfig.getOrder() : "asc"));
	}
......
}

通過設置斷點,發現是 propertyToSortBy 爲 null 了。

檢查導致錯誤的domain class代碼

class Campaign {
	List<PrizeSetting> prizeSettings
    static mapping = {
        campaignStatus enumType: "identity"
        // 指定 獎項 的排序規則
        prizeSettings sort: 'position', order: 'desc'
    }
}

仔細檢查,發現 PrizeSetting 沒有屬性 position,應該排序的字段是 prizeLevel。

修改爲正確代碼

    static mapping = {
        campaignStatus enumType: "identity"
        // 指定 獎項 的排序規則
        prizeSettings sort: 'prizeLevel', order: 'desc'
    }

問題解決!

參考文檔:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章