/* * 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.jdbi.v3.oracle12; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; import org.jdbi.v3.core.result.ResultBearing; import org.jdbi.v3.core.result.ResultProducer; import org.jdbi.v3.core.result.ResultSetException; import org.jdbi.v3.core.statement.StatementContext; import org.jdbi.v3.core.statement.StatementCustomizer; import oracle.jdbc.OraclePreparedStatement; /** * Returns a {@link ResultBearing} from Oracle's "DML Returning" features introduced in 10.2. To use, * add a {@link #returnParameters()} customizer to the statement and register with one or more return parameters. Then * execute the statement with {@link #returningDml()} result producer: * <p> * <pre> * List<Integer> ids = handle.createUpdate("insert into something (id, name) values (17, 'Brian') returning id into ?") * .addCustomizer(OracleReturning.returnParameters().register(1, OracleTypes.INTEGER)) * .execute(OracleReturning.returningDml()) * .mapTo(int.class) * .list(); * * assertThat(ids).containsExactly(17); * </pre> * <p> * This class still is beta, and may be changed incompatibly or removed at any time. */ public class OracleReturning { public static ReturnParameters returnParameters() { return new ReturnParameters(); } public static class ReturnParameters implements StatementCustomizer { private final List<int[]> binds = new ArrayList<>(); ReturnParameters() { } @Override public void beforeExecution(PreparedStatement stmt, StatementContext ctx) throws SQLException { if (!stmt.isWrapperFor(OraclePreparedStatement.class)) { throw new IllegalStateException("Statement is not an instance of, nor a wrapper of, OraclePreparedStatement"); } OraclePreparedStatement statement = stmt.unwrap(OraclePreparedStatement.class); for (int[] bind : binds) { try { statement.registerReturnParameter(bind[0], bind[1]); } catch (Exception e) { throw new RuntimeException(e); } } } /** * Registers a return parameter on the Oracle prepared statement. * * @param index 0-based index of the return parameter * @param oracleType one of the values from {@link oracle.jdbc.OracleTypes} * @return The same instance, for method chaning */ public ReturnParameters register(int index, int oracleType) { binds.add(new int[]{index+1, oracleType}); return this; } } /** * Result producer that returns a {@link ResultBearing} over the statement "DML returning" parameters. Used in * conjunction with {@link #returnParameters()} to register return parameters. * * @return ResultBearing of returned columns. * @see OraclePreparedStatement#getReturnResultSet() */ public static ResultProducer<ResultBearing> returningDml() { return (supplier, ctx) -> ResultBearing.of(getReturnResultSet(supplier, ctx), ctx); } private static Supplier<ResultSet> getReturnResultSet(Supplier<PreparedStatement> supplier, StatementContext ctx) { return () -> { PreparedStatement stmt = supplier.get(); try { if (!stmt.isWrapperFor(OraclePreparedStatement.class)) { throw new IllegalStateException("Statement is not an instance of, nor a wrapper of, OraclePreparedStatement"); } OraclePreparedStatement statement = stmt.unwrap(OraclePreparedStatement.class); ResultSet rs = statement.getReturnResultSet(); if (rs != null) { ctx.addCleanable(rs::close); } return rs; } catch (SQLException e) { throw new ResultSetException("Unable to retrieve return result set", e, ctx); } }; } }