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])
}