Slick 3.0.0 - 仅更新包含非空值的行

2023-12-02

有一个包含列的表

class Data(tag: Tag) extends Table[DataRow](tag, "data") {
  def id = column[Int]("id", O.PrimaryKey)
  def name = column[String]("name")
  def state = column[State]("state")
  def price = column[Int]("price")

  def * = (id.?, name, state, price) <> ((DataRow.apply _).tupled, DataRow.unapply)
}

我想编写一个函数来选择一行,并更新提供的值不为空的列。

def update(id: Int, name: Option[String], state: Option[State], price: Option[Int])

eg.

update(1, None, None, Some(5))将仅更新数据行 1 的价格,保持名称和状态不变

update(1, Some("foo"), None, Some(6))将更新名称和价格,但保持其状态不变。

我想可以使用一些智能映射,但我很难表达它,不确定它如何根据输入(是否定义了它们的值)吐出不同长度的元组,因为它们或多或少“不相关” ” 类。

def update(id: Int, name: Option[String], state: Option[State], price: Option[Int]) = {
  table.fiter(_.id == id). ???? .update(name, state, price)
}

我通过以下方式解决了它。

下面的实现仅当它是 Product 对象时才有效。

执行除 Option 类型为 None 和对象类型为 null 之外的更新语句。

package slick.extensions

import slick.ast._
import slick.dbio.{ Effect, NoStream }
import slick.driver.JdbcDriver
import slick.jdbc._
import slick.lifted._
import slick.relational.{ CompiledMapping, ProductResultConverter, ResultConverter, TypeMappingResultConverter }
import slick.util.{ ProductWrapper, SQLBuilder }

import scala.language.{ existentials, higherKinds, implicitConversions }

trait PatchActionExtensionMethodsSupport { driver: JdbcDriver =>

  trait PatchActionImplicits {
    implicit def queryPatchActionExtensionMethods[U <: Product, C[_]](
        q: Query[_, U, C]
    ): PatchActionExtensionMethodsImpl[U] =
      createPatchActionExtensionMethods(updateCompiler.run(q.toNode).tree, ())
  }

  ///////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////// Patch Actions
  ///////////////////////////////////////////////////////////////////////////////////////////////

  type PatchActionExtensionMethods[T <: Product] = PatchActionExtensionMethodsImpl[T]

  def createPatchActionExtensionMethods[T <: Product](tree: Node, param: Any): PatchActionExtensionMethods[T] =
    new PatchActionExtensionMethodsImpl[T](tree, param)

  class PatchActionExtensionMethodsImpl[T <: Product](tree: Node, param: Any) {
    protected[this] val ResultSetMapping(_, CompiledStatement(_, sres: SQLBuilder.Result, _),
      CompiledMapping(_converter, _)) = tree
    protected[this] val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, Product]]
    protected[this] val TypeMappingResultConverter(childConverter, toBase, toMapped) = converter
    protected[this] val ProductResultConverter(elementConverters @ _ *) =
      childConverter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, Product]]
    private[this] val updateQuerySplitRegExp = """(.*)(?<=set )((?:(?= where)|.)+)(.*)?""".r
    private[this] val updateQuerySetterRegExp = """[^\s]+\s*=\s*\?""".r

    /** An Action that updates the data selected by this query. */
    def patch(value: T): DriverAction[Int, NoStream, Effect.Write] = {
      val (seq, converters) = value.productIterator.zipWithIndex.toIndexedSeq
        .zip(elementConverters)
        .filter {
          case ((Some(_), _), _) => true
          case ((None, _), _) => false
          case ((null, _), _) => false
          case ((_, _), _) => true
        }
        .unzip

      val (products, indexes) = seq.unzip

      val newConverters = converters.zipWithIndex
        .map(c => (c._1, c._2 + 1))
        .map {
          case (c: BaseResultConverter[_], idx) => new BaseResultConverter(c.ti, c.name, idx)
          case (c: OptionResultConverter[_], idx) => new OptionResultConverter(c.ti, idx)
          case (c: DefaultingResultConverter[_], idx) => new DefaultingResultConverter(c.ti, c.default, idx)
          case (c: IsDefinedResultConverter[_], idx) => new IsDefinedResultConverter(c.ti, idx)
        }

      val productResultConverter =
        ProductResultConverter(newConverters: _*).asInstanceOf[ResultConverter[JdbcResultConverterDomain, Any]]
      val newConverter = TypeMappingResultConverter(productResultConverter, (p: Product) => p, (a: Any) => toMapped(a))

      val newValue: Product = new ProductWrapper(products)
      val newSql = sres.sql match {
        case updateQuerySplitRegExp(prefix, setter, suffix) =>
          val buffer = StringBuilder.newBuilder
          buffer.append(prefix)
          buffer.append(
            updateQuerySetterRegExp
              .findAllIn(setter)
              .zipWithIndex
              .filter(s => indexes.contains(s._2))
              .map(_._1)
              .mkString(", ")
          )
          buffer.append(suffix)
          buffer.toString()
      }

      new SimpleJdbcDriverAction[Int]("patch", Vector(newSql)) {
        def run(ctx: Backend#Context, sql: Vector[String]): Int =
          ctx.session.withPreparedStatement(sql.head) { st =>
            st.clearParameters
            newConverter.set(newValue, st)
            sres.setter(st, newConverter.width + 1, param)
            st.executeUpdate
          }
      }
    }
  }
}

Example

// Model
case class User(
  id: Option[Int] = None,
  name: Option[String] = None,
  username: Option[String] = None,
  password: Option[String] = None
)

// Table
class Users(tag: Tag) extends Table[User](tag, "users") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def username = column[String]("username")
  def password = column[String]("password")
  override def * = (id.?, name.?, username.?, password.?) <>(User.tupled, User.unapply)
}

// TableQuery
object Users extends TableQuery(new Users(_))

// CustomDriver 
trait CustomDriver extends PostgresDriver with PatchActionExtensionMethodsSupport {
  override val api: API = new API {}
  trait API extends super.API  with PatchActionImplicits
}

// Insert
Users += User(Some(1), Some("Test"), Some("test"), Some("1234"))

// User patch
Users.filter(_.id === 1).patch(User(name = Some("Change Name"), username = Some("")))

https://gist.github.com/bad79s/1edf9ea83ba08c46add03815059acfca

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Slick 3.0.0 - 仅更新包含非空值的行 的相关文章

  • Scala REPL / SBT Console 是否有配置文件?

    我一直在尝试找到某种点文件来放入 Scala REPL 设置和自定义函数 我特别有兴趣传递它的标志 例如 Dscala color 启用语法突出显示 以及覆盖设置 如结果字符串截断 scala gt power scala gt vals
  • 如何初始化子类型中特征的值?

    如果我写 trait T val t 3 val u 1 t Nil class U extends T override val t 2 new U u 它表明了这一点 List 1 0 我应该如何更改上面的代码以使其显示以下内容 Lis
  • Scala 中的行聚合

    我正在寻找一种方法在 Scala 的数据框中获取一个新列来计算min max中的值col1 col2 col10对于每一行 我知道我可以使用 UDF 来做到这一点 但也许有一种更简单的方法 Thanks Porting 这个Python答案
  • 错误:无法在 scala 中找到或加载主类

    安装 eclipse scala 插件和 eclipse maven scala 插件后 我是 scala 新手 所以我尝试确保在测试 scala hello world 项目后环境正常工作 它按预期工作 但我在尝试执行我从公司存储库中签出
  • Java泛型 - 实现像map这样的高阶函数

    我决定用 Java 编写一些常见的高阶函数 map filter reduce 等 这些函数通过泛型实现类型安全 但我在一个特定函数中遇到通配符匹配问题 为了完整起见 函子接口是这样的 The interface containing th
  • 按元素聚合数组

    Spark scala 相当新 我想知道是否有一种简单的方法以按列方式聚合 Array Double 这是一个例子 c1 c2 c3 1 1 1 0 1 0 3 4 1 2 1 0 0 0 4 3 2 1 0 0 0 0 0 0 2 3 1
  • Scala 匿名函数中的 return 语句

    为什么显式 return 语句 使用return关键字 在匿名函数中从封闭的命名函数返回 而不仅仅是从匿名函数本身返回 例如 以下程序会导致类型错误 def foo String x Integer gt return x foo 我知道建
  • WSClient - 打开的文件太多

    我正在 CentOS 6 上使用 Play Framework 2 4 我的应用程序抛出此异常 java net SocketException Too many open files 我在 Stack Overflow 上搜索了很多主题并
  • 读取不同文件夹深度的多个 csv 文件

    我想递归地将给定文件夹中的所有 csv 文件读入 Spark SQLDataFrame如果可能的话 使用单一路径 我的文件夹结构如下所示 我想包含具有一个路径的所有文件 resources first csv resources subfo
  • 如何使用精炼库定义 A 和 B 取决于彼此的类型类?

    Problem 我有一个案例类 Passenger 从 A 点出发 前往 B 点 有效乘客意味着A点不等于B点 Passenger a Int b Int 问题 我如何使用设计乘客舱refind https index scala lang
  • 比较 javascript 元素和 scala 变量的 Play 框架 Twirl 模板

    如下面的代码示例所示 我想比较 scala 辅助元素内的 javascript 元素 然而 即使存在元素 abcde 它也始终返回 false 除了使用标签之外 如何获取 scala 辅助元素内的 javascript 值 appSeq S
  • 使用 org.apache.hadoop/* 依赖项离线编译 sbt 时遇到的问题

    使用依赖于 org apache hadoop 包的 sbt 进行离线编译时遇到很多麻烦 一个简单的build sbt name Test version 1 0 scalaVersion 2 10 4 libraryDependencie
  • Scala 2.8 中 <:<、<%< 和 =:= 的含义是什么?它们的文档在哪里?

    我可以在 API 文档中看到Predef https scala lang org files archive api 2 8 2 scala Predef 24 html它们是通用函数类型 From gt To 的子类 但仅此而已 嗯什么
  • SBT插件——编译前执行自定义任务

    我刚刚编写了我的第一个 SBT 自动插件 它有一个生成设置文件的自定义任务 如果该文件尚不存在 当显式调用任务时 一切都会按预期工作 但我希望在使用插件编译项目之前自动调用它 无需项目修改其 build sbt 文件 有没有办法实现这一点
  • Scala apply 方法调用,因为括号与隐式参数冲突

    Cay Horstmann 的书 Scala for the Impressive 中有一段关于 apply 方法的注释 有时 表示法会与另一个 Scala 功能发生冲突 隐式参数 例如 表达式 Bonjour sorted 3 产生错误
  • 在 Scala 中反转地图的优雅方法

    目前正在学习Scala 需要反转Map 来进行一些反转值 gt 键查找 我一直在寻找一种简单的方法来做到这一点 但只想到了 Map origMap map kvp gt kvp 2 gt kvp 1 有人有更优雅的方法吗 假设值是唯一的 则
  • 如何从DataFrame中获取最后一行?

    我有一个DataFrame 该DataFrame有两列 value 和 timestamp timestmp 是有序的 我想获取DataFrame的最后一行 我该怎么办 这是我的输入 value timestamp 1 1 4 2 3 3
  • 这种奇怪的 Scala 内存泄漏的原因是什么? [复制]

    这个问题在这里已经有答案了 即使有 7G 的堆空间 这也会耗尽内存 import scala collection mutable Set class Foo val anEmptySet Set Int Set def bar ints
  • 为 Apache Spark 示例运行 Cypher (CAPS)

    我知道这是一个广泛的问题 但这会对neo4j不属于某个领域的用户scala编程 我需要使用Apache Spark 项目的 Cypher https github com opencypher cypher for apache spark
  • 如何使用 *non-case* 类通过 Scala 从 JSON 反序列化?

    我正在编写一个 Scala 应用程序 需要序列化和反序列化 JSON 某些 JSON 对象具有超过 22 个字段 因此我无法使用案例类 并且也无法更改格式 我找到的所有 Scala JSON 库都只能 轻松地 与案例类一起使用 而不是与普通

随机推荐

  • 如何使移动键盘外观与 div 发生碰撞而不是绝对将其自身定位在其上方?

    我正在开发一个简单的聊天组件 但遇到一个问题 在移动设备上 如果我单击文本框发送消息 而不是弹出其上方的消息列表 虚拟键盘会将自己绝对定位在它 这是不可取的 因为我希望用户在键入消息时能够看到最新消息 但是 我不知道如何纠正这种行为 而且我
  • 打算播放 YouTube 播放列表 [重复]

    这个问题在这里已经有答案了 我想从我的应用程序启动 YouTube 播放列表 对于单个视频来说这是显而易见的 startActivity new Intent Intent ACTION VIEW Uri parse http www yo
  • 从 SQL 到 MS Access 查询数据:本地表与传递表

    我创建了一个应用程序 它使用以下逻辑将数据从 SQL 查询到我的 MS Access 应用程序 我使用 ODBC 连接执行存储过程 使用此指定为传递查询以在本地提取数据 它看起来像这样 strSQL EXEC StoredProcedure
  • ImageIcon 不会更新具有相同 URL 的新图像

    我想使用 JLabel Icon 来显示来自我网站的图像 http xxx xxx xxx xxx java pic test jpg 我有一个刷新按钮来新建一个新的 JLabel 和 ImageIcon 为了获取最新的图像 程序运行成功
  • matplotlib 中的 Pandas 自动日期时间格式

    我经常在一个图中绘制来自不同来源的多个时间序列数据 其中一些需要使用 matplotlib 格式化 x 轴时 我使用 matplotlibautofmt xdate 但我更喜欢 pandas 的自动格式化 我知道我可以使用手动设置格式set
  • BeautifulSoup 不同的解析器

    任何人都可以详细说明 html parser 和 html5lib 等解析器之间的区别吗 我偶然发现了一个奇怪的行为 当使用 html parser 时 它会忽略特定位置的所有标签 看看这段代码 from bs4 import Beauti
  • 在 jquery DataTables 中跳过一行的渲染

    如果在初始化期间满足条件 我想跳过行渲染 但我不知道到底将其放置在哪里 我应该把它放进去吗fnCreatedRow or fnPreDrawCallback 我怎样才能做到这一点 这是我的代码 var users tbl users tbl
  • Cygnus 启动错误:ClassNotFoundException

    我的环境是CentOS 6 6的VM 我按照中的说明进行操作https github com telefonicaid fiware cygnus blob master doc quick start guide md安装天鹅座 还安装了
  • 如何使用 Yocto 生成适用于 Windows 的工具链?

    关于我的最后一个问题我问如何获得 Qt 工具链 我在 Linux 主机上尝试过 它可以工作 现在我需要知道如何使该工具链在 Windows 平台上工作 或者我需要什么 Yocto 设置来生成 Qt Windows SDK 安装程序 Woul
  • 使用 numpy.vectorize() 旋转 NumPy 数组的所有元素

    我正处于学习 NumPy 的开始阶段 我有一个 3x3 矩阵的 Numpy 数组 我想创建一个新数组 其中每个矩阵都旋转 90 度 我研究过这个answer但我仍然不明白我做错了什么 import numpy as np 3x3 m np
  • virtualenv pip mysqldb mac os X python

    我试过这个http jazstudios blogspot com 2010 07 installing mysql python mysqldb in html提示在 virtualenv 名为dogme 这篇文章指出了两件重要的事情 e
  • 如何在按下后以编程方式关闭 SearchView?

    我有同样的问题 我发现here我将重申这一点 因为该解决方案并不是 100 完全符合我的需要 目前 我的应用程序的操作栏中有一个 SearchView 当我单击搜索图标时 SearchView 会展开 并且键盘会按预期弹出 单击 Searc
  • 使用未渲染的控件的视觉画笔?

    我现在正在考虑一个想法 但碰壁了 我正在使用控制台应用程序在内存中创建一个视觉控件 准确地说是 DevExpress 图表控件 然后我尝试使用 VisualBrush 将该控件保存到图像中 但它不起作用 因为 我假设 该控件没有被吸引到屏幕
  • Qt4:使全屏窗口无法绕过(锁定屏幕)?

    我的应用程序是一个操作系统锁定屏幕 如 GDM 的锁定屏幕或 KDE 的锁定屏幕 因此我试图使其具有类似的功能 我试图让我的应用程序的窗口悬停在上面all其他窗口并禁用 拦截所有键盘快捷键 ALT TAB CTRL ALT D等 这会导致它
  • 当包含长文本视图时,滚动视图在 ics(android 4.0)上非常慢

    这是我的问题 我正在开发一个新闻应用程序 我使用滚动视图包装文本视图来显示新闻内容 但我发现当textview很长时 在android 4 0 ics上滚动非常慢 并且文本越长 滚动越慢 在 Android 2 3 设备上 一切都如预期的那
  • asp.net web API HTTP PUT 方法

    我有一些资源 UserProfile public UserProfile public string Email get set public string Password get set 我想分别更改电子邮件和密码 同一时间只能为用户
  • Python,睡眠一些代码而不是全部

    我遇到一种情况 在代码中的某个时刻我想触发多个计时器 代码将继续运行 但在某个时刻这些函数将触发并从 给定列表中删除一个项目 类似 但不完全像下面的代码 问题是 我希望这些函数等待一定的时间 我知道如何使用睡眠的唯一方法是使用睡眠 但是当我
  • 如何在 Node (\u00f6) 中转义 UTF-8 字符?

    我有一个使用 ISO Latin 编码的属性文件 但使用特殊字符作为 UTF 8 转义序列 例如以下字符串 Einstellungen l u00f6schen 我尝试了很多不同的组合iconv punycode and JSON pars
  • JavaScript - 如何为选定的文本设置标记?

    我需要highlight选定的文本JavaScript 没有 jQuery 并且有control points or markers 左和右 我真的不知道如何称呼它们 就像在手机上一样 所以我可以随时通过拖动任何控制点来扩展选择 例子 ht
  • Slick 3.0.0 - 仅更新包含非空值的行

    有一个包含列的表 class Data tag Tag extends Table DataRow tag data def id column Int id O PrimaryKey def name column String name