Scala反射以更新案例类Val



我在这里使用Scala和Slick,并且我有一个持久的人,负责完成我的课堂的基本CRUD。为了进行设计决策,我们确实具有更新的时间和创建时间列,所有这些都由应用程序处理,而不是由数据库中的触发器处理。这两个字段都是Joda Datatime实例。这些字段定义为两个特征,称为hasupdatedat和hasCreateDat,用于表

trait HasCreatedAt {
    val createdAt: Option[DateTime]
}
case class User(name:String,createdAt:Option[DateTime] = None) extends HasCreatedAt

我想知道如何使用反射来调用用户复制方法,以更新数据库插入方法中的创建值。

@vptron和 @kevin-wright评论

编辑

我有这样的回购

trait BaseRepo[ID, R] {
    def insert(r: R)(implicit session: Session): ID
  }

我只想仅实现一次插入,我想创建插入以进行更新,这就是为什么我不使用复制方法,否则我需要在使用CreateT列的任何地方实现它。

这个问题在这里回答是为了解决其他问题。我最终使用此代码使用Scala反射执行我的案例类的复制方法。

import reflect._
import scala.reflect.runtime.universe._
import scala.reflect.runtime._
class Empty
val mirror = universe.runtimeMirror(getClass.getClassLoader)
// paramName is the parameter that I want to replacte the value
// paramValue is the new parameter value
def updateParam[R : ClassTag](r: R, paramName: String, paramValue: Any): R = {
  val instanceMirror = mirror.reflect(r)
  val decl = instanceMirror.symbol.asType.toType
  val members = decl.members.map(method => transformMethod(method, paramName, paramValue, instanceMirror)).filter {
    case _: Empty => false
    case _ => true
  }.toArray.reverse
  val copyMethod = decl.declaration(newTermName("copy")).asMethod
  val copyMethodInstance = instanceMirror.reflectMethod(copyMethod)
  copyMethodInstance(members: _*).asInstanceOf[R]
}
def transformMethod(method: Symbol, paramName: String, paramValue: Any, instanceMirror: InstanceMirror) = {
  val term = method.asTerm
  if (term.isAccessor) {
    if (term.name.toString == paramName) {
      paramValue
    } else instanceMirror.reflectField(term).get
  } else new Empty
}

使用此方法,我可以执行案例类的复制方法,替换确定的字段值。

正如评论所说的,不要使用反射更改val。您会使用Java最终变量吗?它使您的代码确实确实出乎意料。如果您需要更改val的值,请不要使用val,请使用var。

trait HasCreatedAt {
    var createdAt: Option[DateTime] = None
}
case class User(name:String) extends HasCreatedAt

尽管在案例类中具有VAR可能会带来一些意外的行为,例如副本无法正常工作。这可能会导致不使用案例类。

另一种方法是使插入方法返回案例类的更新副本,例如:

trait HasCreatedAt {
    val createdAt: Option[DateTime]
    def withCreatedAt(dt:DateTime):this.type
}
case class User(name:String,createdAt:Option[DateTime] = None) extends HasCreatedAt {
    def withCreatedAt(dt:DateTime) = this.copy(createdAt = Some(dt))
}
trait BaseRepo[ID, R <: HasCreatedAt] {
    def insert(r: R)(implicit session: Session): (ID, R) = {
        val id = ???//insert into db
        (id, r.withCreatedAt(??? /*now*/))
    }
}

编辑:

由于我没有回答您的原始问题,您可能知道您在做什么,我正在添加一种方法。

import scala.reflect.runtime.universe._
val user = User("aaa", None)
val m = runtimeMirror(getClass.getClassLoader)
val im = m.reflect(user)
val decl = im.symbol.asType.toType.declaration("createdAt":TermName).asTerm
val fm = im.reflectField(decl)
fm.set(??? /*now*/)

但是,请不要这样做。阅读此stackoveflow答案,以了解可能导致的内容(Vals映射到最终字段)。

最新更新