| 1 16 17 package org.springframework.jdbc.core; 18 19 import java.lang.reflect.InvocationHandler ; 20 import java.lang.reflect.InvocationTargetException ; 21 import java.lang.reflect.Method ; 22 import java.lang.reflect.Proxy ; 23 import java.sql.CallableStatement ; 24 import java.sql.Connection ; 25 import java.sql.PreparedStatement ; 26 import java.sql.ResultSet ; 27 import java.sql.SQLException ; 28 import java.sql.SQLWarning ; 29 import java.sql.Statement ; 30 import java.util.HashMap ; 31 import java.util.List ; 32 import java.util.Map ; 33 34 import javax.sql.DataSource ; 35 36 import org.springframework.dao.DataAccessException; 37 import org.springframework.dao.InvalidDataAccessApiUsageException; 38 import org.springframework.dao.support.DataAccessUtils; 39 import org.springframework.jdbc.SQLWarningException; 40 import org.springframework.jdbc.datasource.ConnectionProxy; 41 import org.springframework.jdbc.datasource.DataSourceUtils; 42 import org.springframework.jdbc.support.JdbcAccessor; 43 import org.springframework.jdbc.support.JdbcUtils; 44 import org.springframework.jdbc.support.KeyHolder; 45 import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; 46 import org.springframework.jdbc.support.rowset.SqlRowSet; 47 import org.springframework.util.Assert; 48 49 91 public class JdbcTemplate extends JdbcAccessor implements JdbcOperations { 92 93 94 private NativeJdbcExtractor nativeJdbcExtractor; 95 96 97 private boolean ignoreWarnings = true; 98 99 103 private int fetchSize = 0; 104 105 109 private int maxRows = 0; 110 111 115 private int queryTimeout = 0; 116 117 122 private boolean skipResultsProcessing = false; 123 124 125 130 public JdbcTemplate() { 131 } 132 133 138 public JdbcTemplate(DataSource dataSource) { 139 setDataSource(dataSource); 140 afterPropertiesSet(); 141 } 142 143 150 public JdbcTemplate(DataSource dataSource, boolean lazyInit) { 151 setDataSource(dataSource); 152 setLazyInit(lazyInit); 153 afterPropertiesSet(); 154 } 155 156 157 163 public void setNativeJdbcExtractor(NativeJdbcExtractor extractor) { 164 this.nativeJdbcExtractor = extractor; 165 } 166 167 170 public NativeJdbcExtractor getNativeJdbcExtractor() { 171 return this.nativeJdbcExtractor; 172 } 173 174 182 public void setIgnoreWarnings(boolean ignoreWarnings) { 183 this.ignoreWarnings = ignoreWarnings; 184 } 185 186 189 public boolean isIgnoreWarnings() { 190 return this.ignoreWarnings; 191 } 192 193 201 public void setFetchSize(int fetchSize) { 202 this.fetchSize = fetchSize; 203 } 204 205 208 public int getFetchSize() { 209 return this.fetchSize; 210 } 211 212 221 public void setMaxRows(int maxRows) { 222 this.maxRows = maxRows; 223 } 224 225 228 public int getMaxRows() { 229 return this.maxRows; 230 } 231 232 240 public void setQueryTimeout(int queryTimeout) { 241 this.queryTimeout = queryTimeout; 242 } 243 244 247 public int getQueryTimeout() { 248 return this.queryTimeout; 249 } 250 251 257 public void setSkipResultsProcessing(boolean skipResultsProcessing) { 258 this.skipResultsProcessing = skipResultsProcessing; 259 } 260 261 264 public boolean isSkipResultsProcessing() { 265 return this.skipResultsProcessing; 266 } 267 268 269 273 public Object execute(ConnectionCallback action) throws DataAccessException { 274 Assert.notNull(action, "Callback object must not be null"); 275 276 Connection con = DataSourceUtils.getConnection(getDataSource()); 277 try { 278 Connection conToUse = con; 279 if (this.nativeJdbcExtractor != null) { 280 conToUse = this.nativeJdbcExtractor.getNativeConnection(con); 282 } 283 else { 284 conToUse = createConnectionProxy(con); 286 } 287 return action.doInConnection(conToUse); 288 } 289 catch (SQLException ex) { 290 DataSourceUtils.releaseConnection(con, getDataSource()); 293 con = null; 294 throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex); 295 } 296 finally { 297 DataSourceUtils.releaseConnection(con, getDataSource()); 298 } 299 } 300 301 312 protected Connection createConnectionProxy(Connection con) { 313 return (Connection ) Proxy.newProxyInstance( 314 ConnectionProxy.class.getClassLoader(), 315 new Class [] {ConnectionProxy.class}, 316 new CloseSuppressingInvocationHandler(con)); 317 } 318 319 320 324 public Object execute(StatementCallback action) throws DataAccessException { 325 Assert.notNull(action, "Callback object must not be null"); 326 327 Connection con = DataSourceUtils.getConnection(getDataSource()); 328 Statement stmt = null; 329 try { 330 Connection conToUse = con; 331 if (this.nativeJdbcExtractor != null && 332 this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) { 333 conToUse = this.nativeJdbcExtractor.getNativeConnection(con); 334 } 335 stmt = conToUse.createStatement(); 336 applyStatementSettings(stmt); 337 Statement stmtToUse = stmt; 338 if (this.nativeJdbcExtractor != null) { 339 stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt); 340 } 341 Object result = action.doInStatement(stmtToUse); 342 handleWarnings(stmt.getWarnings()); 343 return result; 344 } 345 catch (SQLException ex) { 346 JdbcUtils.closeStatement(stmt); 349 stmt = null; 350 DataSourceUtils.releaseConnection(con, getDataSource()); 351 con = null; 352 throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex); 353 } 354 finally { 355 JdbcUtils.closeStatement(stmt); 356 DataSourceUtils.releaseConnection(con, getDataSource()); 357 } 358 } 359 360 public void execute(final String sql) throws DataAccessException { 361 if (logger.isDebugEnabled()) { 362 logger.debug("Executing SQL statement [" + sql + "]"); 363 } 364 365 class ExecuteStatementCallback implements StatementCallback, SqlProvider { 366 public Object doInStatement(Statement stmt) throws SQLException { 367 stmt.execute(sql); 368 return null; 369 } 370 public String getSql() { 371 return sql; 372 } 373 } 374 execute(new ExecuteStatementCallback()); 375 } 376 377 public Object query(final String sql, final ResultSetExtractor rse) throws DataAccessException { 378 Assert.notNull(sql, "SQL must not be null"); 379 Assert.notNull(rse, "ResultSetExtractor must not be null"); 380 if (logger.isDebugEnabled()) { 381 logger.debug("Executing SQL query [" + sql + "]"); 382 } 383 384 class QueryStatementCallback implements StatementCallback, SqlProvider { 385 public Object doInStatement(Statement stmt) throws SQLException { 386 ResultSet rs = null; 387 try { 388 rs = stmt.executeQuery(sql); 389 ResultSet rsToUse = rs; 390 if (nativeJdbcExtractor != null) { 391 rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); 392 } 393 return rse.extractData(rsToUse); 394 } 395 finally { 396 JdbcUtils.closeResultSet(rs); 397 } 398 } 399 public String getSql() { 400 return sql; 401 } 402 } 403 return execute(new QueryStatementCallback()); 404 } 405 406 public void query(String sql, RowCallbackHandler rch) throws DataAccessException { 407 query(sql, new RowCallbackHandlerResultSetExtractor(rch)); 408 } 409 410 public List query(String sql, RowMapper rowMapper) throws DataAccessException { 411 return (List ) query(sql, new RowMapperResultSetExtractor(rowMapper)); 412 } 413 414 public Map queryForMap(String sql) throws DataAccessException { 415 return (Map ) queryForObject(sql, getColumnMapRowMapper()); 416 } 417 418 public Object queryForObject(String sql, RowMapper rowMapper) throws DataAccessException { 419 List results = query(sql, rowMapper); 420 return DataAccessUtils.requiredSingleResult(results); 421 } 422 423 public Object queryForObject(String sql, Class requiredType) throws DataAccessException { 424 return queryForObject(sql, getSingleColumnRowMapper(requiredType)); 425 } 426 427 public long queryForLong(String sql) throws DataAccessException { 428 Number number = (Number ) queryForObject(sql, Long .class); 429 return (number != null ? number.longValue() : 0); 430 } 431 432 public int queryForInt(String sql) throws DataAccessException { 433 Number number = (Number ) queryForObject(sql, Integer .class); 434 return (number != null ? number.intValue() : 0); 435 } 436 437 public List queryForList(String sql, Class elementType) throws DataAccessException { 438 return query(sql, getSingleColumnRowMapper(elementType)); 439 } 440 441 public List queryForList(String sql) throws DataAccessException { 442 return query(sql, getColumnMapRowMapper()); 443 } 444 445 public SqlRowSet queryForRowSet(String sql) throws DataAccessException { 446 return (SqlRowSet) query(sql, new SqlRowSetResultSetExtractor()); 447 } 448 449 public int update(final String sql) throws DataAccessException { 450 Assert.notNull(sql, "SQL must not be null"); 451 if (logger.isDebugEnabled()) { 452 logger.debug("Executing SQL update [" + sql + "]"); 453 } 454 455 class UpdateStatementCallback implements StatementCallback, SqlProvider { 456 public Object doInStatement(Statement stmt) throws SQLException { 457 int rows = stmt.executeUpdate(sql); 458 if (logger.isDebugEnabled()) { 459 logger.debug("SQL update affected " + rows + " rows"); 460 } 461 return new Integer (rows); 462 } 463 public String getSql() { 464 return sql; 465 } 466 } 467 return ((Integer ) execute(new UpdateStatementCallback())).intValue(); 468 } 469 470 public int[] batchUpdate(final String [] sql) throws DataAccessException { 471 Assert.notEmpty(sql, "SQL array must not be empty"); 472 if (logger.isDebugEnabled()) { 473 logger.debug("Executing SQL batch update of " + sql.length + " statements"); 474 } 475 476 class BatchUpdateStatementCallback implements StatementCallback, SqlProvider { 477 private String currSql; 478 public Object doInStatement(Statement stmt) throws SQLException , DataAccessException { 479 int[] rowsAffected = new int[sql.length]; 480 if (JdbcUtils.supportsBatchUpdates(stmt.getConnection())) { 481 for (int i = 0; i < sql.length; i++) { 482 this.currSql = sql[i]; 483 stmt.addBatch(sql[i]); 484 } 485 rowsAffected = stmt.executeBatch(); 486 } 487 else { 488 for (int i = 0; i < sql.length; i++) { 489 this.currSql = sql[i]; 490 if (!stmt.execute(sql[i])) { 491 rowsAffected[i] = stmt.getUpdateCount(); 492 } 493 else { 494 throw new InvalidDataAccessApiUsageException("Invalid batch SQL statement: " + sql[i]); 495 } 496 } 497 } 498 return rowsAffected; 499 } 500 public String getSql() { 501 return currSql; 502 } 503 } 504 return (int[]) execute(new BatchUpdateStatementCallback()); 505 } 506 507 508 512 public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action) 513 throws DataAccessException { 514 515 Assert.notNull(psc, "PreparedStatementCreator must not be null"); 516 Assert.notNull(action, "Callback object must not be null"); 517 if (logger.isDebugEnabled()) { 518 String sql = getSql(psc); 519 logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : "")); 520 } 521 522 Connection con = DataSourceUtils.getConnection(getDataSource()); 523 PreparedStatement ps = null; 524 try { 525 Connection conToUse = con; 526 if (this.nativeJdbcExtractor != null && 527 this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) { 528 conToUse = this.nativeJdbcExtractor.getNativeConnection(con); 529 } 530 ps = psc.createPreparedStatement(conToUse); 531 applyStatementSettings(ps); 532 PreparedStatement psToUse = ps; 533 if (this.nativeJdbcExtractor != null) { 534 psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps); 535 } 536 Object result = action.doInPreparedStatement(psToUse); 537 handleWarnings(ps.getWarnings()); 538 return result; 539 } 540 catch (SQLException ex) { 541 if (psc instanceof ParameterDisposer) { 544 ((ParameterDisposer) psc).cleanupParameters(); 545 } 546 String sql = getSql(psc); 547 psc = null; 548 JdbcUtils.closeStatement(ps); 549 ps = null; 550 DataSourceUtils.releaseConnection(con, getDataSource()); 551 con = null; 552 throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex); 553 } 554 finally { 555 if (psc instanceof ParameterDisposer) { 556 ((ParameterDisposer) psc).cleanupParameters(); 557 } 558 JdbcUtils.closeStatement(ps); 559 DataSourceUtils.releaseConnection(con, getDataSource()); 560 } 561 } 562 563 public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException { 564 return execute(new SimplePreparedStatementCreator(sql), action); 565 } 566 567 579 public Object query( 580 PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse) 581 throws DataAccessException { 582 583 Assert.notNull(rse, "ResultSetExtractor must not be null"); 584 logger.debug("Executing prepared SQL query"); 585 586 return execute(psc, new PreparedStatementCallback() { 587 public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { 588 ResultSet rs = null; 589 try { 590 if (pss != null) { 591 pss.setValues(ps); 592 } 593 rs = ps.executeQuery(); 594 ResultSet rsToUse = rs; 595 if (nativeJdbcExtractor != null) { 596 rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); 597 } 598 return rse.extractData(rsToUse); 599 } 600 finally { 601 JdbcUtils.closeResultSet(rs); 602 if (pss instanceof ParameterDisposer) { 603 ((ParameterDisposer) pss).cleanupParameters(); 604 } 605 } 606 } 607 }); 608 } 609 610 public Object query(PreparedStatementCreator psc, ResultSetExtractor rse) throws DataAccessException { 611 return query(psc, null, rse); 612 } 613 614 public Object query(String sql, PreparedStatementSetter pss, ResultSetExtractor rse) throws DataAccessException { 615 return query(new SimplePreparedStatementCreator(sql), pss, rse); 616 } 617 618 public Object query(String sql, Object [] args, int[] argTypes, ResultSetExtractor rse) throws DataAccessException { 619 return query(sql, new ArgTypePreparedStatementSetter(args, argTypes), rse); 620 } 621 622 public Object query(String sql, Object [] args, ResultSetExtractor rse) throws DataAccessException { 623 return query(sql, new ArgPreparedStatementSetter(args), rse); 624 } 625 626 public void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException { 627 query(psc, new RowCallbackHandlerResultSetExtractor(rch)); 628 } 629 630 public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException { 631 query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch)); 632 } 633 634 public void query(String sql, Object [] args, int[] argTypes, RowCallbackHandler rch) throws DataAccessException { 635 query(sql, new ArgTypePreparedStatementSetter(args, argTypes), rch); 636 } 637 638 public void query(String sql, Object [] args, RowCallbackHandler rch) throws DataAccessException { 639 query(sql, new ArgPreparedStatementSetter(args), rch); 640 } 641 642 public List query(PreparedStatementCreator psc, RowMapper rowMapper) throws DataAccessException { 643 return (List ) query(psc, new RowMapperResultSetExtractor(rowMapper)); 644 } 645 646 public List query(String sql, PreparedStatementSetter pss, RowMapper rowMapper) throws DataAccessException { 647 return (List ) query(sql, pss, new RowMapperResultSetExtractor(rowMapper)); 648 } 649 650 public List query(String sql, Object [] args, int[] argTypes, RowMapper rowMapper) throws DataAccessException { 651 return (List ) query(sql, args, argTypes, new RowMapperResultSetExtractor(rowMapper)); 652 } 653 654 public List query(String sql, Object [] args, RowMapper rowMapper) throws DataAccessException { 655 return (List ) query(sql, args, new RowMapperResultSetExtractor(rowMapper)); 656 } 657 658 public Object queryForObject(String sql, Object [] args, int[] argTypes, RowMapper rowMapper) 659 throws DataAccessException { 660 661 List results = (List ) query(sql, args, argTypes, new RowMapperResultSetExtractor(rowMapper, 1)); 662 return DataAccessUtils.requiredSingleResult(results); 663 } 664 665 public Object queryForObject(String sql, Object [] args, RowMapper rowMapper) throws DataAccessException { 666 List results = (List ) query(sql, args, new RowMapperResultSetExtractor(rowMapper, 1)); 667 return DataAccessUtils.requiredSingleResult(results); 668 } 669 670 public Object queryForObject(String sql, Object [] args, int[] argTypes, Class requiredType) 671 throws DataAccessException { 672 673 return queryForObject(sql, args, argTypes, getSingleColumnRowMapper(requiredType)); 674 } 675 676 public Object queryForObject(String sql, Object [] args, Class requiredType) throws DataAccessException { 677 return queryForObject(sql, args, getSingleColumnRowMapper(requiredType)); 678 } 679 680 public Map queryForMap(String sql, Object [] args, int[] argTypes) throws DataAccessException { 681 return (Map ) queryForObject(sql, args, argTypes, getColumnMapRowMapper()); 682 } 683 684 public Map queryForMap(String sql, Object [] args) throws DataAccessException { 685 return (Map ) queryForObject(sql, args, getColumnMapRowMapper()); 686 } 687 688 public long queryForLong(String sql, |