一起学习Spring4.0(五)Spring对JDBC的支持

本节讲述Spring对JDBC的支持。

JdbcTemplate 简介

为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。

作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法。每个模板方法都能控制整个过程,并允许覆盖过程中的特定任务。通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。

使用 JdbcTemplate更新数据库

用sql语句和参数更新数据库:
update

public int update(String sql,Object… args) throw DataAccessException

批量更新数据库
batchUpdate

public int[] batchUpdate(String sql,List batchArgs)

使用 JdbcTemplate查询数据库

查询单行
queryForObject

public T queryForObject(String sql,ParameterizedRowMapper n,Object… args)throws DataAccessException

便利的BeanPropertyRowMappper实现
org.springframework.jdbc.core.simple
Class ParameterizedBeanPropertyRowMapper

java.lang.Object
BeanPropertyRowMapper
ParameterizedBeanPropertyRowMapper

查询多行
query

public List query(String sql,ParameterizedRowMapper n,Object… args) throws DataAccessException

单值查询
queryForObject

public T queryForObject(String sql,Class requiredType,Object… args) throws DataAccessException

简化JDBC模板查询

每次使用都创建一个JdbcTemplate的新实例,这种做法效率很低下。
JdbcTemplate类被设计成为线程安全的,所以可以在IOC容器中声明它的单个实例,并将这个实例注入到所有的DAO实例中。
JdbcTemplate 也利用了Java 1.5 的特定(自动装箱,泛型,可变长度等)来简化开发。
SpringJDBC框架还提供了一个JdbcDaoSupport类来简化DAO实现。该类声明了jdbcTemplate属性,它可以从IOC容器中注入,或者自动从数据源中创建。
不推荐使用 JdbcDaoSupport,而推荐直接使用 JdbcTemplate作为Dao类的成员变量。

新建项目spring-03。新建文件夹lib。导入如下的包并build path。
c3p0-0.9.1.2.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.2.jar
mysql-connector-java-5.1.22-bin.jar
spring-aop-4.0.4.RELEASE.jar
spring-aspects-4.0.4.RELEASE.jar
spring-beans-4.0.4.RELEASE.jar
spring-context-4.0.4.RELEASE.jar
spring-core-4.0.4.RELEASE.jar
spring-expression-4.0.4.RELEASE.jar
spring-jdbc-4.0.4.RELEASE.jar
spring-orm-4.0.4.RELEASE.jar
spring-tx-4.0.4.RELEASE.jar
spring-web-4.0.4.RELEASE.jar
spring-webmvc-4.0.4.RELEASE.jar

在该项目下新建包com.leezp.spring.jdbc。
在该包下新建Department.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.leezp.spring.jdbc;
public class Department {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + "]";
}
public void setName(String name) {
this.name = name;
}
}

在该包下新建Employee.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.leezp.spring.jdbc;
public class Employee {
private Integer id;
private String lastName;
private String email;
private Department department;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastname() {
return lastName;
}
public void setLastname(String lastname) {
this.lastName = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastname=" + lastName + ", email="
+ email + ", department=" + department + "]";
}
}

新建EmployeeDao.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.leezp.spring.jdbc;
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;
/**
* 注解方式使用JdbcTemplate
*
* @author Lee
*
*/
@Repository
public class EmployeeDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public Employee get(Integer id) {
String sql = " select id,last_name lastName,email from employees where id = ? ";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(
Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, id);
return employee;
}
}

新建DepartmentDao.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.leezp.spring.jdbc;
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.leezp.spring.jdbc.Department;
/**
* 不推荐使用 JdbcDaoSupport,而推荐直接使用 JdbcTemplate作为Dao类的成员变量
*
* @author Lee
*
*/
@Repository
public class DepartmentDao extends JdbcDaoSupport {
@Autowired
public void setDataSource2(DataSource dataSource) {
setDataSource(dataSource);
}
public Department get(Integer id) {
String sql = " select id,dept_name name from departments where id=? ";
RowMapper<Department> rowMapper = new BeanPropertyRowMapper<>(
Department.class);
return getJdbcTemplate().queryForObject(sql, rowMapper, id);
}
}

新建db.properties属性文件。

1
2
3
4
5
6
7
jdbc.user=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring_demo
jdbc.initPoolSize=5
jdbc.maxPoolSize=10

新建applicationContext.xml配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.leezp.spring.jdbc"></context:component-scan>
<!-- 导入资源文件 -->
<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>
</beans>

在该包下new-> JUnit Test Case-> 新建一个JDBCTest.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package com.leezp.spring.jdbc;
import java.sql.SQLException;
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;
private EmployeeDao employeeDao;
private DepartmentDao departmentDao;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
employeeDao = ctx.getBean(EmployeeDao.class);
departmentDao = ctx.getBean(DepartmentDao.class);
}
// 可以 双击方法名,使其变蓝,右键,run as-> JUnit Test 单独运行这一个测试方法
@Test
public void testDepartmentDao() {
System.out.println(departmentDao.get(1));
}
@Test
public void testEmployeeDao() {
System.out.println(employeeDao.get(1));
}
/**
* 获取单个Bean的值,或做统计查询
*
* queryForObject(String sql, Class<Long> requiredType)
*/
@Test
public void testQueryForObject2() {
String sql = " select count(id) from employees ";
long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println(count);
}
/**
* 查到实体类的集合
*
* query(sql, rowMapper, args)
*/
@Test
public void testQueryForList() {
String sql = " SELECT id,last_name lastName,email,dept_id From employees where id>? ";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(
Employee.class);
List<Employee> employees = jdbcTemplate.query(sql, rowMapper, 5);
System.out.println(employees);
}
/**
* 从数据库中获取一条记录,实际得到对应的一个对象
*
* 注意不是调用 queryForObject(String sql, Class<Employee> requiredType,
* Object...args)方法
*
* 而是调用queryForObject(sql, rowMapper, args)方法。
*
* 1.其中的 RowMapper 指定如何去映射结果集的行,常用的实现类为BeanPropertyRowMapper。
*
* 2.使用SQL中列的别名完成列名和类的属性名的映射。例如 last_name lastName。
*
* 3.不支持级联属性,JdbcTemplate到底是一个JDBC的小工具,而不是ORM框架。
*/
// 结果:Employee [id=1, lastname=Tom, email=tom@qq.com, department=null]
@Test
public void testQueryForObject() {
String sql = " SELECT id,last_name lastName,email,dept_id as \"department.id\" From employees where id=? ";
// import org.springframework.jdbc.core.RowMapper;
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(
Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
System.out.println(employee);
}
/**
* 执行批量insert/update/delete
*
* 最后一个参数是 Object[] 的 List 类型: 因为修改一条记录需要一个Object的数组,那么多条不就需要多个Object的数组吗
*/
@Test
public void testBatchUpdate() {
String sql = " INSERT INTO employees(last_name,email,dept_id) values(?,?,?) ";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[] { "AA", "AA@qq.com", 1 });
batchArgs.add(new Object[] { "BB", "BB@qq.com", 2 });
batchArgs.add(new Object[] { "CC", "CC@qq.com", 3 });
jdbcTemplate.batchUpdate(sql, batchArgs);
}
/**
* 执行 INSERT,UPDATE,DELETE
*/
@Test
public void testUpdate() {
String sql = "UPDATE employees SET LAST_NAME=? where ID =? ";
jdbcTemplate.update(sql, "Jack", 5);
}
@Test
public void testDataSource() throws SQLException {
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
}

获取源代码
获取数据库文件

在JDBC模板中使用具名参数

在经典的JDBC用法中,SQL参数是用占位符?表示,并且受到位置的限制。定位参数的问题在于,一旦参数的顺序发生变化,就必须改变参数绑定。
在SpringJDBC框架中,绑定SQL参数的另一种选择是使用具名参数(named parameter)。
具名参数:SQL按名称(以冒号开头)而不是按位置进行指定。具名参数更易于维护,也提升了可读性。具名参数由框架类在运行时用占位符取代。
具名参数只在NamedParameterJdbcTemplate中得到支持。

修改applicationContext.xml配合文件。增加以下内容。

1
2
3
4
5
<!-- 配置namedParameterJdbcTemplate,该对象可以使用具名参数,其没有无参数的构造器,所以必须为其构造器指定参数 -->
<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>

修改Employee.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.leezp.spring.jdbc;
public class Employee {
private Integer deptId;
private String lastName;
private String email;
private Department department;
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastname() {
return lastName;
}
public void setLastname(String lastname) {
this.lastName = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
@Override
public String toString() {
return "Employee [deptId=" + deptId + ", lastName=" + lastName
+ ", email=" + email + ", department=" + department + "]";
}
}

修改JDBCTest.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package com.leezp.spring.jdbc;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
public class JDBCTest {
private ApplicationContext ctx = null;
private JdbcTemplate jdbcTemplate;
private EmployeeDao employeeDao;
private DepartmentDao departmentDao;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
employeeDao = ctx.getBean(EmployeeDao.class);
departmentDao = ctx.getBean(DepartmentDao.class);
namedParameterJdbcTemplate = ctx
.getBean(NamedParameterJdbcTemplate.class);
}
/**
* 使用具名参数时,可以使用update(String sql, SqlParameterSource paramSource)方法进行更新操作
* 1.SQL语句中的参数名和类的属性一致
* 2.使用SqlParameterSource 的 BeanPropertySqlParameterSource实现类作为参数。
*/
@Test
public void testNamedParameterJdbcTemplate2() {
String sql = " INSERT INTO employees(last_name,email,dept_id) values(:lastName,:email,:deptId) ";
Employee employee = new Employee();
employee.setLastname("ABC");
employee.setEmail("lee@leezp.space");
employee.setDeptId(3);
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(
employee);
namedParameterJdbcTemplate.update(sql, paramSource);
}
/**
* 优点:参数多的时候可以为参数起名字,便于维护
*
* 缺点:较为繁琐
*
*/
@Test
public void testNamedParameterJdbcTemplate() {
String sql = " INSERT INTO employees(last_name,email,dept_id) values(:ln,:email,:deptid) ";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("ln", "FF");
paramMap.put("email", "ff@leezp.space");
paramMap.put("deptid", 2);
namedParameterJdbcTemplate.update(sql, paramMap);
}
// 可以 双击方法名,使其变蓝,右键,run as-> JUnit Test 单独运行这一个测试方法
@Test
public void testDepartmentDao() {
System.out.println(departmentDao.get(1));
}
@Test
public void testEmployeeDao() {
System.out.println(employeeDao.get(1));
}
/**
* 获取单个Bean的值,或做统计查询
*
* queryForObject(String sql, Class<Long> requiredType)
*/
@Test
public void testQueryForObject2() {
String sql = " select count(id) from employees ";
long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println(count);
}
/**
* 查到实体类的集合
*
* query(sql, rowMapper, args)
*/
@Test
public void testQueryForList() {
String sql = " SELECT id,last_name lastName,email,dept_id From employees where id>? ";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(
Employee.class);
List<Employee> employees = jdbcTemplate.query(sql, rowMapper, 5);
System.out.println(employees);
}
/**
* 从数据库中获取一条记录,实际得到对应的一个对象
*
* 注意不是调用 queryForObject(String sql, Class<Employee> requiredType,
* Object...args)方法
*
* 而是调用queryForObject(sql, rowMapper, args)方法。
*
* 1.其中的 RowMapper 指定如何去映射结果集的行,常用的实现类为BeanPropertyRowMapper。
*
* 2.使用SQL中列的别名完成列名和类的属性名的映射。例如 last_name lastName。
*
* 3.不支持级联属性,JdbcTemplate到底是一个JDBC的小工具,而不是ORM框架。
*/
// 结果:Employee [id=1, lastname=Tom, email=tom@qq.com, department=null]
@Test
public void testQueryForObject() {
String sql = " SELECT id,last_name lastName,email,dept_id as \"department.id\" From employees where id=? ";
// import org.springframework.jdbc.core.RowMapper;
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(
Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
System.out.println(employee);
}
/**
* 执行批量insert/update/delete
*
* 最后一个参数是 Object[] 的 List 类型: 因为修改一条记录需要一个Object的数组,那么多条不就需要多个Object的数组吗
*/
@Test
public void testBatchUpdate() {
String sql = " INSERT INTO employees(last_name,email,dept_id) values(?,?,?) ";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[] { "AA", "AA@qq.com", 1 });
batchArgs.add(new Object[] { "BB", "BB@qq.com", 2 });
batchArgs.add(new Object[] { "CC", "CC@qq.com", 3 });
jdbcTemplate.batchUpdate(sql, batchArgs);
}
/**
* 执行 INSERT,UPDATE,DELETE
*/
@Test
public void testUpdate() {
String sql = "UPDATE employees SET LAST_NAME=? where ID =? ";
jdbcTemplate.update(sql, "Jack", 5);
}
@Test
public void testDataSource() throws SQLException {
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
}

获取源代码

版权声明:本文为博主原创文章,转载请注明出处 Leezp’s Blog

点击按钮打赏作者!