I need an ability to create a User
object by providing all the values except id
in certain cases, such that the User
object takes care of assigning itself an auto-generated value.
For this I have overloaded the apply method in the companion object, like shown below. But this is causing the compile time error: value tupled is not a member of object
.
Solutions mentioned on StackOverflow and other blogs aren't working, such as: http://queirozf.com/entries/slick-error-message-value-tupled-is-not-a-member-of-object
case class User(id: Long, firstName: String, lastName: String, mobile: Long, email: String)
object User {
private val seq = new AtomicLong
def apply(firstName: String, lastName: String, mobile: Long, email: String): User = {
User(seq.incrementAndGet(), firstName, lastName, mobile, email)
}
}
class UserTableDef(tag: Tag) extends Table[User](tag, "user") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def firstName = column[String]("first_name")
def lastName = column[String]("last_name")
def mobile = column[Long]("mobile")
def email = column[String]("email")
override def * =
(id, firstName, lastName, mobile, email) <> (User.tupled, User.unapply)
}
The source of your problem is that overloaded apply
def.
tupled
does not work with case class
's with less than 2 parameters
or overloaded apply
.
As far as slick's *
(or all) mapping and <>
is concerned, it is supposed to be like,
def * = (tupleMember1, tupleMember2, ...) <> (func1, func2)
Such that,
func1
takes that tuple (tupleMember1, tupleMember2, ...)
as input and returns an instance of mapped class/case class.func1
takes an instance of mapped class/case class and returns that tuple (tupleMember1, tupleMember2, ...)
.So you can provide any function... which meets these requirements.
case class User(id: Long, firstName: String, lastName: String, mobile: Long, email: String)
object User {
private val seq = new AtomicLong
def apply(firstName: String, lastName: String, mobile: Long, email: String): User = {
User(seq.incrementAndGet(), firstName, lastName, mobile, email)
}
def mapperTo(
id: Long, firstName: String,
lastName: String, mobile: Long, email: String
) = apply(id, firstName, lastName, mobile, email)
}
class UserTableDef(tag: Tag) extends Table[User](tag, "user") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def firstName = column[String]("first_name")
def lastName = column[String]("last_name")
def mobile = column[Long]("mobile")
def email = column[String]("email")
override def * =
(id, firstName, lastName, mobile, email) <> ((User.mapperTo _).tupled, User.unapply)
}
One possible solution is to push the secondary constructor
to the case class
definition itself and then use the work around mentioned in the blog post specified in the question.
You can then create User
objects without specifying id
, however, you may need still to use new
keyword, like so new User(firstName, lastName, mobile, email)
.
case class User(id: Long, firstName: String, lastName: String, mobile: Long, email: String) {
def this(firstName: String, lastName: String, mobile: Long, email: String) =
this(User.seq.incrementAndGet(), firstName, lastName, mobile, email)
}
object User {
private val seq = new AtomicLong
}
class UserTableDef(tag: Tag) extends Table[User](tag, "user") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def firstName = column[String]("first_name")
def lastName = column[String]("last_name")
def mobile = column[Long]("mobile")
def email = column[String]("email")
override def * =
(id, firstName, lastName, mobile, email) <> ((User.apply _).tupled, User.unapply)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With