背景
生产上一条sql可读性与性能都不佳,从而用with 语法改造优化,测试通过之后放到项目中执行却出现 | java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed 这个异常,很奇怪优化的sql并不涉及修改.
排查过程,及原因
由于项目不属于我所在项目组,所以只能通过兄弟部门提供的远程调试端口排查;
- 声明对应Exception断点,锁定到抛出这个异常的代码段 最终锁定在com.mysql.jdbc.PreparedStatement中
/**
* Check to see if the statement is safe for read-only slaves after failover.
*
* @return true if safe for read-only.
* @throws SQLException
*/
protected boolean checkReadOnlySafeStatement() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.firstCharOfStmt == 'S' || !this.connection.isReadOnly();
}
}
在这个方法中可以看出判断一条sql语句是否只读,通过两个条件,外层还有一个参数总共三个参数,但这里我主要说方法内的两个,有兴趣看 源码的朋友可以参考com.mysql.jdbc.PreparedStatement.execute()方法,说回__checkReadOnlySafeStatement__这个方法,首先第 二个条件__connection.isReadOnly__这个很好理解,通过连接属性判断很合理,但是第一个条件语句是否为S开头就略显粗燥了,至少我 觉得很不优雅,这也就是低版本的mysql jar包不支持with语法的原因了.
解决方案
如果可以的话当然是选择升级jar包的版本,但是毕竟这是兄弟部门的项目,总不能友情提醒隔壁该升级版本了,但是不要紧这时候看了源码就有 很好的办法绕过去了,虽然这个方法看起来也不是很优雅,但是对比S开头字母判断只读操作来说,也不至于太难看,毕竟如果有拓展表需要关联的 时候这么做合情合理,说到这里大概能猜出来了吧,没错就是在外面强行包多一层SELECT语句.