加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

如何从Scala中的持久层抽象域图层

发布时间:2020-12-16 09:44:08 所属栏目:安全 来源:网络整理
导读:更新: 我已经编辑了标题并添加了这个文本,以便更好地解释我正在尝试实现的内容:我正在尝试从头开始创建一个新的应用程序,但不希望业务层知道持久层,以同样的方式,不希望业务层知道REST API层。下面是一个我想使用的持久层的例子。我正在寻找与此相关的
更新:
我已经编辑了标题并添加了这个文本,以便更好地解释我正在尝试实现的内容:我正在尝试从头开始创建一个新的应用程序,但不希望业务层知道持久层,以同样的方式,不希望业务层知道REST API层。下面是一个我想使用的持久层的例子。我正在寻找与此相关的良好建议,即我需要帮助设计/架构来清理业务逻辑和持久性逻辑之间的责任。也许是将持久性对象编组和解组到域对象的一个??概念。

从SLICK(a.k.a. ScalaQuery)test example,这是如何创建一对多数据库关系。这将创建3个表:a,b和a_to_b,其中a_to_b保留表a和b中的行的链接。

object A extends Table[(Int,String)]("a") {
  def id = column[Int]("id",O.PrimaryKey)
  def s = column[String]("s")
  def * = id ~ s
  def bs = AToB.filter(_.aId === id).flatMap(_.bFK)
}

object B extends Table[(Int,String)]("b") {
  def id = column[Int]("id",O.PrimaryKey)
  def s = column[String]("s")
  def * = id ~ s
  def as = AToB.filter(_.bId === id).flatMap(_.aFK)
}

object AToB extends Table[(Int,Int)]("a_to_b") {
  def aId = column[Int]("a")
  def bId = column[Int]("b")
  def * = aId ~ bId
  def aFK = foreignKey("a_fk",aId,A)(a => a.id)
  def bFK = foreignKey("b_fk",bId,B)(b => b.id)
}

(A.ddl ++ B.ddl ++ AToB.ddl).create
A.insertAll(1 -> "a",2 -> "b",3 -> "c")
B.insertAll(1 -> "x",2 -> "y",3 -> "z")
AToB.insertAll(1 -> 1,1 -> 2,2 -> 2,2 -> 3)

val q1 = for {
  a <- A if a.id >= 2
  b <- a.bs
} yield (a.s,b.s)
q1.foreach(x => println(" "+x))
assertEquals(Set(("b","y"),("b","z")),q1.list.toSet)

作为我的下一步,我想把它放在一个层次上(我仍然希望使用SLICK,但是很好地包装)来处理对象。所以在伪代码中,做一些像

objectOfTypeA.save()
objectOfTypeB.save()
linkAtoB.save(ojectOfTypeA,objectOfTypeB)

或类似的东西。我有关于Java中如何处理这个问题的想法,但是我开始意识到,纯OO语言中的一些面向对象的想法开始失败了。任何人都可以给我一些关于如何在Scala中处理这个问题的指针。

例如:我创建简单对象,只是包装或扩展表对象,然后将这些(组合)包含在另一个管理它们的类中?

任何想法,指导,示例(请),将帮助我更好地处理这个问题作为设计师和编码器将不胜感激。

解决方法

对于简单的持久性要求,一个很好的解决方案是ActiveRecord模式: http://en.wikipedia.org/wiki/Active_record_pattern。这是在Ruby和Play中实现的!框架1.2,您可以在独立应用程序中轻松地在Scala中实现它

唯一的要求是使用单例DB或单例服务来获取对您需要的DB的引用。我个人将根据以下内容进行实施:

>一般特征ActiveRecord
>一个通用的类型类ActiveRecordHandler

利用隐含的力量,您可以获得一个惊人的语法:

trait ActiveRecordHandler[T]{

  def save(t:T):T

  def delete[A<:Serializable](primaryKey:A):Option[T]

  def find(query:String):Traversable[T]
}

object ActiveRecordHandler {
  // Note that an implicit val inside an object with the same name as the trait 
  // is  one of the way to have the implicit in scope.
  implicit val myClassHandler = new ActiveRecordHandler[MyClass] {

    def save(myClass:MyClass) = myClass

    def delete[A <: Serializable](primaryKey: A) = None

    def find(query: String) = List(MyClass("hello"),MyClass("goodbye"))
  }
}

trait ActiveRecord[RecordType] {
  self:RecordType=>


  def save(implicit activeRecordHandler:ActiveRecordHandler[RecordType]):RecordType = activeRecordHandler.save(this)

  def delete[A<:Serializable](primaryKey:A)(implicit activeRecordHandler:ActiveRecordHandler[RecordType]):Option[RecordType] = activeRecordHandler.delete(primaryKey)
}

case class MyClass(name:String) extends ActiveRecord[MyClass] 

object MyClass {
  def main(args:Array[String]) = {
    MyClass("10").save
  }
}

有了这样一个解决方案,你只需要你的类扩展ActiveRecord [T],并且有一个隐式的ActiveRecordHandler [T]来处理这个问题。

实际上还有一个实现:https://github.com/aselab/scala-activerecord是基于类似的想法,而不是使ActiveRecord具有抽象类型,它声明一个通用协同对象。

对ActiveRecord模式的一般但非常重要的评论是它有助于满足持久性方面的简单要求,但无法应对更复杂的需求:例如,当您想要在同一个事务下保留多个对象时。

如果您的应用程序需要更复杂的持久性逻辑,最好的方法是引入一个仅向客户端类公开一组有限的功能的持久性服务,例如

def persist(objectsofTypeA:Traversable [A],objectsOfTypeB:Traversable [B])

还请注意,根据您的应用程序复杂性,您可能希望以不同的方式公开此逻辑:

>作为单例对象,在您的应用程序简单的情况下,您不希望持久性逻辑可插拔通过作为“应用程序上下文”排序的单例对象,以便在启动时的应用程序中,您可以决定要使用的持久性逻辑。>使用某种查找服务模式,如果您的应用程序是分发的。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读