《Spring 2.0技术手册》 读书笔记七-Spring的DAO框架(2)-JDBC支持

在上篇笔记中,说道使用JDBC存取数据库时,流程大体相同,反复使用较为繁琐。因此Spring采用了Template-Callback模式来简化JDBC使用时的流程。

Spring提供了org.springframework.jdbc.core.JdbcTemplate类,它被设计为线程安全,它提供的一些操作方法封装了JDBC流程。使用JdbcTemplate,首先需要一个DataSource对象作为构造对象:JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource)。然后就可以使用JdbcTemplate提供的update/execute/query等系列方法执行具体存取操作了。省去了自己建立连接、创建statement等。

DAO实现类中需有注入DataSource的方法,此方法使用dataSource初始化JdbcTemplate。

  1. private JdbcTemplate jdbcTemplate;  
  2. public void setDataSource(DataSource dataSource){  
  3.     jdbcTemplate=new JdbcTemplate(dataSource);  
  4. }  
  5. /*直接使用JdbcTemplate进行数据存取*/  
  6. public void create(){  
  7. jdbcTemplate.execute("create table user(id int primary key, name varchar(20))");  
  8. }  

Bean定义文件中进行配置:

[xhtml]  view plain copy
  1. <bean id="userDAO" class="org.spring.dao.UserDAO">  
  2.     <property name="dataSource" ref="dataSource"></property>  
  3. </bean>  
 

接着对JdbcTemplate的常用方法进行分类介绍如下:

1. 执行静态陈述

public void execute(final String sql):执行DDL statement,包括create table/index,alter table/index,drop table/index。

2. 更新表格:DML statement-insert,update,delete。使用update系列函数

[java]  view plain copy
  1. public int update(final String sql);  
  2. public int update(String sql, Object... args);  
  3. jdbcTemplate.update(  
  4.     "insert into user(id,name) values(?,?)",   
  5.     new Object[]{id,name});  
  6. public int update(PreparedStatementCreator psc);  
  7. //org.springframework.jdbc.core.PreparedStatementCreator接口定义  
  8. public interface PreparedStatementCreator {  
  9.     /**  
  10.      * Create a statement in this connection. Allows implementations to use 
  11.      * PreparedStatements. The JdbcTemplate will close the created statement. 
  12.      */  
  13.     PreparedStatement createPreparedStatement(Connection con) throws SQLException;  
  14. }  
  15. //使用例子  
  16. jdbcTemplate.update(new PreparedStatementCreator(){  
  17.     public PreparedStatement createPreparedStatement(Connection con)  
  18.             throws SQLException {  
  19.         String sql="insert into user(name) values(?)";  
  20.         PreparedStatement preStmt=con.prepareStatement(sql);  
  21.         preStmt.setString(1, name);  
  22.         return preStmt;  
  23.     }  
  24. });  
  25. //与update(PreparedStatementCreator psc)互补  
  26. public int update(String sql, PreparedStatementSetter pss);  
  27. //org.springframework.jdbc.core.PreparedStatementSetter定义  
  28. public interface PreparedStatementSetter {  
  29.     /**  
  30.      * Set parameter values on the given PreparedStatement. 
  31.      * @param ps the PreparedStatement to invoke setter methods on 
  32.      */  
  33.     void setValues(PreparedStatement ps) throws SQLException;  
  34. }  
  35. //使用例子  
  36. jdbcTemplate.update(  
  37.     "insert into user(name) values(?)",  
  38.     new PreparedStatementSetter(){  
  39.         public void setValues(PreparedStatement ps)throws SQLException {  
  40.             ps.setString(1, name);                        
  41.         }}  
  42.     );  
  43. /* 
  44. 批处理更新语句:首先,实现org.springframework.jdbc.core.BatchPreparedStatementSetter接口。 
  45. 然后,使用JdbcTemplate的方法public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) 
  46. */  
  47.     public int[] insertUsers(List<User> us){  
  48.         final List<User> users=us;  
  49.         String sql1="insert into user (name) values (?)";  
  50.         BatchPreparedStatementSetter setter =  
  51.             new BatchPreparedStatementSetter(){  
  52.             /**  
  53.              * Return the size of the batch. 
  54.              * @return the number of statements in the batch 
  55.              */  
  56.                 public int getBatchSize() {  
  57.                     return users.size();  
  58.                 }  
  59.                 /**  
  60.                  * Set parameter values on the given PreparedStatement. 
  61.                  * @param ps the PreparedStatement to invoke setter methods on 
  62.                  * @param i index of the statement we're issuing in the batch, starting from 0 
  63.                  */  
  64.                 public void setValues(PreparedStatement ps, int i)  
  65.                         throws SQLException {  
  66.                     User user=users.get(i);  
  67.                     ps.setString(1, user.getName());                      
  68.                 }  
  69.         };  
  70.         return jdbcTemplate.batchUpdate(sql1, setter);  
  71.     }  
 

3. 查询.使用queryForXXX系列

  1. public int queryForInt(String sql);  
  2. public <T> T queryForObject(String sql, Object[] args, Class<T> requiredType);  
  3. //每一个Map代表一行数据,key为列名,object为值  
  4. public List<Map<String, Object>> queryForList(String sql);  
  5. /*对查询数据进行处理再传回。 
  6. 使用org.springframework.jdbc.core.RowCallbackHandler接口 
  7. */  
  8. public interface RowCallbackHandler {  
  9.     /** 
  10.      * Implementations must implement this method to process each row of data 
  11.      * in the ResultSet. This method should not call <code>next()</code> on 
  12.      * the ResultSet; it is only supposed to extract values of the current row. 
  13.      * <p>Exactly what the implementation chooses to do is up to it: 
  14.      * A trivial implementation might simply count rows, while another 
  15.      * implementation might build an XML document. 
  16.      * @param rs the ResultSet to process (pre-initialized for the current row) 
  17.      * @throws SQLException if a SQLException is encountered getting 
  18.      * column values (that is, there's no need to catch SQLException) 
  19.      */  
  20.     void processRow(ResultSet rs) throws SQLException;  
  21. }  
  22. /*使用JdbcTemplate的查询函数*/   
  23. public void query(String sql, Object[] args, RowCallbackHandler rch);  
  24. //例子  
  25.     public User find1(Integer id){  
  26.         final User user=new User();  
  27.         jdbcTemplate.query(  
  28.                 "select * from user where id=?",  
  29.                 new Object[]{id},  
  30.                 new RowCallbackHandler(){  
  31.                     public void processRow(ResultSet rs) throws SQLException {  
  32.                         user.setName(rs.getString("name"));  
  33.                     }  
  34.                 });  
  35.         return user;  
  36.     }  

对查询结果进行封装-ORM(对象关系映射)。除了上面提到的使用RowCallbackHandler进行关系-对象映射,还可以使用org.springframework.jdbc.core.RowMapper接口,实现单行、多行封装。使用例子如下:

[java]  view plain copy
  1. public interface RowMapper<T> {  
  2.     /**  
  3.      * Implementations must implement this method to map each row of data 
  4.      * in the ResultSet. This method should not call <code>next()</code> on 
  5.      * the ResultSet; it is only supposed to map values of the current row. 
  6.      * @param rs the ResultSet to map (pre-initialized for the current row) 
  7.      * @param rowNum the number of the current row 
  8.      * @return the result object for the current row 
  9.      */  
  10.     T mapRow(ResultSet rs, int rowNum) throws SQLException;   
  11. }  
  12. //例子  
  13. /*单独定义RowMapper,达到重用。也可以在查询方法中定义RowMapper*/  
  14. class UserRowMapper implements RowMapper<User>{  
  15.     public User mapRow(ResultSet rs, int rowNum) throws SQLException {  
  16.         User user=new User();  
  17.         user.setId(rs.getInt("id"));  
  18.         user.setName(rs.getString("name"));  
  19.         return null;  
  20.     }  
  21. }  
  22. //单行封装  
  23. public User find2(Integer id){  
  24.     User user =(User)jdbcTemplate.queryForObject(  
  25.             "select * from user where id=?",   
  26.             new Object[]{id},   
  27.             new UserRowMapper());  
  28.     return user;  
  29. }  
  30. //多行封装  
  31. public List<User> find3(){  
  32.     List<User> users=jdbcTemplate.query("select * from user",  
  33.             new UserRowMapper());  
  34.     return users;  
  35. }  
 

4. 存取Lob

在JDBC中可以使用Clob(character large object-文字文本文件)与Blob(Binary large object-图像等二进制文件),JdbcTemplate也可以进行大对象的便捷存取。储存大对象使用 public <T> T execute(String sql, PreparedStatementCallback<T> action)方法,读取大对象使用 public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse)。具体过程如下:

[java]  view plain copy
  1. public void storeLob() throws IOException{  
  2.     final File binaryFile=new File("D://wish.jpg");  
  3.     final File txtFile=new File("D://test.txt");  
  4.       
  5.     //转换为流,读到内存  
  6.     final InputStream is=new FileInputStream(binaryFile);  
  7.     final Reader reader=new FileReader(txtFile);  
  8.       
  9.     final LobHandler lobHandler=new DefaultLobHandler();  
  10.     jdbcTemplate.execute("insert into testLob (image,txt) values(?,?)",  
  11.             new AbstractLobCreatingPreparedStatementCallback(lobHandler){  
  12.                 protected void setValues(PreparedStatement ps,  
  13.                         LobCreator lobCreator) throws SQLException,  
  14.                         DataAccessException {  
  15.                     lobCreator.setBlobAsBinaryStream(  
  16.                             ps, 1, is, (int)binaryFile.length());  
  17.                     lobCreator.setClobAsCharacterStream(  
  18.                             ps, 2, reader, (int)txtFile.length());  
  19.                 }  
  20.     });  
  21.     reader.close();  
  22.     is.close();  
  23. }  
  24. //读取BLob/Clob  
  25. public void readLob() throws IOException{  
  26.     final OutputStream os=new FileOutputStream(new File("D://wish1.jpg"));  
  27.     final Writer writer=new FileWriter("D://test1.txt");  
  28.     final LobHandler lobHandler=new DefaultLobHandler();  
  29.     jdbcTemplate.query("select image,txt from testLob where id=?",  
  30.             new Object[]{1},  
  31.             new AbstractLobStreamingResultSetExtractor(){  
  32.                 protected void streamData(ResultSet rs)  
  33.                         throws SQLException, IOException,  
  34.                         DataAccessException {  
  35.                     FileCopyUtils.copy(  
  36.                             lobHandler.getBlobAsBinaryStream(rs, 1),  
  37.                             os);  
  38.                     FileCopyUtils.copy(  
  39.                             lobHandler.getClobAsCharacterStream(rs,2),  
  40.                             writer);  
  41.                 }  
  42.     });  
  43.     os.close();  
  44.     writer.close();  
  45. }  
 

在建立org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback(抽象类)对象时,需传递一个接口org.springframework.jdbc.support.lob.LobHandler实例,对于MySQL、SQL Server、Oracle 10g,LobHander实例使用org.springframework.jdbc.support.lob.DefaultLobHandler。对于oracle 9i可以使用OracleLobHandler。在setValues()方法实现中,使用org.springframework.jdbc.support.lob.LobCreator分别设置Blob与Clob的来源串流,索引1/2表示第一与第二个占位字符'?'的位置,并指定读取长度。

从数据库读取大对象文件时,使用了org.springframework.util.FileCopyUtils的copy方法,将LobHandler取的串流直接转接给文件输出FileWriter/FileOutputStream对象。

下面贴出,抽象类AbstractLobCreatingPreparedStatementCallback的部分定义、接口LobCreator的方法、抽象类AbstractLobStreamingResultSetExtractor部分定义、抽象类FileCopyUtils的copy方法、接口LobHandler的部分方法。

[java]  view plain copy
  1. public abstract class AbstractLobCreatingPreparedStatementCallback implements PreparedStatementCallback<Integer>{  
  2.     /** 
  3.      * Set values on the given PreparedStatement, using the given 
  4.      * LobCreator for BLOB/CLOB arguments. 
  5.      * @param ps the PreparedStatement to use 
  6.      * @param lobCreator the LobCreator to use 
  7.      * @throws SQLException if thrown by JDBC methods 
  8.      * @throws DataAccessException in case of custom exceptions 
  9.      */  
  10.     protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)  
  11.             throws SQLException, DataAccessException;  
  12. }  
  13. public interface LobCreator {     
  14.     void setBlobAsBytes(PreparedStatement ps, int paramIndex, byte[] content)  
  15.             throws SQLException;      
  16.     void setBlobAsBinaryStream(  
  17.             PreparedStatement ps, int paramIndex, InputStream contentStream, int contentLength)  
  18.             throws SQLException;  
  19.     void setClobAsString(PreparedStatement ps, int paramIndex, String content)  
  20.             throws SQLException;  
  21.     void setClobAsAsciiStream(  
  22.             PreparedStatement ps, int paramIndex, InputStream asciiStream, int contentLength)  
  23.             throws SQLException;      
  24.     void setClobAsCharacterStream(  
  25.             PreparedStatement ps, int paramIndex, Reader characterStream, int contentLength)  
  26.             throws SQLException;  
  27.     void close();  
  28. }  
  29. public abstract class AbstractLobStreamingResultSetExtractor implements ResultSetExtractor {  
  30.     /** 
  31.      * Stream LOB content from the given ResultSet to some OutputStream. 
  32.      * <p>Typically used as inner class, with access to surrounding method arguments 
  33.      * and to a LobHandler instance variable of the surrounding class. 
  34.      * @param rs the ResultSet to take the LOB content from 
  35.      * @throws DataAccessException in case of custom exceptions 
  36.      * @see org.springframework.jdbc.support.lob.LobHandler#getBlobAsBinaryStream 
  37.      * @see org.springframework.util.FileCopyUtils 
  38.      */  
  39.     protected abstract void streamData(ResultSet rs) throws SQLException, IOException, DataAccessException;  
  40. }  
  41. public abstract class FileCopyUtils {  
  42.     /** 
  43.      * Copy the contents of the given InputStream to the given OutputStream. 
  44.      * Closes both streams when done. 
  45.      */  
  46.     public static int copy(InputStream in, OutputStream out){};  
  47.     /** 
  48.      * Copy the contents of the given Reader to the given Writer. 
  49.      * Closes both when done. 
  50.      */  
  51.     public static int copy(Reader in, Writer out){};  
  52. }  
  53. public interface LobHandler {  
  54.     /** 
  55.      * Retrieve the given column as binary stream from the given ResultSet. 
  56.      * Might simply invoke <code>ResultSet.getBinaryStream</code> or work with 
  57.      * <code>ResultSet.getBlob</code>, depending on the database and driver.     
  58.      */  
  59.     InputStream getBlobAsBinaryStream(ResultSet rs, int columnIndex) throws SQLException;  
  60.     /** 
  61.      * Retrieve the given column as character stream from the given ResultSet. 
  62.      * Might simply invoke <code>ResultSet.getCharacterStream</code> or work with 
  63.      * <code>ResultSet.getClob</code>, depending on the database and driver. 
  64.      */  
  65.     Reader getClobAsCharacterStream(ResultSet rs, int columnIndex) throws SQLException;  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值