【问题标题】:Slick 3 TransactionSlick 3 交易
【发布时间】:2015-05-14 11:39:10
【问题描述】:

我正在考虑如何将我自己的闭包表实现从另一种语言移植到 Scala,同时考虑到并发性。

我有两个模型,一个节点(id | parentID)和一个 NodeTree(id | 祖先 | 后代),其中每个条目都类似于树中的一条边。

对于每个新节点,我必须执行以下操作: 查询所有祖先(或为它们过滤 TableQuery),然后为每个祖先添加一个 NodeTree-Entry(一条边)

感谢黑豹,我走到了这一步:

private val nodes = TableQuery[Nodes]

override def create(node: Node): Future[Seq[Int]] =
    {
        val createNodesAction = (
            for
            {
                parent <- nodes
                node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = Some(ntId))) += NodeTree(id = None, ancestor = parent.id, descendant = node.id, deleted = None, createdAt = new Timestamp(now.getTime), updatedAt = new Timestamp(now.getTime)))
            } yield (node)
        ).transactionally

        db run createNodesAction
    }

但这会导致类型不匹配;

类型不匹配;找到:slick.lifted.Rep[Long] 需要:Option[Long]

再一次:我想要做的就是:对于每个 parentNode(= 每个父节点的父节点,直到最后一个祖先节点没有父节点!)我想在 nodeTree 中创建一个条目,以便稍后我可以轻松地获取所有后代和祖先只需另一个方法调用即可过滤 NodeTree-Table。

(只是一个闭包表,真的)

编辑:这些是我的模型

case class Node(id: Option[Long], parentID: Option[Long], level: Option[Long], deleted: Option[Boolean], createdAt: Timestamp, updatedAt: Timestamp)

class Nodes(tag: Tag) extends Table[Node](tag, "nodes")
{
    implicit val dateColumnType = MappedColumnType.base[Timestamp, Long](d => d.getTime, d => new Timestamp(d))

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def parentID = column[Long]("parent_id")
    def level = column[Long]("level")
    def deleted = column[Boolean]("deleted")
    def createdAt = column[Timestamp]("created_at")
    def updatedAt = column[Timestamp]("updated_at")

    def * = (id.?, parentID.?, level.?, deleted.?, createdAt, updatedAt) <> (Node.tupled, Node.unapply)
}

case class NodeTree(id: Option[Long], ancestor: Option[Long], descendant: Option[Long], deleted: Option[Boolean], createdAt: Timestamp, updatedAt: Timestamp)

class NodeTrees(tag: Tag) extends Table[NodeTree](tag, "nodetree")
{
    implicit val dateColumnType = MappedColumnType.base[Timestamp, Long](d => d.getTime, d => new Timestamp(d))

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def ancestor = column[Long]("ancestor")
    def descendant = column[Long]("descendant")
    def deleted = column[Boolean]("deleted")
    def createdAt = column[Timestamp]("created_at")
    def updatedAt = column[Timestamp]("updated_at")

    def * = (id.?, ancestor.?, descendant.?, deleted.?, createdAt, updatedAt) <> (NodeTree.tupled, NodeTree.unapply)
}

我想要做的是一个闭包表 (http://technobytz.com/closure_table_store_hierarchical_data.html),当我创建一个节点时它会自动填充它的边缘 (nodeTree)。所以我不想手动将所有这些条目添加到数据库中,但是当我在第 5 级创建节点时,我希望自动创建整个路径(= 节点树表中的条目)。

我希望这能解决一些问题:)

【问题讨论】:

    标签: scala playframework slick slick-3.0


    【解决方案1】:

    试试这个:

    override def create(node: Node): Future[Seq[Int]] =
    {
        val parents = getAllParents(node)
        val createNodesAction = (
          for {
            parent <- parents
            node <- nodeTrees += NodeTree(id = None, ancestor = parent.id, descendant = node.id)
          } yield (node)
        ).transactionally
    
       db run createNodesAction
    }
    

    您不必单独检索父母。它可以在同一个会话中完成。在上面,您可以轻松地将“parents”替换为您想要处理的 TableQuery(带或不带过滤器)。

    还请注意,在这里您将返回受插入操作影响的行数序列。要改为返回节点 ID 列表(假设您在 db 中将节点 ID 标记为 AUTO_INC),那么您可以执行以下操作:

    override def create(node: Node): Future[Seq[Int]] =
    {
        val createNodesAction = (
          for {
            parent <- parents
            node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = Some(ntId))) += NodeTree(id = None, ancestor = parent.id, descendant = node.id)
          } yield (node)
        ).transactionally
    
       db run createNodesAction
    }
    

    不同之处在于:(nodeTrees 返回 nodeTrees.map(_.id) 到 ((ntEntry, ntId) => ntEntry.copy(id = Some(ntId))) 而不仅仅是 (nodeTrees) 检索 auto inc id 并将其映射到结果中。


    更新:试试这个:

    override def create(node: Node): Future[Seq[Int]] =
    {
        def createNodesAction(parentId: Long): DBIOAction[NodeTree, NoStream, Read with Write] = (
          for {
            node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = Some(ntId))) += NodeTree(id = None, ancestor = parentId, descendant = node.id)
          } yield (node)
        ).transactionally
    
       // TODO: Init and pass in 'parents'
       db.run(DBIO.sequence(parents.map(createNodesAction(_.id)))
    }
    

    【讨论】:

    • 我会接受你的回答,但有几个问题:首先:nt.id = ntId -> ntId 是 Long 类型,而此处需要 Option[Long]。还有 nt.id = ntId -> 重新分配给 val :/ 另外,您能否详细说明...是否会更新表查询(如果有更多条目,是否会重新选择?)或者我需要手动执行吗? ?
    • 编辑了解决设置可选值和重新分配给 val 问题的答案。关于您在插入时选择多个条目 的问题,我找不到这样做的方法。这可能是因为 ID 可能是使用另一个 SQL 构造 (last_insert_id) 检索的。目前,为了从插入的行中选择其他项目(如时间戳),我通过对检索到的 id 运行选择查询来手动完成。
    • value id 不是 List[models.Node] 的成员 sighs 我必须解压列表甚至展平它?如果我尝试使用 TableQuery,我什至会得到:类型不匹配;找到:slick.lifted.Rep[Long] 需要:Option[Long]
    • On: found : slick.lifted.Rep[Long] required: Option[Long],当你在执行之前尝试使用一个值时,你会看到 Rep[T] 而不是 T。这是因为每种类型在实际运行之前都被提升到 Rep[T] 执行,然后在执行后映射回 T。请分享您尝试使用的代码。
    • 另外,this 你在找什么。
    【解决方案2】:

    试着改成这一行。

      node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = ntId)) += NodeTree(id = None, ancestor = parent.id, descendant = node.id, deleted = None, createdAt = new Timestamp(now.getTime), updatedAt = new Timestamp(now.getTime)))
    

    它是否解决了问题?很难从您的问题中准确判断出您的模型是什么。

    【讨论】:

    • 我在问题中添加了我的模型代码,尽管我不知道这应该有什么帮助。无论如何,我还添加了另一个关于我想要实现的目标的描述。无论如何感谢您的帮助! :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 1970-01-01
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    相关资源
    最近更新 更多