Spring 基于Template的JDBC使用

JDBC的尴尬

      JDBC作为Java平台关系数据库的标准API,其成功是有目共睹的。几乎所有Java平台的数据访问,都直接地或者间接地使用了JDBC,他是整个Java平台面向关系数据库进行数据访问的基石。作为一个标准,无疑JDBC是成功的。但是要说JDBC在实际的使用过程中也是受人欢迎的,则不尽然了。JDBC标准主要面向较为底层的数据库操作,所有在设计过程中,比较贴近底层以提供尽可能多的功能特色。从这个角度来说,JDBC API的设计无可厚非。可是,过于贴近底层的API设计,对开发人员的使用来说就不一定是好事了。即使执行简单的查询或者更新,开发人员也要按照API的规矩写上一大堆雷同的代码。除了API的使用,JDBC规范在制定数据访问异常处理时,只通过一个SQLException类型来包括一切数据访问异常情况,将SQLException声明为checked exception,且SQLException没有采用将具体的异常情况子类化,以进一步抽象不同的数据访问异常情况,而是采用ErrorCode的方式来区分数据访问过程中所出现的不同异常情况。但是,JDBC规范却把ErrorCode的规范制定留给了各个数据库提供商,这导致不同提供商提供的数据库对应不同的ErrorCode。 

       针对JDBC API在使用过程中容易出错,使用繁琐的问题,以及SQLException对数据访问异常处理能力不足尚待改进的情况,Spring框架提供了一套针对JDBC使用方面的框架类,以改进JDBC API使用过程中的种种不便甚至不合理之处,帮助我们进一步提高开发过程中使用JDBC进行数据访问的开发效率。 

JDBCTemplate的诞生

       为了解决JDBC API在实际使用中的尴尬局面,Spring框架提出了org.springframework.jdbc.core.JdbcTemplate作为数据访问的Helper类。JdbcTemplate是整个Spring数据抽象层提供的所有JDBC API最佳实践的基础,框架内其他更加方便的Helper类以及更高层次的抽象,全部构建于JdbcTemplate之上,理解JdbcTemplate,就理解了Spring框架JDBC API最佳实践的核心。概括的讲,JdbcTemplate主要关注如下2个事情:

  • 封装所有基于JDBC的数据访问代码,以统一的格式和规范来使用JDBC API。所有基于JDBC的数据访问需求现在全部通过JdbcTemplate进行,从而避免了让繁琐易错的基于JDBC API的数据访问代码散落于系统各处。
  • 对SQLException所提供的异常信息在框架内进行统一转译,将基于JDBC的数据访问异常纳入Spring自身的异常层次体系中,统一了数据接口的定义,简化了客户端代码对数据访问异常的处理。

下面看看JdbcTemplate的实现结构图如下:

org.springframework.jdbc.core.JdbcOperations接口定义界定了JdbcTemplate可以使用的JDBC操作集合,该接口提供的操作声明,从查询到更新无所不包,而JdbcTemplate的直接父类是org.springframework.jdbc.support.JdbcAccessor,这是一个抽象类,主要为子类提供一些公用的属性如下: 

DataSource。 

javax.sql.DataSource是JDBC2.0之后引入的接口定义,用来替代基于java.sql.DriverManager的数据库连接创建方式。DataSource的角色可以看作JDBC的连接工厂(ConnectionFactory),具体实现可以引入对数据库连接的缓冲池以及分布式事务支持。所以,Spring数据访问层对数据库资源的访问,全部建立在javax.sql.DataSource标准接口之上,通过超类JdbcAccessor,JdbcTemplate自然也是以此为基准。

1).DataSource的种类 

  •  简单的DataSource实现类

org.springframework.jdbc.datasource.DriverManagerDataSource

  • 拥有连接缓冲池的DataSource实现类

org.apache.commons.dbcp.BasicDataSource

com.mchange.v2.c3p0.ComboPooledDataSource

com.alibaba.druid.pool.DruidDataSource

  • 支持分布式事务的DataDource实现类

 javax.sql.XADataSource

2).DataSource的访问方式

  •  本地DataSource访问

 代码方式:

BasicDataSource dataSourc = new BasicDataSource();
dataSource.setDrierClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/mysql?useUnicode=true");
dataSource.setUsername("xxx");
dataSource.setPassword("password");

 IoC注入xml:

<bean id="dataSource"
		class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName"
			value="org.gjt.mm.mysql.Driver">
		</property>
		<property name="url"
			value="jdbc:mysql://221.11.45.91:3306/new_film?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=UTF8">
		</property>
		<property name="username" value="root"></property>
		<property name="password" value="root"></property>
		<property name="maxActive" value="25" />
		<property name="maxWait" value="25" />
		<!-- 如果设为true则tomcat自动检查恢复重新利用,没有正常关闭的Connection.(默认是false) -->
		<property name="removeAbandoned" value="true"></property>
		<!-- 设定连接在多少秒内被认为是放弃的连接,即可进行恢复利用 -->
		<property name="removeAbandonedTimeout" value="10"></property>
		<!-- 输出回收的日志,可以详细打印出异常从而发现是在那里发生了泄漏 -->
		<property name="logAbandoned" value="true"></property>
</bean>
  • 远程DataSource访问

 在Tomcat 配置文件server.xml(apache-tomcat-9.0.8\conf)中

<Resource 
        name="jdbc/Sybase_claims"
        auth="Container" 
        type="javax.sql.DataSource"
        maxActive="100" 
        maxIdle="30" 
        maxWait="10000"
        username="test" 
        password="test"
        driverClassName="com.sybase.jdbc4.jdbc.SybDriver"
        url="jdbc:sybase:Tds:127.0.0.1:4101?ServiceName=db_claims"/>

 Ioc注入

<bean id="dataSource"
		class="org.springframework.jndi.JndiObjectFactoryBean"
		destroy-method="close">
		<property name="jndiName" value="java:comp/env/jdbc/Sybase_claims" />
</bean>

SQLExceptionTranslator。 

JdbcTemplate直接操作JDBC API,所以它需要捕获在此期间可能发生的SQLException并进行处理,处理的宗旨是将SQLException转译到Spring的数据访问异常层次体系,以统一数据访问异常的处理方式。JdbcTemplate将SQLException转译到Spring数据访问异常层次体系这部分工作转交给了org.springframework.jdbc.support.SQLExceptionTranslator接口来完成,该接口有两个主要实现类,如下图:

SQLErrorCodeSQLExceptionTranslator会基于SQLException所返回的ErrorCode进行异常转译。通常情况下,根据各数据库提供商所提供的ErrorCode进行分析要比基于SQLState的方式准确得多。默认情况下,JdbcTemplate采用SQLErrorCodeSQLExceptionTranslator进行SQLException的转译工作。只有当通过ErrorCode方式无法提供足够信息的时候,才会转而求助于SQLStateSQLExceptionTranslator。SQLStateSQLExceptionTranslator根据SQLException.getSQLState()所返回的信息进行异常转译,虽然基于SQLState的方式理论上应该是最通用的(因为SQLState要求符合XOPEN SQLstate规范或者SQL99规范),但各数据库厂商在执行上存在差异。更多时候偏向于基于ErrorCode的方式。

 SQLErrorCodeSQLExceptionTranslator进行异常转译的流程大体上如以下所述:

1).SQLStateSQLExceptionTranslator中定义了自定义异常转译方式: 

DataAccessException customTranslate(String task,String sql,SQLException sqlEx) 

程序首先会检查该自定义异常转译方法是否能够对当前传入的SQLException进行转译,如果可以直接返回转译后的DataAccessException类型;如果返回为null,则表示当前方法无法对传入的SQLException进行转译,程序流程进入下一步,寻求其他方式进行转译。

2).如果程序运行在Java 6以后,SQLStateSQLExceptionTranslator会尝试让SQLExceptionSuclassTranslator进行异常的转译。对于使用Java 6以前的版本,这一步不做处理。

3).使用org.springframework.jdbc.support.SQLErrorCodesFactory所加载的SQLErrorCodes进行异常转译,SQLErrorCodesFactory加载SQLErrorCodes的流程是:

  • 加载位于Spring发布jar包中org/springframework/jdbc/support/sql-error-codes.xml路径下的记载了各个数据库提供商ErrorCode的配置文件,提供相应的SQLErrorCodes。
  • 如果发现当前应用的Classpath的根路径下存在名称为sql-error-codes.xml的配置文件,则加载该文件内容,并覆盖默认的ErrorCode定义。

4).如果基于ErrorCode的异常转译还是搞不定的话,SQLStateSQLExceptionTranslator将求助于SQLStateSQLExceptionTranslator,最后使用基于SQLState的方式进行SQLException到Spring数据访问异常体系的转译工作。

可以通过以下两步自定义异常:

  • 扩展SQLStateSQLExceptionTranslator以实现其子类,覆写 customTranslate方法
  • 提供sql-error-codes.xml自定义配置,在Classpath的根路径下放置名称为 sql-error-codes.xml的配置文件,格式需与默认的org/springframework/jdbc/support/sql-error-codes.xml文件格式相同

JDBCTemplate的其他类型 

NamedParameterJdbcTemplate

通常执行sql含有?的占位符时,一般使用的是PreparedStatement和CallableStatement来处理,如果是以下这样的sql语句,使用命名的参数符号作为占位符的语句

select * from user where id=:id 

我们可以通过NamedParameterJdbcTemplate来执行,使用它,不是通过Object[]数组的形式为相应占位符提供参数值,而是通过org.springframework.jdbc.core.namedparam.SqlParameterSource接口,该接口定义有两个实现类,分别是org.springframework.jdbc.core.namedparam.MapSqlParameterSource和org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource。 

  • MapSqlParameterSource实现,key的值必须和sql属性名一致
DataSource dataSource = ...;
final String sql = "select count(1) from images where filename=:filename and desc=:desc"; 
NamedParameterJdbcTemplate npJdbcTemplate = new NamedParameterJdbcTemplate(datasource);
SqlParameterSource parameterSource = new MapSqlParameterSource("filename","image.jpg");
parameterSource.addValue("desc","this is a pictrue");
npJdbcTemplate.queryForInt(sql,parameterSource);
  • BeanPropertySqlParameterSource实现,必须pojo的属性和sql属性名一致
public class Image{
    private int id;
    private String filename;
    private byte[] entity;
    private String desc;

    //对应的setter和getter方法
}
DataSource dataSource = ...;
final String sql = "select count(1) from images where filename=:filename and desc=:desc"; 
NamedParameterJdbcTemplate npJdbcTemplate = new NamedParameterJdbcTemplate(datasource);
Image image = new Image();
image.setFilename("","");
image.setDesc("","");
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(image);
npJdbcTemplate.queryForInt(sql,parameterSource);

 SimpleJdbcTemplate 

SimpleJdbcTemplate集JdbcTemplate和namedParameterJdbcTemplate的功能于一身,并且在这二者上添加了Java 5之后引入的动态参数(varargs)、自动拆箱解箱(autoboxing)和泛型(generic)的支持。 

DataSource dataSource = ...;
String sql = "select * from images where filename=:filename and desc=:desc"; 
SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(datasource);
final LobHandler lobHandler = new DefaultLobHandler();
ParameterizedRowMapper<Imager> rowmapper = new ParameterizedRowMapper<Imager>(){
    public Imager mapRow(ResultSet rs,int row)throws SQLException{
        Imager imager = new Imager();
        imager.setFilename(rs.getInt(1));
        imager.setFilename(rs.getString(2));
        imager.setEntity(lobHandler.getBlobAsBytes(rs,3));
        imager.setDesc(rs.getString(4));
        return imager;
    }
}
Imager imager = simpleJdbcTemplate.queryForObject(sql,rowMapper,"","this is a pictrue");

使用DataSourceUtils进行Connection的管理

Connection conn = DataSourceUtils.getConnection(datasource); 

使用JdbcDaoSupport封装JDBC API的DAO层 

    Spring的JdbcDaoSupport重构了DAO实现类,将DataSource和JdbcTemplate全部封装。所以,DAO直接继承JdbcDaoSupport就继承了DataSource和JdbcTemplate。

发布了203 篇原创文章 · 获赞 6 · 访问量 4498

猜你喜欢

转载自blog.csdn.net/weixin_42073629/article/details/105314394
今日推荐