2

Looking for a way to do a batch update using slick. Is there an equivalent updateAll to insertALL? Goole research has failed me thus far.

I have a list of case classes that have varying status. Each one having a different numeric value so I cannot run the typical update query. At the same time, I want to save the multiple update requests as there could be thousands of records I want to update at the same time.

critium
  • 612
  • 5
  • 16
  • There is an open ticket that I believe intends to solve your problem: https://github.com/slick/slick/issues/1015 – Majki Jun 02 '15 at 08:21

2 Answers2

2

Sorry to answer my own question, but what i ended up doing is just dropping down to JDBC and doing batchUpdate.

private def batchUpdateQuery = "update table set value = ? where id = ?"

/**
  * Dropping to jdbc b/c slick doesnt support this batched update
  */
def batchUpate(batch:List[MyCaseClass])(implicit subject:Subject, session:Session) = {
  val pstmt = session.conn.prepareStatement(batchUpdateQuery)

  batch map { myCaseClass =>
    pstmt.setString(1, myCaseClass.value)
    pstmt.setString(2, myCaseClass.id)
    pstmt.addBatch()
  }

  session.withTransaction {
    pstmt.executeBatch()
  }
}
randbw
  • 490
  • 5
  • 13
critium
  • 612
  • 5
  • 16
  • Dont think you need the transaction boundary here. I used it b/c this was originally running 3 batches at once. – critium Sep 04 '14 at 21:24
1

It's not clear to me what you are trying to achieve, insert and update are two different operation, for insert makes sense to have a bulk function, for update it doesn't in my opinion, in fact in SQL you can just write something like this

UPDATE
  SomeTable
SET SomeColumn = SomeValue
WHERE AnotherColumn = AnotherValue

Which translates to update SomeColumn with the value SomeValue for all the rows which have AnotherColumn equal to AnotherValue.

In Slick this is a simple filter combined with map and update

table
  .filter(_.someCulomn === someValue)
  .map(_.FieldToUpdate)
  .update(NewValue)

If instead you want to update the whole row just drop the map and pass a Row object to the update function.

Edit:

If you want to update different case classes I'm lead to think that these case classes are rows defined in your schema and if that's the case you can pass them directly to the update function since it's so defined:

def update(value: T)(implicit session: Backend#Session): Int

For the second problem I can't suggest you a solution, looking at the JdbcInvokerComponent trait it looks like the update function invokes the execute method immediately

def update(value: T)(implicit session: Backend#Session): Int = session.withPreparedStatement(updateStatement) { st =>
  st.clearParameters
  val pp = new PositionedParameters(st)
  converter.set(value, pp, true)
  sres.setter(pp, param)
  st.executeUpdate
}

Probably because you can actually run one update query at the time per table and not multiple update on multiple tables as stated also on this SO question, but you could of course update multiple rows on the same table.

Community
  • 1
  • 1
Ende Neu
  • 15,581
  • 5
  • 57
  • 68
  • but wouldnt it still run one just update per session? – critium Jul 23 '14 at 19:44
  • Sorry, should have made it clear in the edit, I can't be 100% sure but I believe it will execute one update per function call (not session, the session is opened until you close it). Anyway this is a bit deep into `Slick` and I would wait for somebody from the slick team to see the question and add their option to it, could be there's something going on I overlooked. – Ende Neu Jul 23 '14 at 23:10