Phantom is Reactive type-safe Scala driver for Apache Cassandra/Datastax Enterprise. So, first lets explore what Apache Cassandra is with some basic introduction to it.
Apache Cassandra
Apache Cassandra is a free, open source data storage system that was created at Facebook in 2008. It is highly scalable database designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure. It is a type of NoSQL database which is Schema-free. For more about Cassandra refer to this blog Getting Started With Cassandra.
Phantom-DSL
We wanted to integrate Cassandra into Scala ecosystem that’s why we used Phantom-DSL as one of the module of outworkers. So, if you are planning on using Cassandra with Scala, phantom is the weapon of choice because of :-
- Ease of use and quality coding.
- Reducing code and boilerplate by at least 90%.
- Automated schema generation
You don’t need to switch to CQL(Cassandra Query Langguage) to create keyspace in Cassandra.
So, for that first you need to add up the dependency into yout project :-
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
libraryDependencies += "com.outworkers" % "phantom-dsl_2.11" % "2.14.5" |
You need to import this by default in your code:-
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.outworkers.phantom.dsl._ |
Connecting to a Cassandra cluster
Now, you can connect to Cassandra cluster by creating a ContactPoint called Connector which is basically required to run the application. when we start creating databases, we pass in a ContactPoint or what we call a connector in more plain English.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.outworkers.phantom.dsl._ | |
object Defaults { | |
val Connector = ContactPoint.local.keySpace(KeySpace("my_keyspace") | |
.ifNotExists().`with`(replication eqs SimpleStategy.replication_factor(1))) | |
} | |
class CassandraDatabase(override val connector: CassandraConnection) extends Database[CassandraDatabase](connector) { | |
object users extends Users with Connector | |
} | |
object CassandraDatabase extends CassandraDatabase(Connector.default) | |
trait DbProvider extends DatabaseProvider[CassandraDatabase] { | |
override val database = CassandraDatabase | |
} |
You have added the dependency and connected to your Cassandra cluster now, You are ready to create your very first table. Let’s assume we are modelling the CQL schema for a case class that looks like this:-
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class User( | |
id: String, | |
email: Set[String], | |
name: String | |
) |
Here’s how the Phantom DSL code looks like:-
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.outworkers.phantom.dsl._ | |
abstract class Users extends Table[Users, User] { | |
object id extends StringColumn with PartitionKey | |
object email extends SetColumn[String] | |
object name extends StringColumn with PartitionKey | |
} |
where Users is the table and User is the case class. All the fields with their column type has been defined. There must be atleast one field which must be a Partition Key same as a Primary Key in SQL. Partition key tells how your data is partitioned across the cluster.
Earlier, we were using CassandraTable trait instead of Table trait that we have mentioned in the above Phantom DSL code. By extending Table trait you don’t need to extend the RootConnector explicitly as Table trait already extends CassandraTable with RootConnector.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Table[T <: Table[T, R], R] extends CassandraTable[T, R] with TableAliases[T, R] with RootConnector |
So, the question is Why we needed to define RootConnector?
The simple answer for it is RootConnector defines the Keyspace and Session for you. It tells the table a connector with a `session` and `keySpace` will be injected later on.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.outworkers.phantom.connectors | |
trait RootConnector extends scala.AnyRef { | |
implicit def space : com.outworkers.phantom.connectors.KeySpace | |
implicit def session : com.datastax.driver.core.Session | |
} |
You have created Keyspace and a table now, its time to insert data into the Cassandra table. You can do this by writing the below code snippet into the class Users.scala.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def save(user: User): Future[ResultSet] = { | |
insert | |
.value(_.id, user.id) | |
.value(_.name, user.name) | |
.value(_.email, user.email) | |
.future() | |
} |
For quering your table, to get the data out of the table as needed by using various clauses and operators. Here, is the small code snippet :-
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def getAllUsers: Future[List[User]] = { | |
select | |
.fetch() | |
} | |
def getByid(id: String): Future[Option[String]] = { | |
select(_.name) | |
.where(_.id eqs id) | |
.allowFiltering() | |
.one() | |
} | |
def getByQuery(email: String, name: String): Future[Option[(String, Set[String])]] = { | |
select(_.id, _.email) | |
.where(_.email contains email) | |
.and(_.name eqs name) | |
.allowFiltering() | |
.one() | |
} | |
def getByidAndName(id: String): Future[List[User]] = { | |
update | |
.where(_.id eqs id) | |
.modify(_.name setTo "newUser") | |
.future() | |
getAllUsers | |
} |
Hope you liked the blog. Happy coding!! 🙂
References
- http://outworkers.com/blog/post/a-series-on-phantom-part-1-getting-started-with-phantom
- https://github.com/outworkers/phantom/wiki/Querying
Reblogged this on akashsethi24.
Reblogged this on Anurag Srivastava.
Awesome read. Just got here randomly and found this post very informative. Shared it with my friends.
Reblogged this on Coding, Unix & Other Hackeresque Things.
Needed such Cassandra article with Phantom – DSL.. Very simply explained. For more articles on Cassandra you can refer to these articles on https://data-flair.training/blogs/apache-cassandra-tutorial/