Alex A.
Alex A.

Reputation: 2603

Slick: ClassCastException: scala.Some cannot be cast to java.lang.String

First, I have to mention I am still pretty new to Slick, and mostly learning and every time discovering some weird cases, like this.

I have a case class Address define for Slick table as follows:

package model

import driver.PGDriver.api._
import format.DateTimeFormat._
import org.joda.time.DateTime
import play.api.libs.json._
import slick.lifted.Tag

case class Address(id: Option[Int] = None,
                   countryId: Option[Int] = None,
                   continentId: Option[Int] = None,
                   stateId: Option[Int] = None,
                   address1: String,
                   address2: Option[String],
                   city: String,
                   zip: String,
                   createdAt: DateTime = DateTime.now,
                   updatedAt: DateTime = DateTime.now,
                   deletedAt: Option[DateTime] = None,
                   createdBy: Option[String] = None,
                   updatedBy: Option[String] = None)

object Addresses {

  val addresses = TableQuery[Addresses]

  implicit lazy val addressFormat: Format[Address] = Json.format[Address]

  Json.toJson[DateTime](DateTime.now)

}

class Addresses(tag: Tag) extends Table[Address](tag, "address") {

  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

  def countryId = column[Option[Int]]("country_id")
  def continentId = column[Option[Int]]("continent_id")
  def stateId = column[Option[Int]]("state_id")

  def address1 = column[String]("address1", O.SqlType("character varying(255)"))
  def address2 = column[Option[String]]("address1", O.SqlType("character varying(255)"))
  def city = column[String]("city", O.SqlType("character varying(255)"))
  def zip = column[String]("zip", O.SqlType("character varying(30)"))

  def createdAt = column[DateTime]("createdat")
  def updatedAt = column[DateTime]("updatedat")
  def deletedAt = column[Option[DateTime]]("deletedat")

  def createdBy = column[Option[String]]("createdby", O.SqlType("character varying(255)"))
  def updatedBy = column[Option[String]]("updatedby", O.SqlType("character varying(255)"))

  override def * =
    (
      id.?,
      countryId,
      continentId,
      stateId,
      address1,
      address2,
      city,
      zip,
      createdAt,
      updatedAt,
      deletedAt,
      createdBy,
      updatedBy
    ) <> (Address.tupled, Address.unapply)

  def addressStateIdIdx = index("address_state_id_idx", stateId)
  def state =
    foreignKey("address_state_id_fkey", stateId, GeoAreas.areas)(
      _.id.?,
      onUpdate = ForeignKeyAction.Cascade,
      onDelete = ForeignKeyAction.Restrict
    )

  def addressCountryIdIdx = index("address_country_id_idx", countryId)
  def country =
    foreignKey("address_country_id_fkey", countryId, GeoAreas.areas)(
      _.id.?,
      onUpdate = ForeignKeyAction.Cascade,
      onDelete = ForeignKeyAction.Restrict
    )

  def addressContinentIdIdx = index("address_continent_id_idx", continentId)
  def continent =
    foreignKey("address_continent_id_fkey", continentId, GeoAreas.areas)(
      _.id.?,
      onUpdate = ForeignKeyAction.Cascade,
      onDelete = ForeignKeyAction.Restrict
    )

}

This is not my only class with works through Slick, but, for some reason, only because of this class I am getting an exception when trying to create a query with join. Like so:

def search(searchCriteria: SearchCriteria,
             readyOnly: Boolean,
             drop: Long = 0,
             take: Long = 100): Future[Seq[(School, Option[Address])]] = {
    val q = for {
      (school, address) <- Schools.schools joinLeft Addresses.addresses on (_.addressId === _.id)
      if List(
        Some(school.deletedAt.isEmpty),
        searchCriteria.school.name.map(n => school.name.toLowerCase like s"%${n.toLowerCase}%"),
        searchCriteria.school.ready.map(r => school.ready === r)
      ).collect({ case Some(criteria) => criteria }).reduceLeftOption(_ && _).getOrElse(true: Rep[Boolean])
    } yield (school, address)

    db.run(q.drop(drop).take(take).result)
  }

The exception I am getting is: play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[ClassCastException: scala.Some cannot be cast to java.lang.String]]

enter image description here

At this particular point, I don't even have a slight idea where to start looking for a problem. This class is very similarly defined to the other classes I have. All others work fine. Everything compiles without a problem. The exception is thrown in runtime only.

What am I missing? Thanks,

UPDATE

I might have found the problem, however, I am not sure how to fix it and why is it happening. The actual query that is getting executed is this:

SELECT
  x2."id",
  x2."address_id",
  x2."name",
  x2."about",
  x2."number_of_students",
  x2."website_url",
  x2."media_id",
  x2."slug",
  x2."short_description",
  x2."ready",
  x2."classrooms",
  x2."year_established",
  x2."display_copyright",
  x2."createdat",
  x2."updatedat",
  x2."deletedat",
  x2."createdby",
  x2."updatedby",
  x2."dli_number",
  (CASE WHEN (x3."id" IS NULL)
    THEN NULL
   ELSE 1 END),
  x3."id",
  x3."country_id",
  x3."continent_id",
  x3."state_id",
  x3."address1",
  x3."address1",
  x3."city",
  x3."zip",
  x3."createdat",
  x3."updatedat",
  x3."deletedat",
  x3."createdby",
  x3."updatedby"
FROM "school" x2 LEFT OUTER JOIN "address" x3 ON x2."address_id" = x3."id"
WHERE ((x2."deletedat" IS NULL) AND (lower(x2."name") LIKE '%a%')) AND (x2."ready" = TRUE)
LIMIT 100
OFFSET 0

There is some weird case as part of the select. Why is it there? what am I doing wrong?

UPDATE #2

From the other hand, not knowing exactly how slick reads the rows of the query, I am not sure if this case statement could cause the original problem.

But, I am still stuck.

UPDATE #3

So, I just tried the simplest query possible that includes only the address table, like so:

def all(): Future[Seq[Address]] = db.run(Addresses.addresses.filter(_.deletedAt.isEmpty).result)

And I am still getting the same exception.

Update #4

Here is the actual address table:

create table address
(
    id bigserial not nullconstraint address_pkey primary key,
    country_id bigint constraint address_country_id_fkey references geo_area on update cascade on delete restrict,
    continent_id bigint constraint address_continent_id_fkey references geo_area on update cascade on delete restrict,
    state_id bigint constraint address_state_id_idx references geo_area on update cascade on delete restrict,
    address1 varchar(255) not null,
    address2 varchar(255),
    city varchar(255) not null,
    zip varchar(30) not null,
    deletedat timestamp with time zone,
    createdat timestamp with time zone not null,
    updatedat timestamp with time zone not null,
    createdby varchar(255),
    updatedby varchar(255)
);

create index address_country_id_idx on address (country_id);

create index address_continent_id_idx on address (continent_id);

create index address_state_id_idx on address (state_id);

Upvotes: 0

Views: 1626

Answers (1)

Alex A.
Alex A.

Reputation: 2603

I had a feeling that it is something really stupid, and I was right. @Tyler, thanks for pointing me in the right direction The problem was here:

def address1 = column[String]("address1", O.SqlType("character varying(255)"))
  def address2 = column[Option[String]]("address1", O.SqlType("character varying(255)"))

Look at how the address2 is defined. these two had duplicate column names. so obviously it was suppose to be:

def address1 = column[String]("address1", O.SqlType("character varying(255)"))
  def address2 = column[Option[String]]("address2", O.SqlType("character varying(255)"))

The exception is, as usual, useless :)

Upvotes: 1

Related Questions