Scala - 玩吧! 2 - 通用 Crud

Scala - Play! 2 - Generic Crud

我正在尝试创建一个通用的 crud 控制器,但我遇到了 json 问题。

我对我的每个域对象都有一个隐式读写,因为我的 crud 控制器是 "generic",我正在为它传递 T,这导致找不到类型 T 的隐式转换器。

这是我的 TestController

object AddressController extends Controller with CrudController[Address, AddressServiceModule] {

}

这是我的 CrudController

trait CrudController[T, K <: GenericServiceModule[T]] extends Controller {

  var service: K = _

  def get(uuid: String) = Action.async {
    val future = service.genericService.getById(UUID.fromString(uuid))
    future.map {
      case Some(t) => Ok(Json.toJson(t))
      case None => NotFound
    }.recover {
      case t =>
        Logger.error("Something went wrong", t)
        InternalServerError(t.getMessage)
    }
  }
}

然后我得到了

No Json serializer found for type T.

正如我所说,我的地址模型有读写功能

implicit val addressReads = Json.reads[Address]
implicit val addressWrites = Json.writes[Address]

但由于转换器是在编译时验证的,所以我不知道如何解决这种情况。没有机会用相同的逻辑编写 10、20 个 crud 控制器。

有什么想法吗?

更新

只是为了测试,我将 Ok(Json.toJson(t)) 更改为 Ok(t.toString) 然后我遇到了另一个问题...

我的服务上有一个 NullPointer

var service: K = _

我的 GenericServiceModule 如下:

trait GenericServiceModule[T] {

  def genericService: GenericCommonService[T]

  /**
   * Common Services
   * @tparam T
   */
  trait GenericCommonService[T] {
    def getById(uuid: UUID): Future[Option[T]]
    def deleteById(uuid: UUID): Future[ResultSet]
    def insert(t: T): (UUID, Future[ResultSet])
    def update(uuid: UUID, t: T): (UUID, Future[ResultSet])
  }

}

我传递给 AddressController 的 AddressServiceModule 如下:

trait AddressServiceModule extends GenericServiceModule[Address] with CassandraService {

  object genericService extends GenericCommonService[Address] {

    override def getById(uuid: UUID): Future[Option[Address]] = {
      Address.select.where(_.id eqs uuid).one()
    }
}

有没有办法通过我的 CrudController 注入这个服务?

如何为每个伴随对象使用某种特征来要求 ReadsWrites

trait CrudObject[T] {
    val reads: Reads[T]
    val writes: Writes[T]
}

例如:

object Address extends CrudObject[Address] {
    implicit val reads: Reads[Address] = Json.reads[Address]
    implicit val writes: Writes[Address] = Json.writes[Address]

    // other code ..
}

然后在您的通用控制器中,需要引用伴随对象(由 CrudObject 特征标记)和 GenericServiceModule。这不会完全使隐式解析工作,但是一旦你有了那个引用,你就可以依靠继承来使 ReadsWrites 在控制器中可用。

trait CrudController[T] extends Controller {

  def service: GenericServiceModule[T]

  def companion: CrudObject[T]

  implicit def reads: Reads[T] = companion.reads

  implicit def writes: Reads[T] = companion.writes

  def get(uuid: String) = Action.async {
    val future = service.genericService.getById(UUID.fromString(uuid))
    future.map {
      case Some(t) => Ok(Json.toJson(t))
      case None => NotFound
    }.recover {
      case t =>
        Logger.error("Something went wrong", t)
        InternalServerError(t.getMessage)
    }
  }
}

那么您的实现可能如下所示:

object AddressController extends CrudController[Address] {

   def service = AddressServiceModule

   def companion = Address

}