4

Getting Started Cockroach with Scala: An Introduction

 3 years ago
source link: https://blog.knoldus.com/getting-started-cockroach-with-scala-an-introduction/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Getting Started Cockroach with Scala: An Introduction

Reading Time: 2 minutes

Today we are going to discuss that how can we use the Scala with the Cockroach DB? As we all know that Cockroach DB is a distributed SQL database built on top of a transactional and consistent key-value store and now we are going to use it with the Scala. But before starting the journey, To those who have caught the train late,😉 this is what has happened till now:

  1. An Introduction to CockroachDB !!

Now before starting the code please setup Cockroach DB on your local environment.

We have to follow these steps for setting up CockroachDB in your local environment.

  • We can download the Cockroach DB from here and follow the instruction which mentions there.
  • Now run the following commands for starting the Nodes:
# Start node 1:

cockroach start --insecure \
--store=CockroachTest-1 \
--host=localhost

# In a new terminal, start node 2:

cockroach start --insecure \
--store=CockroachTest-2 \
--host=localhost \
--port=26258 \
--http-port=8081 \
--join=localhost:26257

# In a new terminal, start node 3:

cockroach start --insecure \
--store=CockroachTest-3 \
--host=localhost \
--port=26259 \
--http-port=8082 \
--join=localhost:26257

Now your Cockroach DB is UP and in running conditions. Please check the localhost:8080 to see the Cockroach UI.

  • Now create the user:
cockroach user set maxroach --insecure
  • After creating the user, create Database and set Privilege
cockroach sql --insecure -e 'CREATE DATABASE test'

cockroach sql --insecure -e 'GRANT ALL ON DATABASE test TO maxroach'
  • Now Run the following commands for creating the table:
CREATE TABLE IF NOT EXISTS test.user (id VARCHAR(10) PRIMARY KEY, name TEXT, email VARCHAR(60));

After running all these commands, we are ready with our local environment on port 8080.

We will talk step by step to develop this Rest API after which you will able to run this API on your server and start hitting Rest end points.

When we start creating a project we have to use following dependencies:

  "com.typesafe.akka" %% "akka-http-core"   %   "10.0.7"
  "com.typesafe.slick"    %%  "slick"       %   "3.2.0",
  "com.typesafe.slick" %%  "slick-hikaricp" %   "3.2.0"1,
  "io.spray"       %%  "spray-json"         %   "1.3.3",
  "net.codingwell" %%  "scala-guice"        %   "4.1.0",
  "org.postgresql" %   "postgresql"         %   "9.4.1212",
  "io.spray"       %%  "spray-json"         % "1.3.3",
  "net.codingwell" %% "scala-guice"         % "4.1.0",
  • Create DB Component with Postgres and Slick for making the connection with Database:

trait PostgresDbComponent extends DBComponent {

val driver = PostgresProfile

import driver.api.Database

val db: Database = DBConnection.connectionPool

}

object DBConnection {

val connectionPool = Database.forConfig("db")

}

import slick.jdbc.JdbcProfile

trait DBComponent {

val driver: JdbcProfile

import driver.api._

val db: Database

}

  • Create the mapping for binding the data with the case class:

package com.knoldus.DAO.user.mappings

import com.knoldus.DAO.db.DBComponent

import slick.lifted.ProvenShape

case class User(id: String = "", name: String, email: String)

trait UserMapping {

this: DBComponent =>

import driver.api._

class UserMapping(tag: Tag) extends Table[User](tag, "user") {

def id: Rep[String] = column[String]("id", O.PrimaryKey)

def name: Rep[String] = column[String]("name")

def email: Rep[String] = column[String]("email", O.Unique)

def * : ProvenShape[User] = (

id,

name,

email

) <> (User.tupled, User.unapply)

}

val userInfo: TableQuery[UserMapping] = TableQuery[UserMapping]

}

  • Create  User Component which will interact with Cockroach DB for performing the DB operations:

package com.knoldus.DAO.user

import com.google.inject.ImplementedBy

import com.knoldus.DAO.db.{DBComponent, PostgresDbComponent}

import com.knoldus.DAO.user.mappings.{User, UserMapping}

import scala.concurrent.Future

@ImplementedBy(classOf[UserPostgresComponent])

trait UserComponent extends UserMapping {

this: DBComponent =>

import driver.api._

/**

* Inserts user into database

*

* @param user

* @return

*/

def insert(user: User): Future[Int] = {

db.run(userInfo += user)

}

/**

* Fetches user detail using email

*

* @param email

* @return Future[Option[User]]

**/

def getUserByEmail(email: String): Future[Option[User]] = {

db.run(userInfo.filter(user => user.email === email).result.headOption)

}

/**

* Fetches user record with the help of userId

*

* @param userId

* @return Option[User]

*/

def getUserByUserId(userId: String): Future[Option[User]] = {

db.run(userInfo.filter(user => user.id === userId).result.headOption)

}

/**

* Fetches All user from DB

*

* @return

*/

def getAllUsers: Future[List[User]] = {

db.run(userInfo.to[List].result)

}

/**

* Checks if user with user id exists

*

* @param userId

* @return

*/

def isUserIdExists(userId: String): Future[Boolean] = {

val query = userInfo.filter(user => user.id === userId).exists

db.run(query.result)

}

}

class UserPostgresComponent extends UserComponent with PostgresDbComponent

  • Create a user service which will work as a bridge between the Routes API and User Component:

package com.knoldus.service

import com.google.inject.Inject

import com.knoldus.DAO.user.UserComponent

import com.knoldus.DAO.user.mappings.User

import scala.concurrent.Future

class UserService @Inject()(userComponent: UserComponent){

/**

* Inserts user object in db

*

* @param user

*/

def insert(user: User): Future[Int] = userComponent.insert(user)

/**

* Get user by user id

*

* @param id

* @return

*/

def getUserByUserId(id: String): Future[Option[User]] = userComponent.getUserByUserId(id)

/**

* Get user by email id

*

* @param email

* @return

*/

def getUserByEmail(email: String): Future[Option[User]] = userComponent.getUserByEmail(email)

/**

* Get list of all users

*

* @return

*/

def getAllUsers(): Future[List[User]] = userComponent.getAllUsers

/**

* Check whether user exists with user id

*

* @param userId

* @return

*/

def isUserIdExists(userId: String): Future[Boolean] = userComponent.isUserIdExists(userId)

}

  • Now finally create routes:

package com.knoldus.api

import akka.http.scaladsl.model.{HttpResponse, StatusCodes}

import akka.http.scaladsl.server.Directives._

import akka.http.scaladsl.server.Route

import com.google.inject.Inject

import com.knoldus.DAO.user.mappings.User

import com.knoldus.service.UserService

import com.knoldus.util.JsonHelper

import scala.util.{Failure, Success}

class UserApi@Inject()(userService: UserService) extends JsonHelper {

/**

* Default route

*

* @return

*/

def welcomeRoute: Route = path("") {

get {

complete("Cockroach Db starter kit with Akka http")

}

}

/**

* Creates http route to insert user object

*

* @return

*/

def insertUser: Route = path("user" / "add") {

(post & entity(as[User])) { user =>

onComplete(userService.isUserIdExists(user.id)) {

case Success(res) => validate(!res, s"User with id '${user.id}' already exists") {

onComplete(userService.insert(user)) {

case Success(result) => complete("User added successfully")

case Failure(ex) => complete(HttpResponse(StatusCodes.InternalServerError, entity = ex.getMessage))

}

}

case Failure(ex) => complete(HttpResponse(StatusCodes.InternalServerError, entity = ex.getMessage))

}

}

}

/**

* Creates http route to get user by user id

*

* @return

*/

def getUserByUserId: Route = path("user" / "get") {

get {

parameters("id") { id =>

onComplete(userService.getUserByUserId(id)) {

case Success(userOpt) => userOpt match {

case Some(user) => complete(user)

case None => val msg = s"No user found with user id: ${id}"

complete(HttpResponse(StatusCodes.BadRequest, entity = msg))

}

case Failure(ex) => complete(HttpResponse(StatusCodes.InternalServerError, entity = ex.getMessage))

}

}

}

}

/**

* Creates http route to get user by email

*

* @return

*/

def getUserByEmail: Route = path("user" / "get") {

get {

parameters("email") { email =>

onComplete(userService.getUserByEmail(email)) {

case Success(userOpt) => userOpt match {

case Some(user) => complete(user)

case None => val msg = s"No user found with email: ${email}"

complete(HttpResponse(StatusCodes.BadRequest, entity = msg))

}

case Failure(ex) => complete(HttpResponse(StatusCodes.InternalServerError, entity = ex.getMessage))

}

}

}

}

/**

* Creates http route to get list of all users

*

* @return

*/

def getAllUsers: Route = path("user" / "get" / "all") {

get {

onComplete(userService.getAllUsers()) {

case Success(users) => complete(users)

case Failure(ex) => complete(HttpResponse(StatusCodes.InternalServerError, entity = ex.getMessage))

}

}

}

/**

* Creates http route to check whether user exists by given user Id

*

* @return

*/

def isUserIdExists: Route = path("user" / "exists") {

get {

parameters("userId") { userId =>

onComplete(userService.isUserIdExists(userId)) {

case Success(users) => complete(users.toString)

case Failure(ex) => complete(HttpResponse(StatusCodes.InternalServerError, entity = ex.getMessage))

}

}

}

}

val routes = welcomeRoute ~ insertUser ~ getUserByUserId ~ getUserByEmail ~ getAllUsers ~ isUserIdExists

}

So this is the basic idea that how we can use Cockroach DB with scala.
You can get the above working example from the GitHub repo, checkout: GitHub
If You have any questions you can contact me here or on Twitter: @anuragknoldus






About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK