'Scala Slick subqueries and returns
I'm new to Scala with Slick library, I've work just with the Spark framework which is really strightforward with SQL sentences, and I find Slick really hard to understand. My problem is the following, I have 4 methods that are going to use almost the same SQL sentence:
Method One (getListOne(QueryToJoin: ??): List[String]) is going to use:
SELECT DISTINCT(tr.trx_cashier) FROM trxRepo tr JOIN (
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
) trs ON (tr.trx_id = trs.trx_id) WHERE trx_type IN ('sell', 'refund')
Method two (getListTwo(QueryToJoin: ??): List[Int]) is going to use:
SELECT DISTINCT(tr.trxpaymethodid) FROM trxRepo tr JOIN (
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
) trs ON (tr.trx_id = trs.trx_id) WHERE trx_type IN ('sell', 'refund')
Method three (getAllTrxs(QueryToJoin: ?): Future[Seq(Trx)]?) is going to use:
SELECT tr.* FROM trxRepo tr JOIN (
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
) trs ON (tr.trx_id = trs.trx_id) WHERE trx_type IN ('sell', 'refund')
Method four (getAllTrxs(QueryToJoin: ?, DTOLists): Future[Seq(Trx)]?) is going to use:
SELECT tr.* FROM trxRepo tr JOIN (
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
) trs ON (tr.trx_id = trs.trx_id) WHERE trx_type IN ('sell', 'refund')
AND tr.trx_cashier IN (...DTOLists.ListOne...) AND tr.trxpaymethodid IN (...DTOLists.Listtwo...)
As you can see it uses
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
For every method and I don't want to write four methods with the same sentence. And to be honest, I haven't figure out how to write the previous sentences on Scala with Slick.
The following is a diagram with the classes and relations I've got so far:
https://drive.google.com/file/d/11lAqxd7JNUBtv4LgnDYmjKiHzmHqEP8t/view?usp=sharing
My classes code according to the diagram are:
Class TrxService which is the only who is supposed to access the TrxRepository
class TrxService @Inject()(
trxRepository: TrxRepository, trxStateService: TrxStateService
)(implicit executionContext: ExecutionContext) {
def getListsOne()(implicit mapMarkerContext: MapMarkerContext):
Future[DTOLists] = {
val trxState = trxStateService.getAllTrxState()
DTOLists(
transactionRepository.getListOne(trxState),
transactionRepository.getListTwo(trxState)
)
}
}
Class TrxStateService which is the only who is supposed to access the TrxStateRepository
class TrxStateService @Inject()(trxStateRepository: TrxStateRepository) {
def getAllTrxState()(implicit mapMarkerContext: MapMarkerContext)
: Future[Seq[TrxState]] = {
trxStateRepository.getAllTrxState()
}
}
Class TrxRepository which is the only who is supposed to access the data from the database for Trx table
@Singleton
class TrxRepository @Inject()(override val dbConfigProvider: DatabaseConfigProvider)(implicit override val ec: ExecutionContext)
extends BaseRepository[Transaction]
with QrApplicationErrorHandler
with Logging {
private val trxs = TableQuery[TrxTable]
def getAllTxByLists(dtoLists: DTOLists)
(implicit mapMarkerContext: MapMarkerContext): Future[Seq[Transaction]] = {
db.run(
trxs.filter(trxs => trx.terminalNumber == dtoLists.listOne)
.filter(trxs => trx.paymentMethodId == dtoLists.listTwo).result
)
}
def getAllTrx(trxState: Seq[TrxState])(implicit mapMarkerContext: MapMarkerContext): Future[Seq[Transaction]] = {
val query = sql"""SELECT tr.* FROM trxRepo tr JOIN (
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
) trs ON (tr.trx_id = trs.trx_id) WHERE trx_type IN ('sell', 'refund')""".as[Seq[Transaction]]
db.run(query)
}
def getListOne(trxState: Seq[TrxState])(implicit mapMarkerContext: MapMarkerContext): Future[Seq[Int]] = {
/*db.run(
trxs.join(trxState).on(_.trxId = _.trxId)
.filter(trx => trx.trxType === "sell" || trx.trxType === "refund").map(_.trxCashier)
.result
.map(_.toList)
)
*/
db.run(sql"""SELECT tr.trx_cashier FROM trxRepo tr JOIN (
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
) trs ON (tr.trx_id = trs.trx_id) WHERE trx_type IN ('sell', 'refund')""".as(Seq[Int])
)
}
def getListTwo(trxState: Seq[TrxState])(implicit mapMarkerContext: MapMarkerContext): Future[Seq[Int]] = {
/*db.run(
trxs.join(trxState).on(_.trxId = _.trxId)
.filter(trx => trx.trxType === "sell" || trx.trxType === "refund").map(_.trxCashier)
.result
.map(_.toList)
)
*/
db.run(sql"""SELECT tr.trxpaymethodid FROM trxRepo tr JOIN (
SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'
) trs ON (tr.trx_id = trs.trx_id) WHERE trx_type IN ('sell', 'refund')""".as(Seq[String])
)
}
private class TrxTable(tag: Tag) extends Table[Trx](tag, "TrxRepo") {
def trxId = column[Long]("trx_id", O.PrimaryKey)
def trxType = column[String]("trx_type")
def trxPatMethodId = column[Int]("trxpaymethodid")
def trxCashier = column[String]("trx_cashier")
def * =
(
trxId,
trxType,
trxPatMethodId,
trxCashier
) <> (Trx.tupled, Trx.unapply)
}
}
Class TrxStateRepository which is the only who is supposed to access the data from the database for trxState table
@Singleton
class TrxStateRepository @Inject()(override val dbConfigProvider: DatabaseConfigProvider
)(implicit val ec: ExecutionContext)extends BaseRepository[TransactionStatus]
with QrApplicationErrorHandler with Logging {
private val trxState = TableQuery[TransactionStatusTable]
def getLastStatusForTrx(trxId: Long)(implicit mapMarkerContext: MapMarkerContext)
: Future[TransactionStatus] = {
trxState.filter(_.trxId === trxId).sortBy(_.sequence.desc).result.headOption
}
def getAllTrxState()(implicit mapMarkerContext: MapMarkerContext)
: Future[Seq[TrxState]] = {
logger.info(EnumLogger.LastStatusTxsForClosure.toString)
val sequence = (trxState.map(_.sequence).max.asColumnOf[Int]).asColumnOf[Int]
/*
trxState.sortBy(_.sequence)
.filter(trxStates => trxState.status === "open")
*/
for {
t1 <- trxState
if !(trxState.filter(t2 => t1.trxId === t2.trxId).map(_.sequence).max)
} yield t1
/*
db.run(sql"""SELECT ts.* FROM (
SELECT cp.* FROM trxstate
WHERE cp.sequence = (SELECT MAX(sc.sequence) FROM trxstate sc WHERE sc.trx_id = cp.trx_id)
) ts WHERE ts.status = 'open'""".as(Seq[String]))
*/
}
private class TrxStateTable(tag: Tag) extends Table[TrxState](tag, "transaction_status") {
def trxStateId: Rep[Long] = column[Long]("trx_state_id", O.PrimaryKey, O.AutoInc)
def trxId: Rep[Long] = column[Long]("trx_id")
def sequence: Rep[Int] = column[Int]("sequence")
def status: Rep[String] = column[String]("status")
def * =
(
trsxStateId.?,
trxId,
sequence,
status
) <> (TrxState.tupled, TrxState.unapply)
}
}
DTOLists that is the DTO to return
case class DTOLists(
listOne: List[String],
listTwo: List[Int]
)
I couldn't create the query using Slick so I tried to "hardcode" the sentence for every method using Plain SQL, but it generated an error always like Mismatch with the return value.
How can I create these queries with Slick and the return types to make a join with the result of others methods? I tried with Query[], Seq, TableQuery, Future[Seq[]] but I couldn't make it.
I really want the code to be extensible.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
