问题
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)
}
回答1:
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)
}
回答2:
Notice: It is probably a bad idea to use an internal atomic long as an id generator instead of using auto-increment sequence generator that database provide, as these are safe against concurrent access from multiple applications.
Now getting back to the code, the tupled method is defined on a Function and converts a function with N arguments into a function with a single argument, a tuple of N-arity
From your description, it looks like overloading the apply method results in the compiler not being able to determine which "apply" implementation should be picked. So there are two things you should probably do:
- Rename the second apply (and in general, please avoid overloading)
- Explicitly call tupled (i.e. User.withEmptyId.tupled)
回答3:
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)
}
来源:https://stackoverflow.com/questions/41179532/when-overloading-apply-method-slick-error-message-value-tupled-is-not-a-memb