Статьи

Решая ORM — Держите O, бросьте R, не нужно M

ORM имеет простое, готовое к работе решение, скрывающееся от глаз в мире Java. Давайте рассмотрим это в этом посте вместе со следующими темами:

  • ORM / Hibernate в 2014 году — слово на улице
  • ORM по- прежнему Вьетнам компьютерных наук
  • У ORM есть только 2 основные цели
  • Когда ORM имеет смысл?
  • Простое решение проблемы ORM
  • Готовая к производству альтернатива ORM на основе Java

ORM / Hibernate в 2014 году — слово на улице

Прошло уже почти 20 лет с тех пор, как появился ORM, и скоро мы достигнем 15-летия создания фактической и, вероятно, лучшей реализации ORM в мире Java: Hibernate.

Тогда мы ожидаем, что это хорошо известная проблема. Но что в эти дни говорят разработчики о Hibernate и ORM?

Давайте возьмем несколько цитат из двух последних публикаций на эту тему: Мысли об альтернативах Hibernate и JPA Hibernate :

Есть проблемы с производительностью, связанные с использованием Hibernate.

Многие бизнес-операции и отчеты включают в себя написание сложных запросов. Писать их в терминах объектов и поддерживать их кажется трудным.

Нам не нужна книга на 900 страниц, чтобы выучить новый фреймворк.

Как разработчики Java, мы легко можем отнести это к следующему: платформы ORM, как правило, дают загадочные сообщения об ошибках, сопоставление сложно сделать, и поведение во время выполнения, а именно с отложенными исключениями при инициализации, может удивлять при первом обнаружении.

Кому не нужно было поддерживать приложение, использующее шаблон Open Session In View , которое генерировало поток запросов SQL, на оптимизацию которых уходили недели?

Я полагаю, что буквально может потребоваться пара лет, чтобы по-настоящему понять Hibernate, много практики и несколько прочтений книги Java Persistence с книгой Hibernate (еще 600 страниц в ее следующем втором издании).

Обоснована ли критика в спящем режиме?

Лично я так не думаю, на самом деле большинство разработчиков действительно критикуют сложность самого подхода объектно-реляционного отображения, а не конкретной реализации его ORM на данном языке.

Это чувство, кажется, приходит и уходит в периодических волнах, возможно, когда новое поколение разработчиков поражает рабочую силу. После нескольких часов и дней попытки сделать то, что кажется, должно быть намного проще, это только естественное чувство.

Дело в том, что существует проблема: почему многие проекты тратят 30% своего времени на разработку слоя персистентности до сих пор?

ORM — Вьетнам компьютерных наук

Проблема в том, что проблема ORM сложна, и хороших решений нет. Любое решение — огромный компромисс.

ORM был известен как 10 лет назад во Вьетнаме в области компьютерных наук, в блоге одного из создателей Stackoverflow Джеффа Этвуда .

Проблемы ORM хорошо известны, и мы не будем подробно их здесь рассматривать, вот краткая сводка Мартина Фаулера о том, почему ORM сложен:

  • идентичность объекта против идентичности базы данных
  • Как отобразить объектно-ориентированное наследование в реляционном мире
  • однонаправленные ассоциации в базе данных против двунаправленных в мире ОО
  • Навигация данных — ленивая загрузка, нетерпеливое извлечение
  • Транзакции с базой данных и отсутствие откатов в мире OO

Это просто назвать основные препятствия. Проблема также в том, что легко забыть то, что мы пытаемся достичь в первую очередь.

У ORM есть только 2 основные цели

У ORM четко определены две основные цели:

  • отображать объекты из мира ОО в таблицы в реляционной базе данных
  • обеспечить механизм времени выполнения для синхронизации графика объектов в памяти и набора таблиц базы данных в синхронизации

Учитывая это, когда мы должны использовать Hibernate и ORM в целом?

Когда ORM имеет смысл?

ORM имеет смысл, когда данный проект выполняется с использованием подхода, основанного на домене , где вся программа построена на основе набора базовых классов, называемых моделью предметной области, которые представляют концепции в реальном мире, такие как Customer , Invoice и т. Д.

Если у проекта нет минимального порогового уровня сложности, для которого требуется DDD, то ORM может быть излишним. Проблема в том, что даже самые простые корпоративные приложения значительно превышают этот порог, поэтому ORM большую часть времени действительно тянет на себя.

Просто ORM сложен в освоении и полон подводных камней. Итак, как мы можем решить эту проблему?

Простое решение проблемы ORM

Кто-то однажды сказал что-то вроде этого:

Умный человек решает проблему, а мудрый избегает ее.

Как это часто бывает в программировании, мы можем найти решение, вернувшись к началу и посмотрев, что мы пытаемся решить:

эскиз

Поэтому мы пытаемся синхронизировать график объектов в памяти с набором таблиц. Но это два совершенно разных типа структур данных!

Но какая структура данных является наиболее общей? Оказывается, что граф является наиболее общим из двух: на самом деле набор связанных таблиц базы данных на самом деле является просто особым типом графа.

То же самое можно сказать практически о любой другой структуре данных.

Графики и их прохождение очень хорошо понятны и обладают накопленным знанием десятилетий, сходным с теорией, на которой построены реляционные базы данных: реляционная алгебра.

Устранение несоответствия импеданса

Логический вывод состоит в том, что решение для несоответствия импеданса ORM устраняется для устранения самого несоответствия:

Давайте сохраним график объектов домена в памяти в транзакционной базе данных графов!

Это решает проблему сопоставления, устраняя необходимость сопоставления в первую очередь.

Готовое решение для проблемы ORM

Это легче сказать, чем сделать, или это так? Оказывается, что графовые базы данных существуют уже много лет, и ярким примером в сообществе Java является Neo4j .

Neo4j — это стабильный и зрелый продукт, который хорошо понят и задокументирован, см. Neo4J в книге действий . Он может использоваться как внешний сервер или во встроенном режиме внутри самого процесса Java.

Но его основной API — это все о графиках и узлах, что-то вроде этого:

1
2
3
4
5
6
7
GraphDatabaseService gds = new EmbeddedGraphDatabase("/path/to/store"); 
Node forrest=gds.createNode(); 
forrest.setProperty("title","Forrest Gump"); 
forrest.setProperty("year",1994); 
gds.index().forNodes("movies").add(forrest,"id",1);
 
Node tom=gds.createNode();

Проблема в том, что это слишком далеко от разработки, управляемой доменом, запись в нее была бы похожа на ручное кодирование JDBC.

Это типичная задача фреймворка, подобного Hibernate, с большой разницей, что из-за несоответствия импеданса минимально, такой фреймворк может работать гораздо более прозрачно и менее навязчиво.

Оказывается, такие рамки уже написаны.

Пружинная поддержка Neo4J

Род Джонсон (Rod Johnson), один из создателей среды Spring, взял на себя задачу реализовать первоначальную версию интеграции Neo4j, проекта Spring Data Neo4j .

Это важный отрывок из предисловия Рода Джонсона в документации, касающейся дизайна каркаса:

Его использование AspectJ для исключения персистентного кода из вашей доменной модели является по-настоящему инновационным и находится на переднем крае современных технологий Java.

Итак, Spring Data Neo4J — это основанная на AOP инфраструктура, которая оборачивает объекты домена относительно прозрачным способом и синхронизирует граф объектов в памяти с хранилищем транзакционных данных Neo4j.

Он нацелен на то, чтобы написать постоянный слой приложения в упрощенном виде, как Spring Data JPA.

Как выглядит отображение в графическую базу данных

Оказывается, требуется ограниченное отображение ( учебник ). Нам нужно отметить, какие классы мы хотим сделать постоянными, и определить поле, которое будет действовать как Id:

1
2
3
4
5
6
7
8
@NodeEntity
class Movie { 
    @GraphId Long nodeId;
    String id;
    String title;
    int year;
    Set cast;
}

Существуют и другие аннотации (еще 5 на документы), например, для определения индексации и связей со свойствами и т. Д. По сравнению с Hibernate для той же доменной модели существует только небольшая часть аннотаций.

Как выглядит язык запросов?

Рекомендуемый язык запросов — Cypher , это язык ASCII, основанный на искусстве. Запрос может выглядеть, например, так:

1
2
3
4
5
6
// returns users who rated a movie based on movie title (movieTitle parameter) higher than rating (rating parameter)
    @Query("start movie=node:Movie(title={0}) " +
           "match (movie)<-[r:RATED]-(user) " +
           "where r.stars > {1} " +
           "return user")
     Iterable getUsersWhoRatedMovieFromTitle(String movieTitle, Integer rating);

Это язык запросов, называемый Cypher, который основан на искусстве ASCII. Язык запросов сильно отличается от JPQL или SQL и подразумевает кривую обучения.

Тем не менее после кривой обучения этот язык позволяет писать производительные запросы, которые обычно могут быть проблематичными в реляционных базах данных.

Производительность запросов в Graph против реляционных баз данных

Давайте сравним некоторые частые типы запросов и то, как они должны работать в графе и реляционных базах данных:

  • поиск по идентификатору: это реализуется, например, путем выполнения бинарного поиска по дереву индекса, поиска соответствия и следования «указателю» на результат. Это (очень) упрощенное описание, но оно, вероятно, идентично для обеих баз данных. Нет очевидной причины, по которой такой запрос занял бы больше времени в графовой базе данных, чем в реляционной БД.
  • поиск родительских отношений: это тип запроса, с которым сталкиваются реляционные базы данных. Самостоятельные объединения могут привести к декартовым произведениям огромных таблиц, что приведет к остановке базы данных. Графовая база данных может выполнять эти запросы в несколько раз.
  • поиск по неиндексированному столбцу: здесь реляционная база данных может сканировать таблицы быстрее из-за физической структуры таблицы и того факта, что одно чтение обычно приводит к нескольким строкам. Но этого типа запросов (табличных сканирований) следует избегать в любом случае в реляционных базах данных.

Здесь можно сказать больше, но нет никаких указаний (нет общедоступных открытых тестов, связанных с DDD), что хранилище данных на основе графиков не подходит для выполнения DDD из-за производительности запросов.

Выводы

Лично я не могу найти никаких (концептуальных) причин, по которым база данных графов с возможностью транзакций не будет идеально подходить для разработки, управляемой доменом, в качестве альтернативы реляционной базе данных и ORM.

Ни одно хранилище данных никогда не подойдет идеально для каждого варианта использования, но мы можем задать вопрос, не должны ли базы данных графов стать по умолчанию для DDD, а для реляционных исключений.

Исчезновение ORM означало бы значительное сокращение сложности и времени, которое требуется для реализации проекта.

Будущее DDD на предприятии

Устранение несоответствия импеданса и повышение производительности некоторых типов запросов могут стать убийственными функциями, которые определяют внедрение решения DDD на основе графов.

Мы видим практические препятствия: операции предпочитают реляционные базы данных, закрытие контрактов с поставщиками, необходимость изучения нового языка запросов, ограниченный опыт работы на рынке труда и т. Д.

Но есть и экономическое преимущество, и технологии. И когда это так, обычно это только вопрос времени.

Как насчет вас, не могли бы вы вспомнить причину, по которой DDD на основе Graph не будет работать? Не стесняйтесь вмешиваться в комментарии ниже.

Ссылка: Решение ORM — держите O, бросьте R, не нужно M от нашего партнера JCG Алексея Новика в блоге The JHades Blog .