Spring事务

1.声明事务配置
在Spring的配置文件中写入下述代码

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
	 <property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>

在这里使用了数据库连接池, name=“dataSource” ref="dataSource"中的dataSource是指

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8"></property>
		<property name="username" value="root"></property>
		<property name="password" value="root"></property>
		<!-- 连接只读数据库时配置为true, 保证安全 -->
		<property name="readOnly" value="false" /> 
		<!-- 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒 -->
		<property name="connectionTimeout" value="30000" /> 
		<!-- 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟 -->
		<property name="idleTimeout" value="600000" /> 
		<!-- 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQL wait_timeout参数(show variables like '%timeout%';) -->
		<property name="maxLifetime" value="1800000" /> 
		<!-- 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count) -->
		<property name="maximumPoolSize" value="15" />
</bean>

2.使用
使用事务时在Service层的public方法上加@Transactional注解(或是其它要操作数据库的public方法)

3.@Transactional注解的属性
(1.)rollbackFor和rollbackForClassName属性(可以通用):这两个属性在默认值的情况下是在事务方法抛出运行时异常后起作用并把数据库回滚。
举个例子:

    @Transactional(readOnly=false,timeout=3,rollbackFor=MoneyException.class,propagation=Propagation.REQUIRES_NEW)
	public boolean insert(String userId,String bookId, int count) throws MoneyException{
		if(bookDao.enough(bookId, count)) {//书籍足够
			//书籍表库存递减
			bookDao.update(bookId, count);
		}
		double price = bookDao.getPrice(bookId);
		double total = price*count;
		if(moneyDao.enough(userId, total)) {//余额足够
			//订单表添加数据
			Coupon coupon = new Coupon();
			coupon.setId(UUID.randomUUID().toString());
			coupon.setUserId(userId);
			coupon.setBookId(bookId);
			coupon.setTotal(total);
			couponDao.insert(coupon);
			//钱包表递减
			moneyDao.update(userId, total);
		}
		return true;
	}

假设数据库中有一个表书书籍表,用来存放各类数据信息。某本书有库存20本,某位顾客打算购买十本,然而他的账户余额只够八本书的钱。如果不使用@Transactional注解会发生什么样的现象呢?结果就是书籍表中的数量少了十本而账户余额却不足(抛出异常)而没有变化。如果加上该注解会有什么现象呢?结果就是当执行到if(moneyDao.enough(userId, total)) 时,enough(userId, total)抛出运行时异常,此时数据库回滚,余额和书籍数量都不会变化。

值得注意的是当enough(userId, total)抛出检查时异常时不会回滚,此时需要更改rollbackFor属性的属性值 比如:上个例子中rollbackFor=MoneyException.class,直接指定只要出现MoneyException,不管是运行时或是检查时异常均会回滚,但此时一定要注意该异常一定要抛出而不能捕获。

(2.)propagation属性,该属性用来表述事务的传播。
默认值的情况下表述的现象是如果一个描述事务的方法中调用了另一个方法而且该方法也是描述事务的方法,此时两个描述事务的方法是在同一个事务中运行的。
例如:

@Transactional
	public boolean batch(String userId,Map<String,Integer> commodities) throws MoneyException {
		Set<Entry<String, Integer>> set = commodities.entrySet();
		for (Entry<String, Integer> commodity : set) {
			String bookId = commodity.getKey();
			int count = commodity.getValue();
			System.out.println(bookId+","+count);
			couponService.insert(userId,bookId, count);
		}
		return true;
	}

该方法中couponService.insert(userId,bookId, count);调用了insert方法,insert方法也是描述事务的方法,两者在一个事务中运行。

如果propagation属性值为REQUIRES_NEW则是在不同的事务中运行。
例如上个例子中只给insert方法加上@Transactional(propagation=Propagation.REQUIRES_NEW)则 首先执行的batch方法会开启一个事务,而后执行的insert方法开启另外一个事务。比如有两本书A和B库存均为一本,某人余额刚好只够买一本A或是一本B,当他选择购买A和B时则根据处理顺序会有一本购买成功,而另外一本购买失败。

如果上个例子中只给batch方法加上@Transactional(propagation=Propagation.REQUIRES_NEW)则还是属于在同一个事务中运行

如果给batch和insert均加上@Transactional(propagation=Propagation.REQUIRES_NEW)则一定分属于不同的事务。

如果只给insert加上@Transactional(propagation=Propagation.REQUIRES_NEW),当batch方法和insert方法不在同一个类中时则是不同的事务。如果两个方法在同一个类中定义,则此时还是在同一个事务中。

(3.)readOnly属性用来限制对数据库的操作
(4.)timeout属性(单位为秒)用来限制事务在多长时间内处理完毕,如果超过时间未处理完成则抛出异常。
(5.)isolation属性:该属性用来规定事务隔离级别
属性值有五个
1.DEFAULT:默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常为READ_COMMITTED。
2.READ_UNCOMMITTED:表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别可能出现脏读、不可重复读或幻读,因此很少使用该隔离级别。
3.READ_COMMITTED:表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,但可能出现不可重复读或幻读,这也是大多数情况下的推荐值
4.REPEATABLE_READ:表示一个事务在整个过程中可以多次重复执行某个查询,且每次返回的记录都相同,除非数据被当前事务自生修改。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读,但可能出现幻读。
5.SERIALIZABLE:表示所有的事务依次逐个执行,事务之间互不干扰,该级别可以防止脏读、不可重复读和幻读,但是这将严重影响程序的性能,因此通常情况下也不会用到该级别。

事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持:Oracle 支持READ_COMMITED和SERIALIZABLE两种事务隔离级别,默认为READ_COMMITED;MySQL 支持READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE四种事务隔离级别,默认为:REPEATABLE_READ。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值