001/* 002 * Copyright 2015-2022 Transmogrify LLC, 2022-2025 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 javax.annotation.Nonnull; 020import javax.annotation.Nullable; 021import javax.annotation.concurrent.NotThreadSafe; 022import javax.annotation.concurrent.ThreadSafe; 023import java.time.ZoneId; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.List; 028import java.util.Objects; 029import java.util.Optional; 030import java.util.stream.Collectors; 031 032import static java.lang.String.format; 033import static java.util.Objects.requireNonNull; 034 035/** 036 * Data that represents a SQL statement. 037 * 038 * @author <a href="https://www.revetkn.com">Mark Allen</a> 039 * @since 2.0.0 040 */ 041@ThreadSafe 042public final class StatementContext<T> { 043 @Nonnull 044 private final Statement statement; 045 @Nonnull 046 private final List<Object> parameters; 047 @Nullable 048 private final Class<T> resultSetRowType; 049 @Nonnull 050 private final DatabaseType databaseType; 051 @Nonnull 052 private final ZoneId timeZone; 053 054 protected StatementContext(@Nonnull Builder builder) { 055 requireNonNull(builder); 056 057 this.statement = builder.statement; 058 this.parameters = builder.parameters == null ? List.of() : Collections.unmodifiableList(builder.parameters); 059 this.resultSetRowType = builder.resultSetRowType; 060 this.databaseType = builder.databaseType; 061 this.timeZone = builder.timeZone; 062 } 063 064 @Override 065 public int hashCode() { 066 return Objects.hash(getStatement(), getParameters(), getResultSetRowType(), getDatabaseType(), getTimeZone()); 067 } 068 069 @Override 070 public boolean equals(Object object) { 071 if (this == object) 072 return true; 073 074 if (!(object instanceof StatementContext)) 075 return false; 076 077 StatementContext statementContext = (StatementContext) object; 078 079 return Objects.equals(statementContext.getStatement(), getStatement()) 080 && Objects.equals(statementContext.getParameters(), getParameters()) 081 && Objects.equals(statementContext.getResultSetRowType(), getResultSetRowType()) 082 && Objects.equals(statementContext.getDatabaseType(), getDatabaseType()) 083 && Objects.equals(statementContext.getTimeZone(), getTimeZone()); 084 } 085 086 @Override 087 public String toString() { 088 List<String> components = new ArrayList<>(3); 089 090 components.add(format("statement=%s", getStatement())); 091 092 if (getParameters().size() > 0) 093 components.add(format("parameters=%s", getParameters())); 094 095 Class<T> resultSetRowType = getResultSetRowType().orElse(null); 096 097 if (resultSetRowType != null) 098 components.add(format("resultSetRowType=%s", resultSetRowType)); 099 100 components.add(format("databaseType=%s", getDatabaseType().name())); 101 components.add(format("timeZone=%s", getTimeZone().getId())); 102 103 return format("%s{%s}", getClass().getSimpleName(), components.stream().collect(Collectors.joining(", "))); 104 } 105 106 @Nonnull 107 public Statement getStatement() { 108 return this.statement; 109 } 110 111 @Nonnull 112 public List<Object> getParameters() { 113 return this.parameters; 114 } 115 116 @Nonnull 117 public Optional<Class<T>> getResultSetRowType() { 118 return Optional.ofNullable(this.resultSetRowType); 119 } 120 121 @Nonnull 122 public DatabaseType getDatabaseType() { 123 return this.databaseType; 124 } 125 126 @Nonnull 127 public ZoneId getTimeZone() { 128 return this.timeZone; 129 } 130 131 @Nonnull 132 public static <T> Builder<T> with(@Nonnull Statement statement, 133 @Nonnull Database database) { 134 requireNonNull(statement); 135 requireNonNull(database); 136 137 return new Builder<>(statement, database); 138 } 139 140 /** 141 * Builder used to construct instances of {@link StatementContext}. 142 * <p> 143 * This class is intended for use by a single thread. 144 * 145 * @author <a href="https://www.revetkn.com">Mark Allen</a> 146 * @since 2.0.0 147 */ 148 @NotThreadSafe 149 public static class Builder<T> { 150 @Nonnull 151 private final Statement statement; 152 @Nonnull 153 private final DatabaseType databaseType; 154 @Nonnull 155 private final ZoneId timeZone; 156 @Nullable 157 private List<Object> parameters; 158 @Nullable 159 private Class<T> resultSetRowType; 160 161 private Builder(@Nonnull Statement statement, 162 @Nonnull Database database) { 163 requireNonNull(statement); 164 requireNonNull(database); 165 166 this.statement = statement; 167 this.databaseType = database.getDatabaseType(); 168 this.timeZone = database.getTimeZone(); 169 } 170 171 @Nonnull 172 public Builder parameters(@Nullable List<Object> parameters) { 173 this.parameters = parameters; 174 return this; 175 } 176 177 @Nonnull 178 public Builder parameters(@Nullable Object... parameters) { 179 this.parameters = parameters == null ? null : Arrays.asList(parameters); 180 return this; 181 } 182 183 @Nonnull 184 public Builder resultSetRowType(Class<T> resultSetRowType) { 185 this.resultSetRowType = resultSetRowType; 186 return this; 187 } 188 189 @Nonnull 190 public StatementContext build() { 191 return new StatementContext(this); 192 } 193 } 194}