/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.dbcp2; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * TestSuite for BasicDataSource with prepared statement pooling enabled */ public class TestPStmtPoolingBasicDataSource extends TestBasicDataSource { @Override @BeforeEach public void setUp() throws Exception { super.setUp(); // PoolPreparedStatements enabled, should not affect the basic tests ds.setPoolPreparedStatements(true); ds.setMaxOpenPreparedStatements(2); } @Test public void testPreparedStatementPooling() throws Exception { final Connection conn = getConnection(); assertNotNull(conn); final PreparedStatement stmt1 = conn.prepareStatement("select 'a' from dual"); assertNotNull(stmt1); final PreparedStatement stmt2 = conn.prepareStatement("select 'b' from dual"); assertNotNull(stmt2); assertTrue(stmt1 != stmt2); // go over the maxOpen limit PreparedStatement stmt3 = null; try (PreparedStatement ps = conn.prepareStatement("select 'c' from dual")) { fail("expected SQLException"); } catch (final SQLException e) {} // make idle stmt2.close(); // test cleanup the 'b' statement stmt3 = conn.prepareStatement("select 'c' from dual"); assertNotNull(stmt3); assertTrue(stmt3 != stmt1); assertTrue(stmt3 != stmt2); // normal reuse of statement stmt1.close(); try (final PreparedStatement stmt4 = conn.prepareStatement("select 'a' from dual")) { assertNotNull(stmt4); } } /** * Verifies that the prepared statement pool behaves as an LRU cache, * closing least-recently-used statements idle in the pool to make room * for new ones if necessary. */ @Test public void testLRUBehavior() throws Exception { ds.setMaxOpenPreparedStatements(3); final Connection conn = getConnection(); assertNotNull(conn); // Open 3 statements and then close them into the pool final PreparedStatement stmt1 = conn.prepareStatement("select 'a' from dual"); final PreparedStatement inner1 = (PreparedStatement) ((DelegatingPreparedStatement) stmt1).getInnermostDelegate(); final PreparedStatement stmt2 = conn.prepareStatement("select 'b' from dual"); final PreparedStatement inner2 = (PreparedStatement) ((DelegatingPreparedStatement) stmt2).getInnermostDelegate(); final PreparedStatement stmt3 = conn.prepareStatement("select 'c' from dual"); final PreparedStatement inner3 = (PreparedStatement) ((DelegatingPreparedStatement) stmt3).getInnermostDelegate(); stmt1.close(); Thread.sleep(100); // Make sure return timestamps are different stmt2.close(); Thread.sleep(100); stmt3.close(); // Pool now has three idle statements, getting another one will force oldest (stmt1) out final PreparedStatement stmt4 = conn.prepareStatement("select 'd' from dual"); assertNotNull(stmt4); // Verify that inner1 has been closed try { inner1.clearParameters(); fail("expecting SQLExcption - statement should be closed"); } catch (final SQLException ex) { //Expected } // But others are still open inner2.clearParameters(); inner3.clearParameters(); // Now make sure stmt1 does not come back from the dead final PreparedStatement stmt5 = conn.prepareStatement("select 'a' from dual"); final PreparedStatement inner5 = (PreparedStatement) ((DelegatingPreparedStatement) stmt5).getInnermostDelegate(); assertNotSame(inner5, inner1); // inner2 should be closed now try { inner2.clearParameters(); fail("expecting SQLExcption - statement should be closed"); } catch (final SQLException ex) { //Expected } // But inner3 should still be open inner3.clearParameters(); } // Bugzilla Bug 27246 // PreparedStatement cache should be different depending on the Catalog @Test public void testPStmtCatalog() throws Exception { final Connection conn = getConnection(); conn.setCatalog("catalog1"); final DelegatingPreparedStatement stmt1 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual"); final TesterPreparedStatement inner1 = (TesterPreparedStatement) stmt1.getInnermostDelegate(); assertEquals("catalog1", inner1.getCatalog()); stmt1.close(); conn.setCatalog("catalog2"); final DelegatingPreparedStatement stmt2 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual"); final TesterPreparedStatement inner2 = (TesterPreparedStatement) stmt2.getInnermostDelegate(); assertEquals("catalog2", inner2.getCatalog()); stmt2.close(); conn.setCatalog("catalog1"); final DelegatingPreparedStatement stmt3 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual"); final TesterPreparedStatement inner3 = (TesterPreparedStatement) stmt3.getInnermostDelegate(); assertEquals("catalog1", inner3.getCatalog()); stmt3.close(); assertNotSame(inner1, inner2); assertSame(inner1, inner3); } @Test public void testPStmtPoolingWithNoClose() throws Exception { ds.setMaxTotal(1); // only one connection in pool needed ds.setMaxIdle(1); ds.setAccessToUnderlyingConnectionAllowed(true); final Connection conn1 = getConnection(); assertNotNull(conn1); assertEquals(1, ds.getNumActive()); assertEquals(0, ds.getNumIdle()); final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual"); assertNotNull(stmt1); final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate(); assertNotNull(inner1); stmt1.close(); final Connection conn2 = conn1; assertNotNull(conn2); assertEquals(1, ds.getNumActive()); assertEquals(0, ds.getNumIdle()); final PreparedStatement stmt2 = conn2.prepareStatement("select 'a' from dual"); assertNotNull(stmt2); final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate(); assertNotNull(inner2); assertSame(inner1, inner2); } @Test public void testPStmtPoolingAcrossClose() throws Exception { ds.setMaxTotal(1); // only one connection in pool needed ds.setMaxIdle(1); ds.setAccessToUnderlyingConnectionAllowed(true); final Connection conn1 = getConnection(); assertNotNull(conn1); assertEquals(1, ds.getNumActive()); assertEquals(0, ds.getNumIdle()); final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual"); assertNotNull(stmt1); final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate(); assertNotNull(inner1); stmt1.close(); conn1.close(); assertEquals(0, ds.getNumActive()); assertEquals(1, ds.getNumIdle()); final Connection conn2 = getConnection(); assertNotNull(conn2); assertEquals(1, ds.getNumActive()); assertEquals(0, ds.getNumIdle()); final PreparedStatement stmt2 = conn2.prepareStatement("select 'a' from dual"); assertNotNull(stmt2); final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate(); assertNotNull(inner2); assertSame(inner1, inner2); } /** * Tests high-concurrency contention for connections and pooled prepared statements. * DBCP-414 */ @Test public void testMultipleThreads1() throws Exception { ds.setMaxWaitMillis(-1); ds.setMaxTotal(5); ds.setMaxOpenPreparedStatements(-1); multipleThreads(5, false, false, -1, 3, 100, 10000); } }