Стратегии инкапсуляции различий между платформами SQL?

Я работаю над ускорением некоторых медленных операций здесь, и один случай - это дерево родительско-дочерних отношений в таблице. В настоящее время система работает на SQLServer, и после некоторых исследований я обнаружил, что с помощью общих табличных выражений несколько запросов могут быть сжаты в один.

Пока все хорошо, но синтаксис специфичен для SQLServer и для того же действия с (например) Oracle потребуется совершенно другой синтаксис.

Что можно сделать, чтобы структурировать систему для будущей адаптации к другим СУБД? Мои главные проблемы здесь:

  • Операторы SQL замусорены над кодом, и то, что может быть возможно с одним оператором в одной СУБД, может потребовать нескольких операторов в другом, поэтому даже логика программы может потребовать изменений.
  • Даже вещи, которые можно красиво инкапсулировать в одну СУБД (я использую хранимые функции, чтобы скрыть некоторые сложности в БД), имеют незначительную разницу по разным БД или вообще не доступны.

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

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

РЕДАКТИРОВАТЬ: Язык реализации Java, просто использование ORM (как, например, Hibernate) не то, что я ищу (более или менее потребуется переписать ~50% базы кода).

РЕДАКТИРОВАТЬ 2: Я в основном ищу возможности выдвинуть детали в базу данных как можно более совместимым способом, в идеале я бы хотел, чтобы SQL, используемый java-частью, был одинаковым для всех платформ (или требует только очень небольшие изменения из-за синтаксических различий). Для примера с CTE, который я привел, я в настоящее время перенес это в хранимую функцию в надежде, что когда она будет перенесена, функциональность также может быть воспроизведена в функции.

EDIT3: В настоящее время у меня нет острой необходимости поддерживать другие RDBM. Никто не будет обвинять меня, если он работает только с SQLServer. Но, где это возможно, я бы хотел избежать привязки кода Java больше, чем это необходимо, к конкретному поставщику БД.

РЕДАКТИРОВАТЬ 4: Некоторый фон - текущая работа добавляет функциональность к системе - функциональность, для которой она не была разработана или запланирована. Требования поступают постепенно от бизнес-парней, и трудно планировать заранее. Хотя каждое требование само по себе не очень сложно решить, я боюсь, что мы накопим большой беспорядок с тегами, которые невозможно перенести, не пройдя детально каждый запрос. Поскольку сам SQLServer также вносил различные несовместимости с каждым новым основным выпуском, я обеспокоен тем, что даже переход на более новый SQLServer может стать серьезным препятствием в будущем (в прошлом мы выполнили одно такое обновление с 2005 по 2008 год) гладко для материала, который я поддерживаю, но это вызвало уже ряд проблем для одного из наших поставщиков).

4 ответа

Решение
  1. Держитесь подальше от ОРМ. У них есть свое предназначение в проектах, но если вам нужна только независимость от базы данных, это все равно что покупать фабрику General Factory, когда все, что вам нужно, это молоток и гвозди.

  2. Изолируйте свой уровень доступа к данным за набором интерфейсов. Ничто в вашей кодовой базе не должно зависеть от базы данных, кроме реализации этих интерфейсов. Так что ни один класс за пределами этой реализации не должен иметь доступ к чему-либо с 'jdbc', 'jpa' или 'hibernate' в пути к классам. Вы можете написать реальные тесты для этого, используя JDepend или DependencyFinder.

  3. Сделайте так, чтобы ваш текущий доступ к базе данных реализовывал эти интерфейсы.

  4. Имейте автоматизированные тесты, которые работают с фактической базой данных и которые готовы работать с набором различных баз данных.

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

  6. Теперь посмотрите на ваши две реализации и найдите материал, который стоит извлечь, и соответствующим образом реорганизуйте ваш код.

Если вы попытаетесь продвинуть шаг 6 вверх по цепочке, вы столкнетесь с плохими случаями YAGNI и WrongAbstractionException.

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

Вы можете изучить использование инфраструктуры MyBatis. MyBatis отображает запросы SQL и результирующие наборы в объекты Java, а также имеет некоторую поддержку для генерации запросов к базе данных. Внедрение в существующее приложение чрезвычайно легкое и, вероятно, значительно сократит количество строк кода. Взгляните на раздел о поддержке поставщиков Multi-db в их документации для быстрого примера файла сопоставления, который демонстрирует функциональность.

Поскольку вы находитесь в унаследованном коде, я бы выбрал один интерфейс и первоначально одну реализацию, добавив к нему методы для каждой связанной с БД функции, например SaveOrder, которая принимает простой класс, например, Order:), или одним нажатием на Набор данных. По мере продвижения добавляйте больше материала, пока весь ваш sql не будет в реализации, и все, что его использует, проходит через интерфейс.

Затем, когда вы захотите внедрить его в другой БД или, возможно, в другие уровни персистентности, такие как NoSQL, Xml или ORM, вы знаете, что вам нужно сделать, и у вас также есть батарея модульных тестов.

Вы не указали, какую платформу приложений вы используете. Но я бы предположил, что вы используете.Net или Java. Ваша первая линия защиты состоит в том, чтобы использовать escape-последовательности ODBC или JDBC в инструкциях SQL в вашем приложении и помещать в хранимые процессы все, что не может быть обработано этим подходом.

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

РЕДАКТИРОВАТЬ: я вижу, что вы отредактировали свой вопрос, пояснив, что вы используете Java и переход на библиотеку ORM не вариант. Некоторое время назад я работал над разработкой продукта для крупных корпоративных клиентов. У каждого из клиентов была своя стандартная эталонная архитектура, поэтому нам пришлось использовать Oracle, Microsoft SQL Server и UDB/DB2. Мы могли бы поддерживать другие платформы, но некоторые детали становятся нечеткими через 10-12 лет. Мы добились этого, неукоснительно используя JDBC-экранирование для временных констант, функций и хранимых вызовов процедуры, а также внедряя более чувствительные операции в хранимые процедуры, специфичные для базы данных. Поэтому я могу сказать вам, основываясь на моем опыте, что этот подход работает. В настоящее время, благодаря цепочке приобретений, этот продукт является частью портфеля промежуточного программного обеспечения Oracle, поэтому я даже не знаю, поддерживает ли он что-нибудь еще, кроме Oracle.

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

Поэтому я советую оценить, присуще ли вашему приложению использование специфических функций SQL Server. Если это так, вы должны просто согласиться с тем, что переход на другую платформу невозможен без серьезного переписывания. Хотя, если это не так, посмотрите, можете ли вы улучшить свою работу с SQL, используя JDBC escape-коды.

Другие вопросы по тегам