Spring事物不回滾

spring事物不回滾的問題

一、環境介紹

mysql數據庫,springMVC,Mybatis,搭建完成測試過程中一個偶然的原因發現事物不回滾,立馬想到數據庫不支持。這個下面會有詳細說明。

二、mysql數據庫介紹

Mysql的存儲引擎:MyIsAm、InnoDB、MEMORY、MERGE這四種,四種各有各的好處,技術選型可以針對不同的需求選擇不同的方式。

  1.MyIsAm

MyISAM是MySQL的默認存儲引擎。MyISAM不支持事務、也不支持外鍵,但其訪問速度快,對事務完整性沒有要求。 
MyISAM表還支持3中不同的存儲格式: 
1 靜態表 
2 動態表 
3 壓縮表 
靜態表是默認的存儲格式,靜態表中的字段都是非變長的字段,
優點是:存儲非常迅速,容易緩存,出現故障容易恢復;
缺點是:佔用的空間通常比動態表多。
(注意: 在存儲時,列的寬度不足時,用空格補足,當時在訪問的時候並不會得到這些空格)
動態表的字段是變長的,優點是:佔用的空間相對較少,但是頻繁地更新刪除記錄會產生碎片,
需要定期改善性能,並且出現故障的時候恢復相對比較困難。 
壓縮表佔用磁盤空間小,每個記錄是被單獨壓縮的,所以只有非常小的訪問開支。

2.InnoDB

InnoDB存儲引擎提供了具有提交、回滾和崩潰恢復能力的事務安全。但是比起MyISAM存儲引擎,
InnoDB寫的處理效率差一些並且會佔用更多的磁盤空間以保留數據和索引。
而且MySQL支持外鍵存儲引擎只有InnoDB,在創建外鍵的時候,
要求附表必須有對應的索引,子表在創建外鍵的時候也會自動創建對應的索引。(被關聯表的外鍵必須是關聯表的主鍵) 
InnoDB的理想使用場合:高併發,更新操作比較多的表。需要使用事務的表。對自動災難恢復有要求的表。

3.MEMORY

MEMORY存儲引擎使用存在內存中的內容來創建表。
每個MEMORY表只實際對應一個磁盤文件。MEMORY類型的表訪問非常得快,因爲它的數據是放在內存中的,並且默認使用HASH索引。
但是一旦服務關閉,表中的數據就會丟失掉。Memory存儲引擎的使用場合,速度要求快的,臨時數據

  4.MERGE

merge存儲引擎是一組MyISAM表的組合,這些MyISAM表結構必須完全相同,MERGE表中並沒有數據, 對MERGE類型的表可以進行查詢、更新、刪除的操作,這些操作實際上是對內部的MyISAM表進行操作。 對於對MERGE表進行的插入操作,是根據INSERT_METHOD子句定義的插入的表,可以有3個不同的值, first和last值使得插入操作被相應的作用在第一個或最後一個表上,不定義這個子句或者爲NO, 表示不能對這個MERGE表進行插入操作。可以對MERGE表進行drop操作,這個操作只是刪除MERGE表的定義, 對內部的表沒有任何影響。MERGE在磁盤上保留2個以MERGE表名開頭文件:.frm文件存儲表的定義; .MRG文件包含組合表的信息,包括MERGE表由哪些表組成,插入數據時的依據。 可以通過修改.MRG文件來修改MERGE表,但是修改後要通過flush table刷新。

三、解決方案

 現在對mysql的存儲引擎有了解的話,那麼就知道如果你的表的存儲引擎方式是InnoDB的話,那麼不支持事物回滾就是你的配置問題了。下面是具體的解決方法

  1.修改mysql的存儲引擎方式

show engines; #顯示數據庫是否支持InnoDB
更改方式1:修改配置文件my.cnf
打開my.cnf,在[mysqld]最後添加爲上default-storage-engine=InnoDB,重啓數據庫服務,數據庫默認的引擎修改爲InnoDB
更改方式2:在建表的時候指定或者建完表修改
create table tableName( id int primary key, name varchar(50) )type=InnoDB;

建完表之後修改也可以
alter table tableName ENGINE=InnoDB; #mysql5.0以後用這種方式
alter table tableName type = InnoDB; #mysql5.0之前用這種方式
修改
之後查看
show create table tableName; #這個信息可能比較多注意查看ENGINE這個關鍵字。

2.配置方式

<?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:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
		">

	<!-- 配置數據源 使用的是Druid數據源 -->
	<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		init-method="init" destroy-method="close">
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />

		<!-- 初始化連接大小 -->
		<property name="initialSize" value="0" />
		<!-- 連接池最大使用連接數量 -->
		<property name="maxActive" value="20" />
		
		<!-- 連接池最小空閒 -->
		<property name="minIdle" value="0" />
		<!-- 獲取連接最大等待時間 -->
		<property name="maxWait" value="60000" />
		<property name="poolPreparedStatements" value="true" />
		<property name="maxPoolPreparedStatementPerConnectionSize"
			value="33" />
		<!-- 用來檢測有效sql -->
		<property name="validationQuery" value="${validationQuery}" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="testWhileIdle" value="true" />
		<!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<!-- 配置一個連接在池中最小生存的時間,單位是毫秒 -->
		<property name="minEvictableIdleTimeMillis" value="25200000" />
		<!-- 打開removeAbandoned功能 -->
		<property name="removeAbandoned" value="true" />
		<!-- 1800秒,也就是30分鐘 -->
		<property name="removeAbandonedTimeout" value="1800" />
		<!-- 關閉abanded連接時輸出錯誤日誌 -->
		<property name="logAbandoned" value="true" />
		<!-- 監控數據庫 -->
		<property name="filters" value="mergeStat" />
	</bean>

	<!-- myBatis文件 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<!-- 添加mybatis的配置 -->
		<property name="configLocation" value="classpath:mybatis.xml"/>
		<!-- 自動掃描entity目錄, 省掉Configuration.xml裏的手工配置 -->
		<property name="mapperLocations" value="classpath:com/tanrice/dao/impl/*.xml" />
	</bean>

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.tanrice.dao" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>

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


	<!-- 註解的方式配置事務 -->
	<!-- 在需要的地方配置 -->
	<!-- <tx:annotation-driven transaction-manager="transactionManager"  proxy-target-class="true"/> -->

	<!-- 註解方式配置事物 -->
	<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="append*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="edit*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="remove*" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="repair" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="delAndRepair" propagation="REQUIRED" isolation="READ_COMMITTED" />
			<tx:method name="get*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
			<tx:method name="find*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
			<tx:method name="load*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
			<tx:method name="search*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
			<tx:method name="datagrid*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
			<tx:method name="*" propagation="SUPPORTS" isolation="READ_COMMITTED" />
		</tx:attributes>
	</tx:advice>
	<!-- Spring aop事務管理 -->
	<aop:config>
		<aop:pointcut id="transactionPointcut" expression="execution(* com.tanrice.service..*Impl.*(..))" />
		<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
	</aop:config>
	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
         <constructor-arg index="0" ref="sqlSessionFactory" /> 
    </bean>
</beans>

針對不同的情況在service裏面的代碼也不一樣。
1. 使用aop的方式:那麼將會注入所有的切面。這樣務必會造成資源的部分浪費
  2. 使用註解的方式。這樣可以在想要的地方加上 @Transactional註解。但是在交接,或者忙的時候會忘記。
這兩種方式都可以,各有優缺點。下面是插入相關的代碼
@Service
@Transactional
public class UserTaskServiceImpl implements UserTaskService{
public boolean updateUserTask(UserTask userTask,int id) {
		boolean flag = false;
		try{
			flag = userTaskDao.saveUserTask(userTask)>0?true:false;
			System.out.println("插入:"+flag);
			flag = taskDao.updateTaskLeftcountById(id)>0?true:false;
			System.out.println("修改:"+flag);
		}catch(Exception e){
			throw new RuntimeException();
		}
		return flag;
	}
}



測試類
@org.junit.Test
	public void testTrx(){
		UserTask userTask = new UserTask();
		userTask.setFullname("wanda");
		userTask.setUserid(10071);
		userTask.setTaskid(10026);
		userTask.setGettime(new Date().getTime());
		userTask.setState(5);
		
		System.out.println("結果是:"+userTaskService.updateUserTask(userTask, 10026));
	}

結果:在異常情況下,會回滾數據
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章