在上一篇博文里我们把JDBC-Engine的读取操作部分分离出来进行了讨论,在这篇准备把更新Update部分功能介绍一下。当然,JDBC-Engine的功能是基于ScalikeJDBC的,所有的操作和属性都包嵌在SQL这个类型中:

  1. /**
  2. * SQL abstraction.
  3. *
  4. * @param statement SQL template
  5. * @param rawParameters parameters
  6. * @param f extractor function
  7. * @tparam A return type
  8. */
  9. abstract class SQL[A, E <: WithExtractor](
  10. val statement: String,
  11. private[scalikejdbc] val rawParameters: Seq[Any]
  12. )(f: WrappedResultSet => A)
  13. {...}

Update功能置于下面这几个子类中:

  1. /**
  2. * SQL which execute java.sql.Statement#executeUpdate().
  3. *
  4. * @param statement SQL template
  5. * @param parameters parameters
  6. * @param before before filter
  7. * @param after after filter
  8. */
  9. class SQLUpdate(val statement: String, val parameters: Seq[Any], val tags: Seq[String] = Nil)(
  10. val before: (PreparedStatement) => Unit
  11. )(
  12. val after: (PreparedStatement) => Unit
  13. ) {
  14. def apply()(implicit session: DBSession): Int = {
  15. val attributesSwitcher = new DBSessionAttributesSwitcher(SQL("").tags(tags: _*))
  16. session match {
  17. case AutoSession =>
  18. DB.autoCommit(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
  19. case NamedAutoSession(name, _) =>
  20. NamedDB(name, session.settings).autoCommit(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
  21. case ReadOnlyAutoSession =>
  22. DB.readOnly(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
  23. case ReadOnlyNamedAutoSession(name, _) =>
  24. NamedDB(name, session.settings).readOnly(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
  25. case _ =>
  26. DBSessionWrapper(session, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*)
  27. }
  28. }
  29. }
  30. /**
  31. * SQL which execute java.sql.Statement#execute().
  32. *
  33. * @param statement SQL template
  34. * @param parameters parameters
  35. * @param before before filter
  36. * @param after after filter
  37. */
  38. class SQLExecution(val statement: String, val parameters: Seq[Any], val tags: Seq[String] = Nil)(
  39. val before: (PreparedStatement) => Unit
  40. )(
  41. val after: (PreparedStatement) => Unit
  42. ) {
  43. def apply()(implicit session: DBSession): Boolean = {
  44. val attributesSwitcher = new DBSessionAttributesSwitcher(SQL("").tags(tags: _*))
  45. val f: DBSession => Boolean = DBSessionWrapper(_, attributesSwitcher).executeWithFilters(before, after, statement, parameters: _*)
  46. // format: OFF
  47. session match {
  48. case AutoSession => DB.autoCommit(f)
  49. case NamedAutoSession(name, _) => NamedDB(name, session.settings).autoCommit(f)
  50. case ReadOnlyAutoSession => DB.readOnly(f)
  51. case ReadOnlyNamedAutoSession(name, _) => NamedDB(name, session.settings).readOnly(f)
  52. case _ => f(session)
  53. }
  54. // format: ON
  55. }
  56. }
  57. /**
  58. * SQL which execute java.sql.Statement#executeBatch().
  59. *
  60. * @param statement SQL template
  61. * @param parameters parameters
  62. */
  63. class SQLBatch(val statement: String, val parameters: Seq[Seq[Any]], val tags: Seq[String] = Nil) {
  64. def apply[C[_]]()(implicit session: DBSession, cbf: CanBuildFrom[Nothing, Int, C[Int]]): C[Int] = {
  65. val attributesSwitcher = new DBSessionAttributesSwitcher(SQL("").tags(tags: _*))
  66. val f: DBSession => C[Int] = DBSessionWrapper(_, attributesSwitcher).batch(statement, parameters: _*)
  67. // format: OFF
  68. session match {
  69. case AutoSession => DB.autoCommit(f)
  70. case NamedAutoSession(name, _) => NamedDB(name, session.settings).autoCommit(f)
  71. case ReadOnlyAutoSession => DB.readOnly(f)
  72. case ReadOnlyNamedAutoSession(name, _) => NamedDB(name, session.settings).readOnly(f)
  73. case _ => f(session)
  74. }
  75. // format: ON
  76. }
  77. }

按照JDBC-Engine的功能设计要求,我们大约把Update功能分成数据表构建操作DDL、批次运算Batch、和普通Update几种类型。我们是通过JDBCContext来定义具体的Update功能类型:

  1. object JDBCContext {
  2. type SQLTYPE = Int
  3. val SQL_SELECT: Int = 0
  4. val SQL_EXEDDL= 1
  5. val SQL_UPDATE = 2
  6. val RETURN_GENERATED_KEYVALUE = true
  7. val RETURN_UPDATED_COUNT = false
  8. }
  9. case class JDBCContext(
  10. dbName: Symbol,
  11. statements: Seq[String] = Nil,
  12. parameters: Seq[Seq[Any]] = Nil,
  13. fetchSize: Int = 100,
  14. queryTimeout: Option[Int] = None,
  15. queryTags: Seq[String] = Nil,
  16. sqlType: JDBCContext.SQLTYPE = JDBCContext.SQL_SELECT,
  17. batch: Boolean = false,
  18. returnGeneratedKey: Seq[Option[Any]] = Nil,
  19. // no return: None, return by index: Some(1), by name: Some("id")
  20. preAction: Option[PreparedStatement => Unit] = None,
  21. postAction: Option[PreparedStatement => Unit] = None) {
  22. ctx =>
  23. //helper functions
  24. def appendTag(tag: String): JDBCContext = ctx.copy(queryTags = ctx.queryTags :+ tag)
  25. def appendTags(tags: Seq[String]): JDBCContext = ctx.copy(queryTags = ctx.queryTags ++ tags)
  26. def setFetchSize(size: Int): JDBCContext = ctx.copy(fetchSize = size)
  27. def setQueryTimeout(time: Option[Int]): JDBCContext = ctx.copy(queryTimeout = time)
  28. def setPreAction(action: Option[PreparedStatement => Unit]): JDBCContext = {
  29. if (ctx.sqlType == JDBCContext.SQL_UPDATE &&
  30. !ctx.batch && ctx.statements.size == 1)
  31. ctx.copy(preAction = action)
  32. else
  33. throw new IllegalStateException("JDBCContex setting error: preAction not supported!")
  34. }
  35. def setPostAction(action: Option[PreparedStatement => Unit]): JDBCContext = {
  36. if (ctx.sqlType == JDBCContext.SQL_UPDATE &&
  37. !ctx.batch && ctx.statements.size == 1)
  38. ctx.copy(postAction = action)
  39. else
  40. throw new IllegalStateException("JDBCContex setting error: preAction not supported!")
  41. }
  42. def appendDDLCommand(_statement: String, _parameters: Any*): JDBCContext = {
  43. if (ctx.sqlType == JDBCContext.SQL_EXEDDL) {
  44. ctx.copy(
  45. statements = ctx.statements ++ Seq(_statement),
  46. parameters = ctx.parameters ++ Seq(Seq(_parameters))
  47. )
  48. } else
  49. throw new IllegalStateException("JDBCContex setting error: option not supported!")
  50. }
  51. def appendUpdateCommand(_returnGeneratedKey: Boolean, _statement: String, _parameters: Any*): JDBCContext = {
  52. if (ctx.sqlType == JDBCContext.SQL_UPDATE && !ctx.batch) {
  53. ctx.copy(
  54. statements = ctx.statements ++ Seq(_statement),
  55. parameters = ctx.parameters ++ Seq(_parameters),
  56. returnGeneratedKey = ctx.returnGeneratedKey ++ (if (_returnGeneratedKey) Seq(Some(1)) else Seq(None))
  57. )
  58. } else
  59. throw new IllegalStateException("JDBCContex setting error: option not supported!")
  60. }
  61. def appendBatchParameters(_parameters: Any*): JDBCContext = {
  62. if (ctx.sqlType != JDBCContext.SQL_UPDATE || !ctx.batch)
  63. throw new IllegalStateException("JDBCContex setting error: batch parameters only supported for SQL_UPDATE and batch = true!")
  64. var matchParams = true
  65. if (ctx.parameters != Nil)
  66. if (ctx.parameters.head.size != _parameters.size)
  67. matchParams = false
  68. if (matchParams) {
  69. ctx.copy(
  70. parameters = ctx.parameters ++ Seq(_parameters)
  71. )
  72. } else
  73. throw new IllegalStateException("JDBCContex setting error: batch command parameters not match!")
  74. }
  75. def setBatchReturnGeneratedKeyOption(returnKey: Boolean): JDBCContext = {
  76. if (ctx.sqlType != JDBCContext.SQL_UPDATE || !ctx.batch)
  77. throw new IllegalStateException("JDBCContex setting error: only supported in batch update commands!")
  78. ctx.copy(
  79. returnGeneratedKey = if (returnKey) Seq(Some(1)) else Nil
  80. )
  81. }
  82. def setQueryCommand(_statement: String, _parameters: Any*): JDBCContext = {
  83. ctx.copy(
  84. statements = Seq(_statement),
  85. parameters = Seq(_parameters),
  86. sqlType = JDBCContext.SQL_SELECT,
  87. batch = false
  88. )
  89. }
  90. def setDDLCommand(_statement: String, _parameters: Any*): JDBCContext = {
  91. ctx.copy(
  92. statements = Seq(_statement),
  93. parameters = Seq(_parameters),
  94. sqlType = JDBCContext.SQL_EXEDDL,
  95. batch = false
  96. )
  97. }
  98. def setUpdateCommand(_returnGeneratedKey: Boolean, _statement: String, _parameters: Any*): JDBCContext = {
  99. ctx.copy(
  100. statements = Seq(_statement),
  101. parameters = Seq(_parameters),
  102. returnGeneratedKey = if (_returnGeneratedKey) Seq(Some(1)) else Seq(None),
  103. sqlType = JDBCContext.SQL_UPDATE,
  104. batch = false
  105. )
  106. }
  107. def setBatchCommand(_statement: String): JDBCContext = {
  108. ctx.copy (
  109. statements = Seq(_statement),
  110. sqlType = JDBCContext.SQL_UPDATE,
  111. batch = true
  112. )
  113. }
  114. }

JDBCContext还提供了不少的Helper函数来协助构建特别功能的JDBCContext对象,如:setQueryCommand, setDDLCommand, setUpdateCommand, setBatchCommand。这些Helper函数提供Update功能定义的几个主要元素包括:SQL语句主体包括参数占位的statement、输入参数parameter、是否需要返回系统自动产生的主键returnGeneratedKey。在ScalikeJDBC中所有类型的Update功能可以用下面几类内部函数实现,包括:

  1. private[this] def batchInternal[C[_], A](
  2. template: String,
  3. paramsList: Seq[Seq[Any]],
  4. execute: StatementExecutor => scala.Array[A]
  5. )(implicit cbf: CanBuildFrom[Nothing, A, C[A]]): C[A] = {
  6. ensureNotReadOnlySession(template)
  7. paramsList match {
  8. case Nil => Seq.empty[A].to[C]
  9. case _ =>
  10. using(createBatchStatementExecutor(
  11. conn = conn,
  12. template = template,
  13. returnGeneratedKeys = false,
  14. generatedKeyName = None
  15. )) { executor =>
  16. paramsList.foreach {
  17. params =>
  18. executor.bindParams(params)
  19. executor.addBatch()
  20. }
  21. execute(executor).to[C]
  22. }
  23. }
  24. }
  25. private[this] def updateWithFiltersInternal[A](
  26. returnGeneratedKeys: Boolean,
  27. before: (PreparedStatement) => Unit,
  28. after: (PreparedStatement) => Unit,
  29. template: String,
  30. execute: StatementExecutor => A,
  31. params: Seq[Any]
  32. ): A = {
  33. ensureNotReadOnlySession(template)
  34. using(createStatementExecutor(
  35. conn = conn,
  36. template = template,
  37. params = params,
  38. returnGeneratedKeys = returnGeneratedKeys
  39. )) {
  40. executor =>
  41. before(executor.underlying)
  42. val count = execute(executor)
  43. after(executor.underlying)
  44. count
  45. }
  46. }
  47. private[this] def updateWithAutoGeneratedKeyNameAndFiltersInternal[A](
  48. returnGeneratedKeys: Boolean,
  49. generatedKeyName: String,
  50. before: (PreparedStatement) => Unit,
  51. after: (PreparedStatement) => Unit,
  52. template: String,
  53. execute: StatementExecutor => A,
  54. params: Seq[Any]
  55. ): A = {
  56. ensureNotReadOnlySession(template)
  57. using(createStatementExecutor(
  58. conn = conn,
  59. template = template,
  60. params = params,
  61. returnGeneratedKeys = returnGeneratedKeys,
  62. generatedKeyName = Option(generatedKeyName)
  63. )) {
  64. executor =>
  65. before(executor.underlying)
  66. val count = execute(executor)
  67. after(executor.underlying)
  68. count
  69. }
  70. }

我们可以看到所有类型的Update都是通过构建StatementExecutor并按其属性进行运算来实现的:

  1. /**
  2. * java.sql.Statement Executor.
  3. *
  4. * @param underlying preparedStatement
  5. * @param template SQL template
  6. * @param singleParams parameters for single execution (= not batch execution)
  7. * @param isBatch is batch flag
  8. */
  9. case class StatementExecutor(
  10. underlying: PreparedStatement,
  11. template: String,
  12. connectionAttributes: DBConnectionAttributes,
  13. singleParams: Seq[Any] = Nil,
  14. tags: Seq[String] = Nil,
  15. isBatch: Boolean = false,
  16. settingsProvider: SettingsProvider = SettingsProvider.default
  17. ) extends LogSupport with UnixTimeInMillisConverterImplicits with AutoCloseable {...}

这个StatementExcutor类的属性和我们的JDBCContext属性很接近。好了,回到JDBC-Engine Update功能定义。首先是DDL功能:

  1. def jdbcExcuteDDL(ctx: JDBCContext): Try[String] = {
  2. if (ctx.sqlType != SQL_EXEDDL) {
  3. Failure(new IllegalStateException("JDBCContex setting error: sqlType must be 'SQL_EXEDDL'!"))
  4. }
  5. else {
  6. NamedDB(ctx.dbName) localTx { implicit session =>
  7. Try {
  8. ctx.statements.foreach { stm =>
  9. val ddl = new SQLExecution(statement = stm, parameters = Nil)(
  10. before = WrappedResultSet => {})(
  11. after = WrappedResultSet => {})
  12. ddl.apply()
  13. }
  14. "SQL_EXEDDL executed succesfully."
  15. }
  16. }
  17. }
  18. }

 所有JDBC-Engine的Update功能都是一个事务处理Transaction中的多条更新语句。DDL语句不需要参数所以只需要提供statement就足够了。下面是这个函数的使用示范:

  1. ConfigDBsWithEnv("dev").setup('h2)
  2. ConfigDBsWithEnv("dev").loadGlobalSettings()
  3. val dropSQL: String ="""
  4. drop table members
  5. """
  6. val createSQL: String ="""
  7. create table members (
  8. id serial not null primary key,
  9. name varchar(30) not null,
  10. description varchar(1000),
  11. birthday date,
  12. created_at timestamp not null,
  13. picture blob
  14. )"""
  15. var ctx = JDBCContext('h2)
  16. try {
  17. ctx = ctx.setDDLCommand(dropSQL)
  18. .appendDDLCommand(createSQL)
  19. }
  20. catch {
  21. case e: Exception => println(e.getMessage)
  22. }
  23. val resultCreateTable = jdbcExcuteDDL(ctx)
  24. resultCreateTable match {
  25. case Success(msg) => println(msg)
  26. case Failure(err) => println(s"${err.getMessage}")
  27. }

在这里我们修改了上次使用的members表,增加了一个blob类的picture列。这个示范在一个完整的Transaction里包括了两条DDL语句。

批次更新batch-update是指多条输入参数在一条统一的statement上施用:

  1. def jdbcBatchUpdate[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  2. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  3. if (ctx.statements == Nil)
  4. throw new IllegalStateException("JDBCContex setting error: statements empty!")
  5. if (ctx.sqlType != SQL_UPDATE) {
  6. Failure(new IllegalStateException("JDBCContex setting error: sqlType must be 'SQL_UPDATE'!"))
  7. }
  8. else {
  9. if (ctx.batch) {
  10. if (noReturnKey(ctx)) {
  11. val usql = SQL(ctx.statements.head)
  12. .tags(ctx.queryTags: _*)
  13. .batch(ctx.parameters: _*)
  14. Try {
  15. NamedDB(ctx.dbName) localTx { implicit session =>
  16. ctx.queryTimeout.foreach(session.queryTimeout(_))
  17. usql.apply[Seq]()
  18. Seq.empty[Long].to[C]
  19. }
  20. }
  21. } else {
  22. val usql = new SQLBatchWithGeneratedKey(ctx.statements.head, ctx.parameters, ctx.queryTags)(None)
  23. Try {
  24. NamedDB(ctx.dbName) localTx { implicit session =>
  25. ctx.queryTimeout.foreach(session.queryTimeout(_))
  26. usql.apply[C]()
  27. }
  28. }
  29. }
  30. } else {
  31. Failure(new IllegalStateException("JDBCContex setting error: must set batch = true !"))
  32. }
  33. }
  34. }

如果batch-update是某种Insert操作的话我们可以通过cox.batch注明返回由JDBC系统自动产生的唯一键。这些主键一般在构建表时注明,包括:serial, auto_increment等。如果不返回主键则返回update语句的更新状态如更新数据条数等。在上面这个函数里SQLBatchWithGeneratedKey.apply()返回insert数据主键,所以statement必须是INSERT语句。SQLBatch.apply()则用来运算update语句并返回更新数据的条数。下面是jdbcBatchUpdate函数的使用示范:

  1. val insertSQL = "insert into members(name,birthday,description,created_at,picture) values (?, ?, ?, ?, ?)"
  2. val dateCreated = DateTime.now
  3. import java.io.FileInputStream
  4. val picfile = new File("/users/tiger/Nobody.png")
  5. val fis = new FileInputStream(picfile)
  6. ctx = JDBCContext('h2)
  7. try {
  8. ctx = ctx.setBatchCommand(insertSQL).appendBatchParameters(
  9. "John",new LocalDate("2008-03-01"),"youngest user",dateCreated,None).appendBatchParameters(
  10. "peter", None, "no birth date", dateCreated, fis)
  11. .appendBatchParameters(
  12. "susan", None, "no birth date", dateCreated, None)
  13. .setBatchReturnGeneratedKeyOption(JDBCContext.RETURN_GENERATED_KEYVALUE)
  14. }
  15. catch {
  16. case e: Exception => println(e.getMessage)
  17. }
  18. var resultInserts = jdbcBatchUpdate(ctx)
  19. resultInserts match {
  20. case Success(msg) => println(msg)
  21. case Failure(err) => println(s"${err.getMessage}")
  22. }

上面这个例子里一个transaction批次包含了三条Insert语句,其中一条涉及存入picture字段:我们只需要把图像文件InputStream作为普通参数传人即可。我们也可以把任何类型的非batch-update语句捆绑在统一的transaction里运算,而且可以指定每条update返回类型:自动产生的主键或者更新数据条数:

  1. def jdbcTxUpdates[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  2. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  3. if (ctx.statements == Nil)
  4. throw new IllegalStateException("JDBCContex setting error: statements empty!")
  5. if (ctx.sqlType != SQL_UPDATE) {
  6. Failure(new IllegalStateException("JDBCContex setting error: sqlType must be 'SQL_UPDATE'!"))
  7. }
  8. else {
  9. if (!ctx.batch) {
  10. if (ctx.statements.size == 1)
  11. singleTxUpdate(ctx)
  12. else
  13. multiTxUpdates(ctx)
  14. } else
  15. Failure(new IllegalStateException("JDBCContex setting error: must set batch = false !"))
  16. }
  17. }

这个update函数又被细分为单条语句singleTxUpdate和多条语句multiTxUpdates。无论单条或多条update函数又被分为返回主键或更新状态类型的函数:

 

  1. private def singleTxUpdateWithReturnKey[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  2. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  3. val Some(key) :: xs = ctx.returnGeneratedKey
  4. val params: Seq[Any] = ctx.parameters match {
  5. case Nil => Nil
  6. case p@_ => p.head
  7. }
  8. val usql = new SQLUpdateWithGeneratedKey(ctx.statements.head, params, ctx.queryTags)(key)
  9. Try {
  10. NamedDB(ctx.dbName) localTx { implicit session =>
  11. session.fetchSize(ctx.fetchSize)
  12. ctx.queryTimeout.foreach(session.queryTimeout(_))
  13. val result = usql.apply()
  14. Seq(result).to[C]
  15. }
  16. }
  17. }
  18. private def singleTxUpdateNoReturnKey[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  19. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  20. val params: Seq[Any] = ctx.parameters match {
  21. case Nil => Nil
  22. case p@_ => p.head
  23. }
  24. val before = ctx.preAction match {
  25. case None => pstm: PreparedStatement => {}
  26. case Some(f) => f
  27. }
  28. val after = ctx.postAction match {
  29. case None => pstm: PreparedStatement => {}
  30. case Some(f) => f
  31. }
  32. val usql = new SQLUpdate(ctx.statements.head,params,ctx.queryTags)(before)(after)
  33. Try {
  34. NamedDB(ctx.dbName) localTx {implicit session =>
  35. session.fetchSize(ctx.fetchSize)
  36. ctx.queryTimeout.foreach(session.queryTimeout(_))
  37. val result = usql.apply()
  38. Seq(result.toLong).to[C]
  39. }
  40. }
  41. }
  42. private def singleTxUpdate[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  43. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  44. if (noReturnKey(ctx))
  45. singleTxUpdateNoReturnKey(ctx)
  46. else
  47. singleTxUpdateWithReturnKey(ctx)
  48. }
  49. private def noReturnKey(ctx: JDBCContext): Boolean = {
  50. if (ctx.returnGeneratedKey != Nil) {
  51. val k :: xs = ctx.returnGeneratedKey
  52. k match {
  53. case None => true
  54. case Some(k) => false
  55. }
  56. } else true
  57. }
  58. def noActon: PreparedStatement=>Unit = pstm => {}
  59. def multiTxUpdates[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  60. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  61. Try {
  62. NamedDB(ctx.dbName) localTx { implicit session =>
  63. session.fetchSize(ctx.fetchSize)
  64. ctx.queryTimeout.foreach(session.queryTimeout(_))
  65. val keys: Seq[Option[Any]] = ctx.returnGeneratedKey match {
  66. case Nil => Seq.fill(ctx.statements.size)(None)
  67. case k@_ => k
  68. }
  69. val sqlcmd = ctx.statements zip ctx.parameters zip keys
  70. val results = sqlcmd.map { case ((stm, param), key) =>
  71. key match {
  72. case None =>
  73. new SQLUpdate(stm, param, Nil)(noActon)(noActon).apply().toLong
  74. case Some(k) =>
  75. new SQLUpdateWithGeneratedKey(stm, param, Nil)(k).apply().toLong
  76. }
  77. }
  78. results.to[C]
  79. }
  80. }
  81. }

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

  1. val updateSQL = "update members set description = ? where id < ?"
  2. ctx = JDBCContext('h2)
  3. try {
  4. ctx = ctx.setUpdateCommand(JDBCContext.RETURN_GENERATED_KEYVALUE,insertSQL,
  5. "max", None, "no birth date", dateCreated, None)
  6. .appendUpdateCommand(JDBCContext.RETURN_UPDATED_COUNT, updateSQL, "id++", 10)
  7. .appendUpdateCommand(JDBCContext.RETURN_UPDATED_COUNT,"delete members where id = 1")
  8. }
  9. catch {
  10. case e: Exception => println(e.getMessage)
  11. }
  12. var resultUpdates = jdbcTxUpdates[Vector](ctx)
  13. resultUpdates match {
  14. case Success(msg) => println(msg)
  15. case Failure(err) => println(s"${err.getMessage}")
  16. }

在这个例子里我们把insert,update和delete混在了一个transaction里。最后,我们再把试验数据,包括blob字段读出来:

  1. //data model
  2. case class Member(
  3. id: Long,
  4. name: String,
  5. description: Option[String] = None,
  6. birthday: Option[LocalDate] = None,
  7. createdAt: DateTime,
  8. picture: InputStream)
  9. //data row converter
  10. val toMember = (rs: WrappedResultSet) => Member(
  11. id = rs.long("id"),
  12. name = rs.string("name"),
  13. description = rs.stringOpt("description"),
  14. birthday = rs.jodaLocalDateOpt("birthday"),
  15. createdAt = rs.jodaDateTime("created_at"),
  16. picture = rs.binaryStream("picture")
  17. )
  18. ctx = JDBCContext('h2)
  19. ctx = ctx.setQueryCommand("select * from members").setQueryTimeout(Some(1000))
  20. val vecMember: Vector[Member] = jdbcQueryResult[Vector,Member](ctx,toMember)
  21. val buffer = new Array[Byte](1024)
  22. vecMember.foreach {row =>
  23. println(s"id: ${row.id} name: ${row.name}")
  24. println(s"name: ${row.name}")
  25. if (row.picture == null)
  26. println("picture empty")
  27. else {
  28. val fname = s"/users/tiger/pic${row.id}.png"
  29. val file = new File(fname)
  30. val output = new FileOutputStream(file)
  31. println(s"saving picture to $fname")
  32. row.picture.available()
  33. while (row.picture.read(buffer) > 0) {
  34. output.write(buffer)
  35. }
  36. output.close()
  37. }
  38. }

下面是本次讨论的示范源代码:

build.sbt

  1. name := "learn-scalikeJDBC"
  2. version := "0.1"
  3. scalaVersion := "2.12.4"
  4. // Scala 2.10, 2.11, 2.12
  5. libraryDependencies ++= Seq(
  6. "org.scalikejdbc" %% "scalikejdbc" % "3.1.0",
  7. "org.scalikejdbc" %% "scalikejdbc-test" % "3.1.0" % "test",
  8. "org.scalikejdbc" %% "scalikejdbc-config" % "3.1.0",
  9. "com.h2database" % "h2" % "1.4.196",
  10. "mysql" % "mysql-connector-java" % "6.0.6",
  11. "org.postgresql" % "postgresql" % "42.2.0",
  12. "commons-dbcp" % "commons-dbcp" % "1.4",
  13. "org.apache.tomcat" % "tomcat-jdbc" % "9.0.2",
  14. "com.zaxxer" % "HikariCP" % "2.7.4",
  15. "com.jolbox" % "bonecp" % "0.8.0.RELEASE",
  16. "com.typesafe.slick" %% "slick" % "3.2.1",
  17. "ch.qos.logback" % "logback-classic" % "1.2.3"
  18. )

resources/application.conf

 

  1. # JDBC settings
  2. test {
  3. db {
  4. h2 {
  5. driver = "org.h2.Driver"
  6. url = "jdbc:h2:tcp://localhost/~/slickdemo"
  7. user = ""
  8. password = ""
  9. poolInitialSize = 5
  10. poolMaxSize = 7
  11. poolConnectionTimeoutMillis = 1000
  12. poolValidationQuery = "select 1 as one"
  13. poolFactoryName = "commons-dbcp2"
  14. }
  15. }
  16. db.mysql.driver = "com.mysql.cj.jdbc.Driver"
  17. db.mysql.url = "jdbc:mysql://localhost:3306/testdb"
  18. db.mysql.user = "root"
  19. db.mysql.password = "123"
  20. db.mysql.poolInitialSize = 5
  21. db.mysql.poolMaxSize = 7
  22. db.mysql.poolConnectionTimeoutMillis = 1000
  23. db.mysql.poolValidationQuery = "select 1 as one"
  24. db.mysql.poolFactoryName = "bonecp"
  25. # scallikejdbc Global settings
  26. scalikejdbc.global.loggingSQLAndTime.enabled = true
  27. scalikejdbc.global.loggingSQLAndTime.logLevel = info
  28. scalikejdbc.global.loggingSQLAndTime.warningEnabled = true
  29. scalikejdbc.global.loggingSQLAndTime.warningThresholdMillis = 1000
  30. scalikejdbc.global.loggingSQLAndTime.warningLogLevel = warn
  31. scalikejdbc.global.loggingSQLAndTime.singleLineMode = false
  32. scalikejdbc.global.loggingSQLAndTime.printUnprocessedStackTrace = false
  33. scalikejdbc.global.loggingSQLAndTime.stackTraceDepth = 10
  34. }
  35. dev {
  36. db {
  37. h2 {
  38. driver = "org.h2.Driver"
  39. url = "jdbc:h2:tcp://localhost/~/slickdemo"
  40. user = ""
  41. password = ""
  42. poolFactoryName = "hikaricp"
  43. numThreads = 10
  44. maxConnections = 12
  45. minConnections = 4
  46. keepAliveConnection = true
  47. }
  48. mysql {
  49. driver = "com.mysql.cj.jdbc.Driver"
  50. url = "jdbc:mysql://localhost:3306/testdb"
  51. user = "root"
  52. password = "123"
  53. poolInitialSize = 5
  54. poolMaxSize = 7
  55. poolConnectionTimeoutMillis = 1000
  56. poolValidationQuery = "select 1 as one"
  57. poolFactoryName = "bonecp"
  58. }
  59. postgres {
  60. driver = "org.postgresql.Driver"
  61. url = "jdbc:postgresql://localhost:5432/testdb"
  62. user = "root"
  63. password = "123"
  64. poolFactoryName = "hikaricp"
  65. numThreads = 10
  66. maxConnections = 12
  67. minConnections = 4
  68. keepAliveConnection = true
  69. }
  70. }
  71. # scallikejdbc Global settings
  72. scalikejdbc.global.loggingSQLAndTime.enabled = true
  73. scalikejdbc.global.loggingSQLAndTime.logLevel = info
  74. scalikejdbc.global.loggingSQLAndTime.warningEnabled = true
  75. scalikejdbc.global.loggingSQLAndTime.warningThresholdMillis = 1000
  76. scalikejdbc.global.loggingSQLAndTime.warningLogLevel = warn
  77. scalikejdbc.global.loggingSQLAndTime.singleLineMode = false
  78. scalikejdbc.global.loggingSQLAndTime.printUnprocessedStackTrace = false
  79. scalikejdbc.global.loggingSQLAndTime.stackTraceDepth = 10
  80. }

JDBCEngine.scala

  1. package jdbccontext
  2. import java.sql.PreparedStatement
  3. import scala.collection.generic.CanBuildFrom
  4. import scalikejdbc._
  5. import scala.util._
  6. import scalikejdbc.TxBoundary.Try._
  7. object JDBCContext {
  8. type SQLTYPE = Int
  9. val SQL_SELECT: Int = 0
  10. val SQL_EXEDDL= 1
  11. val SQL_UPDATE = 2
  12. val RETURN_GENERATED_KEYVALUE = true
  13. val RETURN_UPDATED_COUNT = false
  14. }
  15. case class JDBCContext(
  16. dbName: Symbol,
  17. statements: Seq[String] = Nil,
  18. parameters: Seq[Seq[Any]] = Nil,
  19. fetchSize: Int = 100,
  20. queryTimeout: Option[Int] = None,
  21. queryTags: Seq[String] = Nil,
  22. sqlType: JDBCContext.SQLTYPE = JDBCContext.SQL_SELECT,
  23. batch: Boolean = false,
  24. returnGeneratedKey: Seq[Option[Any]] = Nil,
  25. // no return: None, return by index: Some(1), by name: Some("id")
  26. preAction: Option[PreparedStatement => Unit] = None,
  27. postAction: Option[PreparedStatement => Unit] = None) {
  28. ctx =>
  29. //helper functions
  30. def appendTag(tag: String): JDBCContext = ctx.copy(queryTags = ctx.queryTags :+ tag)
  31. def appendTags(tags: Seq[String]): JDBCContext = ctx.copy(queryTags = ctx.queryTags ++ tags)
  32. def setFetchSize(size: Int): JDBCContext = ctx.copy(fetchSize = size)
  33. def setQueryTimeout(time: Option[Int]): JDBCContext = ctx.copy(queryTimeout = time)
  34. def setPreAction(action: Option[PreparedStatement => Unit]): JDBCContext = {
  35. if (ctx.sqlType == JDBCContext.SQL_UPDATE &&
  36. !ctx.batch && ctx.statements.size == 1)
  37. ctx.copy(preAction = action)
  38. else
  39. throw new IllegalStateException("JDBCContex setting error: preAction not supported!")
  40. }
  41. def setPostAction(action: Option[PreparedStatement => Unit]): JDBCContext = {
  42. if (ctx.sqlType == JDBCContext.SQL_UPDATE &&
  43. !ctx.batch && ctx.statements.size == 1)
  44. ctx.copy(postAction = action)
  45. else
  46. throw new IllegalStateException("JDBCContex setting error: preAction not supported!")
  47. }
  48. def appendDDLCommand(_statement: String, _parameters: Any*): JDBCContext = {
  49. if (ctx.sqlType == JDBCContext.SQL_EXEDDL) {
  50. ctx.copy(
  51. statements = ctx.statements ++ Seq(_statement),
  52. parameters = ctx.parameters ++ Seq(Seq(_parameters))
  53. )
  54. } else
  55. throw new IllegalStateException("JDBCContex setting error: option not supported!")
  56. }
  57. def appendUpdateCommand(_returnGeneratedKey: Boolean, _statement: String, _parameters: Any*): JDBCContext = {
  58. if (ctx.sqlType == JDBCContext.SQL_UPDATE && !ctx.batch) {
  59. ctx.copy(
  60. statements = ctx.statements ++ Seq(_statement),
  61. parameters = ctx.parameters ++ Seq(_parameters),
  62. returnGeneratedKey = ctx.returnGeneratedKey ++ (if (_returnGeneratedKey) Seq(Some(1)) else Seq(None))
  63. )
  64. } else
  65. throw new IllegalStateException("JDBCContex setting error: option not supported!")
  66. }
  67. def appendBatchParameters(_parameters: Any*): JDBCContext = {
  68. if (ctx.sqlType != JDBCContext.SQL_UPDATE || !ctx.batch)
  69. throw new IllegalStateException("JDBCContex setting error: batch parameters only supported for SQL_UPDATE and batch = true!")
  70. var matchParams = true
  71. if (ctx.parameters != Nil)
  72. if (ctx.parameters.head.size != _parameters.size)
  73. matchParams = false
  74. if (matchParams) {
  75. ctx.copy(
  76. parameters = ctx.parameters ++ Seq(_parameters)
  77. )
  78. } else
  79. throw new IllegalStateException("JDBCContex setting error: batch command parameters not match!")
  80. }
  81. def setBatchReturnGeneratedKeyOption(returnKey: Boolean): JDBCContext = {
  82. if (ctx.sqlType != JDBCContext.SQL_UPDATE || !ctx.batch)
  83. throw new IllegalStateException("JDBCContex setting error: only supported in batch update commands!")
  84. ctx.copy(
  85. returnGeneratedKey = if (returnKey) Seq(Some(1)) else Nil
  86. )
  87. }
  88. def setQueryCommand(_statement: String, _parameters: Any*): JDBCContext = {
  89. ctx.copy(
  90. statements = Seq(_statement),
  91. parameters = Seq(_parameters),
  92. sqlType = JDBCContext.SQL_SELECT,
  93. batch = false
  94. )
  95. }
  96. def setDDLCommand(_statement: String, _parameters: Any*): JDBCContext = {
  97. ctx.copy(
  98. statements = Seq(_statement),
  99. parameters = Seq(_parameters),
  100. sqlType = JDBCContext.SQL_EXEDDL,
  101. batch = false
  102. )
  103. }
  104. def setUpdateCommand(_returnGeneratedKey: Boolean, _statement: String, _parameters: Any*): JDBCContext = {
  105. ctx.copy(
  106. statements = Seq(_statement),
  107. parameters = Seq(_parameters),
  108. returnGeneratedKey = if (_returnGeneratedKey) Seq(Some(1)) else Seq(None),
  109. sqlType = JDBCContext.SQL_UPDATE,
  110. batch = false
  111. )
  112. }
  113. def setBatchCommand(_statement: String): JDBCContext = {
  114. ctx.copy (
  115. statements = Seq(_statement),
  116. sqlType = JDBCContext.SQL_UPDATE,
  117. batch = true
  118. )
  119. }
  120. }
  121. object JDBCEngine {
  122. import JDBCContext._
  123. private def noExtractor(message: String): WrappedResultSet => Nothing = { (rs: WrappedResultSet) =>
  124. throw new IllegalStateException(message)
  125. }
  126. def jdbcQueryResult[C[_] <: TraversableOnce[_], A](
  127. ctx: JDBCContext, rowConverter: WrappedResultSet => A)(
  128. implicit cbf: CanBuildFrom[Nothing, A, C[A]]): C[A] = {
  129. ctx.sqlType match {
  130. case SQL_SELECT => {
  131. val params: Seq[Any] = ctx.parameters match {
  132. case Nil => Nil
  133. case p@_ => p.head
  134. }
  135. val rawSql = new SQLToCollectionImpl[A, NoExtractor](ctx.statements.head, params)(noExtractor(""))
  136. ctx.queryTimeout.foreach(rawSql.queryTimeout(_))
  137. ctx.queryTags.foreach(rawSql.tags(_))
  138. rawSql.fetchSize(ctx.fetchSize)
  139. implicit val session = NamedAutoSession(ctx.dbName)
  140. val sql: SQL[A, HasExtractor] = rawSql.map(rowConverter)
  141. sql.collection.apply[C]()
  142. }
  143. case _ => throw new IllegalStateException("JDBCContex setting error: sqlType must be 'SQL_SELECT'!")
  144. }
  145. }
  146. def jdbcExcuteDDL(ctx: JDBCContext): Try[String] = {
  147. if (ctx.sqlType != SQL_EXEDDL) {
  148. Failure(new IllegalStateException("JDBCContex setting error: sqlType must be 'SQL_EXEDDL'!"))
  149. }
  150. else {
  151. NamedDB(ctx.dbName) localTx { implicit session =>
  152. Try {
  153. ctx.statements.foreach { stm =>
  154. val ddl = new SQLExecution(statement = stm, parameters = Nil)(
  155. before = WrappedResultSet => {})(
  156. after = WrappedResultSet => {})
  157. ddl.apply()
  158. }
  159. "SQL_EXEDDL executed succesfully."
  160. }
  161. }
  162. }
  163. }
  164. def jdbcBatchUpdate[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  165. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  166. if (ctx.statements == Nil)
  167. throw new IllegalStateException("JDBCContex setting error: statements empty!")
  168. if (ctx.sqlType != SQL_UPDATE) {
  169. Failure(new IllegalStateException("JDBCContex setting error: sqlType must be 'SQL_UPDATE'!"))
  170. }
  171. else {
  172. if (ctx.batch) {
  173. if (noReturnKey(ctx)) {
  174. val usql = SQL(ctx.statements.head)
  175. .tags(ctx.queryTags: _*)
  176. .batch(ctx.parameters: _*)
  177. Try {
  178. NamedDB(ctx.dbName) localTx { implicit session =>
  179. ctx.queryTimeout.foreach(session.queryTimeout(_))
  180. usql.apply[Seq]()
  181. Seq.empty[Long].to[C]
  182. }
  183. }
  184. } else {
  185. val usql = new SQLBatchWithGeneratedKey(ctx.statements.head, ctx.parameters, ctx.queryTags)(None)
  186. Try {
  187. NamedDB(ctx.dbName) localTx { implicit session =>
  188. ctx.queryTimeout.foreach(session.queryTimeout(_))
  189. usql.apply[C]()
  190. }
  191. }
  192. }
  193. } else {
  194. Failure(new IllegalStateException("JDBCContex setting error: must set batch = true !"))
  195. }
  196. }
  197. }
  198. private def singleTxUpdateWithReturnKey[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  199. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  200. val Some(key) :: xs = ctx.returnGeneratedKey
  201. val params: Seq[Any] = ctx.parameters match {
  202. case Nil => Nil
  203. case p@_ => p.head
  204. }
  205. val usql = new SQLUpdateWithGeneratedKey(ctx.statements.head, params, ctx.queryTags)(key)
  206. Try {
  207. NamedDB(ctx.dbName) localTx { implicit session =>
  208. session.fetchSize(ctx.fetchSize)
  209. ctx.queryTimeout.foreach(session.queryTimeout(_))
  210. val result = usql.apply()
  211. Seq(result).to[C]
  212. }
  213. }
  214. }
  215. private def singleTxUpdateNoReturnKey[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  216. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  217. val params: Seq[Any] = ctx.parameters match {
  218. case Nil => Nil
  219. case p@_ => p.head
  220. }
  221. val before = ctx.preAction match {
  222. case None => pstm: PreparedStatement => {}
  223. case Some(f) => f
  224. }
  225. val after = ctx.postAction match {
  226. case None => pstm: PreparedStatement => {}
  227. case Some(f) => f
  228. }
  229. val usql = new SQLUpdate(ctx.statements.head,params,ctx.queryTags)(before)(after)
  230. Try {
  231. NamedDB(ctx.dbName) localTx {implicit session =>
  232. session.fetchSize(ctx.fetchSize)
  233. ctx.queryTimeout.foreach(session.queryTimeout(_))
  234. val result = usql.apply()
  235. Seq(result.toLong).to[C]
  236. }
  237. }
  238. }
  239. private def singleTxUpdate[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  240. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  241. if (noReturnKey(ctx))
  242. singleTxUpdateNoReturnKey(ctx)
  243. else
  244. singleTxUpdateWithReturnKey(ctx)
  245. }
  246. private def noReturnKey(ctx: JDBCContext): Boolean = {
  247. if (ctx.returnGeneratedKey != Nil) {
  248. val k :: xs = ctx.returnGeneratedKey
  249. k match {
  250. case None => true
  251. case Some(k) => false
  252. }
  253. } else true
  254. }
  255. def noActon: PreparedStatement=>Unit = pstm => {}
  256. def multiTxUpdates[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  257. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  258. Try {
  259. NamedDB(ctx.dbName) localTx { implicit session =>
  260. session.fetchSize(ctx.fetchSize)
  261. ctx.queryTimeout.foreach(session.queryTimeout(_))
  262. val keys: Seq[Option[Any]] = ctx.returnGeneratedKey match {
  263. case Nil => Seq.fill(ctx.statements.size)(None)
  264. case k@_ => k
  265. }
  266. val sqlcmd = ctx.statements zip ctx.parameters zip keys
  267. val results = sqlcmd.map { case ((stm, param), key) =>
  268. key match {
  269. case None =>
  270. new SQLUpdate(stm, param, Nil)(noActon)(noActon).apply().toLong
  271. case Some(k) =>
  272. new SQLUpdateWithGeneratedKey(stm, param, Nil)(k).apply().toLong
  273. }
  274. }
  275. results.to[C]
  276. }
  277. }
  278. }
  279. def jdbcTxUpdates[C[_] <: TraversableOnce[_]](ctx: JDBCContext)(
  280. implicit cbf: CanBuildFrom[Nothing, Long, C[Long]]): Try[C[Long]] = {
  281. if (ctx.statements == Nil)
  282. throw new IllegalStateException("JDBCContex setting error: statements empty!")
  283. if (ctx.sqlType != SQL_UPDATE) {
  284. Failure(new IllegalStateException("JDBCContex setting error: sqlType must be 'SQL_UPDATE'!"))
  285. }
  286. else {
  287. if (!ctx.batch) {
  288. if (ctx.statements.size == 1)
  289. singleTxUpdate(ctx)
  290. else
  291. multiTxUpdates(ctx)
  292. } else
  293. Failure(new IllegalStateException("JDBCContex setting error: must set batch = false !"))
  294. }
  295. }
  296. }

JDBCEngineDemo.scala

  1. import java.io.File
  2. import java.io.FileOutputStream
  3. import java.io.InputStream
  4. import jdbccontext._
  5. import configdbs._
  6. import org.joda.time._
  7. import scala.util._
  8. import JDBCEngine._
  9. import scalikejdbc._
  10. object CrudDemo extends App {
  11. ConfigDBsWithEnv("dev").setup('h2)
  12. ConfigDBsWithEnv("dev").loadGlobalSettings()
  13. val dropSQL: String ="""
  14. drop table members
  15. """
  16. val createSQL: String ="""
  17. create table members (
  18. id serial not null primary key,
  19. name varchar(30) not null,
  20. description varchar(1000),
  21. birthday date,
  22. created_at timestamp not null,
  23. picture blob
  24. )"""
  25. var ctx = JDBCContext('h2)
  26. try {
  27. ctx = ctx.setDDLCommand(dropSQL)
  28. .appendDDLCommand(createSQL)
  29. }
  30. catch {
  31. case e: Exception => println(e.getMessage)
  32. }
  33. val resultCreateTable = jdbcExcuteDDL(ctx)
  34. resultCreateTable match {
  35. case Success(msg) => println(msg)
  36. case Failure(err) => println(s"${err.getMessage}")
  37. }
  38. val insertSQL = "insert into members(name,birthday,description,created_at,picture) values (?, ?, ?, ?, ?)"
  39. val dateCreated = DateTime.now
  40. import java.io.FileInputStream
  41. val picfile = new File("/users/tiger/Nobody.png")
  42. val fis = new FileInputStream(picfile)
  43. ctx = JDBCContext('h2)
  44. try {
  45. ctx = ctx.setBatchCommand(insertSQL).appendBatchParameters(
  46. "John",new LocalDate("2008-03-01"),"youngest user",dateCreated,None).appendBatchParameters(
  47. "peter", None, "no birth date", dateCreated, fis)
  48. .appendBatchParameters(
  49. "susan", None, "no birth date", dateCreated, None)
  50. .setBatchReturnGeneratedKeyOption(JDBCContext.RETURN_GENERATED_KEYVALUE)
  51. }
  52. catch {
  53. case e: Exception => println(e.getMessage)
  54. }
  55. var resultInserts = jdbcBatchUpdate(ctx)
  56. resultInserts match {
  57. case Success(msg) => println(msg)
  58. case Failure(err) => println(s"${err.getMessage}")
  59. }
  60. val updateSQL = "update members set description = ? where id < ?"
  61. ctx = JDBCContext('h2)
  62. try {
  63. ctx = ctx.setUpdateCommand(JDBCContext.RETURN_GENERATED_KEYVALUE,insertSQL,
  64. "max", None, "no birth date", dateCreated, None)
  65. .appendUpdateCommand(JDBCContext.RETURN_UPDATED_COUNT, updateSQL, "id++", 10)
  66. .appendUpdateCommand(JDBCContext.RETURN_UPDATED_COUNT,"delete members where id = 1")
  67. }
  68. catch {
  69. case e: Exception => println(e.getMessage)
  70. }
  71. var resultUpdates = jdbcTxUpdates[Vector](ctx)
  72. resultUpdates match {
  73. case Success(msg) => println(msg)
  74. case Failure(err) => println(s"${err.getMessage}")
  75. }
  76. //data model
  77. case class Member(
  78. id: Long,
  79. name: String,
  80. description: Option[String] = None,
  81. birthday: Option[LocalDate] = None,
  82. createdAt: DateTime,
  83. picture: InputStream)
  84. //data row converter
  85. val toMember = (rs: WrappedResultSet) => Member(
  86. id = rs.long("id"),
  87. name = rs.string("name"),
  88. description = rs.stringOpt("description"),
  89. birthday = rs.jodaLocalDateOpt("birthday"),
  90. createdAt = rs.jodaDateTime("created_at"),
  91. picture = rs.binaryStream("picture")
  92. )
  93. ctx = JDBCContext('h2)
  94. ctx = ctx.setQueryCommand("select * from members").setQueryTimeout(Some(1000))
  95. val vecMember: Vector[Member] = jdbcQueryResult[Vector,Member](ctx,toMember)
  96. val buffer = new Array[Byte](1024)
  97. vecMember.foreach {row =>
  98. println(s"id: ${row.id} name: ${row.name}")
  99. println(s"name: ${row.name}")
  100. if (row.picture == null)
  101. println("picture empty")
  102. else {
  103. val fname = s"/users/tiger/pic${row.id}.png"
  104. val file = new File(fname)
  105. val output = new FileOutputStream(file)
  106. println(s"saving picture to $fname")
  107. row.picture.available()
  108. while (row.picture.read(buffer) > 0) {
  109. output.write(buffer)
  110. }
  111. output.close()
  112. }
  113. }
  114. }

 

 

 

 

 

 

 

 

 

 

 

版权声明:本文为tiger-xc原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/tiger-xc/p/8431245.html