FunDA的特点之一是以数据流方式提供逐行数据操作支持。这项功能解决了FRM如Slick数据操作以SQL批次模式为主所产生的问题。为了实现安全高效的数据行操作,我们必须把FRM产生的Query结果集转变成一种强类型的结果集,也就是可以字段名称进行操作的数据行类型结果集。在前面的一篇讨论中我们介绍了通过Shape来改变Slick Query结果行类型。不过这样的转变方式需要编程人员对Slick有较深的了解。更重要的是这种方式太依赖Slick的内部功能了。我们希望FunDA可以支持多种FRM,所以应当尽量避免与任何FRM的紧密耦合。看来从FRM的返回结果开始进行数据行类型格式转换是一种比较现实的选择。一般来说我们还是可以假定任何FRM的使用者对于FRM的Query结果集类型是能理解的,因为他们的主要目的就是为了使用这个结果集。那么由FunDA的使用者提供一个Query结果数据行与另一种类型的类型转换函数应该不算是什么太高的要求吧。FunDA的设计思路是由用户提供一个目标类型以及FRM Query结果数据行到这个强类型行类型的类型转换函数后由FunDA提供强类型行结果集。下面先看一个典型的Slick Query例子:

 import slick.driver.H2Driver.api._
import scala.concurrent.duration._
import scala.concurrent.Await object TypedRow extends App { class AlbumsTable(tag: Tag) extends Table[
(Long,String,String,Option[Int],Int)](tag,"ALBUMS") {
def id = column[Long]("ID",O.PrimaryKey)
def title = column[String]("TITLE")
def artist = column[String]("ARTIST")
def year = column[Option[Int]]("YEAR")
def company = column[Int]("COMPANY")
def * = (id,title,artist,year,company)
}
val albums = TableQuery[AlbumsTable]
class CompanyTable(tag: Tag) extends Table[(Int,String)](tag,"COMPANY") {
def id = column[Int]("ID",O.PrimaryKey)
def name = column[String]("NAME")
def * = (id, name)
}
val companies = TableQuery[CompanyTable] val albumInfo = for {
a <- albums
c <- companies
if (a.company === c.id)
} yield(a.title,a.artist,a.year,c.name) val db = Database.forConfig("h2db") Await.result(db.run(albumInfo.result),Duration.Inf).foreach {r =>
println(s"${r._1} by ${r._2}, ${r._3.getOrElse(2000)} ${r._4}")
} }

上面例子里的albumInfo返回结果行类型是个Tuple类型:(String,String,Option[Int],Int),没有字段名的,所以只能用r._1,r._2...这样的位置注明方式来选择字段。用这种形式来使用返回结果很容易造成混乱,选用字段错误。

前面提到:如果用户能提供一个返回行类型和一个转换函数如下:

   case class AlbumRow(title: String,artist: String,year: Int,studio: String)
def toTypedRow(raw: (String,String,Option[Int],String)):AlbumRow =
AlbumRow(raw._1,raw._2,raw._3.getOrElse(),raw._4)

我们可以在读取数据后用这个函数来转换行类型:

   Await.result(db.run(albumInfo.result),Duration.Inf).map{raw =>
toTypedRow(raw)}.foreach {r =>
println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
}

返回行类型AlbumRow是个强类型。现在我吗可以用字段名来选择数据字段值了。不过,还是有些地方不对劲:应该是用户提供了目标行类型和转换函数后,直接调用一个函数就可以得到需要的结果集了。是的,我们就是要设计一套后台工具库来提供这个函数。

下面我们要设计FunDA的数据行类型class FDADataRow。这个类型现在基本上完全是针对Slick而设的,成功完成功能实现后期再考虑松散耦合问题。这个类型需要一个目标行类型定义和一个类型转换函数,外加一些Slick profile, database等信息。然后提供一个目标行类型结果集函数getTypedRows:

package com.bayakala.funda.rowtypes

import scala.concurrent.duration._
import scala.concurrent.Await
import slick.driver.JdbcProfile object DataRowType {
class FDADataRow[SOURCE, TARGET](slickProfile: JdbcProfile,convert: SOURCE => TARGET){
import slickProfile.api._ def getTypedRows(slickAction: DBIO[Iterable[SOURCE]])(slickDB: Database): Iterable[TARGET] =
Await.result(slickDB.run(slickAction), Duration.Inf).map(raw => convert(raw))
} object FDADataRow {
def apply[SOURCE, TARGET](slickProfile: JdbcProfile, converter: SOURCE => TARGET): FDADataRow[SOURCE, TARGET] =
new FDADataRow[SOURCE, TARGET](slickProfile, converter)
} }

下面是这个函数库的使用示范:

   import com.bayakala.funda.rowtypes.DataRowType

   val loader = FDADataRow(slick.driver.H2Driver, toTypedRow _)

   loader.getTypedRows(albumInfo.result)(db).foreach {r =>
println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
}

那么,作为一种数据行,又如何进行数据字段的更新呢?我们应该把它当作immutable object用函数式方法更新:

   def updateYear(from: AlbumRow): AlbumRow =
AlbumRow(from.title,from.artist,from.year+,from.studio) loader.getTypedRows(albumInfo.result)(db).map(updateYear).foreach {r =>
println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
}

updateYear是个典型的函数式方法:传入AlbumRow,返回新的AlbumRow。

下面是这篇讨论中的源代码:

FunDA函数库:

 package com.bayakala.funda.rowtypes

 import scala.concurrent.duration._
import scala.concurrent.Await
import slick.driver.JdbcProfile object DataRowType {
class FDADataRow[SOURCE, TARGET](slickProfile: JdbcProfile,convert: SOURCE => TARGET){
import slickProfile.api._ def getTypedRows(slickAction: DBIO[Iterable[SOURCE]])(slickDB: Database): Iterable[TARGET] =
Await.result(slickDB.run(slickAction), Duration.Inf).map(raw => convert(raw))
} object FDADataRow {
def apply[SOURCE, TARGET](slickProfile: JdbcProfile, converter: SOURCE => TARGET): FDADataRow[SOURCE, TARGET] =
new FDADataRow[SOURCE, TARGET](slickProfile, converter)
} }

功能测试源代码:

 import slick.driver.H2Driver.api._

 import scala.concurrent.duration._
import scala.concurrent.Await object TypedRow extends App { class AlbumsTable(tag: Tag) extends Table[
(Long,String,String,Option[Int],Int)](tag,"ALBUMS") {
def id = column[Long]("ID",O.PrimaryKey)
def title = column[String]("TITLE")
def artist = column[String]("ARTIST")
def year = column[Option[Int]]("YEAR")
def company = column[Int]("COMPANY")
def * = (id,title,artist,year,company)
}
val albums = TableQuery[AlbumsTable]
class CompanyTable(tag: Tag) extends Table[(Int,String)](tag,"COMPANY") {
def id = column[Int]("ID",O.PrimaryKey)
def name = column[String]("NAME")
def * = (id, name)
}
val companies = TableQuery[CompanyTable] val albumInfo =
for {
a <- albums
c <- companies
if (a.company === c.id)
} yield(a.title,a.artist,a.year,c.name) val db = Database.forConfig("h2db") Await.result(db.run(albumInfo.result),Duration.Inf).foreach {r =>
println(s"${r._1} by ${r._2}, ${r._3.getOrElse(2000)} ${r._4}")
} case class AlbumRow(title: String,artist: String,year: Int,studio: String)
def toTypedRow(raw: (String,String,Option[Int],String)):AlbumRow =
AlbumRow(raw._1,raw._2,raw._3.getOrElse(),raw._4) Await.result(db.run(albumInfo.result),Duration.Inf).map{raw =>
toTypedRow(raw)}.foreach {r =>
println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
} import com.bayakala.funda.rowtypes.DataRowType.FDADataRow val loader = FDADataRow(slick.driver.H2Driver, toTypedRow _) loader.getTypedRows(albumInfo.result)(db).foreach {r =>
println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
} def updateYear(from: AlbumRow): AlbumRow =
AlbumRow(from.title,from.artist,from.year+,from.studio) loader.getTypedRows(albumInfo.result)(db).map(updateYear).foreach {r =>
println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
} }

FunDA(1)- Query Result Row:强类型Query结果行的更多相关文章

  1. [20190214]11g Query Result Cache RC Latches补充.txt

    [20190214]11g Query Result Cache RC Latches补充.txt --//上午测试链接:http://blog.itpub.net/267265/viewspace- ...

  2. [20190214]11g Query Result Cache RC Latches.txt

    [20190214]11g Query Result Cache RC Latches.txt --//昨天我重复链接http://www.pythian.com/blog/oracle-11g-qu ...

  3. django+uwsgi+nginx数据表过大引起"out of memory for query result"

    昨天负责的一个项目突然爆“out of memory for query result”. 背景 项目的数据表是保存超过10m的文本数据,通过json方式保存进postgres中,上传一个13m的大文 ...

  4. PL/SQL:these query result are not updateable,include the ROWID to get updateab -----for update

    these query result are not updateable,include the ROWID to get updateab 原因: 其实,选中一个表后,右键,如果选择“query ...

  5. Oralce查询后修改数据,弹窗报提示these query result are not updateable,include the ROWID to get updateable

    select t.*, (select a.ANNEXNAME from base_annex a where a.id = t.closeFile) closeFileName, (select a ...

  6. java.sql.SQLException: Column count doesn't match value count at row 1 Query: insert into category values(null,?,?,?) Parameters: [1111111, 1111, 软件]

    java.sql.SQLException 问题: java.sql.SQLException: Column count doesn't match value count at row 1 Que ...

  7. 在PL/SQL DEV里面有把锁一样的按钮,点击它会跳出“these query result are not updateable,include the ROWID to get updateab

    在PL/SQL DEV里面有把锁一样的按钮,点击它会跳出“these query result are not updateable,include the ROWID to get updateab ...

  8. 九、MySQL报错( (1292, u"Truncated incorrect DOUBLE value: '424a000000066'") result = self._query(query))

    1.数据库sql语句:SELECT seat_id FROM netsale_order_seat os join netsale_order nor on os.order_code=nor.ord ...

  9. Query DSL for elasticsearch Query

    Query DSL Query DSL (资料来自: http://www.elasticsearch.cn/guide/reference/query-dsl/) http://elasticsea ...

随机推荐

  1. 微信小程序初探

    做为码农相信大家最近肯定都会听到微信小程序,虽然现阶段还没有正式开放注册,但大家可以还是可以开发测试. 到微信的WIKI(http://mp.weixin.qq.com/wiki?t=resource ...

  2. Express 教程 01 - 入门教程之经典的Hello World

    目录: 前言 一.Express?纳尼?! 二.开始前的准备工作 三.测试安装之经典的Hello World 四.使用express(1)来生成一个应用程序 五.说明 前言: 本篇文章是建立在Node ...

  3. 网站缓存技术总结( ehcache、memcache、redis对比)

    网站技术高速发展的今天,缓存技术已经成为大型网站的一个关键技术,缓存设计好坏直接关系的一个网站访问的速度,以及购置服务器的数量,甚至影响到用户的体验. 网站缓存按照存放的地点不同,可以分为客户端缓存. ...

  4. excel常用技巧

    复制表格时,如果要加上行标和列标.页面布局->工作表选项:标题,勾上打印->复制下拉框->复制为图片加上打印样式 一行长拆成几行短或几行短变成一行长的文本拆分,可以通过:填充-> ...

  5. WinRT自定义控件第一 - 转盘按钮控件

    之前的文章中,介绍了用WPF做一个转盘按钮控件,后来需要把这个控件移植到WinRT时,遇到了很大的问题,主要原因在于WPF和WinRT还是有很大不同的.这篇文章介绍了这个移植过程,由于2次实现的控件功 ...

  6. 【AI开发第一步】微软认知服务API应用

    目录 介绍 API分类 使用‘视觉’API完成的Demo 点击直接看干货 介绍 从3月份Google家的阿尔法狗打败韩国围棋冠军选手李世石,到之后微软Build2016大会宣布的“智能机器人”战略.种 ...

  7. Async和Await异步编程的原理

    1. 简介 从4.0版本开始.NET引入并行编程库,用户能够通过这个库快捷的开发并行计算和并行任务处理的程序.在4.5版本中.NET又引入了Async和Await两个新的关键字,在语言层面对并行编程给 ...

  8. ASP.NET MVC Model绑定(四)

    ASP.NET MVC Model绑定(四) 前言 前面的篇幅对于Model绑定器IModelBinder以及实现类型.Model绑定器提供程序都作了粗略的讲解,可以把Model绑定器想象成一个大的容 ...

  9. .Net 面试题 3C(CTS,CLS,CLR)

    1.CTS(Common Type System)通用类型系统 CTS不但实现了COM的变量兼容类型,而且还定义了通过用户自定义类型的方式来进行类型扩展.任何以.NET平台作为目标的语言必须建立它的数 ...

  10. 如何优雅地使用Sublime Text

    Sublime Text:一款具有代码高亮.语法提示.自动完成且反应快速的编辑器软件,不仅具有华丽的界面,还支持插件扩展机制,用她来写代码,绝对是一种享受.相比于难于上手的Vim,浮肿沉重的Eclip ...