




已阅读5页,还剩18页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Spring JdbcTemplateJdbcTemplate的API明显地分割为几个部分:1. Query用于从数据库查询数据。该部分的API具有queryForXXX形式,或query方法。每个方法均有3-6个重载版本,query()方法甚至有16个重载版本。具体来说,Query部分的API有:queryForInt, queryForList, queryForLong, queryForMap, queryForObject, queryForRowSet以及query方法。 queryForInt、queryForLongqueryForInt和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(SELECT COUNT(*) FROM customer);示例2:查询customer表中名字含有in的用户数 public long getCustomerCount2()return this.jdbcTemplate.queryForLong(SELECT COUNT(*) FROM customer WHERE name LIKE ?, new Object%in%);在后台的数据库中,name字段被声明为VARCHAR(10)。因此,这条语句相当于:return this.jdbcTemplate.queryForLong(SELECT COUNT(*) FROM customer WHERE name LIKE ?, new Object%in%, new intTypes.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: %in%; nested exception is java.sql.SQLException: Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException - For input string: %in%java.sql.SQLException: Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException - For input string: %in%.可以看到,由于无法将%in%转换为Types.INTEGER,程序出现了异常。 queryForObjectqueryForObject函数共有6个重载版本:Object queryForObject(Stringsql, ClassrequiredType) Object queryForObject(Stringsql, Objectargs, ClassrequiredType) Object queryForObject(Stringsql, Objectargs, intargTypes, ClassrequiredType) Object queryForObject(Stringsql, Objectargs, intargTypes, RowMapperrowMapper) Object queryForObject(Stringsql, Objectargs, RowMapperrowMapper) Object queryForObject(Stringsql, RowMapperrowMapper) 这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(SELECT name FROM customer WHERE id=?, new Object Long.valueOf(1L), , new intTypes.INTEGER, , String.class);如果在上面的程序中,将id的值从1(数据库中存在对应记录)该为-1(数据库中不存在相应的记录),那么程序将会抛出异常:org.springframework.dao.IncorrectResultSizeDataAccessException 如果你不期望如此,可以使用后面要介绍的queryForList来解决这个问题。示例2:获取id为1的用户的性别public Gender getCustomerGender()return (Short)this.jdbcTemplate.queryForObject(SELECT gender FROM customer WHERE id=1, Short.class) = 0) ? Gender.FEMALE : Gender.MALE;由于数据库中customer表的gender列被创建为tinyint(1) default 1,因此我们可以使用Short类来处理。当然,这里还可以用Integer来替代Short:return (Integer)this.jdbcTemplate.queryForObject(SELECT gender FROM customer WHERE id=1, Integer.class) = 0) ? Gender.FEMALE : Gender.MALE;这也不会带来任何错误。甚至我们还可以使用queryForInt/queryForLong来实现:return (this.jdbcTemplate.queryForInt(SELECT gender FROM customer WHERE id=1) = 0) ? Gender.FEMALE : Gender.MALE;示例3:获取id为1的用户对象public Customer getCustomer() return (Customer)this.jdbcTemplate.queryForObject(SELECT id, name, gender, cellphoneno FROM customer WHERE id = ?, 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(id); customer.setName(rs.getString(name);customer.setGender(rs.getShort(gender) = (short)0) ? Gender.FEMALE : Gender.MALE); customer.setCellPhoneNo(rs.getString(cellphoneno);return customer;);RowMapper很简单,不是吗?注意:使用queryForObject方法的时候,需要特别注意。你可能期望对于你的查询,如果数据库中没有对应的记录返回null就好。但是,JdbcTemplate并非如此,它会抛出异常。 queryForList方法queryForList一共有6个重载版本:List queryForList(Stringsql) List queryForList(Stringsql, ClasselementType) List queryForList(Stringsql, Objectargs) List queryForList(Stringsql, Objectargs, ClasselementType) List queryForList(Stringsql, Objectargs, intargTypes) List queryForList(Stringsql, Objectargs, intargTypes, ClasselementType) 参数sql, args, argTypes的含义和前面的含义相同,elementType是返回值的每一个元素的类型。对于elementType的取值与前面的requiredType是相同的。如果不指定的话,返回值将会被设定为一个a list of maps,即返回值中的每个元素都是一个map对象 。该map中的entry的key为查询中的字段名,value为该字段下的值。示例1:获取所有所有用户名SuppressWarnings(unchecked)public List getCustomerNames()return this.jdbcTemplate.queryForList(SELECT name FROM customer, String.class);示例2:获取所有男性的ID,姓名和手机号SuppressWarnings(unchecked)public List getCustomerInfo()return this.jdbcTemplate.queryForList(SELECT id, name, cellphoneno FROM customer WHERE gender=1);可以看到queryForList方法并没有指定elementType参数,因此Spring默认地赋值给它为ListOrderedMap,这就是前面提到的map。如果将这个结果打印出来的话,其输出为:id=1, name=Wang Jin, cellphoneno id=2, name=Wu Dong, cellphoneno据当前数据库的内容,该List中包含两份数据,该输出证实了这一点。示例3:获取名字为Lucky的用户的手机号SuppressWarnings(unchecked)public List getLuckyCellphoneno()return this.jdbcTemplate.queryForList(SELECT cellphoneno FROM customer WHERE name=?, new ObjectLucky);该段程序逻辑上并不复杂。但是有两点需要说明:1). 当前数据库中并不存在名字为Lucky的用户,但是,与queryForObject不同的是,程序并不会抛出异常。相反,程序返回的是一个size为0的List。2). 尽管在逻辑上,返回的List中的每一个元素都应该是一个String对象,换句话说,返回的List应该是List。但是由于我们没有显示地指定elementType参数,Spring会按照默认的情况进行处理,即视返回值为a list of maps。从而,返回值为List类型。 另一方面,Spring似乎提供了”特别的支持”。对于示例3给定的例子,我们可以直接将返回值的类型标记为List, Spring也不会有丝毫的抱怨,照样给出期望的结果。例如,我们获取名字为Wang Jin的用户的id(从数据库中可以看到其值为1):SuppressWarnings(unchecked)public List getWangId()return this.jdbcTemplate.queryForList(SELECT id FROM customer WHERE name=?, new ObjectWang Jin); 这样的程序也不会导致任何的错误,程序还是欢喜地给出了结果:id=1。但是,问题是这并不是期望的输出!我们期望的输出是1。出现这样问题的原因是,queryForList方法是在泛型出现之前便已经存在了,而它的返回值是一个不带任何元素类型的(似乎该种类型的List/Map被称为Raw type, 题外话J)。正是因为如此,才会造成前面的困扰。下面再给出一个类似的例子:#1 List listString = new ArrayList();#2 listString.add(a);#3 listString.add(b);#4 #5 List list = listString;#6 List listInteger = list;#7 System.out.println(listInteger); 这个程序片断首先构建了一个List,并且在第5行将其转换为了一个Raw type的List,实际上这抹去了listString的信息。在第6行的时候,将无元素类型信息的list对象转换为一个List对象。listString,listInteger和list这三个引用都指向了同一个对象。由于Raw type的List的介入,使得指向的这个对象(ArrayList)丢失了元素类型信息,即ArrayList变得与ArrayList无异。但是,由于语法上的保证,listString还是只能向其中加入String对象,listInteger还是只能向其中加入Integer对象。毋庸置疑的,listString/listInteger对于元素的添加仍然会影响到它们指向的对象: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(unchecked)public Customer getCustomerById(long id) List customers = this.jdbcTemplate.queryForList(SELECT name, gender, cellphoneno FROM customer WHERE id=?, new Object Long.valueOf(id), );if(customers.size() = 0)return null;if(customers.size() 1) throw new MoreThanOneCustomerException(More than one customer with id( + id + ) were found.);ListOrderedMap lom = customers.get(0);Customer customer = new Customer();customer.setId(id);customer.setName(String)lom.get(name);customer.setGender(Boolean)lom.getValue(1) ? Gender.MALE : Gender.FEMALE);customer.setCellPhoneNo(String)lom.getValue(2);return customer;正如前面所言,queryForList方法没有显式地设置elementType的值,因此返回了一个包含ListOrderedMap的List对象。程序的后面部分使用了这个ListOrderedMap对象,通过该对象程序提取到了所需的值。关于ListOrderedMap它有几个常用的方法: public Object get(Object key) 根据key返回相应value。 这里key通常表现为一个String对象,即是查询对应的列名。 publc Object get(int index) 不要被该方法迷惑。这个方法是根据index的值(从0开始),返回相应的key值。 public Object getValue(int index) 该方法根据index的值(从0开始),返回相应的value值。该方法会返回一个准确的类型,比如在上面的程序中,lom.getValue(1)的返回值是一个Boolean,而不是我们前面使用的Short(1对应的是gender列)。为了将来程序的可移植性,这里最好不要写成ListOrderedMap,而是写作Map接口:getCustomerByIdSuppressWarnings(unchecked)public Customer getCustomerById(long id) List customers = this.jdbcTemplate.queryForList(SELECT name, gender, cellphoneno FROM customer WHERE id=?, new Object Long.valueOf(id), );if(customers.size() = 0)return null;if(customers.size() 1)throw new MoreThanOneCustomerException(More than one customer with id( + id + ) were found.);Map map = customers.get(0);Customer customer = new Customer();customer.setId(id);customer.setName(String)map.get(name);customer.setGender(Boolean)map.get(gender) ? Gender.MALE : Gender.FEMALE);customer.setCellPhoneNo(String)map.get(cellphoneno);return customer;关于queryForList还有一点需要注意的是,如果没有指定elemenetType的话,返回的List中的Map的key值是来自于数据库中的列,而不是跟查询相关的。例如:List customers = this.jdbcTemplate.queryForList(SELECT , gender, cellphoneno FROM customer c WHERE id=?, new Object Long.valueOf(id), );在使用对应Map的时候,你不能使用 map.get(“”), 这会返回null。你仍然需要使用map.get(“name”),即使用数据库表中相应的列名。再如:List customers = this.jdbcTemplate.queryForList(SELECT name AS nm, gender, cellphoneno FROM customer WHERE id=?, new Object Long.valueOf(id), );你仍然不能使用map.get(“nm”),而只能使用map.get(“name”)。 queryForMap queryForMap共有3个重载版本:Map queryForMap(Stringsql)Map queryForMap(Stringsql, Objectargs) Map queryForMap(Stringsql, Objectargs, intargTypes) 它们用来将查询结果转换为一个Map对象。这些方法要求查询结果为一个1行的数据,然后它们可以将这1行数据按照以相应表字段为key值,查询得到的值为value的方式进行转换。事实上,queryForList方法在没有指定elementType的情况下,其返回值中的元素便是根据此得到的。 有了前面介绍的基础,这个方法的使用便相当直观了。示例1:获取id为1的用户对象SuppressWarnings(unchecked)public Customer getCustomerById(long id) Map map = this.jdbcTemplate.queryForMap(SELECT name, gender, cellphoneno FROM customer WHERE id=?, new Object Long.valueOf(id), ); if(map.size() = 0)return null;Customer customer = new Customer();customer.setId(id);customer.setName(String)map.get(name);customer.setGender(Boolean)map.get(gender) ? Gender.MALE : Gender.FEMALE);customer.setCellPhoneNo(String)map.get(cellphoneno);return customer; . getCustomerById(1L);需要注意的是,queryForMap方法会在先调用queryForObject方法来获得查询的对象。因此,如果查询不到任何对象的话,queryForObject会抛出异常,从而queryForMap也会抛出异常。 queryForRowSet 方法queryForRowSet共有3个重载版本:SqlRowSet queryForRowSet(Stringsql) SqlRowSet queryForRowSet(Stringsql, Objectargs)SqlRowSet queryForRowSet(Stringsql, Objectargs, intargTypes) 这些方法用来从数据库中查询相应的记录,并将查询到的记录封装成为一个SqlRowSet对象。SqlRowSet接口是对javax.sql.RowSet接口的镜像,它提供了一种无连接情况下的数据集访问方法。可以通过其提供的absolute,beforeFirst、first、previous、next等方法来定位要访问的行,也可以通过使用getXXX方法获得当前行的元素值。示例1:获取所有Customer对象public Customer getAllCustomers() List customers = new ArrayList(); SqlRowSet rowSet = this.jdbcTemplate.queryForRowSet(SELECT * FROM customer);while(rowSet.next()Customer customer = new Customer();customer.setId(rowSet.getLong(id);customer.setGender(rowSet.getBoolean(gender) ? Gender.MALE : Gender.FEMALE);customer.setName(rowSet.getString(name);customer.setCellPhoneNo(rowSet.getString(cellphoneno);customers.add(customer);return customers.toArray(new Customer0);示例2:使用previous、next、absolute等方法 SqlRowSet中的数据逻辑关系是用一个被称作“游标”的东西来控制的,如下图所示:游标的最初始状态是位于Before First位置的,即它指向的是数据的第一个元素之前。借由next、previous、absolute、first、last等方法,可以实现游标的移动。如果游标当前指向的对象在DATA的范围之内,那么便可以使用getXXX方法获取相应数据。对于absolute方法,其参数可以视为表示游标的位置,它是从1开始的。也就是说First的row值为1,其余依次类推。SuppressWarnings(unchecked)public void getCustomers()SqlRowSet sqlRowSet = this.jdbcTemplate.queryForRowSet(SELECT id, name, gender, cellphoneno FROM customer);sqlRowSet.absolute(2); /jump to the second recordSystem.out.println(populateCustomer(sqlRowSet);sqlRowSet.absolute(6);System.out.println(populateCustomer(sqlRowSet);sqlRowSet.absolute(1); /jump to the first recordSystem.out.println(populateCustomer(sqlRowSet);if(sqlRowSet.next() System.out.println(populateCustomer(sqlRowSet); if(sqlRowSet.first() /jump to the first row System.out.println(populateCustomer(sqlRowSet);/populate customer info from SqlRowSet instanceprivate Customer populateCustomer(SqlRowSet sqlRowSet)if(sqlRowSet.isAfterLast()System.out.print(After last= );return null;if(sqlRowSet.isBeforeFirst()System.out.print(Before first= );return null;if(sqlRowSet.isFirst()System.out.print(First= );else if(sqlRowSet.isLast()System.out.print(Last= );Customer customer = new Customer();customer.setId(sqlRowSet.getLong(id);customer.setGender(sqlRowSet.getBoolean(gender) ? Gender.MALE : Gender.FEMALE);customer.setName(sqlRowSet.getString(name);customer.setCellPhoneNo(sqlRowSet.getString(cellphoneno);return customer; 目前数据库中共有4条记录:程序的输出如下:(Wu Dongmale)After last= nullFirst= (Wang Jinmale)(Wu Dongmale)First= (Wang Jinmale)对照程序的输出,不难理解SqlRowSet的工作原理。 query方法JdbcTemplate的query方法是拥有最多重载版本数的方法,同时也是最为灵活的方法。Object query(PreparedStatementCreator psc, PreparedStatementSetter pss, ResultSetExtractor rse) Object query(PreparedStatementCreator psc, ResultSetExtractor rse) Object query(String sql, Object args, int argTypes, ResultSetExtractor rse) Object query(String sql, Object args, ResultSetExtractor rse) Object query(String sql, PreparedStatementSetter pss, ResultSetExtractor rse) Object query(String sql, ResultSetExtractor rse) List query(PreparedStatementCreator psc, RowCallbackHandler rch) List query(PreparedStatementCreator psc, RowMapper rowMapper) List query(String sql, Object args, int argTypes, RowCallbackHandler rch) List query(String sql, Object args, int argTypes, RowMapper rowMapper) List query(String sql, Object args, RowCallbackHandler rch) List query(String sql, Object args, RowMapper rowMapper) List query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) List query(String sql, PreparedStatementSetter pss, RowMapper rowMapper) List query(String sql, RowCallbackHandler rch) List query(String sql, RowMapper rowMapper) 从返回值的类型上来看,query方法分为两部分:返回值为Object或List。从query方法的执行上来看,query方法采用JDBC Statement以及PreparedStatement两种。以Object作为返回值的方法都含有一个ResultSetExtractor对象。这是一个接口,它定义了唯一的一个方法:Object extractData(ResultSetrs) throws SQLException, DataAccessException对于query方法来说,ResultSetExtractor接口中的ResultSet是一个最原始的ResultSet,你必须调用它的next方法才能获取到第一行数据。但是,正如Javadoc指出的那样,你不应该关闭这个ResultSet对象,JdbcTemplate对象会自动关闭它。PreparedStatementCreator接口用于创建PerparedStatement对象,PreparedStatemetSetter接口则用于设置PreparedStatemet对象,它们定义的方法分别如下:PreparedStatementCreatorPreparedStatement createPreparedStatement(Connectioncon) throws SQLExceptionPreparedStatementSettervoid setValues(PreparedStatementps) 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(SELECT name, gender, cellphoneno FROM customer WHERE id=?, new Object Long.valueOf(id), new ResultSetExtractor()public Object extractData(ResultSet rs) throws SQLException, DataAccessException if(!rs.next() /从原始的ResultSet中提取数据return null;Customer customer = new Customer();customer.setId(id);customer.setName(rs.getString(name);customer.setGender(rs.getBoolean(gender) ? Gender.MALE : Gender.FEMALE);customer.setCellPhoneNo(rs.getString(cellphoneno);return customer;); 从这里可以看到,ResultSetExtractor接口为程序访问ResultSet提供了一条绿色通道,当然这也是最原始的访问方式。事实上,ResultSetExtractor中的ResultSet并不要求只能返回至多一条记录,任意记录数都是可以接受的。这与传统的JDBC编程是相同的。return (Customer)this.jdbcTemplate.query(SELECT name, gender, cellphoneno FROM customer WHERE gender=?, 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(name);customer.setGender(rs.getBoolean(gender) ? Gender.MALE : Gender.FEMALE);customer.setCellPhoneNo(rs
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 培智学校数学试卷
- 辽宁历年高考数学试卷
- 临沂高三期中数学试卷
- 南铁去年单招数学试卷
- 医学知识培训总结课件
- 2025首都医科大学附属北京积水潭医院招8人(第四批)笔试参考题库附答案解析
- 2025年网络营销策划师职业技能等级考核试卷及答案
- 2025年网络数据分析师综合素质考核试题及答案
- 2025年网络技术维护运营实践操作规范考核试题及答案
- 2025云南楚雄州武定县插甸学校教师招考13人流动笔试参考题库附答案解析
- 2025年全国焊工证理论考试题库及答案
- 乡村基地代运营合同范本
- 2025年烟叶生产考试题库
- 学堂在线 自我认知与情绪管理 章节测试答案
- 房颤导管消融护理
- 多元化经营战略下小米公司盈利能力的分析
- 安徽省2025年公需科目培训测验答案(科目一)
- 2025年汽车驾驶员技师资格证书考试及考试题库含答案
- 浙江省衢州市2024-2025学年高二下学期6月教学质量检测数学试卷(含答案)
- 2025贵州省专业技术人员继续教育公需科目考试题库(2025公需课课程)
- 融资性担保贷款保后检查表
评论
0/150
提交评论