2012-10-10 61 views
0

我有這兩個領域類:GORM - 修改對象的beforeDelete事件不起作用

class User { 
    String username 
    String password 

    static hasMany = [organizations: Organization] 
    static belongsTo = Organization 

    static constraints = { 
     username blank: false, unique: true 
     password blank: false 
    } 

    static mapping = { 
     password column: '`password`' 
     organizations fetch: 'join' 
    } 

    def beforeDelete() { 
     User.withNewSession { 
      this.organizations.each { 
       it.removeFromMembers(this) 
      } 
      this.save() 
     } 
    } 
} 

class Organization { 
    String name; 

    static hasMany = [members: User] 
} 

我試圖做到的,是我認爲很清楚 - 我希望能夠刪除一個用戶,但在此之前,我必須從他被分配到的所有組織中刪除該用戶。 GORM顯然不支持這個,所以我試圖在類User上編寫beforeDelete事件,這會在刪除之前從所有組織中刪除用戶。問題是,上面的代碼不工作,這是個例外,它拋出:

| Error 2012-10-10 16:27:56,898 [http-bio-8080-exec-2] ERROR errors.GrailsExceptionResolver - NonUniqueObjectException occurred when processing request: [POST] /theses-management/user/index - parameters: 
id: 1 
_action_delete: Delete 
a different object with the same identifier value was already associated with the session: [com.redhat.theses.auth.User#1]. Stacktrace follows: 
Message: a different object with the same identifier value was already associated with the session: [com.redhat.theses.auth.User#1] 
    Line | Method 
->> 36 | doCall    in com.redhat.theses.auth.User$_beforeDelete_closure1$$ENlS7bOi 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|  32 | beforeDelete  in com.redhat.theses.auth.User$$ENlS7bOi 
|  46 | onApplicationEvent in org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener 
|  93 | delete    in com.redhat.theses.auth.UserController$$ENlS7klM 
| 195 | doFilter . . . . . in grails.plugin.cache.web.filter.PageFragmentCachingFilter 
|  63 | doFilter   in grails.plugin.cache.web.filter.AbstractFilter 
| 1110 | runWorker . . . . in java.util.concurrent.ThreadPoolExecutor 
| 603 | run    in java.util.concurrent.ThreadPoolExecutor$Worker 
^ 722 | run . . . . . . . in java.lang.Thread 

所以我累了改變beforeDelete事件是這樣的:

def beforeDelete() { 
    User.withNewSession { 
     this.organizations.each { 
      it.removeFromMembers(this) 
     } 
     this.merge() 
     this.save() 
    } 
} 

但後來同樣的例外是被拋出...

的另一種嘗試是這樣的:

def beforeDelete() { 
    User.withNewSession { 
     this.organizations.each { 
      it.removeFromMembers(this) 
     } 
     def user = this.merge() 
     user.save() 
    } 
} 

結果是這樣的例外:

| Error 2012-10-10 16:33:22,669 [http-bio-8080-exec-7] ERROR util.JDBCExceptionReporter - Referential integrity constraint violation: "FK6BC87A0D3E1F37F9: PUBLIC.ORGANIZATION_MEMBERS FOREIGN KEY(USER_ID) REFERENCES PUBLIC.USER(ID)"; SQL statement: 
delete from user where id=? and version=? [23503-164] 
| Error 2012-10-10 16:33:22,673 [http-bio-8080-exec-7] ERROR events.PatchedDefaultFlushEventListener - Could not synchronize database state with session 
Message: could not delete: [com.redhat.theses.auth.User#1] 
    Line | Method 
->> 93 | delete in com.redhat.theses.auth.UserController$$ENlS7klM 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
| 195 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter 
|  63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter 
| 1110 | runWorker in java.util.concurrent.ThreadPoolExecutor 
| 603 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker 
^ 722 | run  in java.lang.Thread 

Caused by JdbcSQLException: Referential integrity constraint violation: "FK6BC87A0D3E1F37F9: PUBLIC.ORGANIZATION_MEMBERS FOREIGN KEY(USER_ID) REFERENCES PUBLIC.USER(ID)"; SQL statement: 
delete from user where id=? and version=? [23503-164] 
->> 329 | getJdbcSQLException in org.h2.message.DbException 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
| 169 | get  in  '' 
| 146 | get . . . in  '' 
| 398 | checkRow in org.h2.constraint.ConstraintReferential 
| 415 | checkRowRefTable in  '' 
| 291 | checkRow in  '' 
| 862 | fireConstraints in org.h2.table.Table 
| 879 | fireAfterRow in  '' 
|  99 | update . in org.h2.command.dml.Delete 
|  73 | update in org.h2.command.CommandContainer 
| 226 | executeUpdate in org.h2.command.Command 
| 143 | executeUpdateInternal in org.h2.jdbc.JdbcPreparedStatement 
| 129 | executeUpdate in  '' 
| 105 | executeUpdate in org.apache.commons.dbcp.DelegatingPreparedStatement 
|  93 | delete . in com.redhat.theses.auth.UserController$$ENlS7klM 
| 195 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter 
|  63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter 
| 1110 | runWorker in java.util.concurrent.ThreadPoolExecutor 
| 603 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker 
^ 722 | run  in java.lang.Thread 
| Error 2012-10-10 16:33:22,680 [http-bio-8080-exec-7] ERROR errors.GrailsExceptionResolver - JdbcSQLException occurred when processing request: [POST] /theses-management/user/index - parameters: 
id: 1 
_action_delete: Delete 
Referential integrity constraint violation: "FK6BC87A0D3E1F37F9: PUBLIC.ORGANIZATION_MEMBERS FOREIGN KEY(USER_ID) REFERENCES PUBLIC.USER(ID)"; SQL statement: 
delete from user where id=? and version=? [23503-164]. Stacktrace follows: 
Message: Referential integrity constraint violation: "FK6BC87A0D3E1F37F9: PUBLIC.ORGANIZATION_MEMBERS FOREIGN KEY(USER_ID) REFERENCES PUBLIC.USER(ID)"; SQL statement: 
delete from user where id=? and version=? [23503-164] 
    Line | Method 
->> 329 | getJdbcSQLException in org.h2.message.DbException 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
| 169 | get     in  '' 
| 146 | get . . . . . . . . . in  '' 
| 398 | checkRow    in org.h2.constraint.ConstraintReferential 
| 415 | checkRowRefTable . . in  '' 
| 291 | checkRow    in  '' 
| 862 | fireConstraints . . . in org.h2.table.Table 
| 879 | fireAfterRow   in  '' 
|  99 | update . . . . . . . in org.h2.command.dml.Delete 
|  73 | update    in org.h2.command.CommandContainer 
| 226 | executeUpdate . . . . in org.h2.command.Command 
| 143 | executeUpdateInternal in org.h2.jdbc.JdbcPreparedStatement 
| 129 | executeUpdate . . . . in  '' 
| 105 | executeUpdate   in org.apache.commons.dbcp.DelegatingPreparedStatement 
|  93 | delete . . . . . . . in com.redhat.theses.auth.UserController$$ENlS7klM 
| 195 | doFilter    in grails.plugin.cache.web.filter.PageFragmentCachingFilter 
|  63 | doFilter . . . . . . in grails.plugin.cache.web.filter.AbstractFilter 
| 1110 | runWorker    in java.util.concurrent.ThreadPoolExecutor 
| 603 | run . . . . . . . . . in java.util.concurrent.ThreadPoolExecutor$Worker 
^ 722 | run     in java.lang.Thread 

你知道我可能會做錯什麼嗎?

感謝您提前給出答案。

+0

如果你刪除'this.save()'會怎麼樣?刪除已經做到了。 –

+0

參照完整性約束違規...與上面相同的異常。 – VaclavDedik

回答

1

我相信發生的事情是,當您遍歷User.organizations集合並刪除User/Organization之間的每個關聯時,Hibernate會嘗試更新同一個User.organizations集合(以反映您的刪除) - 這可以防止關聯刪除,這會導致違反約束。

我從來沒有見過一個使用beforeDelete(不幸的,因爲它似乎是這種邏輯的合理位置),這個問題的解決方案,但已經看到了以下工作方式:

  1. 放置邏輯在刪除之前刪除控制器/服務層中的關聯(再次太糟糕了beforeDelete不起作用:(在這種情況下)爲了避免同時修改User.organizations,你可以製作一個集合的副本並迭代副本,或查詢相關組織,以下是一個示例查詢:

    select o from Organization o join o.members AS m where m.id=:id
  2. 讓用戶/組織之間的關係明確,如:

     
    class UserOrganization { 
        User user 
        Organization org 
    } 
    
    class User { 
        ... 
        Set<Organization> getOrganizations() { 
        UserOrganization.findAllByUser(this).collect{ it.org } as Set 
        } 
    } 
    

如果你看一下SpringSecurityCore插件用戶/機構代碼,還有如何明確地管理協會的一個很好的例子。