在應用程序中放置SQL查詢的最佳位置是什麼?通常,將SQL查詢放在應用程序中的位置?
查詢可能很大,需要格式化。
使用StringBuilder追加查詢看起來非常混亂。
存放於文件和讀取它們當一個請求時每次都 - 看起來像一個壞主意(但我認爲,從文件中讀取數據可以放在一個靜態塊)
在應用程序中放置SQL查詢的最佳位置是什麼?通常,將SQL查詢放在應用程序中的位置?
查詢可能很大,需要格式化。
使用StringBuilder追加查詢看起來非常混亂。
存放於文件和讀取它們當一個請求時每次都 - 看起來像一個壞主意(但我認爲,從文件中讀取數據可以放在一個靜態塊)
保存在資源文件中的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,但沒有必要,只是更容易。
在這個你不必存儲查詢 像所有的可變部分,insert into table_x values (?,?,?);
,並使用statement.setString(1,"hello");
,statement.setInt(2,1);
,statement.setDouble (3,4.555);
,最後是statement.execute();
您可以插入值..
PS:建議將預備的語句字符串存儲在屬性文件中。
問題是在哪裏放置「insert into table_x ...」查詢。特別是當查詢是20行時。 – HanuAthena 2010-11-24 09:42:59
是的,它很好。屬性文件不是一個壞主意。但有時我們需要動態構建查詢,因爲這種方法很好。
@Ansari:格式化時的SQL文件可能會變成20-30行,爲了便於閱讀,我想保留這些行。所以,把它們放在屬性文件中可能沒有幫助。 – HanuAthena 2010-11-24 09:40:24
@HanuAthena:爲什麼不呢?我們可以在多行中寫入屬性的值。我不認爲這會破壞可讀性。無論如何,你會在代碼中做同樣的事情,以避免水平滾動。 – 2010-11-24 09:42:47
您可以將它們放在.properties文件中。使用Apache Commons進行配置,您可以avoid reading files every time。
如果你選擇去這條路線,你可以打破一個查詢分成多個行用反斜槓幫助可讀性:
myLongQuery: select col1, col2, col3, col4 from \
table1 where \
col1 = 'something'
在我的情況,我有一個具體的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(帶同步)進行字符串操作。
我曾經把它們放入裝入我的jar的特殊屬性文件中。然後我使用Properties.load(getClass().getResourceAsStream("queries.properties"))
提取它並使用了準備好的語句。
但是自從我上次使用這種技術以來,現在我認爲除非您有嚴肅的理由這麼做,否則極不推薦。
我認爲JPA的使用是大項目的「正確」解決方案。如果您正在開發像iBatis這樣的小型項目使用映射工具,它允許您將查詢編寫爲註釋。
靜態查詢 - 那些它們只依賴於綁定參數 - 是完全適合在*DAO
類,抽象DB訪問了 - 你只處理DAO API像loadUser(int userId)
或saveUser(User user)
。這樣,查詢如何存儲在DAO中並不是一個大問題,只要你喜歡就行。
我通常不會使用動態查詢,所以我無法給出有關它們的好建議。
我個人傾向於將這些查詢放在一個XML文件中;屬性文件是複雜查詢的噩夢(不要忘記每行查詢後的\
)。當你在這裏時,爲什麼不使用簡單的DAO框架,如iBatis (now MyBatis)這是一個很高興用於簡單和複雜的項目。 :-)
您可能想要考慮的一件事是存儲過程或視圖。我不確定你正在使用什麼類型的數據庫,但是在MS SQL和MySQL中這兩種都是一種選擇。它們不僅提供了一個存放長查詢的地方,而且由於您傳遞的是變量而不僅僅是執行查詢,所以它還可以再次保護SQL注入的可怕內容。現在,我也不知道你的應用程序有多複雜,但總的來說,我傾向於使用一種解決方案,將查詢存儲在數據庫端而不是某個應用程序中。
的閱讀了一下:(維基文章是的,但也有在底部很好的參考) http://en.wikipedia.org/wiki/Stored_procedure http://en.wikipedia.org/wiki/View_(database)
並將其作爲意見和存儲過程本身。 – onedaywhen 2010-11-24 15:13:31