1.事务的基础知识
1.1事务的特性:
原子性:事务不可分割
一致性:事务执行前后数据完整性保持一致
隔离性:一个事务的执行不应该受到其他事务的干扰
持久性:一个事务结束后,数据就持久化到数据库
1.2不考虑隔离性引发的安全问题
++++读问题:
脏读------------一个事务读到另一个事务未提交的数据
不可重复读—一个事务读到另一个事务已经提交的update的数据,导致一个事务中多次查询结果不一致
幻读------------一个事务读到另一个事务已经提交的insert的数据,导致一个事务中多次查询结果不一致
+++++写问题:
丢失更新
1.3解决读的问题:
++++设置事务隔离级别
Read uncommitted(未提交读):任何读问题都解决不了;={}
Read committed(已提交读):解决脏读,无法解决不可重复读和幻读;={orancle默认}
Repeatable read(重复度):解决脏读和不可重复读,无法解决幻读;==={mysql默认}
Serializable:解决所有读问题;
2.Spring事务管理的API
2.1Spring 的事务传播行为==========>>>>解决特别复杂的业务方法之间的调用
===2.1.1.保证多个操作在同一个事务中【一般使用默认的】
====PROPAGATION REQUIRED :默认值,如果A中有事务,使用A中的事务;如果A中没有事务,则创建一个新的事务,将操作包含进来;
2.1.2.保证多个操作不在同一个事务中
====PROPAGATION REQUIRES NEW : 如果A中有事务,将A的事务挂起(暂停),创建新的事务,只包含自身操作;如果A中没有事务,创建一个新事务,包含自身操作;
2.1.3.嵌套式事务
====PROPAGATION NESTED:嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作;如果没有异常,执行通过,若有异常可以选择回滚到最初始位置,也可以回滚到保存点
3.Spring事务管理的认识====模拟转账【未涉及事务】
3.1搭建Spring事务管理环境
1.引入必要的Jar包;
jdbc.properties的配置
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_1
jdbc.username=root
jdbc.password=123456
创建Service接口与实现
创建Dao的接口与实现
配置Service和Dao,交给Spring管理
/**
* @author Leonard
* 转账的业务层接口
*/
public interface AccountService {
public void transfer(String from,String to,Double money);
}
/**
* @author Leonard
* 转账的业务层实现======无事务控制,出现异常,转账出现错误
*/
public class AccountServiceImpl implements AccountService {
//1.注入Dao
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String from, String to, Double money) {
accountDao.outMoney(from, money);//扣钱
int b=1/0;//=================转账异常,程序终止
accountDao.inMoney(to, money);//未收到钱
}
}
/**
* @author Leonard
* 转账的Dao接口
*/
public interface AccountDao {
/**
* @param from转出=========-钱
* @param money
*/
public void outMoney(String from,Double money);
/**
* @param to转入=======+钱
* @param money
*/
public void inMoney(String to,Double money);
}
/**
* @author Leonard
* Dao的实现类
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
/* (non-Javadoc)
* 转账扣钱
*/
@Override
public void outMoney(String from, Double money) {
this.getJdbcTemplate().update("update account set money=money-? where name=?" , money,from);
}
/* (non-Javadoc)
* 转账收钱
*/
@Override
public void inMoney(String to, Double money) {
this.getJdbcTemplate().update("update account set money=money+? where name=?", money,to);
}
}
2.在xml中配置Service和Dao以及JDBC模板,交给Spring管理
<!-- 配置Service -->
<bean id="accountService" class="spring.tx.demo.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 配置Dao -->
<bean id="accountDao" class="spring.tx.demo.AccountDaoImpl">
<!-- 直接给Dao一个连接池即可 -->
<property name="dataSource" ref="datasource"></property>
</bean>
<!-- 1.引入配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 2.引入相应的值 -->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
3.创建测试类操作数据库–模拟转账
/**
* @author Leonard
* 转账环境测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx.xml")
public class AccountTest {
//创建业务层的类
@Resource(name="accountService")
private AccountService accountService;
@Test
public void demo(){
accountService.transfer("bbb", "ccc", 100000d);
}
}
4.Spring事务管理===第一类:编程式事务【了解】
1.配置平台事务管理器和事务管理模板类
<!-- 配置平台事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!-- 配置事务管理模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
2.在业务层注入事务管理模板
<!-- 配置Service -->
<bean id="accountService" class="spring.tx.demo.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
<!-- 注入事务管理模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
3.编写事务管理代码【了解】
对第三节中模拟转账中业务层实现进行升级,防止出现异常导致转账错误
/**
* @author Leonard
* 转账的业务层实现
*/
public class AccountServiceImpl implements AccountService {
//1.注入Dao
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
//2.注入事务管理的模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transfer(final String from, final String to, final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {//事务控制,出现异常,则不会转账成功
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountDao.outMoney(from, money);
int d = 1/0;//=========异常,事务回滚
accountDao.inMoney(to, money);
}
});
}
}
5.【重点】Spring事务管理===第二类:声明式事务管理(通过配置实现)–AOP思想
5.1xml方式
1.引入必要的jar
2.配置事务管理器和配置增强
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>/*====为某个方法添加事务======*/
</tx:attributes>
</tx:advice>
3.AOP配置
<!-- AOP配置 -->
<aop:config>
<aop:pointcut expression="execution(* spring.tx.demo2.spring.tx.demo2.AccountServiceImpl.*(..))" id="pointcut1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
5.2注解方式===tx3.xml
1.配置事务管理器
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
2.开启注解事务
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
3.在业务层添加事务注解-----@Transactional
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)也可以添加隔离级别和传播行为
//添加注解
@Transactional
public class AccountServiceImpl implements AccountService {
//1.注入Dao
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String from, String to, Double money) {
accountDao.outMoney(from, money);
int d = 1/0;//若没有事务控制会出现扣款成功,转账失败情况
accountDao.inMoney(to, money);
}
}
4.测试类测试,查询数据库前后对比
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx3.xml")
public class AccountTest {
//创建业务层的类
@Resource(name="accountService")
private AccountService accountService;
@Test
public void demo(){
accountService.transfer("bbb", "李四", 10000d);
}
}