Spring笔记6---JDBC的集成

一. 无论采用何种持久化技术,JDBC始终都是它们的支撑件,如果没有底层的JDBC,ORM是没有办法操控RDBMS的。

  Spring 对 JDBC的集成主要包含了:

  1.提供各种模板类,简化了对RDBMS的CRUD操作,这些模板包括                    

     JdbcTemplate,NamedParameterJdbcTemplate,SimpleJdbcTemplate等等。这些模板类都是线程安全的,即在多线程环境中能始终保持一致的行为,尤其是Web环境。

 

  2.提供了各种DaoSupport辅助类,进一步简化了模板类的使用。 比如

     JdbcDaoSupport,NamedParameterJdbcDaoSupport,SimpleJdbcDaoSupport。

 

  3.提供了SimpleJdbcInsert和SimpleJdbcCall辅助类。

 

  4.提供了各种数据源类,借助DI容器实现了DataSource的灵活配置。

 

  5.提供了更多高级辅助类,比如:

      将JDBC CRUD操作建模成JAVA对象、LOB处理、操控存储过程、大批量数据处理、获得和生成主键等。

 

二. JdbcTemplate是JDBC集成的核心类,简化了JDBC API的使用。

 

1. 不用重新写调用java.sql.Connection对象的语句。

 

 

jdbcTemplate.execute(new ConnectionCallback(){
   public Object doInConnectin(Connection con) throws SQLException,DataAccessException{
      log.info(con.getMetaData().getDriverName);
      return null;
  }
});

    *Spring 自身会从数据源中获得一Connection对象,并传入到doInConnection回调方法中。

 

2. 有若干方法用于执行静态SQL语句,即使用java.sql.Statement API.

 

 

..... execute(....) throws ....
..... query(.....) throws .....
..... queryForObject (.....) throws .....
..... queryForList (.....) throws .....
..... queryForMap (.....) throws .....
..... queryForRowSet (.....) throws .....
int  update (.....) throws .....
int[]  batchUpdate (.....) throws .....

   *结果集处理接口:ResultSetExtractor,RowCallbackHandler,RowMapper(线程安全,用的最多,包含许多子集,例如ColumnMapRowMapper,BeanPropertyRowMapper).

 

3. 有若干方法用于执行动态sql语句,即使用java.sql.PrepareStatement API.

 

 

..... execute(....) throws ....
..... query(.....) throws .....
..... queryForObject (.....) throws .....
..... queryForList (.....) throws .....
..... queryForMap (.....) throws .....
..... queryForRowSet (.....) throws .....
int  update (.....) throws .....
int[]  batchUpdate (.....) throws .....
 

 *大多方法都含有Object[] args参数,即这些方法都会采用预编译语句,执行效率更高。Hibernate就大量采用了预编译语句。

 

4. 有若干方法用于执行RDBMS的存储操作,即使用java.sql.CallableStatement API.

 

 

..... execute(....) throws ....
Map call(CallablestatementCreator csc,List declaredParameters) throws DataAccessException;
 

三. JdbcDaoSupport是建立在JdbcTemplate基础上的支持类,其辅助类会自动创建相应模板类。

 

    在使用JdbcDaoSupportImpl实现类之前,需要配置示例如下:

 

 

<bean id="jdbcDaoSupport" class="org.springframework.samples.JdbcDaoSupportImpl">
<property name="dataSource" ref="dataSource"/>
<bean>

 

    或者直接提供一个JdbcTemplate给JdbcDaoSupportImpl,如下配置:

 

 

<bean id="jdbcDaoSupport" class="org.springframework.samples.JdbcDaoSupportImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
<bean>
 

四. SimpleJdbcDaoSupport及NamedParameterJdbcDaoSupport;NamedParameterJdbcTemplate及     SimpleJdbcTemplate的相应支持类。

 

 例如:SimpleJdbcInsert和SimpleJdbcCall支持辅助类,前者用于操作表,后者用于操作存储过程或函数,它们的某些功能比SimpleJdbcTemplate更为强大,而且都是线程安全的。

 

*.SimpleJdbcInsert的usingGeneratedKeyColumns()能自动生成主键id,例子如下:

 

 

ownerInsert = new SimpleJdbcInsert(dataSource).withTableName("owner").usingGeneratedKeyColumns("id");

param = new HashMap<String,Object>();
param.put("fistname","Nedd");
param.put("lastname","Want");

log.info(ownerInsert.executeAndReturnKey(param));

 

五.内置的DataSource继承。

 

1.基于测试目的DriverManagerDataSource,SimpleDriverDataSource和SingleConnectionDataSource.

 

2.Apache DBCP数据源。(实现了连接池)

 

 

<bean id = "dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" />
 

  上面的bean中配置了destroy-method属性,当销毁Spring DI容器时,BasicDataSource对象的close()方法便会被触发,从而优雅地关闭掉底层连接池持有的数据库连接。

 

3. 其他数据源。

 

   *Java EE容器内置的数据源:能从JNDI树上查找到已经注册的DataSource实现。示例如下:

 

 

 <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic">
 

 

 

   *LazyConnectionDataSourceProxy:会代理其他数据源。

 

   *TransactionAwareDataSourceProxy:能使得当前的CRUD操作能够参与到Spring的受管事务中。如果当前不存在活动事务,则会使用默认行为,通常在开发中很少用到。

 

   *UserCredentialsDataSourceAdapter: 将连接数据库的用户凭证信息(用户名,密码)传入到getConnection(String username,String password)方法中。一旦配置好后,客户每次获得数据库连接,都会调用getConnection方法。

 

   *IsolationLevelDataSourceAdapter:启用isolationLevelName属性能指定隔离级别。继承于UserCredentialsDataSourceAdapter。

 

   *WebSphereDataSourceAdapter:继承于IsolationLevelDataSourceAdapter,会从WebSphere容器获得数据源。并借助WebSphere专有的api达到启用自定义隔离级别的目的。

 

   *IsolationLevelDataSourceRouter: 会根据当前Spring受管事务启用的隔离级别来选定合适的DataSource数据源。

 

六. 将JDBC操作建模成Java对象。

 

   1. SqlUpdate辅助类

   2. UpdatableSqlQuery辅助类

   3. MappingSqlQuery辅助类 :能将SQL select查询结果集自动转换成领域对象集合,返回给调用者。其子类必须实现mapRow()方法,它内置大量的execute()和findObject()方法。

   4. SqlFunction辅助类。

 

七. 与存储过程交互。

 

   1. JdbcTemplate针对存储过程提供的支持。

 

 

Object execute(CallableStatementCreator csc, CallableStatmentCallback action) throws DataAccessException;


Object execute(String callString, CallableStatmentCallback action) throws DataAccessException;


Object execute(CallableStatementCreator csc, List declaredParameters) throws DataAccessException;
 

 如果使用了CallableStatmentCallback回调接口,则要实现doInCallableStatement()方法。

 

   2. StoredProcedure辅助类。

 

   3. SimpleJdbcCall 辅助类。重要辅助类。

 

 

SimpleJdbcCall sjc = new SimpleJdbcCall(ds);
sjc.withProcedureName("show_sal");

sqlParameterSource sps = new MapSqlParameterSource().addValue("enames","SMITH").addValue("empnos",7369);

log.info(sjc.execute(sps));
 

以上开发者直接将存储过程名告知withProcedureName()方法,并将参数提供给了execute方法。

 

如果需要制定传入传出参数,可借助SimpleJdbcCall内置的 declareParameters()方法,示例如下,SqlParameters用于指定传入参数,而SqlOutParameters用于指定传出参数:

 

 

SimpleJdbcCall sjc = new SimpleJdbcCall(ds).withProcedureName("show_sal");

sjc.declareParameters(new SqlParameter("enames",Types.VARCHAR)).
declareParameters(new SqlParameter("empnos",Types.VARCHAR)).declareParameters(new SqlParameter("sals",Types.FLOAT)).withoutProcedureColumnMetaDataAccess();

sqlParameterSource sps = new MapSqlParameterSource().addValue("enames","SMITH").addValue("empnos",7369);

log.info(sjc.execute(sps));
 

一旦启用了declareParameters()方法,withoutProcedureColumnMetaDataAccess()方法也就该被调用,从而避免再去从底层jdcb驱动获得RDBMS相关元数据,比如存储过程的输入输出参数。

 

 

八. 处理大批量数据。

 

1. JdbcTemplate内置的batchUpdate()方法。

 

 

int[] batchUpdate(String[] sql) throws DataAccessException;

int[] batchUpdate(String[] sql,BatchPreparedStatementSetter pss) throws DataAccessException;

 

 

例如:

 

 

final int[] no= new int[]{324,543,1234,421,32142,3212};

jdbcTemplate.batchUpdate("update emp set sal = ? where empno = ?",new BatchPreparedStatementSetter(){

 public void setValues(PreparedStatement ps,int i) throws SQLException{
  ps.setInt(1,no[i]);
  ps.setFloat(2,no[i]);

}

  public int getBatchSize(){
    return no.length;
}

}){

}
 

 

2. SimpleJdbcTemplate内置的batchUpdate方法。

 

int[] batchUpdate(String sql,Map[] batchValues) ;

int[] batchUpdate(String sql,SqlParameterSource[] batchArgs) ;

int[] batchUpdate(String sql,List<Object> batchArgs);

int[] batchUpdate(String sql,List<Object> batchArgs,int[] argTypes);
 

List对象作为参数示例:

 

SimpleJdbcTemplate sjt = xxx.getBean("simpleJdbcTemplate");

List<Object[]> paramList = new ArrayList<Object[]>();
paramList.add(new Integer[]{2342,23423});
paramList.add(new Integer[]{2342,23423});
paramList.add(new Integer[]{2342,23423});
paramList.add(new Integer[]{2342,23423});
paramList.add(new Integer[]{2342,23423});

sjt.batchUpdate("update emp set sal = ? where empno = ?",paramList);

 最后一sql语句也可以将sql类型告知给batchUpdate(),如下:

 

sjt.batchUpdate("update emp set sal = ? where empno = ?",paramList,new int[]{Types.INTEGER,Types.FLOAT});
 

 

九. 基于JDBC的LOB集成支持。

 

为操控LOB字段,Spring能应用需要启用LobHandler继承链。当前,Spring内置了DefaultLobHandler和OracleLobHandler实现类。通常除了Oracle数据库外,开发者可直接配置DefaultLobHandler实例即可。

如果用OracleLobHandler,还的为它配置NativeJdbcExtractor对象,以获得底层数据库的原生连接。

 

<bean id="imageDatabase" class="xxxxxxxxxxxxx" p:dataSource-ref="dataSource" p:lobHandler-ref="defaultLobHandler"/>

<bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" p:dataSource-ref="dataSource" lazy-init="true"/>
 

1. 读取

 

借用了AbstractLobStreamingResultSetExtractor回调类,这里调用了LobHandler的getBlobAsBinaryStream()方法,即将Blob转换成了二进制流。

 

@Transactional(readOnly=true)
public void streamImage(final name,final OutputStream contentstream) throws DataAccessException{
   getJdbcTemplate().query{
        "select content from imagedb where image_name=?",new Object[]{name}, new AbstractLobStreamingResultSetExtractor(){
         protected void handleNoRowFound() throws LobRetrievalFailureException{
              throw new EmptyResultDataAccessException(
              "image with name '"+name+"' not found in database",1);

           }

    public void streamData(ResultSet rs) throws SQLException,IOException{
   InputStream is = lobHandler.getBlobAsBinaryStream(rs,1);
   if(is!=null){
      FileCopyUtils.copy(is.contentstream);

  }
  
}
     }
  }
}
 

2.存储

 

要使用LobHandler内置的LobCreator对象。需要启用AbstractLobCreatingPreparedStatementCallback回调类。借助LobCreator的setBlobAsBinaryStream()方法能将输入流存储到Blob字段中。setClobAsString()方法能将大文本块存储到Clob字段中。

 

@Transactional
public void storeImage(
  final String name, 
  final InputStream contentstream,
  final int contentLength,
  final String description
) throws DataAccessException{

  getJdbcTemplate().execute(

"insert into imagedb(image_name,content,description) values (?,?,?)",new AbstractLobCreatingPreparedStatementCallback(this.lobHandler)){
 protected void setValues( 
               PreparedStatement ps, LobCreator lobCreator
       ) throws SQLException{
         ps.setString(1,name);
         lobCreator.setBlobAsBinaryStream(
             ps,2,contentstream,contentlength);
         lobCreator.setClobAsString(ps,3,description);
        }
        }

     );

}
 

十. 获得和生成主键。

 

KeyHolder kh = new GeneratedKeyHolder();

int update(PreparedStatementCreator psc,KeyHolder generatedKeyHolder) throws DataAccessException;
 

 

十一. 还需要注意的。

 

1. 要合理设置Statement的fetchSize大小,即JdbcTemplate的fetchSize变量取值。(查询时,权衡时间和空间的优化)。

 

2. 大批量存储时,开发者要控制每批数据的数据量。设置batchSize的大小。

 

3. 尽量采用PreparedStatement操控数据库,而不是Statement。

 

4. 充分挖掘JdbcTemplate,NamedParameterJdbcTemplate,SimpleJdbcTemplate,SimpleJdbcInsert,SimpleJdbcCall等辅助类的功能。不要直接使用Connection对象操控。

 

 

 

 

 

 

 

你可能感兴趣的:(spring,sql,bean,jdbc,数据挖掘)