【发布时间】:2021-03-27 17:00:06
【问题描述】:
我正在创建一个简单的看板应用程序,如下所示,每个看板由一系列阶段组成,每个阶段都有一个级别字段来定义其位置。我希望能够随意添加、移动和删除关卡,所以我必须保持每个关卡的关卡一致,足够简单。
@Entity
@Table(name = "kanbans")
data class Kanban (
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
var id: Int? = null,
@get:NotNull
@get:NotBlank
@Column(name = "name", nullable = false)
var name: String? = null,
@get:NotNull
@get:NotBlank
@Column(name = "description", nullable = false)
var description: String? = null,
@get:NotNull
@Column(name = "closed", nullable = false)
var closed: Boolean? = null,
@get:NotNull
@Column(name = "created_at", nullable = false)
var createdAt: LocalDateTime? = null,
@get:NotNull
@Column(name = "updated_at", nullable = false)
var updatedAt: LocalDateTime? = null,
)
@Entity
@Table(name = "stages")
data class Stage (
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
var id: Int? = null,
@get:NotNull
@get:NotBlank
@Column(name = "name", nullable = false)
var name: String? = null,
@get:NotNull
@get:NotBlank
@Column(name = "description", nullable = false)
var description: String? = null,
@get:NotNull
@Column(name = "closed", nullable = false)
var closed: Boolean? = null,
@get:NotNull
@Column(name = "level", nullable = false)
var level: Int? = null,
@OneToMany(fetch = FetchType.LAZY, mappedBy = "stage")
var tasks: List<Task> = ArrayList(),
@get:NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "kanban_id", nullable = false)
var kanban: Kanban? = null,
@get:NotNull
@Column(name = "created_at", nullable = false)
var createdAt: LocalDateTime? = null,
@get:NotNull
@Column(name = "updated_at", nullable = false)
var updatedAt: LocalDateTime? = null,
)
在创建第一个阶段时,它始终将其级别分配为 0,然后在添加新级别时,级别将定义阶段列表中的阶段位置。问题是,当我尝试更新以前的现有阶段以让位给新阶段时,我发现完成这项工作的唯一方法是将saveAndFlush 调用放在一个循环中,但我发现它不是一个好想法。
@Repository
interface StageRepository : JpaRepository<Stage, Int> {
fun findAllByKanbanAndLevelGreaterThanEqualOrderByLevelDesc(kanban: Kanban, level: Int): List<Stage>
@Modifying
@Transactional
@Query("UPDATE Stage s SET s.level = s.level + 1 WHERE s.kanban = :kanban AND s.level >= :level")
fun incrementLevelForKanbanStagesWhereLevelIsGreaterThan(kanban: Kanban, level: Int)
}
incrementLevelForKanbanStagesWhereLevelIsGreaterThan 方法失败,因为数据库对 level 和 kanban_id 有唯一约束,并出现以下错误:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "stages_kanban_id_level_key"
Detalhe: Key (kanban_id, level)=(337, 1) already exists.
这显然正在发生,因为它在将级别 1 更新为级别 2 之前尝试将级别 0 更新为级别 1,因此我尝试了:
@Transactional
@Query("UPDATE Stage s SET s.level = s.level + 1 WHERE s.kanban = :kanban AND s.level >= :level ORDER BY s.level DESC")
fun incrementLevelForKanbanStagesWhereLevelIsGreaterThan(kanban: Kanban, level: Int)
不编译,
@Service
@Transactional
class StageCrudService: CrudService<Stage, Int, StageRepository, StageValidationService>() {
@Throws(ValidationException::class)
override fun create(model: Stage): Stage {
prepareToCreate(model)
validationService.canSave(model)
incrementKanbanStageLevels(model)
return repository.save(model)
}
private fun prepareToCreate(model: Stage) {
val now = LocalDateTime.now()
val closed = model.closed ?: false
model.closed = closed
model.createdAt = now
model.updatedAt = now
model.level = model.level ?: 0
}
private fun incrementKanbanStageLevels(model: Stage) {
val level = model.level ?: 0
val stages = repository.findAllByKanbanAndLevelGreaterThanEqualOrderByLevelDesc(model.kanban!!, level)
stages.forEach { stage ->
stage.level = stage.level?.plus(1)
}
repository.saveAll(stages)
repository.flush()
}
}
和
private fun incrementKanbanStageLevels(model: Stage) {
val level = model.level ?: 0
val stages = repository.findAllByKanbanAndLevelGreaterThanEqualOrderByLevelDesc(model.kanban!!, level)
stages.forEach { stage ->
stage.level = stage.level?.plus(1)
repository.save(stage)
}
repository.flush()
}
但两者都以与查询相同的方式失败。现在的问题是:
有没有更好的方法来管理这种情况的更新顺序而不是这样做:
private fun incrementKanbanStageLevels(model: Stage) {
val level = model.level ?: 0
val stages = repository.findAllByKanbanAndLevelGreaterThanEqualOrderByLevelDesc(model.kanban!!, level)
stages.forEach { stage ->
stage.level = stage.level?.plus(1)
repository.saveAndFlush(stage)
}
}
【问题讨论】:
标签: spring kotlin jpa spring-data-jpa