JdbcTemplate
用过Spring开发的,ORM框架一般选择MyBatis或者Hibernate,不过,Spring对JDBC API的封装工具JdbcTemplate,也提供了很方便的操作,不需要再在使用jdbc api时捕获那么多受检异常,忍受那么多样板式的代码了。
JdbcTemplate主要提供以下几类方法:
- execute方法:用于执行任何SQL语句,一般用于执行DDL语句;
- update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate批量执行修改;
- query方法及queryForXXX方法:用于执行查询相关语句,query方法返回一个列表,queryForXXX方法一般只返回一个对象;
- 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)提供给用户整个结果集,让用户决定如何处理该结果集。
喜欢的可以关注微信公众号:
####更多文章