问题
I have the following classes. please note the commented-out Role lines.
user model:
case class User(
uid: Option[Long] = None,
// role: Option[Role.Role] = None,
firstName: String,
lastName: String,
middleName: Option[String] = None,
email: String,
avatarUrl: Option[String],
created: DateTime = DateTime.now,
modified: DateTime = DateTime.now
)
UserComponent and UserDAO:
import javax.inject._
import scala.concurrent.Future
import org.krazykat.whatsupdoc.models.User
import play.api.db.slick.{HasDatabaseConfigProvider, DatabaseConfigProvider}
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import slick.driver.{JdbcProfile, JdbcDriver}
import org.joda.time.DateTime
import com.github.tototoshi.slick.GenericJodaSupport
import java.sql.Time
trait UsersComponent { self: HasDatabaseConfigProvider[JdbcProfile] =>
import driver.api._
object PortableJodaSupport extends GenericJodaSupport(driver.asInstanceOf[JdbcDriver])
import PortableJodaSupport._
// implicit val roleMapper = MappedColumnType.base[Role.Role, String](
// e => e.toString,
// s => Role.withName(s)
// )
class Users(tag: Tag) extends Table[User](tag, "USERS") {
def uid = column[Long]("USER_ID", O.PrimaryKey, O.AutoInc)
// def role = column[Role.Role]("ROLE")
def firstName = column[String]("FIRST_NAME")
def lastName = column[String]("LAST_NAME")
def middleName = column[String]("MIDDLE_NAME")
def email = column[String]("EMAIL")
def avatarUrl = column[String]("AVATAR_URL")
def created = column[DateTime]("CREATED")
def modified = column[DateTime]("MODIFIED")
def * = (uid.?, firstName, lastName, middleName.?, email, avatarUrl.?, created, modified) <> (User.tupled, User.unapply _)
}
}
/**
* @author ehud
*/
@Singleton
class UsersDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends UsersComponent with HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
val users = TableQuery[Users]
def count = db.run(users.length.result)
def getById(uid: Long) =
db.run(users.filter(_.uid === uid).result.headOption)
def insert(user: User) =
db.run((users returning users.map(_.uid)) += user).map(id => id)
def delete(user: User) =
db.run(users.filter(_.uid === user.uid).delete)
def update(uid: Long, user: User) = {
val userToUpdate: User = user.copy(Some(uid))
db.run(users.filter(_.uid === uid).update(userToUpdate))
}
}
and a test spec:
class UsersSpec extends Specification {
def dateIs(date: java.util.Date, str: String) = new java.text.SimpleDateFormat("yyyy-MM-dd").format(date) == str
trait WithDatabaseConfig {
lazy val (driver, db) = {
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
(dbConfig.driver, dbConfig.db)
}
}
"User model" should {
def usersDao(implicit app: Application) = {
val app2UsersDAO = Application.instanceCache[UsersDAO]
app2UsersDAO(app)
}
"be inserted to db correctly" in new WithApplication with WithDatabaseConfig {
import driver.api._
val userInsertresult = Await.result(usersDao.insert(
User(None, "firstname", "lastname", Some("middlename"), "me@me.com", Some("avatar"), DateTime.now, DateTime.now)),
Duration.Inf
)
val count = Await.result(usersDao.count, Duration.Inf)
count mustEqual 1
}
when i run this spec, it succeeds. great.
now i want to add a role to my user. so i created the following Role class:
object Role extends Enumeration{
type Role = Value
val None = Value("NONE")
val Admin = Value("ADMIN")
val Root = Value("ROOT")
}
and uncomment the relevant lines (and add it to any instanciation call), and update the evolutions file (the Role part is stored as a VARCHAR). now, when the spec runs, i get the following exception:
[error] ! be inserted to db correctly
[error] Unable to provision, see the following errors:
[error]
[error] 1) Error injecting constructor, java.lang.NullPointerException
[error] at UsersDAO.<init>(UsersDAO.scala:59)
[error] at UsersDAO.class(UsersDAO.scala:59)
[error] while locating UsersDAO
[error]
[error] 1 error (InjectorImpl.java:1025)
[error] com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1025)
[error] com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
[error] play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:321)
[error] play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:316)
[error] play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[error] play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[error] play.utils.InlineCache.fresh(InlineCache.scala:69)
[error] play.utils.InlineCache.apply(InlineCache.scala:55)
I tried researching online, but all the Slick samples I found on how to do Enumerations seem like mine, and the injection error does not show up in the Slick context.
any idea?
回答1:
The problem here is that you are trying to use driver.api.MappedColumnType
from the HasDatabaseConfigProvider
trait in the UsersComponent
trait.
But at the moment of the UsersComponent
initialization driver is not injected yet, that causes NPE.
You can either make your roleMapper
val lazy
or change it from val
to def
:
implicit lazy val roleMapper = ...
OR
implicit def roleMapper = ...
回答2:
Any particular reason you go for Enumeration
? I can't help you if so, but I can suggest using sealed trait + case objects + sealerate.
Create a companion for case class User
and describe Role
object User {
sealed trait Role
case object None extends Role
case object Root extends Role
case object Admin extends Role
// A bit messy stuff to convert Role to String and back
def s = sealerate.values[Role].foldLeft(Map[String, Role]()) { case (m, f) ⇒ m.updated(f.toString, f) }
implicit val roleColumnType: JdbcType[Role] with BaseTypedType[Role] = MappedColumnType.base[Role, String](_.toString, s)
}
Add role
to User
case class
case class User(
uid: Option[Long] = None,
role: Option[User.Role] = User.None,
firstName: String,
lastName: String,
middleName: Option[String] = None,
email: String,
avatarUrl: Option[String],
created: DateTime = DateTime.now,
modified: DateTime = DateTime.now
)
Add role
to Users
table
class Users(tag: Tag) extends Table[User](tag, "USERS") {
...
def role = column[User.Role]("ROLE")
...
}
That's all :)
Brief explanation on Scala enums here.
来源:https://stackoverflow.com/questions/33510259/error-injecting-constructor-when-mapping-enumeration-using-slick-3-1-with-play