在看《pro spring》,看到第八章感觉有点乱。第一例子就开始需要的两个域就没有。对Spring对JDBC的支持原理,以及对样例代码的解释也不够详细。所以自己尝试总结下。
一、传统的JDBC的应用
学习一项新应用,我首先应该是先了解下老的技术是怎样实现的,是什么原因促使新技术的产生,无疑是真正理解一项技术之根本。
一般书籍或手册在介绍自身数据库应用技术时都给了传统的JDBC应用代码,并真对代码进行分析。在这里先不举类似例子了,只把其结论罗列一下。
1. 指定数据库连接参数
2. 打开数据库连接
3. 生命SQL语句
4. 预编译并执行SQL语句
5. 遍历查询结果(如果需要的话)
6. 处理每一次遍历操作
7. 处理抛出的任何异常
8. 处理事务
9. 关闭数据库连接
总之,JDBC的缺点就时太麻烦了,不易编码,容易出错,不利于开发者把精力投入到业务上去。简化JDBC就是新技术的目标。象Spring,hibernate等都通过对JDBC进行封装,以达到简化开发的目的。但是这些技术在自身侧重上略有不同。如Hibernate主要进行Object/Relational Mapping。
二 spring如何封装的JDBC
Spring JDBC包结构
Spring JDBC抽象框架由四个 包构成:core、dataSource、object以及support。
JdbcTemplate类
JdbcTemplate是core包的核心类。它的功能 有:
管理资源的创建和释放工作
管理JDBC核心处理流程,比如SQL语句的创建、执行。
对ResultSet进行遍历并加以提取。
调用存储过程
捕获JDBC异常并将其转换成org.springframework.dao包中定义的,通用的,信息更丰富的异常。
我们该怎么用JdbcTemplate? 使用JdbcTemplate进行编码只需要根据明确定义的一组契约来实现回调接口。
JdbcTemplate的实例化时,构造方法要求传递数据源。一般有两种用法:第一种是把DataSource bean 注入到一个service类中,然后把这个DataSource属性,传递给JdbcTemplate的构造函数来实例化。第二种是在配置文件中,把DataSource bean传递给JdbcTemplate bean。
JdbcTemplate主要方法:
1)执行SQL语句
view plain copy to clipboard print ?
- JdbcTemplate jdbcTemplate; //获得方法从略
- jdbcTemplate.<strong>execute</strong>("create table user (id integer, username varchar(20))" );
2)执行查询 JdbcTemplate提供了非常丰富的查询方法。记忆应该从业务出发,通过查找api得到适当的方法。不断积累的过程中就记住了。
view plain copy to clipboard print ?
- name= "code" >
view plain copy to clipboard print ?
- JdbcTemplate jdbcTemplate; //获得方法从略
- int count =jdbcTemplate.querForInt( "select count(*) from user" );
- List rows = jdbcTemplate.queryForList("select * from mytable" );
3)更新
view plain copy to clipboard print ?
- JdbcTemplate jdbcTemplate; //获得方法从略
- jdbcTemplate.update("update mytable set name = ? where id = ?" , new Object[] {name, new Integer(id)});
4)写入
JdbcTemplate使用了好几种回调接口来向数据库写数据。先来看两个比较简单的接口。
PreparedStatementCreator接口,这个接口的实现者时负责创建PrepareStatement。这个接口提供了一个方法:
PrepareStatement createPreparedStatement(Connection conn) throw SQLException;
实现这个接口时,要负责从Connection参数创建并返回一个PreparedStatement,但不须考虑异常处理。
NamedParameterjdbcTemplate
NamedParameterJdbcTemplate类增加了在SQL语句中使用命名参数的支持。在此之前,在传统的SQL语句中,参数都是用' ?' 占位符来表示的。NameparameterJdbcTemplate类内部封装了一个普通的JdbcTemplate,并作为其代理来完成大部分工作。
NamedParameterjdbcTemplate类是线程安全的,该类的最佳使用方式不是每次操作的时候都实例化一个新的NamedParameterjdbcTemplate,而是针对每个DataSource只配置一个NamedParameterjdbcTemplate实例(比如在Spring IoC容器中使用Spring Ioc来进行配置),然后在那些使用该类的DAO中共享该实例。
注意: NamedParameterJdbcTemplate类内部包装了一个标准的JdbcTemplate类。如果需要访问其内部的JdbcTemplate实例(比如访问JdbcTemplate的一些方法)那么你需要使用getJdbcOperations()方法返回的JdbcOperations接口。(JdbcTemplate实现了JdbcOperations接口)。
SimpleJdbcTemplate
注意:该类所提供的功能仅使用于Java 5
SimpleJdbcTemplate类是JdbcTemplate类的一个包装起(wrapper),它利用了Java 5的一些语言特性,比如Varargs和Autoboxing。对那些习惯了Java 5 的程序员,这些新的语言特性还是很好用的。
下面两端代码是JdbcTemplate-style和SimpleJdbcTemplate-style实现同一个功能
view plain copy to clipboard print ?
1. //JdbcTemplate-style
2.
3. public Actor findActor( long id) {
4.
5. Sring sql = "select id, first_name,last_name from T_ACTOR where id = ?“;
6.
7.
8.
9. new RowMapper(){
10.
11.
12.
13. public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
14.
15. new Actor();
16.
17. "i" ))));
18.
19. "first_name" ));
20.
21. "last_name" ));
22.
23. return actor;
24.
25. }
26.
27. }
28.
29.
30.
31. //正常应该注入
32.
33. new JdbcTemplate( this .getDataSource());
34.
35. return (Actor) jabcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
36.
37.
38.
39. }
view plain copy to clipboard print ?
1. //SimpleJdbcTemplate-stale
2.
3. public Actor findactor( long id) {
4.
5. "select id,first_name, last_name from T_ACTOR where id = ? " ;
6.
7.
8.
9. new ParameterizedRowMapper<Actor>() {
10.
11. public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
12.
13. new Actor();
14.
15. "id" ));
16.
17. "first_name" ));
18.
19. "last_name" ));
20.
21. return actor;
22.
23. }
24.
25. }
26.
27. new SimpleJdbcTemplate( this .getDataSource());
28.
29. return simpleJdbcTemplate.queryForObject(sql, mapper, id);
30.
31. }
数据源
对了对数据库执行JDBC操作,都必须有一个Connection, 在Spring的DAO框架里,Connection对象是通过DataSource获得的。在使用Spring JDBC时,你既可以通过JNDI获得数据源,也可以自行配置数据源(如使用Spring提供的DataSource实现类)。使用后者可以更方便的脱离Web容器来进行单元测试。
这里先简单看一下DriverManagerDataSource,不过DataSource有多种实现。使用DriverManagerDataSource和以前用JDBC类连接没什么两样。实际应用Spring应该通过配置文件加载DataSource,后面会介绍实际配置方法。
view plain copy to clipboard print ?
1. DrviverManagerDataSource dataSource = new DriverManagerDataSource();
2.
3. dataSource.setDriverClassName("org.hsqldb.jabcDriver" );
4.
5. dataSource.setUrl("jdbc:hsqldb:hsql://localhost:" );
6.
7. dataSource.setUername("sq" );
8.
9. dataSource.setPassword("" );
我来具体看看都有那些DataSource类: DataSourceUtiles类
DataSourceUtiles作为一个帮助类提供易用且强大的数据库访问能力,可以使用该类提供的静态方法从JNDI获取数据库连接以及在必要时候关闭之。 它提供支持线程绑定的数据库连接(比如使用DataSourceTransactionManager 的时候,把数据库连接绑定到当前的线程上)。
注:getDataSourceFromJndi(...)方法主要用于那些没有使用bean factory或者application context的场合。
SmartDataSource接口
AbstractDataSource类
SingleConnectionDataSource类
DriverManagerDataSource类
TransactionAwareDataSourceProxy类
DataSourceTransactionManager类
异常处理
因为Spring提倡使用运行期异常而不是检查异常,所以它通过一种机制把检测到已检查异常转译成运行期异常。在这里就是把SQLException转换成Spring JDBC异常。因为Spring的SQL异常是运行期异常,它们的控制粒度要比已检查异常要细的多。
spring的异常转换
spring提供了SQLExceptionTranslator接口的默认实现,可以把通用SQL错误编码翻译成Spring JDBC异常。在大多数情况下,这个实现足够用了。不过仍然可以扩展Spring的默认实现,让JdbcTemplate使用新的SQLExceptionTranslator。
用Java对象来表达JDBC操作
org.springframework.jdbc.object包下的类允许用户更加面向对象的方式访问数据库。比如说,用户可以执行查询并返回一个list, 该list作为一个结果集将把从数据库中取出来的列数据映射到业务对象的属性上。用户也可以执行存储过程。
SqlQuery类
MappingSqlQuery类
SqlUpdate类
StoredProcedure类
1.JdbcTemplate的execute() ,例如:
jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");
使用JdbcTemplate进行查询时,可以使用queryForXXX() 等方法,例如使用queryForInt()方法传回user表格中的数据数目: jdbcTemplate.quertForInt("select count(*) from user"); 也可以使用queryForObject()传回一个查询后的对象,例如传回一个String对象:
String name=(String)jdbcTemplate.queryForObject("selcet name from user where id=?",new Object[]{id},java.lang.String.class);)
单独查询某个数据
public BaseObj getBaseObj(final int ID) {
String sql = "select * from " + DB_TABLE_NAME + " where NewsId=" + ID;
BaseObj obj =(BaseObj) getJdbcTemplate().query(sql,new ResultSetExtractor(){
public Object extractData(ResultSet rs) throws SQLException,DataAccessException {
if (rs.next()) {
NewsObj news = new NewsObj();
news.setID(rs.getInt("NewsID"));
news.setTitle(rs.getString("NewsTitle"));
news.setBigClass(rs.getInt("BigClassId"));
news.setNewsContent(rs.getString("NewsContent"));
news.setNewsKey(rs.getString("NewsKey"));
news.setNewsAuthor(rs.getString("NewsAuthor"));
news.setImg(rs.getBoolean("isImg"));
news.setNewsFrom(rs.getString("NewsFrom"));
return news;
}
return null;
}
});
return obj; }
上面两个例子都是传回单独一笔数据,如果要传回多笔数据,则可以使用queryForList()方法
例如:
List rows=jdbcTemplate().queryForList("select * from user where id="+id.intValue()); 传回的list中包括的是map对象 ,每个map对象代表查询结果中的一笔数据 ,每笔数据包括多个字段,要取得字段中的值,就要使用字段名作为key,
例如:
Iterator it=rows.iterator();
while(it.hasNext()){
Map result=(Map)it.next();
System.out.println(userMap.get("id"));
System.out.println(userMap.get("name"));
System.out.println(userMap.get("age"));
}
在查询的同时,你可以在返回结果之前先进行一些处理 ,这就要实现RowCallbackHandler接口
public User find(Integer id){
final User user=new User();
jdbcTemplate.query("select * from user where id=?",new Object[]{id},
new RowCallBackHandler(){
public void proccessRow(ResultSet rs){
user.setId(new Integer(rs.getInt("id")));
uset.setName(rs.getString("name"));
}});
return user;
}
如果一次要返回多个查询结果对象 ,则可以实现RowMapper接口
public class UserRowMapper implements RowMapper{
public Object MapRow(ResultSet rsmint rowNum) throws SQLException{
User user=new User();
user.setId(new Integer(rs.getInt("id")));
user.setName(rs.getString("name"));
return user;
}
}
使用RowMapper查询单笔数据
public User find(Integer id){
User user=(User)jdbcTemplate.queryForObject("select * from user where id=?",new Object[]{id},new UserRowMapper());
return user;
}
使用RowMapper查询多笔数据
class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs,int index) throws SQLException
{
User u = new User();
u.setId(rs.getString(”ID”));
u.setName(rs.getString(”Name”));
u.setPassword(rs.getString(”Password”));
return u;
}
}
public List select(String where)
{
List list;
String sql = "select * from admin "+where;
list = jdbcTemplate.query(sql,new RowMapperResultReader(new UserRowMapper()));
return list;
}
传回的users对象中,包括了从数据库查询出来的结果,并已经封装成user对象
JdbcTemplate语句使用:
1、使用JdbcTemplate的execute()
代码
jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");
2、如果是UPDATE或INSERT
代码
jdbcTemplate.update("INSERT INTO USER VALUES('"
+ user.getId() + "', '"
+ user.getName() + "', '"
+ user.getSex() + "', '"
+ user.getAge() + "')");
3、带参数的更新
代码
jdbcTemplate.update("UPDATE USER SET name = ? WHERE user_id = ?", new Object[] {name, id});
代码
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)", new Object[] {user.getId(), user.getName(), user.getSex(), user.getAge()});
4、使用JdbcTemplate进行查询时,使用queryForXXX()
代码
int count = jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER");
代码
String name = (String) jdbcTemplate.queryForObject("SELECT name FROM USER WHERE user_id = ?", new Object[] {id}, java.lang.String.class);
代码
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
代码
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
Iterator it = rows.iterator();
while(it.hasNext()) {
Map userMap = (Map) it.next();
System.out.print(userMap.get("user_id") + "/t");
System.out.print(userMap.get("name") + "/t");
System.out.print(userMap.get("sex") + "/t");
System.out.println(userMap.get("age") + "/t");
}
JdbcTemplate将我们使用的JDBC的流程封装起来 ,包括了异常的捕捉、SQL的执行、查询结果的转换等等。spring大量使用Template Method模式来封装固定流程的动作,XXXTemplate等类别都是基于这种方式的实现。
除了大量使用Template Method来封装一些底层的操作细节,spring也大量使用callback方式类回调相关类别的方法以提供JDBC相关类别的功能,使传统的JDBC的使用者也能清楚了解spring所提供的相关封装类别方法的使用。
JDBC的PreparedStatement
代码
final String id = user.getId();
final String name = user.getName();
final String sex = user.getSex() + "";
final int age = user.getAge();
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, id);
ps.setString(2, name);
ps.setString(3, sex);
ps.setInt(4, age);
}
});
代码
final User user = new User();
jdbcTemplate.query("SELECT * FROM USER WHERE user_id = ?",
new Object[] {id},
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
}
});
代码
class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
return user;
}
}
public List findAllByRowMapperResultReader() {
String sql = "SELECT * FROM USER";
return jdbcTemplate.query(sql, new RowMapperResultReader(new UserRowMapper()));
}
在getUser(id)里面使用UserRowMapper
代码
public User getUser(final String id) throws DataAccessException {
String sql = "SELECT * FROM USER WHERE user_id=?";
final Object[] params = new Object[] { id };
List list = jdbcTemplate.query(sql, params, new RowMapperResultReader(new UserRowMapper()));
return (User) list.get(0);
}
网上收集
org.springframework.jdbc.core.PreparedStatementCreator 返回预编译SQL 不能于Object[]一起用
代码
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(sql);
}
1.增删改
org.springframework.jdbc.core.JdbcTemplate 类(必须指定数据源dataSource)
代码
template.update("insert into web_person values(?,?,?)",Object[]);
或
代码
template.update("insert into web_person values(?,?,?)",new PreparedStatementSetter(){ 匿名内部类 只能访问外部最终局部变量
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(index++,3);
}); org.springframework.jdbc.core.PreparedStatementSetter 接口 处理预编译SQL
代码
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(index++,3);
}
2.查询JdbcTemplate.query(String,[Object[]/PreparedStatementSetter],RowMapper/RowCallbackHandler)
org.springframework.jdbc.core.RowMapper 记录映射接口 处理结果集
代码
public Object mapRow(ResultSet rs, int arg1) throws SQLException { int表当前行数
person.setId(rs.getInt("id"));
}
List template.query("select * from web_person where id=?",Object[],RowMapper); org.springframework.jdbc.core.RowCallbackHandler 记录回调管理器接口 处理结果集
代码
template.query("select * from web_person where id=?",Object[],new RowCallbackHandler(){
public void processRow(ResultSet rs) throws SQLException {
person.setId(rs.getInt("id"));
});