When overloading `apply` method: Slick error message 'value tupled is not a member of object'

放肆的年华 提交于 2019-12-22 11:33:55

问题


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:

  1. Rename the second apply (and in general, please avoid overloading)
  2. 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!