【问题标题】:"Error injecting constructor" when mapping Enumeration using Slick 3.1 with play and scala使用带有 play 和 scala 的 Slick 3.1 映射枚举时出现“注入构造函数时出错”
【发布时间】:2015-11-03 22:24:52
【问题描述】:

我有以下课程。请注意注释掉的角色行。

用户模型:

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 和 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))
  }
}

和测试规范:

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
    }

当我运行这个规范时,它成功了。很棒。

现在我想为我的用户添加一个角色。所以我创建了以下角色类:

object Role extends Enumeration{

  type Role = Value

  val None = Value("NONE")
  val Admin = Value("ADMIN")
  val Root = Value("ROOT")
} 

并取消注释相关行(并将其添加到任何实例化调用),并更新进化文件(角色部分存储为 VARCHAR)。现在,当规范运行时,我得到以下异常:

[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)

我尝试在网上进行研究,但我找到的所有关于如何进行枚举的 Slick 示例看起来都像我的一样,并且注入错误不会出现在 Slick 上下文中。

有什么想法吗?

【问题讨论】:

    标签: scala playframework guice slick


    【解决方案1】:

    这里的问题是您试图在 UsersComponent 特征中使用来自 HasDatabaseConfigProvider 特征的 driver.api.MappedColumnType。 但是在 UsersComponent 初始化驱动程序尚未注入的那一刻,这会导致 NPE。 您可以将 roleMapper 设为 lazy 或将其从 val 更改为 def

    implicit lazy val roleMapper = ...
    

    implicit def roleMapper = ...
    

    【讨论】:

      【解决方案2】:

      您选择Enumeration 有什么特别的原因吗?如果是这样,我帮不了你,但我可以建议使用密封特征 + 案例对象 + sealerate

      case class User 创建一个同伴并描述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)
      }
      

      role 添加到User 案例类

      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
      )
      

      role 添加到Users 表中

      class Users(tag: Tag) extends Table[User](tag, "USERS") {
        ...
        def role = column[User.Role]("ROLE") 
        ...
      }
      

      就是这样:)

      Scala 枚举here 的简要说明。

      【讨论】:

      • 效果很好,除了一件事 - UserComponent 使用通用 jdbc 驱动程序(trait UsersComponent { self: HasDatabaseConfigProvider[JdbcProfile] =>)并且 DAO 正在注入 dBConfigProvidor(见上文)。所有这一切,所以我可以释放对特定数据库驱动程序的依赖。问题是 MappedColumnType.base[Role, String] 是 jdbc 驱动程序的一部分,所以我也需要使其通用。有什么想法吗?
      • 不确定,我只使用了特定的驱动程序。我可以看到HasDatabaseConfigProviderMappedColumnType 都在使用here。这有什么帮助吗?
      猜你喜欢
      • 2016-03-23
      • 1970-01-01
      • 1970-01-01
      • 2013-02-16
      • 2019-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多