2010-11-24 2 views
7

Какое место лучше всего разместить в приложении SQL-запросы?В общем, где разместить SQL-запросы в приложении?

Запросов может быть большой и требует форматирования.

Добавление запроса с использованием StringBuilder выглядит очень загроможденным.

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

+0

хранить их как представления и хранимые процедуры в самой базе данных SQL. – onedaywhen 2010-11-24 15:13:31

ответ

12

Держите запрос SQL в файле ресурсов, который вы читаете на константу во время загрузки класса:

private static final String PERSON_QUERY; 

static{ 
    InputStream str = null; 
    try{ 
     str = ThisClass.class.getResourceAsStream("/path/to/query.sql"); 
     PERSON_QUERY = IOUtils.toString(str); 
    }catch(IOException e){ 
     throw new IllegalStateException("Failed to read SQL query", e); 
    }finally{ 
     IOUtils.closeQuitely(str); 
    } 

} 

Таким образом, вы можете использовать свой любимый редактор для редактирования SQL, но вы все равно получите запрос в константа в java.

Если вы сделаете это много, извлечь код на вспомогательный метод:

public static String loadResourceToString(final String path){ 
    final InputStream stream = 
     Thread 
      .currentThread() 
      .getContextClassLoader() 
      .getResourceAsStream(path); 
    try{ 
     return IOUtils.toString(stream); 
    } catch(final IOException e){ 
     throw new IllegalStateException(e); 
    } finally{ 
     IOUtils.closeQuietly(stream); 
    } 
} 

и использовать это в ваших статических блоков:

private static final String PERSON_QUERY; 
private static final String ADDRESS_QUERY; 
private static final String AGE_QUERY; 

static{ 
    PERSON_QUERY = Helper.loadResourceToString("queries/personQuery.sql"); 
    ADDRESS_QUERY = Helper.loadResourceToString("queries/addressQuery.sql"); 
    AGE_QUERY = Helper.loadResourceToString("queries/ageQuery.sql"); 
} 

На мой взгляд, разные языки должны всегда быть разделены. Это ужасная практика для сборки SQL, HTML, XML, JavaScript и т. Д. Из Java-кода. Используйте простые шаблоны или движки шаблонов, такие как Velocity, когда это возможно. Это дает вам много преимуществ, один из которых заключается в том, что вы можете изменить шаблон без перекомпиляции класса java.

PS: Я использую Apache Commons/IO в приведенном выше коде, но это необязательно, просто проще.

2

Читайте о PreparedStatement

.

В этом вам не нужно хранить все переменные части запроса как, insert into table_x values (?,?,?);

и используя statement.setString(1,"hello");, statement.setInt(2,1);, statement.setDouble (3,4.555);

и, наконец, statement.execute(); вы можете вставить значения.

PS: Рекомендуется хранить подготовленные строки инструкций в файле свойств.

+2

проблема заключается в том, где разместить запрос «insert into table_x ...». Особенно, когда запрос, скажем, 20 строк. – HanuAthena 2010-11-24 09:42:59

1

Да, это хорошо. И файл свойств не будет плохой идеей. Но иногда нам нужно строить запросы «на лету», потому что подход StringBuiler в порядке.

+1

@Ansari: Файл SQL при форматировании может перейти к некоторым 20-30 строкам, которые я хочу сохранить для удобства чтения. Таким образом, размещение их в файле свойств может не помочь. – HanuAthena 2010-11-24 09:40:24

+0

@ ХануАтена: Почему бы и нет? Мы можем написать значение свойства на нескольких строках. И я не думаю, что это испортило бы читаемость. В любом случае вы будете делать то же самое в коде, чтобы избежать горизонтальной прокрутки. – 2010-11-24 09:42:47

1

Вы можете поместить их в файл .properties. Используя Apache Commons для конфигурации, вы можете avoid reading files every time.

Если вы решите пойти с этим маршрутом, вы можете помочь readibility разбивая один запрос в несколько рядов с помощью обратной косой черты:

myLongQuery: select col1, col2, col3, col4 from \ 
      table1 where \ 
      col1 = 'something' 
0

В моем случае, у меня есть Specific DAO, где все мои SQL запросы «зарегистрирован» в блоке static final.

Пример:

public class MySQLUserDAO extends UserDAO { 

    private static final String SQL_COUNT = "SELECT COUNT(1) AS TOTAL FROM USER"; 
// private static final String SQL_CREATE = "INSERT INTO USER(FIRST_NAME, MIDDLE_NAME, LAST_NAME, EMAIL_ADDRESS, DOB) VALUES (?, ?, ?, ?, ?)"; 
    private static final String SQL_DELETE = "DELETE FROM USER WHERE USER_ID = ?"; 
    private static final String SQL_RETRIEVE = "SELECT * FROM USER WHERE USER_ID = ?"; 
    private static final String SQL_UPDATE = "UPDATE USER SET FIRST_NAME = ?, MIDDLE_NAME = ?, LAST_NAME = ?, GENDER = ?, EMAIL_ADDRESS = ?, DOB = ? WHERE USER_ID = ?"; 
    private static final String SQL_FIND_EMAIL = "SELECT * FROM USER WHERE EMAIL_ADDRESS = ?"; 
    private static final String SQL_FIND_FIRST_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(FIRST_NAME))) = LOWER(?)"; 
    private static final String SQL_FIND_FIRST_NAME_LIKE = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(FIRST_NAME))) LIKE ?"; 
    private static final String SQL_FIND_LAST_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(LAST_NAME))) = LOWER(?)"; 
    private static final String SQL_FIND_LAST_NAME_LIKE = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(LAST_NAME))) LIKE ?"; 
    private static final String SQL_FIND_BY_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(CONCAT_WS(' ', FIRST_NAME, LAST_NAME)))) LIKE ?"; 

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

Пример:

/* (non-Javadoc) 
    * @see net.imatri.dao.JdbcDAO#create(java.lang.Object) 
    */ 
    @Override 
    public boolean create(UserEntity user) throws DAOException { 
     // TODO Auto-generated method stub 
     PreparedStatement ps = null; 
     ResultSet generatedKeyResultSet = null; 
     boolean created = false; 

     String SQL_CREATE = "INSERT INTO USER(FIRST_NAME, MIDDLE_NAME, LAST_NAME, EMAIL_ADDRESS"; 
     String sqlValues = "(?, ?, ?, ?"; 

     if (user.getGender() != null) { 
      SQL_CREATE += ", GENDER"; 
      sqlValues += ", ?"; 
     } 

     if (user.getBirthDate() != null) { 
      SQL_CREATE += ", DOB"; 
      sqlValues += ", ?"; 
     } 

     SQL_CREATE += ") VALUES " + sqlValues + ")"; 

     try { 
      ps = getConnection().prepareStatement(SQL_CREATE, Statement.RETURN_GENERATED_KEYS); 
      ps.setString(1, user.getFirstName()); 
      ps.setString(2, user.getMiddleName()); 
      ps.setString(3, user.getLastName()); 

      int pos = 4; 
      if (user.getGender() != null) { 
       ps.setString(pos++, user.getGender().toString()); 
      } 

      ps.setString(pos++, user.getEmailAddress()); 

      if (user.getBirthDate() != null) 
       ps.setDate(pos++, new Date(user.getBirthDate().getTime())); 

      ps.executeUpdate(); 
      generatedKeyResultSet = ps.getGeneratedKeys(); 
      if (generatedKeyResultSet != null && generatedKeyResultSet.next()) { 
       user.setId(generatedKeyResultSet.getLong(1)); 
      } 
      created = true; 
     } catch (SQLException e) { 
      // TODO Auto-generated catch block 
      throw new DAOException(e); 
     } finally { 
      try { 
       close(generatedKeyResultSet, ps); 
      } catch (SQLException e) { 
       // TODO Auto-generated catch block 
       logger.error("Error closing statement or resultset.", e); 
      } 
     } 

     return created; 
    } 

Ваш подход не плохо. Мы только что использовали DAO, содержащий SQL, в блоке static final.

Если ваш SQL может вырастить много строк, вы можете использовать StringBuilder (без синхронизации) или StringBuffer (с синхронизацией) для манипуляции строками.

0

Раньше я помещал их в специальный файл свойств, который был упакован в мою банку. Затем я извлек его с помощью Properties.load(getClass().getResourceAsStream("queries.properties")) и использовал подготовленный оператор.

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

Я считаю, что использование JPA является «правильным» решением для крупных проектов.Если вы разрабатываете инструмент сопоставления меньшего проекта, например iBatis, который позволяет писать запросы в виде аннотаций.

0

Статические запросы - те, которые зависят только от параметров связывания - идеально вписываются в *DAO классов, абстрактный доступ к БД прочь - вы имеете дело только с DAO API как loadUser(int userId) или saveUser(User user). Таким образом, как запросы хранятся в DAO, это не большой вопрос, делайте по своему усмотрению.
Обычно я не использую динамические запросы, поэтому я не могу дать им хороший совет.

2

Я лично склоняюсь к размещению этих запросов в файле XML; файл свойств - кошмар для сложных запросов (не забывайте \ после каждой строки запроса). И пока вы на нем, почему бы просто не использовать простую инфраструктуру DAO, например, iBatis (now MyBatis), что приятно использовать для простых и сложных проектов. :-)

0

Одна вещь, которую вы, возможно, захотите изучить, - это хранимые процедуры или представления. Я не уверен, какой тип базы данных вы используете, но в MS SQL и MySQL оба варианта являются опцией. Они предлагают не только место для хранения ваших длинных запросов, но так как вы передаете переменные, а не просто выполняете запрос, это также снова защищает страшный dun dun dunnnnnnn SQL-инъекция. Теперь я также не знаю, насколько сложна ваша заявка, но, как правило, я использую решение, в котором мои запросы хранятся в конце базы данных, а не в приложении.

Немного чтения: (. Вика статья да, но есть хорошие ссылки внизу) http://en.wikipedia.org/wiki/Stored_procedure http://en.wikipedia.org/wiki/View_(database)

Смежные вопросы