Here are the codes (I'm using Slick 2.1):
case class UserRecord(id: Long,
mID: String,
userName: Option[String],
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: String,
avatarUrl: Option[String],
createTime: Timestamp,
updateTime: Timestamp,
status: Int,
socialProviders: Int)
case class UserProfile (
userName: String,
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: String,
avatarURL: Option[String])
class UserTable(tag: Tag) extends Table[UserRecord](tag, "User") {
def id = column[Long]("id", O.AutoInc)
def mID = column[String]("mID", O.PrimaryKey)
def username = column[Option[String]]("username")
def firstName = column[Option[String]]("firstname")
def lastName = column[Option[String]]("lastname")
def fullName = column[Option[String]]("fullname")
def email = column[String]("email")
def avatarUrl = column[Option[String]]("avataurl")
def createTime = column[Timestamp]("createTime")
def updateTime = column[Timestamp]("updateTime")
def status = column[Int]("status")
def socialProviders = column[Int]("socialProviders")
def * = (id, mID, username, firstName, lastName, fullName,
email, avatarUrl, createTime, updateTime, status, socialProviders) <>(UserRecord.tupled, UserRecord.unapply _)
def profile = (username, firstName, lastName, fullName, email, avatarUrl) <> (UserProfile.tupled, UserProfile.unapply _)
}
I tried to create two mappings * and profile in the Table class, however, Slick complains about this:
[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection. Or you use an unsupported type in a Query (e.g. scala List).
[error] Required level: scala.slick.lifted.FlatShapeLevel
[error] Source type: (scala.slick.lifted.Column[Option[String]], scala.slick.lifted.Column[Option[String]], scala.slick.lifted.Column[Option[String]], scala.slick.lifted.Column[Option[String]], scala.slick.lifted.Column[String], scala.slick.lifted.Column[Option[String]])
[error] Unpacked type: (String, Option[String], Option[String], Option[String], String, Option[String])
[error] Packed type: Any
[error] def profile = (username, firstName, lastName, fullName, email, avatarUrl) <> (UserProfile.tupled, UserProfile.unapply _)
[error] ^
I saw a blog about this, but it looks quite complex and has no explanation..
Does anyone have ideas about this? Thanks!
Slick expects Username to be Option[String] in the 'profile' projection
case class UserProfile (
userName: String, // Here
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: String,
avatarURL: Option[String])
Clearly Slick here doesn't know how to map the columns with the case class .
Related
I'm quite new to Scala and need to use slick to build a table mapping for these case classes.
I can do it for a simple case class but the nesting and option parameters are leaving me confused how to do this?
case class Info(fullName: Option[String], dn: Option[String], manager: Option[String], title: Option[String], group: Option[String], sid: Option[String])
case class User(username: String, RiskScore: Float, averageRiskScore: Float, lastSessionId: String, lastActivityTime: Long, info: Info)
I need to end up with a simple table which contains all of the combined parameters.
Given your nested case class definitions, a bidirectional mapping for the * projection similar to the following should work:
case class Info(fullName: Option[String], dn: Option[String], manager: Option[String], title: Option[String], group: Option[String], sid: Option[String])
case class User(username: String, riskScore: Float, averageRiskScore: Float, lastSessionId: String, lastActivityTime: Long, info: Info)
class Users(tag: Tag) extends Table[User](tag, "USERS") {
def username = column[String]("user_name")
def riskScore = column[Float]("risk_score")
def averageRiskScore = column[Float]("average_risk_score")
def lastSessionId = column[String]("last_session_id")
def lastActivityTime = column[Long]("last_acitivity_time")
def fullName = column[Option[String]]("full_name", O.Default(None))
def dn = column[Option[String]]("dn", O.Default(None))
def manager = column[Option[String]]("manager", O.Default(None))
def title = column[Option[String]]("title", O.Default(None))
def group = column[Option[String]]("group", O.Default(None))
def sid = column[Option[String]]("sid", O.Default(None))
def * = (
username, riskScore, averageRiskScore, lastSessionId, lastActivityTime, (
fullName, dn, manager, title, group, sid
)
).shaped <> (
{ case (username, riskScore, averageRiskScore, lastSessionId, lastActivityTime, info) =>
User(username, riskScore, averageRiskScore, lastSessionId, lastActivityTime, Info.tupled.apply(info))
},
{ u: User =>
def f(i: Info) = Info.unapply(i).get
Some((u.username, u.riskScore, u.averageRiskScore, u.lastSessionId, u.lastActivityTime, f(u.info)))
}
)
}
Here is a great relevant Slick article you might find useful.
I have a trait and two case classes that extends it:
trait Authenticatable {
val email: String
val pass: String
val id: Long
val sessionid: String
}
case class Admin(
id: Long,
email: String,
pass: String,
sessionid: Option[String] = None) extends Authenticatable
case class Client(
id: Long,
email: String,
pass: String,
sessionid: Option[String] = None) extends Authenticatable
And I have functions witch should authenticate user, make copy of object with new sessionid and return it.
def auth(email: String, password: String): Try[Admin] ={
checkPass(models.Admin.findBy(sqls"email = $email"), password)
}
def auth(email: String, password: String, customer: Customer): Try[Customer] ={
checkPass(models.Customer.findBy(sqls"email = $email"), password)
}
private def checkPass (model: Option[Authenticatable], password: String): Try[Authenticatable]={
model match {
case Some(m) => check(password, m.pass).map(_ => m)
case _ => Failure(new Exception("Authentication failure!"))
}
}
The problem is: I can't make copy of object in auth function because function checkPass returns Authenticatable and not Client or Admin class and Authenticatable doesn't have copy method of case classes.
What is the right way to solve this problem?
If you use type parameters, you can avoid throwing away the information that checkPass will always return the same type of Authenticable as was given to it:
private def checkPass[A <: Authenticatable](model: Option[A], password: String): Try[A] =
// exactly the same body here
This means that in auth you can have e.g.:
def auth(email: String, password: String): Try[Admin] =
checkPass(models.Admin.findBy(sqls"email = $email"), password)
.map(_.copy(sessionid = Some("whatever")))
I would propose to add an abstract method to Authenticable which sets session ID and which is implemented by individual case classes by using of the generated copy method.
trait Authenticable {
def email: String
def pass: String
def id: Long
def sessionid: Option[String]
def setSessionId(id: String): Authenticable
}
case class Admin(
id: Long,
email: String,
pass: String,
sessionid: Option[String] = None) extends Authenticable {
def setSessionId(id: String) = copy(sessionid = Some(id))
}
case class Client(
id: Long,
email: String,
pass: String,
sessionid: Option[String] = None) extends Authenticable {
def setSessionId(id: String) = copy(sessionid = Some(id))
}
I have trait, this trait is already defined in framework and can not change:
trait GenericProfile {
def firstName: Option[String]
def lastName: Option[String]
def fullName: Option[String]
def email: Option[String]
def avatarUrl: Option[String]
}
I want a class inherit it as:
class BasicProfile(
providerId: String,
userId: String,
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: Option[String],
avatarUrl: Option[String]
) extends GenericProfile{
def providerId=this.providerId //ambiguous reference will be here
...
}
But if I do not re-define the unimplemented method, there is still error since the value in BasicProfile is regarded as private and do not regard it as already implemented.
I understand that it can simply write as override, but I have another class in practice:
case class IdProfile(id:String,
providerId: String,
userId: String,
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: Option[String],
avatarUrl: Option[String])extends BasicProfile(providerId,userId,firstName,lastName, fullName,email, avatarUrl){
}
I do not want the IdProfile override the methods from its parents class BasicProfile, just inherit would be OK.
Since BasicProfile has to make sure all that the defined methods of the trait are implemented (since you don't want to use an abstract class), I'd recommend using a case class for the BasicProfile.
You can extend the BasicProfile with an IdProfile class (not case class) and override the specific methods you are interesed in (or leave them be). If I'm not mistaken that's what your trying to accomplish?
trait GenericProfile {
def firstName: Option[String]
def lastName: Option[String]
def fullName: Option[String]
def email: Option[String]
def avatarUrl: Option[String]
}
case class BasicProfile(
providerId: String,
userId: String,
var firstName: Option[String],
var lastName: Option[String],
var fullName: Option[String],
var email: Option[String],
var avatarUrl: Option[String]
) extends GenericProfile{
}
class IdProfile(id:String,
providerId: String,
userId: String,
firstName: Option[String],
lastName: Option[String],
fullName: Option[String],
email: Option[String],
avatarUrl: Option[String])extends BasicProfile(providerId,userId,firstName,lastName, fullName,email, avatarUrl){
}
If you are trying to stay away from case class I'd recommend taking a look at this Question: Simple Scala getter/setter override
Hope this helps.
To define a readable field in the argument list to a class's constructor, you can use val:
class BasicProfile(
val providerId: String,
val firstName: Option[String],
...
) extends GenericProfile {
...
}
When you do not put val (or alternatively var for a mutable field) on the constructor argument, a field is generally not created.
If you define your class as a case class, then constructor arguments without modifiers are treated as if they have val in front of them, and fields are created for them:
case class BasicProfile(
providerId: String,
...
) extends GenericProfile {
...
}
I'm creating a new domain object mode. In this model, there are Users and Posts. In the future there will be more models (e.g. Comments). I'm trying to learn how to do this using scala to it's full extent. Here's a naive implementation:
class User(val id: String, val creationDate: DateTime, val name: String, val email: String)
class Post(val id: String, val creationDate: DateTime, val user: User, val title: String, val body: String)
And here's another approach attempting to get rid of the duplicate id and creationDate.
class Model(val id: String, val creationDate: DateTime)
class User(id: String, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: String, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)
I'd like to moderate some of my domain objects. To do this, I'd like to add an isApproved: Boolean field.
class Model(val id: String, val creationDate: DateTime)
class User(id: String, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: String, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)
trait Moderated {
val isApproved: Boolean
}
class ModeratedPost(id: String, creationDate: DateTime, val user: User, val title: String, val body: String, val isApproved: Boolean) extends Post(id, creationDate, user, title, body) with Moderated
I'd also like to prevent bugs in my code by type aliasing user and post Ids.
type Id = String
type UserId = Id
type PostId = Id
class Model(val id: Id, val creationDate: DateTime)
class User(id: UserId, creationDate: DateTime, val name: String, val email: String) extends Model(id, creationDate)
class Post(id: PostId, creationDate: DateTime, val user: User, val title: String, val body: String) extends Model(id, creationDate)
trait Moderated {
val isApproved: Boolean
}
class ModeratedPost(id: String, creationDate: DateTime, val user: User, val title: String, val body: String, val isApproved: Boolean) extends Post(id, creationDate, user, title, body) with Moderated
At this point, I've got several questions.
Where should I define my type aliases? I think they have to be defined inside a class, trait, or object.
My goal in using type aliases for my Ids is to catch errors at compile time. I'd like UserId and PostId to be "subclasses" of Id. I.e. if a method took an Id, I could pass in a PostId. How should I do this?
My Moderated trait does not feel very useful. I still have to declare the isApproved on all classes that mix it in. Any tips here?
Idiomatic scala would go something like:
sealed trait Id { def strVal: String }
case class UserId(strVal: String) extends Id
case class PostId(strVal: String) extends Id
trait Model { def id: Id, def creationDate: DateTime)
case class User(
id: UserId,
creationDate: DateTime,
name: String,
email: String
) extends Model
trait Post extends model {
def id: PostId
def user: User,
def title: String,
def body: String
)
trait Moderated { def isApproved: Boolean }
case class UnmoderatedPost(
id: PostId
creationDate: DateTime,
user: User,
title: String,
body: String,
) extends Post
case class ModeratedPost(
id: PostId,
creationDate: DateTime,
user: User,
title: String,
body: String,
isApproved: Boolean
) extends Post with Moderated
You can define your type aliases in package.scala which can be created for each package.
Lets say you have a simple package org.your.project.
Create a file in directory org/your/project called: package.scala
package org.your.project
package object Types {
type Id = String
type UserId = Id
type PostId = Id
}
Then in the class you wish to use the type aliases add:
import org.your.project.Types._
https://stackoverflow.com/a/3401031/2116622
http://www.artima.com/scalazine/articles/package_objects.html
I'd probably not use types for the reasons you are thinking of.
A type of Id could also be an Int but you made it a String.
Anyone reading the code would have to click around the code base to figure out what the Id really is.
I use slick 2.0 rc
I have defined field UserId:
trait TypedId extends Any {
def value: Long
}
trait HasId[ID <: TypedId] {
_: Table[_] =>
def id: Column[ID]
}
case class UserId(value:Long) extends AnyVal with TypedId
case class User(id: Option[UserId],
email: String,
firstName: String,
lastName: String,
phone:String)
when I use it:
class Users(tag: Tag) extends Table[User](tag, "users") with HasId[Option[UserId]] {
def * = (id.?, email, firstName , lastName , phone )<> (User.tupled, User.unapply)
def id= column[Option[UserId]]("ID", O.AutoInc, O.PrimaryKey)
def email = column[String]("EMAIL", O.NotNull)
def firstName = column[String]("FIRST_NAME", O.NotNull)
def lastName = column[String]("LAST_NAME", O.NotNull)
def phone =column[String]("PHONE", O.NotNull)
}
it give me compile error as:
[error] C:\assigment\slick-advanced\app\models\User.scala:27: could not find imp
licit value for parameter tm: scala.slick.ast.TypedType[Option[models.UserId]]
[error] def id= column[Option[UserId]]("ID", O.AutoInc, O.PrimaryKey)
This seems to be a duplicate of the already answered question, which can be found here: on slick 2.0, I find I can not store user defined field