Slick中如何传入列名实现动态数据更新?
Great question! Slick absolutely supports dynamic column updates—you just need to work with its type system a bit to avoid repetitive code. Let's break down how to build that generic updateFood function you're aiming for, using your FoodTable example.
First, Make Sure Your Table Definition is Set Up
First, let's confirm your FoodTable is defined correctly (this is the base we'll build on):
import slick.jdbc.H2Profile.api._ class FoodTable(tag: Tag) extends Table[(String, Int, String, String)](tag, "FOOD_TABLE") { def name = column[String]("Name") def calories = column[Int]("Calories") def carbs = column[String]("Carbs") def protein = column[String]("Protein") // Define the default projection (mapping between table rows and Scala tuples) override def * = (name, calories, carbs, protein) } // Create a query object for the table val foodTable = TableQuery[FoodTable]
Option 1: Dynamic Updates Using Column Name Strings
If you want to stick with passing column names as strings (like your initial example), you'll need a way to map those strings to Slick's Rep column instances. Here's how to do it safely:
Step 1: Map Column Names to Slick Columns
Create a helper function to convert column name strings to their corresponding Rep objects:
import slick.lifted.Rep def getFoodColumn(columnName: String): Rep[_] = columnName.toLowerCase match { case "name" => foodTable.name case "calories" => foodTable.calories case "carbs" => foodTable.carbs case "protein" => foodTable.protein case unknown => throw new IllegalArgumentException(s"Unrecognized column: $unknown") }
Step 2: Build the Generic Update Function
Now we can write a generic update method that uses this helper. We'll use Scala's type constraints to ensure the value you pass matches the column's type:
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future // Assume you have a Database instance named `db` configured elsewhere def updateFood[T](foodName: String, columnName: String, value: T)(implicit ev: T =:= ColumnType[T]): Future[Int] = { // Cast the column to the correct type (safe because we enforce type matching via the implicit) val targetColumn = getFoodColumn(columnName).asInstanceOf[Rep[T]] // Build the update query: filter by food name, select the target column, update with the new value val updateQuery = foodTable.filter(_.name === foodName).map(_ => targetColumn).update(value) // Execute the query and return the number of rows updated db.run(updateQuery) }
How to Use It
You can call this function just like you wanted:
// Update Chocolate's calories to 120 updateFood("Chocolate", "Calories", 120) // Update Chocolate's carbs to "12g" updateFood("Chocolate", "Carbs", "12g")
Option 2: Type-Safe Dynamic Updates (Better for Avoiding Typos)
If you want to eliminate the risk of typos in column names, you can use a sealed trait/enum to represent your columns. This is more type-safe and still avoids repetitive code:
Step 1: Define Type-Safe Column References
sealed trait FoodColumn[T] { def rep: Rep[T] } object FoodColumn { case object Name extends FoodColumn[String] { val rep = foodTable.name } case object Calories extends FoodColumn[Int] { val rep = foodTable.calories } case object Carbs extends FoodColumn[String] { val rep = foodTable.carbs } case object Protein extends FoodColumn[String] { val rep = foodTable.protein } }
Step 2: Build the Type-Safe Update Function
def updateFoodTyped[T](foodName: String, column: FoodColumn[T], value: T): Future[Int] = { val updateQuery = foodTable.filter(_.name === foodName).map(_ => column.rep).update(value) db.run(updateQuery) }
How to Use It
Now you can call it with compile-time type safety:
updateFoodTyped("Chocolate", FoodColumn.Calories, 120) updateFoodTyped("Chocolate", FoodColumn.Carbs, "12g")
Key Takeaways
- Slick does support dynamic column operations—you just need to bridge the gap between string column names (or type-safe references) and Slick's strongly-typed
Repcolumns. - Both approaches let you avoid writing separate update methods for each column, keeping your code DRY.
- The type-safe enum approach is preferred if you want to catch typos at compile time instead of runtime.
内容的提问来源于stack exchange,提问作者Minh Trang Vy

