【问题标题】:Slick: Pass in column to updateSlick:传入列进行更新
【发布时间】:2018-09-25 02:17:04
【问题描述】:

假设我们有一个包含以下列的 FoodTable:NameCaloriesCarbsProtein。我有 Name = ChocolateCalories = 100Carbs = "10g"Protein = "2g" 的条目。

我想知道是否有办法传入列名和要更新的新值。例如,我想要一个类似的方法

def updateFood(food, columnName, value):
     table.filter(_.name === food).map(x => x.columnName).update(value)

Slick 似乎无法使用动态列?我想避免编写 SQL 查询,因为这可能会导致代码中的安全漏洞或错误。真的没有办法吗?

我也不想传递整个对象来更新,因为理想情况下,它应该是:

我想将列 X 更新为值 Y。我应该只需要传入对象的 id、列和要更新到的值。

【问题讨论】:

  • 我想建议这是一种惯用的错误方法(传递列名和列值),因为在这种情况下您将失去类型安全性。这与构造 SQL 查询一样糟糕。我不知道你的实体是什么样的,但也许你可以为你的 DAO 中的每个字段保留一些合理数量的更新方法?

标签: sql database scala slick


【解决方案1】:

我想知道是否有办法传入列名和要更新的新值

这在一定程度上取决于您希望“列名”是什么。为了保持安全,我建议让“列名”成为一个可以选择表中列的函数。

在高层次上看起来像这样:

// Won't compile, but we'll fix that in a moment
def updateFood[V](food: Food, column: FoodTable => Rep[V], value: V): DBIO[Int] =
  foods.filter(_.name === food.name).map(column).update(value)

...我们这样称呼它:

updateFood(choc, _.calories, 99)

注意“列名”是如何从FoodTable 到某个值V 的列的函数。然后您为V 提供一个值,我们会进行正常更新。

问题在于 Slick 知道如何将某些类型的值(String、Int 等)映射到 SQL,但不知道 任何 类型的值。并且上面的代码不会编译,因为V 是不受约束的。

我们可以解决我在V 上添加约束的问题,而且它大部分都可以工作:

// Will compile, will work for basic types
def updateFood[V : slick.ast.BaseTypedType](food: Food, column: FoodTable => Rep[V], value: V): DBIO[Int] =
 foods.filter(_.name === food.name).map(column).update(value)

但是,如果您有自定义列映射,它们将与约束不匹配。我们需要再往前走一步,在范围内有一个隐含的形状:

def updateFood[V](food: Food, column: FoodTable => Rep[V], value: V)(implicit shape: Shape[_ <: FlatShapeLevel, Rep[V], V, _]): DBIO[Int] =
 foods.filter(_.name === food.name).map(column).update(value)

我认为Shape 是 Slick 中的一个额外抽象级别,高于 Rep[V]。 “形状级别”的机制和其他细节我无法解释,因为我还不了解它们! (有一个关于 Slick 设计的演讲,名为“提升嵌入中的多态记录类型”,您可以在 http://slick.lightbend.com/docs/ 找到它)

最后一点:如果你真的希望列名是字符串或类似的东西,我建议将字符串模式匹配(或以某种方式验证)到 FoodTable =&gt; Rep 函数并在你的 SQL 中使用它.这会很棘手,因为您的值 V 必须与您要更新的列的类型相匹配。

在我的脑海中,它可能看起来像这样:

def tryUpdateFood(food: Food, columnName: String, value: String): DBIO[Int] =
 columnName match {
   case "calories" => updateFood(food, _.calories, value.toInt)
   case "carbs" => updateFood(food, _.carbs, value)
   // etc...
   case unknown => DBIO.failed(new Exception(s"Don't know how to update $unknown columns"))
}

我可以想象更好的错误处理,更安全或更智能的值解析,但总的来说,上述方法可以工作。

有关解决动态问题的其他方法的提示,请查看“Slick 数据库应用程序的模式”(也列在:http://slick.lightbend.com/docs/)的演讲,在演示文稿的最后有一个关于“动态排序”的部分”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-10
    • 2016-05-02
    • 1970-01-01
    • 2014-07-22
    相关资源
    最近更新 更多