在上一集的讨论里我们介绍并实现了强类型返回结果行。使用强类型主要的目的是当我们把后端数据库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. 在centos7上安装Jenkins

    在centos7上安装Jenkins 安装 添加yum repos,然后安装 sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins ...

  2. go语言:多个[]byte数组合并成一个[]byte

    场景:在开发中,要将多个[]byte数组合并成一个[]byte,初步实现思路如下: 1.获取多个[]byte长度 2.构造一个二维码数组 3.循环将[]byte拷贝到二维数组中 package gst ...

  3. CSS三个定位——常规、浮动、绝对定位

    .dage { width: 868px; background: #5B8C75; border: 10px solid #A08C5A; margin-top: -125px; margin-le ...

  4. BPM生产安全管理解决方案分享

    一.方案概述生产安全管理是企业生产管理的重要组成部分,组织实施好企业安全管理规划.指导.检查和决策,保证生产处于最佳安全状态是安全管理的重要内容和职责.H3 BPM企业生产安全管理解决方案是一套专门为 ...

  5. Android之网络数据存储

    一.网络保存数据介绍 可以使用网络来保存数据,在需要的时候从网络上获取数据,进而显示在App中. 用网络保存数据的方法有很多种,对于不同的网络数据采用不同的上传与获取方法. 本文利用LeanCloud ...

  6. Linux基础介绍【第二篇】

    远程连接Linux的原理 SHH远程连接介绍 当前,在几乎所有的互联网企业环境中,最常用的Linux提供远程连接服务的工具就是SSH软件,SSH分为SSH客户端和SSH服务端两部分.其中,SSH服务端 ...

  7. 在Ubuntu中搭建.NET开发环境

    Mono简介Mono是Xamarin公司C#和CLR的ECMA标准基于开发的一个开源的.NET实现版本,它是Linux平台上开发.NET应用程序首选.同时其也提供了Xamarin.IOS和Xamari ...

  8. node应用线上部署时锁定包的依赖版本

    npm shrinkwrap 我们使用node开发时,经常需要依赖一些模块来完成功能需求,而我们所依赖的模块也必然会依赖其他模块,就这样一级一级的依赖,而且这些依赖模块并不为我们所控制.一个产品或项目 ...

  9. [DeviceOne开发]-手势动画示例分享

    一.简介 这是iOS下的效果,android下完全一致.通过do_GestureView组件和do_Animation组件,deviceone能很容易实现复杂的跨平台纯原生动画效果,这个示例就是通过手 ...

  10. JavaScript继承的模拟实现

    我们都知道,在JavaScript中只能模拟实现OO中的"类",也就意味着,在JavaScript中没有类的继承.我们也只能通过在原对象里添加或改写属性来模拟实现. 先定义一个父类 ...