我有这两个域类:
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 :(得分:1)
我相信正在发生的事情是,当您迭代User.organizations集合并删除User / Organization之间的每个关联时,Hibernate正在尝试更新同一个User.organizations集合(以反映您的删除) - 这会阻止关联删除,导致您的约束违规。
我从来没有见过使用beforeDelete的这个问题的解决方案(不幸因为它似乎是逻辑的合理位置),但是已经看到了以下解决方法:
放置逻辑以在删除之前删除控制器/服务层中的关联(在删除之前再次太糟糕了:(在这种情况下)。为了避免同时修改User.organizations,您可以制作副本的集合和迭代副本,或查询相关的组织,这是一个示例查询:
select o from Organization o join o.members AS m where m.id=:id
明确用户/组织之间的关联,如:
class UserOrganization { User user Organization org } class User { ... Set<Organization> getOrganizations() { UserOrganization.findAllByUser(this).collect{ it.org } as Set } }
如果你看一下SpringSecurityCore插件用户/权限代码,就会有一个很好的例子来说明如何明确地管理关联。