diff --git a/license.txt b/license.txt index 8deeff6746..00b845b43b 100644 --- a/license.txt +++ b/license.txt @@ -1,4 +1,4 @@ -Copyright 1999-2016 Alibaba Group Holding Ltd. +Copyright 1999-2017 Alibaba Group Holding Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java index 07a325c2f3..d2a473d690 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java @@ -177,6 +177,12 @@ public class DruidPooledConnection extends PoolableWrapper implements javax.sql. stmt.getPreparedStatementHolder().setFetchRowPeak(stmt.getFetchRowPeak()); stmt.setClosed(true); // soft set close + } else if (stmt.isPooled() && holder.isPoolPreparedStatements()) { + // the PreparedStatement threw an exception + stmt.clearResultSet(); + holder.removeTrace(stmt); + + holder.getStatementPool().remove(stmt.getPreparedStatementHolder()); } else { try { //Connection behind the statement may be in invalid state, which will throw a SQLException. diff --git a/src/main/java/com/alibaba/druid/pool/PreparedStatementPool.java b/src/main/java/com/alibaba/druid/pool/PreparedStatementPool.java index d6f64ebc18..d5c3d0b994 100644 --- a/src/main/java/com/alibaba/druid/pool/PreparedStatementPool.java +++ b/src/main/java/com/alibaba/druid/pool/PreparedStatementPool.java @@ -72,6 +72,14 @@ public class PreparedStatementPool { return holder; } + public void remove(PreparedStatementHolder stmtHolder) throws SQLException { + if (stmtHolder == null) { + return; + } + map.remove(stmtHolder.key); + closeRemovedStatement(stmtHolder); + } + public void put(PreparedStatementHolder stmtHolder) throws SQLException { PreparedStatement stmt = stmtHolder.statement; diff --git a/src/test/java/com/alibaba/druid/pool/ClosePoolableStatementTest.java b/src/test/java/com/alibaba/druid/pool/ClosePoolableStatementTest.java new file mode 100644 index 0000000000..8a9c39ed73 --- /dev/null +++ b/src/test/java/com/alibaba/druid/pool/ClosePoolableStatementTest.java @@ -0,0 +1,76 @@ +package com.alibaba.druid.pool; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementSetter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * 测试开启PSCache后关闭DruidPooledPreparedStatement时的情况 + * + * @author DigitalSonic + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:com/alibaba/druid/pool/dataSource.xml") +public class ClosePoolableStatementTest { + @Autowired + private DruidDataSource dataSource; + + private JdbcTemplate jdbcTemplate; + + @Before + public void setUp() throws Exception { + dataSource.setPoolPreparedStatements(true); + dataSource.setMaxPoolPreparedStatementPerConnectionSize(5); + dataSource.init(); + + jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("create table test (id int, val varchar(32), primary key (id))"); + } + + @After + public void tearDown() throws Exception { + jdbcTemplate.execute("drop table test"); + } + + /** + * 关注抛出异常后能否正确将PreparedStatement移出缓存,能正常执行后续SQL + */ + @Test + public void testClose() throws Exception { + insertData(1, "a"); + try { + insertData(1, "a"); + } catch (Exception e) { + assertTrue(e instanceof DuplicateKeyException); + } + try { + insertData(1, "a"); + } catch (Exception e) { + assertTrue(e instanceof DuplicateKeyException); + assertEquals(-1, e.getMessage().indexOf("closed")); + } + } + + private void insertData(final int id, final String val) { + jdbcTemplate.update("insert into test (id, val) values (?, ?)", new PreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps) throws SQLException { + ps.setInt(1, id); + ps.setString(2, val); + } + }); + } +}