我正在将我的 grails 2 应用程序升级到 grails 4。我已经能够纠正所有编译时错误,现在应用程序运行。它在点击控制器操作时会引发此错误。
No enum constant com.company.compositeEvent.ResultsStatus.2
在控制器的这一行中
if (idList){
result = RaceGroup.createCriteria().list([sort: 'startDateTime', order: 'asc', offset: offset, max: max]){
inList('id', idList)
join 'address'
}
}
RaceGroup 有 resultsstatus 属性
ResultsStatus resultsStatus
ResultsStatus 是一个枚举,定义为
enum ResultsStatus {
NO_RESULTS(1),
RACE_RESULTS(2),
EXTERNAL_RESULTS(3)
int id
private ResultsStatus(int id){
this.id = id
}
}
它在 grails 2 中运行良好。我怀疑 grails 4 中发生了一些变化,也许定义枚举的方式是错误的。我感谢任何指南。
更新:
RaceGroup 域定义为
class RaceGroup extends CompositeEvent implements Serializable{
List<Race> races
static hasMany = [races: Race]
static mappedBy = [races: 'raceGroup']
static mapping = {
discriminator RaceGroup.getSimpleName()
races cascade: 'all-delete-orphan'
}
static constraints = {
}
List<SubEvent> getSubEvents(){
this.races
}
}
CompositeEvent 定义为
abstract class CompositeEvent extends BaseEntity implements Serializable, Named, Addressable{
EventGroup eventGroup
/**
* Currency to use for this Event. Defaults to 'USD'
*/
final Currency currency = Currency.getInstance('USD')
/**
* User that created this event.
*/
User user
/**
* Name of this overall event.
* <p>
* 'compositeEventName' is used instead of just 'name' because when sorting
* by name, billingPerson.name is used by grails.
*/
String compositeEventName
/**
* General Information that is entered by the director and displayed on
* the registration page.
*/
String displayMessage
/**
* Location name of the address of this event
*/
String locationName
/**
* Address of this event.
* <p>
* Address is stored as one of its sub-classes: USAddress, CanadaAddress,
* etc.
*/
Address address
/**
* Start date and time of this event.
*/
Date startDateTime
/**
* Time zone to display the formatted date in.
*/
TimeZone timeZone
/**
* End date of this event.
*/
SqlDate endDate
/**
* The total number of possible participants, number reserved spots,
* and the number of allocated spots.
*/
Capacity capacity = new Capacity(reference: this)
String token = UUID.randomUUID().toString().replaceAll('-', '').take(12)
Boolean autoTransferToV6Active
Boolean showMaxParticipantsLimitInRegistrationPage = false
Integer showMaxParticipantsLimitBelowThreshold
/**
* Director on the registration page listed as being in charge of
* this event.
*/
Director director
/**
* Person or organization responsible for handling billing and payment
* in association with this event.
*/
BillingPerson billingPerson
String displayLiveRegistrationsPassword
Boolean displayLiveRegistrations
/**
* Whether the list of individuals registered for this event or any of
* its sub-events will be displayed on the registration page.
*/
Boolean displayParticipants
/**
* Whether the number of individuals registered for this event or any
* sub-events will be displayed on the registration page.
*/
Boolean displayEventRegNum
/**
* Whether the teams registered for this event or any sub-events will
* be displayed on the registration page
*/
Boolean displayTeams
/**
* Whether the number of people in each team will displayed on the
* registration page.
*/
Boolean displayTeamRegNum
/**
* Whether seed marks will be displayed in the list of participants
*/
Boolean displaySeedMarks
/**
* Whether to display the product photos in the public event page
*/
Boolean displayProductImages = true
/**
* Whether this event is listed among active events and can the
* registration page can be accessed.
*/
Boolean published = false // whether this composite event can be viewed
Boolean unlisted = false // whether this composite event is listed on the website
Boolean emailReceipts = true // email a receipt each time someone registers
/**
* The date from which age will be calculated for participants.
*
* The date must be:
* <p><i>
* this.startDateTime - 1 year <= this.ageAsOf <= this.startDateTime
* </i>
*/
SqlDate ageAsOf
/**
* External web-site with information regarding this event
*/
String homePage
/**
* Required fields of each participant that are set by the director.
*/
RequiredParticipantFields requiredFields
/**
* Logo for this event
*/
Image logo
/**
* Taxes applied to this event
*/
Tax tax
/**
* Access code to be access registration for all subEvents. An access code
* set in an order template would be used instead of this if both are set.
*/
String accessCode
/**
* Disclaimer that must be accepted by all participants before registering.
*/
Disclaimer disclaimer
/**
* TimingDetails contains data in regards to requesting and providing
* timing services from RunnerCard
*/
TimingDetails timingDetails
//Status of Referral. Whether or not to show the referral or share on facebook button
ReferralStatus referralStatus
String bibsRange
Boolean assignBibNumbers
//in registration form whether to allow user to select more than 1 shirt
Boolean allowMultiProductSelectionInRegistration
//in event registration page show products only purchase
Boolean enableProductsOnlyPurchase = true
Boolean enableVirtualRace
/**
* Ids of past events all separated by a delimiter comma (,)
* eg 51243, 51234, 64345, 43454
*/
String eventHistory
Set<Product> products
String resultsLink
ResultsStatus resultsStatus
// automatically updated by GORM
Date dateCreated
Date lastUpdated
abstract List<SubEvent> getSubEvents()
static transients = ['name', 'nameIdAndParticipants']
static hasMany = [products: Product]
//static belongsTo = [eventGroup: EventGroup]
static mapping = {
address cascade: 'all'
director cascade: 'save-update,merge,refresh,evict,lock'
displayMessage type: 'text'
requiredFields unique: true
capacity cascade: 'refresh,delete'
bibsRange type: 'text'
}
static namedQueries = {
withAddress{
join 'address'
}
withBilling{
join 'billingPerson'
}
withRequiredFields{
join 'requiredFields'
}
}
static constraints = {
compositeEventName blank: false
showMaxParticipantsLimitInRegistrationPage nullable: true
showMaxParticipantsLimitBelowThreshold nullable: true
autoTransferToV6Active nullable: true
enableVirtualRace nullable: true
eventGroup nullable: true
eventHistory blank:true, nullable: true
displayMessage blank: false, nullable: true
locationName blank: false
referralStatus nullable: true
bibsRange nullable:true
assignBibNumbers nullable: true
allowMultiProductSelectionInRegistration nullable: true
enableProductsOnlyPurchase nullable: true
address()
timeZone bindable: true, inList: TimeService.TIME_ZONES
endDate validator: { SqlDate endDate, CompositeEvent obj ->
if (obj.startDateTime == null) return
Calendar start = Calendar.getInstance()
start.setTime(obj.startDateTime)
start.set(Calendar.HOUR_OF_DAY, 0)
start.set(Calendar.MINUTE, 0)
start.set(Calendar.SECOND, 0)
start.set(Calendar.MILLISECOND, 0)
Date startDay = start.getTime()
DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT, LCH.getLocale())
if (startDay.compareTo(endDate) > 0){
return ["default.invalid.min.message", formatter.format(startDay)]
}
}
director validator: { Director person, CompositeEvent compositeEvent, Errors errors ->
UtilityService.cascadeValidation(person, 'director', errors)
}
billingPerson validator: { BillingPerson person, CompositeEvent compositeEvent, Errors errors ->
UtilityService.cascadeValidation(person, 'billingPerson', errors)
}
displayParticipants()
displayEventRegNum()
displayTeams()
displayTeamRegNum validator: {Boolean val, CompositeEvent obj, Errors errors ->
if (obj.displayTeams != null && val && !obj.displayTeams){
errors.rejectValue("displayTeamRegNum", "compositeEvent.invalid.displayTeamRegNum")
}
}
displaySeedMarks()
displayProductImages()
published()
homePage blank: false, nullable: true, validator: { String homePage, CompositeEvent compositeEvent ->
// If it is a url that isn't http:// or https://
if (compositeEvent.homePage != null
&& !compositeEvent.homePage.matches('^http(s)?://.*')
&& compositeEvent.errors.getFieldErrors('homePage').size() == 0){
return "default.invalid.url.message"
}
}
ageAsOf nullable: true // if null my.calculateAge() will calculate age based on the current Date
requiredFields nullable: true
logo nullable: true
tax nullable: true, validator: { Tax tax, obj, Errors errors ->
UtilityService.cascadeValidation(tax, 'tax', errors)
}
accessCode blank: false, nullable: true
displayLiveRegistrationsPassword nullable: true
displayLiveRegistrations nullable: true
resultsLink nullable: true
resultsStatus nullable: true
disclaimer()
timingDetails nullable: true
}
/**
* Saves capacity of this event at initial save
*/
void beforeInsert(){
this.capacity.save()
}
void afterInsert(){
CompositeEvent.withNewSession{
def productOwnerSettingService = this.getBean("productOwnerSettingService")
productOwnerSettingService.createSetting(this)
}
}
/**
* Checks that the logged in user has delete or admin privileges on
* this object and then deletes all AutoUpdateJob instances and acl
* objects that reference this object.
*/
void beforeDelete(){
CompositeEvent.withNewSession{
def posc = ProductOwnerSettingCompositeEvent.createCriteria().get(){
eq('event', this)
}
def setting = posc?.productOwnerSetting
if(posc){
posc.delete(flush: true)
}
if(setting) {
setting.delete(flush: true)
}
}
super.beforeDelete()
}
}
private Object getBean(String beanName){
try{
return this.domainClass.grailsApplication.mainContext.getBean(beanName)
}
catch(NoSuchBeanDefinitionException e){
return null
}
catch(MissingPropertyException e){
return null
}
}
/* (non-Javadoc)
* @see com.runnercard.Named#getName()
*/
String getName(){ this.compositeEventName }
void setName(String name){ this.compositeEventName = name }
}
地址域定义为
abstract class Address extends BaseEntity implements Serializable{
String address1
String address2
String city
String area
String postalCode
// automatically updated by GORM
Date dateCreated
Date lastUpdated
static belongsTo = [User, BillingPerson, CompositeEvent, RaceParticipant,
EmbeddedRaceParticipant]
static mapping = {
discriminator value: Address.getSimpleName(), column: 'country'
}
static constraints = {
address1 nullable: true
address2 nullable: true
city nullable: true
area nullable: true
postalCode nullable: true
}
static List getCountries(){
return ['usa', 'can', 'other']
}
boolean equals(Object obj){
if (!Address.isInstance(obj)) return false
Address other = (Address) obj
if (this.address1 == other.address1
&& this.address2 == other.address2
&& this.city == other.city
&& this.area == other.area
&& this.postalCode == other.postalCode){
return true
}
else{
return false
}
}
int hashCode(){
HashCodeBuilder builder = new HashCodeBuilder(1333, 1353)
if (this.address1) builder.append(this.address1)
if (this.address2) builder.append(this.address2)
if (this.city) builder.append(this.city)
if (this.area) builder.append(this.area)
if (this.postalCode) builder.append(this.postalCode)
builder.toHashCode()
}
}