I'm trying to insert some data into my database using Slick. I've successfully been able to query the database, but can't quite understand how to insert data using the documentation examples.
I've gotten to the point where all that's supposedly wrong is that my action
isn't of the correct type and it throws the error
type mismatch;
found : slick.dbio.DBIOAction[Unit,slick.dbio.NoStream,slick.dbio.Effect.Write with slick.dbio.Effect.Schema]
required: slick.dbio.DBIOAction[com.ojolabs.customer.avro.CustomerEvent,slick.dbio.NoStream,Nothing]
db.run(action)
I'm not quite certain how to return the types that are specified as required, using the code I've already written.
I'm calling my schema from here:
trait CustomerEventsComponent {
def customEventsManager: CustomerEvents.Async
}
trait DefaultCustomerEvents extends CustomerEventsComponent{
self: DatabaseComponent with ExecutionContextComponent =>
lazy val customEventsManager = new Async {
override def create(phoneNumber: String, createdAt: DateTime): Future[CustomerEvent] = {
val action = Schema.CustomerEvents.userAction
//this is the line that throws the error
db.run(action)
}
}
}
And am creating the action in here
object Schema {
class CustomerEvents(tag: Tag) extends Table[CustomerEvent](tag, "customer_events") {
def id: Rep[UUID] = column[UUID]("id", O.PrimaryKey)
def customerId: Rep[UUID] = column[UUID]("customer_id")
def eventType: Rep[String] = column[String]("type")
def createdAt: Rep[DateTime] = column[DateTime]("created_at")
def version: Rep[Double] = column[Double]("version")
def data: Rep[JsValue] = column[JsValue]("data")
def metadata: Rep[JsValue] = column[JsValue]("metadata")
def * = (id, customerId, eventType, createdAt, version, data, metadata) <> (CustomerEvent.tupled, CustomerEvent.unapply)
}
object CustomerEvents {
val all = TableQuery[CustomerEvents]
val userAction = DBIO.seq(
all.schema.create,
all += CustomerEvent(
UUID.randomUUID(),
UUID.randomUUID(),
"hello",
DateTime.now(),
1.0,
Json.toJson("{\"hello\" : \"its me\"}"),
Json.toJson("{\"hello\" : \"its me\"}"))
)
}
To keep this answer a little shorter, I'm going to refer to DBIO[T]
, which is an alias in Slick for a DBIOAction[T, NoStream, Effect.All]
The error is saying the compiler expects a DBIO[CustomerEvent]
but found a DBIO[Unit]
. It expects that type because create
is defined to return a Future[CustomerEvent]
(so db.run
should return that).
However, Schema.CustomerEvents.userAction
calls DBIO.seq
. seq
is a way to combine actions and ignore the results. The return type of DBIO.seq
is a DBIO[Unit]
(reference: Scala Doc).
So that's why you see the error: the code is combining actions using a method that throws away the result.
There are a few things you could do about this.
If you really don't want the result of the insert, change the type of create
to be Future[Unit]
If you do want a non-Unit
result, you'll need to switch to a different "combinator" than seq
. I'd suggest andThen
in this case, which combines two actions, keeping the value of the second one. I'll explain that in a moment...
+=
defaults to returning the number of rows affected. That would be a Future[Int]
type on create
if that's what you want.
The userAction
would end up as: all.schema.create andThen (all += ...etc)
Or you could use a for comprehension if you prefer:
for {
_ <- all.schema.create
rowsAffected <- all += ...etc
} yield rowsAffected
(which isn't using andThen
but ends up with the same result).
However, if you want the case class as a result... well, you create the case class, so you could yield
it in that for comprehension example above. Slick also supports returning
and into
as a way of changing the return type of an +=
expresson: it's described in the reference manual.