Scala 巧妙地使用db计算字段插入行并获取id

Scala 巧妙地使用db计算字段插入行并获取id,scala,activerecord,slick,Scala,Activerecord,Slick,我正试图用Slick编写一个简单的活动记录风格的DAO,我发现这非常困难。 我想使用case类表示插入一行,使用数据库时间设置其“updated”列,并将新行值作为case类返回 i、 e.DAO应如下所示: class FooDao { def db: JdbcBackend.Database def insert(foo: FooRow): Future[FooRow] = { ??? } } // This class is auto-generated by sl

我正试图用Slick编写一个简单的活动记录风格的DAO,我发现这非常困难。 我想使用case类表示插入一行,使用数据库时间设置其“updated”列,并将新行值作为case类返回

i、 e.DAO应如下所示:

class FooDao {
  def db: JdbcBackend.Database

  def insert(foo: FooRow): Future[FooRow] = {
    ???
  }
}

// This class is auto-generated by slick codegen
case class FooRow(
  id: Int,
  created: Option[java.sql.Timestamp]
  updated: Option[java.sql.Timestamp],
  x1: String,
  x2: String,
  x3: String)
我希望返回的
FooRow
对象具有正确的ID,并且
已创建
由数据库分配的时间戳

我还需要一个
update
方法来更新
foorw
,使用
updated
列的数据库时钟并返回一个新的
foorw

我尝试过的事情: 使用“返回” 我可以插入case类并使用返回的帮助程序获取生成的id,如中所述,即

这是可行的,但我看不到任何方法可以在insert语句中使用
CURRENT\u TIMESTAMP()
来填充
updated
列。类型就是不允许

使用
sqlu
我可以使用
sqlu
编写我想要的SQL,但是我看不到任何方法可以使生成的密钥脱离Slick。JDBC API允许通过
getGeneratedKeys()
访问它,但我看不到如何在这里访问它:

class FooDao {
  def db: JdbcBackend.Database

  def insert(foo: FooRow): Future[FooRow] = {

    val sql: DBIO[Int] = sqlu"""
INSERT INTO  foos
            (created,
             updated,
             x1,
             x2,
             x3)
     VALUES (current_timestamp(),
             current_timestamp(),
             $foo.x1,
             $foo.x2,
             $foo.x3)
               """

    db.run(sql)
        .map(rowCount => ???)
  }
}
使用
active slick
该项目看起来几乎符合我的需要,但它似乎不支持更新/创建的列

使用数据库默认值/触发器 我想我可以使用数据库默认值实现
created
列,但是如何防止Slick在其insert语句中传入显式值并推翻它呢

我想我可以使用数据库触发器实现
updated
列,但我更愿意将逻辑保留在Scala层中,并保持数据库的简单

使用web服务器时钟而不是DB时钟 目前最接近这一点的方法是使用web服务器时钟而不是DB时钟:

class FooDao {
  def db: JdbcBackend.Database

  def insert(foo: FooRow): Future[FooRow] = {

    val now = Some(Timestamp.from(Instant.now()))

    val toInsert = fooRow.copy(
       created = now,
       updated = now)

    db.run((Foos returning Foos.map(_.id)) += toInsert)
        .map(id => toInsert.copy(id = id))
  }
}
但是,我更喜欢为这些列使用数据库时钟,以防不同的web服务器有不同的时间


任何想法都将受到感激。我正在强烈考虑放弃Slick并使用JOOQ,这样做很容易。

为什么会有带有
activerecord
的标签?这只是我的意见。。。Slick和Scala偏向于
函数范式
,而像ActiveRecord这样的东西对大多数Scala程序员来说并不是很有用。不同的范式需要不同的方法。即使是制作比萨饼的最佳配方也不应该应用于制作汉堡。如果你正在寻找一种方法来支持像ActiveRecord这样的任意模型。。。您将需要同时使用scala宏和scala-reflection。所谓ActiveRecord,我的意思是“仅”,即“符合此模式的对象接口将包括诸如Insert、Update和Delete之类的函数,以及或多或少直接对应于基础数据库表中列的属性。”。我不认为这有什么不实用的地方。如果你有更合适的“披萨”食谱,我洗耳恭听。我不知道你这里所说的“任意模式”是什么意思。光滑的代码生成器在编译时之前从DB模式中创建类似“FooRow”的case类。不需要反射或宏。是。。。狡猾的代码gen做到了这一点。但是,对于从scrach开发的应用程序来说,这是正确的方法吗?这不应该是另一种方式吗(你用代码控制DB而不是DB控制代码)?
class FooDao {
  def db: JdbcBackend.Database

  def insert(foo: FooRow): Future[FooRow] = {

    val now = Some(Timestamp.from(Instant.now()))

    val toInsert = fooRow.copy(
       created = now,
       updated = now)

    db.run((Foos returning Foos.map(_.id)) += toInsert)
        .map(id => toInsert.copy(id = id))
  }
}