JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇)
JavaEE学习日志(九十二)
Spring
spring事务控制的api
- org.springframework.orm.hibernate5.
HibernateTransactionManager
: 在hibernate环境下使用 - org.springframework.jdbc.datasource.
DataSourceTransactionManager
: 在jdbcTemplate,mybatis(ibatis)环境下使用
事务的特性
- 原子性:不可分割
- 隔离性:事务之间的隔离级别
- 一致性:要么全部完成,要么全部不完成
- 持久性:一旦提交,持久化到数据库中
事务的隔离级别
- 读未提交:read uncommited
产生的问题:脏读、幻读、不可重复读 - 读已提交:read commited
产生的问题:幻读,不可重复读
解决的问题:脏读 - 重复读:repeatable read
产生的问题:幻读
解决的问题:脏读,不可重复读 - 串行化(序列化):serializable
解决的问题:所有问题,隔离级别最高,效率最低
详细看jdbc中的事务部分
数据库支持的隔离级别
mysql:全支持,默认是repeatable read
oracle:read commited、serializable、read only(只读) 默认是read commited
事务的传播
-
REQUIRED
:必要的,如果没有事务,则新建事务;如果有事务,则加入事务中。spring中指定为默认值
增删改 -
SUPPORTS
:支持的,如果没有事务,非事务执行;如果有事务,加入事务
查询
了解其他事务传播
MANDATORY:可以使用当前事务,如果没有事务,则抛出异常
REQUERS_NEW:新建一个事务,如果存在事务,则挂起事务
NOT_SUPPORTED:必须非事务执行,如果有事务则挂起
NEVER:非事务执行,如果存在事务,抛出异常
NESTED:有事务,嵌套执行;没有事务,执行REQUIRED
是否为只读的事务
- 如果是查询,则为只读的事务,readOnly=true
- 如果是增删改,则为非只读的事务,readOnly=false
spring声明式xml事务管理(推荐)
引入spring的事务管理jar包
<!--spring的事务管理jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
一、创建事务管理器对象
<!--创建事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源属性:事务存在连接中,连接存在连接池中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
二、事务的增强:过滤方法是否需要拦截
有四个属性
isolation
:隔离级别,一般选择默认propagation
:传播的行为read-only
:是否为只读的事务,增删改选择非只读;查询选择只读timeout
:超时时间,单位为s,-1时为永不超时
注意*
:通配符配置,如find*
,只要以find开头即可
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--方法的过滤-->
<tx:attributes>
<!--指定需要拦截的方法
isolation:隔离级别,一般选择默认
propagation:传播的行为
read-only:是否为只读的事务,增删改选择非只读;查询选择只读
timeout:超时时间,单位为s,-1时为永不超时
*:通配符配置,如find*,只要以find开头即可
-->
<!--增删改-->
<tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="del*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<!--查询-->
<tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
<tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
<tx:method name="query*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
</tx:attributes>
</tx:advice>
可以将默认的属性删除掉
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--方法的过滤-->
<tx:attributes>
<!--指定需要拦截的方法
isolation:隔离级别,一般选择默认
propagation:传播的行为
read-only:是否为只读的事务,增删改选择非只读;查询选择只读
timeout:超时时间,单位为s,-1时为永不超时
*:通配符配置,如find*,只要以find开头即可
-->
<!--增删改-->
<tx:method name="insert*" />
<tx:method name="add*" />
<tx:method name="update*" />
<tx:method name="del*" />
<tx:method name="delete*"/>
<tx:method name="save*" />
<!--查询-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="query*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
再简化:可以将查询或增删改的其中一部分,用*来代替
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--方法的过滤-->
<tx:attributes>
<!--查询-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
<!--其他配置事务,增删改-->
<tx:method name="*"></tx:method>
</tx:attributes>
</tx:advice>
三、配置切面
advice-ref
:通知关联对象pointcut
:切入点
<aop:config>
<!--切面配置
advice-ref:通知关联对象
pointcut:切入点
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"></aop:advisor>
</aop:config>
四、测试
业务层
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AccountDao accountDao;
@Override
public void transfer(String fromName, String toName, Float money) {
//查询转账的账户
Account fromAccount = accountDao.findByName(fromName);
//查询接收的账户
Account toAccount = accountDao.findByName(toName);
//开始转账
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//持久化到数据库
accountDao.update(fromAccount);
System.out.println(0/0);
accountDao.update(toAccount);
}
}
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testJdbcTemplate {
@Autowired
AccountService accountService;
@Test
public void test(){
accountService.transfer("aaa","ccc",500f);
}
}
注解的事务管理
引入依赖和上面完全相同
一、创建事务管理器对象
<!--创建事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源属性:事务存在连接中,连接存在连接池中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
二、开启事务的注解管理和aop的自动代理
<!--开启事务的注解管理-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--开启aop的注解,自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
三、标记注解
1、在业务层的类上标记注解@Transactional
会出现一个问题:类中所有的方法都会使用事务管理
2、删除在类上的注解,在方法上标记注解@Transactional
:只有该方法按照事务执行
四、配置属性
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false, timeout = -1)
public void transfer(String fromName, String toName, Float money) {
//查询转账的账户
Account fromAccount = accountDao.findByName(fromName);
//查询接收的账户
Account toAccount = accountDao.findByName(toName);
//开始转账
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//持久化到数据库
accountDao.update(fromAccount);
System.out.println(0/0);
accountDao.update(toAccount);
}
也可以省略
@Transactional()
public void transfer(String fromName, String toName, Float money) {
//查询转账的账户
Account fromAccount = accountDao.findByName(fromName);
//查询接收的账户
Account toAccount = accountDao.findByName(toName);
//开始转账
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//持久化到数据库
accountDao.update(fromAccount);
System.out.println(0/0);
accountDao.update(toAccount);
}