在上一集的讨论里我们介绍并实现了强类型返回结果行。使用强类型主要的目的是当我们把后端数据库SQL批次操作搬到内存里转变成数据流式按行操作时能更方便、准确、高效地选定数据字段。在上集讨论示范里我们用集合的foreach方式模拟了一个最简单的数据流,并把从数据库里批次读取的数据集转换成一串连续的数据行来逐行使用。一般来说完整的流式数据处理流程包括了从数据库中读取数据、根据读取的每行数据状态再对后台数据库进行更新,包括:插入新数据、更新、删除等。那么在上篇中实现的流式操作基础上再添加一种指令行类型就可以完善整个数据处理流程了,就像下面这个图示:

Database => Query -> Collection => Streaming -> DataRow => QueryAction(DataRow) -> ActionRow => execAction(ActionRow) -> Database 

如果我们还是以Slick为目标FRM,那么这个ActionRow的类型就是Slick的DBIO[T]了:

 package com.bayakala.funda.rowtypes
import slick.dbio._
object ActionType {
type FDAAction[T] = DBIO[T]
}

记得有一次在一个Scala讨论区里遇到这样一个问题:如何把a表里的status字段更新成b表的status字段值,转化成SQL语句如下:

 update a,b set a.status=b.status where a.id=b.id

那位哥们的问题是如何用Slick来实现对a表的更新,不能用sql"???" interpolation 直接调用SQL语句,可能因为要求compile time语法check保障吧。这个问题用Slick Query还真的不太容易解决(能不能解决就不想费功夫去想了),这是因为FRM的SQL批次处理弱点。如果用FunDA的流式操作思路就会很容易解决了,只要用join Query把b.status读出来再用b.id=a.id逐个更新a.status。刚好,下面我们就示范通过ActionRow来解决这个问题。先用下面这段代码来设置测试数据:

 import slick.dbio.DBIO
import slick.driver.H2Driver.api._ import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
import slick.jdbc.meta.MTable
object ActionRowTest extends App { class ATable(tag: Tag) extends Table[(Int,String,Int)](tag,"TA") {
def id = column[Int]("id",O.PrimaryKey)
def flds = column[String]("aflds")
def status = column[Int]("status")
def * = (id,flds,status)
}
val tableA = TableQuery[ATable] class BTable(tag: Tag) extends Table[(Int,String,Int)](tag,"TB") {
def id = column[Int]("id",O.PrimaryKey)
def flds = column[String]("bflds")
def status = column[Int]("status")
def * = (id,flds,status)
}
val tableB = TableQuery[BTable] val insertAAction =
tableA ++= Seq (
(,"aaa",),
(,"bbb",),
(,"ccc",),
(,"ddd",),
(,"kkk",)
)
val insertBAction =
tableB ++= Seq (
(,"aaa",),
(,"bbb",),
(,"ccc",),
(,"ddd",),
(,"kkk",)
) val db = Database.forConfig("h2db") def tableExists(tables: Vector[MTable], tblname: String) =
tables.exists {t =>t.name.toString.contains(tblname)} def createSchemaIfNotExists(): Future[Unit] = {
db.run(MTable.getTables).flatMap {
case tables if !tableExists(tables,".TA") && !tableExists(tables,".TB") =>
println("Creating schemas for TA and TB...")
db.run((tableA.schema ++ tableB.schema).create)
case tables if !tableExists(tables,".TA") =>
println("Creating schema for TA ...")
db.run(tableA.schema.create)
case tables if !tableExists(tables,".TB") =>
println("Creating schema for TB ...")
db.run(tableB.schema.create)
case _ =>
println("Schema for TA, TB already created.")
Future.successful()
}
} def insertInitialData(): Future[Unit] = {
val cleanInsert = DBIO.seq(
tableA.delete, tableB.delete,
insertAAction,
insertBAction)
db.run(cleanInsert).andThen {
case Success(_) => println("Data insert completed.")
case Failure(e) => println(s"Data insert failed [${e.getMessage}]")
}
} Await.ready(db.run(sql"DROP TABLE TA; DROP TABLE TB".as[String]),Duration.Inf) val initResult = createSchemaIfNotExists().flatMap {_ => insertInitialData()}
Await.ready(initResult,Duration.Inf) }

用join query先把这两个表相关的字段值搬到内存转成强类型行FDADataRow:

 val selectAB = for {
a <- tableA
b <- tableB
if (a.id === b.id)
} yield (a.id,b.id,a.status,b.status) case class ABRow (id: Int, asts: Int, bsts: Int)
def toABRow(raw: (Int,Int,Int,Int)) = ABRow(raw._1,raw._3,raw._4) import com.bayakala.funda.rowtypes.DataRowType val loader = FDADataRow(slick.driver.H2Driver, toABRow _)
loader.getTypedRows(selectAB.result)(db).foreach {dataRow =>
println(s"ID:${dataRow.id} Status A = ${dataRow.asts}, B = ${dataRow.bsts}")
}

初始结果如下:

ID: Status A = , B =
ID: Status A = , B =
ID: Status A = , B =
ID: Status A = , B =

现在我们把每条数据行DataRow转成动作行ActionRow。然后把每条DataRow的asts字段值替换成bsts的字段值:

 import com.bayakala.funda.rowtypes.ActionType.FDAAction
def updateAStatus(row: ABRow): FDAAction[Int] = {
tableA.filter{r => r.id === row.id}
.map(_.status)
.update(row.asts)
} loader.getTypedRows(selectAB.result)(db).map(updateAStatus(_)).foreach {
actionRow =>
println(s"${actionRow.toString}")
}

显示结果如下:

slick.driver.JdbcActionComponent$UpdateActionExtensionMethodsImpl$$anon$@492691d7
slick.driver.JdbcActionComponent$UpdateActionExtensionMethodsImpl$$anon$@27216cd
slick.driver.JdbcActionComponent$UpdateActionExtensionMethodsImpl$$anon$@558bdf1f
slick.driver.JdbcActionComponent$UpdateActionExtensionMethodsImpl$$anon$@8576fa0

现在每条DataRow已经被转化成jdbc action类型了。

下一步我们只需要运行这些ActionRow就可以完成任务了:

   def execAction(act: FDAAction[Int]) = db.run(act)

    loader.getTypedRows(selectAB.result)(db)
.map(updateAStatus(_))
.map(execAction(_))

现在再看看数据库中的TA表状态:

  loader.getTypedRows(selectAB.result)(db).foreach {dataRow =>
println(s"ID:${dataRow.id} Status A = ${dataRow.asts}, B = ${dataRow.bsts}")
} 结果:
ID: Status A = , B =
ID: Status A = , B =
ID: Status A = , B =
ID: Status A = , B =

我们看到已经正确更新了TA的status字段值。

在这个示范中明显有很多不足之处:如果a.status=b.status应该省略更新步骤。这是因为foreach只能模拟最基本的数据流动。如果我们使用了具备强大功能的Stream工具库如scalaz-stream-fs2,就可以更好控制数据元素的流动。更重要的是scalaz-stream-fs2支持并行运算,那么上面所描述的流程:

Database => Query -> Collection => Streaming -> DataRow => QueryAction(DataRow) -> ActionRow => execAction(ActionRow) -> Database

几个 => 环节:Query、Streaming、QueryAction、execAction将可以并行运算,从而实现充分利用多核CPU硬件资源,提高运算效率的目的。

下面是这次讨论涉及的源代码:

 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)
} }
 package com.bayakala.funda.rowtypes
import slick.dbio._
object ActionType {
type FDAAction[T] = DBIO[T]
}
 import slick.dbio.DBIO
import slick.driver.H2Driver.api._ import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
import slick.jdbc.meta.MTable
object ActionRowTest extends App { class ATable(tag: Tag) extends Table[(Int,String,Int)](tag,"TA") {
def id = column[Int]("id",O.PrimaryKey)
def flds = column[String]("aflds")
def status = column[Int]("status")
def * = (id,flds,status)
}
val tableA = TableQuery[ATable] class BTable(tag: Tag) extends Table[(Int,String,Int)](tag,"TB") {
def id = column[Int]("id",O.PrimaryKey)
def flds = column[String]("bflds")
def status = column[Int]("status")
def * = (id,flds,status)
}
val tableB = TableQuery[BTable] val insertAAction =
tableA ++= Seq (
(,"aaa",),
(,"bbb",),
(,"ccc",),
(,"ddd",),
(,"kkk",)
)
val insertBAction =
tableB ++= Seq (
(,"aaa",),
(,"bbb",),
(,"ccc",),
(,"ddd",),
(,"kkk",)
) val db = Database.forConfig("h2db") def tableExists(tables: Vector[MTable], tblname: String) =
tables.exists {t =>t.name.toString.contains(tblname)} def createSchemaIfNotExists(): Future[Unit] = {
db.run(MTable.getTables).flatMap {
case tables if !tableExists(tables,".TA") && !tableExists(tables,".TB") =>
println("Creating schemas for TA and TB...")
db.run((tableA.schema ++ tableB.schema).create)
case tables if !tableExists(tables,".TA") =>
println("Creating schema for TA ...")
db.run(tableA.schema.create)
case tables if !tableExists(tables,".TB") =>
println("Creating schema for TB ...")
db.run(tableB.schema.create)
case _ =>
println("Schema for TA, TB already created.")
Future.successful()
}
} def insertInitialData(): Future[Unit] = {
val cleanInsert = DBIO.seq(
tableA.delete, tableB.delete,
insertAAction,
insertBAction)
db.run(cleanInsert).andThen {
case Success(_) => println("Data insert completed.")
case Failure(e) => println(s"Data insert failed [${e.getMessage}]")
}
} Await.ready(db.run(sql"DROP TABLE TA; DROP TABLE TB".as[String]),Duration.Inf) val initResult = createSchemaIfNotExists().flatMap {_ => insertInitialData()}
Await.ready(initResult,Duration.Inf) val selectAB = for {
a <- tableA
b <- tableB
if (a.id === b.id)
} yield (a.id,b.id,a.status,b.status) case class ABRow (id: Int, asts: Int, bsts: Int)
def toABRow(raw: (Int,Int,Int,Int)) = ABRow(raw._1,raw._3,raw._4) import com.bayakala.funda.rowtypes.DataRowType.FDADataRow val loader = FDADataRow(slick.driver.H2Driver, toABRow _)
loader.getTypedRows(selectAB.result)(db).foreach {dataRow =>
println(s"ID:${dataRow.id} Status A = ${dataRow.asts}, B = ${dataRow.bsts}")
} import com.bayakala.funda.rowtypes.ActionType.FDAAction
def updateAStatus(row: ABRow): FDAAction[Int] = {
tableA.filter{r => r.id === row.id}
.map(_.status)
.update(row.bsts)
} loader.getTypedRows(selectAB.result)(db).map(updateAStatus(_)).foreach {
actionRow =>
println(s"${actionRow.toString}")
} def execAction(act: FDAAction[Int]) = db.run(act) loader.getTypedRows(selectAB.result)(db)
.map(updateAStatus(_))
.map(execAction(_)) loader.getTypedRows(selectAB.result)(db).foreach {dataRow =>
println(s"ID:${dataRow.id} Status A = ${dataRow.asts}, B = ${dataRow.bsts}")
} }

FunDA(2)- Streaming Data Operation:流式数据操作的更多相关文章

  1. Spark Streaming:大规模流式数据处理的新贵(转)

    原文链接:Spark Streaming:大规模流式数据处理的新贵 摘要:Spark Streaming是大规模流式数据处理的新贵,将流式计算分解成一系列短小的批处理作业.本文阐释了Spark Str ...

  2. Spark Streaming:大规模流式数据处理的新贵

    转自:http://www.csdn.net/article/2014-01-28/2818282-Spark-Streaming-big-data 提到Spark Streaming,我们不得不说一 ...

  3. 翻译-In-Stream Big Data Processing 流式大数据处理

    相当长一段时间以来,大数据社区已经普遍认识到了批量数据处理的不足.很多应用都对实时查询和流式处理产生了迫切需求.最近几年,在这个理念的推动下,催生出了一系列解决方案,Twitter Storm,Yah ...

  4. 字节跳动流式数据集成基于Flink Checkpoint两阶段提交的实践和优化

    背景 字节跳动开发套件数据集成团队(DTS ,Data Transmission Service)在字节跳动内基于 Flink 实现了流批一体的数据集成服务.其中一个典型场景是 Kafka/ByteM ...

  5. Hadoop_11_HDFS的流式 API 操作

    对于MapReduce等框架来说,需要有一套更底层的API来获取某个指定文件中的一部分数据,而不是一整个文件 因此使用流的方式来操作 HDFS上的文件,可以实现读取指定偏移量范围的数据 1.客户端测试 ...

  6. 流式数据分析模型kafka+storm

    http://www.cnblogs.com/panfeng412/archive/2012/07/29/storm-stream-model-analysis-and-discussion.html ...

  7. Java 8 集合之流式(Streams)操作, Streams API 详解

    因为当时公司的业务需要对集合进行各种各样的业务逻辑操作,为了提高性能,就用到了这个东西,因为以往我们以前用集合都是需要去遍历(串行),所以效率和性能都不是特别的好,而Streams就可以使用并行的方式 ...

  8. Spark之 Spark Streaming流式处理

    SparkStreaming Spark Streaming类似于Apache Storm,用于流式数据的处理.Spark Streaming有高吞吐量和容错能力强等特点.Spark Streamin ...

  9. Mysql中使用JDBC流式查询避免数据量过大导致OOM

    一.前言 java 中MySQL JDBC 封装了流式查询操作,通过设置几个参数,就可以避免一次返回数据过大导致 OOM. 二.如何使用 2.1 之前查询 public void selectData ...

随机推荐

  1. CSS 3 学习——渐变

    通过CSS渐变创建的是一个没有固定比例和固定尺寸的<image>类型,也就是说是一张图片,这张图片的尺寸由所应用的元素的相关信息决定.凡是支持图片类型的CSS属性都可以设置渐变,而支持颜色 ...

  2. JQuery的基础和应用

    <参考文档>   1.什么是?    DOM的作用:提供了一种动态的操作HTML元素的方法.    jQuery是一个优秀的js库.用来操作HTML元素的工具.    jQuery和DOM ...

  3. 满堂红CIO邓劲翔:房屋中介突围

    人脸识别.客户关系管理进度监控.业务流程实时监控.网站访问人数及流量实时监控等实际企业应用场景淋漓尽致.羽羽如生的以大屏幕上图表形式展现在人们面前,如果你不去继续询问,你不会知道这是一家才刚刚在房地产 ...

  4. RMS:不能对生产服务器使用测试清单

    问题说明:在使用office软件RMS加密时报:不能对生产服务器使用测试清单,或者使用 rmsbulk.exe进行RMS加密时,报不能连接到RMS服务器. 解决办法: 请到https://suppor ...

  5. iOS从零开始学习直播之1.播放

      对于直播来说,客户端主要做两件事情,推流和播放.今天先讲播放. 播放流程 1.拉流:服务器已有直播内容,从指定地址进行拉取的过程.其实就是向服务器请求数据. 2.解码:对视屏数据进行解压缩. 3. ...

  6. 如何区别char与varchar?

    1.varchar与char两个数据类型用于存储字符串长度小于255的字符,MySQL5.0之前是varchar支持最大255.比如向一个长度为40个字符的字段中输入一个为10个字符的数据.使用var ...

  7. Linux下用netstat查看网络状态、端口状态(转)

    转:http://blog.csdn.net/guodongdongnumber1/article/details/11383019 在linux一般使用netstat 来查看系统端口使用情况步.  ...

  8. Flexbox 自由的布局

    css3提出了一种新的布局方式.她并没有以摧枯拉朽之势博得我的喜爱.我和她的故事总是伴随着苦涩的味道.世道变了,总要做出些选择才能跟紧步伐.她很强大,能满足你天马行空的需求而不必抓掉一大把头发.她却很 ...

  9. 《图解TCP/IP》读书笔记

    一.国际惯例:书托 这是一本图文并茂的网络管理技术书籍,旨在让广大读者理解TCP/IP的基本知识.掌握TCP/IP的基本技能. 书中讲解了网络基础知识.TCP/IP基础知识.数据链路.IP协议.IP协 ...

  10. is和as

    一.明确两个基本概念 隐式转换: a.对于值类型,低精度=>高精度.eg:int=>long b.对于引用类型,子类向祖宗类转换过程.eg:对象=>Object 显式转换:显示转换是 ...