J2EE系列之Spring4學習筆記(十)--Spring對JDBC的支援
阿新 • • 發佈:2019-02-15
一、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類相同。