如何在应用程序启动时连接到Cassandra

时间:2018-03-06 05:20:35

标签: cassandra datastax playframework-2.6

我有Play个应用程序,需要连接到Cassandra。我正在使用Datastax的驱动程序连接到Cassandra。

我可以从控制器连接到数据库。代码段是(完整代码来自http://manuel.kiessling.net/setting-up-a-scala-sbt-multi-project-with-cassandra-connectivity-and-migrations

 val cluster = new Cluster.Builder().
      addContactPoints(uri.hosts.toArray: _*).
      withPort(uri.port).
      withQueryOptions(new QueryOptions().setConsistencyLevel(defaultConsistencyLevel)).build

    val session = cluster.connect
    session.execute(s"USE ${uri.keyspace}")
    session

我在控制器中使用上面的代码如下:

class UserController @Inject()(cc: ControllerComponents)(implicit exec: ExecutionContext) extends AbstractController(cc){

  def addUser = Action.async{ implicit request => {
    println("addUser controller called")
    println("testing database connection")

    val uri = CassandraConnectionUri("cassandra://localhost:9042/killrvideo")
    println(s"got uri object ${uri.host}, ${uri.hosts}, ${uri.port}, ${uri.keyspace}")
    val session = Helper.createSessionAndInitKeyspace(uri)

    val resultSet = session.execute(s"select * from users")
    val row = resultSet.one()
    println("got row ",row)
    val user = User(UUID.randomUUID(),UserProfile(true,Some("m@m.com"),Some("m"),Some("c")))
...
  }

虽然代码有效,但我想我不应该从控制器中连接到数据库。我应该在play应用程序启动时连接到数据库并在控制器中注入连接。但我不知道该怎么做。这是在Play中创建数据库应用程序的正确方法吗?

1 个答案:

答案 0 :(得分:2)

简短说明:

从控制器类连接C *不是一个好习惯。鼓励在访问DB时拥有单独的存储库/存储类。 您将创建一个数据库访问类,并将该类注入您的控制器类的构造函数。 这是一个开源示例应用程序,我遵循创建自己的Cassandra应用程序。 Play-Framework-Cassandra-Example。你可以关注这个项目。

详细说明:

以下是一些基本概念:

第1步: 在application.conf文件中定义数据库配置:

db {
      keyspace = "persons"
      table = "person_info"
      preparedStatementCacheSize = 100
      session {
        contactPoints = ["127.0.0.1"]
        queryOptions {
          consistencyLevel = "LOCAL_QUORUM"
        }
      }
    }

第2步:创建一个Singleton类来主要与Cassandra DB的连接

class CassandraConnectionProvider @Inject()(config: Configuration) extends Provider[CassandraConnection] {
  override def get(): CassandraConnection = {
    val hosts = config.getStringList("db.session.contactPoints")
    val keyspace = config.getString("db.keyspace")

    // Use the Cluster Builder if you need to add username/password and handle SSL or tweak the connection
    ContactPoints(hosts.asScala).keySpace(keyspace)
  }
}

第3步:现在创建一个存储库类,您可以在其中操作CRUD操作到数据库。

class PhantomPersonRepository @Inject()(config: Configuration, connection: CassandraConnection, ec: ExecutionContext)
  extends CassandraTable[PhantomPersonRepository, Person] with PersonRepository[Future] {
  // See https://github.com/outworkers/phantom/wiki/Using-the-Database-class-and-understanding-connectors
  implicit val session: Session = connection.session
  implicit val keySpace: KeySpace = connection.provider.space
  override val tableName: String = config.getString("db.table").getOrElse("person_info")
  implicit val executionContext: ExecutionContext = ec

  object id extends UUIDColumn(this) with PartitionKey

  object firstName extends StringColumn(this) {
    override def name: String = "first_name"
  }

  object lastName extends StringColumn(this) {
    override def name: String = "last_name"
  }

  object studentId extends StringColumn(this) {
    override def name: String = "student_id"
  }

  object gender extends EnumColumn[Gender.Value](this)

  override implicit val monad: Monad[Future] = cats.instances.future.catsStdInstancesForFuture

  override def create(person: Person): Future[Person] =
    insert.value(_.id, person.id)
      .value(_.firstName, person.firstName)
      .value(_.lastName, person.lastName)
      .value(_.studentId, person.studentId)
      .value(_.gender, person.gender)
      .consistencyLevel_=(ConsistencyLevel.LOCAL_QUORUM)
      .future()
      .map(_ => person)

  // https://github.com/outworkers/phantom/wiki/Querying#query-api
  override def find(personId: UUID): Future[Option[Person]] =
    select.where(_.id eqs personId)
      .consistencyLevel_=(ConsistencyLevel.LOCAL_QUORUM)
      .one()

  override def update(person: Person): Future[Person] = create(person)
.....

步骤4:现在将此存储库类注入Controller类并访问DB:

@Singleton
class PersonController @Inject()(personRepo: PersonRepository[Future])(implicit ec: ExecutionContext) extends Controller {

  def create: Action[JsValue] = Action.async(parse.json) { request =>
    onValidationSuccess[CreatePerson](request.body) { createPerson =>
      val person = Person(UUID.nameUUIDFromBytes(createPerson.studentId.getBytes()), createPerson.firstName,
        createPerson.lastName, createPerson.studentId, createPerson.gender.toModel)
      personRepo.find(person.id).flatMap {
        case None => personRepo.create(person).map(createdPerson => Created(createdPerson.toJson))
        case Some(existing) => Future.successful(Conflict(existing.toJson))
      }.recover { case _ => ServiceUnavailable }
    }
  } 
 .....

希望这会有所帮助。所有代码都归功于calvinlfer