尝试用关系执行软删除实体会导致异常

时间:2015-03-23 22:14:44

标签: grails gorm

最近,我在应用程序中实现了大多数实体的软删除(至少是我需要的实体)。 实现如下:

Goal.groovy

class Goal {

   String definition;
   Account account;
   boolean tmpl = false;
   String tmplName;

   Goal template

   Timestamp dateCreated
   Timestamp lastUpdated
   Timestamp deletedAt

   static belongsTo = [
      account: Account,
      template: Goal
   ]

   static hasMany = [perceptions: Perception, sessions: RankingSession]

   static mapping = {
      autoTimestamp true

      table 'goal'

      definition type: 'text'
      tmplName column: '`tmpl_name`'
      perceptions sort:'title', order:'asc'
      dateCreated column: 'date_created'
      lastUpdated column: 'last_updated'
      deletedAt column: 'deleted_at'
   }
   ...
   def beforeDelete() {
      if (deletedAt == null) {
         Goal.executeUpdate('update Goal set deletedAt = ? where id = ?', [new Timestamp(System.currentTimeMillis()), id])
      }

      return false
   }
   ...

Perception.groovy

class Perception {

   String title
   String definition
   Goal goal

   Timestamp dateCreated
   Timestamp lastUpdated
   Timestamp deletedAt

   static hasMany = [left: Rank, right: Rank]
   static mappedBy = [left: "left", right: "right"]

   static belongsTo = [goal: Goal]

   static namedQueries = {
      notDeleted {
         isNull 'deletedAt'
      }
   }

   static mapping = {
      autoTimestamp true

      table 'perception'

      definition type: 'text'
      dateCreated column: 'date_created'
      lastUpdated column: 'last_updated'
      deletedAt column: 'deleted_at'
   }

   static constraints = {
      title blank: false, size: 1..255
      definition nullable: true, blank: true, size: 1..5000
      goal nullable: false
      lastUpdated nullable: true
      deletedAt nullable: true
   }

   /**
    * before delete callback to prevent physical deletion
    *
    * @return
    */
   def beforeDelete() {
      if (deletedAt == null) {
         Perception.executeUpdate('update Perception set deletedAt = ? where id = ?', [new Timestamp(System.currentTimeMillis()), id])
      }

      return false
   }
}

Rank.groovy

class Rank {

   Perception left
   Perception right

   Integer leftRank
   Integer rightRank

   RankingSession session

   static belongsTo = [session: RankingSession]

   static mapping = {
      table 'rank'
   }

   static constraints = {
      leftRank range: 0..1, nullable: true 
      rightRank range: 0..1, nullable: true
      left nullable: false
      right nullable: false
      session nullable: false
   }
}

我的问题发生在删除(逻辑删除)上。我通过以下方式通过服务类执行删除:

GoalService.groovy

@Transactional
class GoalService {

   /**
    * Deletes goal 
    * 
    * @param goal
    * @return
    */
   def deleteGoal(Goal goal) {
      if (goal.tmpl == true) {
         throw new ValidationException("Provided object is a template!")
      }

      def perceptions = Perception.notDeleted.findAllByGoal(goal)
      for (perception in perceptions) {
         perception.delete()
      }

      goal.delete()
   }
}

我有一个用例,它在一个条件下工作,并在另一个条件下运行异常。

#1 现有目标,包含已分配的感知数量。删除按预期工作:目标和感知标记为已删除。

#2 具有感知的目标+与感知相关联的Rank对象数。 当我试图删除这样一个目标时,我得到一个例外:

    Error 2015-03-23 14:52:10,294 [http-nio-8080-exec-9] ERROR spi.SqlExceptionHelper  - Column 'left_id' cannot be null
| Error 2015-03-23 14:52:10,357 [http-nio-8080-exec-9] ERROR errors.GrailsExceptionResolver  - MySQLIntegrityConstraintViolationException occurred when processing request: [POST] /triz/rrm/goal/1/delete - parameters:
SYNCHRONIZER_TOKEN: 57fda8f2-8025-45e0-ac60-592234f54ef1
SYNCHRONIZER_URI: /triz/rrm/goals
Column 'left_id' cannot be null. Stacktrace follows:
Message: Column 'left_id' cannot be null
    Line | Method
->>  411 | handleNewInstance  in com.mysql.jdbc.Util
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|    386 | getInstance        in     ''
|   1041 | createSQLException in com.mysql.jdbc.SQLError
|   4237 | checkErrorPacket   in com.mysql.jdbc.MysqlIO
|   4169 | checkErrorPacket . in     ''
|   2617 | sendCommand        in     ''
|   2778 | sqlQueryDirect . . in     ''
|   2834 | execSQL            in com.mysql.jdbc.ConnectionImpl
|   2156 | executeInternal .  in com.mysql.jdbc.PreparedStatement
|   2441 | executeUpdate      in     ''
|   2366 | executeUpdate . .  in     ''
|   2350 | executeUpdate      in     ''
|    129 | doCall . . . . . . in triz.rrm.RrmGoalController$_delete_closure5
|    127 | delete             in triz.rrm.RrmGoalController

我已经尝试了一切,包括:

  • 以物理方式删除所有约束
  • 使用"级联:'保存更新'"关系

没有任何帮助,我唯一能理解的是它与级联相关的事实,但为什么GORM试图级联'删除'如果实际上我正在更新对象?

1 个答案:

答案 0 :(得分:0)

您收到例外是因为Rank引用了Perception,但您没有在任何一方指定belongsTo

你在Rank

中有这个
Perception left

这就是你得到Column 'left_id' cannot be null. Stacktrace follows...的原因。

因此,要解决此问题,请删除您要删除的每个rank已发送的perception个对象,或在belongsTo = [left: Perception]中指定Rank