【问题标题】:Using Couchbase as cache over Slick在 Slick 上使用 Couchbase 作为缓存
【发布时间】:2014-11-18 05:57:24
【问题描述】:

我正在尝试使用 Couchbase 作为使用 Slick 访问的关系数据库的缓存层。我的与问题相关的代码框架如下:

class RdbTable[T <: Table[_]](implicit val bucket: CouchbaseBucket) {
  type ElementType = T#TableElementType
  private val table = TableQuery[T].baseTableRow

  private def cacheAll(implicit session: Session) =
    TableQuery[T].list foreach (elem => cache(elem))

  private def cache(elem: ElementType) =
    table.primaryKeys foreach (pk => bucket.set[ElementType](key(pk, elem), elem))

  private def key(pk: PrimaryKey, elem: ElementType) = ???
  .......
}

如您所见,我想通过其所有主键来缓存每个元素。为此,我需要获取给定元素的该键的值。但是我没有看到一种明显的方法来计算主键的值(列值,如果是单列键;元组值,如果是多列)。

有什么建议吗?请注意,代码必须不知道实际的表及其列是什么。它必须是完全通用的。

【问题讨论】:

    标签: scala caching primary-key couchbase slick


    【解决方案1】:

    我们正在做类似的事情,使用 Redis 作为缓存。我们的每条记录只有一个主键,但在某些情况下,我们需要在缓存键中包含额外的数据以避免歧义(例如,我们有一个表示两条记录之间关联的 ManyToMany 记录;当我们返回一个 ManyToMany 记录时我们将嵌入一个(但不是两个)关联记录,因此在缓存键中我们需要包含我们要返回的关联记录的类型。

    abstract trait Record {
      val cacheKey: CacheKey
    }
    
    trait ManyToManyRecord extends Record {
      override val cacheKey: ManyToManyCacheKey
    }
    
    class CacheKey(recordType: String, key: Int) {
      def getKey: String = recordType + ":" + key.toString
    }
    
    class ManyToManyCacheKey(recordType: String, key: Int, assocType: String) extends CacheKey {
      def getKey: String = recordType + ":" + key.toString + ":" + assocType
    }
    

    我们所有的表都使用一个名为“id”的整数主键,因此我们很容易弄清楚“key”的值是什么。如果您正在使用更复杂的模式并且不想手动为所有记录/表类型写出“def key:String”(或其他)定义,那么您可以尝试使用Slick code generation 自动使用直接从模式创建的“def key”生成记录/表类/对象。但是,Slick 代码生成(或任何其他代码生成工具)的学习曲线非常陡峭,因此如果这是您唯一的用途,那么您最好手动生成“def key”。 (我们使用代码生成工具生成了大约 20-30% 的代码,因此学习如何使用该工具的初始投资得到了回报)

    【讨论】:

    • 谢谢,Zim-Zam!是的,我想我可以在声明的 key() 中混合一个特征,并在每个具体的表类中定义它——然后跳过一些箍来调用它,因为 T 被定义为扩展 Table [_]。不是最好的解决方案,但肯定是可能的。但是,我的直觉告诉我,它不应该是必要的。 PrimaryKey 对象知道它由哪些列组成,并且 T#TableElementType 行知道它的值,因此应该可以将它仅投影到主键列上。似乎没有简单的编程方式可以做到这一点,这真是太奇怪了!或者有吗?
    【解决方案2】:

    Slick 没有内置的实体主键提取器。您可以做的是使用接口、类型类或反射。例如。以下变体:

    要么让你的实体实现一个特征

    trait HasPrimaryKey{
      def primaryKey: Any
    }
    class RdbTable[T <: Table[_ <: HasPrimaryKey]](implicit val bucket: CouchbaseBucket) {
      ...
      private def key(elem: ElementType) = elem.primaryKey
    
    // and for each entity:
    case class Person( ... ) extends HasPrimaryKey{
      def primaryKey = ...
    }
    

    或类型类

    trait KeyTypeClass[E,T <: Table[E]]{
      def key(e: E): Any
    }
    class RdbTable[T <: Table[_]](implicit val bucket: CouchbaseBucket, keyTC: KeyTypeClass[T]) {
      ...
      private def key(elem: ElementType) = keyTC(elem)
    
    // and for each entity:
    implicit val personKey = new KeyTypeClass[Person,PersonTable]{
      def key(p: Person) = ...
    }
    

    或使用反射来迭代主键并将值从实体的相应字段中提取出来。

    Zim-Zam 提到的代码生成可以帮助处理重复的元素。

    【讨论】:

    • 谢谢,cvogt!遗憾的是 Slick 中没有主键提取器。在您的建议中,我更喜欢第二个,因为我不想用变通方法代码污染域模型类。
    • 进一步问题:如何按此键(E)函数的值过滤查询?当我在 Query 实例上调用 filter(...) 时,“_”的类型是 T,而不是 E。
    • E和T需要分别定义函数,如果两者都需要,需要定义两次。
    • 谢谢。但是如果我将第二个函数定义为返回 Seq[Column[_]]: trait PrimaryKey[E, T &lt;: Table[E]] {&lt;/br&gt; type KeyType &lt;: Seq[Column[_]]&lt;/br&gt; def extract(t: T): KeyType&lt;/br&gt; def key(e: E): String&lt;/br&gt; } 然后尝试使用 a 中序列中的每一列 query.filter(pk.extract(_)(n) === key(n)) 表达式,我收到以下错误: "value === is not a member of slick.driver.PostgresDriver.simple.Column[_$1]"
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 2015-07-16
    • 2011-03-16
    • 1970-01-01
    • 2015-09-19
    相关资源
    最近更新 更多