也许不会看见 2010-09-28
Spring的JDBC框架能够承担资源管理和异常处理的工作,从而简化我们的JDBC代码,
让我们只需编写从数据库读写数据所必需的代码。Spring把数据访问的样板代码隐藏到模板类之下,
结合Spring的事务管理,可以大大简化我们的代码.
Spring提供了3个模板类:
JdbcTemplate:Spring里最基本的JDBC模板,利用JDBC和简单的索引参数查询提供对数据库的简单访问。
NamedParameterJdbcTemplate:能够在执行查询时把值绑定到SQL里的命名参数,而不是使用索引参数。
SimpleJdbcTemplate:利用Java5的特性,比如自动装箱、通用(generic)和可变参数列表来简化JDBC模板的使用。
具体使用哪个模板基本上取决于个人喜好。
使用Spring的JdbcTemplate来实现简单的增删改查,首先建立测试数据表person
createtableperson(
idintnotnullprimarykeyauto_increment,
namevarchar(20)notnull
)
导入依赖的jar包,由于测试中数据源使用的是dbcp数据源,需要以下jar包支持:
commons-logging.jar
commons-pool.jar
commons-dbcp.jar
同时还必须导入数据库驱动jar包:mysql-connector-java-3.1.8-bin.jar
建立实体bean
Person.javapackage 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;
}
} 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.javapackage 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();
} 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.javapackage 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});
}
} 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
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;
}
} 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注入到我们的业务类中
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
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
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);
}
} 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:Incorrectresultsize:expected1,actual0
难道Spring的queryForObject在查找不到记录的时候会抛出异常,看了一下Spring的源代码 发现确实如此: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();
} 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.javapackage 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});
}
} 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
得到预期的结果.
从上面代码可以看出,使用Spring提供的JDBCTemplate类很大程度减少了我们的代码量,
比起以前我们写JDBC操作,需要先获取Connection,然后是PreparedStatement,再到Result,
使用SpringJDBCTemplate写出来的代码看起来更加简洁,开发效率也比较快.
在数据库的操作中,事务是一个重要的概念,举个例子:
大概每个人都有转账的经历。当我们从A帐户向B帐户转100元后,银行的系统会从A帐户上扣除100而在B帐户上加100,这是一般的正常现象。
但是一旦系统出错了怎么办呢,这里我们假设可能会发生两种情况:
(1)A帐户上少了100元,但是B帐户却没有多100元。
(2)B帐户多了100元钱,但是A帐户上却没有被扣钱。
这种错误一旦发生就等于出了大事,那么再假如一下,你要转账的是1亿呢?
所以上面的两种情况分别是你和银行不愿意看到的,因为谁都不希望出错。那么有没有什么方法保证一旦A帐户上没有被扣钱而B帐户上也没有被加钱;
或者A帐户扣了100元而B帐户准确无误的加上100元呢。也就是说要么转账顺利的成功进行,要么不转账呢?可以,这就是数据库事务机制所要起到的作用和做的事情。
Spring对事务的管理有丰富的支持,Spring提供了编程式配置事务和声明式配置事务:
声明式事务有以下两种方式
一种是使用Annotation注解的方式(官方推荐)
一种是基于Xml的方式
采用任何一种方式我们都需要在我们的bean.xml中添加事务支持: