/*
* 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
import java.sql.Timestamp;
import java.util.function.Consumer;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DefaultBinding;
import org.jooq.impl.SQLDataType;
/**
* An SPI (Service Provider Interface) that exposes all low-level interactions
* with JDBC bind variables.
* <p>
* This SPI is used by jOOQ users to implement support for custom data types
* that would otherwise not be supported by jOOQ and/or JDBC. All of jOOQ's
* internal support for bind variable types is implemented in
* {@link DefaultBinding}.
*
* @param <T> The database type - i.e. any type available from
* {@link SQLDataType}
* @param <U> The user type
* @author Lukas Eder
*/
public interface Binding<T, U> extends Serializable {
/**
* A converter that can convert between the database type and the custom
* type.
*/
Converter<T, U> converter();
/**
* Generate SQL code for the bind variable.
* <p>
* Implementations should generate SQL code onto
* {@link BindingSQLContext#render()}, given the context's bind variable
* located at {@link BindingSQLContext#value()}. Examples of such SQL code
* are:
* <ul>
* <li><code>"?"</code>: Default implementations can simply generate a
* question mark.<br>
* <br>
* </li>
* <li><code>"123"</code>: Implementations may choose to inline bind
* variables to influence execution plan generation.<br>
* <br>
* {@link RenderContext#paramType()} contains information whether inlined
* bind variables are expected in the current context.<br>
* <br>
* </li>
* <li><code>"CAST(? AS DATE)"</code>: Cast a database to a more specific
* type. This can be useful in databases like Oracle, which map both
* <code>DATE</code> and <code>TIMESTAMP</code> SQL types to
* {@link Timestamp}.<br>
* <br>
* {@link RenderContext#castMode()} may contain some hints about whether
* casting is suggested in the current context.<br>
* <br>
* </li>
* <li><code>"?::json"</code>: Vendor-specific bind variables can be
* supported, e.g. {@link SQLDialect#POSTGRES}'s JSON data type.</li>
* </ul>
* <p>
* Implementations must provide consistent behaviour between
* {@link #sql(BindingSQLContext)} and
* {@link #set(BindingSetStatementContext)}, i.e. when bind variables are
* inlined, then they must not be bound to the {@link PreparedStatement} in
* {@link #set(BindingSetStatementContext)}
*
* @param ctx The context object containing all argument objects.
* @throws SQLException Implementations are allowed to pass on all
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void sql(BindingSQLContext<U> ctx) throws SQLException;
/**
* Register a {@link CallableStatement}'s <code>OUT</code> parameter.
*
* @param ctx The context object containing all argument objects.
* @throws SQLException Implementations are allowed to pass on all
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void register(BindingRegisterContext<U> ctx) throws SQLException;
/**
* Set a {@link PreparedStatement}'s <code>IN</code> parameter.
*
* @param ctx The context object containing all argument objects.
* @throws SQLException Implementations are allowed to pass on all
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void set(BindingSetStatementContext<U> ctx) throws SQLException;
/**
* Set a {@link SQLOutput}'s <code>IN</code> parameter.
*
* @param ctx The context object containing all argument objects.
* @throws SQLException Implementations are allowed to pass on all
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void set(BindingSetSQLOutputContext<U> ctx) throws SQLException;
/**
* Get a {@link ResultSet}'s <code>OUT</code> value.
* <p>
* Implementations are expected to produce a value by calling
* {@link BindingGetResultSetContext#value(Object)}, passing the resulting
* value to the method.
*
* @param ctx The context object containing all argument objects.
* @throws SQLException Implementations are allowed to pass on all
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void get(BindingGetResultSetContext<U> ctx) throws SQLException;
/**
* Get a {@link CallableStatement}'s <code>OUT</code> value.
* <p>
* Implementations are expected to produce a value by calling
* {@link BindingGetStatementContext#value(Object)}, passing the resulting
* value to the method.
*
* @param ctx The context object containing all argument objects.
* @throws SQLException Implementations are allowed to pass on all
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void get(BindingGetStatementContext<U> ctx) throws SQLException;
/**
* Get a {@link SQLInput}'s <code>OUT</code> value.
* <p>
* Implementations are expected to produce a value by calling
* {@link BindingGetSQLInputContext#value(Object)}, passing the resulting
* value to the method.
*
* @param ctx The context object containing all argument objects.
* @throws SQLException Implementations are allowed to pass on all
* {@link SQLException}s to the caller to be wrapped in
* {@link DataAccessException}s.
*/
void get(BindingGetSQLInputContext<U> ctx) throws SQLException;
/**
* Construct a binding from functions.
*/
static <T, U> Binding<T, U> of(
Converter<T, U> converter,
Consumer<? super BindingSQLContext<U>> sqlContext,
Consumer<? super BindingGetResultSetContext<U>> getResultSetContext,
Consumer<? super BindingSetStatementContext<U>> setStatementContext
) {
return of(
converter,
sqlContext,
getResultSetContext,
setStatementContext,
ctx -> { throw new UnsupportedOperationException(); },
ctx -> { throw new UnsupportedOperationException(); },
ctx -> { throw new UnsupportedOperationException(); },
ctx -> { throw new UnsupportedOperationException(); }
);
}
/**
* Construct a binding from functions.
*/
static <T, U> Binding<T, U> of(
Converter<T, U> converter,
Consumer<? super BindingSQLContext<U>> sqlContext,
Consumer<? super BindingGetResultSetContext<U>> getResultSetContext,
Consumer<? super BindingSetStatementContext<U>> setStatementContext,
Consumer<? super BindingRegisterContext<U>> registerContext,
Consumer<? super BindingGetStatementContext<U>> getStatementContext
) {
return of(
converter,
sqlContext,
getResultSetContext,
setStatementContext,
registerContext,
getStatementContext,
ctx -> { throw new UnsupportedOperationException(); },
ctx -> { throw new UnsupportedOperationException(); }
);
}
/**
* Construct a binding from functions.
*/
static <T, U> Binding<T, U> of(
Converter<T, U> converter,
Consumer<? super BindingSQLContext<U>> sqlContext,
Consumer<? super BindingGetResultSetContext<U>> getResultSetContext,
Consumer<? super BindingSetStatementContext<U>> setStatementContext,
Consumer<? super BindingRegisterContext<U>> registerContext,
Consumer<? super BindingGetStatementContext<U>> getStatementContext,
Consumer<? super BindingGetSQLInputContext<U>> getSqlInputContext,
Consumer<? super BindingSetSQLOutputContext<U>> setSqlOutputContext
) {
return new Binding<T, U>() {
/**
* Generated UID
*/
private static final long serialVersionUID = -7780201518613974266L;
@Override
public final Converter<T, U> converter() {
return converter;
}
@Override
public final void sql(BindingSQLContext<U> ctx) throws SQLException {
sqlContext.accept(ctx);
}
@Override
public final void register(BindingRegisterContext<U> ctx) throws SQLException {
registerContext.accept(ctx);
}
@Override
public final void set(BindingSetStatementContext<U> ctx) throws SQLException {
setStatementContext.accept(ctx);
}
@Override
public final void set(BindingSetSQLOutputContext<U> ctx) throws SQLException {
setSqlOutputContext.accept(ctx);
}
@Override
public final void get(BindingGetResultSetContext<U> ctx) throws SQLException {
getResultSetContext.accept(ctx);
}
@Override
public final void get(BindingGetStatementContext<U> ctx) throws SQLException {
getStatementContext.accept(ctx);
}
@Override
public final void get(BindingGetSQLInputContext<U> ctx) throws SQLException {
getSqlInputContext.accept(ctx);
}
};
}
}