001/*
002 * Copyright 2015-2022 Transmogrify LLC, 2022-2026 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.pyranid;
018
019import org.jspecify.annotations.NonNull;
020
021import java.sql.PreparedStatement;
022import java.sql.SQLException;
023import java.util.List;
024
025import static java.util.Objects.requireNonNull;
026
027/**
028 * Contract for binding parameters to SQL prepared statements.
029 * <p>
030 * A production-ready concrete implementation is available via the following static methods:
031 * <ul>
032 *   <li>{@link #withDefaultConfiguration()}</li>
033 *   <li>{@link #withCustomParameterBinders(List)}</li>
034 * </ul>
035 * How to acquire an instance:
036 * <pre>{@code  // With out-of-the-box defaults
037 * PreparedStatementBinder default = PreparedStatementBinder.withDefaultConfiguration();
038 *
039 * // Customized
040 * PreparedStatementBinder custom = PreparedStatementBinder.withCustomParameterBinders(List.of(...));}</pre>
041 * Or, implement your own: <pre>{@code  PreparedStatementBinder myImpl = new PreparedStatementBinder() {
042 *   @Override
043 *   <T> void bindParameter(
044 *     @NonNull StatementContext<T> statementContext,
045 *     @NonNull PreparedStatement preparedStatement,
046 *     @NonNull Integer parameterIndex,
047 *     @NonNull Object parameter
048 *   ) throws SQLException {
049 *     // Bind the parameter at the specified index to the PreparedStatement
050 *   }
051 * };}</pre>
052 *
053 * @author <a href="https://www.revetkn.com">Mark Allen</a>
054 * @since 1.0.0
055 */
056@FunctionalInterface
057public interface PreparedStatementBinder {
058        /**
059         * Binds a single parameter to a SQL prepared statement.
060         * <p>
061         * This function is only invoked when {@code parameter} is non-null.
062         *
063         * @param preparedStatement the prepared statement to bind to
064         * @param statementContext  current SQL context
065         * @param parameterIndex    the index of the parameter we are binding
066         * @param parameter         the parameter we are binding to the {@link PreparedStatement}, if any
067         * @throws SQLException if an error occurs during binding
068         */
069        <T> void bindParameter(@NonNull StatementContext<T> statementContext,
070                                                                                                 @NonNull PreparedStatement preparedStatement,
071                                                                                                 @NonNull Integer parameterIndex,
072                                                                                                 @NonNull Object parameter) throws SQLException;
073
074        /**
075         * Acquires a concrete implementation of this interface with out-of-the-box defaults.
076         * <p>
077         * The returned instance is thread-safe.
078         *
079         * @return a concrete implementation of this interface with out-of-the-box defaults
080         */
081        @NonNull
082        static PreparedStatementBinder withDefaultConfiguration() {
083                return new DefaultPreparedStatementBinder();
084        }
085
086        /**
087         * Acquires a concrete implementation of this interface, specifying "surgical" per-type custom parameter binders.
088         * <p>
089         * The returned instance is thread-safe.
090         *
091         * @param customParameterBinders the "surgical" per-type custom parameter binders
092         * @return a concrete implementation of this interface
093         */
094        @NonNull
095        static PreparedStatementBinder withCustomParameterBinders(@NonNull List<CustomParameterBinder> customParameterBinders) {
096                requireNonNull(customParameterBinders);
097                return new DefaultPreparedStatementBinder(customParameterBinders);
098        }
099}