首页 文章

在Scala Slick中实现类实例的成员修改的最佳方法?

提问于
浏览
0

我正试图在现实生活中使用Slick的嵌入式嵌入方法(体育俱乐部成员的个人数据自我管理) . 我已经设法从数据库中检索信息并更新记录(实现Member作为案例类并使用案例类复制方法,如下所示)但我很难找到实现成员修改的最佳方法 . 自然的方式 .

我已经考虑了两种可能性:1)维护类实例的不变性并实现一般的setter(参见代码)2)使构造函数参数“var”(什么会抑制不变性,因此不理想)

坚持使用选项1,我想出了以下代码(摘录,而不是整个源代码):

case class Member(id: Int, name: String, firstname: Option[String] = None,
      birthDate: Option[Date] = None, gender: Option[String] = None, country: Option[String] = None,
      healthNotes: Option[String]) {

      // Just a try so far
      def set(n: String = name, fn: Option[String] = firstname, bd : Option[Date] = birthDate)
        (implicit session: Session) = { 
        val m = this.copy(id,n,fn,bd)
        Members.update(m)
        m
      }
    }

    object Members extends Table[Member]("clubmembers") 
        with CayenneAutoPKSupport {

      def id = column[Int]("Member_ID", O.PrimaryKey) // This is the primary key column
      def name = column[String]("Name")
      def firstname = column[Option[String]]("Firstname")
      def birthDate = column[Option[Date]]("BirthDate")
      def gender = column[Option[String]]("Gender")
      def country = column[Option[String]]("Country")
      def healthNotes = column[Option[String]]("HealthNotes")

      // Every table needs a * projection with the same type as the table's type parameter
      def * = id ~ name ~ firstname ~ birthDate ~ gender ~ country ~ healthNotes <> (Member.apply _, Member.unapply _)
}

这按预期工作,但我希望set方法的命名参数具有相同的名称(什么会使调用更“自然”) . 我尝试了以下(无济于事)

def set( name: String = this.name, …

这不编译,我可以想象为什么编译器不满意(OTOH当前的实现似乎工作)但我也可以想象它可以工作 . 无论如何:有人看到了实现这一目标的方法吗?

或者,作为Slick持久化对象实现可修改类实例的最佳实践,会建议什么?

提前感谢任何提示 .

问候

2 回答

  • 0

    实际上,如果对参数和默认值使用相同的名称,原始代码将起作用 . 它只是Scala IDE中的语法高亮显示器似乎无法理解:如果名称不同,默认值(正确)突出显示为类成员,它们只是显示为名称相同的参数本身 .

    这是当前版本(按预期工作但未正确语法突出显示):

    def set(name: String = name, firstname: String = firstname, birthDate : Option[Date] = birthDate,
            gender: String = gender, country: String = country, 
            addressBlockId: Option[Int] = addressBlockId, healthNotes: String = healthNotes)
            (implicit session: Session) = { 
        val m = this.copy(id,name,Option(firstname),birthDate,
            Option(gender),Option(country),addressBlockId,Option(healthNotes))
        m
      }
    

    注意:字符串参数最好也作为Option [String]传递 .

    欢迎提出意见和建议 .

    问候

  • 1

    如果这是你想要实现的目标,这将为我编译:

    def set(name: String = name, firstname: Option[String] = firstname, birthDate : Option[Date] = birthDate)(implicit session: Session) = {
        val m = Member(id, name, firstname, birthDate, gender, country, healthNotes)
        Members.update(m)
        m
      }
    ...
    member.set(firstname = Some("name"))
    

    我不建议在你的case类中做数据库函数 . 我们通常使用案例类作为简单的数据存储对象,并且只定义有助于管理该数据的函数 .

    成员的更新应该在您的成员DAO类中进行,可能最简单的解决方案是这样的:

    object MemberDao {
      def updateMemberDetails(member: Member, name: String, firstname: Option[String], birthdate : Option[Date]) = {
        val updatedMember = member.copy(name = name, firstname = firstname, birthDate = birthdate)
        Members.update(updatedMember)
      }
    }
    

    处理可修改类的另一个解决方案是使用以下行的模式:

    case class Member(id: Int, name: String, firstname: Option[String] = None, birthDate: Option[Date] = None, gender: Option[String] = None, country: Option[String] = None, healthNotes: Option[String]) {
      def updateName(n: String) = this.copy(name = n)
      def updateFirstName(fn: Option[String]) = this.copy(firstname = fn)
      def updateBirthDate(bd: Option[Date]) = this.copy(birthDate = bd)
    }
    

    最后,如果你想要一个保存函数,你可以通过隐式语法注入一个流畅的样式语法,它总是返回一个定义了保存函数的新成员 .

    case class Member(id: Int, name: String, firstname: Option[String] = None, birthDate: Option[Date] = None, gender: Option[String] = None, country: Option[String] = None, healthNotes: Option[String])
    
    object Updatable {
      implicit class UpdatableMember(member: Member) {
        def updateName(n: String) = member.copy(name = n)
        def updateFirstName(fn: Option[String]) = member.copy(firstname = fn)
        def updateBirthDate(bd: Option[Date]) = member.copy(birthDate = bd)
        def save(implicit session: Session) = {
          ???
        }
      }
    }
    
    object MemberDao {
      def updateMember(member: Member) = {
        import Updatable._
        DB withSession { implicit session =>
          member.updateFirstName(Some("First Name")).updateBirthDate(Some(new Date(123456789))).save
        }
      }
    }
    

    希望您能找到一些有用的选项 . 如果我误解了你的要求,那么请评论!

    更新

    您不一定要为每个成员都有一个更新方法,但您可能会在那里执行其他逻辑 . 如果您想要一个简单的解决方案,那么就像使用Scala的内置复制功能一样 .

    val member = MemberDao.getMember(123)
    val updatedMember = member.copy(birthDate = Some(new Date(123456789)), name = "Updated name")
    MemberDao.persist(updatedMember)
    

    如果这对您不起作用,请解释原因 .

    我在基本上存储数据的类中使用save方法的问题是它没有考虑Seperation of Concerns . 此外,您的设置功能也有副作用,这可能会使其他开发人员感到困惑(并且功能不太强) . 如果您只想设置名字然后将其传递到另一个类来设置生日,该怎么办?你想做两个数据库事务吗?我猜不会 .

    这就是Data Access Object and its advantages发挥作用的地方 . 您可以将案例类的所有数据库操作放在一个类中,从而分离关注点:您的案例类包含数据并且具有仅对数据执行操作的函数,并且您的DAO具有查询/持久化/更新/删除您的案例类 . 这也使得测试应用程序变得更加容易,因为它保存了函数 .

    很多人_1116209_,这些陈述只是我的意见 .

    不使用DAO

    我建议为可更新的案例类定义一个特征:

    trait DatabaseAccess[T] {
      def create(implicit session: Session): T
      def update(implicit session: Session): T
      def delete(implicit session: Session): Boolean
    }
    
    case class Member(id: Int, name: String, firstname: Option[String] = None, birthDate: Option[Date] = None, gender: Option[String] = None, country: Option[String] = None, healthNotes: Option[String]) extends DatabaseAccess[Member] {
      def create(implicit session: Session): Member = ???
      def delete(implicit session: Session): Boolean = ???
      def update(implicit session: Session): Member = ???
    }
    

    这些只是想法,我认为没有正确或错误的解决方案 . 选择一个对您有意义的,并为您提供必要的可用性/灵活性比率 .

    最佳做法

    您询问了Scala最佳实践 . 您的问题更多的是软件设计问题,因此阅读一般的OO软件设计可以帮助您 . Scala的案例类别非常多更简单的编写POJO的方法 . 因此,在将任何新函数添加到案例类之前,请问自己一个问题:“我会把它放在POJO中吗?” . 如果是的话,那就继续吧:)

相关问题