1. 程式人生 > >spring3: 對JDBC的支持 之 Spring提供的其它幫助 SimpleJdbcInsert/SimpleJdbcCall/SqlUpdate/JdbcTemplate 生成主鍵/批量處理

spring3: 對JDBC的支持 之 Spring提供的其它幫助 SimpleJdbcInsert/SimpleJdbcCall/SqlUpdate/JdbcTemplate 生成主鍵/批量處理

4.3 復合 AC lex con add 兩個 HR sel

7.4 Spring提供的其它幫助

7.4.1 SimpleJdbc方式

Spring JDBC抽象框架提供SimpleJdbcInsert和SimpleJdbcCall類,這兩個類通過利用JDBC驅動提供的數據庫元數據來簡化JDBC操作。

1、SimpleJdbcInsert: 用於插入數據,根據數據庫元數據進行插入數據,本類用於簡化插入操作,提供三種類型方法:execute方法用於普通插入、executeAndReturnKey及executeAndReturnKeyHolder方法用於插入時獲取主鍵值、executeBatch方法用於批處理。

@Test  
public void testSimpleJdbcInsert() {  
    SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate);  
    insert.withTableName("test");  
    Map<String, Object> args = new HashMap<String, Object>();  
    args.put("name", "name5");  
    insert.compile();  
    //1.普通插入  
    insert.execute(args);  
    Assert.assertEquals(1, jdbcTemplate.queryForInt("select count(*) from test"));  
    //2.插入時獲取主鍵值  
    insert = new SimpleJdbcInsert(jdbcTemplate);  
    insert.withTableName("test");  
    insert.setGeneratedKeyName("id");  
    Number id = insert.executeAndReturnKey(args);  
    Assert.assertEquals(1, id);  
    //3.批處理  
    insert = new SimpleJdbcInsert(jdbcTemplate);  
    insert.withTableName("test");  
    insert.setGeneratedKeyName("id");  
    int[] updateCount = insert.executeBatch(new Map[] {args, args, args});  
    Assert.assertEquals(1, updateCount[0]);  
    Assert.assertEquals(5, jdbcTemplate.queryForInt("select count(*) from test"));  
}  

  

  • new SimpleJdbcInsert(jdbcTemplate) 首次通過DataSource對象或JdbcTemplate對象初始化SimpleJdbcInsert;
  • insert.withTableName("test") 用於設置數據庫表名;
  • args 用於指定插入時列名及值,如本例中只有name列名,即編譯後的sql類似於“insert into test(name) values(?)”;
  • insert.compile() 可選的編譯步驟,在調用執行方法時自動編譯,編譯後不能再對insert對象修改;
  • 執行: execute方法用於執行普通插入;executeAndReturnKey用於執行並獲取自動生成主鍵(註意是Number類型),必須首先通過setGeneratedKeyName設置主鍵然後才能獲取,如果想獲取復合主鍵請使用setGeneratedKeyNames描述主鍵然後通過executeReturningKeyHolder獲取復合主鍵KeyHolder對象;executeBatch用於批處理;

2、SimpleJdbcCall: 用於調用存儲過程及自定義函數,本類用於簡化存儲過程及自定義函數調用。

@Test  
public void testSimpleJdbcCall1() {  
    //此處用mysql,因為hsqldb調用自定義函數和存儲過程一樣  
    SimpleJdbcCall call = new SimpleJdbcCall(getMysqlDataSource());  
    call.withFunctionName("FUNCTION_TEST");  
    call.declareParameters(new SqlOutParameter("result", Types.INTEGER));  
    call.declareParameters(new SqlParameter("str", Types.VARCHAR));  
    Map<String, Object> outVlaues = call.execute("test");  
    Assert.assertEquals(4, outVlaues.get("result"));  
}  

  

  • new SimpleJdbcCall(getMysqlDataSource()) :通過DataSource對象或JdbcTemplate對象初始化SimpleJdbcCall;
  • withFunctionName("FUNCTION_TEST") 定義自定義函數名;自定義函數sql語句將被編譯為類似於{?= call …}形式;
  • declareParameters 描述參數類型,使用方式與StoredProcedure對象一樣;
  • 執行: 調用execute方法執行自定義函數;
@Test  
public void testSimpleJdbcCall2() {  
    //調用hsqldb自定義函數得使用如下方式  
    SimpleJdbcCall call = new SimpleJdbcCall(jdbcTemplate);  
    call.withProcedureName("FUNCTION_TEST");  
call.declareParameters(new SqlReturnResultSet("result",  
new ResultSetExtractor<Integer>() {  
        @Override  
        public Integer extractData(ResultSet rs)  
throws SQLException, DataAccessException {  
          while(rs.next()) {  
            return rs.getInt(1);  
          }  
          return 0;  
    }}));  
    call.declareParameters(new SqlParameter("str", Types.VARCHAR));  
    Map<String, Object> outVlaues = call.execute("test");  
    Assert.assertEquals(4, outVlaues.get("result"));  
}  

  調用hsqldb數據庫自定義函數與調用mysql自定義函數完全不同,詳見StoredProcedure中的解釋。

@Test  
public void testSimpleJdbcCall3() {  
  SimpleJdbcCall call = new SimpleJdbcCall(jdbcTemplate);  
  call.withProcedureName("PROCEDURE_TEST");  
  call.declareParameters(new SqlInOutParameter("inOutName", Types.VARCHAR));  
  call.declareParameters(new SqlOutParameter("outId", Types.INTEGER));  
  SqlParameterSource params =  
  new MapSqlParameterSource().addValue("inOutName", "test");  
  Map<String, Object> outVlaues = call.execute(params);  
  Assert.assertEquals("Hello,test", outVlaues.get("inOutName"));  
  Assert.assertEquals(0, outVlaues.get("outId"));  
}  

  

與自定義函數調用不同的是使用withProcedureName來指定存儲過程名字;其他參數描述等完全一樣。

7.4.2 控制數據庫連接

Spring JDBC通過DataSource控制數據庫連接,即通過DataSource實現獲取數據庫連接。

Spring JDBC提供了一下DataSource實現:

  • DriverManagerDataSource :簡單封裝了DriverManager獲取數據庫連接;通過DriverManager的getConnection方法獲取數據庫連接;
  • SingleConnectionDataSource :內部封裝了一個連接,該連接使用後不會關閉,且不能在多線程環境中使用,一般用於測試;
  • LazyConnectionDataSourceProxy :包裝一個DataSource,用於延遲獲取數據庫連接,只有在真正創建Statement等時才獲取連接,因此再說實際項目中最後使用該代理包裝原始DataSource從而使得只有在真正需要連接時才去獲取。

第三方提供的DataSource實現主要有C3P0、Proxool、DBCP等,這些實現都具有數據庫連接池能力。

DataSourceUtils: Spring JDBC抽象框架內部都是通過它的getConnection(DataSource dataSource)方法獲取數據庫連接,releaseConnection(Connection con, DataSource dataSource) 用於釋放數據庫連接,DataSourceUtils用於支持Spring管理事務,只有使用DataSourceUtils獲取的連接才具有Spring管理事務。

7.4.3 獲取自動生成的主鍵

有許多數據庫提供自動生成主鍵的能力,因此我們可能需要獲取這些自動生成的主鍵,JDBC 3.0標準支持獲取自動生成的主鍵,且必須數據庫支持自動生成鍵獲取。

1 )JdbcTemplate 獲取自動生成主鍵方式:

@Test  
public void testFetchKey1() throws SQLException {  
    final String insertSql = "insert into test(name) values(‘name5‘)";  
    KeyHolder generatedKeyHolder = new GeneratedKeyHolder();  
    jdbcTemplate.update(new PreparedStatementCreator() {  
        @Override  
       public PreparedStatement createPreparedStatement(Connection conn)  
            throws SQLException {  
            return conn.prepareStatement(insertSql, new String[]{"ID"});  
      }}, generatedKeyHolder);  
    Assert.assertEquals(0, generatedKeyHolder.getKey());  
}  

  

使用JdbcTemplate的update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)方法執行需要返回自動生成主鍵的插入語句,其中psc用於創建PreparedStatement並指定自動生成鍵,如“prepareStatement(insertSql, new String[]{"ID"})”;generatedKeyHolder是KeyHolder類型,用於獲取自動生成的主鍵或復合主鍵;如使用getKey方法獲取自動生成的主鍵。

2 )SqlUpdate 獲取自動生成主鍵方式:

@Test  
public void testFetchKey2() {  
    final String insertSql = "insert into test(name) values(‘name5‘)";  
    KeyHolder generatedKeyHolder = new GeneratedKeyHolder();  
    SqlUpdate update = new SqlUpdate();  
    update.setJdbcTemplate(jdbcTemplate);  
    update.setReturnGeneratedKeys(true);  
    //update.setGeneratedKeysColumnNames(new String[]{"ID"});  
    update.setSql(insertSql);  
    update.update(null, generatedKeyHolder);  
    Assert.assertEquals(0, generatedKeyHolder.getKey());  
}  

  

SqlUpdate獲取自動生成主鍵方式和JdbcTemplate完全一樣,可以使用setReturnGeneratedKeys(true)表示要獲取自動生成鍵;也可以使用setGeneratedKeysColumnNames指定自動生成鍵列名。

3 )SimpleJdbcInsert 前邊示例已介紹,此處就不演示了。

7.4.4 JDBC批量操作

JDBC批處理用於減少與數據庫交互的次數來提升性能,Spring JDBC抽象框架通過封裝批處理操作來簡化批處理操作

1 )JdbcTemplate 批處理: 支持普通的批處理及占位符批處理;

@Test  
public void testBatchUpdate1() {  
    String insertSql = "insert into test(name) values(‘name5‘)";  
    String[] batchSql = new String[] {insertSql, insertSql};  
    jdbcTemplate.batchUpdate(batchSql);  
    Assert.assertEquals(2, jdbcTemplate.queryForInt("select count(*) from test"));  
}  

   直接調用batchUpdate方法執行需要批處理的語句即可。

@Test  
public void testBatchUpdate2() {  
    String insertSql = "insert into test(name) values(?)";  
    final String[] batchValues = new String[] {"name5", "name6"};  
    jdbcTemplate.batchUpdate(insertSql, new BatchPreparedStatementSetter() {  
        @Override  
        public void setValues(PreparedStatement ps, int i) throws SQLException {  
            ps.setString(1, batchValues[i]);  
        }  
        @Override  
        public int getBatchSize() {  
            return batchValues.length;  
        }  
    });  
    Assert.assertEquals(2, jdbcTemplate.queryForInt("select count(*) from test"));  
}  

  

JdbcTemplate還可以通過batchUpdate(String sql, final BatchPreparedStatementSetter pss)方法進行批處理,該方式使用預編譯語句,然後通過BatchPreparedStatementSetter實現進行設值(setValues)及指定批處理大小(getBatchSize)。

2 )NamedParameterJdbcTemplate 批處理: 支持命名參數批處理;

@Test  
public void testBatchUpdate3() {  
    NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);  
    String insertSql = "insert into test(name) values(:myName)";  
    UserModel model = new UserModel();  
    model.setMyName("name5");  
    SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(new Object[] {model, model});  
    namedParameterJdbcTemplate.batchUpdate(insertSql, params);  
    Assert.assertEquals(2, jdbcTemplate.queryForInt("select count(*) from test"));  
}  

  

通過batchUpdate(String sql, SqlParameterSource[] batchArgs)方法進行命名參數批處理,batchArgs指定批處理數據集。SqlParameterSourceUtils.createBatch用於根據JavaBean對象或者Map創建相應的BeanPropertySqlParameterSource或MapSqlParameterSource。

3) SimpleJdbcTemplate 批處理: 已更簡單的方式進行批處理;

@Test  
public void testBatchUpdate4() {  
    SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(jdbcTemplate);  
    String insertSql = "insert into test(name) values(?)";  
    List<Object[]> params = new ArrayList<Object[]>();  
    params.add(new Object[]{"name5"});  
    params.add(new Object[]{"name5"});  
    simpleJdbcTemplate.batchUpdate(insertSql, params);  
    Assert.assertEquals(2, jdbcTemplate.queryForInt("select count(*) from test"));  
}  

  

本示例使用batchUpdate(String sql, List<Object[]> batchArgs)方法完成占位符批處理,當然也支持命名參數批處理等。

4 )SimpleJdbcInsert 批處理:

@Test  
public void testBatchUpdate5() {  
    SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate);  
    insert.withTableName("test");  
    Map<String, Object> valueMap = new HashMap<String, Object>();  
    valueMap.put("name", "name5");  
    insert.executeBatch(new Map[] {valueMap, valueMap});  
   Assert.assertEquals(2, jdbcTemplate.queryForInt("select count(*) from test"));  
}  

   如代碼所示,使用executeBatch(Map<String, Object>[] batch)方法執行批處理。

spring3: 對JDBC的支持 之 Spring提供的其它幫助 SimpleJdbcInsert/SimpleJdbcCall/SqlUpdate/JdbcTemplate 生成主鍵/批量處理