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例子:

  1. import slick.driver.H2Driver.api._
  2. import scala.concurrent.duration._
  3. import scala.concurrent.Await
  4.  
  5. object TypedRow extends App {
  6.  
  7. class AlbumsTable(tag: Tag) extends Table[
  8. (Long,String,String,Option[Int],Int)](tag,"ALBUMS") {
  9. def id = column[Long]("ID",O.PrimaryKey)
  10. def title = column[String]("TITLE")
  11. def artist = column[String]("ARTIST")
  12. def year = column[Option[Int]]("YEAR")
  13. def company = column[Int]("COMPANY")
  14. def * = (id,title,artist,year,company)
  15. }
  16. val albums = TableQuery[AlbumsTable]
  17. class CompanyTable(tag: Tag) extends Table[(Int,String)](tag,"COMPANY") {
  18. def id = column[Int]("ID",O.PrimaryKey)
  19. def name = column[String]("NAME")
  20. def * = (id, name)
  21. }
  22. val companies = TableQuery[CompanyTable]
  23.  
  24. val albumInfo = for {
  25. a <- albums
  26. c <- companies
  27. if (a.company === c.id)
  28. } yield(a.title,a.artist,a.year,c.name)
  29.  
  30. val db = Database.forConfig("h2db")
  31.  
  32. Await.result(db.run(albumInfo.result),Duration.Inf).foreach {r =>
  33. println(s"${r._1} by ${r._2}, ${r._3.getOrElse(2000)} ${r._4}")
  34. }
  35.  
  36. }

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

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

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

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

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

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

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

  1. package com.bayakala.funda.rowtypes
  2.  
  3. import scala.concurrent.duration._
  4. import scala.concurrent.Await
  5. import slick.driver.JdbcProfile
  6.  
  7. object DataRowType {
  8. class FDADataRow[SOURCE, TARGET](slickProfile: JdbcProfile,convert: SOURCE => TARGET){
  9. import slickProfile.api._
  10.  
  11. def getTypedRows(slickAction: DBIO[Iterable[SOURCE]])(slickDB: Database): Iterable[TARGET] =
  12. Await.result(slickDB.run(slickAction), Duration.Inf).map(raw => convert(raw))
  13. }
  14.  
  15. object FDADataRow {
  16. def apply[SOURCE, TARGET](slickProfile: JdbcProfile, converter: SOURCE => TARGET): FDADataRow[SOURCE, TARGET] =
  17. new FDADataRow[SOURCE, TARGET](slickProfile, converter)
  18. }
  19.  
  20. }

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

  1. import com.bayakala.funda.rowtypes.DataRowType
  2.  
  3. val loader = FDADataRow(slick.driver.H2Driver, toTypedRow _)
  4.  
  5. loader.getTypedRows(albumInfo.result)(db).foreach {r =>
  6. println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
  7. }

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

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

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

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

FunDA函数库:

  1. package com.bayakala.funda.rowtypes
  2.  
  3. import scala.concurrent.duration._
  4. import scala.concurrent.Await
  5. import slick.driver.JdbcProfile
  6.  
  7. object DataRowType {
  8. class FDADataRow[SOURCE, TARGET](slickProfile: JdbcProfile,convert: SOURCE => TARGET){
  9. import slickProfile.api._
  10.  
  11. def getTypedRows(slickAction: DBIO[Iterable[SOURCE]])(slickDB: Database): Iterable[TARGET] =
  12. Await.result(slickDB.run(slickAction), Duration.Inf).map(raw => convert(raw))
  13. }
  14.  
  15. object FDADataRow {
  16. def apply[SOURCE, TARGET](slickProfile: JdbcProfile, converter: SOURCE => TARGET): FDADataRow[SOURCE, TARGET] =
  17. new FDADataRow[SOURCE, TARGET](slickProfile, converter)
  18. }
  19.  
  20. }

功能测试源代码:

  1. import slick.driver.H2Driver.api._
  2.  
  3. import scala.concurrent.duration._
  4. import scala.concurrent.Await
  5.  
  6. object TypedRow extends App {
  7.  
  8. class AlbumsTable(tag: Tag) extends Table[
  9. (Long,String,String,Option[Int],Int)](tag,"ALBUMS") {
  10. def id = column[Long]("ID",O.PrimaryKey)
  11. def title = column[String]("TITLE")
  12. def artist = column[String]("ARTIST")
  13. def year = column[Option[Int]]("YEAR")
  14. def company = column[Int]("COMPANY")
  15. def * = (id,title,artist,year,company)
  16. }
  17. val albums = TableQuery[AlbumsTable]
  18. class CompanyTable(tag: Tag) extends Table[(Int,String)](tag,"COMPANY") {
  19. def id = column[Int]("ID",O.PrimaryKey)
  20. def name = column[String]("NAME")
  21. def * = (id, name)
  22. }
  23. val companies = TableQuery[CompanyTable]
  24.  
  25. val albumInfo =
  26. for {
  27. a <- albums
  28. c <- companies
  29. if (a.company === c.id)
  30. } yield(a.title,a.artist,a.year,c.name)
  31.  
  32. val db = Database.forConfig("h2db")
  33.  
  34. Await.result(db.run(albumInfo.result),Duration.Inf).foreach {r =>
  35. println(s"${r._1} by ${r._2}, ${r._3.getOrElse(2000)} ${r._4}")
  36. }
  37.  
  38. case class AlbumRow(title: String,artist: String,year: Int,studio: String)
  39. def toTypedRow(raw: (String,String,Option[Int],String)):AlbumRow =
  40. AlbumRow(raw._1,raw._2,raw._3.getOrElse(),raw._4)
  41.  
  42. Await.result(db.run(albumInfo.result),Duration.Inf).map{raw =>
  43. toTypedRow(raw)}.foreach {r =>
  44. println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
  45. }
  46.  
  47. import com.bayakala.funda.rowtypes.DataRowType.FDADataRow
  48.  
  49. val loader = FDADataRow(slick.driver.H2Driver, toTypedRow _)
  50.  
  51. loader.getTypedRows(albumInfo.result)(db).foreach {r =>
  52. println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
  53. }
  54.  
  55. def updateYear(from: AlbumRow): AlbumRow =
  56. AlbumRow(from.title,from.artist,from.year+,from.studio)
  57.  
  58. loader.getTypedRows(albumInfo.result)(db).map(updateYear).foreach {r =>
  59. println(s"${r.title} by ${r.artist}, ${r.year} ${r.studio}")
  60. }
  61.  
  62. }

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. ES6(块级作用域)

    我们都知道在javascript里是没有块级作用域的,而ES6添加了块级作用域,块级作用域能带来什么好处呢?为什么会添加这个功能呢?那就得了解ES5没有块级作用域时出现了哪些问题. ES5在没有块级作 ...

  2. webpack学习总结

    前言 在还未接触webpack,就有几个疑问: 1. webpack本质上是什么? 2. 跟异步模块加载有关系吗? 3. 可否生成多个文件,一定是一个? 4. 被引用的文件有其他异步加载模块怎么办? ...

  3. OpenGL ES 3.0: 图元重启(Primitive restart)

    [TOC] 背景概述 在OpenGL绘制图形时,可能需要绘制多个并不相连的图形.这样的情况下这几个图形没法被当做一个图形来处理.也就需要多次调用 DrawArrays 或 DrawElements. ...

  4. a标签绝对定位,点击区域被图片遮挡(IE下)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. mono for android中使用dapper或petapoco对sqlite进行数据操作

    在mono for android中使用dapper或petapoco,很简单,新建android 类库项目,直接把原来的文件复制过来,对Connection连接报错部分进行注释和修改就可以运行了.( ...

  6. Xamarin.Android广播接收器与绑定服务

    一.前言 学习了前面的活动与服务后,你会发现服务对于活动而言似乎就是透明的,相反活动对于服务也是透明的,所以我们还需要一中机制能够将服务和活动之间架起一座桥梁,通过本节的学习,你将会学到广播与绑定服务 ...

  7. Linux学习日记-使用EF6 Code First(四)

    一.在linux上使用EF 开发环境 VS2013+mono 3.10.0 +EF 6.1.0 先检测一下EF是不是6的 如果不是  请参阅 Linux学习日记-EF6的安装升级(三) 由于我的数据库 ...

  8. 赠书:HTML5 Canvas 2d 编程必读的两本经典

    赠书:HTML5 Canvas 2d 编程必读的两本经典 这两年多一直在和HTML5 Canvas 打交道,也带领团队开发了世界首款基于HTML5 Canvas 的演示文档工具---AxeSlide( ...

  9. javascript关于立即函数

    以前的知识总是忘,遇到代码又看不懂.要再复习一下,顺便记录一下. 关于立即执行函数:  函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名 ...

  10. 《JavaScript高级程序设计》读书笔记 2

    1,动态模型模式 function Person (name,age,job) { this.name=name; this.age=age; this.job=job; if(typeof this ...