【问题标题】:Getting 'Referential integrity constraint violation' when deleting domain object删除域对象时出现“参照完整性约束违规”
【发布时间】:2012-11-23 08:32:32
【问题描述】:

在我疯狂的宇宙中,一个房间可以有很多椅子,而椅子可以“属于”很多房间。 在 grails 中它看起来像这样。椅子不应该知道他们属于哪个房间。

class Room {
    String name
    static hasMany = [chairs: Chair]
    static constraints = {
    }
}
class Chair {
    String name
    static constraints = {
    }
}

我想删除一把椅子并自动删除所有拥有该椅子的房间域对象中对椅子的任何引用。 我成功了,但有一个我不喜欢的修复程序。在 ChairController 我做了以下

def deleleChair(){
    def chairToDelete = Chair.get(params.id)
    Room.findAll().each {room->
        if(room.chairs.contains(chairToDelete)){
            room.removeFromChairs(chairToDelete)
            room.save(failOnError:true)
        }
    }
    chairToDelete.delete(params.chairId)
}

是否需要设置休眠配置以使其自动执行此操作?这在现实世界的场景中似乎很常见。当我可能决定(更疯狂的例子)Car 域对象可以有很多椅子时,我不想实现相同的代码。

我尝试使用 grails 事件推送插件。我有一个 ChairService,它有一个监听 gorm 中 beforeDelete 事件的动作。

class ChairService {
    @grails.events.Listener(topic = 'beforeDelete', namespace = "gorm")
    def handleDeletedChair(Chair chair){
         Room.findAll().each {room->
            if(room.chairs.contains(chairToDelete)){
                room.removeFromChairs(chairToDelete)
                room.save(failOnError:true, flush:true)
            }
        }
    }
}

每当尝试删除椅子时都会调用此函数,但是当它返回到 ChairController 以执行实际的椅子删除时,删除操作仍然认为对 Room 的引用仍然存在并抛出一个

 Caused by JdbcSQLException: Referential integrity constraint    violation:"FK4ACA6A6151428364: PUBLIC.ROOM_CHAIR FOREIGN KEY(CHAIR_ID) REFERENCES PUBLIC.CHAIR(ID)"; SQL statement:

从椅子上删除 id=?和版本=? [23503-164]

我希望这个逻辑与椅子分开,椅子不应该与房间无关。

【问题讨论】:

  • 如果您不在乎椅子可以看到房间,这会变得容易得多。

标签: sql hibernate grails


【解决方案1】:

因为这种关系不是双向存在的,所以我不确定是否有更优雅的解决方案。

但是,如果您要处理很多房间/椅子,我建议您使用带有预测的条件查询来仅加载您要删除的 ID,而不是加载所有房间的椅子。 更好使用executeUpdate 方法进行“批量”删除。这样做也应该有助于解决您的错误。

grails criteria query docs...

deleting objects in grails docs...

【讨论】:

    【解决方案2】:

    我在 ChairController.delete 中发现了错误,这是抛出的错误。 删除发生并删除关联,但抛出异常。 我的解决方案很讨厌,我基本上是捕获了异常并在之后渲染了相关视图。

    Caused by HibernateOptimisticLockingFailureException: Object of class [test.cascade.Chair] with identifier [2]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [test.cascade.Chair#2]
    ->>   10 | doCall             in test.cascade.ChairService$_handleDeletedChair_closure1
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    |      7 | handleDeletedChair in test.cascade.ChairService
    |    238 | invoke . . . . . . in org.grails.plugin.platform.events.registry.DefaultEventsRegistry$ListenerHandler
    |    159 | invokeListeners    in org.grails.plugin.platform.events.registry.DefaultEventsRegistry
    |     69 | event . . . . . .  in org.grails.plugin.platform.events.publisher.DefaultEventsPublisher
    |    149 | event              in org.grails.plugin.platform.events.EventsImpl
    |     70 | onApplicationEvent in org.grails.plugin.platform.events.publisher.GormBridgePublisher
    |   1110 | runWorker          in java.util.concurrent.ThreadPoolExecutor
    |    603 | run . . . . . . .  in java.util.concurrent.ThreadPoolExecutor$Worker
    ^    722 | run                in java.lang.Thread
    
    Caused by StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [test.cascade.Chair#2]
    ->>   10 | doCall             in test.cascade.ChairService$_handleDeletedChair_closure1
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    |      7 | handleDeletedChair in test.cascade.ChairService
    |    238 | invoke . . . . . . in org.grails.plugin.platform.events.registry.DefaultEventsRegistry$ListenerHandler
    |    159 | invokeListeners    in org.grails.plugin.platform.events.registry.DefaultEventsRegistry
    |     69 | event . . . . . .  in org.grails.plugin.platform.events.publisher.DefaultEventsPublisher
    |    149 | event              in org.grails.plugin.platform.events.EventsImpl
    |     70 | onApplicationEvent in org.grails.plugin.platform.events.publisher.GormBridgePublisher
    |   1110 | runWorker          in java.util.concurrent.ThreadPoolExecutor
    |    603 | run . . . . . . .  in java.util.concurrent.ThreadPoolExecutor$Worker
    ^    722 | run                in java.lang.Thread
    

    【讨论】:

      【解决方案3】:

      一个简单的解决方案是使用标准 SQL:

      import grails.events.Listener
      import groovy.sql.Sql
      
      class ChairService {
          def dataSource // autowired
      
          @Listener(topic='beforeDelete', namespace='gorm')
              def handleDeletedChair(Chair chairToDelete) {
              new Sql(dataSource).execute('DELETE FROM room_chair WHERE chair_id=?', [chairToDelete.id])
              return true
          }
      }
      

      由于这是 SQL,它可能取决于您使用的数据库(我用 H2 测试过)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多