1、事务概念
事务是一组操作执行的单元,对于数据库来说,要么完全执行,要么完全不执行。通过一组相关操作组合为一个要么全部成功,要么全部失败的单元,可以简化错误回复并使应用程序更加可靠。
事务的ACID
- Atomic(原子性):要么都发生,要么都不发生。
- Consistent(一致性):事务执行前后,数据一致性。例如tom账户减去100,相应的jerry账户就多出100
- Isolate(隔离性):当同时存在多个事务的时候,事务之间应该不收干扰。
- Durable(持久性):永久保存,例如保存到数据库中等
脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
数据库准备了以下隔离级别来规避以上问题的产生
2、Spring事务管理
Spring事务管理机制:三个核心组件
1、PlatformTransactionManager平台事务管理器
2、TransactionDefinition
- 事务定义信息和事务管理相关参数
- 隔离级别、传播级别
- 超时 、事务是否只读
3、TransactionStatus
事务运行过程中的动态信息,比如提交,回滚
一些数据库的默认隔离级别
大部分数据库设置隔离级别为Read Commited
SqlServer:Read Commited
Oracle:Read Commited
但是Mysql设置隔离级别:
Mysql:Repeatable_Read
3、事务的传播行为
4、Spring AOP注解实现事务
1、配置事务管理器,并把事务管理器交给spring事务注解驱动
2、在目标类上使用注解@Transactional
Demo示例:对于转账成功操作和非成功操作
1、数据库导入
代码
CREATE DATABASE spring;
USE spring;
CREATE TABLE Account(id INT,username VARCHAR(20),money INT);
INSERT INTO account(id,username,money)VALUES(1,'tom',1000);
INSERT INTO account(id,username,money)VALUES(2,'cat',1000);
2、dao,daoImpl
public interface AccountDao {
void in(String inUser, int money);
void out(String outUser, int money);
}
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
public void in(String inUser, int money) {
// TODO Auto-generated method stub
this.getJdbcTemplate().update(
"update account set money = money + ? where username = ?",
money, inUser);
}
public void out(String outUser, int money) {
// TODO Auto-generated method stub
this.getJdbcTemplate().update(
"update account set money = money - ? where username = ?",
money, outUser);
}
}
3、service,serviceImpl
public interface AccountService {
public abstract void transfer(String outUser, String inUser, int money);
}
import org.springframework.transaction.annotation.Transactional;
import com.example.spring.dao.AccountDao;
@Transactional
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outUser, String inUser, int money) {
// TODO Auto-generated method stub
accountDao.out(outUser, money);
System.out.println("out money sucess");
int i=1/0;
accountDao.in(inUser, money);
System.out.println("in money sucess");
}
}
4、applicationContext.xml文件配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!-- 配置dao -->
<bean id="accountDao" class="com.example.spring.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置service -->
<bean id="accountService" class="com.example.spring.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- # 配置事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- #将事务管理配置给spring,交予事务注解驱动 -->
<tx:annotation-driven transaction-manager="txManager" />
</beans>
5、测试代码
@Test
public void testString() {
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
xmlPath);
AccountService accountService=(AccountService) applicationContext.getBean("accountService");
accountService.transfer("tom", "cat", 100);
}
经测试在异常情况下,转账操作正确无误。