1、事务的特性
事务的四种特性:
原子性:体现一个事务的操作的不可分割,要么权执行,要么全不执行。
一致性:事务的执行结果必须从一种一致性状态变到另一种一致性状态。最典型的就是转账,两个账户A、B总金额为5000,不管A、B如何转账,转几次,当事务结束A、B账户总金额还为5000。
隔离型:即并发执行的事务操作同一张表时相互之间不能相互影响。举例说明就是对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,不能出现交叉执行。
持久性:指事务一旦被提交,对数据库中数据的改变时永久性的。
2、事务的隔离性(解决脏读、不可重读和幻读问题)
隔离级别所解决的问题:
脏读(Dirty Reads):一个事务读取了另一个事务还未提交的数据,这里主要指事务读取了另一个事务未回滚前的数据。
不可重读(Non-RepeatableReads):不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值。例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,此时发生了不可重复读。
幻读:指事务A对符合条件的n条数据进行批量修改,在此过程中(事务A还未提交)事务B向数据库中插入了一条符合事务A条件的数据,此时事务A再读取时发现有未修改的数据,此时便产生幻读。
注意:不可重读和幻读均读取了另一个事务已提交的数据,这一点区别于脏读,但不可重读针对一条数据,而幻读则针对批量数据。
四种隔离级别:
SERIALIZABLE:可避免脏读、不可重复读、幻读的发生
REPEATABLE_READ:可避免脏读、不可重复读的发生
READ_COMMITTED:可避免脏读的发生
READ_UNCOMMITTED:最低级别,任何情况都无法保证
3、事务的传播性(解决事务创建时机的问题)
七种传播级别:
PROPAGATION_REQUIRED:如果上下文中已经存在事务,那么就加入到事务中执行;如果当前上下文中不存在事务,则新建事务执行。
PROPAGATION_SUPPORTS:如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行
PROPAGATION_MANDATORY:该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别
PROPAGATION_REQUIRES_NEW:每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行
PROPAGATION_NOT_SUPPORTED:若上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务
PROPAGATION_NEVER:上下文中不能存在事务,一旦有事务,就抛出runtime异常
PROPAGATION_NESTED:如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务
一、配置代码
web.xml
<!--spring打开session及事务配置--> <filter> <filter-name>SpringOpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>SpringOpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
1、注解方式
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--spring的配置文件 与springMVC的配置文件对包的重复扫描装配会照成失效在主容器中(applicationContext.xml),将Controller的注解排除掉--> <context:component-scan base-package="example"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 创建sessinFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="packagesToScan"> <list> <value>example.entity</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hbm2ddl.auto">update</prop> </props> </property> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> </beans>
然后在service层的类或方法上加@Transactional即可。
注解方式需添加<tx:annotation-driven transaction-manager="transactionManager"/>,且@Transactional只能放在public修饰的方法或类,其它访问权限前添加无效。
事务的隔离级别及传播方式可通过下面方式配置(propagation配置传播方式默认为REQUIRED,isolation配置隔离级别,默认为DEFAULT,用的为数据库的默认隔离级别,大部分数据库默认隔离级别为READ_COMMITTED,mysql为REPEATABLE_READ)
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
2、aop方式
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--spring的配置文件 与springMVC的配置文件对包的重复扫描装配会照成失效在主容器中(applicationContext.xml),将Controller的注解排除掉--> <context:component-scan base-package="example"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_hibernate"/> <property name="username" value="root"/> <property name="password" value=""/> </bean> <!-- 创建sessinFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="packagesToScan"> <list> <value>example.entity</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hbm2ddl.auto">update</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut expression="execution(* example.service..*.*(..))" id="fooServiceOperation"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/> </aop:config> </beans>
事务的隔离级别及传播方式可通过下面方式配置(propagation配置传播方式,isolation配置隔离级别)
<tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
二、配置后事务不起作用原因
1、dao层需使用sessionFactory.getCurrentSession()方法获得当前session,不能使用sessionFactory.openSession()
原因:
采用getCurrentSession()创建的Session会绑定到当前的线程中去、而采用OpenSession()则不会。
采用getCurrentSession()创建的Session在commit或rollback后会自动关闭,采用OpenSession()必须手动关闭。
2、applicationContext.xml中需添加下面配置
<!--spring的配置文件 与springMVC的配置文件对包的重复扫描装配会照成失效在主容器中(applicationContext.xml),将Controller的注解排除掉-->
<context:component-scan base-package="example">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>