在 JDBC 中编写 SQL 查询的最佳实践是啥

Posted

技术标签:

【中文标题】在 JDBC 中编写 SQL 查询的最佳实践是啥【英文标题】:What is the Best practice for writing a SQL query in JDBC在 JDBC 中编写 SQL 查询的最佳实践是什么 【发布时间】:2014-01-18 16:39:59 【问题描述】:

我正在通过使用 JSP 和带有 JDBC 的 Servlet 开发电子商务 Web 应用程序来学习 Java Web 开发。我正在检查 GitHub、GoogleCode 等中的一些项目,并且遇到了一些我发现不寻常的代码,例如在如下界面中声明 select、update、insert 方法:

public interface DBDriver 

public void init( DBConnection connection );
public ResultSet selectQuery( String sqlStatement );
public int updateQuery( String sqlStatement );
public ResultSet select( String table, String[] fields, String where );
public ResultSet select( String table, String[] fields, String where, String[] join, String groupBy[], String having, String orderBy[], int start, int limit );
public int insert( String table, HashMap<String, String> fields );
public int update( String table, HashMap<String, String> fields, String where );
public int update( String table, HashMap<String, String> fields, String where, String orderBy[], int start, int limit );
public int delete( String table, String where );
public int delete( String table, String where, String orderBy[], int start, int limit );
public DBConnection getConnection();

并在另一个类中实现这些方法,例如:DBDriverSQL。

实现的方法之一是:

public ResultSet select( String table, String[] fields, String where, String[] join, String groupBy[], String having, String orderBy[], int start, int limit ) 
    StringBuilder sql = new StringBuilder();

    /* Make sure a table is specified */
    if( table == null ) 
        throw new RuntimeException();
    

    sql.append( "SELECT " );

    /* Empty field list means we'll select all fields */
    if( fields == null || fields.length < 1 ) 
        sql.append( "*" );
    
    else 
        sql.append( Util.joinArray( fields, "," ) );
    

    /* Add table and fields list to query */
    sql.append( " FROM " ).append( getFullTableName( table ) );

    /* Any JOINs?*/
    if( join != null && join.length > 0 ) 
        sql.append( " " ).append( Util.joinArray( join, " " ) );
    

    /* Searching on a WHERE condition? */
    if( where != null && !where.isEmpty() ) 
        sql.append( " WHERE " ).append( where );
            

    /* Add GROUP BY clause */
    if( groupBy != null && groupBy.length > 0 ) 
        sql.append( Util.joinArray( groupBy, "," ) );
    

    if( having != null && !having.isEmpty() ) 
        sql.append( " HAVING " ).append( having );
    

    if( orderBy != null && orderBy.length > 0 ) 
        sql.append( " ORDER BY " ).append( Util.joinArray( orderBy, "," ) );
    

    if( limit > 0 ) 
        if( start < 1 ) 
            start = 0;
        

        sql.append( " LIMIT " ).append( start ).append( "," ).append( limit );
    

    /* Return the compiled SQL code */
    return selectQuery( sql.toString() );

这些方法在控制器 Servlet 中调用,用于从数据库中提取数据。示例:

String where = "listId = " + listId;
    String[] fields =  "b.*, l.listId, l.price, l.comment, l.listDate, l.active, l.condition, l.currency, u.*" ;
    String[] join =  "INNER JOIN bzb.book b ON l.isbn=b.isbn",
                "INNER JOIN bzb.user u ON l.userId=u.userId" ;
    ResultSet result = bzb.getDriver().select( "booklisting l", fields, where, join, null, null, null, 0, 1 );

我的问题是,与标准 JDBC 过程相比,这种方法是否被认为是一种好的做法,例如:

String sql = "select SetID,SetName,SetPrice,SetQuality from setdetails  where heroID = " + id;

        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();

        while (rs.next()) 

            lists.add(new Set(rs.getInt("SetID"), rs.getString("SetName"), rs.getString("SetPrice"), rs.getString("SetQuality")));
        
        return lists;

【问题讨论】:

对于您的琐碎 SQL 语句,上述框架不是必需的。 【参考方案1】:

我建议您使用标准的 JDBC 方式(或者如果 SQL 足够复杂和/或在项目的许多部分中使用,则将 SQL 作为存储过程移至数据库)。我已经使用连续几页的连接编写了 SQL 语句,使用 DBDriver 方法看起来会很丑。我建议保持让你的所有代码易于阅读的目标,而不是尝试任何难以阅读的编码,这样你就不用再输入几行代码了。一个类似的论点适用于结构不良的代码或丑陋的代码以实现较小的性能提升。 请注意,许多 SQL 模板的目标是避免为每个 SQL 查询一遍又一遍地编写锅炉代码(try/catch/finally/handle exceptions)。您提供的示例没有这样做。它只帮助您构建 sql 语句。

我建议您使用 try/catch/finally 块,在 finally 块中以与创建它们相反的顺序关闭连接、preparedStatement 和 resultSet(在关闭它们之前先检查它们是否为空)。 如果抛出 SQLException,则捕获它,将导致问题(用于记录目的)的记录的主键值添加到 SQLException,然后重新抛出它。

【讨论】:

您可以将查询外部化到您知道的属性文件中【参考方案2】:

当你使用PreparedStament 时,传递原始参数是没有意义的。我会做一些小的修改,使其更符合Preparedstatement 合规性:

String sql = "select SetID,SetName,SetPrice,SetQuality from setdetails  where heroID = ?";

        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setInt(1,intVal); 

【讨论】:

以上是关于在 JDBC 中编写 SQL 查询的最佳实践是啥的主要内容,如果未能解决你的问题,请参考以下文章

在java中存储sql查询的最佳方法是啥

目前持久性的最佳实践是啥?

在连接上扩展 SQL 查询的最佳实践?

编写/调试复杂 PL/pgSQL 查询的最佳实践

最佳实践:如何通过 JDBC 检查 SQL.DATE 中的特定 java.util.Calendar/Date?

Swift 中全局变量和函数的最佳实践是啥?