`

Mongodb Scala DAO

 
阅读更多

转载: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 DAO层封装是数据库操作的重要一环,它在应用程序与数据库之间建立了一层抽象,使得数据访问更加简便和高效。在这个项目中,我们主要基于MongoDB 3.0版本和Spring Data 1.5进行整合,实现了DAO层的封装。下面...

    mongodbDao的基本操作

    mongodbDao的基本操作

    Node.js对于MongoDB的操作DAO的封装

    上述代码定义了一个名为`MongoDBDAO`的类,它包含了连接数据库、插入数据、查询数据、更新数据和删除数据的方法。`connect()`方法用于建立到MongoDB服务器的连接,`close()`方法则用于断开连接。其他方法分别对应了...

    MongoDB的Scala驱动ReactiveMongo.zip

    ReactiveMongo 是一个 MongoDB 的 Scala 驱动,提供完全的非堵塞和异步 I/O 操作。 示例代码: def test() { import reactivemongo.api._ import scala.concurrent.ExecutionContext.Implicits.global val...

    mongo-scala-drive的使用demo

    在 Scala 中与 MongoDB 进行交互,通常我们会使用 `mongo-scala-driver`,而不是 `mongo-java-driver`,因为 Scala 驱动提供了更符合 Scala 语言特性的 API 设计。本示例将详细介绍如何使用 `mongo-scala-driver` ...

    mongodb资源:MongoDB的基于DAO的基本操作

    mongodb资源:MongoDB的基于DAO的基本操作

    spark 3.4.2 mongodb sql与scala混合开发关键代码

    在Spark 3.4.2版本中,与MongoDB的集成以及SQL和Scala的混合开发是大数据处理领域的重要应用场景。本篇文章将详细讲解如何利用这些技术进行高效的数据操作。 首先,Spark SQL是Apache Spark的一个重要组件,它允许...

    simple-mongo:在Mongo Scala驱动程序之上构建MongoDB DAO模式

    这个项目利用了MongoDB的Scala驱动程序,创建了一个数据访问对象(DAO)模式,使得CRUD(创建、读取、更新和删除)操作变得更加直观和便捷。 **MongoDB与Scala驱动程序** MongoDB是一款流行的NoSQL数据库系统,以...

    play2+scala+mongodb demo示例

    【play2+scala+mongodb demo示例】是一个用于学习如何集成和使用Play Framework 2、Scala编程语言以及MongoDB数据库的实战项目。这个项目旨在帮助初学者理解这三者之间的协同工作,通过创建一个基本的CRUD(创建、...

    大数据实战项目商品推荐系统源码+项目说明(Spark、Scala、MongoDB).zip

    大数据实战项目商品推荐系统源码+项目说明(Spark、Scala、MongoDB).zip大数据实战项目商品推荐系统源码+项目说明(Spark、Scala、MongoDB).zip大数据实战项目商品推荐系统源码+项目说明(Spark、Scala、MongoDB)...

    MongoDB的Scala开发包Subset.zip

    Subset is a library to ease extracting fields from MongoDB documents, serializing them back and constructing queries. 标签:Subset

    mongodb_整合java_dao,以及用mongodb做附近人,或者是根据经纬多获取附近商家

    在“mongodb_整合java_dao”这个主题中,我们将讨论如何在Java应用中集成MongoDB,创建DAO层来执行CRUD(创建、读取、更新、删除)操作。首先,你需要在项目中添加MongoDB Java驱动的依赖,通常是通过Maven或Gradle...

    大数据课程的期末项目,基于spark、hadoop hdfs、mongodb,使用scala,进行电影推荐.zip

    该项目是一个大数据课程的期末项目,主要利用了Spark、Hadoop HDFS和MongoDB等技术,通过Scala编程语言来实现电影推荐系统。这个系统是基于大数据处理的,因此涉及到的知识点非常广泛,涵盖了分布式计算、数据存储、...

    scala_mongodb

    2. **MongoDB Scala Driver**: 官方提供的驱动程序,它为Scala提供了一个直接的绑定,允许开发者用Scala语法直接操作MongoDB。这个驱动程序完全支持MongoDB的所有特性,包括新引入的聚合管道和文档模型。 在实际...

    linux安装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

    基于Spark+Scala+MongoDB的大数据实战,商品推荐系统设计与实现.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息等)正在做...

    Mongodb 学习 PPT上课教程

    6. 丰富的 BSON 数据类型: MongoDB 还提供了丰富的 BSON 数据类型,还有 MongoDB 的官方不同语言的 driver 支持(C/C++、C#、Java、Node.js、Perl、PHP、Python、Ruby、Scala)。 MongoDB 的应用场景: 1. ...

    MongoDB 在java中的应用 纯Java操作

    - 为了简化对MongoDB的操作,可以设计一个通用的DAO层来封装基本的CRUD操作。 - **获取数据库**: ```java MongoDatabase database = mongoClient.getDatabase("test"); ``` - **获取集合**: ```java ...

    btrz-simple-dao:***非常简单*** MongoDb的DAO在承诺的mongo之上(已发布)(类别

    MongoDb的一个非常简单的DAO。 与NodeJS 6.11.1及更高版本兼容。 变更记录 查看发布 一般用法 该API非常简单流畅。 simpleDao .for(Account) .find({}) .toArray() .then(function (results) { // do ...

    mongodb-测试数据

    MongoDB是一种流行的开源、分布式文档数据库,常被用于构建高性能、可扩展的应用程序。这个“mongodb-测试数据”压缩包显然包含了一些用于测试MongoDB功能的样例数据集,特别是针对增、删、改、查(CRUD)操作的学习...

Global site tag (gtag.js) - Google Analytics