1、什么是事务?
事务就是一系列的动作,它们被当做一个单独的工作单元,这些动作要么全 部完成,要么全部不起作用
2、举例说明什么是事务
假如在网上买一本书,这本书的单价是30元,显示库存还有10本;我们想要买一本,但发现我们手机里的钱的余额还有20元,这时如果是正常购买这本书的话,会有两步:①、点击购买,购买成功;我们手机的钱的余额减30 ②、这本书的库存会变成还有9本。但我们此时手机里的钱的余额不够买一本这样的书,我们点击付款,会出现余额不足,购买失败,我们的账户的余额是不变的,但此时,书的库存可能还会减1
这个买书的一系列动作:①、余额减30 ②、书的库存减1
就是一个事务,但当这两个动作一个成功,一个失败就会出现问题了,就需要我们使用Spring的事务管理去对这个事务进行管理,以确保这个事务的正确性。
3、事务具备的四种特性,原子性、一致性、隔离性 和 持久性
(1)原子性
事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
(2)一致性
事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
(3)隔离性
指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
(4)持久性
指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
4、如何在Spring中配置事务管理
①.用注解配置事务的步骤:
1).在Spring的配置文件applicatonContext.xml中配置管理数据源的事务管理器
2).在Spring的配置文件applicatonContext.xml中启动事务注解 (需要导入tx命名空间)
<!-- 扫描com.pdsu.spring下的包及其子包 -->
<context:component-scan base-package="com.pdsu.spring"></context:component-scan>
<!-- 导入外部文件,数据源 -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置Spring的JdbcTemplate,能够简单的使用增删改查-->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!-- 配置事务管理器,管理数据源 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager>
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!-- 配置Spring的JdbcTemplate,能够简单的使用增删改查-->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!-- 配置事务管理器,管理数据源 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!-- 启动事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
3).在BookShopServiceImpl中添加事务注解
@Service("BookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
//添加事务注解
//使用propagation 指定事务的传播行为,即当前的事务方法被另外一个方法调用时
//如何使用事务,默认取值为 REQUIRED,即使用调用方法的事务
//REQUIRES_NEW:事务自己的事务,调用的事务方法的事务被挂起
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void purchase(String username, String isbn) {
//1.获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新书的库存
bookShopDao.updateBookStock(isbn);
//3.更新用户余额
bookShopDao.updateUserAccount(username, price);
}
4).一个事务在另一个事务中的传播行为
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
①.在事务的传播行为中所有都默认的是使用第一事务。 事务1 调用 事务2,这时事务2就会变成事务1 的一部分
②.自己指定当前事务的传播行为
事务1 调用 事务2,我们为事务2指定开启一个新的事务,当事务1,执行到事务2时,事务2就会把事务1挂起,开启自己的事务,直到自己的事务执行完,才会再执行事务1
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
//添加事务注解
//使用propagation 指定事务的传播行为,即当前的事务方法被另外一个方法调用时
//如何使用事务,默认取值为 REQUIRED,即使用调用方法的事务
//REQUIRES_NEW:事务自己的事务,调用的事务方法的事务被挂起
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void purchase(String username, String isbn) {
//1.获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新书的库存
bookShopDao.updateBookStock(isbn);
//3.更新用户余额
bookShopDao.updateUserAccount(username, price);
}
}
5、在Spring中用xml文件的方式配置事务
①.手动配置我们实现的各个接口的bean;因为我们没有使用注解所以就不能再使用自动扫描包来配置bean了
②.配置事务管理器
③.配置事务属性
④.配置事务切入点,以及把事务切入点和事务属性关联起来
<context:property-placeholder location="classpath:db.properties"/>
<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 配置Spring的JdbcTemplate,能够简单的使用增删改查-->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!-- 配置bean,在配置时要注意,我们配置的是接口的实现类,而不是接口 -->
<bean id="BookShopDao" class="com.pdsu.spring.tx.xml.BookShopDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="bookShopService" class="com.pdsu.spring.tx.xml.service.Impl.BookShopServiceImpl">
<property name="bookShopDao" ref="BookShopDao"></property>
</bean>
<bean id="Cashier" class="com.pdsu.spring.tx.xml.service.Impl.CashierImpl">
<property name="bookShopService" ref="bookShopService"></property>
</bean>
<!-- 配置事物管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="DataSource" ref="pooledDataSource"></property>
</bean>
<!-- 配置事务属性:隔离级别、传播行为等 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 根据方法名指定事务的属性 -->
<tx:method name="purchase" propagation="REQUIRES_NEW"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入点,以及把事务切入点和事务属性关联起来 ;要导入aop命名空间-->
<aop:config>
<!-- 在配置切点时一定要注意(* com.pdsu.spring.tx.xml.BookShopService.*(..))第一个星号后有一个空格,不加会报错 -->
<aop:pointcut expression="execution(* com.pdsu.spring.tx.xml.service.*.*(..))"
id="txpointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
</aop:config>