Spring的JDBC框架能够承担资源管理和异常处理的工作,从而简化我们的JDBC代码,
让我们只需编写从数据库读写数据所必需的代码。Spring把数据访问的样板代码隐藏到模板类之下,
结合Spring的事务管理,可以大大简化我们的代码.
Spring提供了3个模板类:
JdbcTemplate:Spring里最基本的JDBC模板,利用JDBC和简单的索引参数查询提供对数据库的简单访问。
NamedParameterJdbcTemplate:能够在执行查询时把值绑定到SQL里的命名参数,而不是使用索引参数。
SimpleJdbcTemplate:利用Java 5的特性,比如自动装箱、通用(generic)和可变参数列表来简化JDBC模板的使用。
具体使用哪个模板基本上取决于个人喜好。
使用Spring的JdbcTemplate来实现简单的增删改查,首先建立测试数据表person
create table person(
id int not null primary key auto_increment,
name varchar(20) not null
)
导入依赖的jar包,由于测试中数据源使用的是dbcp数据源,需要以下jar包支持:
commons-logging.jar
commons-pool.jar
commons-dbcp.jar
同时还必须导入数据库驱动jar包:mysql-connector-java-3.1.8-bin.jar
建立实体bean
Person.java
Java代码
1. package com.royzhou.jdbc;
2.
3. public class PersonBean {
4. private int id;
5. private String name;
6.
7. public PersonBean() {
8. }
9.
10. public PersonBean(String name) {
11. this.name = name;
12. }
13.
14. public PersonBean(int id, String name) {
15. this.id = id;
16. this.name = name;
17. }
18.
19. public int getId() {
20. return id;
21. }
22.
23. public void setId(int id) {
24. this.id = id;
25. }
26.
27. public String getName() {
28. return name;
29. }
30.
31. public void setName(String name) {
32. this.name = name;
33. }
34.
35. public String toString() {
36. return this.id + ":" + this.name;
37. }
38. }
package com.royzhou.jdbc;
public class PersonBean {
private int id;
private String name;
public PersonBean() {
}
public PersonBean(String name) {
this.name = name;
}
public PersonBean(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return this.id + ":" + this.name;
}
}
接口类:
PersonService.java
Java代码
1. package com.royzhou.jdbc;
2.
3. import java.util.List;
4.
5. public interface PersonService {
6.
7. public void addPerson(PersonBean person);
8.
9. public void updatePerson(PersonBean person);
10.
11. public void deletePerson(int id);
12.
13. public PersonBean queryPerson(int id);
14.
15. public List<PersonBean> queryPersons();
16. }
package com.royzhou.jdbc;
import java.util.List;
public interface PersonService {
public void addPerson(PersonBean person);
public void updatePerson(PersonBean person);
public void deletePerson(int id);
public PersonBean queryPerson(int id);
public List<PersonBean> queryPersons();
}
实现类:
PersonServiceImpl.java
Java代码
1. package com.royzhou.jdbc;
2.
3. import java.util.List;
4.
5. import javax.sql.DataSource;
6. import java.sql.Types;
7.
8. import org.springframework.jdbc.core.JdbcTemplate;
9.
10. public class PersonServiceImpl implements PersonService {
11.
12. private JdbcTemplate jdbcTemplate;
13.
14. /**
15. * 通过Spring容器注入datasource
16. * 实例化JdbcTemplate,该类为主要操作数据库的类
17. * @param ds
18. */
19. public void setDataSource(DataSource ds) {
20. this.jdbcTemplate = new JdbcTemplate(ds);
21. }
22.
23. public void addPerson(PersonBean person) {
24. /**
25. * 第一个参数为执行sql
26. * 第二个参数为参数数据
27. * 第三个参数为参数类型
28. */
29. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
30. }
31.
32. public void deletePerson(int id) {
33. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
34. }
35.
36. public PersonBean queryPerson(int id) {
37. /**
38. * new PersonRowMapper()是一个实现RowMapper接口的类,
39. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
40. */
41. PersonBean pb = (PersonBean) jdbcTemplate.queryForObject("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
42. return pb;
43. }
44.
45. @SuppressWarnings("unchecked")
46. public List<PersonBean> queryPersons() {
47. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
48. return pbs;
49. }
50.
51. public void updatePerson(PersonBean person) {
52. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
53. }
54. }
package com.royzhou.jdbc;
import java.util.List;
import javax.sql.DataSource;
import java.sql.Types;
import org.springframework.jdbc.core.JdbcTemplate;
public class PersonServiceImpl implements PersonService {
private JdbcTemplate jdbcTemplate;
/**
* 通过Spring容器注入datasource
* 实例化JdbcTemplate,该类为主要操作数据库的类
* @param ds
*/
public void setDataSource(DataSource ds) {
this.jdbcTemplate = new JdbcTemplate(ds);
}
public void addPerson(PersonBean person) {
/**
* 第一个参数为执行sql
* 第二个参数为参数数据
* 第三个参数为参数类型
*/
jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
}
public void deletePerson(int id) {
jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
}
public PersonBean queryPerson(int id) {
/**
* new PersonRowMapper()是一个实现RowMapper接口的类,
* 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
*/
PersonBean pb = (PersonBean) jdbcTemplate.queryForObject("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
return pb;
}
@SuppressWarnings("unchecked")
public List<PersonBean> queryPersons() {
List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
return pbs;
}
public void updatePerson(PersonBean person) {
jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
}
}
PersonRowMapper.java
Java代码
1. package com.royzhou.jdbc;
2.
3. import java.sql.ResultSet;
4. import java.sql.SQLException;
5.
6. import org.springframework.jdbc.core.RowMapper;
7.
8. public class PersonRowMapper implements RowMapper {
9. //默认已经执行rs.next(),可以直接取数据
10. public Object mapRow(ResultSet rs, int index) throws SQLException {
11. PersonBean pb = new PersonBean(rs.getInt("id"),rs.getString("name"));
12. return pb;
13. }
14. }
package com.royzhou.jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class PersonRowMapper implements RowMapper {
//默认已经执行rs.next(),可以直接取数据
public Object mapRow(ResultSet rs, int index) throws SQLException {
PersonBean pb = new PersonBean(rs.getInt("id"),rs.getString("name"));
return pb;
}
}
我们需要在bean.xml中配置DataSource,并且将datasource注入到我们的业务类中
Xml代码
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xmlns:context="http://www.springframework.org/schema/context"
5. xmlns:aop="http://www.springframework.org/schema/aop"
6. xsi:schemaLocation="http://www.springframework.org/schema/beans
7. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
8. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
9. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
10.
11. <context:property-placeholder location="classpath:jdbc.properties"/>
12. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
13. <property name="driverClassName" value="${driverClassName}"/>
14. <property name="url" value="${url}"/>
15. <property name="username" value="${username}"/>
16. <property name="password" value="${password}"/>
17. <!-- 连接池启动时的初始值 -->
18. <property name="initialSize" value="${initialSize}"/>
19. <!-- 连接池的最大值 -->
20. <property name="maxActive" value="${maxActive}"/>
21. <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
22. <property name="maxIdle" value="${maxIdle}"/>
23. <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
24. <property name="minIdle" value="${minIdle}"/>
25. </bean>
26.
27. </beans>
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="${initialSize}"/>
<!-- 连接池的最大值 -->
<property name="maxActive" value="${maxActive}"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="${maxIdle}"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="${minIdle}"/>
</bean>
</beans>
jdbc.properties
Java代码
1. driverClassName=org.gjt.mm.mysql.Driver
2. url=jdbc:mysql://localhost:3306/royzhou?useUnicode=true&characterEncoding=UTF-8
3. username=root
4. password=123456
5. initialSize=1
6. maxActive=500
7. maxIdle=2
8. minIdle=1
driverClassName=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost:3306/royzhou?useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
initialSize=1
maxActive=500
maxIdle=2
minIdle=1
编写我们的测试类:TestJdbcTemplate.java
Java代码
1. package com.royzhou.jdbc;
2.
3. import org.springframework.context.ApplicationContext;
4. import org.springframework.context.support.ClassPathXmlApplicationContext;
5.
6. public class TestJdbcTemplate {
7. public static void main(String[] args) {
8. ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
9. PersonService ps = (PersonService)ctx.getBean("personService");
10. ps.addPerson(new PersonBean("royzhou"));
11. PersonBean pb = ps.queryPerson(1);
12. System.out.println(pb);
13. pb.setName("haha");
14. ps.updatePerson(pb);
15. pb = ps.queryPerson(1);
16. System.out.println(pb);
17. ps.deletePerson(1);
18. pb = ps.queryPerson(1);
19. System.out.println(pb);
20. }
21. }
package com.royzhou.jdbc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestJdbcTemplate {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
PersonService ps = (PersonService)ctx.getBean("personService");
ps.addPerson(new PersonBean("royzhou"));
PersonBean pb = ps.queryPerson(1);
System.out.println(pb);
pb.setName("haha");
ps.updatePerson(pb);
pb = ps.queryPerson(1);
System.out.println(pb);
ps.deletePerson(1);
pb = ps.queryPerson(1);
System.out.println(pb);
}
}
上面代码先插入一条记录,然后修改,之后删除,运行之后出现异常,异常信息:
EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
难道Spring的queryForObject在查找不到记录的时候会抛出异常,看了一下Spring的源代码 发现确实如此:
Java代码
1. public Object queryForObject(String sql, Object[] args, int[] argTypes, RowMapper rowMapper) throws DataAccessException {
2. List results = (List) query(sql, args, argTypes, new RowMapperResultSetExtractor(rowMapper, 1));
3. return DataAccessUtils.requiredUniqueResult(results);
4. }
5.
6. public Object queryForObject(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException {
7. List results = (List) query(sql, args, new RowMapperResultSetExtractor(rowMapper, 1));
8. return DataAccessUtils.requiredUniqueResult(results);
9. }
10.
11. public Object queryForObject(String sql, RowMapper rowMapper) throws DataAccessException {
12. List results = query(sql, rowMapper);
13. return DataAccessUtils.requiredUniqueResult(results);
14. }
15.
16. public static Object requiredUniqueResult(Collection results) throws IncorrectResultSizeDataAccessException {
17. int size = (results != null ? results.size() : 0);
18. if (size == 0) {
19. throw new EmptyResultDataAccessException(1); // 问题在这里
20. }
21. if (!CollectionUtils.hasUniqueObject(results)) {
22. throw new IncorrectResultSizeDataAccessException(1, size);
23. }
24. return results.iterator().next();
25. }
public Object queryForObject(String sql, Object[] args, int[] argTypes, RowMapper rowMapper) throws DataAccessException {
List results = (List) query(sql, args, argTypes, new RowMapperResultSetExtractor(rowMapper, 1));
return DataAccessUtils.requiredUniqueResult(results);
}
public Object queryForObject(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException {
List results = (List) query(sql, args, new RowMapperResultSetExtractor(rowMapper, 1));
return DataAccessUtils.requiredUniqueResult(results);
}
public Object queryForObject(String sql, RowMapper rowMapper) throws DataAccessException {
List results = query(sql, rowMapper);
return DataAccessUtils.requiredUniqueResult(results);
}
public static Object requiredUniqueResult(Collection results) throws IncorrectResultSizeDataAccessException {
int size = (results != null ? results.size() : 0);
if (size == 0) {
throw new EmptyResultDataAccessException(1); // 问题在这里
}
if (!CollectionUtils.hasUniqueObject(results)) {
throw new IncorrectResultSizeDataAccessException(1, size);
}
return results.iterator().next();
}
发现当查找不到记录是,requiredUniqueResult方法做了判断,抛出异常, 想不明白为什么Spring要在这里做这样的判断,为啥不返回null????
重新修改PersonServiceImple类,把queryPerson方法改为使用列表查询的方式再去根据index取
PersonServiceImpl.java
Java代码
1. package com.royzhou.jdbc;
2.
3. import java.util.List;
4.
5. import javax.sql.DataSource;
6. import java.sql.Types;
7.
8. import org.springframework.jdbc.core.JdbcTemplate;
9.
10. public class PersonServiceImpl implements PersonService {
11.
12. private JdbcTemplate jdbcTemplate;
13.
14. /**
15. * 通过Spring容器注入datasource
16. * 实例化JdbcTemplate,该类为主要操作数据库的类
17. * @param ds
18. */
19. public void setDataSource(DataSource ds) {
20. this.jdbcTemplate = new JdbcTemplate(ds);
21. }
22.
23. public void addPerson(PersonBean person) {
24. /**
25. * 第一个参数为执行sql
26. * 第二个参数为参数数据
27. * 第三个参数为参数类型
28. */
29. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
30. }
31.
32. public void deletePerson(int id) {
33. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
34. }
35.
36. @SuppressWarnings("unchecked")
37. public PersonBean queryPerson(int id) {
38. /**
39. * new PersonRowMapper()是一个实现RowMapper接口的类,
40. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
41. */
42. List<PersonBean> pbs = (List<PersonBean>)jdbcTemplate.query("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
43. PersonBean pb = null;
44. if(pbs.size()>0) {
45. pb = pbs.get(0);
46. }
47. return pb;
48. }
49.
50. @SuppressWarnings("unchecked")
51. public List<PersonBean> queryPersons() {
52. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
53. return pbs;
54. }
55.
56. public void updatePerson(PersonBean person) {
57. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
58. }
59. }
package com.royzhou.jdbc;
import java.util.List;
import javax.sql.DataSource;
import java.sql.Types;
import org.springframework.jdbc.core.JdbcTemplate;
public class PersonServiceImpl implements PersonService {
private JdbcTemplate jdbcTemplate;
/**
* 通过Spring容器注入datasource
* 实例化JdbcTemplate,该类为主要操作数据库的类
* @param ds
*/
public void setDataSource(DataSource ds) {
this.jdbcTemplate = new JdbcTemplate(ds);
}
public void addPerson(PersonBean person) {
/**
* 第一个参数为执行sql
* 第二个参数为参数数据
* 第三个参数为参数类型
*/
jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
}
public void deletePerson(int id) {
jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
}
@SuppressWarnings("unchecked")
public PersonBean queryPerson(int id) {
/**
* new PersonRowMapper()是一个实现RowMapper接口的类,
* 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
*/
List<PersonBean> pbs = (List<PersonBean>)jdbcTemplate.query("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
PersonBean pb = null;
if(pbs.size()>0) {
pb = pbs.get(0);
}
return pb;
}
@SuppressWarnings("unchecked")
public List<PersonBean> queryPersons() {
List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
return pbs;
}
public void updatePerson(PersonBean person) {
jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
}
}
再次运行测试类,输出:
1:royzhou
1:haha
null
得到预期的结果.