001 /*
002 * The MIT License
003 *
004 * Copyright (c) 2012, Ninja Squad
005 *
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be included in
014 * all copies or substantial portions of the Software.
015 *
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
022 * THE SOFTWARE.
023 */
024
025 package com.ninja_squad.dbsetup.bind;
026
027 import java.math.BigDecimal;
028 import java.math.BigInteger;
029 import java.sql.Date;
030 import java.sql.Time;
031 import java.sql.Timestamp;
032 import java.sql.Types;
033 import java.util.Calendar;
034
035 /**
036 * Utility class allowing to get various kinds of binders. The {@link DefaultBinderConfiguration} uses binders
037 * returned by this class, based on the type of the parameter.
038 * @author JB Nizet
039 */
040 public final class Binders {
041
042 private static final Binder DEFAULT_BINDER = new DefaultBinder();
043 private static final Binder DATE_BINDER = new DateBinder();
044 private static final Binder TIMESTAMP_BINDER = new TimestampBinder();
045 private static final Binder DECIMAL_BINDER = new DecimalBinder();
046 private static final Binder INTEGER_BINDER = new IntegerBinder();
047 private static final Binder TIME_BINDER = new TimeBinder();
048 private static final Binder STRING_BINDER = new StringBinder();
049
050 private Binders() {
051 }
052
053 /**
054 * Returns the default binder. This binder is normally used for columns of a type that is not handled by the other
055 * binders. It is also used when the metadata are not used and the Insert thus doesn't know the type of the column.
056 * It simply uses <code>stmt.setObject()</code> to bind the parameter, except if the value being bound is of some
057 * some well-known type not handled by JDBC:
058 * <ul>
059 * <li><code>enum</code>: the name of the enum is bound</li>
060 * <li><code>java.util.Date</code>: the date is transformed to a java.sql.Timestamp</li>
061 * <li><code>java.util.Calendar</code>: the date is transformed to a java.sql.Timestamp</li>
062 * </ul>
063 */
064 public static Binder defaultBinder() {
065 return DEFAULT_BINDER;
066 }
067
068 /**
069 * Returns a binder suitable for columns of type CHAR and VARCHAR. The returned binder supports values of type
070 * <ul>
071 * <li><code>String</code></li>
072 * <li><code>enum</code>: the name of the enum is used as bound value</li>
073 * <li><code>Object</code>: the <code>toString()</code> of the object is used as bound value</li>
074 * </ul>
075 */
076 public static Binder stringBinder() {
077 return STRING_BINDER;
078 }
079
080 /**
081 * Returns a binder suitable for columns of type DATE. The returned binder supports values of type
082 * <ul>
083 * <li><code>java.sql.Date</code></li>
084 * <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a java.sql.Date</li>
085 * <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a java.sql.Date
086 * </li>
087 * <li><code>String</code>: the string is transformed to a java.sql.Date using the <code>Date.valueOf()</code>
088 * method</li>
089 * </ul>
090 * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
091 */
092 public static Binder dateBinder() {
093 return DATE_BINDER;
094 }
095
096 /**
097 * Returns a binder suitable for columns of type TIMESTAMP. The returned binder supports values of type
098 * <ul>
099 * <li><code>java.sql.Timestamp</code></li>
100 * <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a java.sql.Timestamp</li>
101 * <li><code>java.util.Calendar: the milliseconds of the calendar are used to construct a
102 * java.sql.Timestamp</code></li>
103 * <li><code>String</code>: the string is transformed to a <code>java.sql.Timestamp</code> using the
104 * <code>Timestamp.valueOf()</code> method, or using the <code>java.sql.Date.valueOf()</code> method if the
105 * string has less than 19 characters</li>
106 * </ul>
107 * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
108 */
109 public static Binder timestampBinder() {
110 return TIMESTAMP_BINDER;
111 }
112
113 /**
114 * Returns a binder suitable for columns of type TIME. The returned binder supports values of type
115 * <ul>
116 * <li><code>java.sql.Time</code></li>
117 * <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a
118 * <code>java.sql.Time</code></li>
119 * <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a
120 * <code>java.sql.Time</code>
121 * </li>
122 * <li><code>String</code>: the string is transformed to a java.sql.Time using the
123 * <code>Time.valueOf()</code> method</li>
124 * </ul>
125 * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
126 */
127 public static Binder timeBinder() {
128 return TIME_BINDER;
129 }
130
131 /**
132 * Returns a binder suitable for numeric, decimal columns. The returned binder supports values of type
133 * <ul>
134 * <li><code>String</code>: the string is transformed to a java.math.BigDecimal using its constructor</li>
135 * </ul>
136 * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
137 */
138 public static Binder decimalBinder() {
139 return DECIMAL_BINDER;
140 }
141
142 /**
143 * Returns a binder suitable for numeric, integer columns. The returned binder supports values of type
144 * <ul>
145 * <li><code>enum</code>: the enum is transformed into an integer by taking its ordinal</li>
146 * <li><code>String</code>: the string is transformed to a <code>java.math.BigInteger</code> using its
147 * constructor</li>
148 * </ul>
149 * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
150 */
151 public static Binder integerBinder() {
152 return INTEGER_BINDER;
153 }
154
155 /**
156 * The implementation for {@link Binders#stringBinder()}
157 * @author JB Nizet
158 */
159 private static final class StringBinder implements Binder {
160 @Override
161 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
162 if (value instanceof String) {
163 stmt.setString(param, (String) value);
164 }
165 else if (value instanceof Enum<?>) {
166 stmt.setString(param, ((Enum<?>) value).name());
167 }
168 else if (value == null) {
169 stmt.setObject(param, null);
170 }
171 else {
172 stmt.setString(param, value.toString());
173 }
174 }
175
176 @Override
177 public String toString() {
178 return "Binders.stringBinder";
179 }
180 }
181
182 /**
183 * The implementation for {@link Binders#timeBinder()}
184 * @author JB Nizet
185 */
186 private static final class TimeBinder implements Binder {
187 @Override
188 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
189 if (value instanceof Time) {
190 stmt.setTime(param, (Time) value);
191 }
192 else if (value instanceof java.util.Date) {
193 stmt.setTime(param, new Time(((java.util.Date) value).getTime()));
194 }
195 else if (value instanceof java.util.Calendar) {
196 stmt.setTime(param, new Time(((java.util.Calendar) value).getTimeInMillis()));
197 }
198 else if (value instanceof String) {
199 stmt.setTime(param, Time.valueOf((String) value));
200 }
201 else {
202 stmt.setObject(param, value);
203 }
204 }
205
206 @Override
207 public String toString() {
208 return "Binders.timeBinder";
209 }
210 }
211
212 /**
213 * The implementation for {@link Binders#integerBinder()}
214 * @author JB Nizet
215 */
216 private static final class IntegerBinder implements Binder {
217 @Override
218 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
219 if (value instanceof BigInteger) {
220 stmt.setObject(param, value, Types.BIGINT);
221 }
222 else if (value instanceof Enum<?>) {
223 stmt.setInt(param, ((Enum<?>) value).ordinal());
224 }
225 else if (value instanceof String) {
226 stmt.setObject(param, new BigInteger((String) value), Types.BIGINT);
227 }
228 else {
229 stmt.setObject(param, value);
230 }
231 }
232
233 @Override
234 public String toString() {
235 return "Binders.integerBinder";
236 }
237 }
238
239 /**
240 * The implementation for {@link Binders#decimalBinder()}
241 * @author JB Nizet
242 */
243 private static final class DecimalBinder implements Binder {
244 @Override
245 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
246 if (value instanceof String) {
247 stmt.setBigDecimal(param, new BigDecimal((String) value));
248 }
249 else {
250 stmt.setObject(param, value);
251 }
252 }
253
254 @Override
255 public String toString() {
256 return "Binders.decimalBinder";
257 }
258 }
259
260 /**
261 * The implementation for {@link Binders#timestampBinder()}
262 * @author JB Nizet
263 */
264 private static final class TimestampBinder implements Binder {
265 // the number of chars in yyyy-mm-dd hh:mm:ss
266 private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;
267
268 @Override
269 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
270 if (value instanceof Timestamp) {
271 stmt.setTimestamp(param, (Timestamp) value);
272 }
273 else if (value instanceof java.util.Date) {
274 stmt.setTimestamp(param, new Timestamp(((java.util.Date) value).getTime()));
275 }
276 else if (value instanceof java.util.Calendar) {
277 stmt.setTimestamp(param, new Timestamp(((java.util.Calendar) value).getTimeInMillis()));
278 }
279 else if (value instanceof String) {
280 String valueAsString = (String) value;
281 if (valueAsString.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {
282 stmt.setTimestamp(param, Timestamp.valueOf(valueAsString));
283 }
284 else {
285 Date valueAsDate = Date.valueOf(valueAsString);
286 stmt.setTimestamp(param, new Timestamp(valueAsDate.getTime()));
287 }
288 }
289 else {
290 stmt.setObject(param, value);
291 }
292 }
293
294 @Override
295 public String toString() {
296 return "Binders.timestampBinder";
297 }
298 }
299
300 /**
301 * The implementation for {@link Binders#dateBinder()}
302 * @author JB Nizet
303 */
304 private static final class DateBinder implements Binder {
305 @Override
306 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
307 if (value instanceof Date) {
308 stmt.setDate(param, (Date) value);
309 }
310 else if (value instanceof java.util.Date) {
311 stmt.setDate(param, new Date(((java.util.Date) value).getTime()));
312 }
313 else if (value instanceof java.util.Calendar) {
314 stmt.setDate(param, new Date(((java.util.Calendar) value).getTimeInMillis()));
315 }
316 else if (value instanceof String) {
317 stmt.setDate(param, Date.valueOf((String) value));
318 }
319 else {
320 stmt.setObject(param, value);
321 }
322 }
323
324 @Override
325 public String toString() {
326 return "Binders.dateBinder";
327 }
328 }
329
330 /**
331 * The implementation for {@link Binders#defaultBinder()}
332 * @author JB Nizet
333 */
334 private static final class DefaultBinder implements Binder {
335 @Override
336 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
337 Object boundValue;
338 if (value instanceof Enum) {
339 boundValue = ((Enum) value).name();
340 }
341 else if (value instanceof java.util.Date) {
342 boundValue = new Timestamp(((java.util.Date) value).getTime());
343 }
344 else if (value instanceof Calendar) {
345 boundValue = new Timestamp(((Calendar) value).getTime().getTime());
346 }
347 else {
348 boundValue = value;
349 }
350 stmt.setObject(param, boundValue);
351 }
352
353 @Override
354 public String toString() {
355 return "Binders.defaultBinder";
356 }
357 }
358 }