當一個 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'
}
問題解決!
參考文檔: