Spring JDBC采用模板的設計模式來完成設計。抽象類中定義模板方法,在模板方法中對處理過程進行描述,然后每個具體的過程實現則交由子類來實現。
1,設計原理
在Spring JDBC中,JdbcTemplate是一個主要的模板類,該類繼承JdbcAccessor,實現JdbcOperation接口。
在JdbcAccessor中對DataSource進行管和配置。
JdbcOperation接口則定義了操作數據庫的基本方法。
2,JdbcTemplate的基本使用
使用JdbcTemplate,可以直接進行對數據庫操作的調用,忽略異常處理以及建立連接,數據結果處理等一系列操作。
3,JdbcTemplate的execute實現。
如下為JdbcTemplate的execute方法源碼:
@Override
public void execute(final String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
@Override
public Object doInStatement(Statement stmt) throws SQLException {//重寫
stmt.execute(sql);
return null;
}
@Override
public String getSql() {
return sql;
}
}
execute(new ExecuteStatementCallback());
}
@Override
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(getDataSource());//獲取COnnection
Statement stmt = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
stmt = conToUse.createStatement();//Statement
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}
T result = action.doInStatement(stmtToUse);//調用傳入接口的子類的實現
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
4,JdbcTemplate的query方法
@Override
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
@Override
public T doInStatement(Statement stmt) throws SQLException {//內部類重寫此方法
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
@Override
public String getSql() {
return sql;
}
}
return execute(new QueryStatementCallback());
}
我們查看在execute方法中的,發現最終調用的是doInStatement,該方法均在內部類中重寫。
5,使用Connection。
Spring通過DataSourceUtils對Connection進行管理。在數據庫應用中,數據庫的Connection的使用往往和事務處理相關。
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);//如果已經存在與當前線程綁定的數據源,則直接取出
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
}
具體的dataSource對象則通過IoC容器實現注入。
1,SqlQuery的實現
Spring除了提供基本操作外,還提供一些O/R映射基本,比如MappingSqlQuery。在代碼中,我們往往實現自己的類來實現數據庫到POJO的映射,具體的實現省略。
public abstract class MappingSqlQuery<T> extends MappingSqlQueryWithParameters<T> {
/**
* Constructor that allows use as a JavaBean.
*/
public MappingSqlQuery() {
}
/**
* Convenient constructor with DataSource and SQL string.
* @param ds DataSource to use to obtain connections
* @param sql SQL to run
*/
public MappingSqlQuery(DataSource ds, String sql) {
super(ds, sql);
}
/**
* This method is implemented to invoke the simpler mapRow
* template method, ignoring parameters.
* @see #mapRow(ResultSet, int)
*/
@Override
protected final T mapRow(ResultSet rs, int rowNum, Object[] parameters, Map<?, ?> context)
throws SQLException {
return mapRow(rs, rowNum);
}
/**
* Subclasses must implement this method to convert each row of the
* ResultSet into an object of the result type.
* <p>Subclasses of this class, as opposed to direct subclasses of
* MappingSqlQueryWithParameters, don't need to concern themselves
* with the parameters to the execute method of the query object.
* @param rs ResultSet we're working through
* @param rowNum row number (from 0) we're up to
* @return an object of the result type
* @throws SQLException if there's an error extracting data.
* Subclasses can simply not catch SQLExceptions, relying on the
* framework to clean up.
*/
protected abstract T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
以RdbmsOperation為例。
public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
if (isCompiled()) {
throw new InvalidDataAccessApiUsageException("Cannot add parameters once the query is compiled");
}
this.declaredParameters.add(param);
}
private final List<SqlParameter> declaredParameters = new LinkedList<SqlParameter>();
查看對應的compile操作
/**
* Compile this query.
* Ignores subsequent attempts to compile.
* @throws InvalidDataAccessApiUsageException if the object hasn't
* been correctly initialized, for example if no DataSource has been provided
*/
public final void compile() throws InvalidDataAccessApiUsageException {
if (!isCompiled()) {
if (getSql() == null) {
throw new InvalidDataAccessApiUsageException("Property 'sql' is required");
}
try {
this.jdbcTemplate.afterPropertiesSet();
}
catch (IllegalArgumentException ex) {
throw new InvalidDataAccessApiUsageException(ex.getMessage());
}
compileInternal();
this.compiled = true;
if (logger.isDebugEnabled()) {
logger.debug("RdbmsOperation with SQL [" + getSql() + "] compiled");
}
}
}
compileInternal操作在SqlOperation中完成。
* Overridden method to configure the PreparedStatementCreatorFactory
* based on our declared parameters.
*/
@Override
protected final void compileInternal() {
this.preparedStatementFactory = new PreparedStatementCreatorFactory(getSql(), getDeclaredParameters());
this.preparedStatementFactory.setResultSetType(getResultSetType());
this.preparedStatementFactory.setUpdatableResults(isUpdatableResults());
this.preparedStatementFactory.setReturnGeneratedKeys(isReturnGeneratedKeys());
if (getGeneratedKeysColumnNames() != null) {
this.preparedStatementFactory.setGeneratedKeysColumnNames(getGeneratedKeysColumnNames());
}
this.preparedStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());
onCompileInternal();
}
在compile之后,執行查詢時,執行的是SqlQuery的executeByNamedParam方法。
public List<T> executeByNamedParam(Map<String, ?> paramMap, Map<?, ?> context) throws DataAccessException {
validateNamedParameters(paramMap);
ParsedSql parsedSql = getParsedSql();//獲取到要執行的sql
MapSqlParameterSource paramSource = new MapSqlParameterSource(paramMap);
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
//配置好sql需要的parameter及RowMapper
Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, getDeclaredParameters());
RowMapper<T> rowMapper = newRowMapper(params, context);
//JdbcTemplate進行查詢
return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, params), rowMapper);
}
以上就是基本的源碼。其中還涉及到一些我們常用的其他源碼,以及對Hibernate的封裝。個人覺得Hibernate過於繁瑣,以及HQL語句對sql的瘋轉反而過於嚴重,不是很喜歡,所以對Spring 對Hibernate的源碼塊跳過。
ps:Spring對數據庫JdbcTemplate的封裝較為簡單。終於不再像看IoC和AOP那樣子暈了。
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。