1
votes

I am using Slick to map case class to tables. In certain case classes, I need to have an additional field which is not there in the database table. This field value will be updated based on some business conditions. However, I am not able to do that since, the case class and slick table needs to have the same fields.

In java with Hibernate, the same was achieved using @Transient annotation. How can I solve this issue ? I tied by adding @transient annotation to the case class field. But, the slick mapping is showing compilation errors.

4
Crosslink your double posts in the future! groups.google.com/d/msgid/scalaquery/… - cvogt

4 Answers

4
votes

How about this?

case class Container(name: String, width: Float, height: Float, isApproved: Boolean)

val c: Container = ...
val approvedContainer = c.copy(isApproved=true)

And in Slick

...
def * = (name, width, height, false) <> (Container.tupled, Container.unapply)
...

I haven't tried it and there is a chance you run into insertion problems with the above, which you may be able to work around or not.

Another way to do it:

case class Container(name: String, width: Float, height: Float)(val isApproved: Boolean)
object Container{
  def applyFunction = (Container.apply _).tupled
  def approve(c: Container) = applyFunction(Container.unapply(c).get)(true)
  def createNonApproved = (t: (String, Float, Float)) => applyFunction(t)(false)
}

And in Slick

...
def * = (name, width, height, false) <> (Container.createNonApproved, Container.unapply)
...

Be aware that equality of case classes only compares the first argument list.

scala> Container("foo",1,2)()
res2: Container = Container(foo,1.0,2.0)

scala> Container.approve(res2)
res3: Container = Container(foo,1.0,2.0)

scala> res2 == res3
res4: Boolean = true

scala> res2.isApproved
res5: Boolean = false

scala> res3.isApproved
res6: Boolean = true

Be aware of https://issues.scala-lang.org/browse/SI-3664

1
votes

Have you tried this?

case class Container(name: String, width: Float, height: Float, condition: Boolean)

class Containers(tag: Tag) extends Table[Container](tag, "CONTAINERS") {
  def name = column[String]("NAME", O.PrimaryKey)
  def width = column[Float]("WIDTH")
  def height = column[Float]("HEIGHT")

  def applyBusinessCondition(): Boolean = ...

  def * = (name, width, height, applyBusinessCondition()) <> (Container.tupled, Container.unapply)
}

I think you could use a function inside or outside the class Containers and call it in the * projection of the Table. You could too set the condition to False inside the Table definition and change it afterwards.

1
votes

Have the same problem, solved with this code:

case class Container(name: String, width: Float, height: Float, isApproved: Boolean)

class Containers(tag: Tag) extends Table[Container](tag, "CONTAINERS") {

  def name = column[String]("NAME", O.PrimaryKey)
  def width = column[Float]("WIDTH")
  def height = column[Float]("HEIGHT")

  def create = (name: String, width: Float, height: Float) =>
    Container(name, width, height, isApproved = false)

  def destroy(container: Container) =
    Some((container.name, container.width, container.height))

  def * = (name, width, height) <> (create.tupled, destroy)
}
0
votes

I haven't tried @jvican solution. I will try that. But I solved it in another way without changing the fields of case classes and slick mapping tables.

I created a trait

trait Approvable[TId <:AnyVal]{
  var isApproved: Boolean = false
}

Then I mixed in my case class with Approvable trait. After the business condition is checked, i update the isApproved field with true/false.