在上一集的讨论里我们介绍并实现了强类型返回结果行。使用强类型主要的目的是当我们把后端数据库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程序的151个建议(第6章:枚举和注解___建议88~92)

    建议88:用枚举实现工厂方法模式更简洁 工厂方法模式(Factory Method Pattern)是" 创建对象的接口,让子类决定实例化哪一个类,并使一个类的实例化延迟到其它子类" ...

  2. 网站里加入QQ在线客服

    1.开启"QQ在线状态"服务  http://jingyan.baidu.com/article/b24f6c823425a586bfe5da1f.html http://www. ...

  3. Android中AlarmManager使用示例(持续更新,已经更改)

    现在普遍的手机都会有一个闹钟的功能,如果使用Android来实现一个闹钟可以使用AtarmManager来实现.AtarmManager提供了一种系统级的提示服务,允许你安排在将来的某个时间执行一个服 ...

  4. ILJMALL project过程中遇到Fragment嵌套问题:IllegalArgumentException: Binary XML file line #23: Duplicate id

    出现场景:当点击"分类"再返回"首页"时,发生error退出   BUG描述:Caused by: java.lang.IllegalArgumentExcep ...

  5. 【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践

    提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OS ...

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

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

  7. hexo+github搭建个人博客

    最近用hexo+github搭建了自己的个人博客-https://liuyfl.github.io,其中碰到了一些问题,记录下来,以便查阅. hexo+github在win7环境下搭建个人博客:hex ...

  8. .NET Core性能测试组件BenchmarkDotNet 支持.NET Framework Mono

    .NET Core 超强性能测试组件BenchmarkDotNet 支持Full .NET Framework, .NET Core (RTM), Mono. BenchmarkDotNet支持 C# ...

  9. hadoop 笔记(zookeeper)

    1.安装 需要提前安装java环境,本文下载zookeeper-3.3.6.tar.gz包. 1.1 tar -zxvf zookeeper-3.3.6.tar.gz 1.2 修改conf中的zoo_ ...

  10. 简易nginx TCP反向代理设置

    nginx从1.9.0开始支持TCP反向代理,之前只支持HTTP.这是我的系统示意图: 为何需要? 为什么需要反向代理?主要是: 负载均衡 方便管控 比如我现在要更新后端服务器,如果不用负载均衡的话, ...