Пользователь
0,0
рейтинг
23 ноября 2012 в 13:50

Разработка → SORM. Новый элегантный и масштабируемый ORM фреймворк для Scala tutorial

Тот, кому доводилось иметь дело с выбором ORM для Scala, наверняка, наслышан о таких библиотеках, как Slick (Scala Query), Squeryl, Circumflex и пр., и, не менее вероятно, согласится со следующими утверждениями: они не абстрагируются от реляционных концепций, они требуют описания модели специфическими способами, API зачастую запутан и рассредоточен, и, наконец, то, насколько предложенные этими библиотеками абстракции в действительности упрощают работу с базой данных является, по меньшей мере, сомнительным.

Так и родилась идея создать фреймворк, который
  1. возведёт абстракцию над базой данных до более высокого уровня, представляя её через стандартные типы данных Scala: примитивы, кортежи, опции, коллекции и тд., а так же стандартные кейс-классы, представляющие собой сущности,
  2. выполняя первую задачу, будет «чистым», что означает полное исключение концепций реляционной стороны из API фреймворка: никаких таблиц, строк и реляционных связей,
  3. возведёт в принцип основы функционального программирования: только немутируемые типы данных, сведение State к минимуму,
  4. будет выполнять за пользователя всё, что он может, за счёт чего достигнет минимизации boilerplate.

Как видите, задачи все стояли достаточно бескомпромиссные, однако они были решены. За счёт этого удалось добиться последовательности, простоты и вытекающей интуитивности фреймворка, — и это при отнюдь не слабых возможностях. Boilerplate удалось, и вовсе, исключить. Дальше код.

// Модель данных. 
// Никаких аннотаций, никаких особых типов данных и пр. Самые обыкновенные 
// кейс-классы, которые Вы можете использовать в любом месте приложения - 
// к примеру, передавать во view.
case class Artist ( name : String, genres : Set[Genre] )
case class Genre ( name : String ) 

// Объявление базы данных. Все настройки SORM указываются тут же:
import sorm._
object Db extends Instance (
  entities = Set() + Entity[Artist]() + Entity[Genre](),
  url = "jdbc:h2:mem:test"
)

// Сохраним несколько значений в БД:
val metal = Db.save( Genre("Metal") )
val rock = Db.save( Genre("Rock") )
Db.save( Artist("Metallica", Set() + metal + rock) )
Db.save( Artist("Dire Straits", Set() + rock) )

// Получим кое-что:
// Option[Artist]:
val metallica = Db.query[Artist].whereEqual("name", "Metallica").fetchOne() 
// Stream[Artist]:
val rockArtists = Db.query[Artist].whereEqual("genres.item.name", "Rock").fetch() 


Как говорится, that's all folks! Это полный код программы, создающей базу данных и сохраняющей и запрашивающей из неё сущности. Никаких дополнительных действий со стороны пользователя для её работоспособности не требуется.

Информацию по установке, документацию, туториал и сравнение со Slick (Scala Query) Вы найдёте на официальном сайте SORM.

Спойлер: в следующих версиях SORM грядут макросы и вытекающее ещё большее упрощение API.
Никита Волков @mojojojo
карма
12,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (21)

  • +1
    Выглядит любопытно, надо попробовать. Ну и с первый постом что-ли :)
    • 0
      Спасибо!
      • 0
        Не только с постом, но, похоже, и с фреймворком!) Вы автор?
        • 0
          А все, понял, прошу прощения. Ниже вы писали о том, что вы его разработчик, а не разработчик на нём)
        • 0
          Спасибо. Да, автор.
  • +1
    а в чем заключается масштабируемость данного фреймвокра?
    • +1
      На данный момент это встроенный connection pooling. Вот всё, что нужно изменить в примере, для того, чтобы программа поддерживала 16 соединений к БД:
      object Db extends Instance (
        entities = Set() + Entity[Artist]() + Entity[Genre](),
        url = "jdbc:h2:mem:test",
        poolSize = 16 // <--
      )
      

      Рассматривается возможность (в зависимости от спроса и фидбека) в будущем добавить поддержку подключения и балансирования нагрузки на матрице серверов самим фреймворком, т.е. непосредственная поддержка масштабируемости на распределение нагрузки и расширения объема. Пока особых препятствий в этой идее не предвижу
      • 0
        Вы разработчик? Как с Akka будет работать?
        • 0
          Я разработчик. С Akka работает без проблем. Проверено на собственных проектах.
          • 0
            Короткий пример с Akka можете показать?
            • +1
              Объект Db Вам доступен из любого контекста в Вашем приложении, что освобождает от какой-либо специфики в работе с актёрами.

              Вот кусок кода из одного моего приложения:
              class DbNegotiator extends Actor {
                def receive = {
                  case CreateTasks(asins) =>
                    Db.transaction {
                      val existingTasks = Db.query[Task].whereIn("asin", asins).fetch().toStream
                      // reset the failed generated ones
                      existingTasks
                        .filter(_.failure != None)
                        .filter(_.generated)
                        .map(_.copy(failure = None, generated = false, outdated = false))
                        .foreach(Db.save)
                      // create the new ones
                      asins.view
                        .diff(existingTasks.map(_.asin))
                        .foreach(a => Db.save(Task(_, Db.now())))
                    }
                }
              }
              
              
  • 0
    Как-то просто все, где-то подвох:)
    • 0
      Всё дело в том, что первым этапом в разработке данного фреймворка решался вопрос, того, как он будет использоваться, и уже под это писалась вся подноготная. В этом, мне кажется, и заключается его принципиальное отличие от таких библиотек, как Squeryl или Slick, в которых фичи появлялись по ходу разработки, а вопрос гармоничности АПИ был совершенно второстепенным.
  • 0
    > whereEqual(«genres.item.name», «Rock»)
    Ссылаться на поля строковыми параметрами как-то противоречит идеологии typesafe.

    Есть фреймворк QueryDsl. Это не ORM, но надстройка над ним для typesafe запросов. ORM-backend-ом может являться все что угодно — JPA/JDO/MongoDB/SQL. Изначально сделан под Java, но есть порт под Scala: blog.mysema.com/2010/09/querying-with-scala.html
    • 0
      Да, противоречит, но:
      1. Далеко не всякое typesafe решение оказывается удобнее. Во многих случаях в жертву приносится функциональность и лаконичность.
      2. Не стоит преувеличивать возможности typesafe. Это, всего лишь, статическая проверка типов. Она не спасает от массы рантайм-ошибок другого происхождения, из-за чего, как ни крути, приложение надо-таки тестировать.
      3. Как то же живут люди с питонами, руби и т.д. Да, мало того, что живут, порой динамику и в культ возводят.

      Однако, как бы то ни было, сейчас идёт работа по интеграции макросов, на которых будет основано новое статическое АПИ для запросов, благодаря чему и данный вопрос будет исчерпан.
  • +1
    Над чем абстракция? Над JDBC обычным, я так понимаю? Т.е. все запросы блокирующие/синхронные? Вы там выше пример с Аккой природите, правильно ли я понимаю, что thread блокируется в момент, когда вы делаете fetch (ну или последующую операцию на его результате) и это всё не reactive? Если да, то говорить, что это «работает» с Аккой всё-таки не совсем честно ;)
    • 0
      1. Если Вы знаете лучший способ коннектиться к Postgre или Mysql из JVM, чем «JDBC обычный», уж, пожалуйста, поделитесь. Ладно лучший — хотя бы даже иной.

      2. Запросы блокирут тот тред, на котором запущены. Естесственно. 2+2 тоже блокирует тот тред, на котором вычисляется. Объявление future тоже блокирует тред на момент объявления и выделения треда для вложенных вычислений. Любая вычислительная операция неизбежно блокирует тот тред, на котором исполняется при любой модели программирования. Главное, что SORM не блокирует обращения к БД, сделанные с разных тредов, благодаря пулингу соединений, чего, к примеру, лишён Slick.

      3. Если то, в чём Вы пытаетесь упрекнуть — это отсутствие асинхронности, то да, в библиотеке её нет. Впрочем, и реализовывать её было бы глупо, т.к. вот всё, что требуется для того, чтобы в Scala сделать операцию асинхронной:

      // sync:
      val artists = Db.query[Artist].fetch()
      // async:
      future{ Db.query[Artist].fetch() }.foreach{ artists => ... }
      

      4. Мне кажется, у Вас ошибочное представление о модели актёров. Суть в том, что она сама и является абстракцией над многопоточностью. Альтернативный Future подход к асинхронным вычислениям. Так, в приложении, из которого я приводил пример, параллельно работает 16 инстансов такого актёра, и никто не мешает изменить их количество на, скажем, 1000, однако встаёт вопрос разумности.

      Поэтому, да, SORM работает с Akka, без всяких увиливаний.
      • –1
        Зря вы так реагируете, я ведь на конструктивную дискуссию расчитывал, а вы думаете мне делать больше нечего, только дай очередной велосипед потролить.

        Т.е. вы правда даже представить себе не можете других вариантов как можно было бы работать с реляционной БД вроде PostgreSQL/MySQL не будь у вас JDBC с драйверами от вендоров? Для вас так естественно, что «запрос» (обычная сетевая операция ввода вывода, ожидающая ответа от удалённого сервера) блокирует поток? Акка с блокирующими акторами – это примерно как забивание гвоздей микроскопом, поверьте, она вам на самом деле нафиг не нужна. Вы просто мелко плаваете и о высокой нагрузке и масштабировании имеете очень отдалённое представление, поэтому вы смотрите на всё зашореным взглядом и пишете 100500-й никому не нужный ОРМ-велосипед, в то время как простаивают серьёзные задачи, за решение которых коммьюнити действительно сказало бы вам спасибо.
        • 0
          Посоветуйте плз альтернативу для использования с Аккой
        • 0
          Раз Вы считаете, что конструктивная дискуссия начинается с претенциозной критики, которую Вы даже не пытаетесь обосновать, и перетекает в непосредственные оскорбления, на этом я её прекращу.
          • 0
            Т.е. вы считаете, что когда я сказал «не совсем честно» – это было «претенциозно», а когда вы в ответ на это (вполне кстати конструктивное) замечание сказали, что я вобще не понимаю, что такое Акка, и вобще ерунду какую-то несу – это вот нормально.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.