事务的作用
1.当数据库操作序列中个别操作失败时,提供一种方式 使得数据库状态恢复到正常状态,保障数据库即使在异常状态下,也能保持数据一致性(要么操作前,要么操作后状态)
2.当出现并发访问数据库时,在多个访问间进行相互隔离,防止并发访问操作结果 互相干扰
事务管理
PlatformTransactionManager
此接口定义了事务的基本操作
获取事务:基于事务状态
TransactionStatus getTransaction(TransactionDefinition definition)
提交事务:
void commit(TransactionStatus status)
回滚事务
void rollback(TransactionStatus status)
可以发现事务的操作,都是基于TransationDefinition的
TransationDefinition:此接口定义了事务的基本信息
获取事务定义名称:
Strign getName()
获取事务的读写属性:
boolean isReadOnly()
获取事务隔离级别:
int getIsolationLevel()
获取事务超时时间:
int getTimeout()
获取事务传播行为特征:
int getPropagationBehavior()
TransactionStatus
此接口定义了事务在执行过程中某个时间点上的状态信息及对应的状态操作
获取事务是否处于新开启事务状态
boolean isNewTransaction()
获取事务是否处于已完成状态
boolean isCompleted()
获取事务是否处于回滚状态
boolean isRollbackOnly()
刷新事务状态
void flush()
获取事务是否具有回滚存储点
boolean hasSavepoint()
设置事务处于回滚状态
void setRollbackOnly()
事务管理(代码)
案例介绍:银行转账业务说明
银行转账操作中,涉及从A账户到B账户的资金转移操作。数据层仅提供单条数据的基础操作,未设计多账户间的业务操作。
1.pom.xml导入依赖
AccountServive 业务层接口
public interface AccountService{
outName 出账用户名
inName 入账用户名
money 转账金额
public void transfer(String outName,String inName,Double money);
}
AccountServiceImpl
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao){
this.account = account;
}
public void transfer(String outName,String inName,Double money){
accountDao.inMoney(outName,money);
accountDao.outMoney(inName,money);
}
}
数据层接口AccountDao
public interface AccountDao{
入账操作
name 入账用户名
money 入账金额
void inMoney(@Param("name") String name,@Param("money") Double money);
出账操作
name 出账用户名
money 出账金额
void outMoney(@Param("name") String name,@Param("money") Double money);
}
mybaits配置文件AccountDao.xml
接收 inMoney和outMoney
ApplicationContext.xml
执行类
ApplicationContext ctx = new ClassPathXmlApplicationContext("application")
AccountService accountService = (AccountService) ctx.getBean("accountService")
accountService.transfer("Jock1","Jock2",100D);
添加一个错误,会造成错误前的代码执行,后面的不执行,数据就会发生不统一,我们需要通过事务,要么全部不运行,要么全部运行
AccountServiceImpl
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao){
this.account = account;
}
//0.添加数据源,然后再ApplicationContext.xml中注入dataSource
private DataSource dataSource;
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
public void transfer(String outName,String inName,Double money){
//1.开启事务
PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
//2.事务定义对象
TransactionDefinition td = new DefaulTransactionDefinition();
//3.返回得到事务状态对象,控制事务执行
TransactionStatus ts = ptm.getTransaction(td);
accountDao.inMoney(outName,money);
//前提:添加错误
int i = 1/0
accountDao.outMoney(inName,money);
//4.提交事务
ptm.commit(ts);
}
}
开启事务后,抛出了异常,然后两个数据都没变,因为回滚了,报错后,操作前数据什么样,操作后数据还是那样,防止报错后数据的不统一
通过AOP改造编程式事务控制
如果我每次都在AccountService中有新的事务数据,那我每次都写一遍很麻烦,通过AOP来改变
1.导入ASpectJ包
2.创建事务管理通知TxAdvice
private DataSource dataSource;
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
1.Proceeding 并且 抛异常
public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable{
把共性的代码复制过来
PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
TransactionDefinition td = new DefaulTransactionDefinition();
TransactionStatus ts = ptm.getTransaction(td);
//2.环绕通知
Object ret = pjp.proceed(pjp.getArgs());
ptm.commit(ts);
//3.返回
return ret
}
4.开启AOP注解
<aop:config>
<aop:pointcut id = "pt" expression="execution(* *..transfer(..))"/>
配置通知类
<aop:aspect ref = "txAdvice">
<aop:around method = "transactionManager' pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
5.最后再ApplicationContext中通知类
<bean id="txAdvice" class="...路径">
<property name = "dataSource" ref="dataSource"/>
</bean>
3.服务层书写
事务管理(XML声明式)
1.ApplicationContext.xml中配置
之前在里面书写的
<bean id="txAdvice" class="...路径">
<property name = "dataSource" ref="dataSource"/>
</bean>
使用tx的命名空间来做,修改为:
1.首先先需要一个事务管理器
<bean id = "txManager" class="org.springframe...DataSourceTransactionManager全类名">
<property name = "dataSource" ref="dataSource"/>
</bean>
2.定义事务管理的通知类
<tx:advice id="txAdvice" transaction-manager="txManager">
定义哪些事务被控制
<tx:attributes>
读写事务:readonly false
所有的操作都加读写事务
<tx:method name = "*" read-only="false"/>
所有获取的操作,只有读操作
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
3.使用AOP(不用在配置方法了,默认有)
<aop:config>
<aop:pointcut id = "pt"
expression="execution(* ithei.service.*Service.*(..))"/>
配置通知类,使用advisor绑定tx
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
自此以后,txAdvice文件可以删除了
tx:method属性
<tx:method name="" read-only="true" 方法/>
timeout:超时时长,-1:不限定
isolation:隔离级,DEFAULT 默认跟着数据库走
no-rollback-for="java.lang.Atrith"事务遇到lang异常是不会处理的
和rollback-for:排除掉回滚异常
事务中如果出现了异常就不要回滚了,后面那个是要回滚
事务传播行为
事务管理员 与 事务协调员
事务管理(注解)
AccountServive 业务层接口
public interface AccountService{
outName 出账用户名
inName 入账用户名
money 转账金额
//第一步:替换<aop:config>
@Transactional(
/*
配置里有一些超时时间等配置,替换后在这里配置
name="transfer"
read-only="false"
timeout="-1"
isolation="DEFAULT"
no-rooback-for=""
rollback-for=""
propagation="REQUIRED"
*/
改为:
readOnly = false,
timeout = -1,
isolation = Isolation.DEFAULT,
rollbackFor
= {java.lang.ArithmeticException,IOExpection},
noRollbackFor
= {},
propagation
= Propagation.REQUIRED
)
public void transfer(String outName,String inName,Double money);
}
AccountServiceImpl
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao){
this.accountDao = accountDao;
}
public void transfer(String outName,String inName,Double money){
accountDao.inMoney(outName,money);
int i = 1/0;
accountDao.outMoney(inName,money);
}
}
数据层接口AccountDao
public interface AccountDao{
入账操作
name 入账用户名
money 入账金额
void inMoney(@Param("name") String name,@Param("money") Double money);
出账操作
name 出账用户名
money 出账金额
void outMoney(@Param("name") String name,@Param("money") Double money);
}
ApplicationContext.xml
<bean id = "txManager" class="org.springframe...DataSourceTransactionManager全类名">
<property name = "dataSource" ref="dataSource"/>
</bean>
//第二步,替换tx:advice
<tx:annotation-driven transaction-manager="txManager"/>
<!--被替换了
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method
name="transfer"
read-only="false"
timeout="-1"
isolation="DEFAULT"
no-rooback-for=""
rollback-for=""
propagation="REQUIRED"
/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id = "pt"
expression="execution(* ithei.service.*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
-->
执行类
ApplicationContext ctx = new ClassPathXmlApplicationContext("application")
AccountService accountService = (AccountService) ctx.getBean("accountService")
accountService.transfer("Jock1","Jock2",100D);
声明式事务(纯注解驱动)
AccountService
public interface AccountService{
@Transactional
public void transfer(String outName,String inName,Double money){
}
}
AccountServiceImpl
@Service("accountService")
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
public void transfer(String outName,String inName,Double money){
accountDao.inMoney(outName,money);
int i = 1/0;
accountDao.outMoney(inName,money);
}
}
SpringConfig
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBaitisConfig.class})
1.替代annotionDriver,并且指定manager(在JDBC写)
@EnableTransactionManagement
public class SpringConfig{
}
JDBCConfig
UserServiceTest
设定spring专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
设定加载的spring上下文对应配置
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest{
@Autowired
private Account Service accountService;
@Test
public void testTransfer(){
accountService.transfer("Jock1","Jock2",100D)
}
}