始终在方法的开头和结尾执行代码

Always execute code at beginning and end of method

我正在使用以下代码连接到 MongoDb:

   def insert() = {

    val mc = new com.mongodb.MongoClient("localhost", 27017);
    val db = mc.getDatabase("MyDb");

    //My insert code

    mc.close();

  }                                               //> insert: ()Unit

我有多种打开和关闭连接的方法。 能否上线:

  val mc = new com.mongodb.MongoClient("localhost", 27017);
val db = mc.getDatabase("MyDb");
mc.close();

被提取以便在方法的开头和结尾隐式调用它们。 Scala 隐式是否适合这种情况或是否需要反射?

你可以这样做而不是隐式:

  def mongoConn(ip:String, port:Int, dbName:String):(Database => Unit) => Unit  = {
    f => {
      val mc = new com.mongodb.MongoClient(ip, port)
      val db = mc.getDatabase(dbName)

      f(db)

      mc.close()
    }
  }

  val conn =  mongoConn("localhost", 27017, "MyDb")

  conn(db => {
    //insert code
  })

您可以定义一个方法来执行某些 'work' 函数,例如

def withMongoDb[T](work: DB => T): T = {
  val mc = new com.mongodb.MongoClient("localhost", 27017)
  // I don't actually know what type `db` is so I'm calling it `DB`
  val db: DB = mc.getDatabase("MyDb")

  try { work(db) }
  finally { mc.close() }
}

然后你可以像这样使用它:

withMongoDb { db =>
  db.insert(...)
  db.query(...)
}

这与 Slick, pre-3.0 中使用的方法类似,即 withSessionwithTransaction

现在,如果您实现了一些方便的方法,例如

def insertStuff(values: Seq[Int])(implicit db: DB) = {
  db.insert(values)
}

然后您可以将 db 标记为隐含在 withMongoDb 调用中,有效地确保您不会意外地在该块之外调用 insertStuff

withMongoDb { implicit db =>
  insertStuff(Seq(1,2,3,4))
}

insertStuff(Seq(1,2,3,4)) // compile error

一种常见的模式是使用 call-by-name 方法,您可以在其中传递接受 DB 并对其执行某些操作的函数。 call-by-name方法可以方便创建client等,执行里面的代码

def withDB[A](block: DB => A): A = {
  val mc = new com.mongodb.MongoClient("localhost", 27017);
  val db = mc.getDatabase("MyDb");
  try block(db) finally mc.close()
}

并使用它:

def insert() = withDB { db =>
   // do something with `db`
}

然而,看一下 documentation 说:

A MongoDB client with internal connection pooling. For most applications, you should have one MongoClient instance for the entire JVM.

这使得上述方法看起来不是个好主意,假设这是您正在使用的版本。我肯定会看到一些并发问题,试图这样做并且打开了太多连接。

但是,您可以遵循相同的模式,将创建的连接填充到单例对象中。但是,当您的应用程序关闭时,您需要管理客户端的关闭。

object Mongo {
  lazy val mc = new com.mongodb.MongoClient("localhost", 27017);
  lazy val db = mc.getDatabase("MyDb");

  def withDB[A](block: DB => A): A = block(db)

  def close(): Unit = mc.close()
}