001/*
002 * Copyright 2015-2018 Transmogrify 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 static java.util.Objects.requireNonNull;
020
021import java.nio.ByteBuffer;
022import java.sql.PreparedStatement;
023import java.sql.Timestamp;
024import java.time.Instant;
025import java.util.Date;
026import java.util.List;
027import java.util.Locale;
028import java.util.UUID;
029
030/**
031 * Basic implementation of {@link PreparedStatementBinder}.
032 * 
033 * @author <a href="http://revetkn.com">Mark Allen</a>
034 * @since 1.0.0
035 */
036public class DefaultPreparedStatementBinder implements PreparedStatementBinder {
037  private final DatabaseType databaseType;
038
039  /**
040   * Creates a {@code PreparedStatementBinder} for the given {@code databaseType}.
041   * 
042   * @param databaseType
043   *          the type of database we're working with
044   */
045  public DefaultPreparedStatementBinder(DatabaseType databaseType) {
046    this.databaseType = requireNonNull(databaseType);
047  }
048
049  @Override
050  public void bind(PreparedStatement preparedStatement, List<Object> parameters) {
051    requireNonNull(preparedStatement);
052    requireNonNull(parameters);
053
054    try {
055      for (int i = 0; i < parameters.size(); ++i)
056        preparedStatement.setObject(i + 1, normalizeParameter(parameters.get(i)));
057    } catch (Exception e) {
058      throw new DatabaseException(e);
059    }
060  }
061
062  /**
063   * Massages a parameter into a JDBC-friendly format if needed.
064   * <p>
065   * For example, we need to do special work to prepare a {@link UUID} for Oracle.
066   * 
067   * @param parameter
068   *          the parameter to (possibly) massage
069   * @return the result of the massaging process
070   */
071  protected Object normalizeParameter(Object parameter) {
072    if (parameter == null)
073      return null;
074
075    if (parameter instanceof Date)
076      return new Timestamp(((Date) parameter).getTime());
077    if (parameter instanceof Instant)
078      return new Timestamp(((Instant) parameter).toEpochMilli());
079    if (parameter instanceof Locale)
080      return ((Locale) parameter).toLanguageTag();
081    if (parameter instanceof Enum)
082      return ((Enum<?>) parameter).name();
083
084    // Special handling for Oracle
085    if (databaseType() == DatabaseType.ORACLE) {
086      if (parameter instanceof java.util.UUID) {
087        ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
088        byteBuffer.putLong(((UUID) parameter).getMostSignificantBits());
089        byteBuffer.putLong(((UUID) parameter).getLeastSignificantBits());
090        return byteBuffer.array();
091      }
092
093      // Other massaging here if needed...
094    }
095
096    return parameter;
097  }
098
099  /**
100   * What kind of database are we working with?
101   * 
102   * @return the kind of database we're working with
103   */
104  protected DatabaseType databaseType() {
105    return this.databaseType;
106  }
107}