JdbcTemplate完全学习

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

JdbcTemplate完全学习
概述
Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核⼼类,所以其他模板类都是基于它封装完成的,JDBC模板类是第⼀种⼯作模式。

JdbcTemplate类通过模板设计模式帮助我们消除了冗长的代码,只做需要做的事情(即可变部分),并且帮我们做哪些固定部分,如连接的创建及关闭。

JdbcTemplate类对可变部分采⽤回调接⼝⽅式实现,如ConnectionCallback通过回调接⼝返回给⽤户⼀个连接,从⽽可以使⽤该连接做任何事情、StatementCallback通过回调接⼝返回给⽤户⼀个Statement,从⽽可以使⽤该Statement做任何事情等等,还有其他⼀些回调接⼝
Spring除了提供JdbcTemplate核⼼类,还提供了基于JdbcTemplate实现的NamedParameterJdbcTemplate类⽤于⽀持命名参数绑定、
SimpleJdbcTemplate类⽤于⽀持Java5+的可变参数及⾃动装箱拆箱等特性。

JdbcTemplate类⽀持的回调类:
预编译语句及存储过程创建回调:⽤于根据JdbcTemplate提供的连接创建相应的语句;
PreparedStatementCreator:通过回调获取JdbcTemplate提供的Connection,由⽤户使⽤该Conncetion创建相关的PreparedStatement;CallableStatementCreator:通过回调获取JdbcTemplate提供的Connection,由⽤户使⽤该Conncetion创建相关的CallableStatement;
预编译语句设值回调:⽤于给预编译语句相应参数设值;
PreparedStatementSetter:通过回调获取JdbcTemplate提供的PreparedStatement,由⽤户来对相应的预编译语句相应参数设值;
BatchPreparedStatementSetter:;类似于PreparedStatementSetter,但⽤于批处理,需要指定批处理⼤⼩;
⾃定义功能回调:提供给⽤户⼀个扩展点,⽤户可以在指定类型的扩展点执⾏任何数量需要的操作;
ConnectionCallback:通过回调获取JdbcTemplate提供的Connection,⽤户可在该Connection执⾏任何数量的操作;
StatementCallback:通过回调获取JdbcTemplate提供的Statement,⽤户可以在该Statement执⾏任何数量的操作;
PreparedStatementCallback:通过回调获取JdbcTemplate提供的PreparedStatement,⽤户可以在该PreparedStatement执⾏任何数量的操作;
CallableStatementCallback:通过回调获取JdbcTemplate提供的CallableStatement,⽤户可以在该CallableStatement执⾏任何数量的操作;
结果集处理回调:通过回调处理ResultSet或将ResultSet转换为需要的形式;
RowMapper:⽤于将结果集每⾏数据转换为需要的类型,⽤户需实现⽅法mapRow(ResultSet rs, int rowNum)来完成将每⾏数据转换为相应的类型。

RowCallbackHandler:⽤于处理ResultSet的每⼀⾏结果,⽤户需实现⽅法processRow(ResultSet rs)来完成处理,在该回调⽅法中⽆需执⾏rs.next(),该操作由JdbcTemplate来执⾏,⽤户只需按⾏获取数据然后处理即可。

ResultSetExtractor:⽤于结果集数据提取,⽤户需实现⽅法extractData(ResultSet rs)来处理结果集,⽤户必须处理整个结果集;
下⾯详细讲解jdbcTmeplate的CRUD操作:
(⼀)增加、删除、修改操作:
1)增加、更新、删除(⼀条sql语句)(sql固定,不需要参数):
(a) int update(final String sql)
其中sql参数为需要传⼊的插⼊sql语句。

(b)int update(PreparedStatementCreator psc)
public void test() {
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn)
throws SQLException {
return conn.prepareStatement("insert into test(name) values('name1')");
}
});
}
(c)如果需要返回新插⼊数据的主键,采⽤如下⽅法(使⽤KeyHolder keyholder=new GeneratedKeyHolder();获得主键,jdbcTemplate和NamedParameterJdbcTemplate都可以通过此⽅法获得主键):
int update(PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)
public void test() {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn)
throws SQLException {
return conn.prepareStatement("insert into test(name) values('name1')");
}
},keyHolder);
int i = keyHolder.getKey().intValue();//这就是刚插⼊的数据的主键
}
2)增加、更新、删除(⼀条sql语句)(sql需要注⼊参数填充‘?’):
(a)int update(String sql, PreparedStatementSetter pss)
public void test() {
String sql = "insert into test(name) values (?)";
//返回的是更新的⾏数
int count = jdbcTemplate.update(sql, new PreparedStatementSetter(){
@Override
public void setValues(PreparedStatement pstmt)
throws SQLException {
pstmt.setObject(1, "name4");
}
});
}
(b)int update(String sql, Object[] args, int[] argTypes)
其中参数含义: sql:预处理sql语句; args:sql需要注⼊的参数; argTypes:需要注⼊的sql参数的JDBC类型(java.sql.Types中来获取类型的常量);
public void test() {
String sql = "insert into test(name,age,create_date) values (?,?,?)";
Date now = new Date(System.currentTimeMillis());
//返回的是更新的⾏数
int count = jdbcTemplate.update(sql, new Object[]{"⼩明",14,now}, new int[]{Types.VARCHAR,Types.INTEGER,Types.DATE});
}
(c)int update(String sql, Object... args)
其实内部还是调⽤⽅法a实现的,JdbcTemplate提供这种更简单的⽅式“update(String sql, Object... args)”来实现设值,所以只要当使⽤该种⽅式不满⾜需求时才应使⽤PreparedStatementSetter(上⾯⽅法a)。

public void test() {
String sql = "insert into test(name,age,create_date) values (?,?,?)";
Date now = new Date(System.currentTimeMillis());
//返回的是更新的⾏数
int count = jdbcTemplate.update(sql, "⼩明", 14, now);
}
public void test() {
String sql = "insert into test(name,age,create_date) values (?,?,?)";
Date now = new Date(System.currentTimeMillis());
//返回的是更新的⾏数
int count = jdbcTemplate.update(sql, new Object[]{"⼩明",14,now});
}
这两种实际上调⽤的都是该⽅法,由此可见Object...args实际上就是可变的数组,⽽数组长度是固定的,必须先定义⼀个数组,⽽Object...args在传递时参数可以任意,所以也可以传递⼀个固定的Object数组。

(d)int update(PreparedStatementCreator psc)
使⽤该⽅法可以⾃⼰使⽤原始jdbc⽅式给预编译sql注⼊参数,来进⾏增加、删除、更新操作:
public void test(final Customer customer) {//参数也是局部变量,也必须⽤final修饰,内部类中才能访问(全局变量不⽤)
//⽅法局部必须是final的,匿名内部类中才能引⽤
final String sql = "insert into test(name,age,create_date) values (?,?,?)";
Date now = new Date(System.currentTimeMillis());
//返回的是更新的⾏数
int count = jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn)
throws SQLException {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, customer.getName());
ps.setInt(2, customer.getAge());
ps.setDate(3, customer.getCreateDate());
return ps;
}
});
}
如果需要返回插⼊的主键,只能⽤此⽅法,增加KeyHolder参数:
public void test(final Customer customer) {//参数也是局部变量,也必须⽤final修饰,内部类中才能访问(全局变量不⽤)
KeyHolder keyHolder = new GeneratedKeyHolder();
//⽅法局部必须是final的,匿名内部类中才能引⽤
final String sql = "insert into test(name,age,create_date) values (?,?,?)";
Date now = new Date(System.currentTimeMillis());
//返回的是更新的⾏数
int count = jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn)
throws SQLException {
PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); //有的数据库版本不⼀样,需要添加第⼆个参数,不然会报错; ps.setString(1, customer.getName());
ps.setInt(2, customer.getAge());
ps.setDate(3, customer.getCreateDate());
return ps;
}
},keyHolder);
int i = keyHolder.getKey().intValue();//这就是刚插⼊的数据的主键 }
3)批量增加、删除、更新数据(多条sql语句)
(a)批量执⾏多条sql(固定的sql,不需要注⼊参数,但是sql格式不固定)
int[] batchUpdate(final String[] sql)
参数是⼀个String数组,存放多条sql语句;返回值是int数组,即每条sql更新影响的⾏数。

(b)批量执⾏多条sql(预处理sql,需要注⼊参数)
int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss)
参数sql:⼀条预处理sql(如果是批量处理预处理sql,那么sql的格式就是固定的,只填充参数⽽已);第⼆个参数就是回调类,前⾯有统⼀介绍回调类。

举两个例⼦,⼀个更新,⼀个插⼊:
批量插⼊:
public void test(final List<Customer> customer) {//参数也是局部变量,也必须⽤final修饰,内部类中才能访问(全局变量不⽤)
String sql = "insert into test(name,age,create_date) values (?,?,?)";
//返回的是更新的⾏数
int[] count = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i)
throws SQLException {
//注⼊参数值
ps.setString(1, customer.get(i).getName());
ps.setInt(2, customer.get(i).getAge());
ps.setDate(3, customer.get(i).getCreateDate());
}
@Override
public int getBatchSize() {
//批量执⾏的数量
return customer.size();
}
});
}
批量更新:
public void test(final List<Customer> customer) {//参数也是局部变量,也必须⽤final修饰,内部类中才能访问(全局变量不⽤)
String sql = "update test set name = ?,age = ? where id = ?";
//返回的是更新的⾏数
int[] count = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i)
throws SQLException {
//注⼊参数值
ps.setString(1, customer.get(i).getName());
ps.setInt(2, customer.get(i).getAge());
ps.setInt(3, customer.get(i).getId());
}
@Override
public int getBatchSize() {
//批量执⾏的数量
return customer.size();
}
});
}
(c)批量处理多条预处理sql语句还有下⾯⼏种简单⽅法(参数和前⾯类似,这⾥就不详解):
int[] batchUpdate(String sql, List<Object[]> batchArgs);
int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes);
前⾯讲了增加、删除、更新操作,这节讲⼀下查询。

查询操作:
(⼀)查询⼀个值(不需要注⼊参数)
queryForObject(String sql, Class<T> requiredType);
注意:参数requiredType只能是String,Integer这种类型,不能是⾃定义的实体类型,只能返回⼀个值,不能映射对象(映射对象下⾯会说);
sql:预处理sql;requiredType:查询单列结果的类型;
public void test() {
String sql = "select count(*) from test";
int count = jdbcTemplate.queryForObject(sql, Integer.class);
}
(⼆)查询⼀个值(使⽤预处理sql,需要注⼊参数)
queryForObject(String sql, Object[] args, Class<T> requiredType);
public void test(Integer id) {
String sql = "select name from test where id = ?";
String name = jdbcTemplate.queryForObject(sql, new Object[]{id}, String.class);
}
还有如下⽅式:queryForObject(String sql, Object[] args, int[] argTypes, Class<T> requiredType);
(三)查询单⾏记录,转换成⼀个对象(固定sql,不需要参数)
<T> T queryForObject(String sql, RowMapper<T> rowMapper)
public void test() {
String sql = "select name,age from test where id = 10";
Customer customer = jdbcTemplate.queryForObject(sql, new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int i)
throws SQLException {
Customer c = new Customer();
c.setName(rs.getString("name"));
c.setAge(rs.getInt("age"));
return c;
}
});
}
(四)查询单⾏记录,转换成⼀个对象(预处理sql,需要注⼊参数)
<T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)
public void test(Integer id) {//参数也是局部变量,也必须⽤final修饰,内部类中才能访问(全局变量不⽤)
String sql = "select name,age from test where id = ?";
Customer customer = jdbcTemplate.queryForObject(sql, new Object[]{id}, new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int paramInt)
throws SQLException {
Customer c = new Customer();
c.setName(rs.getString("name"));
c.setAge(rs.getInt("age"));
return c;
}
});
}
也可以使⽤如下⽅式:(1)<T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper);
(2)<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);
(五)查询数据库中⼀列多⾏数据,即查询数据库中单列数据存⼊⼀个list中,⽅式如下:(固定sql,没参数)
(a)List<Map<String, Object>> queryForList(String sql)
这个⽅法封装成map放⼊list中,key:列名(Oracle数据库sql执⾏结果列名默认为⼤写,需要⼩写⽤as取别名,别名⽤双引号) value:列的值
public void test() {//如果Oracle⽤这个sql查询,返回的列名就是NAME(⼤写的),对应Map⾥⾯的key就是NAME
String sql = "select name from test where id > 0";
//如果⽤这个sql查询,返回的列名就是name(⼩写的),对应Map⾥⾯的key就是name
String sql2 = "select name as \"name\" from test where id > 0";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);//这⾥⽤的是第⼀个sql
}
(b)<T> List<T> queryForList(String sql, Class<T> elementType)
这个⽅法就是直接将单类型数据存⼊List中。

注意:这个T虽然是泛型,但是只⽀持Integer.class String.class 这种单数据类型的,⾃⼰定义的Bean不⽀持。

(所以⽤来查询单列数据)
public void test() {
String sql = "select name from test where id > 0";
List<String> list = jdbcTemplate.queryForList(sql, String.class);
}
(六)查询数据库中⼀列多⾏数据,即查询数据库中单列数据存⼊⼀个list中,⽅式如下:(预处理sql,需要注⼊参数)
(a)<T> List<T> queryForList(String sql, Object[] args, Class<T> elementType)
注意:这个T虽然是泛型,但是只⽀持Integer.class String.class 这种单数据类型的,⾃⼰定义的Bean不⽀持。

(所以⽤来查询单列数据,同前⾯⼀个意思,后⾯不再解释)
public void test(Integer id) {
String sql = "select name from test where id > ?";
List<String> list = jdbcTemplate.queryForList(sql, new Object[]{id}, String.class);
}
还有如下⽅式实现:(1)<T> List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType);
(2)<T> List<T> queryForList(String sql, Class<T> elementType, Object... args);
(b)List<Map<String, Object>> queryForList(String sql, Object... args)
封装成map存⼊List,和之前⼀样,要注意Oracle数据库返回的列名默认是⼤写的,如果需要,⽤别名变⼩写。

public void test(Integer id) {
String sql = "select name from test where id > ?";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, new Object[]{id});
}
还有⼀种⽅式实现:List<Map<String, Object>> queryForList(String sql, Object[] args, int[] argTypes);
(七)查询多条数据(固定sql,没有参数)
(a)<T> List<T> query(String sql, RowMapper<T> rowMapper)
每条数据映射为java对象,放⼊List中。

public void test() {
String sql = "select name,age from test where id > 10";
List<Customer> list = jdbcTemplate.query(sql, new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int rowNum)
throws SQLException {
//这⾥必须new对象,不能在⽅法外new,然后⽤同⼀个,因为是⼀个List,查询出来多个对象映射,
//必须保证每⼀次调⽤都使⽤⼀个新的。

//如果不new,⽽是使⽤同⼀个对象,会导致存⼊到List中的都是⼀样的对象(都是最后⼀个对象)。

Customer customer = new Customer();
customer.setName(rs.getString("name"));
customer.setAge(rs.getInt("age"));
return customer;
}
});
}
该⽅法也可以把每⼀⾏数据转换为⾃定义key-value的map对象放⼊list中,如下:(所以说使⽤回调类⽐简单⽅法更强⼤,⾥⾯逻辑⾃⼰按需求写)
public void test() {
String sql = "select name,age from test where id > 10";
List<Map<Integer,Object>> list = jdbcTemplate.query(sql, new RowMapper<Map<Integer,Object>>() {
@Override
public Map<Integer,Object> mapRow(ResultSet rs, int rowNum)
throws SQLException {
Map<Integer, Object> map = new HashMap<Integer, Object>();
map.put(rowNum, rs.getString("name"));
map.put(rowNum, rs.getInt("age"));
return map;
}
});
}
map中的key,value类型根据需要⾃定义,⾮常⽅便。

像这种实现RowMapper<T>接⼝的匿名类,T可以为Map,也可以为⾃定义的对象类型,如上两种,根据需要选择。

(b) void query(String sql, RowCallbackHandler rch)
注意:如果使⽤RowCallbackHandler 回调类,这个⽅法是没有返回值的,⽽是在回调类中将结果放⼊预先定义的List中,⽤法如下:public void test() {
String sql = "select name,age from test where id > 10";
//局部变量,必须⽤final修饰,内部类中才能访问(全局变量不⽤)
final List<Customer> list = new ArrayList<Customer>();
jdbcTemplate.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
Customer customer = new Customer();
customer.setName(rs.getString("name"));
customer.setAge(rs.getInt("age"));
list.add(customer);
}
});
}
当然,这种⽅式也可以转换为map,存⼊list中,和上⾯a⽅式⼀样,这⾥就不详解了。

(c)<T> T query(final String sql, final ResultSetExtractor<T> rse)
ResultSetExtractor使⽤回调⽅法extractData(ResultSet rs)提供给⽤户整个结果集,让⽤户决定如何处理该结果集。

public void test() {
String sql = "select name,age from test where id > 10";
List<Customer> list = jdbcTemplate.query(sql, new ResultSetExtractor<List<Customer>>() {
@Override
public List<Customer> extractData(ResultSet rs)
throws SQLException, DataAccessException {
List<Customer> result = new ArrayList<Customer>();
while(rs.next()) {
Customer customer = new Customer();
customer.setName(rs.getString("name"));
customer.setAge(rs.getInt("age"));
result.add(customer);
}
return result;
}
});
}
同样也可以转换为map对象放⼊list中,如下:
public void test() {
String sql = "select name,age from test where id > 10";
List<Map<String, Integer>> list = jdbcTemplate.query(sql, new ResultSetExtractor<List<Map<String, Integer>>>() {
@Override
public List<Map<String, Integer>> extractData(ResultSet rs)
throws SQLException, DataAccessException {
List<Map<String, Integer>> result = new ArrayList<Map<String, Integer>>();
while(rs.next()) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put(rs.getString("name"), rs.getInt("age"));
result.add(map);
}
return result;
});
}
(d)<T> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper)
public void test() {//局部变量,必须⽤final修饰,内部类中才能访问(全局变量不⽤)
final String sql = "select name,age from test where id > 10";
List<Customer> list = jdbcTemplate.query(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn)
throws SQLException {
PreparedStatement ps = conn.prepareStatement(sql);
//如果sql是预处理的,需要传⼊参数,可以在这⾥写jdbc代码传⼊,后⾯就不列举这种⽅法了
return ps;
}
}, new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int rowNum)
throws SQLException {
Customer customer = new Customer();
customer.setAge(rs.getInt("age"));
customer.setName(rs.getString("name"));
return customer;
}
});
}
可以将RowMapper换成ResultSetExtractor或者RowCallbackHandler回调类,和前⾯⼀样,因此还有下⾯两种⽅法:
(1)void query(PreparedStatementCreator psc, RowCallbackHandler rch);
(2)<T> T query(PreparedStatementCreator psc, ResultSetExtractor<T> rse);
(⼋)查询多条数据(预处理sql,需要传⼊参数)
(a)<T> List<T> query(String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper)
public void test(final Integer id) {//参数也是局部变量,也必须⽤final修饰,内部类中才能访问(全局变量不⽤)
String sql = "select name,age from test where id > ?";
List<Customer> list = jdbcTemplate.query(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps)
throws SQLException {
ps.setInt(1, id);
}
}, new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int rowNum)
throws SQLException {
Customer customer = new Customer();
customer.setName(rs.getString("name"));
customer.setAge(rs.getInt("age"));
return customer;
}
});
}
⽤RowMapper回调类还有三种⽅法:
(1)<T> List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper);
(2)<T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper);
(3)<T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args);
⽽如果把回调类换成ResultSetExtractor或者RowCallbackHandler回调类,⼜有⼋种⽅法(像上⾯⼀样各有四种),这⾥就不举出来了,和前⾯⽤法⼀致。

使⽤Spring的JdbcTemplate或者NamedParameterJdbcTemplate查询数据库,获取结果,数据库表字段和实体类⾃动对应,可以使⽤BeanPropertyRowMapper。

⾃动绑定,需要列名称和Java实体类名字⼀致,如:属性名 “userName” 可以匹配数据库中的列字段(这⾥说的列字段是sql执⾏结果的 ⾃动绑定,需要列名称和
列名,也就是如果有别名就⽤别名,(Oracle默认列名⼤写)) "USERNAME" 或 “user_name”。

这样,我们就不需要⼀个个⼿动绑定了,⼤⼤提⾼了开发效率。

下⾯讲解BeanPropertyRowMapper⽤法:
BeanPropertyRowMapper<T> implements RowMapper<T>这个类是实现了RowMapper接⼝的,所以之前讲的查询中,能⽤到RowMapper回调类的都可以⽤BeanPropertyRowMapper来将结果直接映射为实体类。

public List<UserEntity> findUser(UserEntity user) {
List<UserEntity> userList = jdbcTemplate.query(SEL_BY_USERNAME_PWD,
new Object[] { user.getUserName(), user.getPwd() },
new BeanPropertyRowMapper<UserEntity>(UserEntity.class));
return userList;
}
正如上⾯,直接利⽤BeanPropertyRowMapper的构造⽅法传递⼀个需要映射的类的class对象进去即可实现,当然必须满⾜之前说的要求:
1.属性名“userName”要按如下规则匹配sql结果列:结果列要是"user_name"(⼤⼩写都⾏),因为BeanPropertyRowMapper的中会将结果列都转为⼩写去和对象中set属性对应;
2.属性名“user”这对应结果列为“USER”(⼤⼩写都⾏),理由同上;。

相关文档
最新文档