I'm trying to perform multiple joins in Slick 3.1.1. The result that I would like to achieve is this:
SELECT * FROM customer LEFT JOIN customer_address ON customer.id = customer_address.customer_id LEFT JOIN address ON customer_address.address_id = address.id
I have tried the following:
val query = for {
c <- Customer
ca <- CustomerAddress if ca.customerId === c.id
a <- Address if a.id === ca.addressId
} yield (c, a)
The problem here is that if a customer doesn't have an address that it doesn't appear which makes sense.
Then I tried this:
val query2 = for {
(c, ca, a) <- (Customer joinLeft CustomerAddress on (_.id === _.customerId)) joinLeft Address on (_._2.addressId === _.id)
} yield (c, a)
The problem here is that I get an error on _._2.addressId
because _._2
is a Rep
object.
Exact error:
Error:(21, 110) value addressId is not a member of slick.lifted.Rep[Option[Models.Tables.CustomerAddress]]
(c, ca, a) <- (Customer joinLeft CustomerAddress on (_.id === _.customerId)) joinLeft Address on (_._2.addressId === _.id)
Auto generated Slick Tables class: http://pastebin.com/e4M3cGU8
How can I get the results that I want with Slick?
Slick is a Functional Relational Mapping library for Scala that allows us to query and access a database like other Scala collections. We can write database queries in Scala instead of SQL, thus providing typesafe queries.
Slick currently generates implicit joins in SQL ( select ... from a, b where ...) for monadic joins, and explicit joins ( select ... from a join b on ...) for applicative joins. This is subject to change in future versions.
However, this is not possible in standard SQL, so Slick has to compile them down to applicative joins, which is possible in many useful cases but not in all of them (and there are cases where it is possible in theory but Slick cannot perform the required transformation yet). If a monadic join cannot be properly translated, it will fail at runtime.
A cross-join is created with a flatMap operation on a Query (i.e. by introducing more than one generator in a for-comprehension): If you add a filter expression, it becomes an inner join: The semantics of these monadic joins are the same as when you are using flatMap on Scala collections.
What you have to do is also map the results.
val query2 = for {
((c, ca), a) <- (Customer joinLeft CustomerAddress on (_.id === _.customerId)) joinLeft Address on (_._2.map(_.addressId) === _.id)
} yield (c, a)
This should work. // ((c, ca), a)
instead of (c, ca, a)
val query2 = for {
((c, ca), a) <- (Customer joinLeft CustomerAddress on (_.id === _.customerId)) joinLeft Address on (_._2.addressId === _.id)
} yield (c, a)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With