事务概述
事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的多个数据库操作,这些操作要么都执行,要么都不执行。
事务的四大特性(ACID):原子性(atomicity), 一致性(consistency), 隔离性(isolation), 持久性(durability).
基于注解的Spring事务@Teansaction
数据库表 :
consumer:
book:
- 配置jdbc操作环境:
<!-- 引入外部文件 -->
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<!-- 配置数据源 ${}:取出配置文件中的值 #{}Spring的表达式语言 -->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<!-- 使用jdbc_Template操作数据库 -->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<constructor-arg name="dataSource" value="#{dataSource}"></constructor-arg>
</bean>
- 配置事务管理器
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- 开启包扫描与基于注解的事务控制模式
<context:component-scan base-package="com.xian.*"></context:component-scan>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
数据库操作:
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
//获取图书的价格
public int getBook_price(String book_name) {
String sql = "select price from book where book_name=?";
return jdbcTemplate.queryForObject(sql, Integer.class, book_name);
}
//减库存 -1
public void reduceCount(String book_name) {
String sql = "update book set count=count-1 where book_name=?";
jdbcTemplate.update(sql, book_name);
}
//减余额
public void reduceMoney(String name, int price) {
String sql = "UPDATE consumer SET money=money - ? WHERE name=?";
jdbcTemplate.update(sql, price, name);
}
事务操作:
@Service
public class BookService {
@Autowired
BookDao bookDao;
//买书结账
@Transactional()
public void checkout(String name, String book_name) {
//减库存
bookDao.reduceCount(book_name);
int price = bookDao.getBook_price(book_name);
//减钱
bookDao.reduceMoney(name, price);
System.out.println("结账成功");
}
测试:
class Test {
ApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml");
@org.junit.jupiter.api.Test
void test() {
BookService bean = ioc.getBean(BookService.class);
bean.checkout("user_1", "java");
System.out.println("结账完成!");
}
事务细节
@Transaction:
- value() = transactionManager() : 设置要使用的事务控制器, 对应于xml中 ‘’<tx:annotation-driven transaction-manager= ‘…’ />’’ 参数.
- transactionManager()
- Propagation() default Propagation.REQUIRED : 事务的传播行为
事务的传播行为:
事务的传播与事务的行为:
如果有多个事务进行嵌套运行, 子事务是否要和大事务共用一个事务
REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务当前事务和之前的大事务公用一个事务
事务的属性继承于父事务..
REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。当前事务总是使用一个新的事务, 如果已有事务, 会将之前的事务挂起
事务的属性是自己调整的.
SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务
MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
NESTED: 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
- isolation() default Isolation.DEFAULT :事务的隔离级别
读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
- timeout() default TransactionDefinition.TIMEOUT_DEFAULT:
超时设置(秒为单位). - readOnly() default false : 设置事务为只读事务.
异常回滚:
- rollbackFor() : 设置哪些异常是需要回滚的
- rollbackForClassName()
- noRollbackFor() : 设置哪些异常不回滚
- noRollbackForClassName()
运行时异常(非检查异常): 可以不用处理, 默认都回滚
编译时异常(检查异常): 要么try-catch, 要么在方法上声明throws, 默认不回滚
关于xml事务配置
示例:
<!-- 配置事务方法 -->
<!-- 告诉spring哪些是事务方法(事务切面按照我们的切入点表达式切入事务方法 -->
<bean class="com.xian.service.BookService" id="bookService">
</bean>
<aop:config>
<aop:pointcut expression="execution(* com.xian.ser*.*.*(..))" id="tx"/>
<!-- advice-ref: 指向事务管理器的配置 -->
<aop:advisor advice-ref="myAdvice" pointcut-ref="tx"/>
</aop:config>
<!-- 配置事务管理器 -->
<tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
<!-- 事务属性 -->
<tx:attributes>
<!-- 指明哪些方法是事务方法: 切入点表达式说明事务管理器要切入的方法,
method: 指明哪些方法要加事务 -->
<tx:method name="updatePrice" propagation="REQUIRED"/>
<tx:method name="checkout" timeout="1"/>
</tx:attributes>
</tx:advice>