2010-11-24 32 views
7

在應用程序中放置SQL查詢的最佳位置是什麼?通常,將SQL查詢放在應用程序中的位置?

查詢可能很大,需要格式化。

使用StringBuilder追加查詢看起來非常混亂。

存放於文件和讀取它們當一個請求時每次都 - 看起來像一個壞主意(但我認爲,從文件中讀取數據可以放在一個靜態塊)

+0

並將其作爲意見和存儲過程本身。 – 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"); 
} 

在我看來,不同的語言應始終分離。從Java代碼中組裝SQL,HTML,XML,JavaScript等是非常糟糕的做法。儘可能使用普通模板或模板引擎,如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

是的,它很好。屬性文件不是一個壞主意。但有時我們需要動態構建查詢,因爲這種方法很好。

+1

@Ansari:格式化時的SQL文件可能會變成20-30行,爲了便於閱讀,我想保留這些行。所以,把它們放在屬性文件中可能沒有幫助。 – HanuAthena 2010-11-24 09:40:24

+0

@HanuAthena:爲什麼不呢?我們可以在多行中寫入屬性的值。我不認爲這會破壞可讀性。無論如何,你會在代碼中做同樣的事情,以避免水平滾動。 – 2010-11-24 09:42:47

1

您可以將它們放在.properties文件中。使用Apache Commons進行配置,您可以avoid reading files every time

如果你選擇去這條路線,你可以打破一個查詢分成多個行用反斜槓幫助可讀性:

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

在我的情況,我有一個具體的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; 
    } 

你的做法是不壞。我們剛剛習慣於在static final塊中擁有DAO包含的SQL。

如果您的SQL可以增長很多行,則可以使用StringBuilder(不帶同步)或StringBuffer(帶同步)進行字符串操作。

0

我曾經把它們放入裝入我的jar的特殊屬性文件中。然後我使用Properties.load(getClass().getResourceAsStream("queries.properties"))提取它並使用了準備好的語句。

但是自從我上次使用這種技術以來,現在我認爲除非您有嚴肅的理由這麼做,否則極不推薦。

我認爲JPA的使用是大項目的「正確」解決方案。如果您正在開發像iBatis這樣的小型項目使用映射工具,它允許您將查詢編寫爲註釋。

0

靜態查詢 - 那些它們只依賴於綁定參數 - 是完全適合在*DAO類,抽象DB訪問了 - 你只處理DAO API像loadUser(int userId)saveUser(User user)。這樣,查詢如何存儲在DAO中並不是一個大問題,只要你喜歡就行。
我通常不會使用動態查詢,所以我無法給出有關它們的好建議。

2

我個人傾向於將這些查詢放在一個XML文件中;屬性文件是複雜查詢的噩夢(不要忘記每行查詢後的\)。當你在這裏時,爲什麼不使用簡單的DAO框架,如iBatis (now MyBatis)這是一個很高興用於簡單和複雜的項目。 :-)

0

您可能想要考慮的一件事是存儲過程或視圖。我不確定你正在使用什麼類型的數據庫,但是在MS SQL和MySQL中這兩種都是一種選擇。它們不僅提供了一個存放長查詢的地方,而且由於您傳遞的是變量而不僅僅是執行查詢,所以它還可以再次保護SQL注入的可怕內容。現在,我也不知道你的應用程序有多複雜,但總的來說,我傾向於使用一種解決方案,將查詢存儲在數據庫端而不是某個應用程序中。

的閱讀了一下:(維基文章是的,但也有在底部很好的參考) http://en.wikipedia.org/wiki/Stored_procedure http://en.wikipedia.org/wiki/View_(database)

相關問題