gpt4 book ai didi

scala - 如何从Scala的持久层中提取域层

转载 作者:行者123 更新时间:2023-12-03 11:43:16 25 4
gpt4 key购买 nike

更新:
我已经编辑了标题并添加了此文本,以更好地解释我要实现的目标:我试图从头开始创建新的应用程序,但不希望业务层了解持久性层,以相同的方式,人们不希望业务层知道REST API层。以下是我要使用的持久层的示例。我正在寻找有关与此集成的好的建议,即我需要设计/体系结构方面的帮助,以在业务逻辑和持久性逻辑之间明确划分职责。也许是将持久性对象编码和解码到域对象的概念。

从SLICK(也称为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中轻松实现它

唯一的要求是拥有一个单例数据库或一个单例服务来获得对所需数据库的引用。我个人将根据以下情况进行实现:

  • 通用特征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])
    还请注意,根据您的应用程序复杂性,您可能希望以不同的方式公开此逻辑:

    在您的应用程序很简单的情况下,将
  • 作为单例对象,并且您不希望持久性逻辑可插入
  • 通过充当“应用程序上下文”排序的单例对象,以便在启动时在应用程序中可以决定要使用的持久性逻辑。
  • 如果您的应用程序是分布式的,则
  • 具有某种查找服务模式。
  • 关于scala - 如何从Scala的持久层中提取域层,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11542153/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com