Какова наилучшая практика для вложения PreparedStatements? - PullRequest
1 голос
/ 23 декабря 2008

У меня есть несколько случаев, когда раздел устаревших операторов SQL основан на зависимости. например.

if (x !=null)
{
  SQL = "SELECT z WHERE x > y";
}
else
{
  SQL = "SELECT z WHERE x <= y";
} 

SQL2 = SQL + " JOIN a ON b";

Я создаю PreparedStatements из этого устаревшего кода. Какова лучшая практика здесь. Должен ли я создать PreparedStatement для переменной SQL и вложить его в SQL2, должно ли быть несколько PreparedStatement на основе SQL2 без вложенности, или что-то совсем другое?

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

РЕДАКТИРОВАТЬ: дизайн проекта требует использования PreparedStatements, у меня нет выбора использования библиотек в данный момент.

Ответы [ 6 ]

4 голосов
/ 23 декабря 2008

> Должен ли я создать PreparedStatement для переменной SQL и вложить его в SQL2

нет

Нет

> Или должно быть несколько PreparedStatements на основе SQL2 без вложенности

Да

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

Неважно, похоже, что вы повторяете строки, смысл в том, чтобы максимально упростить операторы.

Я бы сделал что-то вроде этого:

final class DatabaseQueries {
    public final static String SOME_SCENARIO       = "SELECT z WHERE x > y JOIN A, B ";
    public final static String SOME_OTHER_SCENARIO = "SELECT z WHERE x <= y JOIN A, B";
 }

А затем используйте его из своего класса:

 PreparedStatement pstmt = getCon().prepareStatement( getQuery() );


 private String getQuery() { 
     if( x != null ) { 
          return DatabaseQueries.SOME_SCENARIO;
     } else { 
           return DatabaseQueries.SOME_OTHER_SCENARIO;
     }
 }

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

final class DataBaseQueries { 
    // this one is private
    private final static String JOIN_A_B = " join A, B ";
    public final static String SOME_SCENARIO       = "SELECT z WHERE x > y " + JOIN_A_B ;
    public final static String SOME_OTHER_SCENARIO = "SELECT z WHERE x <= y " + JOIN_A_B ;

}

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

Если запросов слишком много, вы можете заменить их, чтобы загрузить их из ResourceBundle, как в этом вопросе

Надеюсь, это поможет.

2 голосов
/ 23 декабря 2008

Ибатис очень хорош в этом.

<select id="queryName" parameterClass="com.blah.X"><!<[CDATA[
  SELECT z
  FROM a
  JOIN b ON a.id = b.foreign_key
  WHERE

  <isNotNull property="value">
    x > y
  </isNotNull>

  <isNull property="value">
    x <= y
  </isNull>

]]></select>

Это лишь малая часть того, что может сделать Ибатис, но он очень легкий. Отличная технология.

1 голос
/ 23 декабря 2008

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

Вы можете использовать некоторую библиотеку для построения частей SQL-запроса. Я работал над такой библиотекой в ​​PHP под названием Zend_Db_Select.

edit: Я немного погуглил для похожей библиотеки для Java, и нашел эту опцию, которая может быть полезна:

  • Squiggle - небольшая библиотека Java для динамического создания операторов SQL SELECT. [Его] сладкое место предназначено для приложений, которым необходимо создавать сложные запросы с критериями, которые меняются во время выполнения. Обычно бывает довольно сложно понять, как построить эту строку. Squiggle снимает большую часть этой боли.

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

Поиск в Google для "java query builder" нашел ряд других опций, но некоторые не были бесплатными. Некоторые были визуальными построителями запросов, а не программными построителями запросов.

Другой вариант - использовать сложную инфраструктуру объектно-реляционного отображения, такую ​​как Hibernate, но это кажется излишним для вашей текущей задачи.

0 голосов
/ 23 декабря 2008

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

SELECT z WHERE (? IS NOT NULL AND x > y) OR (? IS NULL AND x <= y)

и затем связать соответствующее значение для параметра.

Не уверен, что это лучший подход ... люди могут найти код менее понятным. Но это возможно.

0 голосов
/ 23 декабря 2008

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

Я бы по крайней мере нарушил логику и sql в качестве первого прохода. То, что вы не хотите, это что-то вроде этого:

String sql = "yadda yadda yadda ? yadda yadda WHERE ";
if (mystery condition 1){
   sql = sql + " page=?"
}
else if (mystery condition 2)
{
 sql = sql + " ORDER BY ? "
}

Через некоторое время вы не сможете сказать, какие операторы строятся. Не стоит экономить на дублировании исходного sql. Это может быть легче понять как:

private static final String FIND_PAGE_QUERY = "...."
private static final String ORDER_BY_QUERY =" ..."

if (mystery condition 1){
   return process(FIND_PAGE_QUERY, parameters);
}
else if (mystery condition 2)
{
  return process(ORDER_BY_QUERY, parameters);
}

, а затем просто создайте что-то, чтобы обернуть ваши запросы и переданные параметры, например Spring JDBC Row Mappers или что-то подобное. Это все еще уродливо, но это легко сделать в качестве дополнительного шага по сравнению с тем, что у вас есть, и, по крайней мере, устранит некоторую путаницу при создании запросов.

0 голосов
/ 23 декабря 2008

Вот похожий вопрос

Краткий ответ - лучшего пути нет. Вы можете получить что-то вроде этого:

String selectQuery =
  (new SelectQuery())
  .addColumns(t1Col1, t1Col2, t2Col1)
  .addJoin(SelectQuery.JoinType.INNER_JOIN, joinOfT1AndT2)
  .addOrderings(t1Col1)
  .validate().toString();

Но мне это еще хуже.

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