在应用程序中放置SQL查询的最佳位置是什么?
查询可能很大,需要格式化。
使用StringBuilder追加查询看起来非常混乱。
将它们存储在文件中并在每次发出请求时读取它们 - 看起来都是个坏主意。(但我认为从文件读取可以放在静态块中)
答案 0 :(得分:16)
将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,但它没有必要,只是更容易。
答案 1 :(得分:2)
在此,您无需存储查询的所有可变部分
比如,insert into table_x values (?,?,?);
并使用statement.setString(1,"hello");
,statement.setInt(2,1);
,statement.setDouble (3,4.555);
最后statement.execute();
您可以插入值..
PS:建议将准备好的语句字符串存储在属性文件中。
答案 2 :(得分:2)
我个人倾向于将这些查询放在XML文件中;属性文件是复杂查询的噩梦(不要忘记每行查询后的\
)。当你在这里时,为什么不使用像iBatis (now MyBatis)这样的简单DAO框架,这对于简单和复杂的项目都很有用。 : - )
答案 3 :(得分:1)
StringBuiler
方法很好。
答案 4 :(得分:1)
您可以将它们放在.properties文件中。使用Apache Commons进行配置,您可以avoid reading files every time。
如果您选择使用此路线,则可以通过使用反斜杠将一个查询拆分为更多行来提高可读性:
myLongQuery: select col1, col2, col3, col4 from \
table1 where \
col1 = 'something'
答案 5 :(得分: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;
}
你的方法还不错。我们刚刚习惯让DAO在static final
块中包含SQL。
如果你的SQL可以增长很多行,你可以使用StringBuilder(没有同步)或StringBuffer(同步)进行字符串操作。
答案 6 :(得分:0)
我曾经将它们放入装在我的jar中的特殊属性文件中。然后我使用Properties.load(getClass().getResourceAsStream("queries.properties"))
提取它并使用预备语句。
但是自从我上次使用这种技术以来已经过去了很多年,现在我认为除非你有充分的理由这样做,否则不建议这样做。
我认为使用JPA对于大型项目来说是一个“正确”的解决方案。如果您正在开发像iBatis这样的小型项目使用映射工具,它允许您将查询编写为注释。
答案 7 :(得分:0)
静态查询 - 仅依赖于绑定参数的查询 - 完全适合*DAO
类,它抽象数据库访问 - 您只处理DAO API,如loadUser(int userId)
或{ {1}}。这样查询存储在DAO中的方式不是一个大问题,可以随意使用
我通常不使用动态查询,所以我无法给出好的建议。
答案 8 :(得分:0)
您可能想要研究的一件事是存储过程或视图。我不确定你使用的是什么类型的数据库,但在MS SQL和MySQL中这两种都是一种选择。它们不仅提供存储长查询的位置,而且由于您传入变量而不仅仅是执行查询,因此这也会再次保护可怕的 dun dun dunnnnnnn SQL注入。现在,我也不知道您的应用程序有多复杂,但总的来说,我倾向于使用一种解决方案,其中我的查询存储在数据库端,而不是存储在某个应用程序中。
一点阅读:(维基文章是的,但底部有很好的参考。) http://en.wikipedia.org/wiki/Stored_procedure http://en.wikipedia.org/wiki/View_(数据库)