转载:http://blog.yunglinho.com/blog/2012/04/30/mongodb-dao/
這篇文章將說說,我使用 MongoDB & Cashbah 的一些心得,這程式的主架構是 我從 simplistic 學來的。
The Abstract DAO
底下是一個很簡單的 CRUD 的 MongoDB DAO. 在這邊,我們建立一個 DAO 時,我們會把這 個 DAO 用的儲存空間 MongoCollection 傳進來,另外要注意的一點是,這個 MongoCollection 的 WriteConcert 必需是 Strict 的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
/**
* Base class for all DAO classes.
*/
abstract class AbstractDAO[T, ID <: Serializable](protected[locadz] val mongoCol: MongoCollection) {
/** builder to turn mongodb object into model object */
implicit protected val builder: DBObject => Either[DataAccessException, T]
/** builder to turn model object into mongodb object */
implicit protected val extractor: T => DBObject
/** this dao expects the underlying mongoCol would throw exception when error occurs. */
require(mongoCol.getWriteConcern != null)
require(mongoCol.getWriteConcern.getW >= 1, "Write must be >= 1, was %d".format(mongoCol.getWriteConcern.getW))
/** insert object into the underly*/
def insert(obj: T): Either[DataAccessException, ID] = {
try {
val dbObj: DBObject = extractor(obj)
val res: WriteResult = mongoCol.insert(dbObj)
Right(dbObj.getAs[ID]("_id").get)
} catch {
case e: MongoException => Left(new DataAccessException(e))
case e: DataAccessException => Left(e)
}
}
def save(obj: T): Option[DataAccessException] = {
try {
val res = mongoCol.save[T](obj)
None
} catch {
case e: MongoException => Some(new DataAccessException(e))
case e: DataAccessException => Some(e)
}
}
def findById(id: ID): Either[DataAccessException, Option[T]] = {
try {
mongoCol.findOneByID(id) match {
case None => Right(None)
case Some(dbObj) => builder.apply(dbObj).right.map(Option(_))
}
} catch {
case e: DataAccessException => Left(e)
}
}
def delete(id: ID): Option[DataAccessException] = {
try {
mongoCol.remove(MongoDBObject("_id" -> id))
None
} catch {
case e: MongoException => Some(new DataAccessException(e))
}
}
def find[T](dbObj: DBObject)(implicit builder: (DBObject) => Either[DataAccessException, T]): Either[DataAccessException, Iterable[T]] = {
try {
Right(mongoCol.find(dbObj)
.flatMap(found => {
builder.apply(found).fold(
e => throw e,
r => Some(r))
}).toStream)
} catch {
case e: DataAccessException => Left(e)
}
}
}
|
這邊有兩個需要被實作的東西
- val builder: DBObject => Either[DataAccessException, T]
- val extractor: T => DBObject
而怎麼建立這兩個東西,就是全文的重點了。
How to use AbstractDAO
先來看一個範例;實作上,在 UserDAO object 上,我們先把欄位的名稱及資料型態,一個個的定好
- ‘val Name = attribute("name", Conversion.String)`
- ‘val Email = attribute("email", Conversion.String)’
- ‘val CreateDate = attribute("ctime", Conversion.JodaDate)’
而這個 attribute method 做的是,把資料名稱存起來,然後當這個 Attribute instance
- 收到一個 DBObject 時,把對應的欄位取出來,並將值給轉成定義的格式
- 收到一個 property value 時,把他轉成 (name -> value) pair
第一個用法的這個方式,用在從 builder 上面,一個個的把值從 DBObject 抓出來再來建立物件, 第二個用法用在,把物件轉成 DBObject 時,及用在建立 query 時。
UserDAO 實作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
class UserDAO(mongoCol: MongoCollection) extends AbstractDAO[User, String](mongoCol) {
import UserDAO._
implicit protected val builder: (DBObject) => Either[DataAccessException, User] = UserDAO.dbObjectToUser _
implicit protected val extractor: (User) => DBObject = UserDAO.userToDBObject _
def this(dbCol: DBCollection) = this(dbCol.asScala)
}
object UserDAO {
import Attributes._
val Name = attribute("name", Conversion.String)
val Email = attribute("email", Conversion.String)
val CreateDate = attribute("ctime", Conversion.JodaDate)
def dbObjectToUser(dbObj: DBObject): Either[DataAccessException, User] = {
try {
val id = dbObj.getAs[String]("_id").get
Right(
new User(id,
Name(dbObj),
Email(dbObj),
CreateDate(dbObj)
)
)
} catch {
case e: DataAccessException => Left(e)
}
}
implicit def userToDBObject(user: User): DBObject = {
val ret = MongoDBObject("_id" -> user.id)
ret += Name(user.externalId)
ret += Email(user.verifiedId)
ret += CreateDate(user.createDate)
return ret
}
}
|
Attribute & Conversion 部份實作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
trait Attribute[A, B <: Any] {
val name: String
val conversion: Conversion[A, B]
protected implicit val manifestA: Manifest[A]
protected implicit val manifestB: Manifest[B]
}
trait RequiredAttribute[A, B <: Any] extends Attribute[A, B] {
def apply(value: A): Tuple2[String, B] = {
name -> conversion.apply(value)
}
def apply(value: DBObject): A = {
value.getAs[B](name).map(b => conversion.unapply(b)).getOrElse(throw new MissingRequiredAttributeException(name))
}
}
def attribute[A, B <: Any](name: String, dataType: Conversion[A, B])(implicit manifestA: Manifest[A], manifestB: Manifest[B]) = {
new SingleValueRequiredAttribute[A, B](name, dataType)
}
trait Conversion[A, B] {
def apply(source: A): B
def unapply(value: B): A
}
object Conversion {
case object String extends Conversion[String, String] {
override def apply(source: String) = source
override def unapply(value: String) = value
}
/**
* Conversion for DateTime to DateTime.
*/
object JodaDate extends Conversion[DateTime, DateTime] {
RegisterJodaTimeConversionHelpers()
override def apply(source: DateTime): DateTime = source
override def unapply(value: DateTime): DateTime = value
}
}
|
How to Use Attributes to Build Query
前面的 Attribute(s) 不只是用來產做 builder 及 extractor 用的,他們最好用的地方在於 建立 query 時,如下
1
2
3
|
def findByDateRange(start: DateTime, end: DateTime): Either[DataAccessException, User] = {
return find(CreateDate.name $gth CreateDate(start)._2 $lt CreateDate(range)._2)
}
|
這邊,我們可以看到用 Conversion 來描述資料型態的好處是,當你儲存的資料型態有變時,你不用去更 改你的 MongoDB query ,欄位的名稱、資料型別的轉換,都被封裝起來,使用者在寫 Query 時,不用去 記欄位的名稱,也不用去記這個欄位的型態是什麼, MongoDB 支不支援這個型態。
第二個好處是, Casbah 本來只支援 java primitive type & java.util.Date ,透過 Conversion , 我們可以支援任意的資料型態於 query 中,只要透過新的 Conversion 類別,我們就可以增加 MongoDB 可以 支援的資料型態。
例如,如果我們想增加對 java.net.URL 的支援,只要訂義以下的 Conversion 即可
1
2
3
4
5
6
|
case object Url extends Conversion[URL, String] {
override def apply(source: URL) = source.toExternalForm
override def unapply(value: String) = new URL(value)
}
|
Appendix:
Attribute.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package com.locadz.model.mongodb
import com.mongodb.DBObject
import com.mongodb.casbah._
import com.locadz.model.exception.MissingRequiredAttributeException
/**
* Date: 2/20/12
*/
object Attributes {
trait Attribute[A, B <: Any] {
val name: String
val conversion: Conversion[A, B]
protected implicit val manifestA: Manifest[A]
protected implicit val manifestB: Manifest[B]
}
trait OptionalAttribute[A, B <: Any] extends Attribute[A, B] {
def apply(value: Option[A]): Tuple2[String, Any] = {
val v = value.map(conversion.apply(_)).getOrElse(null)
name -> v
}
def apply(value: DBObject): Option[A] = value.getAs[B](name).map(b => conversion.unapply(b))
}
trait RequiredAttribute[A, B <: Any] extends Attribute[A, B] {
def apply(value: A): Tuple2[String, B] = {
name -> conversion.apply(value)
}
def apply(value: DBObject): A = {
value.getAs[B](name).map(b => conversion.unapply(b)).getOrElse(throw new MissingRequiredAttributeException(name))
}
}
case class SingleValueRequiredAttribute[A, B <: Any](name: String, conversion: Conversion[A, B])(implicit val manifestA: Manifest[A], implicit val manifestB: Manifest[B]) extends RequiredAttribute[A, B]
case class SingleValueOptionalAttribute[A, B <: Any](name: String, conversion: Conversion[A, B])(implicit val manifestA: Manifest[A], implicit val manifestB: Manifest[B])
extends OptionalAttribute[A, B]
def attribute[A, B <: Any](name: String, dataType: Conversion[A, B])(implicit manifestA: Manifest[A], manifestB: Manifest[B]) = {
new SingleValueRequiredAttribute[A, B](name, dataType)
}
def optionalAttribute[A, B <: Any](name: String, dataType: Conversion[A, B])(implicit manifestA: Manifest[A], manifestB: Manifest[B]) = {
new SingleValueOptionalAttribute[A, B](name, dataType)
}
}
|
转载:http://blog.yunglinho.com/blog/2012/04/30/mongodb-dao/
分享到:
相关推荐
MongoDB DAO层封装是数据库操作的重要一环,它在应用程序与数据库之间建立了一层抽象,使得数据访问更加简便和高效。在这个项目中,我们主要基于MongoDB 3.0版本和Spring Data 1.5进行整合,实现了DAO层的封装。下面...
mongodbDao的基本操作
上述代码定义了一个名为`MongoDBDAO`的类,它包含了连接数据库、插入数据、查询数据、更新数据和删除数据的方法。`connect()`方法用于建立到MongoDB服务器的连接,`close()`方法则用于断开连接。其他方法分别对应了...
ReactiveMongo 是一个 MongoDB 的 Scala 驱动,提供完全的非堵塞和异步 I/O 操作。 示例代码: def test() { import reactivemongo.api._ import scala.concurrent.ExecutionContext.Implicits.global val...
在 Scala 中与 MongoDB 进行交互,通常我们会使用 `mongo-scala-driver`,而不是 `mongo-java-driver`,因为 Scala 驱动提供了更符合 Scala 语言特性的 API 设计。本示例将详细介绍如何使用 `mongo-scala-driver` ...
mongodb资源:MongoDB的基于DAO的基本操作
在Spark 3.4.2版本中,与MongoDB的集成以及SQL和Scala的混合开发是大数据处理领域的重要应用场景。本篇文章将详细讲解如何利用这些技术进行高效的数据操作。 首先,Spark SQL是Apache Spark的一个重要组件,它允许...
这个项目利用了MongoDB的Scala驱动程序,创建了一个数据访问对象(DAO)模式,使得CRUD(创建、读取、更新和删除)操作变得更加直观和便捷。 **MongoDB与Scala驱动程序** MongoDB是一款流行的NoSQL数据库系统,以...
【play2+scala+mongodb demo示例】是一个用于学习如何集成和使用Play Framework 2、Scala编程语言以及MongoDB数据库的实战项目。这个项目旨在帮助初学者理解这三者之间的协同工作,通过创建一个基本的CRUD(创建、...
大数据实战项目商品推荐系统源码+项目说明(Spark、Scala、MongoDB).zip大数据实战项目商品推荐系统源码+项目说明(Spark、Scala、MongoDB).zip大数据实战项目商品推荐系统源码+项目说明(Spark、Scala、MongoDB)...
Subset is a library to ease extracting fields from MongoDB documents, serializing them back and constructing queries. 标签:Subset
在“mongodb_整合java_dao”这个主题中,我们将讨论如何在Java应用中集成MongoDB,创建DAO层来执行CRUD(创建、读取、更新、删除)操作。首先,你需要在项目中添加MongoDB Java驱动的依赖,通常是通过Maven或Gradle...
该项目是一个大数据课程的期末项目,主要利用了Spark、Hadoop HDFS和MongoDB等技术,通过Scala编程语言来实现电影推荐系统。这个系统是基于大数据处理的,因此涉及到的知识点非常广泛,涵盖了分布式计算、数据存储、...
2. **MongoDB Scala Driver**: 官方提供的驱动程序,它为Scala提供了一个直接的绑定,允许开发者用Scala语法直接操作MongoDB。这个驱动程序完全支持MongoDB的所有特性,包括新引入的聚合管道和文档模型。 在实际...
/usr/local/mongodb/mongodb-linux-2.0.7/bin/mongod --dbpath=/usr/local/mongodb/data/db --logpath=/usr/local/mongodb/mongodb-linux-2.0.7/logs/mongodb.log --logappend --port=27017 --fork 知识点 6:配置...
基于Spark+Scala+MongoDB的大数据实战,商品推荐系统设计与实现.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息等)正在做...
6. 丰富的 BSON 数据类型: MongoDB 还提供了丰富的 BSON 数据类型,还有 MongoDB 的官方不同语言的 driver 支持(C/C++、C#、Java、Node.js、Perl、PHP、Python、Ruby、Scala)。 MongoDB 的应用场景: 1. ...
- 为了简化对MongoDB的操作,可以设计一个通用的DAO层来封装基本的CRUD操作。 - **获取数据库**: ```java MongoDatabase database = mongoClient.getDatabase("test"); ``` - **获取集合**: ```java ...
MongoDb的一个非常简单的DAO。 与NodeJS 6.11.1及更高版本兼容。 变更记录 查看发布 一般用法 该API非常简单流畅。 simpleDao .for(Account) .find({}) .toArray() .then(function (results) { // do ...
MongoDB是一种流行的开源、分布式文档数据库,常被用于构建高性能、可扩展的应用程序。这个“mongodb-测试数据”压缩包显然包含了一些用于测试MongoDB功能的样例数据集,特别是针对增、删、改、查(CRUD)操作的学习...