1. 程式人生 > >J2EE系列之Spring4學習筆記(十)--Spring對JDBC的支援

J2EE系列之Spring4學習筆記(十)--Spring對JDBC的支援

一、JdbcDaoSupport類的引入

上一節的工程裡面資料庫操作實現類StudentDaoImpl類中要想使用Spring操作資料庫需要首先定義JdbcTemplate類的物件,這一節我們引入Spring對JDBC支援的JdbcDaoSupport類,首先看一下這個類中的部分內容:

/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.jdbc.core.support;

import java.sql.Connection;

import javax.sql.DataSource;

import org.springframework.dao.support.DaoSupport;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLExceptionTranslator;

/**
 * Convenient super class for JDBC-based data access objects.
 *
 * <p>Requires a {@link javax.sql.DataSource} to be set, providing a
 * {@link org.springframework.jdbc.core.JdbcTemplate} based on it to
 * subclasses through the {@link #getJdbcTemplate()} method.
 *
 * <p>This base class is mainly intended for JdbcTemplate usage but can
 * also be used when working with a Connection directly or when using
 * {@code org.springframework.jdbc.object} operation objects.
 *
 * @author Juergen Hoeller
 * @since 28.07.2003
 * @see #setDataSource
 * @see #getJdbcTemplate
 * @see org.springframework.jdbc.core.JdbcTemplate
 */
public abstract class JdbcDaoSupport extends DaoSupport {

	private JdbcTemplate jdbcTemplate;


	/**
	 * Set the JDBC DataSource to be used by this DAO.
	 */
	public final void setDataSource(DataSource dataSource) {
		if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
			this.jdbcTemplate = createJdbcTemplate(dataSource);
			initTemplateConfig();
		}
	}

	/**
	 * Create a JdbcTemplate for the given DataSource.
	 * Only invoked if populating the DAO with a DataSource reference!
	 * <p>Can be overridden in subclasses to provide a JdbcTemplate instance
	 * with different configuration, or a custom JdbcTemplate subclass.
	 * @param dataSource the JDBC DataSource to create a JdbcTemplate for
	 * @return the new JdbcTemplate instance
	 * @see #setDataSource
	 */
	protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}

	/**
	 * Return the JDBC DataSource used by this DAO.
	 */
	public final DataSource getDataSource() {
		return (this.jdbcTemplate != null ? this.jdbcTemplate.getDataSource() : null);
	}

	/**
	 * Set the JdbcTemplate for this DAO explicitly,
	 * as an alternative to specifying a DataSource.
	 */
	public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
		initTemplateConfig();
	}

	/**
	 * Return the JdbcTemplate for this DAO,
	 * pre-initialized with the DataSource or set explicitly.
	 */
	public final JdbcTemplate getJdbcTemplate() {
	  return this.jdbcTemplate;
	}

	
}

可以看到這個類中定義了JdbcTemplate類的物件,並且裡面有這個物件的get和set方法,裡面還有DataSource類的set方法。

我們修改StudentDaoImpl類,讓這個類繼承JdbcDaoSupport類:

package com.test.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import com.test.dao.StudentDao;
import com.test.model.Student;

public class StudentDaoImpl extends JdbcDaoSupport implements StudentDao{


	@Override
	public int addStudent(Student student) {
		String sql = "insert into t_student values(null,?,?)";
		Object []params = new Object[]{student.getName(),student.getAge()};
		return this.getJdbcTemplate().update(sql,params);
		 
	}

	@Override
	public int updateStudent(Student student) {
		String sql = "update t_student set name=?,age=? where id=?";
		Object []params = new Object[]{student.getName(),student.getAge(),student.getId()};
		return this.getJdbcTemplate().update(sql,params);
	}

	@Override
	public int deleteStudent(int id) {
		String sql = "delete from t_student where id=?";
		Object []params = new Object[]{id};
		return this.getJdbcTemplate().update(sql,params);
	}

	@Override
	public List<Student> findStudents() {
		String sql = "select * from t_student";
		final List<Student> studentList = new ArrayList<Student>();
		this.getJdbcTemplate().query(sql, new RowCallbackHandler(){

			@Override
			public void processRow(ResultSet rs) throws SQLException {
				Student student = new Student();
				student.setId(rs.getInt("id"));
				student.setName(rs.getString("name"));
				student.setAge(rs.getInt("age"));
				studentList.add(student);
			}
			
		});
		return studentList;
	}

}

這裡把之前定義的JdbcTemplate類的物件給去掉了,並呼叫了get方法來獲取父類JdbcDaoSupport類中的JdbcTemplate類物件。

這裡不需要定義JdbcTemplate類的物件了,修改Spring配置檔案:

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
	<context:property-placeholder location="jdbc.properties"/>
	
	
	<bean id="studentDao" class="com.test.dao.impl.StudentDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<bean id="studentService" class="com.test.service.impl.StudentServiceImpl">
		<property name="studentDao" ref="studentDao"></property>
	</bean>
	
</beans>

這裡去掉了定義JdbcTemplate類的例項,把dataSource物件值注入到studentDao例項中。

執行各個測試方法,效果和上一節相同。

二、NamedParameterJdbcTemplate 類的使用

還是在上一節部落格的工程基礎上進行修改。上一節中使用Spring操作資料庫使用的是JdbcTemplate類,使用這個類時要先寫好sql語句然後呼叫JdbcTemplate類中的方法即可。這裡在寫的sql語句格式如下:

String sql="insert into t_student values(null,?,?)";
		Object []params=new Object[]{student.getName(),student.getAge()};
		return jdbcTemplate.update(sql,params);

要傳入的引數用問號?表示,這樣的話程式碼的可讀性不好。

這裡我們不使用JdbcTemplate類了,而是使用Spring對JDBC操作支援的另一個類NamedParameterJdbcTemplate類,這個類的使用方法與JdbcTemplate相差不多,但是它支援命名引數變數。

擷取一部分這個類的程式碼為:

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.jdbc.core.namedparam;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlRowSetResultSetExtractor;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.util.Assert;

/**
 * Template class with a basic set of JDBC operations, allowing the use
 * of named parameters rather than traditional '?' placeholders.
 *
 * <p>This class delegates to a wrapped {@link #getJdbcOperations() JdbcTemplate}
 * once the substitution from named parameters to JDBC style '?' placeholders is
 * done at execution time. It also allows for expanding a {@link java.util.List}
 * of values to the appropriate number of placeholders.
 *
 * <p>The underlying {@link org.springframework.jdbc.core.JdbcTemplate} is
 * exposed to allow for convenient access to the traditional
 * {@link org.springframework.jdbc.core.JdbcTemplate} methods.
 *
 * <p><b>NOTE: An instance of this class is thread-safe once configured.</b>
 *
 * @author Thomas Risberg
 * @author Juergen Hoeller
 * @since 2.0
 * @see NamedParameterJdbcOperations
 * @see org.springframework.jdbc.core.JdbcTemplate
 */
public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations {

	/** Default maximum number of entries for this template's SQL cache: 256 */
	public static final int DEFAULT_CACHE_LIMIT = 256;


	/** The JdbcTemplate we are wrapping */
	private final JdbcOperations classicJdbcTemplate;

	private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;

	/** Cache of original SQL String to ParsedSql representation */
	@SuppressWarnings("serial")
	private final Map<String, ParsedSql> parsedSqlCache =
			new LinkedHashMap<String, ParsedSql>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
				@Override
				protected boolean removeEldestEntry(Map.Entry<String, ParsedSql> eldest) {
					return size() > getCacheLimit();
				}
			};


	/**
	 * Create a new NamedParameterJdbcTemplate for the given {@link DataSource}.
	 * <p>Creates a classic Spring {@link org.springframework.jdbc.core.JdbcTemplate} and wraps it.
	 * @param dataSource the JDBC DataSource to access
	 */
	public NamedParameterJdbcTemplate(DataSource dataSource) {
		Assert.notNull(dataSource, "DataSource must not be null");
		this.classicJdbcTemplate = new JdbcTemplate(dataSource);
	}


	@Override
	public <T> T query(String sql, SqlParameterSource paramSource, ResultSetExtractor<T> rse)
			throws DataAccessException {

		return getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rse);
	}

	@Override
	public <T> T query(String sql, Map<String, ?> paramMap, ResultSetExtractor<T> rse)
			throws DataAccessException {

		return query(sql, new MapSqlParameterSource(paramMap), rse);
	}

	@Override
	public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
		return query(sql, EmptySqlParameterSource.INSTANCE, rse);
	}

	@Override
	public void query(String sql, SqlParameterSource paramSource, RowCallbackHandler rch)
			throws DataAccessException {

		getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rch);
	}

	@Override
	public void query(String sql, Map<String, ?> paramMap, RowCallbackHandler rch)
			throws DataAccessException {

		query(sql, new MapSqlParameterSource(paramMap), rch);
	}

	@Override
	public void query(String sql, RowCallbackHandler rch) throws DataAccessException {
		query(sql, EmptySqlParameterSource.INSTANCE, rch);
	}


	@Override
	public int update(String sql, SqlParameterSource paramSource) throws DataAccessException {
		return getJdbcOperations().update(getPreparedStatementCreator(sql, paramSource));
	}

	@Override
	public int update(String sql, Map<String, ?> paramMap) throws DataAccessException {
		return update(sql, new MapSqlParameterSource(paramMap));
	}

	@Override
	public int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder)
			throws DataAccessException {

		return update(sql, paramSource, generatedKeyHolder, null);
	}

	
	
}

可以看到這個類中需要賦值的屬性沒有,但是構造這個類的物件的時候需要傳入DataSource類的物件。

1.修改StudentDaoImpl類程式碼:

package com.test.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import com.test.dao.StudentDao;
import com.test.model.Student;

public class StudentDaoImpl implements StudentDao{

	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	
	

	public void setNamedParameterJdbcTemplate(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
		this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
	}

	@Override
	public int addStudent(Student student) {
		String sql="insert into t_student values(null,:name,:age)";
		MapSqlParameterSource sps = new MapSqlParameterSource();
		sps.addValue("name", student.getName());
		sps.addValue("age", student.getAge());
		return namedParameterJdbcTemplate.update(sql,sps);
	}

	@Override
	public int updateStudent(Student student) {
		String sql="update t_student set name=:name,age=:age where id=:id";
		MapSqlParameterSource sps = new MapSqlParameterSource();
		sps.addValue("name", student.getName());
		sps.addValue("age", student.getAge());
		sps.addValue("id", student.getId());
		return namedParameterJdbcTemplate.update(sql,sps);
	}

	@Override
	public int deleteStudent(int id) {
		String sql="delete from t_student where id=:id";
		MapSqlParameterSource sps = new MapSqlParameterSource();
		sps.addValue("id", id);
		return namedParameterJdbcTemplate.update(sql,sps);
	}

	@Override
	public List<Student> findStudents() {
		String sql="select * from t_student";
		final List<Student> studentList=new ArrayList<Student>();
		namedParameterJdbcTemplate.query(sql, new RowCallbackHandler(){

			@Override
			public void processRow(ResultSet rs) throws SQLException {
				Student student=new Student();
				student.setId(rs.getInt("id"));
				student.setName(rs.getString("name"));
				student.setAge(rs.getInt("age"));
				studentList.add(student);
			}
			
		});
		return studentList;
	}

}

這裡使用了NamedParameterJdbcTemplate這個類。在寫sql語句的時候需要傳入的引數使用“:引數名”表示,這樣的話增加了程式的可讀性。傳入的引數使用鍵值對的方式儲存在MapSqlParameterSource類的物件中。

2.修改Spring配置檔案為:

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    
	<context:property-placeholder location="jdbc.properties"/>
    
    <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    	<constructor-arg ref="dataSource"></constructor-arg>
    </bean>
	
	
	<bean id="studentDao" class="com.test.dao.impl.StudentDaoImpl">
		<property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property>
	</bean> 
	
	<bean id="studentService" class="com.test.service.impl.StudentServiceImpl">
		<property name="studentDao" ref="studentDao"></property>
	</bean> 
	
</beans>

這裡定義了NamedParameterJdbcTemplate類的例項,使用構造方法注入DataSource類物件。把定義的NamedParameterJdbcTemplate物件例項注入到StudentDaoImpl例項中。

3.執行各個測試方法,效果與使用JdbcTemplate類相同。