java jdbc 的Statement 接口中定义了setQueryTimeout 方法,该方法API:
void setQueryTimeout(int seconds) throws SQLException 将驱动程序等待 Statement 对象执行的秒数设置为给定秒数。如果超过该限制,则抛出 SQLException。 JDBC 驱动程序必须将此限制应用于 execute、executeQuery 和 executeUpdate 方法。 JDBC 驱动程序实现也可以将此限制应用于 ResultSet 方法 (有关详细信息,请参考驱动程序供应商文档)。 参数: seconds - 以秒为单位的查询超时限制;0 表示没有任何限制 抛出: SQLException - 如果发生数据库访问错误,在已关闭的 Statement 上调用此方法,或者不满足条件 seconds >= 0 另请参见: getQueryTimeout()
即如果数据库jdbc驱动支持情况下,可实现超时异常,这对于某些情况特别有用:
1. sql执行时间特别长,导至执行线程长时间阻塞,减低请求处理的吞吐量。
2. sql执行产生死锁,或等待另外的连接释放锁,而一直阻塞。
MyBatis设置setQueryTimeout的方式:
1.mybatis配置的xml中配置:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!--设置SQL执行超时时间,超时抛出异常--> <setting name="defaultStatementTimeout" value="30"/> </settings> </configuration>
2.在sql配置的xml中配置timeout,优先级高于1
<update id="updateName" timeout="20"> update my_table set name=#{param1} where id= #{param2} </update>
MyBatis 在哪块调用Statement.setQueryTimeout方法??
public abstract class BaseStatementHandler implements StatementHandler { @Override public Statement prepare(Connection connection) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); setStatementTimeout(statement);//设置sql执行超时时间 setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } } protected void setStatementTimeout(Statement stmt) throws SQLException { Integer timeout = mappedStatement.getTimeout(); Integer defaultTimeout = configuration.getDefaultStatementTimeout(); if (timeout != null) { // 如果配置的SQL中设置了timeout ? // 如: // <update id="updateName" timeout="20"> // update test_user set name=#{param1} where id= #{param2} // </update> // stmt.setQueryTimeout(timeout); } else if (defaultTimeout != null) { //如果设置了全局的超时时间 //如在mybatis 的xml配置文件中Settings 下设置了: // <setting name="defaultStatementTimeout" value="20"/> stmt.setQueryTimeout(defaultTimeout); } } }
如果jdbc驱动支持,则超时会抛出异常。
经测:PostgreSQL 9.5 支持。
示例异常:
Caused by: org.postgresql.util.PSQLException: ERROR: canceling statement due to user request 在位置:while updating tuple (0,12) in relation "my_table" at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2270) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1998) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255) at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:570) at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:420) at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:413) at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:493) at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:45) at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:73) at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:115) at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:170) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:386)