当一个 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'
}
问题解决!
参考文档: