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”,搞不清楚哪一个对哪一个错,反正就是不支持。。。