1、在xml文件中加入事务管理器
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${jdbc.driver}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="initialPoolSize" value="${connection_pools.initial_pool_size}" /> <property name="minPoolSize" value="${connection_pools.min_pool_size}" /> <property name="maxPoolSize" value="${connection_pools.max_pool_size}" /> <property name="maxIdleTime" value="${connection_pools.max_idle_time}" /> <property name="acquireIncrement" value="${connection_pools.acquire_increment}" /> <property name="checkoutTimeout" value="${connection_pools.checkout_timeout}" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceXmlLocation" value="classpath*:/persistence.xml" /> <property name="persistenceUnitName" value="persistenceUnit" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop> <prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop> <prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop> <prop key="hibernate.jdbc.fetch_size">${hibernate.jdbc.fetch_size}</prop> <prop key="hibernate.jdbc.batch_size">${hibernate.jdbc.batch_size}</prop> <prop key="hibernate.connection.isolation">2</prop> <prop key="javax.persistence.validation.mode">none</prop> <prop key="hibernate.search.default.directory_provider">filesystem</prop> <prop key="hibernate.search.default.indexBase">${java.io.tmpdir}/${system.project_name}/index</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.use_sql_comments">false</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.use_sql_comments">true</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
2、在类上或者方法上加入@Transactional
标注在类前:标示类中所有方法都进行事务处理
标注在接口、实现类的方法前:标示方法进行事务处理
事务传播行为:
@Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况)
@Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) 必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务。如果其他bean没有声明事务,那就不用事务
事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED) 读取未提交数据(会出现脏读, 不可重复读),基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默认) 读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ) 可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE) 串行化
由于Spring事务管理是基于接口代理或者动态字节码技术,通过AOP实施事务增强的。基于接口动态代理的AOP事务增强来说,由于接口的方法是public的,这句要求实现类的实现方法必须是public 的(不能是protect,private等),同时不能使用static的修饰符。所以可以实施接口动态代理的方法也只能是“public”或者“public fianl ” 其他方法不可能被动态代理,相应的也就不能实施AOP增强,也无法进行Spring事务增强。
@Transactional 注解标识的方法,处理过程尽量的简单。尤其是带锁的事务方法,能不放在事务里面的最好不要放在事务里面。可以将常规的数据库查询操作放在事务前面进行,而事务内进行增、删、改、加锁查询等操作。
原理:
运行配置@Transactional注解的测试类的时候,具体会发生如下步骤
1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令[不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]
2)事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象
根据上面所述,我们所使用的客户代码应该具有如下能力:
1)每次执行数据库命令的时候
如果在事务的上下文环境中,那么不直接创建新的connection对象,而是尝试从DataSource实例的某个与DataSourceTransactionManager相关的某处容器中获取connection对象;在非事务的上下文环境中,直接创建新的connection对象
2)每次执行完数据库命令的时候
如果在事务的上下文环境中,那么不直接关闭connection对象,因为在整个事务中都需要使用该connection对象,而只是释放本次数据库命令对该connection对象的持有;在非事务的上下文环境中,直接关闭该connection对象