Skip to content

CRUD trait for Slick 2.0

December 11, 2013

This post was similar with what I’ve used in the past months before upgrading Slick to 2.0. From this version, some refactoring were needed.

For each of my case class tables I have a RichTable abstract class from which I extend in order to not repeat id column definition for all the tables.


abstract class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
 def id = column[Int]("id", O.PrimaryKey)

and an example of a case class to describe table is:

class Locations(tag:Tag) extends RichTable[LocationRow](tag, "location") with Logging {
def description = column[String]("description")
def addressId = column[Int]("address_id")
def partyId = column[Int]("party_id")
def contactPersonId = column[Int]("contact_person_id", O.Nullable)
def mainLocation = column[Boolean]("main_location")

//fks
def address = foreignKey("fk_address", addressId, Addresses.tableQuery)(_.id)
def contactPerson = foreignKey("fk_contact_person", contactPersonId, Users.tableQuery)(_.id)
def party = foreignKey("fk_party", partyId, Parties.tableQuery)(_.id)

def * = (id.?, description, addressId, partyId, contactPersonId.?, mainLocation) <> (LocationRow.tupled, LocationRow.unapply _)
}

As you noticed in documentation, we have to use TableQuery of type of our case class so I define this in a companion object of the above case class:

object Locations extends GenericTableUtils[Locations, LocationRow] with Logging {

val tableQuery = TableQuery[Locations]

/**
* Override insert in order to generate id dynamically. Return id of inserted entity.
*/
def insert(model: LocationRow)(implicit db: Session): Int = {
model.id match {
case None => {
val generatedId = getNextId(classOf[LocationRow].getSimpleName())
tableQuery += model.copy(id = Some(generatedId))
generatedId
}
case Some(id) => {
tableQuery += model
id
}
}
}

// other specific methods

}

and here it comes my generic trait:

trait GenericTableUtils[T <: RichTable[A], A] {

val tableQuery: TableQuery[T, T#TableElementType]

/**
 * To dynamically generate ids for insert, use sequences (they are created when schema is created).
 */
 def getNextId(seqName: String)(implicit session: Session) =
 (StaticQuery[Int] + "select nextval('" + seqName + "_seq') ").first

/**
 * Find a specific entity by id.
 */
 def findById(id: Int)(implicit session: Session): Option[A] = {
 val byId = tableQuery.findBy(_.id)
 byId(id).list.headOption
 }

/**
 * Delete a specific entity by id. If successfully completed return true, else false
 */
 def delete(id: Int)(implicit session: Session): Boolean =
 findById(id) match {
 case Some(entity) => { tableQuery.where(_.id === id).delete; true }
 case None => false
 }

/**
 * Update a specific entity by id. If successfully completed return true, else false
 */
 def update(id: Int, entity: A)(implicit session: Session): Boolean = {
 findById(id) match {
 case Some(e) => { tableQuery.where(_.id === id).update(entity); true }
 case None => false
 }
 }
 }

Insert method is more tricky to move it in generic trait but other methods works pretty cool there.
Ps: I use Postgresql, that’s why I generate ids in that way.

Advertisements

From → Scala, Slick

2 Comments
  1. Jiunjiun Ma permalink

    Nice. But in line 3 of GenericTableUtils, shouldn’t it be (according to the slick 2.0.0-RC1 scaladoc)

    val tableQuery: TableQuery[T]

    • http://stackoverflow.com/a/20093938/174349

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: