View Javadoc

1   /*
2    * Copyright 2004-2007 Brian McCallister
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.skife.jdbi.v2.unstable.oracle;
18  
19  import org.skife.jdbi.v2.StatementContext;
20  import org.skife.jdbi.v2.exceptions.ResultSetException;
21  import org.skife.jdbi.v2.tweak.ResultSetMapper;
22  import org.skife.jdbi.v2.tweak.StatementCustomizer;
23  
24  import java.lang.reflect.Method;
25  import java.sql.PreparedStatement;
26  import java.sql.ResultSet;
27  import java.sql.SQLException;
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  /**
32   * Provides access to Oracle's "DML Returning" features introduced in 10.2. To use,
33   * add the statement customizer to a DML statement and bind (positionally) the return
34   * params. Sadly, I think they (and the mapper) have to be positional. Usage is
35   * like this:
36   * <pre><code>
37   * public void testFoo() throws Exception
38   * {
39   *     Handle h = dbi.open();
40   * <p/>
41   *     OracleReturning&lt;Integer&gt; or = new OracleReturning&lt;Integer&gt;(new ResultSetMapper&lt;Integer&gt;() {
42   *         public Integer map(int index, ResultSet r) throws SQLException
43   *         {
44   *             return r.getInt(1);
45   *         }
46   *     });
47   * <p/>
48   *     or.registerReturnParam(1, OracleTypes.INTEGER);
49   * <p/>
50   *     h.createStatement("insert into something (id, name) values (17, 'Brian') returning id into ?")
51   *             .addStatementCustomizer(or)
52   *             .execute();
53   *     List&lt;Integer&gt; ids = or.getReturnedResults();
54   * <p/>
55   *     assertEquals(1, ids.size());
56   *     assertEquals(Integer.valueOf(17), ids.getAttribute(0));
57   *     h.close();
58   * }
59   * </code></pre>
60   * Though you can bind multiple params, and whatnot
61   */
62  @Deprecated
63  public class OracleReturning<ResultType> implements StatementCustomizer
64  {
65      private ResultSetMapper<ResultType> mapper;
66      private final List<int[]> binds = new ArrayList<int[]>();
67      private StatementContext context;
68      private List<ResultType> results;
69  	private Class<?> oraclePS ;
70  	private Method registerReturnParameter ;
71  	private Method getReturnResultSet ;
72  	private Object stmt ;
73  
74      /**
75       * Provide a mapper which knows how to do positional access, sadly the
76       * {@link org.skife.jdbi.v2.BeanMapper} uses the names in the result set
77       *
78       * @param mapper Must use only positional access to the result set
79       */
80      public OracleReturning(ResultSetMapper<ResultType> mapper)
81      {
82          this.mapper = mapper;
83  	    try {
84  		    this.oraclePS = Class.forName("oracle.jdbc.OraclePreparedStatement");
85  		    this.registerReturnParameter = oraclePS.getMethod("registerReturnParameter", new Class[]{int.class, int.class});
86  		    this.getReturnResultSet = oraclePS.getMethod("getReturnResultSet");
87  	    }
88  	    catch (Exception e) {
89  		    throw new RuntimeException(e);
90  	    }
91      }
92  
93      /**
94       * @see StatementCustomizer#beforeExecution(java.sql.PreparedStatement,org.skife.jdbi.v2.StatementContext)
95       */
96      public void beforeExecution(PreparedStatement stmt, StatementContext ctx) throws SQLException
97      {
98          this.context = ctx;
99  
100         if (!oraclePS.isAssignableFrom(stmt.getClass())) {
101             try {
102                 final Method get_delegate = stmt.getClass().getMethod("getDelegate");
103                 final Object candidate = get_delegate.invoke(stmt);
104                 if (!oraclePS.isAssignableFrom(candidate.getClass())) {
105                     throw new Exception("Obtained delegate, but it still wasn't an OraclePreparedStatement");
106                 }
107 	            else {
108 	                this.stmt = candidate ;
109                 }
110             }
111             catch (Exception e) {
112                 throw new IllegalStateException("Statement is not an OraclePreparedStatement, nor" +
113                                                 "one which we know how to find it from", e);
114             }
115         }
116 	    else {
117 	        this.stmt = stmt ;
118         }
119         for (int[] bind : binds) {
120 	        try {
121 		        registerReturnParameter.invoke(this.stmt, bind[0], bind[1]);
122 	        }
123 	        catch (Exception e) {
124 		        throw new RuntimeException(e);
125 	        }
126         }
127     }
128 
129     public void afterExecution(PreparedStatement stmt, StatementContext ctx) throws SQLException
130     {
131         ResultSet rs;
132 		try {
133 			rs = (ResultSet) this.getReturnResultSet.invoke(this.stmt);
134 		}
135 		catch (Exception e) {
136 			throw new ResultSetException("Unable to retrieve return result set", e, ctx);
137 		}
138         this.results = new ArrayList<ResultType>();
139         try {
140             int i = 0;
141             while (rs.next()) {
142                 results.add(mapper.map(i++, rs, context));
143             }
144         }
145         catch (SQLException e) {
146             throw new ResultSetException("Unable to retrieve results from returned result set", e, ctx);
147         }
148     }
149 
150 
151     /**
152      * Callable after the statement has been executed to obtain the mapped results.
153      *
154      * @return the mapped results
155      */
156     public List<ResultType> getReturnedResults()
157     {
158         return results;
159     }
160 
161     /**
162      * Used to specify the types (required) of the out parameters. You must
163      * register each of them.
164      *
165      * @param position   1 based position of the out parameter
166      * @param oracleType one of the values from oracle.jdbc.driver..OracleTypes
167      *
168      * @return The same instance, in case you want to chain things
169      */
170     public OracleReturning<ResultType> registerReturnParam(int position, int oracleType)
171     {
172         binds.add(new int[]{position, oracleType});
173         return this;
174     }
175 }