Код SQL внутри классов Java - PullRequest
11 голосов
/ 07 мая 2009

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

В настоящее время наши запросы хранятся в виде строковых констант внутри самих классов обслуживания и передаются в утилиту, которая будет выполняться SimpleJdbcTemplate. Мы находимся в тупике, где читаемость должна быть сбалансирована с ремонтопригодностью. Код SQL внутри самого класса более удобен в обслуживании, поскольку он находится в коде, который его использует. С другой стороны, если мы храним эти запросы во внешнем файле (плоский или XML), сам SQL будет более читабельным по сравнению с синтаксисом экранированной строки Java.

Кто-нибудь сталкивался с подобной проблемой? Что такое хороший баланс? Где вы храните свой собственный SQL в своем проекте?

Пример запроса следующий:

private static final String FIND_ALL_BY_CHEAPEST_AND_PRODUCT_IDS = 
"    FROM PRODUCT_SKU T \n" +
"    JOIN \n" +
"    ( \n" +
"        SELECT S.PRODUCT_ID, \n" +
"               MIN(S.ID) as minimum_id_for_price \n" +
"          FROM PRODUCT_SKU S \n" +
"         WHERE S.PRODUCT_ID IN (:productIds) \n" +
"      GROUP BY S.PRODUCT_ID, S.SALE_PRICE \n" +
"    ) FI ON (FI.PRODUCT_ID = T.PRODUCT_ID AND FI.minimum_id_for_price = T.ID) \n" +
"    JOIN \n" +
"    ( \n" +
"        SELECT S.PRODUCT_ID, \n" +
"               MIN(S.SALE_PRICE) as minimum_price_for_product \n" +
"          FROM PRODUCT_SKU S \n" +
"         WHERE S.PRODUCT_ID IN (:productIds) \n" +
"      GROUP BY S.PRODUCT_ID \n" +
"    ) FP ON (FP.PRODUCT_ID = T.PRODUCT_ID AND FP.minimum_price_for_product = T.sale_price) \n" +
"WHERE T.PRODUCT_ID IN (:productIds)";

Вот так это будет выглядеть в плоском файле SQL:

--namedQuery: FIND_ALL_BY_CHEAPEST_AND_PRODUCT_IDS
FROM PRODUCT_SKU T 
JOIN 
( 
    SELECT S.PRODUCT_ID, 
           MIN(S.ID) as minimum_id_for_price 
      FROM PRODUCT_SKU S 
     WHERE S.PRODUCT_ID IN (:productIds) 
  GROUP BY S.PRODUCT_ID, S.SALE_PRICE 
) FI ON (FI.PRODUCT_ID = T.PRODUCT_ID AND FI.minimum_id_for_price = T.ID) 
JOIN 
( 
    SELECT S.PRODUCT_ID, 
           MIN(S.SALE_PRICE) as minimum_price_for_product 
      FROM PRODUCT_SKU S 
     WHERE S.PRODUCT_ID IN (:productIds) 
  GROUP BY S.PRODUCT_ID 
) FP ON (FP.PRODUCT_ID = T.PRODUCT_ID AND FP.minimum_price_for_product = T.sale_price) 
WHERE T.PRODUCT_ID IN (:productIds)

Ответы [ 16 ]

0 голосов
/ 07 мая 2009

У нас был проект, в котором мы использовали именно тот подход, который вы используете, за исключением того, что мы выводили каждый запрос в отдельный текстовый файл. Каждый файл считывался (один раз) с использованием среды ResourceLoader Spring, и приложение работало через такой интерфейс:

public interface SqlResourceLoader {
    String loadSql(String resourcePath);
}

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

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

0 голосов
/ 07 мая 2009

Мы храним весь наш SQL в классе как набор статических финальных строк. Для удобства чтения мы разбили его на несколько строк, объединенных с помощью +. Кроме того, я не уверен, если вам нужно что-либо избежать - «Строки» заключены в одинарные кавычки в sql.

0 голосов
/ 07 мая 2009

Один из вариантов - использовать iBatis . Он довольно легкий по сравнению с полнофункциональным ORM, таким как Hibernate, но предоставляет средства для хранения ваших запросов SQL вне ваших файлов .java

0 голосов
/ 07 мая 2009

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

0 голосов
/ 07 мая 2009

Почему бы не использовать хранимые процедуры вместо запросов жесткого кодирования? Хранимые Procs повысят удобство сопровождения и обеспечат большую безопасность для таких вещей, как атаки Interjection SQL.

0 голосов
/ 07 мая 2009

Я полагаю, что сохранение запроса во внешнем файле, а затем его чтение приложением при необходимости представляет ОГРОМНУЮ дыру в безопасности.

Что произойдет, если злой вдохновитель получит доступ к этому файлу и изменит ваш запрос?

Например, изменения

 select a from A_TABLE;

К

 drop table A_TABLE;

ИЛИ

 update T_ACCOUNT set amount = 1000000

Кроме того, он добавляет сложность необходимости поддерживать две вещи: код Java и файлы запросов SQL.

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

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

Например

public String findCheapest (String tableName){

      //returns query.
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...