How to write generic query executor method for delete? - slick

I'm trying to write a generic query executor method. It works fine for insert, update, getList. For delete code did not compile. Is there any way do this?
import scala.slick.driver.JdbcProfile
trait DBComponent {
val profile: JdbcProfile
import profile.simple._
def dbObject(): Database
}
trait QueryExecutor { this: DBComponent =>
import profile.simple._
def execute[T <: Rep[_], F, C[_]](query: Query[T, F, C]): List[F] = dbObject.withSession { implicit session: Session => query.list }
def executeUpdate[T <: Rep[_], F, C[_]](query: Query[T, F, C], value: F): Int = dbObject.withSession { implicit session: Session => query.update(value) }
def executeInsert[T <: Rep[_], F, C[_]](query: Query[T, F, C], value: F): Int = dbObject.withSession { implicit session: Session => query.insert(value) }
def executeDelete[T <: Rep[_], F, C[_]](query: Query[T, F, C]): Int = dbObject.withSession { implicit session: Session => query.delete }*/
//getting error => delete is not a member of QueryExecutor.this.profile.simple.Query[T,F,C]
}

Related

Extracting an interface from an array of keys of an object to perform Pick and Omit

I'm having trouble coming up with the necessary typings for the following two functions:
const pick = (obj, ...names) =>
names.reduce((ret, next) => {
if (typeof obj[next] != 'undefined') ret[next] = obj[next];
return ret;
}, {});
const strip = (obj, ...names) =>
pick(obj, ...Object.keys(obj).filter(key => !names.includes(key)));
I've tried something like the following:
type Picker = <T extends object, U extends (keyof T)[]>(obj: T, ...names: U) =>
Pick<U, T>;
type Stripper = <T extends object, U extends (keyof T)[]>(obj: T, ...names: U) =>
Omit<U, T>;
But those types require passing a type T that extends object, not array of strings. How can I construct a type from Array<keyof T> to pass into Pick and Omit?
After rephrasing the question a couple of times, it occured to me that you could create a union type from an array by using the index accessor:
type Picker = <U extends object, V extends (keyof U)[]>(obj: U, ...keys: V) =>
Pick<U, V[number]>;
type Stripper = <U extends object, V extends (keyof U)[]>(obj: U, ...keys: V) =>
Omit<U, V[number]>;

custom type conversion from string to some object

I want to add conversion to class String for object with type Example.
When I do like this
class Example {
def x = 5
}
class ExampleConversionCategory {
static def support = String.&asType
static Object asType(String self, Class cls) {
if (cls == Example.class) {
"convert"
} else { support(cls) } // argument type mismatch
}
}
String.mixin(ExampleConversionCategory)
def x = "5" as int
println x
I get exception
java.lang.IllegalArgumentException: argument type mismatch
What is the problem? cls has Class type.
You were pretty close...
Notice that the asType method is implemented by the Groovy's String extension class, called StringGroovyMethods.
So the code that works is this one:
import groovy.transform.Canonical
import org.codehaus.groovy.runtime.StringGroovyMethods
#Canonical
class Example {
def x = 5
}
class ExampleConversionCategory {
static final def convert = StringGroovyMethods.&asType
static def asType( String self, Class cls ) {
if ( cls == Example ) new Example( x: 10 )
else convert( self, cls )
}
}
String.mixin( ExampleConversionCategory )
println "5" as int
println 'A' as Example
Which prints:
5
Example(10)

Upper bound for Slick 3.1.1 query type

I have a DAO helper trait that provides common functionality to DAOs. It needs to be able to access the table query, and run actions. I'm having trouble defining or otherwise providing the query type to the helper trait.
Below is some code, also available in a short demo project on GitHub, in the action branch.
First, db is defined in trait DBComponent:
trait DBComponent {
import slick.driver.JdbcProfile
val driver: JdbcProfile
import driver.api._
val db: Database
}
The classes to be persisted extend HasId:
trait HasId {
def id: Option[Int] = None
}
Here is one such class to be persisted:
case class BankInfo(
owner: String,
branches: Int,
bankId: Int,
override val id: Option[Int] = None
) extends HasId
The problem is that I don't know how to set QueryType in the following DAO helper trait; I expect that most of the errors that follow are a consequence of the improper type that I used:
/** Handles all actions pertaining to HasId or that do not require parameters */
trait DbAction[T <: HasId] { this: DBComponent =>
import driver.api._ // defines DBIOAction
type QueryType <: slick.lifted.TableQuery[Table[T]] // this type is wrong! What should it be?
def tableQuery: QueryType
// Is this defined correctly?
def run[R](action: DBIOAction[R, NoStream, Nothing]): Future[R] = db.run { action }
def deleteById(id: Option[Long]): Unit =
for { i <- id } run { tableQuery.filter(_.id === id).delete } // id is unknown because QueryType is wrong
def findAll: Future[List[T]] = run { tableQuery.to[List].result } // also b0rked
// Remaining methods shown on GitHub
}
FYI, here is how the above will be used. First, the trait that defines the table query:
trait BankInfoTable extends BankTable { this: DBComponent =>
import driver.api._
class BankInfoTable(tag: Tag) extends Table[BankInfo](tag, "bankinfo") {
val id = column[Int]("id", O.PrimaryKey, O.AutoInc)
val owner = column[String]("owner")
val bankId = column[Int]("bank_id")
val branches = column[Int]("branches")
def bankFK = foreignKey("bank_product_fk", bankId, bankTableQuery)(_.id)
def * = (owner, branches, bankId, id.?) <> (BankInfo.tupled, BankInfo.unapply)
}
val tableQuery = TableQuery[BankInfoTable]
def autoInc = tableQuery returning tableQuery.map(_.id)
}
It all comes together here:
trait BankInfoRepositoryLike extends BankInfoTable with DbAction[BankInfo]
{ this: DBComponent =>
import driver.api._
#inline def updateAsync(bankInfo: BankInfo): Future[Int] =
run { tableQuery.filter(_.id === bankInfo.id.get).update(bankInfo) }
#inline def getByIdAsync(id: Int): Future[Option[BankInfo]] =
run { tableQuery.filter(_.id === id).result.headOption }
}
Suggestions?
Full working example:
package com.knol.db.repo
import com.knol.db.connection.DBComponent
import com.knol.db.connection.MySqlDBComponent
import scala.concurrent.{Await, Future}
import concurrent.duration.Duration
trait LiftedHasId {
def id: slick.lifted.Rep[Int]
}
trait HasId {
def id: Option[Int]
}
trait GenericAction[T <: HasId]{this: DBComponent =>
import driver.api._
type QueryType <: slick.lifted.TableQuery[_ <: Table[T] with LiftedHasId]
val tableQuery: QueryType
#inline def deleteAsync(id: Int): Future[Int] = db.run { tableQuery.filter(_.id === id).delete }
#inline def delete(id: Int): Int = Await.result(deleteAsync(id), Duration.Inf)
#inline def deleteAllAsync(): Future[Int] = db.run { tableQuery.delete }
#inline def deleteAll(): Int = Await.result(deleteAllAsync(), Duration.Inf)
#inline def getAllAsync: Future[List[T]] = db.run { tableQuery.to[List].result }
#inline def getAll: List[T] = Await.result(getAllAsync, Duration.Inf)
#inline def getByIdAsync(id: Int): Future[Option[T]] =
db.run { tableQuery.filter(_.id === id).result.headOption }
#inline def getById(id: Int): Option[T] = Await.result(getByIdAsync(id), Duration.Inf)
#inline def deleteById(id: Option[Int]): Unit =
db.run { tableQuery.filter(_.id === id).delete }
#inline def findAll: Future[List[T]] = db.run { tableQuery.to[List].result }
}
trait BankInfoRepository extends BankInfoTable with GenericAction[BankInfo] { this: DBComponent =>
import driver.api._
type QueryType = TableQuery[BankInfoTable]
val tableQuery=bankInfoTableQuery
def create(bankInfo: BankInfo): Future[Int] = db.run { bankTableInfoAutoInc += bankInfo }
def update(bankInfo: BankInfo): Future[Int] = db.run { bankInfoTableQuery.filter(_.id === bankInfo.id.get).update(bankInfo) }
/**
* Get bank and info using foreign key relationship
*/
def getBankWithInfo(): Future[List[(Bank, BankInfo)]] =
db.run {
(for {
info <- bankInfoTableQuery
bank <- info.bank
} yield (bank, info)).to[List].result
}
/**
* Get all bank and their info.It is possible some bank do not have their product
*/
def getAllBankWithInfo(): Future[List[(Bank, Option[BankInfo])]] =
db.run {
bankTableQuery.joinLeft(bankInfoTableQuery).on(_.id === _.bankId).to[List].result
}
}
private[repo] trait BankInfoTable extends BankTable{ this: DBComponent =>
import driver.api._
class BankInfoTable(tag: Tag) extends Table[BankInfo](tag, "bankinfo") with LiftedHasId {
val id = column[Int]("id", O.PrimaryKey, O.AutoInc)
val owner = column[String]("owner")
val bankId = column[Int]("bank_id")
val branches = column[Int]("branches")
def bank = foreignKey("bank_product_fk", bankId, bankTableQuery)(_.id)
def * = (owner, branches, bankId, id.?) <> (BankInfo.tupled, BankInfo.unapply)
}
protected val bankInfoTableQuery = TableQuery[BankInfoTable]
protected def bankTableInfoAutoInc = bankInfoTableQuery returning bankInfoTableQuery.map(_.id)
}
object BankInfoRepository extends BankInfoRepository with MySqlDBComponent
case class BankInfo(owner: String, branches: Int, bankId: Int, id: Option[Int] = None) extends HasId
You're trying to abstract over the result type with HasId but your code doesn't actually care about that. The id values that you're using are the ones from the lifted type, i.e. the table row class, so you need an abstraction at this level:
trait LiftedHasId {
def id: slick.lifted.Rep[Int]
}
Then in DbAction:
type QueryType <: slick.lifted.TableQuery[_ <: Table[T] with LiftedHasId]
And BankInfoTable must define a concrete type for it:
type QueryType = slick.lifted.TableQuery[BankInfoTable]
Or you could add it as a second type parameter to DbAction (just like Query has two type parameters for the lifted type and the result type).

Call method multiple times with each member of a collection as parameter

Let's say that I have a collection of parameters
def params = ['a','b','c']
Is there a short way to run a method that accepts a single parameter once for every element of a collection to replace this:
params.each {
foo(it)
}
with something more declarative (like a "reverse" spread operator)?
You can use collect:
def params = ['a','b','c']
def foo(param) {
'foo-' + param
}
assert ['foo-a', 'foo-b', 'foo-c'] == params.collect { foo(it) }
Or just a closure
def foo = { a -> a + 2 }
def modified = list.collect foo
You can use method pointer:
def l = [1,2,3]
l.each(new A().&lol)
class A {
def lol(l) {
println l
}
}
Or add a method that will do the task you need:
def l = [1,2,3]
List.metaClass.all = { c ->
delegate.collect(c)
}
l.all(new A().&lol)
class A {
def lol(l) {
println l
return l+2
}
}

Coercion befuddlement in Groovy

Why does the following
class Test {
#Test
void go() {
def foo1 = new MockFoo1() as Foo
def foo2 = new MockFoo2() as Foo
}
interface Foo {}
class MockFoo1 {}
class MockFoo2 {}
}
Result in a java.lang.IllegalArgumentException: argument type mismatch on the foo2 coercion?
This only happens if I coerce 2 objects of 2 different types to the same interface during a single path of execution. The groovy approved way of using closures or maps to achieve this kind of duck typing works fine.
Any light shed appreciated.
It's a bug with the ProxyGenerator adapterCache. As a workaround, you can also use some Groovy trickery to make this work:
interface Foo {
static a = {
[MockFoo1, MockFoo2].each {
it.metaClass.asType = { Class klazz ->
try {
DefaultGroovyMethods.asType(delegate, klazz)
} catch (e) {
def cache = ProxyGenerator.INSTANCE.#adapterCache.#cache
cache.each { k, v ->
cache.remove(k)
}
DefaultGroovyMethods.asType(delegate, klazz)
}
}
}
}()
}
class MockFoo1 {}
class MockFoo2 {}
def a = new MockFoo1() as Foo
def b = new MockFoo2() as Foo
assert a instanceof Foo
assert b instanceof Foo
Hope this helps!

Resources