I am attempting to learn to use Slick to query MySQL. I have the following type of query working to get a single Visit object:
Q.query[(Int,Int), Visit]("""
select * from visit where vistor = ? and location_code = ?
""").firstOption(visitorId,locationCode)
What I would like to know is how can I change the above to query to get a List[Visit] for a collection of Locations...something like this:
val locationCodes = List("loc1","loc2","loc3"...)
Q.query[(Int,Int,List[String]), Visit]("""
select * from visit where vistor = ? and location_code in (?,?,?...)
""").list(visitorId,locationCodes)
Is this possible with Slick?
You can generate in clause automaticly like this:
And use implicit SetParameter as pagoda_5b says
It doesn't work because the
StaticQuery object
(Q
) expects to implicitly set the parameters in the query string, using the type parameters of thequery
method to create a sort of setter object (of typescala.slick.jdbc.SetParameter[T]
).The role of
SetParameter[T]
is to set a query parameter to a value of typeT
, where the required types are taken from thequery[...]
type parameters.From what I see there's no such object defined for
T = List[A]
for a genericA
, and it seems a sensible choice, since you can't actually write a sql query with a dynamic list of parameters for theIN (?, ?, ?,...)
clauseI did an experiment by providing such an implicit value through the following code
with this in scope, you should be able to execute your code
But you must always manually guarantee that the
locationCodes
size is the same as the number of?
in yourIN
clauseIn the end I believe that a cleaner workaround could be created using macros, to generalize on the sequence type. But I'm not sure it would be a wise choice for the framework, given the aforementioned issues with the dynamic nature of the sequence size.
If you have a complex query and the for comprehension mentioned above is not an option, you can do something like the following in Slick 3. But you need to make sure you validate the data in your list query parameter yourself to prevent SQL injection:
The # in front of the variable reference disables the type validation and allows you to solve this without supplying a function for the implicit conversion of the list query parameter.
As the other answer suggests, this is cumbersome to do with static queries. The static query interface requires you to describe the bind parameters as a
Product
.(Int, Int, String*)
is not valid scala, and using(Int,Int,List[String])
needs some kludges as well. Furthermore, having to ensure thatlocationCodes.size
is always equal to the number of(?, ?...)
you have in your query is brittle.In practice, this is not too much of a problem because you want to be using the query monad instead, which is the type-safe and recommended way to use Slick.
This is assuming you have your tables set up like this:
Note that you can always wrap your query in a method.