grails 4 没有枚举常量?

grails 4 no enum constant?

我正在将我的 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

ResultStatus 是一个枚举,定义为

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()
    }
}

问题是我不得不把它放在映射中

    resultsStatus enumType: 'ordinal'