Spring - JdbcTemplate
更新时间:2023-10-03 21:07:01 阅读量: 综合文库 文档下载
- spring推荐度:
- 相关推荐
Spring JdbcTemplate
JdbcTemplate的API明显地分割为几个部分: 1. Query
用于从数据库查询数据。该部分的API具有queryForXXX形式,或query方法。每个方法均有3-6个重载版本,query()方法甚至有16个重载版本。具体来说,Query部分的API有:queryForInt, queryForList, queryForLong, queryForMap, queryForObject, queryForRowSet以及query方法。 ? queryForInt、queryForLong
queryForInt和queryForLong这两个方法非常相似,只是它们的返回值上存在差异。以queryForLong为例:
long queryForLong(String sql)
long queryForLong(String sql, Object[] args)
long queryForLong(String sql, Object[] args, int[] argTypes)
其中,sql为需要执行的SQL语句,args对应了sql中的参数,argTypes则是对应sql中各个参数的类型。值得注意的是,这些方法要求数据库的返回值只能为一个1行1列的long型数据(超过或者SQL查询语句没有相应返回),否则将抛出IncorrectResultSizeDataAccessException异常。 示例1:查询customer表中的用户数 public long getCustomerCount(){ } return this.jdbcTemplate.queryForLong(\); 示例2:查询customer表中名字含有in的用户数 public long getCustomerCount2(){ } return this.jdbcTemplate.queryForLong( \COUNT(*) FROM customer WHERE name LIKE ?\, new Object[]{\}); 在后台的数据库中,name字段被声明为VARCHAR(10)。因此,这条语句相当于:
return this.jdbcTemplate.queryForLong(
\,
new Object[]{\}, new int[]{Types.VARCHAR});
如果错误地指定了相应字段的类型,将可能导致异常。例如这里将Types.VARCHAR错误地指定为java.sql.Types.INTEGER,将招致如下异常:
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [SELECT COUNT(*) FROM customer WHERE name LIKE ?]; SQL state [S1000]; error code [0]; Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException - For input string: \nested exception is java.sql.SQLException: Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException - For input string: \java.sql.SQLException: Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException - For input string: \...... 可以看到,由于无法将%in%转换为Types.INTEGER,程序出现了异常。
? queryForObject
queryForObject函数共有6个重载版本: Object queryForObject(String sql, Class requiredType) Object queryForObject(String sql, Object[] args, Class requiredType) Object queryForObject(String sql, Object[] args, int[] argTypes, Class requiredType) Object queryForObject(String sql, Object[] args, int[] argTypes, RowMapper rowMapper) Object queryForObject(String sql, Object[] args, RowMapper rowMapper) Object queryForObject(String sql, RowMapper rowMapper) 这6个API又明显地分为两个部分:前3个为一部分,后3个为一部分。参数sql,args,argsType的含义与前面介绍的一致。参数requiredType表明了相应函数的返回值类型。对于该参数来说是实际上是有限制的,比如它不能被设定为自定义类型,例如这里的Customer类。如果你需要将返回值设定为自定义类型,你需要使用后3个API来完成。RowMapper接口的功能就是将结果集中的某行转换为一个Object。
与queryForInt/queryForLong一样,前3个API都被期望返回一个1行1列的数据,该行数据就是结果数据(直接为结果),否则会抛出异常。因而,参数requiredType的值必须是一个对应数据库中的表字段对应的Java类型。例如:MySQL数据库中的VARCHAR类型对应着java.lang.String, BIGINT对应着java.lang.Long等等。对于后3个API,由于使用的策略是将结果集中的某一行转换为一个Object,这要求返回结果须为单一的一行(不限列的数量),否则会抛出异常。 示例1:获取id为1的用户的姓名 public String getCustomerName(){ } return (String)this.jdbcTemplate.queryForObject( \, new Object[]{ Long.valueOf(1L), }, new int[]{Types.INTEGER, }, String.class); 如果在上面的程序中,将id的值从1(数据库中存在对应记录)该为-1(数据库中不存在相应的记录),那么程序将会抛出异常:
org.springframework.dao.IncorrectResultSizeDataAccessException
如果你不期望如此,可以使用后面要介绍的queryForList来解决这个问题。 示例2:获取id为1的用户的性别
public Gender getCustomerGender(){ return ((Short)this.jdbcTemplate.queryForObject( \, Short.class) == 0) ? Gender.FEMALE : Gender.MALE; } 由于数据库中customer表的gender列被创建为tinyint(1) default '1',因此我们可以使用Short类来处理。当然,这里还可以用Integer来替代Short:
return ((Integer)this.jdbcTemplate.queryForObject(
\,
Integer.class) == 0) ? Gender.FEMALE : Gender.MALE;
这也不会带来任何错误。甚至我们还可以使用queryForInt/queryForLong来实现:
return (this.jdbcTemplate.queryForInt(
\gender FROM customer WHERE id=1\) == 0) ? Gender.FEMALE
: Gender.MALE;
示例3:获取id为1的用户对象 public Customer getCustomer() { return (Customer)this.jdbcTemplate.queryForObject( \, new Object[]{ Long.valueOf(1L), }, new RowMapper(){ public Object mapRow(ResultSet rs, int index) throws SQLException { Customer customer = new Customer();//构造Customer对象 customer.setId(rs.getInt(\)); customer.setName(rs.getString(\)); customer.setGender((rs.getShort(\) == (short)0) ? Gender.FEMALE : customer.setCellPhoneNo(rs.getString(\)); Gender.MALE); } return customer; });
注
RowMapper很简单,不是吗?
意:使用queryForObject方法的时候,需要特别注意。你可能期望对于你的查询,如果数据库中没有对应的记录返回null就好。但是,JdbcTemplate并非如此,它会抛出异常。 ? queryForList
方法queryForList一共有6个重载版本:
List queryForList(String sql) List queryForList(String sql, Class elementType) List queryForList(String sql, Object[] args) List queryForList(String sql, Object[] args, Class elementType) List queryForList(String sql, Object[] args, int[] argTypes) List queryForList(String sql, Object[] args, int[] argTypes, Class elementType)
参数sql, args, argTypes的含义和前面的含义相同,elementType是返回值的每一个元素的类型。对于elementType的取值与前面的requiredType是相同的。如果不指定的话,返回值将会被设定为一个a list of maps,即返回值中的每个元素都是一个map对象 。该
map中的entry的key为查询中的字段名,value为该字段下的值。 示例1:获取所有所有用户名 @SuppressWarnings(\) public List
@SuppressWarnings(\) public List
2). 尽管在逻辑上,返回的List中的每一个元素都应该是一个String对象,换句话说,返回的List应该是List
另一方面,Spring似乎提供了”特别的支持”。对于示例3给定的例子,我们可以直接将返回值的类型标记为List
这样的程序也不会导致任何的错误,程序还是欢喜地给出了结果:[{id=1}]。但是,问
题是这并不是期望的输出!我们期望的输出是[1]。出现这样问题的原因是,queryForList方法是在泛型出现之前便已经存在了,而它的返回值是一个不带任何元素类型的(似乎该种类型的
List/Map被称为Raw type, 题外话?)。正是因为如此,才会造成前面的困扰。下面再给出一个类似的例子:
#1 List
listInteger.add(1); //这会导致向ArrayList中添加一个Integer对象 listString.add(“c”); //这会导致向ArrayList中添加一个String对象 这两个add方法都是OK的,因为它们指向的那个ArrayList对象已经没有了型别意识,这些对象会被当作Object对象而添加到其中。
PS: 表达式listString==list以及listInteger==list都会返回true,而listString==listInteger则会出现编译期错误。泛型的引入,如同Auto-boxing的引入一样,破坏了==运算符的传递性。
结论 在使用queryForList的时候如果你期望访问的List中的元素为某一个特定的类型,那么请指定明确的elementType,否则将会返回a list of maps。
示例3:获取id为1的用户对象 @SuppressWarnings(\) public Customer getCustomerById(long id) { List
PreparedStatementCreator接口用于创建PerparedStatement对象,PreparedStatemetSetter接口则用于设置PreparedStatemet对象,它们定义的方法分别如下: PreparedStatementCreator PreparedStatement createPreparedStatement(Connection con) throws SQLException PreparedStatementSetter void setValues(PreparedStatement ps) throws SQLException JdbcTemplate的所有方法只要出现了二者之一,便会使用PreparedStatement来处理。 ? Object query(String sql, ResultSetExtractor rse)
Object query(String sql, Object[] args, int[] argTypes, ResultSetExtractor rse)
Object query(String sql, Object[] args, ResultSetExtractor rse)
很显然,这三个query方法有相当程度上的相似性,主要取决于是否要指定参数以及参数的SQL类型。
示例1 :通过id获得用户对象 public Customer getCustomerById(final long id) { return (Customer)this.jdbcTemplate.query( \, new Object[]{ Long.valueOf(id)}, new ResultSetExtractor(){ public Object extractData(ResultSet rs) throws SQLException, DataAccessException { } }); } if(!rs.next()) //从原始的ResultSet中提取数据 Customer customer = new Customer(); customer.setId(id); customer.setName(rs.getString(\)); customer.setGender((rs.getBoolean(\)) ? Gender.MALE : Gender.FEMALE); return null; customer.setCellPhoneNo(rs.getString(\)); return customer; 从这里可以看到,ResultSetExtractor接口为程序访问ResultSet提供了一条绿色通道,当然这也是最原始的访问方式。事实上,ResultSetExtractor中的ResultSet并不要求只能返回至多一条记录,任意记录数都是可以接受的。这与传统的JDBC编程是相同的。
return (Customer)this.jdbcTemplate.query( \, new Object[]{ false }, //返回性别为女的用户 new ResultSetExtractor(){ public Object extractData(ResultSet rs) throws SQLException, DataAccessException { if(!rs.next()) //这个ResultSet是不只一条记录的,当前数据库中有2条记录 }); } return null; Customer customer = new Customer(); customer.setId(id); customer.setName(rs.getString(\)); customer.setGender((rs.getBoolean(\)) ? Gender.MALE : Gender.FEMALE); customer.setCellPhoneNo(rs.getString(\)); return customer; 这个例子再次证明ResultSetExtractor提供的ResultSet和传统方式下进行查询得到的结果集对象是完全相同的,只是你不要关闭它(不过在当前版本中,即使在程序中调用rs.close()也不会出现任何问题)。
? Object query(PreparedStatementCreator psc, PreparedStatementSetter pss, ResultSetExtractor rse)
Object query(PreparedStatementCreator psc, ResultSetExtractor rse) Object query(String sql, PreparedStatementSetter pss, ResultSetExtractor rse)
这三个方法是以PreparedStatement方式进行工作的。作为一个示例,我们仍然使用前面的获取第一个性别为女的用户为例: getFirstFemaleCustomer public Customer getFirstFemaleCustomer () { return (Customer)this.jdbcTemplate.query( new PreparedStatementCreator(){ public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { return connection.prepareStatement( \); }}, new PreparedStatementSetter(){ }, new ResultSetExtractor(){ public Object extractData(ResultSet rs) throws SQLException, DataAccessException { public void setValues(PreparedStatement ps) throws SQLException { } ps.setBoolean(1, false); if(!rs.next()) Customer customer = new Customer(); customer.setId(id); return null; customer.setName(rs.getString(\)); customer.setGender((rs.getBoolean(\)) ? Gender.MALE : Gender.FEMALE); customer.setCellPhoneNo(rs.getString(\)); } } rs.close(); return customer; }); 这段代码使用了Object query(PreparedStatementCreator psc, PreparedStatementSetter pss, ResultSetExtractor rse)形式,即实现了PreparaedStatementCreator、PreparedStatementSetter和ResultSetExtractor这三个接口。使用其余两种方式是类似的,这里不再赘述。
对于返回值为List的query方法,也可以分为两部分。
? List query(PreparedStatementCreator psc, RowCallbackHandler rch)
List query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch) List query(String sql, Object[] args, RowCallbackHandler rch)
List query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) List query(String sql, RowCallbackHandler rch)
这些方法都包含一个RowCallbackHandler接口,你可以在该接口中定义你用来处理当前行的方法。
示例1:获取所有男性用户,并在获取的过程中打印用户的名字 public List getAllMaleCustomers(){ } return this.jdbcTemplate.query( \, new Object[]{ true, }, new RowCallbackHandler(){ public void processRow(ResultSet rs) throws SQLException { System.out.println(rs.getString(\)); }); }
当query方法在处理结果集中的每一行的时候都会回调RowCallbackHandler,就在这个时候我们将打印用户名字的代码添加了进来。如果运行该段程序,可以看到用户的名字是正确打印出来了的。然而,返回的List却是一个null。 打开Javadoc,发现该方法的返回值部分的说明如下: Returns: the result List in case of a ResultReader, or null else
很显然,我们自己定义的内部类不是一个ResultReader对象,因此List的值为null。利用Eclipse的Open Type Hierarchy功能,可以看到在Spring中实现了ResultReader的类共有三个,其中包括2个内部类和RowWrapperResultReader:
现在来使用RowMapperResultReader类:
@SuppressWarnings(\) public List
? List query(PreparedStatementCreator psc, RowMapper rowMapper)
List query(String sql, Object[] args, int[] argTypes, RowMapper rowMapper) List query(String sql, Object[] args, RowMapper rowMapper)
List query(String sql, PreparedStatementSetter pss, RowMapper rowMapper) List query(String sql, RowMapper rowMapper)
这几个方法相对简单:RowMapper将ResultSet中的某一行影射为一个对象,该对象最终会被添加到作为结果的List对象中。 @SuppressWarnings(\) public List
JdbcTemplate使用的update方法可以在其中使用的SQL语句包括UPDATE,CREATE、INSERT等。JdbcTemplate提供的API中与Update相关的有: int update(PreparedStatementCreator psc) int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) protected int update(PreparedStatementCreator psc, PreparedStatementSetter pss) int update(String sql) int update(String sql, Object[] args) int update(String sql, Object[] args, int[] argTypes) int update(String sql, PreparedStatementSetter pss) int[] batchUpdate(String[] sql) int[] batchUpdate(String sql, BatchPreparedStatementSetter pss) 上表的下半部分的两个方法是与批量更新相关的,上半部分则是通常情况下的更新操作。其中, int update(String sql) ,int update(String sql, Object[] args),int
update(String sql, Object[] args, int[] argTypes) ,int update(String sql, PreparedStatementSetter pss)最容易理解。
示例1: 添加用户对象
public void addCustomer(final Customer customer){ int retVal = this.jdbcTemplate.update( \, new PreparedStatementSetter(){ public void setValues(PreparedStatement ps) throws SQLException { } }); if(retVal > 0) ps.setString(1, customer.getName()); ps.setBoolean(2, (Gender.FEMALE == customer.getGender()) ? false : true); ps.setString(3, customer.getCellPhoneNo());
正在阅读:
高二数学期末试卷02-06
汽车故障诊断教案10-06
项目融资复习题08-12
2014年初级护师专业实践能力考试试题及答案解析(六)05-06
婚礼游戏和洞房游戏大全05-31
商法二历年试题和答案 - 图文11-13
小学大课间活动安排表03-29
- 发电电气运行规程1
- 英文简历
- 最全辅导员招聘考试题库
- 4.3崇明岛的未来的样子
- 2012年上海市普通高校招生二本批次各校投档分数线
- 江苏省如皋中学2017-2018学年第一学期高三第二次阶段测试12月数
- 农业转移人口社会参与机制浅谈
- 2017-2018学年度牛津译林版8B英语初二期中试卷及答案
- 家长委员会上的讲话
- 05继电保护设备检修规程
- 组织行为学考试重点(陈春花)
- 2016年云南省公务员考试《行测》模拟试卷(十七)
- 规避“10号文”红筹系列之案例分析
- 钱寨小学学生读书活动评价方案
- 五大联赛派系
- 国际结算课件新
- 材料科学导论 - 图文
- 领导干部任前廉政法规考试模拟试题
- 汽车综合实训
- 医疗质量管理目录
- JdbcTemplate
- Spring
- 《就业指导与创业教育》期末考试试卷
- 博弈论有关名词解释
- 新北师版四年级下册歌手大赛教学设计
- 应用地球物理读书报告
- 幼儿园大班综合应彩云优质公开课《大熊的拥抱节》完整教案
- 王熙凤的性格特点分析
- 海南省三亚市城市化发展战略调研报告 - 图文
- 2018届江西省南昌市高三第三次理科数学模拟试题Word版含答案
- 醋酸工艺设计 - 图文
- 语言文字工作相关制度汇编(1—6)
- 农商银行在省联社成立十周年座谈会上的发言
- 闸阀专业英语
- 乙酸乙酯皂化反应速率常数的测定 实验报告(1)
- 过控自动化考前复习
- 传播学教程笔记(详细版) -
- 东北石油大学2012工程硕士报考须知
- 非全日制用工制度解析
- 四三档同步环项目可行性研究报告(发改立项备案+2013年最新案例范文)详细编制方案
- 人教版小学六年级英语下册Unit4单元测试卷(1)有答案
- 电缆招标书 doc-晨鸣纸业