Play-Mongo Getting StartedModel and Collection常用操作 简洁易用的 Play Framework MongoDB 模块

程序名称:Play-Mongo Getting StartedModel and Collection常用操作

授权协议: Apache

操作系统: 跨平台

开发语言: Scala

Play-Mongo Getting StartedModel and Collection常用操作 介绍

play-mongo 介绍

play-mongo 是一个专门为 Play Framework 开发的 Mongodb
模块, 旨在为 Play Framework 提供一种简洁的 Mongodb 访问方式。 该项目基于 Mongodb 官方的 Mongodb Scala
Driver
开发,并且提供了更多实用功能,例如,

  • 更简洁多样的数据库交方式

  • 自动识别模型类(Model),自动编解码

  • 自动完成 JsValue 和 BsonValue 互转

  • 更方便的 GridFS 交互

  • Change Stream 转 Akka Stream.

  • 支持关联查询(Relationship Query)

Getting Started

打开build.sbt,添加如下依赖,

libraryDependencies += "cn.playscala" % "play-mongo_2.12" % "0.1.0"

打开 conf/application.conf, 添加数据库连接,

mongodb.uri = "mongodb://user:password@host:port/play-community?authMode=scram-sha1"

然后需要配置 Model 位置, 配置代码需要在应用启动之前执行,

Mongo.setModelsPackage("models")

建议将上述代码放置在顶层包路径下的默认 Module 类中,

class Module extends AbstractModule {
  override def configure() = {
    Mongo.setModelsPackage("models")
    bind(classOf[InitializeService]).asEagerSingleton
  }
}

至此便可以将 Mongo 实例注入到任意需要的地方,

@Singleton
class Application @Inject()(cc: ControllerComponents, mongo: Mongo) extends AbstractController(cc) {}

Model and Collection

Model 类使用 @Entity 注解标注, 一个 model 实例表示 mongodb collection 中的一个文档, 一个 mongodb
collection 在概念上类似于关系数据库的一张表。

@Entity("common-user")
case class User(_id: String, name: String, password: String, addTime: Instant)

@Entity 注解参数用于指定关联的 mongodb collection 名称, 如果未指定,则默认为 Model 类名称。 作为约定,Model
类使用 _id 字段作为唯一标识, 该字段同时也是 mongodb collection 的默认主键。

我们可以通过两种方式访问 mongodb collection, 第一种方式是使用 model 类,

mongo.find[User]().list().map{ users => ... }

这里的参数类型 User 不仅用于指定关联的 mongodb collection, 而且用于指明返回的结果类型。 这意味着查询操作将会在
common-user collection 上执行, 并且返回的结果类型是 User。 需要注意的是,在该方式下无法改变返回的结果类型。

第二种方式是使用 mongo.getCollection 方法,

mongo.collection("common-user").find[User]().list().map{ user => }

在这里, find 方法上的参数类型 User 仅仅用于指定返回的结果类型, 我们可以通过更改该参数类型设置不同的返回结果类型,

mongo.collection("common-user").find[JsObject]().list().map{ jsObjList => }
mongo.collection("common-user").find[CommonUser](Json.obj("userType" -> "common")).list().map{ commonUsers => }

当然,我们也可以使用 model 类指定关联的 mongodb collection,

mongo.collection[User].find[User]().list().map{ user => }

第1个参数类型 User 用于指定关联的 mongodb collection, 第2个参数类型 User 用于指定返回的结果类型。
我们仍然可以通过改变第2个参数类型从而改变返回的结果类型。

常用操作

以下示例代码默认执行了 import play.api.libs.json.Json._ 导入, 所以 Json.obj() 可以被简写为
obj() 。

// 插入 Model
mongo.insert[User](User("0", "joymufeng", "123456", Instant.now))

// 插入 Json
val jsObj = obj("_id" -> "0", "name" -> "joymufeng", "password" -> "123456", "addTime" -> Instant.now)
mongo.collection[User].insert(jsObj)
mongo.collection("common-user").insert(jsObj)
mongo.updateById[User]("0", obj("$set" -> obj("password" -> "123321")))
mongo.updateOne[User](obj("_id" -> "0"), obj("$set" -> obj("password" -> "123321")))

mongo.collection[User].updateById("0", obj("$set" -> obj("password" -> "123321")))
mongo.collection[User].updateOne(obj("_id" -> "0"), obj("$set" -> obj("password" -> "123321")))

mongo.collection("common-user").updateById("0", obj("$set" -> obj("password" -> "123321")))
mongo.collection("common-user").updateOne(obj("_id" -> "0"), obj("$set" -> obj("password" -> "123321")))
mongo.findById[User]("0") // Future[Option[User]]
mongo.find[User](obj("_id" -> "0")).first // Future[Option[User]]

mongo.collection[User].findById[User]("0") // Future[Option[User]]
mongo.collection[User].find[User](obj("_id" -> "0")).first // Future[Option[User]]

mongo.collection[User].findById[JsObject]("0") // Future[Option[JsObject]]
mongo.collection[User].find[JsObject](obj("_id" -> "0")).first // Future[Option[JsObject]]

mongo.collection("common-user").findById[User]("0") // Future[Option[User]]
mongo.collection("common-user").find[User](obj("_id" -> "0")).first // Future[Option[User]]

mongo.collection("common-user").findById[JsObject]("0") // Future[Option[JsObject]]
mongo.collection("common-user").find[JsObject](obj("_id" -> "0")).first // Future[Option[JsObject]]
mongo.deleteById[User]("0")
mongo.deleteOne[User](obj("_id" -> "0"))

mongo.collection[User].deleteById("0")
mongo.collection[User].deleteOne(obj("_id" -> "0"))

mongo.collection("common-user").deleteById("0")
mongo.collection("common-user").deleteOne(obj("_id" -> "0"))
// Upload and get the fileId
mongo.gridFSBucket.uploadFromFile("image.jpg", "image/jpg", new File("./image.jpg")).map{ fileId =>
  Ok(fileId)
}

// Download file by fileId
mongo.gridFSBucket.findById("5b1183fed3ba643a3826325f").map{
  case Some(file) =>
    Ok.chunked(file.stream.toSource)
      .as(file.getContentType)
  case None =>
    NotFound
}

我们可以通过 toSource 方法将 Change Stream 转换成 Akka Source,之后便会有趣很多。例如下面的代码拥有如下几个功能:

  • 将从 Change Stream 接收到的元素进行缓冲,以方便批处理,当满足其中一个条件时便结束缓冲向后传递:

    • 缓冲满10个元素

    • 缓冲时间超过了1000毫秒

  • 对缓冲后的元素进行流控,每秒只允许通过1个元素

    mongo
      .collection[User]
      .watch()
      .fullDocument
      .toSource
      .groupedWithin(10, 1000.millis)
      .throttle(elements = 1, per = 1.second, maximumBurst = 1, ThrottleMode.shaping)
      .runForeach{ seq => 
        // …
      }

@Entity("common-article")
case class Article(_id: String, title: String, content: String, authorId: String)

@Entity("common-author")
case class Author(_id: String, name: String)

mongo.find[Article].fetch[Author]("authorId").list().map{ _.map{ t =>
    val (article, author) = t
  }
}

对于满足查询条件的每一个 article , 将会根据匹配条件 article.authorId == author._id 拉取关联的 author。

在处理 Json 时要格外小心,因为 Json 使用 JsNumber 表示所有数值类型,但是 Bson 拥有更加丰富的数值类型,这导致了 Json 和
Bson 之间的转换过程是不可逆的,因为双方的类型信息并不对称。 下面我们仔细分析常见的几个场景。在讨论中将会用到如下的 Model 定义:

@Entity("common-user")
case class User(_id: String, name: String, setting: UserSetting)
case class UserSetting(gender: String, age: Int)

我们经常使用下面代码将一个 Model 类实例插入 mongodb ,

mongo.insert[User].insert(User("0", "joymufeng", UserSetting("male", 32)))

在调用底层驱动的插入操作之前,需要先将 User 转换成 Bson 。这个转换过程是可逆的,当从 mongodb 读取数据时,可以成功地将
Bson 转换回 User。

当使用 Json DSL 构建一个 JsObject 对象时,所有的数值类型(例如 Byte, Short, Int, Long, Float 和
Double)均会被转换成 JsNumber 类型(内部使用BigDecimal存储数据),数值的具体类型在这个转换过程中丢失了。
在调用底层驱动程序前,Json 将会被转换为 Bson,JsNumber 将会被转换为
BsonDecimal128。当从数据库读取写入的数据时,我们没办法恢复已经丢失的数值类型信息。 例如我们通常会执行如下更新操作,

mongo.update[User](Json.obj("_id" -> "0"), Json.obj("$set" -> UserSetting("male", 18)))
// Or
mongo.update[User](Json.obj("_id" -> "0"), Json.obj("$set" -> Json.obj("setting" -> Json.obj("gender" -> "male", "age" -> 18))))

不管是 UserSetting("male", 32), 还是 Json.obj("gender" -> "male", "age" -> 18)
最终都会被转换为 Json.obj("gender" -> JsString("male"), "age" -> JsNumber(BigDecimal(18))。 所以,在更新操作执行完成后, user.setting.age 字段在数据库中的类型为
NumberDecimal, 当执行读取操作时便会发生类型转换错误,

mongo.findById[User]("0")
// [BsonInvalidOperationException: Invalid numeric type, found: DECIMAL128]

当试图将 BigDecimal 转换为 Int 时出错了. 因为在这个转换过程中会导致数值精度丢失。 为了解决这个问题, 我们在转换
JsNumber 时尽量将其转换为较窄的数值类型,以保证其可以被安全地转换回来。 例如 Json.obj("age" -> JsNumber(18.0))会被转换为 BsonDocument("age", BsonInt32(18))。

Play-Mongo Getting StartedModel and Collection常用操作 官网

https://gitee.com/joymufeng/play-mongo

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


SchemaCrawler提供一组用于增强标准JDBC Metadata的API.SchemaCrawler还包含一个命令行工具能够将数据库结构和数据以一种易读的形式输出.
ER Master 是一个用于设计ER模型图的Eclipse插件。提供的功能包括:从数据库导入关系生成ER图,导出设计图,导出DDL数据定义语句等。目前完整支持的数据库包括
Eclipse下用于画数据库ER图的插件,主要特性如下: 图形化编辑ER图 从数据库结构中导入ER图
PowerDesigner 是Sybase的企业建模和设计解决方案,采用模型驱动方法,将业务与IT结合起来,可帮助部署有效的企业体系架构,并为研发生命周期管理提供强大的分析与设计技术。PowerDesigner
Mogwai ERDesigner NG是一个实体关系建模工具类似于ERWin。它设计成让数据库建模变得尽可能简易并为整个开发过程提供支持,从数据库设计到模式
Power*Architect 是一个数据建模工具,主要用在数据仓库和数据集市的模型设计。
MySQL Workbench是一款专为MySQL设计的ER/数据库建模工具。它是著名的数据库设计工具DBDesigner4的继任者。你可以用MySQL
DbWrench 是一个数据库设计和同步软件,为数据库开发人员提升生产率,可轻松的创建和修改数据库。
是一个采用Python开发的使用ER图的数据库建模工具
CA公司的数据库建模工具 (非开源) ,为你提供一个易于使用的用户界面(UI)环境,简化数据库设计过程,并将许多令人疲惫的任务自动化,如创建高性能事务和数据仓库数据库。这是一款可以和PowerDesigner并驾齐驱的
Altova MapForce® 2008是您首选的数据综合和网络服务器实现工具。它可以通过映射数据源到WSDLO操作的方法,实现多种文件之间的转化,包括XML文件, 数据库文件,
一个用Java开发的可视化数据库设计工具,支持JDBC 2.0的数据库
一个图形化的数据库模型浏览工具,SchemaSpy analyzes database metadata to reverse engineer
从事软件开发多年,感觉数据库在整个软件开发过程中扮演一个不可或缺的角色。一旦一个业务项目进入立项阶段,需要支持哪些数据库平台,如何使用该种数据库平台都将成为架构师需要考虑的内容,对于一般的开发人员,
Middlegen一个免费数据库驱动(database-driven)的代码生成引擎。它基于JDBC,Velocity,Ant和XDoclet.
Ermodeller是一个用于数据建模的CASE工具。该工具支持概念建模和逻辑建模,并在最后生成特定数据库的物理模型。支持的数据库包括:MySQL、PotgreSQL、Oracle、Pointbase,对其它数据库的支持需要通过XML文件自定义
GMOD is the G eneric M odel O rganism D atabase project, a collection of open source software tools for creating and managing genome-
pgDesigner 是一个为PostgreSQL数据库设计的建模工具
Enterprise Architect是一款计算机辅助软件工程(CASE)工具,用于设计和构建软件系统、业务流程建模及更多通用的建模。
mybatis 通用 basedao,含自动生成通用 XMLMapper。 可通过此工具自动生成 Mybatis XML 文件和 dao 的 class 类