无法更改Grails中的域类属性值

时间:2014-04-13 17:00:25

标签: grails gorm grails-2.0 grails-domain-class

我正在开发 Grails 2.3.7 应用程序,但我在使用选择框更改域属性时遇到问题。每当我尝试更改属性并保存时,我都会得到HibernateException: identifier of an instance of Ethnicity was altered from X to Y。我不想改变种族的身份,我只是想将ApplicationPerson的种族从一个改为另一个。

有几点需要注意:

  1. 我使用相同的控制器操作来创建和更新此人。
  2. personInstance.ethnicity之前将null设置为personInstance.properties = params将使保存工作,但我 不知道为什么,我不想为每个协会做这件事 我想改变。
  3. 我意识到域模型看起来很奇怪。这是一个我无法改变的遗留数据库。
  4. 以下是我的域类:

    class ApplicationPerson implements Serializable {
        Integer appId
        Integer applicationSequenceNumber
        String firstName
        String lastName
        Ethnicity ethnicity
    
        static mapping = {
            id composite: ['appId', 'applicationSequenceNumber'],
               generator: 'assigned'
        }
    }
    
    class Ethnicity {
        String code
        String description
    
        static mapping = {
            id name: 'code', generator: 'assigned'
        }
    }
    

    我的_form.gsp更新了Ethnicity(我删除了所有其他保存得很好的属性):

    <div class="fieldcontain ${hasErrors(bean: personInstance, 
                                         field: 'ethnicity', 'error')} ">
        <label for="ethnicity">Ethnicity</label>
        <g:select id="ethnicity" 
                  name="ethnicity.code" 
                  from="${Ethnicity.list()}" 
                  optionKey="code" 
                  value="${personInstance?.ethnicity?.code}" />
    </div>
    

    最后,我的控制器操作形式POST到:

    def save() {
        Application app = applicationService.getCurrentApplication()
    
        // Find/Create and save Person
        ApplicationPerson personInstance = app.person
        if (!personInstance) {
            personInstance = 
                new ApplicationPerson(appId: app.id, 
                                      applicationSequenceNumber: app.sequenceNumber)
        }
        personInstance.properties = params
    
        if (!personInstance.validate()) {
            respond personInstance.errors, view:'edit'
            return
        }
    
        personInstance.save flush:true
        redirect action: 'list'
    }
    

1 个答案:

答案 0 :(得分:2)

select元素中的名称从ethnicity.code修改为personInstance.etnicity.code,如下所示:

<g:select id="ethnicity" 
          name="personInstance.ethnicity.code" 
          from="${Ethnicity.list()}" 
          optionKey="code" 
          value="${personInstance?.ethnicity?.code}" />

所选选项的名称绑定到params作为键,选定的值绑定为 value反对密钥。使用ethnicity.code会尝试修改现有ethnicity的主键,而不是修改应用程序人员的种族。

<强>更新
名称的上述更改是可选的(可以在您不需要将params指定为域类的属性的情况下使用)。以前的名称ethnicity.code也应该有效,但在控制器操作中也需要进行更改以便设置种族:

//or use params.ethnicity.code
//if name is ethnicity.code
person.ethnicity = Ethnicity.load(params.personInstance.ethnicity.code)

//or use params if name is ethnicity.code
person.properties = params.personInstance

根据传入的Ethnicity加载现有code,然后在设置属性之前将其设置为person

问题在于codeEthnicity的主键。如果Ethnicity有一个单独的标识列(例如,默认的长id),那么您的确切实现将在数据绑定的帮助下工作。但是,由于提到您正在使用旧数据库,我想您将无法修改表以添加id的另一列。所以你最好的选择是load(与从hibernate缓存中加载的行相比便宜)从传入的代码中获取种族,然后将其设置为person。

如果可能的话,您还可以看到尝试缓存种族域名,因为这将是主数据和缓存的良好候选者。