-
Notifications
You must be signed in to change notification settings - Fork 106
SalatWithPlay2
Disclaimer: I don't know anything about the Play framework! But I can try to help you if Salat doesn't work with Play.
Thanks to Leon Radley for this cool Play plugin!
I can't thank members of the Play community enough for their help in making Salat work with Play framework.
- courtesy of Bjarte Karlsen, bjartek/computer-database-mongo/
- courtesy of Alexandre Bedritski, Mironor/Play-2.0-Scala-MongoDb-Salat-exemple
And thanks again to Aaron White for his help with Play 1.2. (That page is now outdated, but is available in the wiki history if you are interested.)
Salat provides a ModelCompanion
trait to provide easy access to serialization and DAO methods from your model companion
object.
- convenience methods to convert a model object
- convenience methods for CRUD operations
- insert
- save
- update
- remove
- convenience methods for querying
- typed methods for finding using an ID or a list of IDs
- count using query criteria or just specifying fields that must be present or absent in your documents
-
findAll()
returns anIterator
typed to your model object
Create a companion object to your model object, and extend ModelCompanion[ObjectType, ID]
.
ModelCompanion[ObjectType, ID]
defines def dao: DAO[ObjectType, ID]
.
DAO[ObjectType, ID]
is a top-level DAO interface. You will probably want to supply an instance of SalatDAO[ObjectType, ID]
,
but as long as you implement DAO[ObjectType, ID]
you can supply whatever you want. For instance, you might choose
to extend SalatDAO[ObjectType, ID]
to create MyCustomDAO[ObjectType, ID]
.
It is your choice to supply the dao
instance as a def
, a val
, or a lazy val
in your model companion.
In the example below, we are using a Context
provided by Salat. If you want to use your own custom context, replace
import com.novus.salat.global._
with an import for your own context.
package model
import com.novus.salat.global._
import com.novus.salat.annotations._
import com.mongodb.casbah.Imports._
import org.scala_tools.time.Imports._
import com.novus.salat.dao.{ SalatDAO, ModelCompanion }
object MyModel extends ModelCompanion[MyModel, ObjectId] {
val collection = MongoConnection()("my_db")("my_model_coll")
val dao = new SalatDAO[MyModel, ObjectId](collection = collection) {}
}
case class MyModel(@Key("_id") id: ObjectId,
x: String,
y: Int,
z: List[Double],
d: DateTime)
See ModelCompanionSpec
.
val _id = new ObjectId
val d = new DateTime
val x = "Test"
val y = 99
val z = 1d :: 2d :: 3d :: Nil
val m = MyModel(id = _id, x = x, y = y, z = z, d = d)
scala> val m = MyModel(id = _id, x = x, y = y, z = z, d = d)
m: model.MyModel = MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00)
scala> val dbo = MyModel.toDBObject(m)
dbo: com.mongodb.casbah.Imports.DBObject = { "_typeHint" : "model.MyModel" , "_id" : { "$oid" : "4f8ce7f94cea76aefbcd1b6e"} , "x" : "Test" , "y" : 99 , "z" : [ 1.0 , 2.0 , 3.0] , "d" : { "$date" : "2012-04-17T03:48:09Z"}}
scala> MyModel.toObject(dbo)
res2: model.MyModel = MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00)
scala> MyModel.toPrettyJson(m)
res3: String =
{
"_typeHint":"model.MyModel",
"_id":{
"$oid":"4f8ce7f94cea76aefbcd1b6e"
},
"x":"Test",
"y":99,
"z":[1.0,2.0,3.0],
"d":"2012-04-17T03:48:09Z"
}
scala> MyModel.toCompactJson(m)
res4: String = {"_typeHint":"model.MyModel","_id":{"$oid":"4f8ce7f94cea76aefbcd1b6e"},"x":"Test","y":99,"z":[1.0,2.0,3.0],"d":"2012-04-17T03:48:09Z"}
From model object to lift-json JObject
scala> MyModel.toJson(m)
res5: net.liftweb.json.JsonAST.JObject = JObject(List(JField(_typeHint,JString(model.MyModel)), JField(_id,JObject(List(JField($oid,JString(4f8ce7f94cea76aefbcd1b6e))))), JField(x,JString(Test)), JField(y,JInt(99)), JField(z,JArray(List(JDouble(1.0), JDouble(2.0), JDouble(3.0)))), JField(d,JString(2012-04-17T03:48:09Z))))
scala> val s = """{
| "_typeHint":"model.MyModel",
| "_id":{
| "$oid":"4f8ce7f94cea76aefbcd1b6e"
| },
| "x":"Test",
| "y":99,
| "z":[1.0,2.0,3.0],
| "d":"2012-04-17T03:48:09Z"
| }"""
s: java.lang.String =
{
"_typeHint":"model.MyModel",
"_id":{
"$oid":"4f8ce7f94cea76aefbcd1b6e"
},
"x":"Test",
"y":99,
"z":[1.0,2.0,3.0],
"d":"2012-04-17T03:48:09Z"
}
scala> MyModel.fromJSON(s)
res0: model.MyModel = MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-17T03:48:09.000Z)
For examples of using a lift-json JObject
, refer to ModelCompanionSpec
.
scala> MyModel.toMap(m)
res1: Map[String,Any] = Map((x,Test), (_typeHint,com.novus.salat.test.dao.MyModel), (y,99), (_id,4f8ce7f94cea76aefbcd1b6e), (z,List(1.0, 2.0, 3.0)), (d,2012-04-16T23:47:09.640-04:00))
scala> val m = Map("_id" -> _id, "x" -> x, "y" -> y, "z" -> z, "d" -> d)
m: scala.collection.immutable.Map[java.lang.String,Any] = Map((x,Test), (y,99), (_id,4f8ce7f94cea76aefbcd1b6e), (z,List(1.0, 2.0, 3.0)), (d,2012-04-16T23:47:09.640-04:00))
scala> MyModel.fromMap(m)
res2: model.MyModel = MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:47:09.640-04:00)
scala> val someId = MyModel.insert(m)
someId: Option[com.mongodb.casbah.Imports.ObjectId] = Some(4f8ce7f94cea76aefbcd1b6e)
scala> someId == Some(_id)
res6: Boolean = true
scala> MyModel.save(m.copy(z = 0d :: z))
scala> MyModel.findOneByID(_id)
res8: Option[model.MyModel] = Some(MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(0.0, 1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00))
scala> MyModel.update(q = MongoDBObject("_id" -> _id),
| o = MongoDBObject("$set" -> MongoDBObject("x" -> "Test2")),
| upsert = false, multi = false, wc = MyModel.dao.collection.writeConcern)
scala> MyModel.findOneByID(_id)
res10: Option[model.MyModel] = Some(MyModel(4f8ce7f94cea76aefbcd1b6e,Test2,99,List(0.0, 1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00))
scala> MyModel.insert(m)
res17: Option[com.mongodb.casbah.Imports.ObjectId] = Some(4f8ce7f94cea76aefbcd1b6e)
scala> MyModel.findOneByID(_id)
res18: Option[model.MyModel] = Some(MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00))
scala> MyModel.remove(m)
scala> MyModel.findOneByID(_id)
res20: Option[model.MyModel] = None
scala> MyModel.count(MongoDBObject("x" -> "Test"))
res3: Long = 1
SalatMongoCursor[ObjectType]
is a MongoDB cursor typed to ObjectType
. You can convert it to an iterator, a list or
any other type of collection.
The only difference between SalatMongoCursor
and MongoCursor
is that SalatMongoCursor
takes care of deserialization boilerplate so you don't have to.
scala> MyModel.find(MongoDBObject("x" -> "Test"))
res5: com.novus.salat.dao.SalatMongoCursor[model.MyModel] = non-empty iterator
scala> MyModel.find(MongoDBObject("x" -> "Test")).toList
res6: List[model.MyModel] = List(MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00))
scala> MyModel.findOne(MongoDBObject("x" -> "Test"))
res10: Option[model.MyModel] = Some(MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00))
scala> MyModel.findOneByID(_id)
res4: Option[model.MyModel] = Some(MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00))
scala> MyModel.ids(MongoDBObject("x" -> "Test"))
res11: List[com.mongodb.casbah.Imports.ObjectId] = List(4f8ce7f94cea76aefbcd1b6e)
-
primitiveProjection[P]
brings backOption[P]
whereP
is a type whose deserialization is handled by the mongo-java-driver or Casbah. Examples includeInt
,Double
,String
, andDateTime
. -
projection[P]
brings backOption[P]
whereP
is a case class, trait or abstract superclass whose serialization is handled by Salat.
scala> MyModel.primitiveProjection[Int](MongoDBObject("x" -> "Test"), "y")
res12: Option[Int] = Some(99)
-
primitiveProjections[P]
brings backList[P]
whereP
is a type whose deserialization is handled by the mongo-java-driver or Casbah. Examples includeInt
,Double
,String
, andDateTime
. -
projections[P]
brings backList[P]
whereP
is a case class, trait or abstract superclass whose serialization is handled by Salat.
scala> MyModel.primitiveProjections[Int](MongoDBObject("x" -> "Test"), "y")
res13: List[Int] = List(99)
findAll()
returns Iterator[ObjectType]
by default.
scala> MyModel.findAll()
res14: Iterator[model.MyModel] = non-empty iterator
scala> MyModel.findAll().toList // probably a bad idea!
res15: List[model.MyModel] = List(MyModel(4f8ce7f94cea76aefbcd1b6e,Test,99,List(1.0, 2.0, 3.0),2012-04-16T23:48:09.640-04:00))
- first, restart
- second, clean and then restart
- third, try using
ModelCompanion
- as a last resort, you can brute force the issue by adding a call to
Context#clearAllGraters()
inGlobalSettings#onStart
Make sure that all your DAOs are declared as val
in ModelCompanion
implementations.
If you encounter runtime exceptions like this, try creating a custom Salat context:
IncompatibleClassChangeError: Class myApp.models.Event does not implement the requested interface myApp.models.Endable
First, create a package object with your custom Salat context, and import that everywhere instead.
import com.novus.salat._
import play.api._
import play.api.Play.current
package object myApp {
implicit val ctx = {
val c = new Context() {
val name = "Custom Context"
}
c.registerClassLoader(Play.classloader)
c
}
}
Then make sure to remove everywhere you have imported the out-of-box default Salat context:
import com.novus.salat.global._
and replace it with an import of your custom context:
import myApp._
Thanks to ornicar for contributing this tip to the wiki!