2013-12-23 3 views
1

Я присоединился к гордым рядам людей, пытающихся использовать Squeryl в качестве рамки ORM для веб-приложения. (Для записи я использую Scalatra в качестве фактической веб-структуры, но я не думаю, что это вопрос Scalatra.) Это означает, что я присоединился к рядам людей, пытающихся создать эффективный уровень абстракции, чтобы DRY наш общие операции. Например, довольно часто можно увидеть примеры, как так:Абстрактные операции CRUD с использованием Squeryl и базового класса

// First Model 

package com.myproj 
import com.myproj.Schema 

class Foo() extends KeyedEntity { 
    val id = 0 
    def getAll() = { from(Schema.Foo)(s => select(s)) }  
} 

// Different Model 

package com.myproj 
import com.myproj.Schema 

class Bar() extends KeyedEntity { 
    val id = 0  
    def getAll() = { from(Schema.Boo)(s => select(s)) } 
} 

Итак, с одной стороны, я рою как синтаксис скупых Squeryl есть. С другой: это довольно повторяющийся. То, что я хочу, больше похоже на:

// Base 

class BaseEntity extends KeyedEntity { 
    val id = 0 
    def getAll() = { from(table)(s => select(s)) } 
} 

// New model 

class Foo extends BaseEntity 

// New model 

class Bar extends BaseEntity 

У меня в основном такая работа. Расширение KeyedEntity довольно прямолинейно. Есть только одна проблема: как вы определяете таблицу в BaseEntity, чтобы классы, расширяющие ее, могли получить к ней доступ? Честно говоря, это в корне может быть вопросом о том, что у меня недостаточно сложное понимание системы типа Scala. Я все равно представляю его здесь.

Я попробовал несколько вещей:

  1. Декларирование val table в абстрактном BaseEntity. Это привело меня к довольно смешному типу проверки. val table: Table[T] работает только в том случае, если я также определяю T как тип, а затем дочерние классы вызывают ошибки компилятора, когда они пытаются предоставить таблицу другого типа.
  2. Напишите базовый класс, чтобы ожидать, что таблицы будут переданы в качестве аргумента для каждой функции. Это означает, что каждая модель все еще должна вызывать ее родительский метод для передачи аргумента table.
  3. Я взломал с помощью this SO post, который использует TypeTags. Однако плакат не предоставил достаточно информации для меня, чтобы понять его реализацию.
  4. Вышеупомянутый сообщение SO имеет комментарий, в котором предлагается метод org.squeryl.Schema.findTablesFor. Опять же с потенциальными проблемами новичков: я не добился успеха в том, как реализовать это как ответ. Я пытался что-то вроде:

    класс BaseEntity {
    валь таблицы = findTablesFor (это)
    }

Но тогда, я вернусь итератор, и я немного не уверен, что делать это.

So. Есть ли «правильный» способ сделать это? Разумеется, есть чистый способ перемещения операций CRUD в базовый класс - я просто не могу понять, как это понять.

Редактировать

Итак, вот что у меня есть, используя Squeryl 9.5-6:

// Schema 
package com.myproj.schema 
object MySchema extends Schema { 
    val foo = table[Foo]("foos") 
    val bar = table[Bar]("bars") 
} 

// BaseEntity 
package com.myproj.models 
import com.myproj.schema.MySchema 
abstract class BaseEntity extends Keyedentity[Long] { 
    val id: Long = 0 
    val table = MySchema.findTablesFor(this).head 
} 

// Class 
package com.myproj.models 
case class Foo (
    val name: String, 
    val extra: Option[String] 
) extends BaseEntity { 
    def this() = this("", None) 
} 

установки, как это. findTablesFor всегда возвращает пустой итератор. Он компилируется, но выдает ошибки во время выполнения, пытаясь вызвать головку на пустой итератор (как вы сказали, это будет). Обработка ошибки не является проблемой; неспособность найти таблицу kinda есть.

Мысли?

+0

Прежде всего, измените элемент таблицы на def или, по крайней мере, на ленивый val, чтобы отложить оценку. Вы можете увидеть, что findTablesFor делает здесь: https://github.com/max-l/Squeryl/blob/79fd98876e62044e87521c291e68b4a961cb6ebb/src/main/scala/org/squeryl/Schema.scala#L78. Определение таблицы добавит ее в коллекцию _tables: https://github.com/max-l/Squeryl/blob/79fd98876e62044e87521c291e68b4a961cb6ebb/src/main/scala/org/squeryl/Schema.scala#L338. Я могу только представить, что это порядок операций. –

+0

Hrm. К сожалению, ни «lazy val table», ни 'def table' не имеют никакого значения - он работает, но все равно не находит таблицы. Я использую Scala 2.10.2 - возможно ли это проблема совместимости? – Gastove

+0

(Для записи: если я вызываю функции squeryl гораздо более явно - скажем, но выполняю 'from (MySchema.Foo) (s => select (s))' - он отлично работает, как и MySchema.create. .) – Gastove

ответ

3

таблица val: Таблица [T] работает только в том случае, если я также определяю T как тип, а затем дочерние классы вызывают ошибки компилятора, когда они пытаются предоставить таблицу другого типа.

Вы можете сделать это самостоятельно. Я не уверен, что я рекомендовал бы его, но он должен работать:

class BaseEntity[T] { 
    self: T => 

    val table: Table[T] 

} 

Тогда ваша реализация будет выглядеть так:

class MyEntity extends BaseEntity[MyEntity] 

Использование findTablesFor, вероятно, является лучшим решением. Нет ничего, что помешало бы вам сопоставить класс с несколькими таблицами в схеме Squeryl. Вы могли бы:

val tableA = table[MyEntity] 

val tableB = table[MyEntity] 

Следовательно, причина возврата Iterable для всех соответствующих таблиц. Если вы знаете, что вы не собираетесь делать это, хотя, вы можете просто использовать первый результат:

val table = MySchema.findTableFor(this).head 

Обратите внимание, что это вызовет исключение, если нет соответствующих таблиц не найдено.

+0

Во-первых, спасибо за мгновенное время отклика! Очень признателен. Второй: 'MySchema.findTablesFor (this)' возвращает только Итерируемый размер 0. Я обновляю свой пост с более строгим кодом - я должен делать что-то неправильно. – Gastove

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