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

    Java列表踩过的坑 其中subList是RandomAccessSubList,不是序列化的列表,不可以加入tair. 加入tair测试代码 @Autowired private CacheMana ...

  2. 【原】无脑操作:express + MySQL 实现CRUD

    基于node.js的web开发框架express简单方便,很多项目中都在使用.这里结合MySQL数据库,实现最简单的CRUD操作. 开发环境: IDE:WebStorm DB:MySQL ------ ...

  3. 原生JS实现-星级评分系统

    今天我又写了个很酷的实例:星级评分系统(可自定义星星个数.显示信息) sufuStar.star();使用默认值5个星星,默认信息 var msg = [........]; sufuStar.sta ...

  4. 多个ul中第一个li获取定位

    如果我们只是获取一个ul中的第一个li的话,那么我们可以这样写: $("ul li:first"); $("ul li").eq(0); $("ul ...

  5. Oracle补全日志(Supplemental logging)

    Oracle补全日志(Supplemental logging)特性因其作用的不同可分为以下几种:最小(Minimal),支持所有字段(all),支持主键(primary key),支持唯一键(uni ...

  6. Linux根文件系统分析之init和busybox

    Hi,大家好!我是CrazyCatJack.今天给大家讲解Linux根文件系统的init进程和busybox的配置及编译. 先简单介绍一下,作为一个嵌入式系统,要想在硬件上正常使用的话.它的软件组成大 ...

  7. 【一起学OpenFOAM】系列由来

    1 为什么要学习OpenFOAM 掐指算起来,接触CFD也差不多有十个年头了,其间一直使用的商用CFD软件,有Fluent.CFX.StarCCM+等,这些商用软件各有其优缺点,都能较好的解决常规的工 ...

  8. 让 ASP.NET vNext 在 Mac OS 中飞呀飞。。。

    写在前面 阅读目录: 娓娓道来 Install ASP.NET vNext Command Line Tools 安装 Homebrew 使用 Homebrew,安装 KVM Install Subl ...

  9. Flappy Bird 源码走读

    参考:https://github.com/kirualex/SprityBird 该项目基于spritekit,代码的结构很清楚,感觉用来学习spritekit非常不错. 1.项目只有一个viewC ...

  10. WCF : 修复 Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service 问题

    摘要 : 最近遇到了一个奇怪的 WCF 安全配置问题, WCF Service 上面配置了Windows Authentication. IIS上也启用了 Windows Authentication ...