1. 程式人生 > >spring之JdbcTemplate查詢資料的兩種方式

spring之JdbcTemplate查詢資料的兩種方式

在spring中對原生的jdbc操作進行封裝成模板類JdbcTemplate類,之所以封裝,是因為原生jdbc操作不但麻煩而且囉嗦,使業務程式碼和資料庫操作程式碼混在一起,相當雜亂。而且如果你獲得資料來源連線之後如果忘了關閉,就會有資料連線洩露的風險,久而久之,系統崩潰。而使用JdbcTemplate就不一樣了,spring對於資料的操作採用模板模式進行,分為模板和回撥兩個部分,對於連線資料庫,釋放資源這種不變的操作封裝在模板中,而對於資料庫的資料訪問封裝在回撥介面中進行,spring的這一方法,不可謂不經典!

今天的主題說的是JdbcTemplate對於查詢資料的操作,有兩種介面都可以進行查詢,分別是RowCallbackHandler()和RowMapper<T>,下面先來看看例子:

package com.smart.pojo;
//實體類,部落格論壇
public class Forum {

    private int forumId;
    private String forumName;
    private String forumDesc;

    public int getForumId() {
        return forumId;
    }

    public void setForumId(int forumId) {
        this.forumId = forumId;
    }

    public String getForumName() {
        return forumName;
    }

    public void setForumName(String forumName) {
        this.forumName = forumName;
    }

    public String getForumDesc() {
        return forumDesc;
    }

    public void setForumDesc(String forumDesc) {
        this.forumDesc = forumDesc;
    }
}

資料訪問介面如下:

@Repository
public class ForumDao {

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    //查詢資料,使用RowCallbackHandler處理結果集
    public Forum getForum(final int forumId){
        String sql = "select forum_name,forum_desc from t_forum where forum_id=?";
        final Forum forum = new Forum();

        //將結果集資料行中的資料抽取到forum物件中
        jdbcTemplate.query(sql, new Object[]{forumId}, new RowCallbackHandler() {
            public void processRow(ResultSet rs) throws SQLException {
                forum.setForumId(forumId);
                forum.setForumName(rs.getString("forum_name"));
                forum.setForumDesc(rs.getString("forum_desc"));
            }
        });
        return forum;
    }

    //查詢資料,批量查詢,呼叫RowCallbackHandler()介面
    public List<Forum> getForums(final int fromId , final int toId){
        String sql = "select * from t_forum where forum_id between ? and ?";
        final List<Forum> forumList = new ArrayList<Forum>();

        jdbcTemplate.query(sql, new Object[]{fromId, toId}, new RowCallbackHandler() {//將結果集中的資料對映到List中
            public void processRow(ResultSet rs) throws SQLException {
                Forum forum = new Forum();
                forum.setForumId(rs.getInt("forum_id"));
                forum.setForumName(rs.getString("forum_name"));
                forum.setForumDesc(rs.getString("forum_desc"));
                forumList.add(forum);
            }
        });
        return forumList;
    }

    ////查詢資料,批量查詢,呼叫RowMapper()介面
    public List<Forum> getForumsByRowMapper(final int fromId , final int toId){
        String sql = "select * from t_form where forum_id between ? and ?";
        return jdbcTemplate.query(sql, new Object[]{fromId, toId}, new RowMapper<Forum>() {
            public Forum mapRow(ResultSet rs, int rowNum) throws SQLException {
                Forum forum = new Forum();
                forum.setForumId(rs.getInt("forum_id"));
                forum.setForumName(rs.getString("forum_name"));
                forum.setForumDesc(rs.getString("forum_desc"));
                return forum;
            }
        });
    }
    
    
}

可以看出,無論是單個物件查詢還是集合查詢,都可以使用RowCallbackHandler回撥介面,通過該介面可以定義如何從結果集中獲取資料。而RowMapper<T>僅需定義結果集行和物件的對映關係即可。

那麼問題來了,二者有何區別?

【我們知道,通過JDBC查詢返回一個ResultSet結果集時,JDBC並不會一次性將匹配的資料都載入到JVM中,而是隻返回一批次的資料(由JDBC驅動程式決定,如ORACLE的JDBC驅動預設返回10行),當通過ResultSet#next()遊標滾動結果集超過資料範圍時,JDBC再獲取一批資料。這樣以一種“批量化+序列化”的處理方式避免大結果集處理時JVM記憶體的過大開銷。】

當處理大結果集時,如果使用Row Mapper,那麼採用的方式是將結果集中的所有資料都放到一個List<T>物件中,這樣將會佔用大量的JVM記憶體,甚至可能引發OutOfMemoryException,這時,可以採用RowCallbackHandler介面,在processRow()介面方法內部一邊獲取資料一邊完成處理,這樣資料就不會在記憶體中堆積,可大大減少對JVM記憶體的佔用。

****************************************************************************************************************************

舉例:如果程式要求給所有系統使用者傳送一封郵件,而系統使用者數量為100萬。一種方案是採用RowMapper,返回一個List<User>集合,再通過遍歷這個List<User>,逐個傳送郵件;而另一種方案是採用RowCallbackHandler介面,在processRow()介面方法內部逐行獲取User資料後,立即呼叫郵件服務傳送郵件。雖然這兩種方案都達到了相同的目的,但第一種方案會在程式執行過程中,在JVM中產生一個系統使用者數大小為100萬條的List<User>,從而導致極低的系統性能和巨大的記憶體開銷,甚至引起系統崩潰。

結論:採用RowMapper的操作方式是先獲取資料,再處理資料;而RowCallbackHandler得操作方式是一邊獲取資料一邊處理,處理完就丟棄。因此,可以將RowMapper看作採用批量化資料處理策略,而RowCallbackHandler則採用流化處理資料。