七
29
23_传智播客Spring2.5视频教程_使用Spring注解方式管理事务与传播行为详解
前面讲解到使用@Transactionl对业务方法进行事务管理;执行业务方法前打开事务,执行完毕后关闭事务;那么,
Q:Spring什么时候决定事务提交,什么时候决定事务回滚(失败)呢?
A:Spring默认时对运行期例外回滚,对用户例外(比如check)不回滚;
check例外,一个方法抛出的例外需要调用者捕获的话,就是check例外;
一个方法抛出的例外,不需要调用者捕获的话,就是非check例外;如运行期例外;
—————
运行期例外 // unchecked;默认回滚
Service层代码:
public void delete( Integer personid ){jdbcTemplate.update(“delete from person where id=?”, new Object[]{personid} ,new int[] {java.sql.Types.INTEGER});throw new RuntimeException(“运行期例外”);}
此时单元测试时调用是不需要check该异常的
@Test public void delete(){personService.delete(6);}
需要check的例外 // checked; 默认不回滚
Service层代码:
public void delete( Integer personid ) throws Exception{jdbcTemplate.update(“delete from person where id=?”, new Object[]{personid} ,new int[] {java.sql.Types.INTEGER});throw new Exception(“非运行期例外”);}
单元测试如下:
@Test public void delete(){try{personService.delete(6);}catch (Exception e){e.printStackTrace();}}
如果需要check的例外需要回滚,则需要设置@Transactional参数
@Transactional ( rollbackFor=Exception.class )public void delete( Integer personid ) throws Exception{jdbcTemplate.update(“delete from person where id=?”, new Object[]{personid} ,new int[] {java.sql.Types.INTEGER});throw new Exception(“非运行期例外”);}
当某个方法不需要事务是怎么处理?
比如,getPerson(Integer personid)方法就不需要开启事务。开启事务是需要消耗性能的;
这时,我们需要是使用@Transactional(propagation=Propagation.NOT_SUPPORTED)来指明该方法不需要事务处理;NOT_SUPPORTED为事务传播属性;
事务传播属性
REQUIRED:业务方法需要在一个事务中运行,如果方法运行时已经处在一个事务中,那么如何加入到该事务,否则为自己创建一个新事务;// 默认80%都是这种事务传播行为;
NOT_SUPPORTED:声明方法不需要事务,如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,知道方法执行结束,新事务才算结束,原先的事务才会恢复执行。
MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。
SUPPORTS:这一事务属性表明,如果业务方法在某个事物范围内被调用,则方法称为该事务的一部分。如果业务方法在事务范围外被调用,则方法在某有事务的环境下执行。
Never:执行业务方法绝对不能在事务范围内执行。如果业务方法在某个事物中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行,它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效;
另,
前几种REQUIRED,NOT_SUPPORTED,REQUIRESNEW,MANDATORY,SUPPORTS,Never在EJB中也有体现(Spring借鉴EJB的地方);最后一种NESTED为Spring独有;
—————-
事务传播属性NESTED介绍
@Resource OtherService otherService;public void xxx(){stmt.executeUpdate(“update person set name=’888′ where id = 1″);otherService.update();// OtherService的update方法的事务传播属性为NESTEDstmt.executeUpdate(“delete from person where id = 9″);}
上面的xxx()方法事务在内部执行的过程
Connection conn = null;try {conn.setAutoCommit(false);Statement stmt = conn.createStatement();stmt.executeUpdate(“update person set name = ’888′ where id = 1″);Savepoint savepoint = conn.setSavepoint();try{conn.createStatement().executeUpdate(“update person set name = ’222′ where sid = 2″);}catch(Exception ex){conn.rollback(savepoint);}stmt.executeUpdate(“delete from person where id = 9″);conn.commit();stmt.close();}catch (Exception e){conn.rollback();}finally{try{if(null != conn && !conn.isClosed()) conn.close();}catch( SQLException e){e.printStackTrace();}}
总结:
内部事务不会影响到外部事物的结果;
但外部事务如果失败,内部事务同样也要回滚;
—————-
数据库系统提供了四种事务隔离级别
数据库系统提供了四种事务隔离级别供用户选择。不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低。大多数据库默认的隔离界别为Read Commited,如SQL Server 2000,当然也有少部分数据库默认的隔离级别为Repeatable Read,如Mysql;SQL Server 2005提供了默认的Read Commited的隔离级别,当然也通过快照技术提供了Repeatable Read隔离级别(推荐使用后者)的支持;
- Read Uncommited:读未提交数据(会出现脏读,不可重复读和幻读);
- Read Commited:读提交数据(会出现不可重复读和幻读);
- Repeatable Read:可重复读(会出现幻读)
- Serializable:串行化
脏读:一个事务读取到另一个事务未提交的更新数据;
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已经提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已经提交的更新数据。
—————-
快照技术实现可重复读
—————-
代码:
创建表:person.sql
/*
Navicat MySQL Data TransferSource Server : localhost
Source Server Version : 50133
Source Host : localhost:3306
Source Database : itcastTarget Server Type : MYSQL
Target Server Version : 50133
File Encoding : 65001Date: 2010-07-23 15:54:58
*/SET FOREIGN_KEY_CHECKS=0;
– —————————-
– Table structure for `person`
– —————————-
DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;– —————————-
– Records of person
– —————————-
PersonServiceBean.java
package cn.itcast.service.impl;import cn.itcast.bean.Person;
import cn.itcast.service.PersonService;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;/**
*
* @author kang.cunhua
*/
@Transactional
public class PersonServiceBean implements PersonService {private JdbcTemplate jdbcTemplate;@Override
public void save(Person person) {
System.out.println(“俺是save(Person person)方法!”);
jdbcTemplate.update(“insert into person(name) values(?)”,
new Object[]{person.getName()},
new int[]{java.sql.Types.VARCHAR});}@Override
public void update(Person person) {
System.out.println(“俺是update(Person person)方法!”);
jdbcTemplate.update(“update person set name=? where id=?”,
new Object[]{person.getName(), person.getId()},
new int[]{java.sql.Types.VARCHAR, java.sql.Types.INTEGER});}@Override
public Person getPerson(Integer personid) {
System.out.println(“俺是getPerson(Integer personid)方法!”);
return (Person) jdbcTemplate.queryForObject(“select * from person where id=?”, new Object[]{personid},
new int[]{java.sql.Types.INTEGER},
new PersonRowMapper());
}@Override
public List<Person> getPersons() {
System.out.println(“俺是getPersons()方法!”);
return (List<Person>) jdbcTemplate.query(“select * from person”, new PersonRowMapper());
}@Override
public void delete(Integer personid) {
System.out.println(“俺是delete(Integer personid)方法!”);
jdbcTemplate.update(“delete from person where id=?”,
new Object[]{personid},
new int[]{java.sql.Types.INTEGER});
throw new RuntimeException(“运行期例外”); // 抛出运行期例外,测试事务回滚!
}/**
* @param dataSource the dataSource to set
*/
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
PersonServiceBeanTest.java
package cn.itcast.service.impl;import cn.itcast.bean.Person;
import cn.itcast.service.PersonService;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/**
*
* @author kang.cunhua
*/
public class PersonServiceBeanTest {private static PersonService personService;public PersonServiceBeanTest() {
}@BeforeClass
public static void setUpClass() throws Exception {
ApplicationContext cxt = new ClassPathXmlApplicationContext(“beans.xml”);
personService = (PersonService) cxt.getBean(“personService”);
}@AfterClass
public static void tearDownClass() throws Exception {
}@Before
public void setUp() {
}/**
* Test of save method, of class PersonServiceBean.
*/
@Test
public void testSave() {
for (int i = 0; i < 5; i++) {
personService.save(new Person(“传智播客” + i));
}}/**
* Test of getPerson method, of class PersonServiceBean.
*/
@Test
public void testGetPerson() {
Person person = personService.getPerson(2);
System.out.println(” testGetPerson() :” + person.getName());
}/**
* Test of update method, of class PersonServiceBean.
*/
@Test
public void testUpdate() {
Person person = personService.getPerson(2);
person.setName(“张**”);
personService.update(person);}/**
* Test of getPersons method, of class PersonServiceBean.
*/
@Test
public void testGetPersons() {
for (Person person:personService.getPersons()) {
System.out.println(“testGetPersons():” + person.getName());
}
}/**
* Test of delete method, of class PersonServiceBean.
*/
@Test
public void testDelete() {
personService.delete(5);
}
}
另,代码变动不大。其余代码和上一讲一样。主要是事务理念的理解。为今后的应用打下基础。
路过