我想知道是否有办法传入列名和要更新的新值
这在一定程度上取决于您希望“列名”是什么。为了保持安全,我建议让“列名”成为一个可以选择表中列的函数。
在高层次上看起来像这样:
// 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 => 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/)的演讲,在演示文稿的最后有一个关于“动态排序”的部分”。