学习Spring中的JdbcTemplate(1)

[size=large]来自:CSDN java060515
学习Spring中的JdbcTemplate
1. 简介Spring提供的JDBC抽象框架由core, datasource,object和 support四个不同的包组成。

就和它名字的暗示一样,org.springframework.jdbc.core包里定义了提供核心功能的类。其中有各种SQLExceptionTranslator和DataFieldMaxValueIncrementer的实现以及一个

用于JdbcTemplate的DAO基础类。

org.springframework.jdbc.datasource包里有一个用以简化数据源访问的工具类,以及各种数据源的简单实现,以被用来在J2EE容器之外不经修改地测试JDBC代码。这个工具

类提供了从JNDI获得连接和可能用到的关闭连接的静态方法。它支持绑定线程的连接,比如被用于DataSourceTransactionManager。

接着,org.springframework.jdbc.object包里是把关系数据库的查询,更新和存储过程封装为线程安全并可重用对象的类。这中方式模拟了JDO,尽管查询返回的对象理所当然

的“脱离”了数据库连接。这个JDBC的高层抽象依赖于org.springframework.jdbc.core包中所实现的底层抽象。

最后在org.springframework.jdbc.support包中你可以找到 SQLException的转换功能和一些工具类。

在JDBC调用中被抛出的异常会被转换成在定义org.springframework.dao包中的异常。这意味着使用Spring JDBC抽象层的代码不需要实现JDBC或者RDBMS特定的错误处理。所有

的转换后的异常都是unchecked,它允许你捕捉那些你可以恢复的异常,并将其余的异常传递给调用者。

2. 使用JDBC核心类控制基本的JDBC处理和错误处理2.1. JdbcTemplate这是在JDBC核心包中最重要的类。它简化了JDBC的使用,因为它处理了资源的建立和释放。它帮助你避免

一些常见的错误,比如忘了总要关闭连接。它运行核心的JDBC工作流,如Statement的建立和执行,而只需要应用程序代码提供SQL和提取结果。这个类执行SQL查询,更新或者

调用存储过程,模拟结果集的迭代以及提取返回参数值。它还捕捉JDBC的异常并将它们转换成 org.springframework.dao包中定义的通用的,能够提供更多信息的异常体系。

使用这个类的代码只需要根据明确定义的一组契约来实现回调接口。 PreparedStatementCreator回调接口创建一个由该类提供的连接所建立的PreparedStatement,并提供SQL和

任何必要的参数。CallableStatementCreateor实现同样的处理,只是它创建了CallableStatement。RowCallbackHandler接口从数据集的每一行中提取值。

这个类可以直接通过数据源的引用实例化,然后在服务中使用,也可以在ApplicationContext中产生并作为bean的引用给服务使用。注意:数据源应当总是作为一个bean在

ApplicationContext中配置,第一种情况它被直接传递给服务,第二种情况给JdbcTemplate。因为这个类把回调接口和SQLExceptionTranslator接口作为参数表示,所以没有必

要为它定义子类。这个类执行的所有SQL都会被记入日志。

2.2. 数据源为了从数据库获得数据,我们需要得到数据库的连接。 Spring采用的方法是通过一个数据源。数据源是JDBC规范的一部分,它可以被认为是一个通用的连接工厂。

它允许容器或者框架将在应用程序代码中隐藏连接池和事务的管理操作。开发者将不需要知道连接数据库的任何细节,那是设置数据源的管理员的责任。虽然你很可能在开发或

者测试的时候需要兼任两种角色,但是你并不需要知道实际产品中的数据源是如何配置的。

使用Spring的JDBC层,你可以从JNDI得到一个数据源,也可以通过使用Spring发行版提供的实现自己配置它。后者对于脱离Web容器的单元测试是十分便利的。我们将在本节中

使用DriverManagerDataSource实现,当然以后还会提到其他的一些的实现。 DriverManagerDataSource和传统的方式一样获取JDBC连接。为了让DriverManager可以装载驱动类

,你必须指定JDBC驱动完整的类名。然后你必须提供相对于各种JDBC驱动的不同的URL。你必须参考你所用的驱动的文档,以获得需要使用的正确参数。最后,你还必须提供用

来连接数据库的用户名和密码下面这个例子说明如何配置DriverManagerDataSource:

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName( "org.hsqldb.jdbcDriver");
dataSource.setUrl( "jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername( "sa");
dataSource.setPassword( "");
2.3. SQLExceptionTranslatorSQLExceptionTranslator是一个需要实现的接口,它被用来处理SQLException和我们的数据访问异常

org.springframework.dao.DataAccessException之间的转换。

实现可以是通用的(比如使用JDBC的SQLState值),也可以为了更高的精确度特殊化 (比如使用Oracle的ErrorCode)。

SQLErrorCodeSQLExceptionTranslator 是SQLExceptionTranslator的实现,它被默认使用。比供应商指定的SQLState更为精确。 ErrorCode的转换是基于被保存在SQLErrorCodes

型的JavaBean中的值。这个类是由SQLErrorCodesFactory建立并填充的,就像它的名字说明的, SQLErrorCodesFactory是一个基于一个名为"sql-error-codes.xml"的配置文件

的内容建立SQLErrorCodes的工厂。这个文件带有供应商的码一起发布,并且是基于DatabaseMetaData信息中的DatabaseProductName,适合当前数据库的码会被使用。

SQLErrorCodeSQLExceptionTranslator使用以下的匹配规则:

使用子类实现的自定义转换。要注意的是这个类本身就是一个具体类,并可以直接使用,在这种情况下,将不使用这条规则。

使用ErrorCode的匹配。在默认情况下,ErrorCode是从SQLErrorCodesFactory得到的。它从classpath中寻找ErrorCode,并把从数据库metadata中得到的数据库名字嵌入它们。

如果以上规则都无法匹配,那么是用SQLStateSQLExceptionTranslator作为默认转换器。

SQLErrorCodeSQLExceptionTranslator可以使用以下的方式继承:

public class MySQLErrorCodesTransalator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
if (sqlex.getErrorCode() == -12345)
return new DeadlockLoserDataAccessException(task, sqlex);
return null;
}
}
在这个例子中,只有特定的ErrorCode'-12345'被转换了,其他的错误被简单的返回,由默认的转换实现来处理。要使用自定义转换器时,需要通过setExceptionTranslator 方法

将它传递给JdbcTemplate,并且在所有需要使用自定义转换器的数据访问处理中使用这个JdbcTemplate 下面是一个如何使用自定义转换器的例子:

// create a JdbcTemplate and set data source
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
// create a custom translator and set the datasource for the default translation lookup
MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator();
tr.setDataSource(dataSource);
jt.setExceptionTranslator(tr);
// use the JdbcTemplate for this SqlUpdate
SqlUpdate su = new SqlUpdate();
su.setJdbcTemplate(jt);
su.setSql("update orders set shipping_charge = shipping_charge * 1.05");
su.compile();
su.update();
这个自定义的转换器得到了一个数据源,因为我们仍然需要默认的转换器在sql-error-codes.xml中查找ErrorCode。

2.4. 执行Statement要执行一个SQL,几乎不需要代码。你所需要的全部仅仅是一个数据源和一个JdbcTemplate。一旦你得到了它们,你将可以使用JdbcTemplate提供的大量方便

的方法。下面是一个例子,它显示了建立一张表的最小的但有完整功能的类。

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAStatement {
private JdbcTemplate jt;
private DataSource dataSource;

public void doExecute() {
jt = new JdbcTemplate(dataSource);
jt.execute("create table mytable (id integer, name varchar(100))");
}

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
2.5. 执行查询除了execute方法,还有大量的查询方法。其中的一些被用来执行那些只返回单个值的查询。也许你需要得到合计或者某一行中的一个特定的值。如果是这种情况

,你可以使用queryForInt, queryForLong或者queryForObject。后者将会把返回的JDBC类型转换成参数中指定的Java类。如果类型转换无效,那么将会抛出一个

InvalidDataAccessApiUsageException。下面的例子有两个查询方法,一个查询得到int,另一个查询得到String。

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class RunAQuery {
private JdbcTemplate jt;
private DataSource dataSource;

public int getCount() {
jt = new JdbcTemplate(dataSource);
int count = jt.queryForInt("select count(*) from mytable");
return count;
}

public String getName() {
jt = new JdbcTemplate(dataSource);
String name = (String) jt.queryForObject("select name from mytable", java.lang.String.class);
return name;
}

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
除了得到单一结果的查询方法之外,还有一些方法,可以得到一个包含查询返回的数据的List。其中最通用的一个方法是queryForList,它返回一个List,其中每一项都是一个

表示字段值的Map。你用下面的代码在上面的例子中增加一个方法来得到一个所有记录行的List:

public List getList() {
jt = new JdbcTemplate(dataSource);
List rows = jt.queryForList("select * from mytable");
return rows;
}
返回的List会以下面的形式: [{name=Bob, id=1}, {name=Mary, id=2}].

2.6. 更新数据库还有很多更新的方法可以供你使用。我将展示一个例子,说明通过某一个主键更新一个字段。在这个例子里,我用了一个使用榜定参数的SQL Statement。大多

数查询和更新的方法都有这个功能。参数值通过对象数组传递。

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAnUpdate {
private JdbcTemplate jt;
private DataSource dataSource;

public void setName(int id, String name) {
jt = new JdbcTemplate(dataSource);
jt.update("update mytable set name = ? where id = ?", new Object[] {name, new Integer(id)});
}

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}

3. 控制如何连接数据库
3.1. DataSourceUtils这个辅助类提供从JNDI获取连接和在必要时关闭连接的方法。它支持线程绑定的连接,比如被用于DataSourceTransactionManager。

注意:方法getDataSourceFromJndi用以针对那些不使用BeanFactory或者ApplicationContext 的应用。对于后者,建议在factory中配置你的bean甚至JdbcTemplate的引用:使

用JndiObjectFactoryBean可以从JNDI中得到一个DataSource 并将DataSource的引用给别的bean。要切换到另一个DataSource 就仅仅是一个配置的问题:你甚至可以用一个非

JNDI的DataSource来替换 FactoryBean的定义!

3.2. SmartDataSource实现这个接口的类可以提供一个关系数据库的连接。它继承javax.sql.DataSource接口,使用它可以知道在一次数据库操作后,是否需要关闭连接。如果

我们需要重用一个连接,那么它对于效率是很有用的。

3.3. AbstractDataSource这个实现Spring的DataSource的抽象类,关注一些"无趣"的东西。如果你要写自己的DataSource实现,你可以继承这个类。

3.4. SingleConnectionDataSource这个SmartDataSource的实现封装了单个在使用后不会关闭的连接。所以很明显,它没有多线程的能力。

如果客户端代码想关闭这个认为是池管理的连接,比如使用持久化工具的时候,需要将suppressClose设置成true。这样会返回一个禁止关闭的代理来接管物理连接。需要注意

的是,你将无法将不再能将这个连接转换成本地Oracle连接或者类似的连接。

它的主要作用是用来测试。例如,它可以很容易的让测试代码脱离应用服务器测试,而只需要一个简易的JNDI环境。和DriverManagerDataSource相反,它在所有的时候都重用一

个连接,以此来避免建立物理连接过多的消耗。

3.5. DriverManagerDataSource这个SmartDataSource的实现通过bean的属性配置JDBC驱动,并每次都返回一个新的连接。

它对于测试或者脱离J2EE容器的独立环境都是有用的,它可以作为不同的ApplicationContext中的数据源bean,也可以和简易的JNDI环境一起工作。被认为是池管理的

Connection.close()操作的调用只会简单的关闭连接,所以任何使用数据源的持久化代码都可以工作。

3.6. DataSourceTransactionManager这个PlatformTransactionManager的实现是对于单个JDBC数据源的。从某个数据源绑定一个JDBC连接到一个线程,可能允许每个数据源一个

线程连接。

应用程序的代码需要通过DataSourceUtils.getConnection(DataSource)取得JDBC连接代替 J2EE标准的方法DataSource.getConnection。这是推荐的方法,因为它会抛出

org.springframework.dao中的unchecked的异常代替SQLException。 Framework中所有的类都使用这种方法,比如JdbcTemplate。如果不使用事务管理,那么就会使用标准的方

法,这样他就可以在任何情况下使用。

支持自定义的隔离级,以及应用于适当的JDBC statement查询的超时。要支持后者,应用程序代码必须使用JdbcTemplate或者对每一个创建的statement 都调用

DataSourceUtils.applyTransactionTimeout。

因为它不需要容器支持JTA,在只有单个资源的情况下,这个实现可以代替JtaTransactionManager。如果你坚持需要的连接的查找模式,两者间的切换只需要更换配置。不过

需要注意JTA不支持隔离级。[/size]
阅读更多
个人分类: 我的收藏
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭