org.springframework.jdbc.object.StoredProcedureTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.jdbc.object.StoredProcedureTests.java

Source

/*
 * Copyright 2002-2008 the original author or authors.
 *
 * Licensed 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.springframework.jdbc.object;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.sql.ResultSetMetaData;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;

import javax.sql.DataSource;

import org.easymock.MockControl;
import org.apache.commons.logging.LogFactory;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.AbstractJdbcTests;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SimpleRowCountCallbackHandler;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlReturnResultSet;
import org.springframework.jdbc.core.support.AbstractSqlTypeValue;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * @author Thomas Risberg
 * @author Trevor Cook
 * @author Rod Johnson
 */
public class StoredProcedureTests extends AbstractJdbcTests {

    private final boolean debugEnabled = LogFactory.getLog(JdbcTemplate.class).isDebugEnabled();

    private MockControl ctrlCallable;
    private CallableStatement mockCallable;

    protected void setUp() throws Exception {
        super.setUp();

        ctrlCallable = MockControl.createControl(CallableStatement.class);
        mockCallable = (CallableStatement) ctrlCallable.getMock();
    }

    protected void tearDown() throws Exception {
        super.tearDown();
        if (shouldVerify()) {
            ctrlCallable.verify();
        }
    }

    protected void replay() {
        super.replay();
        ctrlCallable.replay();
    }

    public void testNoSuchStoredProcedure() throws Exception {
        SQLException sex = new SQLException("Syntax error or access violation exception", "42000");
        mockCallable.execute();
        ctrlCallable.setThrowable(sex);
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + NoSuchStoredProcedure.SQL + "()}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();

        NoSuchStoredProcedure sproc = new NoSuchStoredProcedure(mockDataSource);
        try {
            sproc.execute();
            fail("Shouldn't succeed in running stored procedure which doesn't exist");
        } catch (BadSqlGrammarException ex) {
            // OK
        }
    }

    private void testAddInvoice(final int amount, final int custid) throws Exception {
        AddInvoice adder = new AddInvoice(mockDataSource);
        int id = adder.execute(amount, custid);
        assertEquals(4, id);
    }

    private void testAddInvoiceUsingObjectArray(final int amount, final int custid) throws Exception {
        AddInvoiceUsingObjectArray adder = new AddInvoiceUsingObjectArray(mockDataSource);
        int id = adder.execute(amount, custid);
        assertEquals(5, id);
    }

    public void testAddInvoices() throws Exception {
        mockCallable.setObject(1, new Integer(1106), Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.setObject(2, new Integer(3), Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.registerOutParameter(3, Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(3);
        ctrlCallable.setReturnValue(new Integer(4));
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + AddInvoice.SQL + "(?, ?, ?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        testAddInvoice(1106, 3);

    }

    public void testAddInvoicesUsingObjectArray() throws Exception {
        mockCallable.setObject(1, new Integer(1106), Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.setObject(2, new Integer(4), Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.registerOutParameter(3, Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(3);
        ctrlCallable.setReturnValue(new Integer(5));
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + AddInvoice.SQL + "(?, ?, ?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        testAddInvoiceUsingObjectArray(1106, 4);

    }

    public void testAddInvoicesWithinTransaction() throws Exception {
        mockCallable.setObject(1, new Integer(1106), Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.setObject(2, new Integer(3), Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.registerOutParameter(3, Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(3);
        ctrlCallable.setReturnValue(new Integer(4));
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + AddInvoice.SQL + "(?, ?, ?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();

        TransactionSynchronizationManager.bindResource(mockDataSource, new ConnectionHolder(mockConnection));

        try {
            testAddInvoice(1106, 3);
        } finally {
            TransactionSynchronizationManager.unbindResource(mockDataSource);
        }
    }

    /**
     * Confirm no connection was used to get metadata.
     * Does not use superclass replay mechanism.
     * @throws Exception
     */
    public void testStoredProcedureConfiguredViaJdbcTemplateWithCustomExceptionTranslator() throws Exception {
        mockCallable.setObject(1, new Integer(11), Types.INTEGER);
        ctrlCallable.setVoidCallable(1);
        mockCallable.registerOutParameter(2, Types.INTEGER);
        ctrlCallable.setVoidCallable(1);
        mockCallable.execute();
        ctrlCallable.setReturnValue(false, 1);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(2);
        ctrlCallable.setReturnValue(new Integer(5), 1);
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable(1);
        // Must call this here as we're not using setUp()/tearDown() mechanism
        ctrlCallable.replay();

        ctrlConnection = MockControl.createControl(Connection.class);
        mockConnection = (Connection) ctrlConnection.getMock();
        mockConnection.prepareCall("{call " + StoredProcedureConfiguredViaJdbcTemplate.SQL + "(?, ?)}");
        ctrlConnection.setReturnValue(mockCallable, 1);
        mockConnection.close();
        ctrlConnection.setVoidCallable(1);
        ctrlConnection.replay();

        MockControl dsControl = MockControl.createControl(DataSource.class);
        DataSource localDs = (DataSource) dsControl.getMock();
        localDs.getConnection();
        dsControl.setReturnValue(mockConnection, 1);
        dsControl.replay();

        class TestJdbcTemplate extends JdbcTemplate {
            int calls;

            public Map call(CallableStatementCreator csc, List declaredParameters) throws DataAccessException {
                calls++;
                return super.call(csc, declaredParameters);
            }

        }
        TestJdbcTemplate t = new TestJdbcTemplate();
        t.setDataSource(localDs);
        // Will fail without the following, because we're not able to get a connection from the
        // DataSource here if we need to to create an ExceptionTranslator
        t.setExceptionTranslator(new SQLStateSQLExceptionTranslator());
        StoredProcedureConfiguredViaJdbcTemplate sp = new StoredProcedureConfiguredViaJdbcTemplate(t);

        assertEquals(sp.execute(11), 5);
        assertEquals(1, t.calls);

        dsControl.verify();
        ctrlCallable.verify();
        ctrlConnection.verify();
    }

    /**
     * Confirm our JdbcTemplate is used
     * @throws Exception
     */
    public void testStoredProcedureConfiguredViaJdbcTemplate() throws Exception {
        mockCallable.setObject(1, new Integer(1106), Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.registerOutParameter(2, Types.INTEGER);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(2);
        ctrlCallable.setReturnValue(new Integer(4));
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + StoredProcedureConfiguredViaJdbcTemplate.SQL + "(?, ?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        JdbcTemplate t = new JdbcTemplate();
        t.setDataSource(mockDataSource);
        StoredProcedureConfiguredViaJdbcTemplate sp = new StoredProcedureConfiguredViaJdbcTemplate(t);

        assertEquals(sp.execute(1106), 4);
    }

    public void testNullArg() throws Exception {
        MockControl ctrlResultSet = MockControl.createControl(ResultSet.class);
        ResultSet mockResultSet = (ResultSet) ctrlResultSet.getMock();
        mockResultSet.next();
        ctrlResultSet.setReturnValue(true);

        mockCallable.setNull(1, Types.VARCHAR);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + NullArg.SQL + "(?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        ctrlResultSet.replay();

        NullArg na = new NullArg(mockDataSource);
        na.execute((String) null);
    }

    public void testUnnamedParameter() throws Exception {
        replay();
        try {
            UnnamedParameterStoredProcedure unp = new UnnamedParameterStoredProcedure(mockDataSource);
            fail("Shouldn't succeed in creating stored procedure with unnamed parameter");
        } catch (InvalidDataAccessApiUsageException idaauex) {
            // OK
        }
    }

    public void testMissingParameter() throws Exception {
        replay();

        try {
            MissingParameterStoredProcedure mp = new MissingParameterStoredProcedure(mockDataSource);
            mp.execute();
            fail("Shouldn't succeed in running stored procedure with missing required parameter");
        } catch (InvalidDataAccessApiUsageException idaauex) {
            // OK
        }
    }

    public void testStoredProcedureExceptionTranslator() throws Exception {
        SQLException sex = new SQLException("Syntax error or access violation exception", "42000");
        mockCallable.execute();
        ctrlCallable.setThrowable(sex);
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + StoredProcedureExceptionTranslator.SQL + "()}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();

        StoredProcedureExceptionTranslator sproc = new StoredProcedureExceptionTranslator(mockDataSource);
        try {
            sproc.execute();
            fail("Custom exception should be thrown");
        } catch (CustomDataException ex) {
            // OK
        }
    }

    public void testStoredProcedureWithResultSet() throws Exception {
        MockControl ctrlResultSet = MockControl.createControl(ResultSet.class);
        ResultSet mockResultSet = (ResultSet) ctrlResultSet.getMock();
        mockResultSet.next();
        ctrlResultSet.setReturnValue(true);
        mockResultSet.next();
        ctrlResultSet.setReturnValue(true);
        mockResultSet.next();
        ctrlResultSet.setReturnValue(false);
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockResultSet.close();
        ctrlResultSet.setVoidCallable();

        mockCallable.execute();
        ctrlCallable.setReturnValue(true);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getResultSet();
        ctrlCallable.setReturnValue(mockResultSet);
        mockCallable.getMoreResults();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + StoredProcedureWithResultSet.SQL + "()}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        ctrlResultSet.replay();

        StoredProcedureWithResultSet sproc = new StoredProcedureWithResultSet(mockDataSource);
        sproc.execute();

        ctrlResultSet.verify();
        assertEquals(2, sproc.getCount());
    }

    public void testStoredProcedureWithResultSetMapped() throws Exception {
        MockControl ctrlResultSet = MockControl.createControl(ResultSet.class);
        ResultSet mockResultSet = (ResultSet) ctrlResultSet.getMock();
        mockResultSet.next();
        ctrlResultSet.setReturnValue(true);
        mockResultSet.getString(2);
        ctrlResultSet.setReturnValue("Foo");
        mockResultSet.next();
        ctrlResultSet.setReturnValue(true);
        mockResultSet.getString(2);
        ctrlResultSet.setReturnValue("Bar");
        mockResultSet.next();
        ctrlResultSet.setReturnValue(false);
        mockResultSet.close();
        ctrlResultSet.setVoidCallable();

        mockCallable.execute();
        ctrlCallable.setReturnValue(true);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getResultSet();
        ctrlCallable.setReturnValue(mockResultSet);
        mockCallable.getMoreResults();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + StoredProcedureWithResultSetMapped.SQL + "()}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        ctrlResultSet.replay();

        StoredProcedureWithResultSetMapped sproc = new StoredProcedureWithResultSetMapped(mockDataSource);
        Map res = sproc.execute();

        ctrlResultSet.verify();

        List rs = (List) res.get("rs");
        assertEquals(2, rs.size());
        assertEquals("Foo", rs.get(0));
        assertEquals("Bar", rs.get(1));

    }

    public void testStoredProcedureWithUndeclaredResults() throws Exception {
        MockControl ctrlResultSet1 = MockControl.createControl(ResultSet.class);
        ResultSet mockResultSet1 = (ResultSet) ctrlResultSet1.getMock();
        mockResultSet1.next();
        ctrlResultSet1.setReturnValue(true);
        mockResultSet1.getString(2);
        ctrlResultSet1.setReturnValue("Foo");
        mockResultSet1.next();
        ctrlResultSet1.setReturnValue(true);
        mockResultSet1.getString(2);
        ctrlResultSet1.setReturnValue("Bar");
        mockResultSet1.next();
        ctrlResultSet1.setReturnValue(false);
        mockResultSet1.close();
        ctrlResultSet1.setVoidCallable();

        MockControl ctrlMetaData = MockControl.createControl(ResultSetMetaData.class);
        ResultSetMetaData mockMetaData = (ResultSetMetaData) ctrlMetaData.getMock();
        mockMetaData.getColumnCount();
        ctrlMetaData.setReturnValue(2);
        mockMetaData.getColumnLabel(1);
        ctrlMetaData.setReturnValue("spam");
        mockMetaData.getColumnLabel(2);
        ctrlMetaData.setReturnValue("eggs");

        MockControl ctrlResultSet2 = MockControl.createControl(ResultSet.class);
        ResultSet mockResultSet2 = (ResultSet) ctrlResultSet2.getMock();
        mockResultSet2.getMetaData();
        ctrlResultSet2.setReturnValue(mockMetaData);
        mockResultSet2.next();
        ctrlResultSet2.setReturnValue(true);
        mockResultSet2.getObject(1);
        ctrlResultSet2.setReturnValue("Spam");
        mockResultSet2.getObject(2);
        ctrlResultSet2.setReturnValue("Eggs");
        mockResultSet2.next();
        ctrlResultSet2.setReturnValue(false);
        mockResultSet2.close();
        ctrlResultSet2.setVoidCallable();

        mockCallable.execute();
        ctrlCallable.setReturnValue(true);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getResultSet();
        ctrlCallable.setReturnValue(mockResultSet1);
        mockCallable.getMoreResults();
        ctrlCallable.setReturnValue(true);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getResultSet();
        ctrlCallable.setReturnValue(mockResultSet2);
        mockCallable.getMoreResults();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(0);
        mockCallable.getMoreResults();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + StoredProcedureWithResultSetMapped.SQL + "()}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        ctrlResultSet1.replay();
        ctrlMetaData.replay();
        ctrlResultSet2.replay();

        StoredProcedureWithResultSetMapped sproc = new StoredProcedureWithResultSetMapped(mockDataSource);
        Map res = sproc.execute();

        ctrlResultSet1.verify();
        ctrlResultSet2.verify();

        assertEquals("incorrect number of returns", 3, res.size());

        List rs1 = (List) res.get("rs");
        assertEquals(2, rs1.size());
        assertEquals("Foo", rs1.get(0));
        assertEquals("Bar", rs1.get(1));

        List rs2 = (List) res.get("#result-set-2");
        assertEquals(1, rs2.size());
        Object o2 = rs2.get(0);
        assertTrue("wron type returned for result set 2", o2 instanceof Map);
        Map m2 = (Map) o2;
        assertEquals("Spam", m2.get("spam"));
        assertEquals("Eggs", m2.get("eggs"));

        Number n = (Number) res.get("#update-count-1");
        assertEquals("wrong update count", 0, n.intValue());
    }

    public void testStoredProcedureSkippingResultsProcessing() throws Exception {
        mockCallable.execute();
        ctrlCallable.setReturnValue(true);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + StoredProcedureWithResultSetMapped.SQL + "()}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();

        JdbcTemplate jdbcTemplate = new JdbcTemplate(mockDataSource);
        jdbcTemplate.setSkipResultsProcessing(true);
        StoredProcedureWithResultSetMapped sproc = new StoredProcedureWithResultSetMapped(jdbcTemplate);
        Map res = sproc.execute();

        assertEquals("incorrect number of returns", 0, res.size());
    }

    public void testStoredProcedureSkippingUndeclaredResults() throws Exception {
        MockControl ctrlResultSet1 = MockControl.createControl(ResultSet.class);
        ResultSet mockResultSet1 = (ResultSet) ctrlResultSet1.getMock();
        mockResultSet1.next();
        ctrlResultSet1.setReturnValue(true);
        mockResultSet1.getString(2);
        ctrlResultSet1.setReturnValue("Foo");
        mockResultSet1.next();
        ctrlResultSet1.setReturnValue(true);
        mockResultSet1.getString(2);
        ctrlResultSet1.setReturnValue("Bar");
        mockResultSet1.next();
        ctrlResultSet1.setReturnValue(false);
        mockResultSet1.close();
        ctrlResultSet1.setVoidCallable();

        mockCallable.execute();
        ctrlCallable.setReturnValue(true);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getResultSet();
        ctrlCallable.setReturnValue(mockResultSet1);
        mockCallable.getMoreResults();
        ctrlCallable.setReturnValue(true);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getMoreResults();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + StoredProcedureWithResultSetMapped.SQL + "()}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        ctrlResultSet1.replay();

        JdbcTemplate jdbcTemplate = new JdbcTemplate(mockDataSource);
        jdbcTemplate.setSkipUndeclaredResults(true);
        StoredProcedureWithResultSetMapped sproc = new StoredProcedureWithResultSetMapped(jdbcTemplate);
        Map res = sproc.execute();

        ctrlResultSet1.verify();

        assertEquals("incorrect number of returns", 1, res.size());

        List rs1 = (List) res.get("rs");
        assertEquals(2, rs1.size());
        assertEquals("Foo", rs1.get(0));
        assertEquals("Bar", rs1.get(1));
    }

    public void testParameterMapper() throws Exception {
        mockCallable.setString(1, "EasyMock for interface java.sql.Connection");
        ctrlCallable.setVoidCallable();
        mockCallable.registerOutParameter(2, Types.VARCHAR);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(2);
        ctrlCallable.setReturnValue("OK");
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + ParameterMapperStoredProcedure.SQL + "(?, ?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        ParameterMapperStoredProcedure pmsp = new ParameterMapperStoredProcedure(mockDataSource);
        Map out = pmsp.executeTest();
        assertEquals("OK", out.get("out"));
    }

    public void testSqlTypeValue() throws Exception {
        int[] testVal = new int[] { 1, 2 };
        mockCallable.getConnection();
        ctrlCallable.setDefaultReturnValue(mockConnection);
        mockCallable.setObject(1, testVal, Types.ARRAY);
        ctrlCallable.setVoidCallable();
        mockCallable.registerOutParameter(2, Types.VARCHAR);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(2);
        ctrlCallable.setReturnValue("OK");
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + SqlTypeValueStoredProcedure.SQL + "(?, ?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        SqlTypeValueStoredProcedure stvsp = new SqlTypeValueStoredProcedure(mockDataSource);
        Map out = stvsp.executeTest(testVal);
        assertEquals("OK", out.get("out"));
    }

    public void testNumericWithScale() throws Exception {
        mockCallable.getConnection();
        ctrlCallable.setDefaultReturnValue(mockConnection);
        mockCallable.registerOutParameter(1, Types.DECIMAL, 4);
        ctrlCallable.setVoidCallable();
        mockCallable.execute();
        ctrlCallable.setReturnValue(false);
        mockCallable.getUpdateCount();
        ctrlCallable.setReturnValue(-1);
        mockCallable.getObject(1);
        ctrlCallable.setReturnValue(new BigDecimal("12345.6789"));
        if (debugEnabled) {
            mockCallable.getWarnings();
            ctrlCallable.setReturnValue(null);
        }
        mockCallable.close();
        ctrlCallable.setVoidCallable();

        mockConnection.prepareCall("{call " + NumericWithScaleStoredProcedure.SQL + "(?)}");
        ctrlConnection.setReturnValue(mockCallable);

        replay();
        NumericWithScaleStoredProcedure nwssp = new NumericWithScaleStoredProcedure(mockDataSource);
        Map out = nwssp.executeTest();
        assertEquals(new BigDecimal("12345.6789"), out.get("out"));
    }

    private static class StoredProcedureConfiguredViaJdbcTemplate extends StoredProcedure {

        public static final String SQL = "configured_via_jt";

        public StoredProcedureConfiguredViaJdbcTemplate(JdbcTemplate t) {
            setJdbcTemplate(t);
            setSql(SQL);
            declareParameter(new SqlParameter("intIn", Types.INTEGER));
            declareParameter(new SqlOutParameter("intOut", Types.INTEGER));
            compile();
        }

        public int execute(int intIn) {
            Map in = new HashMap();
            in.put("intIn", new Integer(intIn));
            Map out = execute(in);
            Number intOut = (Number) out.get("intOut");
            return intOut.intValue();
        }
    }

    private static class AddInvoice extends StoredProcedure {

        public static final String SQL = "add_invoice";

        public AddInvoice(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlParameter("amount", Types.INTEGER));
            declareParameter(new SqlParameter("custid", Types.INTEGER));
            declareParameter(new SqlOutParameter("newid", Types.INTEGER));
            compile();
        }

        public int execute(int amount, int custid) {
            Map in = new HashMap();
            in.put("amount", new Integer(amount));
            in.put("custid", new Integer(custid));
            Map out = execute(in);
            Number id = (Number) out.get("newid");
            return id.intValue();
        }
    }

    private static class AddInvoiceUsingObjectArray extends StoredProcedure {

        public static final String SQL = "add_invoice";

        public AddInvoiceUsingObjectArray(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlParameter("amount", Types.INTEGER));
            declareParameter(new SqlParameter("custid", Types.INTEGER));
            declareParameter(new SqlOutParameter("newid", Types.INTEGER));
            compile();
        }

        public int execute(int amount, int custid) {
            Map out = execute(new Object[] { amount, custid });
            System.out.println("####### " + out);
            Number id = (Number) out.get("newid");
            return id.intValue();
        }
    }

    private static class NullArg extends StoredProcedure {

        public static final String SQL = "takes_null";

        public NullArg(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlParameter("ptest", Types.VARCHAR));
            compile();
        }

        public void execute(String s) {
            Map in = new HashMap();
            in.put("ptest", s);
            Map out = execute(in);
        }
    }

    private static class NoSuchStoredProcedure extends StoredProcedure {

        public static final String SQL = "no_sproc_with_this_name";

        public NoSuchStoredProcedure(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            compile();
        }

        public void execute() {
            execute(new HashMap());
        }
    }

    private static class UnnamedParameterStoredProcedure extends StoredProcedure {

        public UnnamedParameterStoredProcedure(DataSource ds) {
            super(ds, "unnamed_parameter_sp");
            declareParameter(new SqlParameter(Types.INTEGER));
            compile();
        }

        public void execute(int id) {
            Map in = new HashMap();
            in.put("id", new Integer(id));
            Map out = execute(in);
        }
    }

    private static class MissingParameterStoredProcedure extends StoredProcedure {

        public MissingParameterStoredProcedure(DataSource ds) {
            setDataSource(ds);
            setSql("takes_string");
            declareParameter(new SqlParameter("mystring", Types.VARCHAR));
            compile();
        }

        public void execute() {
            execute(new HashMap());
        }
    }

    private static class StoredProcedureWithResultSet extends StoredProcedure {

        public static final String SQL = "sproc_with_result_set";

        private final SimpleRowCountCallbackHandler handler = new SimpleRowCountCallbackHandler();

        public StoredProcedureWithResultSet(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlReturnResultSet("rs", this.handler));
            compile();
        }

        public void execute() {
            execute(new HashMap());
        }

        public int getCount() {
            return this.handler.getCount();
        }
    }

    private static class StoredProcedureWithResultSetMapped extends StoredProcedure {

        public static final String SQL = "sproc_with_result_set";

        public StoredProcedureWithResultSetMapped(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlReturnResultSet("rs", new RowMapperImpl()));
            compile();
        }

        public StoredProcedureWithResultSetMapped(JdbcTemplate jt) {
            setJdbcTemplate(jt);
            setSql(SQL);
            declareParameter(new SqlReturnResultSet("rs", new RowMapperImpl()));
            compile();
        }

        public Map execute() {
            Map out = execute(new HashMap());
            return out;
        }

        private static class RowMapperImpl implements RowMapper {
            public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
                return rs.getString(2);
            }
        }
    }

    private static class ParameterMapperStoredProcedure extends StoredProcedure {

        public static final String SQL = "parameter_mapper_sp";

        public ParameterMapperStoredProcedure(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlParameter("in", Types.VARCHAR));
            declareParameter(new SqlOutParameter("out", Types.VARCHAR));
            compile();
        }

        public Map executeTest() {
            Map out = null;
            out = execute(new TestParameterMapper());
            return out;
        }

        private static class TestParameterMapper implements ParameterMapper {

            private TestParameterMapper() {
            }

            public Map createMap(Connection conn) throws SQLException {
                Map inParms = new HashMap();
                String testValue = conn.toString();
                inParms.put("in", testValue);
                return inParms;
            }
        }
    }

    private static class SqlTypeValueStoredProcedure extends StoredProcedure {

        public static final String SQL = "sql_type_value_sp";

        public SqlTypeValueStoredProcedure(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlParameter("in", Types.ARRAY, "NUMBERS"));
            declareParameter(new SqlOutParameter("out", Types.VARCHAR));
            compile();
        }

        public Map executeTest(final int[] inValue) {
            Map in = new HashMap(1);
            in.put("in", new AbstractSqlTypeValue() {
                public Object createTypeValue(Connection con, int type, String typeName) {
                    //assertEquals(Connection.class, con.getClass());
                    //assertEquals(Types.ARRAY, type);
                    //assertEquals("NUMBER", typeName);
                    return inValue;
                }
            });
            Map out = null;
            out = execute(in);
            return out;
        }
    }

    private static class NumericWithScaleStoredProcedure extends StoredProcedure {

        public static final String SQL = "numeric_with_scale_sp";

        public NumericWithScaleStoredProcedure(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            declareParameter(new SqlOutParameter("out", Types.DECIMAL, 4));
            compile();
        }

        public Map executeTest() {
            Map in = new HashMap(1);
            Map out = null;
            out = execute(in);
            return out;
        }
    }

    private static class StoredProcedureExceptionTranslator extends StoredProcedure {

        public static final String SQL = "no_sproc_with_this_name";

        public StoredProcedureExceptionTranslator(DataSource ds) {
            setDataSource(ds);
            setSql(SQL);
            getJdbcTemplate().setExceptionTranslator(new SQLExceptionTranslator() {
                public DataAccessException translate(String task, String sql, SQLException sqlex) {
                    return new CustomDataException(sql, sqlex);
                }

            });
            compile();
        }

        public void execute() {
            execute(new HashMap());
        }
    }

    private static class CustomDataException extends DataAccessException {

        public CustomDataException(String s) {
            super(s);
        }

        public CustomDataException(String s, Throwable ex) {
            super(s, ex);
        }
    }

}