slick源码阅读笔记三 ShapedValue在Table和Query上的应用

转载请注明出处
https://www.cnblogs.com/elyw/p/10294453.html

目录

slick源码阅读笔记 目录&总览

slick源码阅读笔记一 slick.ast

slick源码阅读笔记二 slick.lifted.Rep

slick源码阅读笔记三 ShapedValue 在Table和Query上的应用

slick源码阅读笔记四 隐式转换--column与Query查询功能实现

slick源码阅读笔记五 Query查询条件生成与OptionMapper

1. ShapedValue

1.1. ShapedValue 和 TypedRep对比

# TypedRep 使用Type描述Rep表示的数据类型,实现数据库类型到scala类型的映射,为scala和Node提供了沟通的桥梁。

# ShapedValue 使用Shape来描述ShapedValue由哪些更基础数据组成,可以是其他的Rep也可以是其他的数据类型。  而ShapedValue本身也是Rep,可以使用encodeRef方法将shape上下文传递给其他的Node。

# ShapedValue 还提供了类型转换功能,下文会详细说明。

1.2 Shape

Shape之于ShapedValue, 类似于 Type之于TypedRep。

  • Type是对数据库类型的描述,以及Type描述的数据于scala类之间的映射关系。
  • shape则描述了该Rep是由哪些更基础的类型组成。

注意:实际上Shape的能力更加通用, 不过这里着重于Shape在Query上的使用,不会深入探讨其在生成sql和sql参数绑定上的应用(后面可能会写一篇博客专门讲这个问题)。

1.2.1 Shape的继承树

1.2.2 Shape的核心功能

Shape的核心方法

 /** 
  * [打包数据:Packed]:  (Column[Int], Column[(Int, String)], (Column[Int], Column[Option[Double]]))
  * [未打包数据:Unpacked]: (Int, (Int, String), (Int, Option[Double]))
  * [混合数据:Mixed]:混合了未打包和已打包的数据
  * 例如:  (Column[Int], Column[(Int, String)], (Int, Option[Double]))
  * 
  */

  /**  将混合的数据打包成带有类型上下文的数据 */
  def pack(value: Mixed): Packed

  /** 将Shape转化为不允许[混合数据]的Shape */
  def packedShape: Shape[Level, Packed, Unpacked, Packed]

  /** 
   *  生成一个包含 sql 参数,以及生成具体参数方法的 Packed类型对象
   *  compiler 会用Packed来生成sql 语句中的参数,  然后用 extract来生成具体的数据做参数绑定
   */
  def buildParams(extract: Any => Unpacked): Packed

  /** 
   * 将value的上下文传递给 path
   *  这里虽然写着Mixed,不过实际上都是 Packed (Mixed 和Packed类型相同,参照packedShape方法)
   */
  def encodeRef(value: Mixed, path: Node): Any

  /** 将Mixed转化为Node,  实际使用过程中都是Packed (Mixed 和Packed类型相同,参照packedShape方法) */
  def toNode(value: Mixed): Node

1.2.3 ProductNodeShape

ProductNodeShape 是一种嵌套Shape, 用来描述复杂的数据结构 Tuple, case class,根据实际情况还会嵌套附带其他上下文

1.2.4 RepShape

用来描述slick.lifted.Rep及其子类的 Shape

/**
 * 核心方法只有两个
 * 1. 更新抽象语法树时,通过encodeRef将旧的上下文传递给更新后的语法树
 * 2. 从shapedValue中直接获取抽象语法树
 */
def encodeRef(value: Mixed, path: Node) = value.encodeRef(path)
def toNode(value: Mixed): Node = value.toNode

1.2.5 primitiveShape

数据库原子类型Shape, 用来描述Literal。

例如: select 1 中的 1

1.2.6 unitShape

专门用来描述 scala的Unit类型

1.2.7 ProvenShapeShape

专门用来描述 select * 中的 号,select查询全部列时将号展开为实际的列

1.3 ShapedValue 在Query中的应用

lazy val shaped = {
    /** 
     * 每个Node要对应一个自己的Rep, Table也是Rep的一种,所以新的Node都要对应产生新Table对象
     * Node包含在Tag中,因此每个Tag都要产生新的Table对象
     * 
     * cons 是Table工厂, 每次调用cons会产生一个新的Table对象 
     * 
     * 这个设计给笔者的感觉不是很好, 因为他把用在内部的Tag的概念暴露出去了, 不懂内部实现的使用者会一脸懵逼
     */
    val baseTable = cons(new BaseTag { base =>
      def taggedAs(path: Node): AbstractTable[_] = cons(new RefTag(path) {
        def taggedAs(path: Node) = base.taggedAs(path) //直接使用base.tageAs,避免递归后方法深度越来越深
      })
    })

    /**
     * 通过Table对象以及该Table对应的Shape 生成ShapedValue
     * Packed 指定为 Table
     * Unpacked 指定为 Table对应的Row的 case class, 不过没什么用因为RepShape不支持buildParams
     * 所以这里的ShapedValue主要是Query在做各种操作的时候将Table上附带的上下文信息传递给更新后的抽象语法树
     */
    ShapedValue(baseTable, RepShape[FlatShapeLevel, E, E#TableElementType])
  }
posted @ 2019-01-22 18:05  贰零妖舞  阅读(348)  评论(0编辑  收藏  举报