Reputation: 12429
I'm trying to filter my table with:
val applicationId = ...
val application = Applications.filter(_.id === applicationId).first
where id
and applicationId
are UUIDs.
I was getting an error that:
scala.slick.SlickException: UUID does not support a literal representation
I found that I need to use bind
:
// val applicationId is a UUID
val application = Applications.filter(_.id === applicationId.bind).first
However, this is throwing an exception
java.util.NoSuchElementException: Invoker.first
even though I am querying with a UUID that I know is in the table
This is the selectStatement
that Slick is producing. I'm not sure why it doesn't include the UUID?
select x2."Id" from "Application" x2 where x2."Id" = ?
Upvotes: 3
Views: 2618
Reputation: 8337
I've been struggling with this today. I don't know if I have a good solution, but I can give a few options.
I'm using MySQL and that database supports a syntax to send byte data as a string:
mysql> desc user;
+---------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| user_uuid | binary(16) | NO | PRI | NULL | |
| email | varchar(254) | NO | UNI | NULL | |
| display_name | varchar(254) | NO | | NULL | |
| password_hash | varchar(254) | YES | | NULL | |
+---------------+--------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
mysql> select * from user where user_uuid = x'e4d369040f044b6e9561765c356907d3';
+------------------+-----------------+--------------+---------------+
| user_uuid | email | display_name | password_hash |
+------------------+-----------------+--------------+---------------+
| ???????????? | [email protected] | Foo | NULL |
+------------------+-----------------+--------------+---------------+
1 row in set (0.00 sec)
To show that the UUID is correct:
mysql> select hex(user_uuid), email, display_name from user where user_uuid = x'e4d369040f044b6e9561765c356907d3';
+----------------------------------+-----------------+--------------+
| hex(user_uuid) | email | display_name |
+----------------------------------+-----------------+--------------+
| E4D369040F044B6E9561765C356907D3 | [email protected] | Foo |
+----------------------------------+-----------------+--------------+
On that basis, the following patch to MySQLDriver.scala
(version 1.0.1) works nicely:
$ git diff
diff --git a/src/main/scala/scala/slick/driver/MySQLDriver.scala b/src/main/scala/scala/slick/driver/
index 84a667e..1aa5cca 100644
--- a/src/main/scala/scala/slick/driver/MySQLDriver.scala
+++ b/src/main/scala/scala/slick/driver/MySQLDriver.scala
@@ -174,9 +174,15 @@ trait MySQLDriver extends ExtendedDriver { driver =>
}
}
+ import java.util.UUID
+
override val uuidTypeMapperDelegate = new UUIDTypeMapperDelegate {
override def sqlType = java.sql.Types.BINARY
override def sqlTypeName = "BINARY(16)"
+
+ override def valueToSQLLiteral(value: UUID): String =
+ "x'"+value.toString.replace("-", "")+"'"
}
}
}
With that you can do code like:
def findUserByUuid(uuid: UUID): Option[UserRecord] = db.withSession {
// Note: for this to work with slick 1.0.1 requires a tweak to MySQLDriver.scala
// If you don't, the "===" in the next line will fail with:
// "UUID does not support a literal representation".
val query = (for (u <- UserRecordTable if u.uuid === uuid) yield u)
query.firstOption
}
I'm not sure which database you're using but a similar approach may work. I haven't performance tested any of this.
There are a few options to avoid the slick code patch.
Using the UUIDHelper from http://nineofclouds.blogspot.com.au/2013/04/storing-uuids-with-anorm.html, you can encode the UUID into a byte array and do a static query. Somewhat inelegant but works like:
import UuidHelper
implicit object SetUUID extends SetParameter[UUID] {
def apply(v: UUID, pp: PositionedParameters) { pp.setBytes(UuidHelper.toByteArray(v)) }
}
implicit val getUserRecordResult = GetResult(r =>
UserRecord(UuidHelper.fromByteArray(r.nextBytes()), r.<<, r.<<, r.<<)
)
val userByUuid = StaticQuery[UUID, UserRecord] + "select * from user where user_uuid = ?"
val user = userByUuid(user.uuid).first
// do what you want with user.
The below is probably the least elegant, but included for completeness:
implicit val getUserRecordResult = GetResult(r => UserRecord(UuidHelper.fromByteArray(r.nextBytes()), r.<<, r.<<, r.<<))
val uuid = user.uuid.toString.replace("-", "")
val userByUuid = StaticQuery.queryNA[UserRecord](s"select * from user where user_uuid = x'$uuid'")
val user = userByUuid().first
HTH
EDIT: a pull request against master below. Note that the pull request is against master (2.0.0), but I tested it successfully only against 1.0.1.
https://github.com/slick/slick/pull/240
Upvotes: 3