Spring 事務 readOnly 究竟是怎麼回事?

Spring的事務常常會有這樣的配置:java

1 <tx:method name="search*" read-only="true" /> 

或者這樣的註記:mysql

1 @Transactional(readOnly = true)

正好我正在作的項目中這樣配置了,並且偶然發現配置了不生效,本着「不弄明白對不起祖國對不起人民」的精神,參考了很多帖子和文檔,總結了網上形形色色的答案,稍有收穫,規整以下,不正確請指出。sql

1 readonly並非全部數據庫都支持的,不一樣的數據庫下會有不一樣的結果。 2 設置了readonly後,connection都會被賦予readonly,效果取決於數據庫的實現。 3 在ORM中,設置了readonly會賦予一些額外的優化,例如在Hibernate中,會被禁止flush等。

經實踐,上面的觀點基本正確。數據庫

環境:Spring-3.1.一、jdk六、oracle-11gR二、mysql-5.6.1六、ojdbc六、mysql-connector-java-5.1.3一、ibatis-2.3.4.726等,使用的Spring的DataSourceTransactionManager 事務管理器。性能優化

查看DataSourceTransactionManager 相關代碼可知readOnly值最終是傳給Connection的:oracle

複製代碼
 1         // Set read-only flag.  2 if (definition != null && definition.isReadOnly()) {  3 try {  4 if (logger.isDebugEnabled()) {  5 logger.debug("Setting JDBC Connection [" + con + "] read-only");  6  }  7 con.setReadOnly(true);  8  }  9 catch (SQLException ex) { 10 Throwable exToCheck = ex; 11 while (exToCheck != null) { 12 if (exToCheck.getClass().getSimpleName().contains("Timeout")) { 13 // Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0 14 throw ex; 15  } 16 exToCheck = exToCheck.getCause(); 17  } 18 // "read-only not supported" SQLException -> ignore, it's just a hint anyway 19 logger.debug("Could not set JDBC Connection read-only", ex); 20  } 21 catch (RuntimeException ex) { 22 Throwable exToCheck = ex; 23 while (exToCheck != null) { 24 if (exToCheck.getClass().getSimpleName().contains("Timeout")) { 25 // Assume it's a connection timeout that would otherwise get lost: e.g. from Hibernate 26 throw ex; 27  } 28 exToCheck = exToCheck.getCause(); 29  } 30 // "read-only not supported" UnsupportedOperationException -> ignore, it's just a hint anyway 31 logger.debug("Could not set JDBC Connection read-only", ex); 32  } 33 }
複製代碼

一、在oracle下測試,發現不支持readOnly,也就是不論Connection裏的readOnly屬性是true仍是false均不影響SQL的增刪改查;框架

二、在mysql下測試,發現支持readOnly,設置爲true時,只能查詢,若增刪改會異常:post

1 Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed 2 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910) 3 at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:792)

三、爲了排除各類框架封裝的影響,寫JDBC原生代碼也是相同的結論。性能

====================疑問的分隔線==============================================測試

網上的各類資料裏衆說紛紜:

「只讀事務」並非一個強制選項,它只是一個「暗示」,提示數據庫驅動程序和數據庫系統,這個事務並不包含更改數據的操做,那麼JDBC驅動程序和數據庫就有可能根據這種狀況對該事務進行一些特定的優化,比方說不安排相應的數據庫鎖,以減輕事務對數據庫的壓力,畢竟事務也是要消耗數據庫的資源的。
可是你非要在「只讀事務」裏面修改數據,也並不是不能夠,只不過對於數據一致性的保護不像「讀寫事務」那樣保險而已。
所以,「只讀事務」僅僅是一個性能優化的推薦配置而已,並不是強制你要這樣作不可。

---------------------------------------------

早期的提問會有這樣的結論:

readOnly對oracle不生效是由於:

1 con.setReadOnly(true); 2 con.setAutoCommit(false);

autoCommit與readOlny賦值的順序對其有影響,readonly在後則生效,readolny在前是無效的可進行insert/update/delete操做。

一樣,DataSourceTransactionManager 裏也是由於這個緣由:

複製代碼
 1             Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);  2  txObject.setPreviousIsolationLevel(previousIsolationLevel);  3  4 // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,  5 // so we don't want to do it unnecessarily (for example if we've explicitly  6 // configured the connection pool to set it already).  7 if (con.getAutoCommit()) {  8 txObject.setMustRestoreAutoCommit(true);  9 if (logger.isDebugEnabled()) { 10 logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); 11  } 12 con.setAutoCommit(false); 13 }
複製代碼

由於DataSourceUtils.prepareConnectionForTransaction(con, definition)裏會先設置readOnly屬性,致使readOnly對oracle不生效;

-------------------------------------------------

他們的實踐結果使我不得不相信他們說的真是這樣,但以我如今的環境測試,無論什麼順序均無影響,readOnly就是對oracle不生效;看那些帖子時間已經是幾年前,版本差異太大,已無從考證……

====================疑問的分隔線==============================================

我也看到有人找到oracle的官方文檔連接裏句子來講緣由:

http://docs.oracle.com/cd/B14117_01/java.101/b10979/tips.htm#i1007231

http://docs.oracle.com/cd/B19306_01/java.102/b14355/apxtips.htm#i1007231

http://docs.oracle.com/cd/B28359_01/java.111/b31224/apxtips.htm#i1007231

http://docs.oracle.com/cd/E11882_01/java.112/e16548/apxtips.htm#i1007231

10.一、10.2和11.1的文檔裏寫着:

 1 Read-only connections are supported by the Oracle server, but not by the Oracle JDBC drivers. 

11.2的文檔裏寫着:

 1 Read-only connections are supported by Oracle JDBC drivers, but not by the Oracle server. 

再結合oracle能夠設置 「set transaction read only」,搞不清楚哪一個對哪一個錯,反正就是不支持。。。

相關文章
相關標籤/搜索