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

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

接口类: 
PersonService.java

  1. package com.royzhou.jdbc;
  2. import java.util.List;
  3. public interface PersonService {
  4. public void addPerson(PersonBean person);
  5. public void updatePerson(PersonBean person);
  6. public void deletePerson(int id);
  7. public PersonBean queryPerson(int id);
  8. public List<PersonBean> queryPersons();
  9. }

实现类: 
PersonServiceImpl.java

  1. package com.royzhou.jdbc;
  2. import java.util.List;
  3. import javax.sql.DataSource;
  4. import java.sql.Types;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. public class PersonServiceImpl implements PersonService {
  7. private JdbcTemplate jdbcTemplate;
  8. /**
  9. * 通过Spring容器注入datasource
  10. * 实例化JdbcTemplate,该类为主要操作数据库的类
  11. * @param ds
  12. */
  13. public void setDataSource(DataSource ds) {
  14. this.jdbcTemplate = new JdbcTemplate(ds);
  15. }
  16. public void addPerson(PersonBean person) {
  17. /**
  18. * 第一个参数为执行sql
  19. * 第二个参数为参数数据
  20. * 第三个参数为参数类型
  21. */
  22. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
  23. }
  24. public void deletePerson(int id) {
  25. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
  26. }
  27. public PersonBean queryPerson(int id) {
  28. /**
  29. * new PersonRowMapper()是一个实现RowMapper接口的类,
  30. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
  31. */
  32. PersonBean pb = (PersonBean) jdbcTemplate.queryForObject("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
  33. return pb;
  34. }
  35. @SuppressWarnings("unchecked")
  36. public List<PersonBean> queryPersons() {
  37. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
  38. return pbs;
  39. }
  40. public void updatePerson(PersonBean person) {
  41. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
  42. }
  43. }

PersonRowMapper.java

  1. package com.royzhou.jdbc;
  2. import java.sql.ResultSet;
  3. import java.sql.SQLException;
  4. import org.springframework.jdbc.core.RowMapper;
  5. public class PersonRowMapper implements RowMapper {
  6. //默认已经执行rs.next(),可以直接取数据
  7. public Object mapRow(ResultSet rs, int index) throws SQLException {
  8. PersonBean pb = new PersonBean(rs.getInt("id"),rs.getString("name"));
  9. return pb;
  10. }
  11. }

我们需要在bean.xml中配置DataSource,并且将datasource注入到我们的业务类中

  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. <context:property-placeholder location="classpath:jdbc.properties"/>
  11. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  12. <property name="driverClassName" value="${driverClassName}"/>
  13. <property name="url" value="${url}"/>
  14. <property name="username" value="${username}"/>
  15. <property name="password" value="${password}"/>
  16. <!-- 连接池启动时的初始值 -->
  17. <property name="initialSize" value="${initialSize}"/>
  18. <!-- 连接池的最大值 -->
  19. <property name="maxActive" value="${maxActive}"/>
  20. <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
  21. <property name="maxIdle" value="${maxIdle}"/>
  22. <!--  最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
  23. <property name="minIdle" value="${minIdle}"/>
  24. </bean>
  25. </beans>

jdbc.properties

  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

编写我们的测试类:TestJdbcTemplate.java

  1. package com.royzhou.jdbc;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. public class TestJdbcTemplate {
  5. public static void main(String[] args) {
  6. ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
  7. PersonService ps = (PersonService)ctx.getBean("personService");
  8. ps.addPerson(new PersonBean("royzhou"));
  9. PersonBean pb = ps.queryPerson(1);
  10. System.out.println(pb);
  11. pb.setName("haha");
  12. ps.updatePerson(pb);
  13. pb = ps.queryPerson(1);
  14. System.out.println(pb);
  15. ps.deletePerson(1);
  16. pb = ps.queryPerson(1);
  17. System.out.println(pb);
  18. }
  19. }

上面代码先插入一条记录,然后修改,之后删除,运行之后出现异常,异常信息: 
EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

难道Spring的queryForObject在查找不到记录的时候会抛出异常,看了一下Spring的源代码 发现确实如此:

  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. public Object queryForObject(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException {
  6. List results = (List) query(sql, args, new RowMapperResultSetExtractor(rowMapper, 1));
  7. return DataAccessUtils.requiredUniqueResult(results);
  8. }
  9. public Object queryForObject(String sql, RowMapper rowMapper) throws DataAccessException {
  10. List results = query(sql, rowMapper);
  11. return DataAccessUtils.requiredUniqueResult(results);
  12. }
  13. public static Object requiredUniqueResult(Collection results) throws IncorrectResultSizeDataAccessException {
  14. int size = (results != null ? results.size() : 0);
  15. if (size == 0) {
  16. throw new EmptyResultDataAccessException(1); // 问题在这里
  17. }
  18. if (!CollectionUtils.hasUniqueObject(results)) {
  19. throw new IncorrectResultSizeDataAccessException(1, size);
  20. }
  21. return results.iterator().next();
  22. }

发现当查找不到记录是,requiredUniqueResult方法做了判断,抛出异常, 想不明白为什么Spring要在这里做这样的判断,为啥不返回null????

重新修改PersonServiceImple类,把queryPerson方法改为使用列表查询的方式再去根据index取 
PersonServiceImpl.java

  1. package com.royzhou.jdbc;
  2. import java.util.List;
  3. import javax.sql.DataSource;
  4. import java.sql.Types;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. public class PersonServiceImpl implements PersonService {
  7. private JdbcTemplate jdbcTemplate;
  8. /**
  9. * 通过Spring容器注入datasource
  10. * 实例化JdbcTemplate,该类为主要操作数据库的类
  11. * @param ds
  12. */
  13. public void setDataSource(DataSource ds) {
  14. this.jdbcTemplate = new JdbcTemplate(ds);
  15. }
  16. public void addPerson(PersonBean person) {
  17. /**
  18. * 第一个参数为执行sql
  19. * 第二个参数为参数数据
  20. * 第三个参数为参数类型
  21. */
  22. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
  23. }
  24. public void deletePerson(int id) {
  25. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
  26. }
  27. @SuppressWarnings("unchecked")
  28. public PersonBean queryPerson(int id) {
  29. /**
  30. * new PersonRowMapper()是一个实现RowMapper接口的类,
  31. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
  32. */
  33. List<PersonBean> pbs = (List<PersonBean>)jdbcTemplate.query("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
  34. PersonBean pb = null;
  35. if(pbs.size()>0) {
  36. pb = pbs.get(0);
  37. }
  38. return pb;
  39. }
  40. @SuppressWarnings("unchecked")
  41. public List<PersonBean> queryPersons() {
  42. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
  43. return pbs;
  44. }
  45. public void updatePerson(PersonBean person) {
  46. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
  47. }
  48. }

再次运行测试类,输出: 
1:royzhou 
1:haha 
null

得到预期的结果.

从上面代码可以看出,使用Spring提供的JDBCTemplate类很大程度减少了我们的代码量, 
比起以前我们写JDBC操作,需要先获取Connection,然后是PreparedStatement,再到Result, 
使用Spring JDBCTemplate写出来的代码看起来更加简洁,开发效率也比较快.

在数据库的操作中,事务是一个重要的概念,举个例子:

大概每个人都有转账的经历。当我们从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中添加事务支持:

  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. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  9. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  10. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
  11. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
  12. <context:property-placeholder location="classpath:jdbc.properties" />
  13. <bean id="dataSource"
  14. class="org.apache.commons.dbcp.BasicDataSource"
  15. destroy-method="close">
  16. <property name="driverClassName" value="${driverClassName}" />
  17. <property name="url" value="${url}" />
  18. <property name="username" value="${username}" />
  19. <property name="password" value="${password}" />
  20. <!-- 连接池启动时的初始值 -->
  21. <property name="initialSize" value="${initialSize}" />
  22. <!-- 连接池的最大值 -->
  23. <property name="maxActive" value="${maxActive}" />
  24. <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
  25. <property name="maxIdle" value="${maxIdle}" />
  26. <!--  最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
  27. <property name="minIdle" value="${minIdle}" />
  28. </bean>
  29. <bean id="personService"
  30. class="com.royzhou.jdbc.PersonServiceImpl">
  31. <property name="dataSource" ref="dataSource"></property>
  32. </bean>
  33. <bean id="txManager"
  34. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  35. <property name="dataSource" ref="dataSource" />
  36. </bean>
  37. <tx:annotation-driven transaction-manager="txManager" />
  38. </beans>

<tx:annotation-driven transaction-manager="txManager" />  这句话的作用是注册事务注解处理器 
定义好配置文件后我们只需要在我们的类上加上注解@Transactional,就可以指定这个类需要受Spring的事务管理 
默认Spring为每个方法开启一个事务,如果方法发生运行期错误unchecked(RuntimeException),事务会进行回滚 
如果发生checked Exception,事务不进行回滚.

例如在下面的例子中,我们为PersonServiceImpl添加事务支持.

  1. package com.royzhou.jdbc;
  2. import java.util.List;
  3. import javax.sql.DataSource;
  4. import java.sql.Types;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. import org.springframework.transaction.annotation.Transactional;
  7. @Transactional
  8. public class PersonServiceImpl implements PersonService {
  9. private JdbcTemplate jdbcTemplate;
  10. /**
  11. * 通过Spring容器注入datasource
  12. * 实例化JdbcTemplate,该类为主要操作数据库的类
  13. * @param ds
  14. */
  15. public void setDataSource(DataSource ds) {
  16. this.jdbcTemplate = new JdbcTemplate(ds);
  17. }
  18. public void addPerson(PersonBean person) {
  19. /**
  20. * 第一个参数为执行sql
  21. * 第二个参数为参数数据
  22. * 第三个参数为参数类型
  23. */
  24. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
  25. throw new RuntimeException("运行期例外");
  26. }
  27. public void deletePerson(int id) {
  28. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
  29. }
  30. @SuppressWarnings("unchecked")
  31. public PersonBean queryPerson(int id) {
  32. /**
  33. * new PersonRowMapper()是一个实现RowMapper接口的类,
  34. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
  35. */
  36. List<PersonBean> pbs = (List<PersonBean>)jdbcTemplate.query("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
  37. PersonBean pb = null;
  38. if(pbs.size()>0) {
  39. pb = pbs.get(0);
  40. }
  41. return pb;
  42. }
  43. @SuppressWarnings("unchecked")
  44. public List<PersonBean> queryPersons() {
  45. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
  46. return pbs;
  47. }
  48. public void updatePerson(PersonBean person) {
  49. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
  50. }
  51. }

在addPerson方法中我们抛出了一个运行期例外,以此来检查Spring的事务管理.

编写测试类运行:

  1. package com.royzhou.jdbc;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. public class TestJdbcTemplate {
  5. public static void main(String[] args) {
  6. ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
  7. PersonService ps = (PersonService)ctx.getBean("personService");
  8. ps.addPerson(new PersonBean("royzhou"));
  9. }
  10. }

运行测试类,后台输出异常:java.lang.RuntimeException: 运行期例外 
查看数据库发现数据没有插入,说明事务进行了回滚.

再次测试修改为抛出checked Exception

  1. package com.royzhou.jdbc;
  2. import java.util.List;
  3. import javax.sql.DataSource;
  4. import java.sql.Types;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. import org.springframework.transaction.annotation.Transactional;
  7. @Transactional
  8. public class PersonServiceImpl implements PersonService {
  9. private JdbcTemplate jdbcTemplate;
  10. /**
  11. * 通过Spring容器注入datasource
  12. * 实例化JdbcTemplate,该类为主要操作数据库的类
  13. * @param ds
  14. */
  15. public void setDataSource(DataSource ds) {
  16. this.jdbcTemplate = new JdbcTemplate(ds);
  17. }
  18. public void addPerson(PersonBean person) throws Exception {
  19. /**
  20. * 第一个参数为执行sql
  21. * 第二个参数为参数数据
  22. * 第三个参数为参数类型
  23. */
  24. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
  25. throw new Exception("checked 例外");
  26. }
  27. public void deletePerson(int id) {
  28. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
  29. }
  30. @SuppressWarnings("unchecked")
  31. public PersonBean queryPerson(int id) {
  32. /**
  33. * new PersonRowMapper()是一个实现RowMapper接口的类,
  34. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
  35. */
  36. List<PersonBean> pbs = (List<PersonBean>)jdbcTemplate.query("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
  37. PersonBean pb = null;
  38. if(pbs.size()>0) {
  39. pb = pbs.get(0);
  40. }
  41. return pb;
  42. }
  43. @SuppressWarnings("unchecked")
  44. public List<PersonBean> queryPersons() {
  45. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
  46. return pbs;
  47. }
  48. public void updatePerson(PersonBean person) {
  49. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
  50. }
  51. }

后台输出异常:java.lang.Exception: checked 例外 
查看数据库发现数据插入,说明事务没有进行了回滚.

说明了Spring的事务支持默认只对运行期异常(RuntimeException)进行回滚,这里可能有个疑问,我们执行sql操作的时候会发生sql异常,不属于运行期异常,那Spring是怎么进行事务回滚的呢 ???? 
查看了一下JdbcTemplate的源代码发现,JdbcTemplate的处理方法如下:

  1. public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException {
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Executing SQL batch update [" + sql + "]");
  4. }
  5. return (int[]) execute(sql, new PreparedStatementCallback() {
  6. public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
  7. try {
  8. int batchSize = pss.getBatchSize();
  9. if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
  10. for (int i = 0; i < batchSize; i++) {
  11. pss.setValues(ps, i);
  12. ps.addBatch();
  13. }
  14. return ps.executeBatch();
  15. }
  16. else {
  17. int[] rowsAffected = new int[batchSize];
  18. for (int i = 0; i < batchSize; i++) {
  19. pss.setValues(ps, i);
  20. rowsAffected[i] = ps.executeUpdate();
  21. }
  22. return rowsAffected;
  23. }
  24. }
  25. finally {
  26. if (pss instanceof ParameterDisposer) {
  27. ((ParameterDisposer) pss).cleanupParameters();
  28. }
  29. }
  30. }
  31. });
  32. }

在代码中捕获了SQLException然后抛出一个org.springframework.dao.DataAcceddException,该异常继承自org.springframework.core.NestedRuntimeException,NestedRuntimeException 
是一个继承自RuntimeException的抽象类,Spring jdbcTemplate处理发生异常处理后抛出来得异常基本上都会继承NestedRuntimeException,看完之后才确信了Spring默认只对RuntimeException进行回滚

当然我们可可以修改Spring的默认配置,当发生RuntimeException我们也可以不让他进行事务回滚 
只需要加上一个@Transactional(noRollbackFor=RuntimeException.class) 
注意@Transactional只能针对public属性范围内的方法添加

  1. package com.royzhou.jdbc;
  2. import java.util.List;
  3. import javax.sql.DataSource;
  4. import java.sql.Types;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. import org.springframework.transaction.annotation.Transactional;
  7. @Transactional
  8. public class PersonServiceImpl implements PersonService {
  9. private JdbcTemplate jdbcTemplate;
  10. /**
  11. * 通过Spring容器注入datasource
  12. * 实例化JdbcTemplate,该类为主要操作数据库的类
  13. * @param ds
  14. */
  15. public void setDataSource(DataSource ds) {
  16. this.jdbcTemplate = new JdbcTemplate(ds);
  17. }
  18. @Transactional(noRollbackFor=RuntimeException.class)
  19. public void addPerson(PersonBean person) {
  20. /**
  21. * 第一个参数为执行sql
  22. * 第二个参数为参数数据
  23. * 第三个参数为参数类型
  24. */
  25. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
  26. throw new RuntimeException("运行期例外");
  27. }
  28. public void deletePerson(int id) {
  29. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
  30. }
  31. @SuppressWarnings("unchecked")
  32. public PersonBean queryPerson(int id) {
  33. /**
  34. * new PersonRowMapper()是一个实现RowMapper接口的类,
  35. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
  36. */
  37. List<PersonBean> pbs = (List<PersonBean>)jdbcTemplate.query("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
  38. PersonBean pb = null;
  39. if(pbs.size()>0) {
  40. pb = pbs.get(0);
  41. }
  42. return pb;
  43. }
  44. @SuppressWarnings("unchecked")
  45. public List<PersonBean> queryPersons() {
  46. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
  47. return pbs;
  48. }
  49. public void updatePerson(PersonBean person) {
  50. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
  51. }
  52. }

运行测试类:

  1. package com.royzhou.jdbc;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. public class TestJdbcTemplate {
  5. public static void main(String[] args) {
  6. ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
  7. PersonService ps = (PersonService)ctx.getBean("personService");
  8. ps.addPerson(new PersonBean("royzhou"));
  9. }
  10. }

后台抛出异常,查看数据库,记录插入进去了,说明我们配置事务不对RuntimeException回滚生效了. 
既然可以配置不对RuntimeException回滚,那我们也可以配置对Exception进行回滚,主要用到的是 
@Transactional(rollbackFor=Exception.class)

对于一些查询工作,因为不需要配置事务支持,我们配置事务的传播属性: 
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

readOnly=true表示事务中不允许存在更新操作.

关于事务的传播属性有下面几种配置: 
REQUIRED:业务方法需要在一个事务中运行,如果方法运行时,已经处于一个事务中,那么加入到该事务中,否则自己创建一个新的事务.(Spring默认的事务传播属性) 
NOT_SUPPORTED:声明方法不需要事务,如果方法没有关联到一个事务,容器不会为它开启事务,如果方法在一个事务中被调用,该事务被挂起,在方法调用结束后,原先的事务便会恢复执行 
REQUIRESNEW:不管是否存在事务,业务方法总会为自己发起一个新的事务,如果方法运行时已经存在一个事务,则该事务会被挂起,新的事务被创建,知道方法执行结束,新事务才结束,原先的事务才恢复执行. 
MANDATORY:指定业务方法只能在一个已经存在的事务中执行,业务方法不能自己发起事务,如果业务方法没有在事务的环境下调用,则容器会抛出异常 
SUPPORTS:如果业务方法在事务中被调用,则成为事务中的一部分,如果没有在事务中调用,则在没有事务的环境下执行 
NEVER:指定业务方法绝对不能在事务范围内运行,否则会抛出异常. 
NESTED:如果业务方法运行时已经存在一个事务,则新建一个嵌套的事务,该事务可以有多个回滚点,如果没有事务,则按REQUIRED属性执行. 注意:业务方法内部事务的回滚不会对外部事务造成影响,但是外部事务的回滚会影响内部事务

关于使用注解的方式来配置事务就到这里, 
我们还可以使用另外一种方式实现事务的管理,通过xml文件的配置,主要通过AOP技术实现:

首先把我们的业务类的注解去掉:

  1. package com.royzhou.jdbc;
  2. import java.util.List;
  3. import javax.sql.DataSource;
  4. import java.sql.Types;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. public class PersonServiceImpl implements PersonService {
  7. private JdbcTemplate jdbcTemplate;
  8. /**
  9. * 通过Spring容器注入datasource
  10. * 实例化JdbcTemplate,该类为主要操作数据库的类
  11. * @param ds
  12. */
  13. public void setDataSource(DataSource ds) {
  14. this.jdbcTemplate = new JdbcTemplate(ds);
  15. }
  16. public void addPerson(PersonBean person) {
  17. /**
  18. * 第一个参数为执行sql
  19. * 第二个参数为参数数据
  20. * 第三个参数为参数类型
  21. */
  22. jdbcTemplate.update("insert into person values(null,?)", new Object[]{person.getName()}, new int[]{Types.VARCHAR});
  23. throw new RuntimeException("运行期例外");
  24. }
  25. public void deletePerson(int id) {
  26. jdbcTemplate.update("delete from person where id = ?", new Object[]{id}, new int[]{Types.INTEGER});
  27. }
  28. @SuppressWarnings("unchecked")
  29. public PersonBean queryPerson(int id) {
  30. /**
  31. * new PersonRowMapper()是一个实现RowMapper接口的类,
  32. * 执行回调,实现mapRow()方法将rs对象转换成PersonBean对象返回
  33. */
  34. List<PersonBean> pbs = (List<PersonBean>)jdbcTemplate.query("select id,name from person where id = ?", new Object[]{id}, new PersonRowMapper());
  35. PersonBean pb = null;
  36. if(pbs.size()>0) {
  37. pb = pbs.get(0);
  38. }
  39. return pb;
  40. }
  41. @SuppressWarnings("unchecked")
  42. public List<PersonBean> queryPersons() {
  43. List<PersonBean> pbs = (List<PersonBean>) jdbcTemplate.query("select id,name from person", new PersonRowMapper());
  44. return pbs;
  45. }
  46. public void updatePerson(PersonBean person) {
  47. jdbcTemplate.update("update person set name = ? where id = ?", new Object[]{person.getName(), person.getId()}, new int[]{Types.VARCHAR, Types.INTEGER});
  48. }
  49. }

然后我们需要在bean.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. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  9. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  10. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
  11. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
  12. <context:property-placeholder location="classpath:jdbc.properties" />
  13. <bean id="dataSource"
  14. class="org.apache.commons.dbcp.BasicDataSource"
  15. destroy-method="close">
  16. <property name="driverClassName" value="${driverClassName}" />
  17. <property name="url" value="${url}" />
  18. <property name="username" value="${username}" />
  19. <property name="password" value="${password}" />
  20. <!-- 连接池启动时的初始值 -->
  21. <property name="initialSize" value="${initialSize}" />
  22. <!-- 连接池的最大值 -->
  23. <property name="maxActive" value="${maxActive}" />
  24. <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
  25. <property name="maxIdle" value="${maxIdle}" />
  26. <!--  最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
  27. <property name="minIdle" value="${minIdle}" />
  28. </bean>
  29. <bean id="personService"
  30. class="com.royzhou.jdbc.PersonServiceImpl">
  31. <property name="dataSource" ref="dataSource"></property>
  32. </bean>
  33. <bean id="txManager"
  34. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  35. <property name="dataSource" ref="dataSource" />
  36. </bean>
  37. <!-- 定义事务传播属性 -->
  38. <tx:advice id="txAdvice" transaction-manager="txManager">
  39. <tx:attributes>
  40. <tx:method name="query*" propagation="NOT_SUPPORTED" read-only="true"/>
  41. <tx:method name="*" propagation="REQUIRED"/>
  42. </tx:attributes>
  43. </tx:advice>
  44. <aop:config>
  45. <aop:pointcut id="transactionPointCut" expression="execution(* com.royzhou.jdbc..*.*(..))"/>
  46. <aop:advisor pointcut-ref="transactionPointCut" advice-ref="txAdvice"/>
  47. </aop:config>
  48. </beans>

从新运行测试类 
后台抛出RuntimeException异常,查看数据库,没有插入数据,说明事务进行回滚,XML方式的配置也生效了.

另外还有一个编程式的事务处理,但是它有些侵入性。通常我们的事务需求并没有要求在事务的边界上进行如此精确的控制。 
我们一般采用"声明式事务"。

总结一下: 
事务是企业应用开发的重要组成部分,他使软件更加可靠。它们确保一种要么全有要么全无的行为,防止数据不一致而导致的不可预测的错误发生。 
它们同时也支持并发,防止并发应用线程在操作同一数据时互相影响。以前我们写Jdbc代码的时候,可能需要自己手动去开启事务,然后方法执行结束之后 
再去提交事务,全部都嵌套在我们的业务代码之中,具有很强的侵入性.... 
使用Spring提供事务管理机制,我们只需要配置XML或使用Annotion进行注解就可以实现事务的管理和配置,减少了代码之间的耦合,配置也很方便,很大程度上提升了我们的开发效率.

Spring JdbcTemplate 与 事务管理 学习的更多相关文章

  1. Spring jdbctemplate和事务管理器 全注解配置 不使用xml

    /** * spring的配置类,相当于bean.xml */@Configuration//@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans ...

  2. Spring jdbctemplate和事务管理器

    内部bean 对象结构: @Autowiredprivate IAccountService accountService; @Service("accountService")@ ...

  3. spring事务管理学习

    spring事务管理学习 spring的事务管理和mysql自己的事务之间的区别 参考很好介绍事务异常回滚的文章 MyBatis+Spring 事务管理 spring中的事务回滚例子 这篇文章讲解了@ ...

  4. spring深入学习(五)-----spring dao、事务管理

    访问数据库基本是所有java web项目必备的,不论是oracle.mysql,或者是nosql,肯定需要和数据库打交道.一开始学java的时候,肯定是以jdbc为基础,如下: private sta ...

  5. Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制

    Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制 201311.27 代码下载 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 前言 ...

  6. Spring入门5.事务管理机制

    Spring入门5.事务管理机制 20131126 代码下载 : 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 回顾之前的知识,Spring 最为核心的两个部分 ...

  7. (转)使用Spring配置文件实现事务管理

    http://blog.csdn.net/yerenyuan_pku/article/details/52886207 前面我们讲解了使用Spring注解方式来管理事务,现在我们就来学习使用Sprin ...

  8. Spring中的事务管理

    事务简介: 事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性 事务就是一系列的动作,它们被当作一个单独的工作单元.这些动作要么全部完成,要么全部不起作用 事务的四个关键属性( ...

  9. Spring中的事务管理详解

    在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...

随机推荐

  1. Android学习总结(十四) ———— ListView Item多布局的实现

    一.基本概念 实现一个Item的多布局.像我们经常在用的各种即时通讯工具,QQ.微信等,假设他们的会话界面是ListView实现的,那么ListView就有多种Item布局,要实现ListView里面 ...

  2. vijos 1190 繁忙的都市

    描述 城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造.城市C的道路是这样分布的:城市中有n个交叉路口,有些交叉路口之间有道路相连,两个交叉路口之间最多有一条道路 ...

  3. Eclipse Java类编辑器里出现乱码的解决方案

    如图:在Java Class编辑器里出现的这种乱码,非常烦人. 解决方案:Windows->Preference->General->Appearance, 在里面将Theme设置成 ...

  4. vue cli的安装与使用

    一.简介 vue作为前端开发的热门工具,受到前端开发人员的喜爱.为了简化项目的构建,采用vue cli来简化开发. vue cli是一个全局安装的npm包,提供了终端使用的命令.vue create可 ...

  5. 设置与使用SQL Server的字符集(Collation,即排序规则)

    目录 目录 正确认识SQL Server的字符集 选择合适的SQL Server字符集 错误使用SQL Server的字符集 参考资料 正确认识SQL Server的字符集 SQL Server作为一 ...

  6. Linux安全调优1:CentOS防火墙的设置与优化

    CentOS防火墙的设置与优化 时间:2014-09-11 02:11来源:blog.csdn.net 作者:成长的小虫 的BLOG 举报 点击:4908次 一.设置主机防火墙. 开放: 服务器的:w ...

  7. Ubuntu创建应用快捷方式

    Ubuntu创建应用快捷方式 新建一个.desktop文件 vi eclipse.desktop 然后又进行编辑 [Desktop Entry] Encoding=UTF-8 Name=eclipse ...

  8. 几种优化web页面加载速度的策略

    剥离静态资源请求到CDN 一般在主域名下的HTTP请求里都会携带大量Cookie信息,最大4KB,每个域名下最多50条:但如果仅仅访问js/css/jpeg等静态资源文件的话是不需要Cookie信息, ...

  9. ajax实现上传图片保存到后台并读取

    上传图片有两种方式: 1.fileReader  可以把图片解析成base64码的格式,简单粗暴 2.canvas  可以重新绘制一张图片,可以先把获取得到的图片的blob放进canvas里面,再生成 ...

  10. 【java】抽象类继承关系

    抽象类: 抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充. 一个类不能同时被 abstract 和 final 修饰.如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将 ...