spring中的事务
博客专区 > A__17 的博客 > 博客详情
spring中的事务
A__17 发表于4个月前
spring中的事务
  • 发表于 4个月前
  • 阅读 0
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

1)spring的事务管理器:

package org.springframework.transaction;

public interface PlatformTransactionManager {

	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;
}

2)spring的事务定义类:

package org.springframework.transaction;
import java.sql.Connection;

public interface TransactionDefinition {

	// 事务的传播行为
	int PROPAGATION_REQUIRED = 0;		// 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。											
	int PROPAGATION_SUPPORTS = 1;		// 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
	int PROPAGATION_MANDATORY = 2;		// 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
	int PROPAGATION_REQUIRES_NEW = 3;	// 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
	int PROPAGATION_NOT_SUPPORTED = 4;	// 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
	int PROPAGATION_NEVER = 5;			// 以非事务方式运行,如果当前存在事务,则抛出异常。
	int PROPAGATION_NESTED = 6;			// 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则等价于PROPAGATION_REQUIRED。

	
	// 事务的隔离级别
	int ISOLATION_DEFAULT = -1;			// 使用底层数据库的默认隔离级别。
	int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
	int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
	int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
	int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

	/**
	 * Use the default timeout of the underlying transaction system, or none if timeouts are not supported.
	 */
	// 事务超时:指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。
	// 默认为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
	int TIMEOUT_DEFAULT = -1;

	int getPropagationBehavior();

	int getIsolationLevel();

	int getTimeout();

	boolean isReadOnly();

	String getName();
}

3)spring中事务的回滚:

1)spring推荐以抛出异常的方式来回滚一个事务。
2)spring事务管理器会捕捉任何未处理的异常,然后根据相应的配置来判断是否回滚抛出异常的事务。
	1>默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
	2>可以配置在抛出哪些异常时回滚事务,包括checked异常;也可以明确设置哪些异常抛出时不回滚事务。

4)@Transactional注解

属性					类型							描述
value					String				可选的限定描述符,指定使用的事务管理器
propagation				enum 				可选的事务传播行为设置
isolation				enum 				可选的事务隔离级别设置
readOnly				boolean				读写或只读事务,默认读写
timeout					int 				(in seconds granularity)事务超时时间设置

rollbackFor				(继承Throwable的)Class数组		导致事务回滚的异常类数组
rollbackForClassName	(继承Throwable的)类名数组		导致事务回滚的异常类名字数组
noRollbackFor			(继承Throwable的)Class数组		不会导致事务回滚的异常类数组
noRollbackForClassName	(继承Throwable的)类名数组		不会导致事务回滚的异常类名字数组

用法:可以写在类(接口)上,也可以方法上。
	1)当作用于类上时,该类的所有public方法将都具有该类型的事务属性。
	2)可以在方法上使用该注解来覆盖类级别上的设置。
	3)Spring建议不要在接口或接口方法上使用该注解,因为只有在使用基于接口的代理时它才会生效。
	4)在private、protected、default的方法上使用该注解,则该注解将被忽略,也不会抛出任何异常。
	5)默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是说:类内部方法调用本类内部的其它方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰!

5)原理:

1)spring的事务管理是通过spring的AOP(动态代理)来实现的。

	spring中事务生效的一个前提:调用的方法必须被TransactionInterceptor拦截。
		说明:只有通过TransactionInterceptor拦截的方法才会被加入到spring的事务管理中。
		举例:
			在方法A中调用@Transactional修饰的方法B时,如果方法A与方法B在同一个类TestClass中,则加在方法B上的@Transactional注解不起作用,因为此时方法A对方法B的调用是不会被拦截器拦截的!
			解决办法:在方法A中,使用当前的代理对象来调用方法B。eg:((TestClass)AopContext.currentProxy()).B();

2)对于正常的事务管理,必须关闭数据库的自动提交模式,spring会将底层连接的自动提交特性设置为false。

	package org.springframework.jdbc.datasource;
	public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean {

		private DataSource dataSource;

		/**
		 * This implementation sets the isolation level but ignores the timeout.
		 */
		[@Override](https://my.oschina.net/u/1162528)
		protected void doBegin(Object transaction, TransactionDefinition definition) {
			DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
			Connection con = null;

			try {
				if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
					Connection newCon = this.dataSource.getConnection();
					if (logger.isDebugEnabled()) {
						logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
					}
					txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
				}

				txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
				con = txObject.getConnectionHolder().getConnection();

				Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
				txObject.setPreviousIsolationLevel(previousIsolationLevel);

				// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
				// so we don't want to do it unnecessarily (for example if we've explicitly
				// configured the connection pool to set it already).
				if (con.getAutoCommit()) {
					txObject.setMustRestoreAutoCommit(true);
					if (logger.isDebugEnabled()) {
						logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
					}
					con.setAutoCommit(false); // 【spring将底层连接的自动提交设置为false】
				}
				txObject.getConnectionHolder().setTransactionActive(true);

				int timeout = determineTimeout(definition);
				if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
					txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
				}

				// Bind the session holder to the thread.
				if (txObject.isNewConnectionHolder()) {
					TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
				}
			}

			catch (Throwable ex) {
				DataSourceUtils.releaseConnection(con, this.dataSource);
				throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
			}
		}
	}

6)配置文件:

1)注解的形式:
	spring + mybatis:
		说明:mybatis会自动参与到spring事务的管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理不起作用。
	
		<!-- 定义事务管理器 -->  
		<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
			<property name="dataSource" ref="dataSource" />
		</bean> 
		<!-- 使用注解的形式管理事务 -->  
		<tx:annotation-driven transaction-manager="transactionManager" /> 
	
		<!-- 定义SqlSessionFactory -->
		<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
			<property name="dataSource" ref="dataSource" />
			<property name="configLocation" value="classpath:/mybatis_advertise.xml" />
		</bean>

	spring + hibernate:
		<!-- 事务管理器 -->
		<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
			<property name="sessionFactory" ref="sessionFactory" />
		</bean>
		<!-- 使用注解的形式管理事务 -->
		<tx:annotation-driven transaction-manager="transactionManager"/>

		<!-- 定义SessionFactory -->  
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
			<property name="dataSource" ref="dataSource" />
            <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        </bean>  


2)切面的形式:

	<!-- 定义事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 事务的处理策略:相当于注解形式中的@Transactional注解 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 设置事务的属性,相当于@Transactional注解的属性 -->
			<tx:method name="query*" read-only="true" />
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="*" /> 
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<!--定义需要使用事务的切入点 -->
		<aop:pointcut id="myPointcut" expression="execution(* com.jxn.mybatis.service.*.*(..))" />
		<!--将事务的处理策略应用到指定的切入点 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
	</aop:config>
共有 人打赏支持
粉丝 0
博文 35
码字总数 37964
×
A__17
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: