slick schema extension with activeslick - slick

I have defined Person and Org (organization) schemas as
trait Schema { this: Tables with TableQueries with Profile =>
import jdbcDriver.simple._
class PersonsTable(tag: Tag) extends EntityTable[Player](tag, "PERSON") { def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def dateCreated = column[Date]("DATE_CREATED")
def firstName = column[String]("FIRST_NAME")
def lastName = column[String]("LAST_NAME")}
class OrgTable(tag: Tag) extends EntityTable[Player](tag, "ORG") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def dateCreated = column[Date]("DATE_CREATED")
def = column[String]("NAME")
}
}
I soon realized that the schemas shared common fields like id (primary key) and dateCreated. So, I grouped them together so I don't have to repeat them,
trait Schema { this: Tables with TableQueries with Profile =>
import jdbcDriver.simple._
class PersonsTable(tag: Tag) extends EntityTable[Player](tag, "PERSON") with CommonSchema#CommonFields { def firstName = column[String]("FIRST_NAME")
def lastName = column[String]("LAST_NAME")}
class OrgTable(tag: Tag) extends EntityTable[Player](tag, "ORG") with CommonSchema#CommonFields {
def = column[String]("NAME")
}
}
trait CommonSchema { this: Tables with TableQueries with Profile =>
import jdbcDriver.simple._
trait CommonFields{this: Table[_] =>
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def dateCreated = column[Date]("DATE_CREATED")
}
}
I could not get this to compile. The compiler complained "CommonSchema is not a legal prefix for a constructor". How do I get it to work? I am using ActiveSlick with this setup and I am using a generic jdbcDriver for these classes as a place holder until the final moment when I run this classes with a specific Slick driver i.e. scala.slick.driver.H2Driver.simple._. Thanks

Related

How to make a generic id class in Python 3.7

Coming from a Java background, I know that for example an ID can be implemented as a generic like this:
public class Id<T> {
private String _id;
private Id(String idString) {
this._id = idString;
}
public static <Type> Id<Type> valueOf(String idString) {
return new Id<Type>(idString);
}
}
Id<ObjectA> a = Id.valueOf("1");
Id<ObjectB> b = Id.valueOf("1");
In this example, even though both IDs hold the String "1", they cannot be compared since their generic types are different.
I'm trying to implement the same structure in Python and started out with a NamedTuple:
from typing import NamedTuple
class Id(NamedTuple):
id: str
def __eq__(self, other):
if not isinstance(other, Id):
return False
else:
if self.id != other.id:
return False
return True
I want to bring generics in so that I can create an Id just like in Java. Is that possible and if so, how?

how to connect to Cassandra at application start up

I have a Play application which need to connect to Cassandra. I am using Datastax's driver to connect to Cassandra.
I am able to connect to the db from a controller. The code snippet is (full code is from 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
I am using the above code in a controller as follows:
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")))
...
}
Though the code works, I suppose I shouldn't be connecting to the database from within a controller. I should connect to the database when the play application starts and inject the connection in the controller. But I don't know how to do this. Is this the right way to create a database application in Play?
Short description:
It's not a good practice to connect C* from controller class. It is encouraged to have a separate repository/storage class while accessing DB. You will create a DB accessing class and inject that class to your controller class's constructor.
Here is an open-source sample application what I followed to create my own Cassandra application. Play-Framework-Cassandra-Example. You can follow this project.
Long description:
Here are some basic concepts how to do it:
Step 1:
Define DB configuration in application.conf file:
db {
keyspace = "persons"
table = "person_info"
preparedStatementCacheSize = 100
session {
contactPoints = ["127.0.0.1"]
queryOptions {
consistencyLevel = "LOCAL_QUORUM"
}
}
}
step 2: create a Singleton class to main the connection with 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)
}
}
Step 3: Now create a repository class where you can operate CRUD operation into DB.
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)
.....
Step 4: Now Inject this repository classes to your Controller class and access 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 }
}
}
.....
Hope this helps. All code credits to calvinlfer

How do you read existing method #annotations in groovy?

I'm trying to get the methods annotated with #XmlElement in the Top myxml but it the `myxml.methods[1]
import javax.xml.bind.annotation.*
import groovy.transform.*
import javax.xml.bind.*
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#TupleConstructor
class Top {
String myBackingField
#XmlElement
public String getChildElement() {
return myBackingField
}
}
def myxml = new Top("child_one")
So far I got:
def mymethod = parsed.metaClass.methods.find { it.name == 'getChildElement' }
But the CachedMethod API doesn't give access to annotations.
From CachedMethod you can get to the java.lang.reflect.Method using CachedMethod.getCachedMethod() and from there you can use the Method.getAnnotations() or Method.isAnnotationPresent():
def mymethod = myxml.metaClass.methods.find { it.name == 'getChildElement' }
// mymethod.class is org.codehaus.groovy.reflection.CachedMethod
mymethod.cachedMethod.annotations // [#XmlElement(name=##default, namespace=##default,
// type=class XmlElement$DEFAULT, defaultValue=,
// required=false, nillable=false)]
To get only the methods with that annotation you can
def mymethods = myxml.metaClass.methods.findAll {
it.cachedMethod.isAnnotationPresent(XmlElement)
}
mymethods[0].class // CachedMethod backed by Method with XmlElement annotation

How do I get the annotation value 'used'

I want to retrieve the annotation value used from MyAnnot. I am getting 3 annotations in the list even though there are only 2. Also, I have tried obtaining the used field of MyAnnot but with no success. I would like to return a map where MyAnnot's used is the key and type as the map's value.
// Then, define your class with it's annotated Fields
class MyClass {
#MyAnnot(used = "Hey", type = "There")
String fielda
#MyAnnot(used = "denn", type = "Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation(obj, annotClass) {
def op = []
def annos = []
def i = 0
obj.properties.findAll { prop ->
obj.getClass().declaredFields.find {
it.name == prop.key && annotClass in it.declaredAnnotations*.annotationType()
annos=it.declaredAnnotations
i++
if(annos)
op << annos[0] as Set
// println"Props ${annos[0]}"
}
}
op.each{ println "${it} and i is ${i}"}
}
// Then, define an instance of our class
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
// And print the results of calling our method
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
First of all you need to mark you annotation with: #Retention(RetentionPolicy.RUNTIME) to make it available for processing at runtime, so it will be:
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
Then, used and type are not fields, nor properties but methods, so they must be invoked and get.
The script will be:
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Retention
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
class MyClass {
#MyAnnot(used="Hey" ,type="There")
String fielda
#MyAnnot(used="denn", type="Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation( obj, annotClass ) {
def c = obj.getClass()
c.declaredFields.findAll { field ->
field.isAnnotationPresent(annotClass)
}.collect { found ->
def a = found.getAnnotation(annotClass)
[(a.used()): a.type()]
}.sum()
}
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
Mind that passing only a class of annotation is not sufficient, since you don't know the methods (used and type) to be invoked on the annotation. The following method will work only for MyAnnot class.

Slick 3.0 Mapping to a List

I have a mapping definition in Slick as below:
class MyTable(tag: Tag) extends Table[MyTableRwo](tag, "myTable") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def otherTableIds = column[Seq[Long]]("ids")
}
The other table looks like:
class MyOtherTable(tag: Tag) extends Table[MyOtherTableRow](tag, "myOtherTable") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def myTableId = // this is a foreign key to the MyTable
}
where the def otherTableIds should come from another table called MyOtherTable. MyTable has a one-to-many relationship with the MyOtherTable. What I would want Slick to do is to get only the Id's of the MyOtherTable when I load the MyTable for a given id!
Any clues?

Resources