因为测试时采用的是一个连接这种极端条件,因此该现象非常容易复现,且是100%的复现,但是在测试条件下,并非100%复现,而是在重启之后会好一段时间,一段时间以后就会重新出现这个情况。
如果将读取类的代码稍加修改:
public class GetClient { private void query() throws SQLException { System.out.println("start"); MysqlClient instance = MysqlClient.getInstance(); Connection conn = instance.getConnection(); conn.setAutoCommit(true); String sql = "select uname from test1"; PreparedStatement statement = conn.prepareStatement(sql); ResultSet rs = statement.executeQuery(); while (rs.next()) { System.out.println(rs.getString("uname")); } statement.close(); rs.close(); conn.close(); } private void nothing() throws SQLException { MysqlClient instance = MysqlClient.getInstance(); Connection conn = instance.getConnection(); conn.setAutoCommit(false); conn.close(); } public static void main(String[] args) throws SQLException, InterruptedException, ClassNotFoundException { DBconfigEntity entity = new DBconfigEntity(); entity.setDbName("test"); entity.setDbPasswd("123456"); entity.setDbUser("root"); entity.setIp("127.0.0.1"); entity.setPort(3306); MysqlClient.init(entity); GetClient client = new GetClient(); client.nothing(); while (true) { client.query(); Thread.sleep(5000); } } }注意我在query方法中加入这一句:conn.setAutoCommit(true);
此时这个问题不再出现。
源码分析 jdbc驱动源码分析Connection是Java提供的一个标准接口:java.sql.Connection,其具体实现是:com.mysql.jdbc.ConnectionImpl。
分析jdbc驱动代码可知,jdbc默认的AutoCommit状态是TRUE:
这实际上和MySQL的默认值是一样的。
tomcat-jdbc源码分析tomcat-jdbc的close方法由拦截器实现,具体的逻辑代码:
if (compare(CLOSE_VAL,method)) { if (connection==null) return null; //noop for already closed. PooledConnection poolc = this.connection; this.connection = null; pool.returnConnection(poolc); return null; }实际上此处只是将连接还给了连接池,没有对连接进行任何处理。
tomcat-jdbc维护了两个Queue:busy和idle,用于存放空闲和已借出连接,连接还给连接池的过程简单的说就是将该连接从busy队列中移除,并放在idle队列中的过程。
boneCP源码分析根据实际使用的经验看,boneCP连接池在使用的过程中并没有出现这个问题,分析boneCP的Connection具体实现,发现在close方法的具体实现中,有这样的一段代码逻辑:
if (!getAutoCommit()) { setAutoCommit(true); }这段逻辑会判断该连接的AutoCommit属性是否为FALSE,如果是,就自动将其置为TRUE。
因此,在这个连接被交还回连接池时,AutoCommit属性总是TRUE。
结论任何查询接口都应该在获取连接以后进行AutoCommit的设置,将其设置为true。