[TOC]
# 事务其他属性
事务还有一些其他的特点,如在业务bean——PersonServiceBean中,有些业务方法是不需要进行事务管理的,比方说获取数据的方法,那么这个时候我们就需要用到事物的propagation属性了,该属性指定了事务的传播行为。所以,我们应将getPerson()方法的代码修改为:
~~~
/**
* 使用JdbcTemplate获取一条记录
*/
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public Person getPerson(Integer personid) {
return jdbcTemplate.queryForObject("select * from person where id=?", new Object[]{personid},
new int[]{java.sql.Types.INTEGER}, new PeronRowMapper());
}
~~~
还要将getPersons()方法的代码修改为:
~~~
/**
* 使用JdbcTemplate获取多条记录
*/
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public List<Person> getPersons() {
return jdbcTemplate.query("select * from person", new PeronRowMapper());
}
~~~
这样,当这2个业务方法执行的时候,它都不会开启事务,能节约资源,提供效率。
下面,我们就来对事务传播属性做一个总结:
* REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么这个时候就会加入到该事务中,如果当前没有事务环境的话,就会为自己创建一个新的事务。默认情况下,业务方法的事务传播属性就是REQUIRED。在应用开发中,80%的情况下都会使用这种事务传播属性。
* NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用(在其他业务bean的方法中被调用了,而其他业务bean的方法是开启了事务的),该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
* REQUIRESNEW:该属性表明不管当前是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。
* MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出异常。
* SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。即当标注了事务传播属性——SUPPORTS的业务方法在另一个bean的业务方法中执行时,如果另一个bean的业务方法开启了事务,那么执行到标注了事务传播属性——SUPPORTS的业务方法时,它就会处在事务中执行,如果另一个bean的业务方法也没开启事务,那么标注了事务传播属性——SUPPORTS的业务方法也在没有事务的环境中进行。
* Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行。
* NESTED:如果一个活动的事务存在,则当前方法运行在一个嵌套的事务中。 如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效果。
接下来,我们着重介绍事务传播属性——NESTED。如有下面一段代码:
~~~
@Resource OtherService otherService;
public void xxx() {
stmt.executeUpdate("update person set name='888' where id=1");
otherService.update(); // OtherService的update()方法的事务传播属性为NESTED
stmt.executeUpdate("delete from person where id=9");
}
~~~
将以上代码展开,可能就变成了如下这样一段代码:
~~~
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();
}
}
}
~~~
其中,OtherService中标注事务传播属性为NESTED的update()方法,就相当于这样一段代码:
~~~
Savepoint savepoint = conn.setSavepoint(); // 保存点
try{
conn.createStatement().executeUpdate("update person set name='222' where sid=2");
}catch(Exception ex){
conn.rollback(savepoint);
}
~~~
我们也就明白了内部事务的回滚不会对外部事务造成影响。
# 事务的其他属性
除了事务传播属性外,事务还有一些其他的属性:
* readOnly属性:设置为只读事务,对于只读事务,它就不能进行更新操作,一般只存在数据读取的时候,可以将readOnly属性设置为true,可提供效率。
* timeout属性:代表事务的超时时间,默认为30s,一般情况下都不需要设置超时时间。
# 事务的isolation属性
事务的isolation属性指定了事务的隔离级别,实际上事务的隔离级别并不是由Spring容器决定的,而是由底层数据库决定的。
# 数据库系统提供了四种事务隔离级
数据库系统提供了四种事务隔离级别供用户选择。不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,但对并发访问数据库的性能影响最大。Read Uncommited的隔离级别最低。大多数据库默认的隔离级别为Read Commited,如SqlServer,当然也有少部分数据库默认的隔离级别为Repeatable Read,如Mysql。
* Read Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)
* Read Commited:读已提交数据(会出现不可重复读和幻读)
* Repeatable Read:可重复读(会出现幻读)
* Serializable:串行化
脏读:一个事务读取到另一事务未提交的更新新据。前提是并发的两个或多个事务。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。
可重复读:在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。目前要实现可重复读的话,一般数据库采用快照技术,在某一时刻(点),当你访问数据的时候,它把这个数据作为一个镜像,以后在同一个事务中再去读取相同记录的数据时,它都可以从快照里面返回这个数据,不管外部怎么样对它操作,在多次读取的时候都不会受到影响。
幻读:一个事务读取到另一事务已提交的insert数据。
- 基础
- 编译和安装
- scanner类(键盘录入)
- Random类(随机数)
- 数组
- 方法
- 类
- ArrayList集合
- char与int
- eclipse
- IDEA
- 变量与常量
- 常用API
- String,StringBuffer,StringBuilder
- 正则,Date,DateFormat,Calendar
- 包装类,System,Math,Arrays,BigInteger,BigDecimal
- 集合,迭代器,增强for,泛型
- List,set,判断集合唯一
- map,Entry,HashMap,Collections
- 异常
- IO
- File
- 递归
- 字节流
- 字符流
- IO流分类
- 转换流
- 缓冲流
- 流的操作规律
- properties
- 序列化流与反序列化流
- 打印流
- commons-IO
- IO流总结
- 多线程
- 线程池
- 线程安全
- 线程同步
- 死锁
- lock接口
- ThreadLoad
- 等待唤醒机制
- 线程状态
- jdbc
- DBUtils
- 连接池DBCP
- c3p0连接池
- 网络编程
- 多线程socket上传图片
- 反射
- xml
- 设计模式
- 装饰器模式
- web service
- tomcat
- Servlet
- response
- request
- session和cookie
- JSP
- EL
- JSTL
- 事务
- 监听器Listener
- 过滤器Filter
- json
- linux安装软件
- 反射详解
- 类加载器和注解
- 动态代理
- jedis
- Hibernate
- 简介
- 创建映射文件
- Hibernate核心配置文件
- 事务和增删改查
- HibernateUtils
- 持久化对象的三种状态
- 检索方式
- query
- Criteria
- SQLQuery
- 持久化类
- 主键生成策略
- 缓存
- 事务管理
- 关系映射
- 注解
- 优化
- struts2
- 搭建
- 配置详解
- Action
- 结果跳转方式
- 访问ServletAPI方式
- 如何获得参数
- OGNL表达式
- valueStack 值栈
- Interceptor拦截器
- spring
- 导包
- IOC和DI
- Bean获取与实例化
- Bean属性注入
- spring注解
- 注解分层
- junit整合
- aop
- 动态代理实现
- cglib代理实现
- aop名词
- spring的aop
- aop-xml详解
- aop-注解详解
- 代理方式选择
- jdbcTemplate
- spring事务管理
- 回滚注意
- 事务传播属性
- MyBatis
- MyBatis简介
- 入门程序
- 与jdbc hibernate不同
- 原始Dao开发
- Mapper动态代理方式
- SqlMapConfig.xml配置文件
- 输入参数pojo包装类
- resultMap
- 动态sql
- 一对一关联
- 一对多
- 整合spring
- 逆向工程
- maven
- maven简介
- 仓库
- maven目录结构
- maven常用命令
- 生命周期
- eclipse中maven插件
- 入门程序
- 整合struct
- 依赖范围
- 添加插件
- idea配置
- jar包冲突
- 分模块开发
- 构建可执行的jar包(包含依赖jar包)
- springMVC
- 处理流程
- java面试
- java版本升级
- java1-8版本变更
- java9新特性
- 锁
- java资料
- idea
- jdk版本切换
- log4j
- 入门实例
- 基本使用方法
- Web中使用Log4j
- spring中使用log4j
- java代码优化