Spring的事务经常会有这样的配置:
1 <tx:method name="search*" read-only="true" />
或者这样的注记:
1 @Transactional(readOnly = true)
正好我正在做的项目中这样配置了,而且偶然发现配置了不生效,本着“不弄明白对不起祖国对不起人民”的精神,参考了不少帖子和文档,总结了网上形形色色的答案,稍有收获,规整如下,不正确请指出。
1 readonly并不是所有数据库都支持的,不同的数据库下会有不同的结果。 2 设置了readonly后,connection都会被赋予readonly,效果取决于数据库的实现。 3 在ORM中,设置了readonly会赋予一些额外的优化,例如在Hibernate中,会被禁止flush等。
经实践,上面的观点基本正确。
环境:Spring-3.1.1、jdk6、oracle-11gR2、mysql-5.6.16、ojdbc6、mysql-connector-java-5.1.31、ibatis-2.3.4.726等,使用的Spring的DataSourceTransactionManager 事务管理器。
查看DataSourceTransactionManager 相关代码可知readOnly值最终是传给Connection的:
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 }
1、在oracle下测试,发现不支持readOnly,也就是不论Connection里的readOnly属性是true还是false均不影响SQL的增删改查;
2、在mysql下测试,发现支持readOnly,设置为true时,只能查询,若增删改会异常:
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)
3、为了排除各种框架封装的影响,写JDBC原生代码也是相同的结论。
“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。
因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可。