Slick MappedColumnType - scala

I would like to add a case class SupplierID to my code. And I would like to simplify the * method by auto-Mapping SupplierID and Int Types.
case class SupplierID(id: Int)
case class Supplier(id: SupplierID, name: String, street: String, city: String, state: String, zip: String)
// Definition of the SUPPLIERS table using case class
class Suppliers(tag: Tag) extends Table[Supplier](tag, "SUPPLIERS") {
def id = column[Int]("SUP_ID", O.PrimaryKey) // This is the primary key column
def name = column[String]("SUP_NAME")
def street = column[String]("STREET")
def city = column[String]("CITY")
def state = column[String]("STATE")
def zip = column[String]("ZIP")
def * ={
(id, name, street, city, state, zip).shaped.<>({ tuple => Supplier.apply(SupplierID(tuple._1), tuple._2, tuple._3, tuple._4, tuple._5, tuple._6 )}, {
(s : Supplier) => Some{(s.id.id, s.name, s.street, s.city, s.state, s.zip)}
})
}
}
I'm trying to implement a column Type like this
// And a ColumnType that maps it to Int
implicit val SupplierIDColumnType = MappedColumnType.base[SupplierID, Int](
s => s.id, // map Bool to Int
i => SupplierID(i) // map Int to Bool
)
How to use such mappingtype ?

The solution:
case class SupplierID(id: Int)
// And a ColumnType that maps it to Int
implicit val SupplierIDColumnType = MappedColumnType.base[SupplierID, Int](
s => s.id, // map SupplierID to Int
i => SupplierID(i) // map Int to SupplierID
)
case class Supplier(id: SupplierID, name: String, street: String, city: String, state: String, zip: String)
// Definition of the SUPPLIERS table using case class
class Suppliers(tag: Tag) extends Table[Supplier](tag, "SUPPLIERS") {
def id = column[SupplierID]("SUP_ID", O.PrimaryKey) // This is the primary key column
def name = column[String]("SUP_NAME")
def street = column[String]("STREET")
def city = column[String]("CITY")
def state = column[String]("STATE")
def zip = column[String]("ZIP")
def * = (id, name, street, city, state, zip) <>(Supplier.tupled , Supplier.unapply _)
}

Related

Slick: how to get an object attribute in a result set?

Given the following Scala class enhanced with Slick:
class Users(tag: Tag) extends Table[(Int, String, String)](tag, "users") {
def id: Rep[Int] = column[Int]("sk", O.PrimaryKey)
def firstName: Rep[String] = column[String]("first_name")
def lastName: Rep[String] = column[String]("last_name")
def * : ProvenShape[(Int, String, String)] = (id, firstName, lastName)
}
I need to print the last names in a query loop:
val db = Database.forConfig("dbconfig")
try {
val users: TableQuery[Users] = TableQuery[Users]
val action = users.result
val future = db.run(action)
future onComplete {
case Success(u) => u.foreach { user => println("last name : " + **user.lastName**) }
case Failure(t) => println("An error has occured: " + t.getMessage)
}
} finally db.close
But Scala doesn't recognize user.lastName (I get an error saying that "Scala doesn't recognize the symbol"). How to print the last names ?
The problem is you're using Table[(Int, String, String)]. user in your case is therefore an instance of type (Int, String, String), so it doesn't have a lastName. Use user._3 to get at the tuple's third element (the last name). Even better might be to use a case class instead of a tuple:
case class DBUser(id: Int, firstName: String, lastName: String)
class Users(tag: Tag) extends Table[DBUser](tag, "users") {
def id: Rep[Int] = column[Int]("sk", O.PrimaryKey)
def firstName: Rep[String] = column[String]("first_name")
def lastName: Rep[String] = column[String]("last_name")
def * = (id, firstName, lastName) <> (DBUser.tupled, DBUser.unapply)
}

Using multiple projection in slick for the same table

I have a user table for which I will like to have multiple projections. For example, Can I have something like
class Users(tag: Tag) extends Table [User] (tag, "user") {
def * = (id.?, emailId, firstName, lastName, gender) <> (User.tupled, User.unapply)
def allDetails = (id.?, emailId, firstName, lastName, gender, createdTime, modifiedTime)
...
}
I searched on Google but could not find anything. Can somebody tell me how can I use allDetails?
I will like to do
object Users {
val users = TableQuery[Users]
def getAllDetails = ??? // How can I use allDetails here
}
Had the same need recently, and started to use something like that:
abstract class AnyUserTable[T](tag: Tag) extends Table[T](tag, "user") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def emailId = column[String]("email_id")
def firstName = column[String]("firstname")
def lastName = column[String]("lastname")
def gender = column[String]("gender")
}
class Users(tag: Tag) extends AnyUserTable[User](tag) {
def * = (emailId, firstName, lastName, gender, id.?) <> (User.tupled, User.unapply)
}
class UserDetails(tag: Tag) extends AnyUserTable[UserDetail](tag) {
def createdTime = column[Option[Timestamp]]("created_time")
def modifiedTime = column[Option[Timestamp]]("modified_time")
def * = (emailId, firstName, lastName, gender, createdTime, modifiedTime, id.?) <> (UserDetail.tupled, UserDetail.unapply)
}
object Users {
val users = TableQuery[Users]
val getAllDetails = TableQuery[UserDetails] // That is how I propose to get all the details
}
borrowing the case classes from Sky's answer.
case class User(
emailId: String, firstName: String, lastName: String,
gender: String, id: Option[Int] = None)
case class UserDetail(
emailId: String,
firstName: String, lastName: String, gender: String,
createdTime: Option[Timestamp],
modifiedTime: Option[Timestamp], id: Option[Int] = None)
I guess that is pretty close from what I would be tempted to do using updatable views in straight sql.
Do like this:
class UserTable(tag: Tag) extends Table[UserInfo](tag, "user") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def emailId = column[String]("email_id", O.NotNull)
def firstName = column[String]("firstname")
def lastName = column[String]("lastname")
def gender = column[String]("gender")
def createdTime = column[Option[Timestamp]]("created_time")
def modifiedTime = column[Option[Timestamp]]("modified_time")
def userInfo = (emailId, firstName, lastName, gender, createdTime, modifiedTime, id.?) <> (User.tupled, User.unapply)
def * = (emailId, firstName, lastName, gender, id.?) <> (UserInfo.tupled, UserInfo.unapply)
}
case class User(emailId: String,
firstName: String, lastName: String, gender: String,
createdTime: Option[Timestamp],
modifiedTime: Option[Timestamp], id: Option[Int] = None)
case class UserInfo(emailId: String, firstName: String, lastName: String,
gender: String, id: Option[Int] = None)
for extra projection:
val userTable = TableQuery[UserTable]
def insert(userInfo: UserInfo) (implicit session: Session): Int = {
//return auto incremeted id
userTable returning (userTable.map(_.id)) += userInfo
}
def update(userInfo: UserInfo)(implicit session: Session) = {
userTable.filter(_.id === userInfo.id).update(userInfo)
}
def getUserInfo()(implicit session: Session): List[UserInfo] = {
userTable.list
}

Optional nested mapped entity in Slick 2.1.0

I Understand that nested classes work fine with Slick and we have that working in our product. We are facing an issue when the nested class is optional. For instance, look at the following code
class EmpTable(tag: Tag) extends Table[Emp](tag, "emp") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def aId = column[Option[Int]]("a_id")
def location = column[Option[String]]("address")
def address = (aId, location) <> (Address.tupled, Address.unapply)
def * = (id, name, address.?) <> (Emp.tupled, Emp.unapply)
}
case class Emp(id: Int, name: String, address: Option[Address])
case class Address(aId: Option[Int], location: Option[String])
This code does not compile because address does not have method ? .
Is there an easy way to get this working?
I got a solution for this:
class EmpTable(tag: Tag) extends Table[Emp](tag, "emp") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def aId = column[Option[Int]]("a_id")
def location = column[Option[String]]("address")
def * = (id, name, address, auth) <> (Emp.tupled, Emp.unapply)
def address = (aId, location).<>[Option[Address], (Option[Int], Option[String])]({ ad: (Option[Int], Option[String]) =>
ad match {
case (Some(id), Some(loc)) => Some(Address(id, loc))
case _ => None
}
}, {
adObj: Option[Address] =>
adObj match {
case add: Option[Address] => Some((add.map(_.aId), add.map(_.location)))
}
})

Slick and nesting case classes a no go?

I am using slick (and the play framework) to build an application on top of an existing database. I cannot change the database structure.
My database has the following 2 tables:
Meeting:
id (PK)
name
chairman_id (FK to Person.id)
houseman_id (FK to Person.id)
Person:
id (pk)
first_name
last_name
I wanted to define my case classes like this:
case class Meeting (
id: Int,
name: String,
chairman: Person,
houseman: Person
)
case class Person (
id: Int,
firstName: String,
lastName: String
)
But from the very minimal slick documentation around this, it looks like I have to keep the ids in the case class rather than using "Person". Is that correct?
Whats the best approach for this? Sorry for the relatively open question, very new to scala, slick and play.
Thanks,
Ed
You have foreign keys, they don't translate to case classes, they translate to ids:
case class Meeting (
id: Int,
name: String,
chairmanId: Int,
housemanId: Int)
case class Person (
id: Int,
firstName: String,
lastName: String)
And the schema would be something like:
case class Meeting (
id: Int,
name: String,
chairmanId: Int,
housemanId: Int)
case class Person (
id: Int,
firstName: String,
lastName: String)
class Meetings(tag: Tag) extends Table[Meeting](tag, "meeting") {
def * = (id, name, chairmanId, housemanId) <>(Meeting.tupled, Meeting.unapply)
def ? = (id.?, name, chairmanId, housemanId).shaped.<>({
r => import r._
_1.map(_ => Meeting.tupled((_1.get, _2, _3, _4)))
}, (_: Any) => throw new Exception("Inserting into ? projection not supported."))
val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
val name: Column[String] = column[String]("name")
val chairmanId: Column[Int] = column[Int]("chairmanId")
val housemanId: Column[Int] = column[Int]("housemanId")
lazy val meetingChairmanFk =
foreignKey("meeting_chairman_fk", chairmanId, persons)(r => r.id, onUpdate = ForeignKeyAction.Restrict, onDelete = ForeignKeyAction.Cascade)
lazy val meetingHousemanFk =
foreignKey("meeting_houseman_fk", housemanId, persons)(r => r.id, onUpdate = ForeignKeyAction.Restrict, onDelete = ForeignKeyAction.Cascade)
}
lazy val meetings = new TableQuery(tag => new Meetings(tag))
class Persons(tag: Tag) extends Table[Person](tag, "person") {
def * = (id, firstName, lastName) <>(Person.tupled, Person.unapply)
def ? = (id.?, firstName, lastName).shaped.<>({
r => import r._
_1.map(_ => Person.tupled((_1.get, _2, _3)))
}, (_: Any) => throw new Exception("Inserting into ? projection not supported."))
val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
val firstName: Column[String] = column[String]("firstname")
val lastName: Column[String] = column[String]("lastname")
}
lazy val persons = new TableQuery(tag => new Persons(tag))
And it could be used like this:
val thisMeeting = meetings.filter(_.name === "thisMeeting").join(persons).on((m, p) => m.housemanId === p.id || m.chairmanId === p.id).list()
Or using for comprehension (which I personally find more legible):
val thatMeething = (for {
m <- meetings
p <- persons if (p.id === m.chairmanId || p.id === m.housemanId) && m.name === "thatMeeting"
} yield m.id).run
Note that the second query corresponds to an implicit inner join, other types of join are also supported, you can find them here.

SLICK How to define bidirectional one-to-many relationship for use in case class

I am using SLICK 1.0.0-RC2. I have defined the following two tables Directorate and ServiceArea where Directorate has a one to many relationship with ServiceArea
case class Directorate(dirCode: String, name: String)
object Directorates extends Table[Directorate]("DIRECTORATES") {
def dirCode = column[String]("DIRECTORATE_CODE", O.PrimaryKey)
def name = column[String]("NAME")
def * = dirCode ~ name <> (Directorate, Directorate.unapply _)
}
case class ServiceArea(areaCode: String, dirCode: String, name: String)
object ServiceAreas extends Table[ServiceArea]("SERVICE_AREAS") {
def areaCode = column[String]("AREAE_CODE", O.PrimaryKey)
def dirCode = column[String]("DIRECTORATE_CODE")
def name = column[String]("NAME")
def directorate = foreignKey("DIR_FK", dirCode, Directorates)(_.dirCode)
def * = areaCode ~ dirCode ~ name <> (ServiceArea, ServiceArea.unapply _)
}
To make the Directorate case class useful in my Play application form I am trying to redefine the Directorate case class to have a Seq of ServiceAreas that are related to that Directorate.
case class Directorate(dirCode: String, name: String, serviceAreas: Seq[ServiceArea])
My problem is now with the Directorate table projection. I have attempted to create a method in Directorates:
def serviceAreas = (for { a <- ServiceAreas
if (a.dirCode === dirCode)
} yield (a)).list map {
case t: ServiceArea => t
}
so that I can try something like
def * = dirCode ~ name ~ serviceAreas <> (Directorate, Directorate.unapply _)
but this cannot not work as serviceAreas only goes one way.
It seems reasonable to me that for the Directorate case class to be a useful domain object that it should be able contain the related ServiceAreas.
I'm wondering how I should traverse the inverse relationship so that Directorate table projection will work.
I'm sure there is a more elegant solution, but this should do the trick:
import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession
object SlickExperiments2 {
Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {
(Directorates.ddl ++ ServiceAreas.ddl).create
case class Directorate(dirCode: String, name: String) {
def serviceAreas: Seq[ServiceArea] = (for {
a <- ServiceAreas
if (a.dirCode === dirCode)
} yield (a)).list
}
object Directorates extends Table[Directorate]("DIRECTORATES") {
def dirCode = column[String]("DIRECTORATE_CODE", O.PrimaryKey)
def name = column[String]("NAME")
def * = dirCode ~ name <> (Directorate, Directorate.unapply _)
}
case class ServiceArea(areaCode: String, dirCode: String, name: String)
object ServiceAreas extends Table[ServiceArea]("SERVICE_AREAS") {
def areaCode = column[String]("AREAE_CODE", O.PrimaryKey)
def dirCode = column[String]("DIRECTORATE_CODE")
def name = column[String]("NAME")
def directorate = foreignKey("DIR_FK", dirCode, Directorates)(_.dirCode)
def * = areaCode ~ dirCode ~ name <> (ServiceArea, ServiceArea.unapply _)
}
Directorates.insert(Directorate("Dircode", "Dirname"))
ServiceAreas.insertAll(ServiceArea("a", "Dircode", "A"), ServiceArea("b", "Dircode", "B"))
val sa = (for{
d <- Directorates
} yield d).list map { case t: Directorate => t.serviceAreas}
println(sa)
}
//> List(List(ServiceArea(a,Dircode,A), ServiceArea(b,Dircode,B)))
}