前面总结中分别使用了单纯的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操作。