Need to update table in slick 3.0.2 while using scala/playframework.
form.fold (
errForm => {
BadRequest();
},
data => {
** update each column of the table after running db.run
}
)
suppose your form object has 3 fields a, b, and c. then your update statement will look like
val query = db_user_info.filter(_.username === data.username).map(r => (r.a, r.b, r.c)).update(form.a, form.b, form.c)
Related
I'm currently facing an issue with my update query in my scala-slick3 project. I have a Report-Class, which contains multiple Products and each Product contains multiple Parts. I want to implement a function that marks every Part of every Product within this Report as assessed.
I thought about doing something like this:
def markProductPartsForReportAsAssessed(reportId: Int) = {
val query = for {
(products, parts) <- (report_product_query filter(_.reportId === reportId)
join (part_query filter(_.isAssessed === false))
on (_.productId === _.productId))
} yield parts.isAssessed
db.run(query.update(true))
}
Now, when I run this code slick throws this exception:
SlickException: A query for an UPDATE statement must resolve to a comprehension with a single table.
I already looked at similiar problems of which their solutions (like this or this) weren't really satisfying to me.
Why does slick throw this excpetion or why is it a problem to begin with? I was under the impression that my yield already takes care of not "updating multiple tables".
Thanks in advance!
I guess it's because the UPDATE query requires just one table. If you write SQL for the above query, it can be
UPDATE parts a SET isAccessed = 'true'
WHERE a.isAccessed = 'false' and
exists(select 'x' from products b
where a.productId = b.producId and b.reportId = reportId)
Therefore, you can put conditions related with 'Product' table in the filter as follows.
val reportId = "123" // some variable
val subQuery = (reportId:Rep[String], productId:Rep[String]) =>
report_product_query.filter(r => r.report_id === reportId && r.product_id === productId)
val query = part_query.filter(p => p.isAccesssed === false:Rep[Boolean] &&
subQuery(reportId, p.productId).exists).map(_.isAccessed)
db.run(query.update(true))
I have a database table InventoryLotUsage which has columns id, inventoryLotId, and date. I want to delete an InventoryLot, but before I can do that I need to update the InventoryLotUsage rows that have a foreign key inventoryLotId, based on date and some other conditions.
My question is how do I query for data using monadic joins, do some computations on it and use the result of those computations to run an update all in one transaction in Slick?
I was attempting to get a sequence of rows like this
for {
il <- InventoryLot.filter(_.id === id)
lotUsage <- InventoryLotUsage.filter(_.inventoryLotId === id).result
groupedUsage = lotUsage.groupBy(_.date)
...
}
my IDE suggests that lotUsage will be a Seq[InventoryLotUsageRows], but when compiling I get a type error because of the .result.
type mismatch;
found : slick.dbio.DBIOAction[FacilityProductRepository.this.InventoryLot,slick.dbio.NoStream,slick.dbio.Effect.Read with slick.dbio.Effect.Read with slick.dbio.Effect.Read]
required: slick.lifted.Query[?,?,?]
lotUsage <- InventoryLotUsage.filter(_.inventoryLotId === id).result
Without using .result its type is the InventoryLotUsage table. How can I wrangle the query into something usable for computation?
You need to compose DBIOActions to archive desired result. For example:
Load all data that you need
val loadStaffAction = (for {
il <- InventoryLot.filter(_.id === id)
lotUsage <- InventoryLotUsage.filter(_.inventoryLotId === id)
} yield (il, lotUsage)).result
Then you could use map/flatMap on loadStaffAction to create update statements based on computations. You can also use for-comprehensions here.
val updateAction = loadStaffAction.map { result =>
// creating update statements based on some computations and conditions
DBIO.seq(
InventoryLotUsage.filter(_.id === inventory1.id).map(_.name).update("new value"),
InventoryLotUsage.filter(_.id === inventory2.id).map(_.name).update("another value"),
);
}
After this you can run all queries in one transaction
db.run(updateAction.transactionally)
I have requirement where i have to insert into book table and based on auto Id generated , I have to insert into bookModules table.For every bookModule autoincrement Id , I have to insert into bookAssoc table. But bookModule is populated using seq[bookMods] and bookAssociation data is populated using Seq[userModRoles].
I have written below code to achieve this but it is only executing action1. My inner actions are not getting executed. Please help me .
val action1 =bookDao.insert(book)
val action2 = action1.map { id => DBIO.sequence(
bookMods.map { bookMod =>
bookModDao.insert(new bookModule(None, id, bookMod.moduleId, bookMod.isActive))
.map { bookModId =>
userModRoles.map { userModRole =>
bookAssocDao.insert(new bookAssociation(None, bookModId, userModRole.moduleId, userModRole.roleId))
}
}
})
}
db.run(action2.transactionally)
EDIT 1: Adding code in for comphrension
val action1 = for{
bookId<-bookDao.insert(book) // db transaction
bookMod<-bookModules// this is scala collection // Iterate each element and Insert into tables
bookModId<-bookModDao.insert(new bookModule(None, bookId, bookMod.moduleId, bookMod.isActive))
userModRole<-userModRoles //// this is scala collection // Iterate each element and Insert into tables
bookAssocDao.insert(new bookAssociation(None, bookModId, userModRole.moduleId, userModRole.roleId))
}yield()
db.run(action2.transactionally)
You need to split your logic into two parts.
1) DBIO
2) iterates over collections.
In that case, a solution should be easy. But without using for comprehension.
bookModules.map{ bookMod =>
userModRoles.map{ userModRole =>
db.run(bookDao.insert(book).flatMap{ bookId =>
bookModDao.inser(new bookModule(None, bookId, bookMod.moduleId, bookMod.isActive)).map{ bookModId =>
bookAssocDao.insert(new bookAssociation(None, bookModId, userModRole.moduleId, userModRole.roleId))
}
}).transactionally
}
}
Try something like this. It should work. You can think about move db.run to Dao classes. And here, probably it's service you should work on Futures.
And sorry if I make some mistake with parenthesis, but here it's difficult to have everything clear :)
i have this code !!
this code for insert multiple row..
def insertDocSetting(data: List[ModelDocumentSetting]) = DocumentSettingTable ++= data
and this is for update multiple row!!
def updateDocSetting(data: Seq[ModelDocumentSetting])= {
for (a <- data){
DocumentSettingTable.filter(_.doc_proc_list_id === a.doc_proc_list_id).update(a)
}
}
but ,i have problem to get the result..
how to create slick update multiple row
You will need to map the result of your filter into a tuple before you can update it.
You can check how to do it in the documentation. But it will be something like:
def updateDocSetting(data: Seq[ModelDocumentSetting])= {
for (a <- data){
DocumentSettingTable
.filter(_.doc_proc_list_id === a.doc_proc_list_id)
.map(doc => (doc.element1, doc.element2))
.update(("new element1", "new element2"))
}
}
I'm trying to select ALL of some table in a query. I have to specify a where clause (or so I tihnk), so what goes in there?
def all() = transaction { from(AppDB.users)(s => where(WHAT GOES HERE?) select(s)).toIndexedSeq }
This should do it.
from(AppDB.users)(s => select(s))