My Grails is version 2.5.0
I have two domain classes:
import org.apache.commons.lang.builder.HashCodeBuilder
class TemplateHeader implements Serializable {
int headerId
int ver
String templateName
String extraDescription
String serviceType
String createdBy
boolean isActive
String updatedBy
Date dateCreated
Date lastUpdated
static hasMany = [templateItems: TemplateItem]
static mapping = {
id composite: ['headerId', 'ver']
ver comment:''
templateName length: 40
serviceType length: 20
extraDescription length: 60
isActive defaultValue:false
templateItems sort:'itemNo', order:'asc'
}
static constraints = {
headerId unique: 'ver'
templateName nullable: false
serviceType nullable: false
}
boolean equals(other) {
if (!(other instanceof TemplateItem)) {
return false
}
other.id == id && other.ver == ver
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append id
builder.append ver
builder.toHashCode()
}
}
=======================================================
import org.apache.commons.lang.builder.HashCodeBuilder
class TemplateItem implements Serializable {
int itemNo
String itemName;
String unitName;
int unitPrice;
double defaultValue
boolean allowChange
String extraComment
String createdBy
String updatedBy
Date dateCreated
Date lastUpdated
static belongsTo = [templateHeader:TemplateHeader]
static mapping = {
id composite: [ 'templateHeader', 'itemNo']
itemNo comment:''
itemName length: 60
unitName length: 4
unitPrice comment:''
extraComment length: 60
defaultValue comment:''
allowChange comment:''
}
static constraints = {
itemName nullable: false
unitName nullable: false
extraComment nullable: true
defaultValue nullable: false
}
boolean equals(other) {
if (!(other instanceof TemplateItem)) {
return false
}
other.itemNo == itemNo && other.templateHeaderId == templateHeaderId
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append templateHeaderId
builder.append itemNo
builder.toHashCode()
}
}
When I run the grails application, it shows the below Exception when build tables:
|Running Grails application
context.GrailsContextLoaderListener Error initializing the application: Error creating bean with name 'transactionManagerPostProcessor': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Invocation of init method failed; nested exception is org.codehaus.groovy.grails.exceptions.GrailsDomainException: property from sort clause not found: accudata_portal.TemplateItem.itemNo
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManagerPostProcessor': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Invocation of init method failed; nested exception is org.codehaus.groovy.grails.exceptions.GrailsDomainException: property from sort clause not found: accudata_portal.TemplateItem.itemNo
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Invocation of init method failed; nested exception is org.codehaus.groovy.grails.exceptions.GrailsDomainException: property from sort clause not found: accudata_portal.TemplateItem.itemNo
... 4 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Invocation of init method failed; nested exception is org.codehaus.groovy.grails.exceptions.GrailsDomainException: property from sort clause not found: accudata_portal.TemplateItem.itemNo
... 4 more
Caused by: org.codehaus.groovy.grails.exceptions.GrailsDomainException: property from sort clause not found: accudata_portal.TemplateItem.itemNo
... 4 more
Error |
Forked Grails VM exited with error
Does somebody tell me how to fix it ? Thank you
I wonder why your'e not getting errors for fields that you are using in mapping closure and for which you have not defined any mappings like itemNo, unitPrice, defaultValue etc.
Also the equals condition in your TemapletHeader domain should be:
if (!(other instanceof TemplateHeader))
Now comming to your question, you are getting this error because the itemNo property is inside TemplateItem domain not in TemplateHeader domain. Also the sort keyword accepts a map.
To sort TemplateItem objects based on the itemNo field, you would have to define the sort property in mapping closure for TemplateItem domain
static mapping = {
.
.
.
sort 'itemNo': 'asc'
}
Now whenever you will make a query to TemplateItem, the default sorting will be on itemNo field. So if you use
TemplateItem.findAllByTemplateHeader(templateHeaderInstance)
it will give sorted TemplateItems based on itemNo.
By defining the sort property in TemplateItem domain will not sort the templateItems field in TemplateHeader domain. Reason for which is that while fetching an association for a domain object Grails ignores the sort field for that domain. If you look at the sql being generated while fetching the TemplateHeader object and its associations you will see the difference.
Apparently there is no direct way for what you want to achieve, depending upon whether do you want to sort TemapleHeader objects based on templateItems or just sort TemplateItems within a single TemplateHeader object
To sort TemplateHeader objects based on the TemplateItems, add below sort property to mapping closure of TemplateHeader domain:
static mapping = {
.
.
.
sort 'templateItems.itemNo': 'asc'
}
And when you will execute this GORM
TemplateHeader header = TemplateHeader.list()
it generates following sql
select this_.header_id as header_i1_0_1_, this_.ver as ver2_0_1_, .... from template_header this_ inner join template_item templateit1_ on this_.header_id=templateit1_.template_header_header_id and this_.ver=templateit1_.template_header_ver order by templateit1_.item_no asc
Now the problem with this query is that its execution cost is very high. It is using inner join between TemplateHeader and TemplateItem domains. So if you have 2 records in TemplateHeader domain and 10 in TemplateItem domain, it will fetch 10 record while you need only 2. Another issue is that templateItems property will still be fetched lazily. So while you do header.templateItems, it will again make a query to database and the result will not be sorted based on itemNo. sql will look like this:
select templateit0_.template_header_header_id as template1_0_0_, templateit0_.template_header_ver as template2_0_0_, ... from template_item templateit0_ where templateit0_.template_header_header_id=? and templateit0_.template_header_ver=?
So if you want to sort the templateItems for a TemplateHeader object, you can over ride the getter for templateItems and sort the result there:
Set<TemplateItem> getTemplateItems() {
return templateItems?.sort { item_1, item_2 -> item_1?.itemNo <=> item_2?.itemNo }
}
Just in case if you get an error like this while adding TemplateItem object to TemplateHeader object:
No signature of method: TemplateHeader.addToTemplateItems() is applicable for argument types: (TemplateItem)
then try with declaring templateItems as List.