2014-01-29 3 views
3

Я использую scala и slick здесь, и у меня есть baserepository, который отвечает за выполнение основной crud моих классов. Для конструктивного решения у нас есть обновленные периоды времени и времени, которые обрабатываются приложением, а не триггерами в базе данных. Оба этих поля являются экземплярами Joda DataTime. Эти поля определены в двух признаков, называемых HasUpdatedAt и HasCreatedAt, для таблицScala Reflection для обновления класса case val

trait HasCreatedAt { 
    val createdAt: Option[DateTime] 
} 

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

Я хотел бы знать, как я могу использовать отражение для вызова метода копирования пользователя, чтобы обновить значение createdAt при вставке базы данных метод.

Edit после @vptron и @ Кевин-райт комментариев

У меня есть репо, как этот

trait BaseRepo[ID, R] { 

    def insert(r: R)(implicit session: Session): ID 
    } 

Я хочу, чтобы осуществить вставку только один раз, и я хочу быть createdAt обновлено, поэтому я не использую метод копирования, иначе мне нужно реализовать его везде, где я использую столбец createdAt.

+1

Зачем вам нужно отражение? Метод 'copy' не является приватным. – vptheron

+5

НЕ ДЕЛАЙТЕ ЭТО. У вас нет гарантии, что scalac не будет делать оптимизацию при условии, что неизменяемые объекты, по сути, неизменяемы. Используйте 'copy', вот для чего это! –

+0

Спасибо за комментарии @vptheron, я просто обновил вопрос, чтобы добавить более подробные сведения, объясняющие, почему я не хочу использовать метод копирования здесь. – dirceusemighini

ответ

3

Этот вопрос был дан здесь, чтобы помочь другим с такой проблемой. В конечном итоге я использую этот код для выполнения метода копирования классов case с использованием 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 
} 

С этим я могу выполнить метод копирования классов case, заменив определенное значение поля.

2

Как указано в комментарии, не изменяйте значение val используя отражение. Вы бы это сделали с переменной переменной java? Это делает ваш код действительно неожиданным. Если вам нужно изменить значение val, не используйте val, используйте var.

trait HasCreatedAt { 
    var createdAt: Option[DateTime] = None 
} 

case class User(name:String) extends HasCreatedAt 

Хотя наличие var в классе case может привести к неожиданному поведению, например. копия не будет работать должным образом. Это может привести к предположению, что для этого не используйте класс case.

Другой подход должен был бы сделать метод вставки возвращает обновленную копию класса дела, например:

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*/)) 
    } 
} 

EDIT:

Поскольку я не ответил на свой вопрос, и вы может знать, что вы делаете. Я добавляю способ сделать это.

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*/) 

Но опять же, пожалуйста, не делайте этого. Прочтите this stackoveflow answer, чтобы получить представление о том, что он может вызвать (карта vals для окончательных полей).

+2

Привет, Мартин, спасибо за ответ, намерение этого вопроса состоит в том, чтобы знать, есть ли способ вызвать метод копирования R с использованием отражения, а не переназначить значение val.Я не нашел определение копии и как его называть, используя только аргумент, в этом случае createdAt – dirceusemighini

+0

Ах, извините, я неправильно понял вопрос. Выполнение этого кажется более сложным, поскольку оно включает вызов метода с параметрами по умолчанию. Как это делается, вы можете увидеть по адресу http://stackoverflow.com/questions/14034142/how-do-i-access-default-parameter-values-via-scala-reflection/14034802#14034802. –

Смежные вопросы