前面總結中分別使用了單純的jdbc以及jdbc配合數據庫連接池的方式進行了簡單的數據庫查詢操作,從代碼編寫的復雜度以及性能兩個方面來看使用數據庫連接池都好於用單純的jdbc。另外,在上述兩種方式中我們也看到了很多的重復代碼,比如獲取connection、獲取statement,關閉connection、關閉statement等,這些代碼在我們每一次的數據庫操作中都是重復的,是否有更好的屏蔽這種重復操作的方式呢?這篇文章給出答案。
通用的操作流程如下:
在流程中只有執行sql在每次數據庫操作中是不一樣的,其他的步驟都是相同的,那就需要解決這個問題,而這個問題與模板設計模式所解決的問題非常相似。模板設計模式的定義:模板方法模式:在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。而spring提供了相關實現JdbcTemplate,下面代碼示例如何使用:
一、spring配置文件:spring-context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置組件自動掃描裝配路徑及篩選條件 --> <context:component-scan base-package="com.*"/> </beans>
二、jdbctemplate的配置文件:spring-context-jdbctemplate.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置spring自帶的數據源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/test"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
三、具體實現的service類:JdbcTemplateService
package com.test.database.jdbctemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; @Service public class JdbcTemplateService { @Autowired JdbcTemplate jdbcTemplate; public void testJdbcTemplate() { String sql = "select * from user"; System.out.println(jdbcTemplate.queryForList(sql)); } }
四、測試類:JdbcTemplateMain.java
package com.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.test.database.jdbctemplate.JdbcTemplateService; public class JdbcTemplateMain { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml", "spring-context-jdbctemplate.xml"); context.getBean(JdbcTemplateService.class).testJdbcTemplate(); } }
五、執行結果為(數據庫有兩條記錄):
[{id=1, name=tim, password=tim}, {id=2, name=park, password=park}]
另外:配置文件中配置的數據源(dataSource)也可以是一個數據庫連接池。
*************************************************************************************
下面分析一下JdbcTemplate是如何完成數據庫操作的:
jdbcTemplate.queryForList(sql)
跳到JdbcTemplate類中: 下一步: @Override public List<Map<String, Object>> queryForList(String sql) throws DataAccessException { return query(sql, getColumnMapRowMapper()); } 下一步: @Override public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException { return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper))); } 下一步(進入query方法): @Override @Nullable 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 + "]"); } /** * Callback to execute the query. */ class QueryStatementCallback implements StatementCallback<T>, SqlProvider { @Override @Nullable public T doInStatement(Statement stmt) throws SQLException { ResultSet rs = null; try { rs = stmt.executeQuery(sql); return rse.extractData(rs); } finally { JdbcUtils.closeResultSet(rs); } } @Override public String getSql() { return sql; } } return execute(new QueryStatementCallback()); } 下一步: @Override @Nullable public <T> T execute(StatementCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(obtainDataSource()); //獲取connection(算法骨架) Statement stmt = null; try { stmt = con.createStatement(); //獲取statement(算法骨架) applyStatementSettings(stmt); T result = action.doInStatement(stmt); //執行具體的操作(延遲到子類的操作) 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. String sql = getSql(action); JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw translateException("StatementCallback", sql, ex); } finally { JdbcUtils.closeStatement(stmt);//關閉statement(算法骨架) DataSourceUtils.releaseConnection(con, getDataSource());//關閉connection(算法骨架) } }
通過上述代碼分析可以看出,spring的JdbcTemplate為我們實現了模板流程和方法(即算法骨架),我們需要做的就是配置好JdbcTemplate的dataSource,並且將JdbcTemplate納入spring的管理,然后編寫具體的數據庫訪問邏輯就可以了。
一是為我們省了繁瑣的通用流程,像獲取關閉connection、statment;
二是為我們提供了非常豐富且優雅的api進行sql操作。