spring3: 對JDBC的支持 之 Spring提供的其它幫助 SimpleJdbcInsert/SimpleJdbcCall/SqlUpdate/JdbcTemplate 生成主鍵/批量處理
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 生成主鍵/批量處理