为使JDBC更加易于使用,Spring在JDBC API上定义了抽象层,以此建立一个JDBC存取框架。作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,每个模板方法都能控制整个过程,并允许覆盖过程中的特定任务;该方式可以在尽可能保留灵活性的情况下, 将数据库存取的工作量降到最低。
Spring对JDBC的支持的核心测试代码下载地址:http://download.csdn.net/download/bingbeichen/9783606。
1. 使用JdbcTemplate操作数据库
Spring的JdbcTemplate可以实现更新、批量更新、查询单行、查询多行和查询单值等功能,具体实现步骤如下说明。
第一步:额外导入C3P0数据源、MySQL数据库和JdbcTemplate所依赖的jar包,具体如下图所示:
第二步:在src目录下创建db.properties文件,用于存放连接数据库所需要的基本信息,文件内容如下所示:
jdbc.user=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///spring4
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
第三步:在src目录下创建Spring bean的配置文件applicationContext.xml,用于配置C3P0数据源和Spring的JdbcTemplate,核心配置代码如下:
<!-- 导入资源文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置C3P0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置Spring的JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
第四步:创建JUnit测试类,开始使用JdbcTemplate操作数据库,测试类的核心代码如下:
package com.qiaobc.spring.jdbc;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
public class JDBCTest {
private ApplicationContext ctx = null;
private JdbcTemplate jdbcTemplate = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
}
/**
* 1. 测试C3P0数据源是否配置成功
* @throws Exception
*/
@Test
public void testDataSource() throws Exception {
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
/**
* 2.1 更新:执行INSERT、UPDATE 或 DELETE操作
*/
@Test
public void testUpdate() {
String sql = "UPDATE employees SET last_name = ? WHERE id = ?";
jdbcTemplate.update(sql, "bb", 1);
}
/**
* 2.2 批量更新:执行批量的INSERT、UPDATE 或 DELETE操作
* 异常:Field 'id' doesn't have a default value,原因是创建表时未设置主键为自增模式
*/
@Test
public void testBatchUpdate() {
String sql = "INSERT INTO employees(first_name, last_name, email) VALUES(?, ?, ?)";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{"qiao1", "jundang", "qiaojd@163.com"});
batchArgs.add(new Object[]{"qiao2", "jundang", "qiaojd@163.com"});
batchArgs.add(new Object[]{"qiao3", "jundang", "qiaojd@163.com"});
batchArgs.add(new Object[]{"qiao4", "jundang", "qiaojd@163.com"});
batchArgs.add(new Object[]{"qiao5", "jundang", "qiaojd@163.com"});
jdbcTemplate.batchUpdate(sql, batchArgs);
}
/**
* 3.1 查询单行:从数据库中获取一条记录,实际得到对应的对象
* 注意:不是调用queryForObject(String sql, Class<Employee> requiredType, Object... args)方法
* 而需要调用queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args)方法:
* 1). RowMapper用于指定如何映射结果集的行,常用的实现类为BeanPropertyRowMapper
* 2). 使用SQL语句中列的别名完成列名和类属性名的映射,如first_name firstName
* 3). 不支持级联属性的映射,JdbcTemplate的实质仍是JDBC而不是ORM
*/
@Test
public void testQueryForObject() {
String sql = "SELECT id, last_name lastName, first_name firstName, email FROM employees WHERE id = ?";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 5);
System.out.println(employee);
}
/**
* 3.2 查询多行:获取实体类的集合
*/
@Test
public void testQueryForList() {
String sql = "SELECT id, last_name lastName, first_name firstName, email FROM employees WHERE id > ?";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
List<Employee> employees = jdbcTemplate.query(sql, rowMapper , 2);
System.out.println(employees);
}
/**
* 3.3 获取单个列的值或做统计查询
* 使用queryForObject(String sql, Class<Integer> requiredType)
*/
@Test
public void testQueryForValue() {
String sql = "SELECT count(id) FROM employees";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(count);
}
}
2. 使用JdbcTemplate实现DAO类
JdbcTemplate利用了Java 1.5的特性(自动装箱、泛型、可变长度等)来简化开发,其实现DAO类的方式主要有两种,具体说明如下:
- 推荐使用: JdbcTemplate类是线程安全的,可在IoC容器中声明其单个实例后,将该实例注入到所有的DAO类中;
- 较少使用: Spring的JDBC框架还提供JdbcDaoSupport类来简化DAO实现,其声明了jdbcTemplate属性,可以从IOC容器中注入或自动从数据源中创建。
2.1 方式一:注入JdbcTemplate
package com.qiaobc.spring.jdbc.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.qiaobc.spring.jdbc.Employee;
/**
* 推荐使用:在使用时仅创建一个JdbcTemplate的实例即可使用
* 需要在配置文件中设置自动扫描该类:
* <context:component-scan base-package="com.qiaobc.spring.jdbc.dao"></context:component-scan>
* @author qiaobc
*
*/
@Repository
public class EmployeeDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public Employee get(Integer id) {
String sql = "SELECT id, first_name firstName, last_name lastName, email FROM employees WHERE id = ?";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
return jdbcTemplate.queryForObject(sql, rowMapper , id);
}
}
2.2 方式二:扩展JdbcDaoSupport
package com.qiaobc.spring.jdbc.dao;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import com.qiaobc.spring.jdbc.Employee;
/**
* 不推荐使用,推荐直接使用JdbcTemplate作为DAO类的成员变量
* @author qiaobc
* Caused by: java.lang.IllegalArgumentException: 'dataSource' or 'jdbcTemplate' is required
*/
@Repository
public class EmployeeKzDao extends JdbcDaoSupport {
@Autowired
public void setDataSource1(DataSource dataSource) { // 其父类的setDataSource()方法是final的
setDataSource(dataSource);
}
public Employee get(Integer id) {
String sql = "SELECT id, first_name firstName, last_name lastName, email FROM employees WHERE id = ?";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
return getJdbcTemplate().queryForObject(sql, rowMapper , id);
}
}
3. 在JDBC模板中使用具名参数
Spring的NamedParameterJdbcTemplate类支持具名参数,即SQL语句按名称(以冒号开头)而不是按位置(以?号表示)进行参数绑定,其更易于维护且可读性较强。
@Test
public void testNamedParameterJdbcTemplate() {
String sql = "INSERT INTO employees(first_name, last_name, email) VALUES(:firstName, :lastName, :email)";
// 可以使用具名参数,即按名称进行参数绑定,但较为麻烦
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("firstName", "qiao");
paramMap.put("lastName", "bei");
paramMap.put("email", "qiaobei@163.com");
namedParameterJdbcTemplate.update(sql, paramMap);
// 使用update(String sql, SqlParameterSource paramSource)方法实现更新操作较为简单,要求具名参数名和类的属性名相同
Employee employee = new Employee(null, "qiao", "dabei", "qiaodabei@163.com");
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(employee);
namedParameterJdbcTemplate.update(sql, paramSource);
}