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<Integer> or = new OracleReturning<Integer>(new ResultSetMapper<Integer>() {
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<Integer> 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 }