通常,在应用程序中放置SQL查询的位置?

时间:2010-11-24 09:32:05

标签: java sql

在应用程序中放置SQL查询的最佳位置是什么?

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

使用StringBuilder追加查询看起来非常混乱。

将它们存储在文件中并在每次发出请求时读取它们 - 看起来都是个坏主意。(但我认为从文件读取可以放在静态块中)

9 个答案:

答案 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)

了解PreparedStatement

在此,您无需存储查询的所有可变部分 比如,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_(数据库)