2014-09-23 2 views
0

Я пытаюсь использовать Couchbase в качестве уровня кэша для реляционной базы данных, доступ к которой осуществляется с помощью Slick. Скелет моего кода, что это отношение к вопросу заключается в следующем:Использование 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) = ??? 
    ....... 
} 

Как вы можете видеть, я хочу, чтобы кэшировать каждый элемент на всех его первичных ключей. Для этого мне нужно получить значение этого ключа для данного элемента. Но я не вижу очевидного способа вычислить значение первичного ключа (значение столбца, если одноколоночный ключ, значение кортежа, если используется несколько столбцов).

Любые предложения о том, что делать? Обратите внимание, что код НЕ ДОЛЖЕН знать, что представляют собой фактические таблицы и их столбцы. Он должен быть полностью общим.

ответ

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 
} 

Все наши таблицы используют целочисленное первичный ключ, называемый «идентификатор», так что это легко для нас, чтобы выяснить, что значение «ключ» есть. Если вы работаете с более сложной схемой и не хотите вручную выписывать определения «def key: String» (или что-то еще) для всех ваших типов записей/таблиц, вы можете попробовать использовать Slick code generation для автоматической генерации записи/table classes/objects с «def key», созданным непосредственно из схемы. Однако кривая обучения для генерации кода Slick (или любого другого инструмента генерации кода) является крутой, поэтому, если это ваше единственное использование, то вам, вероятно, лучше будет генерировать «def key» вручную. (Мы генерируем где-то между 20-30% нашего кода с помощью инструмента генерации кода, поэтому первоначальные инвестиции в изучение способов использования инструмента окупились)

+0

Спасибо, Zim-Zam! Да, я полагаю, что я мог бы смешивать в признаке с объявленным ключом () и определить его в каждом конкретном классе таблицы, а затем перепрыгнуть через некоторые обручи, чтобы вызвать его, поскольку T определяется как расширяющая таблицу [_] , Не лучшее решение, но, безусловно, возможно. Однако моя интуиция подсказывает мне, что это не обязательно. Объект PrimaryKey знает, в каких столбцах он создан, и строка T # TableElementType знает свои значения, поэтому ее можно проецировать только на столбцы первичного ключа. Как странно, что нет простого программного способа сделать это! Или есть? – silverberry

0

Слик не поставляется со встроенным экстрактором первичного ключа для субъектов. Что вы можете сделать, это использовать либо интерфейсы, классы классов, либо отражение. Например. Варианты следующий:

Либо сделать ваше лицо реализовать черты

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, может помочь с повторяющимися элементами.

+0

Спасибо, cvogt! Жаль, что в Slick нет экстрактора первичных ключей. Из ваших предложений, я предпочитаю второй, поскольку я не хочу загрязнять классы модели домена с помощью обходного кода. – silverberry

+0

Дальнейший вопрос: как фильтровать запрос по значению этой функции (E)? Когда я вызываю фильтр (...) на экземпляре Query, «_» имеет тип T, а не E. – silverberry

+0

Вам нужно определить функции отдельно для E и T, поэтому, если вам нужно обоим, вам нужно определить его дважды. – cvogt

Смежные вопросы