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则采用流化处理数据。

猜你喜欢

转载自blog.csdn.net/weixin_38437243/article/details/80733637
今日推荐