Spring源码之JdbcTemplate分析

JdbcTemplate

用过Spring开发的,ORM框架一般选择MyBatis或者Hibernate,不过,Spring对JDBC API的封装工具JdbcTemplate,也提供了很方便的操作,不需要再在使用jdbc api时捕获那么多受检异常,忍受那么多样板式的代码了。

JdbcTemplate主要提供以下几类方法:

  1. execute方法:用于执行任何SQL语句,一般用于执行DDL语句;
  2. update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate批量执行修改;
  3. query方法及queryForXXX方法:用于执行查询相关语句,query方法返回一个列表,queryForXXX方法一般只返回一个对象;
  4. call方法:用于执行存储过程、函数相关语句,一般使用不到

JdbcTemplate类支持设置自定义的回调,比如回调产生预编译语句和存储过程。

如PreparedStatementCreator,使用PreparedStatementCreator,我们可以通过回调获取到JdbcTemplate提供的数据库连接Connection,然后再利用该Conncetion创建自定义的预编译语句PreparedStatement,如,

// 预编译语句及存储过程创建回调、自定义功能回调使用:
@Test
public void testPreparedStatement1() {
    int count = jdbcTemplate.execute(new PreparedStatementCreator() {
    @Override
    public PreparedStatement createPreparedStatement(Connection conn)
        throws SQLException {
        return conn.prepareStatement("select count(*) from test");
    }}, new PreparedStatementCallback<Integer>() {
    @Override
    public Integer doInPreparedStatement(PreparedStatement pstmt)
        throws SQLException, DataAccessException {
        pstmt.execute();
        ResultSet rs = pstmt.getResultSet();
        rs.next();
        return rs.getInt(1);
    }});
    Assert.assertEquals(0, count);
}
// 首先使用PreparedStatementCreator创建一个预编译语句,其次由JdbcTemplate通过PreparedStatementCallback回调传回,由用户决定如何执行该PreparedStatement。此处我们使用的是execute方法。

还有CallableStatementCreator,使用CallableStatementCreator,我们可以通过回调获取到JdbcTemplate提供的数据库连接Connection,然后再利用该Conncetion创建自定义的CallableStatement。

还有PreparedStatementSetter,使用PreparedStatementSetter,我们可以通过回调获取到Spring创建的预编译语句PreparedStatement,然后再对该PreparedStatement的占位符参数’?'设置相应的值。如,

// 预编译语句设值回调使用:
@Test
public void testPreparedStatement2() {
    String insertSql = "insert into test(name) values (?)";
    int count = jdbcTemplate.update(insertSql, new PreparedStatementSetter() {
        @Override
        public void setValues(PreparedStatement pstmt) throws SQLException {
            pstmt.setObject(1, "name4");
    }});
    Assert.assertEquals(1, count);
    String deleteSql = "delete from test where name=?";
    count = jdbcTemplate.update(deleteSql, new Object[] {"name4"});
    Assert.assertEquals(1, count);
}
// 通过JdbcTemplate的int update(String sql, PreparedStatementSetter pss)执行预编译sql,其中sql参数为“insert into test(name) values (?) ”,该sql有一个占位符需要在执行前设值,PreparedStatementSetter实现就是为了设值,使用setValues(PreparedStatement pstmt)回调方法设值相应的占位符位置的值。JdbcTemplate也提供一种更简单的方式“update(String sql, Object... args)”来实现设值,所以只要当使用该种方式不满足需求时才应使用PreparedStatementSetter。

还有BatchPreparedStatementSetter,类似于上面的PreparedStatementSetter,但用于批量设置和批量执行,而且指定批量的量大小size。

还有自定义功能回调,可以提供一个扩展点,在指定类型的扩展点可以执行任何数量需要的操作,如,
ConnectionCallback,使用ConnectionCallback,我们可以通过回调获取到JdbcTemplate提供的数据库连接Connection,然后再利用该Conncetion执行任何数量的操作;

StatementCallback,使用StatementCallback,我们可以通过回调获取到JdbcTemplate提供的Statement语句,然后再利用该Statement执行任何数量的操作;

PreparedStatementCallback,使用PreparedStatementCallback,我们可以通过回调获取到JdbcTemplate提供的预编译语句PreparedStatement,然后再利用该PreparedStatement执行任何数量的操作;

CallableStatementCallback,使用CallableStatementCallback,我们可以通过回调获取到JdbcTemplate提供的回调语句CallableStatement,然后再利用该CallableStatement执行任何数量的操作。

对于JdbcTemplate查询出来的数据,如果要映射成Java对象,一般需要传递一个RowMapper实现,RowMapper用于将结果集的每行数据转换为需要的类型,需要实现方法mapRow(ResultSet rs, int rowNum)来完成转换。如,

// 结果集处理回调:
@Test
public void testResultSet1() {
    jdbcTemplate.update("insert into test(name) values('name5')");
    String listSql = "select * from test";
    List result = jdbcTemplate.query(listSql, new RowMapper<Map>() {
        @Override
        public Map mapRow(ResultSet rs, int rowNum) throws SQLException {
            Map row = new HashMap();
            row.put(rs.getInt("id"), rs.getString("name"));
            return row;
    }});
    Assert.assertEquals(1, result.size());
    jdbcTemplate.update("delete from test where name='name5'");
}
// RowMapper接口提供mapRow(ResultSet rs, int rowNum)方法将结果集的每一行转换为一个Map,当然可以转换为其他类,如表的对象画形式。

BeanPropertyRowMapper

对于RowMapper,使用最多的实现是BeanPropertyRowMapper,由名字可以看出,BeanPropertyRowMapper用于匹配Java类属性和数据库表字段,其具体实现是,内部存放一个MappedFields的HashMap,key是属性名称,value是Java类的属性。

首先,类的属性(该属性要有write方法)名称全部转化成小写,放进map中。

其次,只要类属性名称中有大写字母,把所有这样的大写字母都转化成字符串和小写的形式:“_小写”,把转化后的整个属性名称放进map中。

####注意(BeanPropertyRowMapper中的坑)

BeanPropertyRowMapper默认数据库字段名和Python变量名命名类似,以下划线分割单词;
默认Java类的属性名是以camelCase形式或者是下划线形式(这两种中的任一种都可以)。

如果数据库字段名不使用下划线分割,则应全部使用小写。这个时候,Java类属性名也应全部使用小写,不能使用camelCase,如数据库字段名为companyaddress,那么Java类该属性对应的名字也应该为companyaddress,不能为companyAddress。

如,BeanPropertyRowMapper在创建的时候会调用initialize方法来初始化,这个时候,就会把Java类的属性的camelCase风格的名称转化成下划线分割的名称和全小写的名称,如,

这里写图片描述

除了RowMapper,还有RowCallbackHandler,用于处理结果集中的每一行,需要实现processRow(ResultSet rs)方法。注意,在processRow方法中不要执行rs.next(),这个操作由JdbcTemplate来做,我们只需要获取数据就行。

@Test
public void testResultSet2() {
    jdbcTemplate.update("insert into test(name) values('name5')");
    String listSql = "select * from test";
    final List result = new ArrayList();
    jdbcTemplate.query(listSql, new RowCallbackHandler() {
        @Override
        public void processRow(ResultSet rs) throws SQLException {
            Map row = new HashMap();
            row.put(rs.getInt("id"), rs.getString("name"));
            result.add(row);
    }});
    Assert.assertEquals(1, result.size());
    jdbcTemplate.update("delete from test where name='name5'");
}
// RowCallbackHandler接口也提供方法processRow(ResultSet rs),能将结果集的行转换为需要的形式。

还有ResultSetExtractor,用于提取ResultSet数据,需要实现extractData(ResultSet rs)方法。注意,在extractData方法中需要我们处理整个ResultSet结果集数据。

@Test
public void testResultSet3() {
    jdbcTemplate.update("insert into test(name) values('name5')");
    String listSql = "select * from test";
    List result = jdbcTemplate.query(listSql, new ResultSetExtractor<List>() {
        @Override
        public List extractData(ResultSet rs)
        throws SQLException, DataAccessException {
            List result = new ArrayList();
            while(rs.next()) {
                Map row = new HashMap();
                row.put(rs.getInt("id"), rs.getString("name"));
                result.add(row);
            }
            return result;
    }});
    Assert.assertEquals(0, result.size());
    jdbcTemplate.update("delete from test where name='name5'");
}
// ResultSetExtractor使用回调方法extractData(ResultSet rs)提供给用户整个结果集,让用户决定如何处理该结果集。

喜欢的可以关注微信公众号:

这里写图片描述

####更多文章

  1. 我自己的头条号: JavaEE、Spring源码之JdbcTemplate分析
发布了14 篇原创文章 · 获赞 37 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/c315838651/article/details/72454337