All Downloads are FREE. Search and download functionalities are using the official Maven repository.

skinny.orm.feature.OptimisticLockWithVersionFeature.scala Maven / Gradle / Ivy

The newest version!
package skinny.orm.feature

import scalikejdbc._, SQLInterpolation._
import skinny.orm.exception.OptimisticLockException
import org.slf4j.LoggerFactory

/**
 * Optimistic lock with version.
 *
 * @tparam Entity entity
 */
trait OptimisticLockWithVersionFeature[Entity] extends CRUDFeature[Entity] {

  private[this] val logger = LoggerFactory.getLogger(classOf[OptimisticLockWithVersionFeature[Entity]])

  /**
   * Lock version field name.
   */
  val lockVersionFieldName = "lockVersion"

  // add default lockVersion value to creation query
  addAttributeForCreation(column.field(lockVersionFieldName), 1L)

  /**
   * Returns where condition part which search by primary key and lock vesion.
   *
   * @param id primary key
   * @param version lock version
   * @return query part
   */
  def updateByIdAndVersion(id: Long, version: Long) = {
    updateBy(sqls.eq(column.field(primaryKeyName), id).and.eq(column.field(lockVersionFieldName), version))
  }

  private[this] def updateByHandler(session: DBSession, where: SQLSyntax, namedValues: Seq[(SQLSyntax, Any)], count: Int): Unit = {
    if (count == 0) {
      throw new OptimisticLockException(
        s"Conflict ${lockVersionFieldName} is detected (condition: '${where.value}', ${where.parameters.mkString(",")}})")
    }
  }
  afterUpdateBy(updateByHandler _)

  override def updateBy(where: SQLSyntax): UpdateOperationBuilder = new UpdateOperationBuilderWithVersion(this, where)

  /**
   * Update query builder/executor.
   *
   * @param mapper mapper
   * @param where condition
   */
  class UpdateOperationBuilderWithVersion(mapper: CRUDFeature[Entity], where: SQLSyntax)
      extends UpdateOperationBuilder(mapper, where, beforeUpdateByHandlers, afterUpdateByHandlers) {
    // appends additional part of update query
    private[this] val c = defaultAlias.support.column.field(lockVersionFieldName)
    addUpdateSQLPart(sqls"${c} = ${c} + 1")
  }

  /**
   * Deletes a single entity by primary key and lock version.
   *
   * @param id primary key
   * @param version lock version
   * @param s db session
   * @return deleted count
   */
  def deleteByIdAndVersion(id: Long, version: Long)(implicit s: DBSession) = {
    deleteBy(sqls.eq(column.field(primaryKeyName), id).and.eq(column.field(lockVersionFieldName), version))
  }

  override def deleteBy(where: SQLSyntax)(implicit s: DBSession): Int = {
    val count = super.deleteBy(where)
    if (count == 0) {
      throw new OptimisticLockException(
        s"Conflict ${lockVersionFieldName} is detected (condition: '${where.value}', ${where.parameters.mkString(",")}})")
    } else {
      count
    }
  }

  override def updateById(id: Long): UpdateOperationBuilder = {
    logger.info("#updateById ignore optimistic lock. If you need to lock with version in this case, use #updateBy instead.")
    super.updateBy(byId(id))
  }

  override def deleteById(id: Long)(implicit s: DBSession): Int = {
    logger.info("#deleteById ignore optimistic lock. If you need to lock with version in this case, use #deleteBy instead.")
    super.deleteBy(byId(id))
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy