0

次の 2 つのドメイン クラスがあります。

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

私が間違っているかもしれないことを知っていますか?

事前にご回答いただきありがとうございます。

4

1 に答える 1

1

User.organizations コレクションを反復処理し、User/Organization 間の各関連付けを削除すると、Hibernate は同じ User.organizations コレクションを更新しようとしている (削除を反映するため) と考えられます。これにより、関連付けの削除が防止されます。これにより、制約違反が発生します。

beforeDelete を使用するこの問題の解決策を見たことがありません (そのロジックの論理的な場所のように見えるので残念です) が、次の回避策を見てきました:

  1. 削除の前にコントローラー/サービス層に関連付けを削除するロジックを配置します (これもまた、この状況では beforeDelete が機能しないのは残念です:()。 User.organizations を同時に変更することを避けるために、コレクションのコピーを作成して反復することができます。関連付けられた組織のコピーまたはクエリ。サンプル クエリは次のとおりです。

    組織から o を選択 o o.members AS に参加 m.id=:id
  2. 次のように、ユーザー/組織間の関連付けを明示的にします。

    クラスUserOrganization {
      ユーザー ユーザー
      組織組織
    }
    
    クラスユーザー{
      ...
      Set<組織> getOrganizations() {
        UserOrganization.findAllByUser(this).collect{ it.org } as Set
      }
    }
    

SpringSecurityCore プラグインの User/Authority コードを見ると、関連付けを明示的に管理する方法の良い例があります。

于 2012-10-14T08:28:56.333 に答える