【问题标题】:Scala, Play - Problems with json type conversions when trying to implement a repositoryScala,Play - 尝试实现存储库时出现 json 类型转换的问题
【发布时间】:2013-09-27 11:02:21
【问题描述】:

我是 Scala 新手,第一次尝试使用 Scala 和 Play 2.1 创建 web 应用程序。

我正在尝试使用响应式 Mongo 和响应式 Mongo Play 插件创建一个 UserRepository 类,该类将作为用户对象到 MongoDB 的存储库。

这是我不明白的事情。考虑这个存储库类:

@Singleton
class UserRepository @Inject() (db: DB) {
    import scala.concurrent.ExecutionContext.Implicits.global

    val collection:JSONCollection = db.collection("users")

    def find(terms: (String, JsValueWrapper)*): Future[List[User]] = {
      find(Json.obj(terms:_*))
    }

    //Problematic method that sometimes works sometimes not
    def findOne(terms: (String, JsValueWrapper)*): Future[Option[User]] = {
       findOne(Json.obj(terms:_*))
    }

    def find(selector: JsObject): Future[List[User]] = {
       collection.find(selector).cursor[User].toList()
    }

    def findOne(selector: JsObject): Future[Option[User]] = {
       collection.find(selector).one[User]
    }

    def findByEmailAndPassword(email: String, password: String): Future[Option[User]] = {
       //problematic method works fine here
       findOne("email" -> email, "password" -> password)
    }

} 

我想创建一个使用可变参数元组来描述 Mongo 选择器对象的 find 方法。 我在存储库类本身的 findByEmailAndPassword 方法中使用它,它工作正常。

更新:

如果我只提供一个元组(无论从哪里调用),调用 findOne 就不能以键 -> 值的形式工作。 这有效:

findOne(("email", "example@test.com"))
findOne("email" -> "example@test.com", "password" -> "foo")
val term: (String, JsValueWrapper) = "email" ->  "example@test.com"
findOne(term)

这不起作用:

findOne("email" -> "example@test.com")

为什么?

更新 2:

我已经将方法更新为如下所示(因此我可以在存储库类之外提供写入和读取对象):

def findOne[V](terms: (String, V)*)(implicit reads: Reads[T], writes: Writes[V]): Future[Option[T]] = {
  val objMap = terms.map(entry => (entry._1, Json.toJsFieldJsValueWrapper(entry._2)))
  findOne(Json.obj(objMap:_*))
}

本次更新后,不再出现错误。虽然我仍然不明白为什么它一开始就不起作用。

原始问题:

但是,当我在这样的控制器中使用存储库时:

@Singleton
class TestController @Inject() (val userRepository: UserRepository) extends Controller {

  def testFind = Action { implicit request =>
    Async {
      //This call to findOne doesn't work
      val query = userRepository.findOne("email" -> "test@example.com")

      //This call works fine:
      //val query = userRepository.findOne(Json.obj("email" -> "test@example.com"))
      query.map(
        userOption => userOption.map(u => Ok("Found user: " + u.toString)).getOrElse(Ok("No user found.")))
      }
    }
}

当我在 Controller 类中调用 userRepository.findOne("email" -> "test@example.com") 时,它不起作用。但是当我在存储库类中对相同的方法进行类似的调用时它确实如此 - 它工作正常(例如 findByEmailAndPassword 工作正常)。从控制器调用时我得到的错误是:

Overloaded method value [findOne] cannot be applied to ((String, String))

我认为这是隐式转换问题,但我在控制器类中确实有与存储库类中相同的所有导入,但在控制器中使用方法时,String 不会转换为 JsValueWrapper。

任何帮助将不胜感激,也欢迎声明 findOne(terms*) 方法的替代解决方案,谢谢。

【问题讨论】:

  • 为什么不直接使用object UserRepository 而不是@Singleton 注解?
  • 我使用 Guice 作为依赖注入来实例化控制器类并填充依赖项,例如存储库 (eng.42go.com/play-framework-dependency-injection-guice)。原因是我不喜欢静态对象与其他对象的硬耦合。以后替换实现更容易/更好(例如,将 mongo 存储库换成其他一些 NoSql 存储库)并且在测试中更容易模拟。但也可能是因为我来自 Java 世界,我还没有弄清楚如何使用 Scala 方式进行依赖注入(例如,蛋糕模式目前对我来说有点太难了)。
  • @KresimirNesek 这是使用默认参数的simplified Scala "DI" technique。无论您如何看待这种技术,如果您放弃 Java 风格的编码并学习编写惯用的 Scala 代码,您将免于痛苦。看看 Play 是如何编写的,以及由真正了解 Scala 的人(Spores、Akka、Scalatra)编写的其他 Scala 框架。我相信如果你思考和编码的功能更多,DI 就没那么有用了(我认为,因为无论如何 DI 感觉它是一种功能范式。)

标签: json scala playframework playframework-2.1


【解决方案1】:

也许您应该另外定义另一个 findOne(term: (String, JsValueWrapper)) 内部委托给 findOne(terms: (String, JsValueWrapper)*) 的方法?

【讨论】:

  • 感谢您的回答。我确实设法让方法像我期望的那样工作(我在更新 2 中添加了描述)。虽然您的建议可能有效,但真正的问题是为什么它不像我那样有效。
猜你喜欢
  • 1970-01-01
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
  • 2017-01-10
相关资源
最近更新 更多