scala - 具有依赖注入的数据库操作的可重用代码
问题描述
我有多个参与者管理写入 mongo db 的数据模型。
object LevelManager {
val collectionName = "levels"
}
@Singleton
class LevelManager @Inject()(
val reactiveMongoApi: ReactiveMongoApi) extends Actor with ActorLogging with InjectedActorSupport {
def collection: Future[JSONCollection] = reactiveMongoApi.database.map(_.collection[JSONCollection](LevelManager.collectionName))
override def receive: Receive = {
case msg:GetById =>
var level = collection.flatMap(c => c.find(Json.obj("_id" -> msg.id), Option.empty[JsObject]).one[LevelModel].map {
result =>
logger.info( result )
}
}
}
这工作得很好,但是这个数据库代码被用于每个演员,我没有设法只拥有它一次。我不确定这是否也是一个聪明的方法。它源自没有依赖注入的旧 scala 时代,所有东西都放在对象特征中。
所以我正在寻找具有基本 db io 处理的特征或其他东西
编辑:在依赖注入之前,我能够使用这样的特征:
trait BaseModel[T] {
val collectionName: String
val db = ReactiveMongoPlugin.db
def load(id: Long)(implicit fmt: Format[T]) = {
val coll = db.collection[JSONCollection](collectionName)
coll.find(Json.obj("_id" -> id)).one[T]
}
def loadAll()(implicit fmt: Format[T]) = {
val coll = db.collection[JSONCollection](collectionName)
coll.find(Json.obj()).cursor[T].collect[Vector]()
}
def save(id: Long, model: T)(implicit fmt: Format[T]) = {
val coll = db.collection[JSONCollection](collectionName)
val doc = Json.toJson(model).as[JsObject] + ("_id" -> Json.toJson(id))
coll.save(doc).map { lastError =>
if (!lastError.ok) Logger.error(lastError.message)
lastError.ok
}
}
解决方案
最后我用def collection: Future[JSONCollection]创建了一个特征,现在我可以访问我最喜欢的 db 函数了。这是我的目标,让生活变得更好。但是,如果这有任何缺点,我对最近的反馈感到不安。
trait DBStuff[T] {
def collection: Future[JSONCollection]
def log: LoggingAdapter
def save(id: String, model: T)(implicit fmt: Format[T]) = {
val doc:JsObject = Json.toJson(model).as[JsObject] + ("_id" -> Json.toJson(id))
collection.flatMap(_.update.one(Json.obj("_id" -> id), doc, true)).map(lastError => if (!lastError.ok) log.warning(s"Mongo LastError: %s".format(lastError)))
}
def loadOne(id: String)(implicit fmt: Format[T]): Future[Option[T]] = loadOne( Json.obj("_id" -> id) )
def loadOne(obj: JsObject, projection:Option[JsObject] = None )(implicit fmt: Format[T]): Future[Option[T]] = {
collection.flatMap(_.find( obj, projection).one[T].map {
result =>
result
}.recover {
case err => log.error(s"DB Loading Error: $err")
None
})
}
def loadAll()(implicit fmt: Format[T]):Future[Vector[T]] = {
loadAll(Json.obj(), None )
}
def loadAll( obj: JsObject, projection:Option[JsObject] = None)(implicit fmt: Format[T]):Future[Vector[T]] = {
collection.flatMap(_.find(obj, projection ).cursor[T]().collect[Vector](Int.MaxValue, Cursor.FailOnError()).map {
result => result
}.recover {
case err =>
log.error(s"DB Loading Error: $err")
Vector.empty
})
}
...
}
@Singleton
class TaskManager @Inject()(
val reactiveMongoApi: ReactiveMongoApi
) extends Actor with ActorLogging with InjectedActorSupport with DBStuff[TaskModel] {
def collection: Future[JSONCollection] = reactiveMongoApi.database.map(_.collection[JSONCollection](TaskManager.collectionName))
override def preStart() = {
loadAll() map {
result =>
//What ever
}
}
推荐阅读
- django - Dockerizing由nginx服务的多节点python django后端
- elasticsearch - 无法根据查询和文档标记化从弹性搜索中获得正确的结果
- c++ - msvc内联静态成员变量重定义
- c++ - 无锁单生产者/单消费者循环缓冲区
- javascript - 从一页链接到第二页的下拉菜单
- python-3.x - 如何检查字符串是否存在于非字母数字列中?
- firebase - firestore 生成两个相同的随机密钥的机会是多少?
- linq - 使用 Entity Framework Core 根据字符串值及其所有子项获取父项
- here-api - 这些限制通知的含义是什么
- python - 计算 DataFrame 中标记化项目的单词