【问题标题】:MongoDB-Scala-Play: Write/Read composite IdMongoDB-Scala-Play:写入/读取复合 ID
【发布时间】:2013-10-10 13:51:45
【问题描述】:

我刚刚将我的应用程序移植到Play 2.2 并修改了一些代码以使用最新版本的SecureSocial - 我看到案例类UserId 已重命名为IdentityId。嗯,我可以注册一个新用户了……数据成功保存到MongoDB:

> db.identities.find()
{ "_id" : ObjectId("5256a6ffe4b000f9ba0b6ce5"), "identityId" : { "userId" : "joey", "providerId" : "userpass" }, "firstName" : "Joey", "lastName" : "Smith", "fullName" : "Joey Smith", "email" : "joey@mydomain.com", "authMethod" : { "method" : "userPassword" }, "passwordInfo" : { "hasher" : "bcrypt", "password" : "$2a$10$vDr02XZ5dAHmJHDexbBnROWejhtnYFufPJtHPr8IT.rqsCSAAu5Ju" } }

...但是一旦我尝试登录 - 当然这意味着从 MongoDb 读取数据,玩具不再工作。我总是收到NoSuchElementException,因为数据不是从数据库中读取的。

下面是我的完整代码:

import com.mongodb.casbah.Imports._
import com.novus.salat._
import com.novus.salat.annotations._
import com.novus.salat.dao._
import play.api.Play.current
import play.api.libs.json._
import play.api.libs.functional.syntax._
import securesocial.core._
import se.radley.plugin.salat._
import se.radley.plugin.salat.Binders._
import mongodbContext._

/**
 * Defines an account identity to be used by [[securesocial.core.SecureSocial]]
 * when signing up or signing in. @see securesocial.core.Identity.
 */
case class AccountIdentity(
  identityId: IdentityId,
  firstName: String,
  lastName: String,
  fullName: String,
  email: Option[String],
  avatarUrl: Option[String],
  authMethod: AuthenticationMethod,
  oAuth1Info: Option[OAuth1Info] = None,
  oAuth2Info: Option[OAuth2Info] = None,
  passwordInfo: Option[PasswordInfo] = None
) extends Identity

/**
 * Provides database access and serialization for [[AccountIdentity]] data.
 */
object AccountIdentity extends ((
  IdentityId,
  String,
  String,
  String,
  Option[String],
  Option[String],
  AuthenticationMethod,
  Option[OAuth1Info],
  Option[OAuth2Info],
  Option[PasswordInfo]) => AccountIdentity) with AccountIdentityDAO with SocialUserJson {

  /**
   * Returns an `AccountIdentity` initialized with the specified identity.
   *
   * @param     identity The identity that Initializes the `AccountIdentity`.
   */
  def apply(identity: Identity) : AccountIdentity = {
    AccountIdentity(
      identity.identityId,
      identity.firstName,
      identity.lastName,
      identity.fullName,
      identity.email,
      identity.avatarUrl,
      identity.authMethod,
      identity.oAuth1Info,
      identity.oAuth2Info,
      identity.passwordInfo
    )
  }
}

/**
 * Provides database access for [[AccountIdentity]] data.
 */
trait AccountIdentityDAO extends ModelCompanion[AccountIdentity, IdentityId] {

  private def collection = mongoCollection("identities")
  val dao = new SalatDAO[AccountIdentity, IdentityId](collection) {}

  collection.ensureIndex(DBObject("email" -> 1, "_id.providerId" -> 1), "emailProvider", unique = true)

  /**
   * Finds the [[AccountIdentity]] identified by the specified identity id.
   *
   * @param     identityId The identity id.
   * @return    An `Option` value containing the `AccountIdentity`, or `None`
   *            if there is no `AccountIdentity` identified by `identityId`.
   */
  def find(identityId: IdentityId): Option[AccountIdentity] = {
    dao.findOne(DBObject("_id.userId" -> identityId.userId, "_id.providerId" -> identityId.providerId))
  }

   /**
   * Finds the [[AccountIdentity]] identified by the specified email and provider id.
   *
   * @param     email The user email.
   * @param     providerId The provider id.
   * @return    An `Option` value containing the `AccountIdentity`, or `None` if
   *            there is no `AccountIdentity` associated with `email` and `providerId`.
   */
  def findByEmailAndProvider(email: String, providerId: String): Option[AccountIdentity] = {
    dao.findOne(DBObject("email" -> email, "_id.providerId" -> providerId))
  }
}

/**
 * Provides functionality for serializing/deserializing [[AccountIdentity]]
 * data to/from JSON.
 */
trait SocialUserJson {

  import IdentityId._
  import AuthenticationMethod._
  import OAuth1Info._
  import OAuth2Info._
  import PasswordInfo._

  /**
   * An [[AccountIdentity]] serialized to JSON.
   */
  implicit val identityJsonWrite = new Writes[AccountIdentity] {
    def writes(identity: AccountIdentity): JsValue = {
      Json.obj(
        "identityId" -> identity.identityId,
        "firstName" -> identity.firstName,
        "lastName" -> identity.lastName,
        "fullName" -> identity.fullName,
        "email" -> identity.email,
        "avatarUrl" -> identity.avatarUrl,
        "authMethod" -> identity.authMethod,
        "oAuth1Info" -> identity.oAuth1Info,
        "oAuth2Info" -> identity.oAuth2Info,
        "passwordInfo" -> identity.passwordInfo
      )
    }
  }

  /**
   * An [[AccountIdentity]] deserialized from JSON.
   */
  implicit val identityJsonRead = (
    (__ \ 'identityId).read[IdentityId] ~
    (__ \ 'firstName).read[String] ~
    (__ \ 'lasttName).read[String] ~
    (__ \ 'fullName).read[String] ~
    (__ \ 'email).readNullable[String] ~
    (__ \ 'avatarUrl).readNullable[String] ~
    (__ \ 'authMethod).read[AuthenticationMethod] ~
    (__ \ 'oAuth1Info).readNullable[OAuth1Info] ~
    (__ \ 'oAuth2Info).readNullable[OAuth2Info] ~
    (__ \ 'passwordInfo).readNullable[PasswordInfo]
  )(AccountIdentity)
}

如您所见,我正在使用复合键...所以也许我在方法 findfindByEmailAndProvider 中做错了什么。

EDIT1: ...当然我还为IdentityId实现了序列化/反序列化:

import play.api.libs.json._
import play.api.libs.functional.syntax._

/**
 * Provides functionality for serializing/deserializing [[securesocial.core.IdentityId]]
 * data to/from JSON.
 */
object IdentityId {

  /**
   * An `IdentityId` serialized to JSON.
   */
  implicit val identityIdJsonWrite = new Writes[securesocial.core.IdentityId] {
    def writes(identityId: securesocial.core.IdentityId): JsValue = {
      Json.obj(
        "userId" -> identityId.userId,
        "providerId" -> identityId.providerId
      )
    }
  }

  /**
   * An `IdentityId` deserialized from JSON.
   */
  implicit val identityIdJsonRead = (
    (__ \ 'userId).read[String] ~
    (__ \ 'providerId).read[String]
  )(securesocial.core.IdentityId.apply _)
}

【问题讨论】:

    标签: mongodb scala playframework securesocial salat


    【解决方案1】:

    找到...我只需要将identityId 重新映射到_id

    import com.novus.salat._
    import com.mongodb.casbah.Imports._
    
    import play.api.Play
    import play.api.Play.current
    
    /**
     * Defines a custom Salat context for MongoDB.
     */
    package object mongodbContext {
    
      /**
       * The custom context that globally overrides the Salat defaults.
       */
      implicit val context = {
        val context = new Context {
          val name = "global"
          override val typeHintStrategy = StringTypeHintStrategy(when = TypeHintFrequency.WhenNecessary, typeHint = "_t")
        }
    
        context.registerGlobalKeyOverride(remapThis = "identityId", toThisInstead = "_id")
        context.registerClassLoader(Play.classloader)
        context
      }
    }
    

    ...最终数据库中的数据是正确的:

    > db.identities.find()
    { "_id" : { "userId" : "joey", "providerId" : "userpass" }, "firstName" : "Joey", "lastName" : "Smith", "fullName" : "Joey Smith", "email" : "joey@mydomain.com", "authMethod" : { "method" : "userPassword" }, "passwordInfo" : { "hasher" : "bcrypt", "password" : "$2a$10$lWxiZz3XJcj3ckOOTTgleOo3tBy2zZvO.LfX9jeUtatHENcTHQ.hS" } }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-20
      • 2017-08-21
      • 1970-01-01
      相关资源
      最近更新 更多