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>BigInteger</code>: the object is transformed to a String and bound using
146 * <code>stmt.setObject()</code>, with <code>BIGINT</code> as target type.
147 * </li>
148 * <li><code>enum</code>: the enum is transformed into an integer by taking its ordinal</li>
149 * <li><code>String</code>: the string is bound using <code>stmt.setObject()</code>, with <code>BIGINT</code> as
150 * target type.
151 * </li>
152 * </ul>
153 * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
154 */
155 public static Binder integerBinder() {
156 return INTEGER_BINDER;
157 }
158
159 /**
160 * The implementation for {@link Binders#stringBinder()}
161 * @author JB Nizet
162 */
163 private static final class StringBinder implements Binder {
164 @Override
165 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
166 if (value instanceof String) {
167 stmt.setString(param, (String) value);
168 }
169 else if (value instanceof Enum<?>) {
170 stmt.setString(param, ((Enum<?>) value).name());
171 }
172 else if (value == null) {
173 stmt.setObject(param, null);
174 }
175 else {
176 stmt.setString(param, value.toString());
177 }
178 }
179
180 @Override
181 public String toString() {
182 return "Binders.stringBinder";
183 }
184 }
185
186 /**
187 * The implementation for {@link Binders#timeBinder()}
188 * @author JB Nizet
189 */
190 private static final class TimeBinder implements Binder {
191 @Override
192 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
193 if (value instanceof Time) {
194 stmt.setTime(param, (Time) value);
195 }
196 else if (value instanceof java.util.Date) {
197 stmt.setTime(param, new Time(((java.util.Date) value).getTime()));
198 }
199 else if (value instanceof Calendar) {
200 stmt.setTime(param, new Time(((Calendar) value).getTimeInMillis()));
201 }
202 else if (value instanceof String) {
203 stmt.setTime(param, Time.valueOf((String) value));
204 }
205 else {
206 stmt.setObject(param, value);
207 }
208 }
209
210 @Override
211 public String toString() {
212 return "Binders.timeBinder";
213 }
214 }
215
216 /**
217 * The implementation for {@link Binders#integerBinder()}
218 * @author JB Nizet
219 */
220 private static final class IntegerBinder implements Binder {
221 @Override
222 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
223 if (value instanceof BigInteger) {
224 stmt.setObject(param, value.toString(), Types.BIGINT);
225 }
226 else if (value instanceof Enum<?>) {
227 stmt.setInt(param, ((Enum<?>) value).ordinal());
228 }
229 else if (value instanceof String) {
230 stmt.setObject(param, value, Types.BIGINT);
231 }
232 else {
233 stmt.setObject(param, value);
234 }
235 }
236
237 @Override
238 public String toString() {
239 return "Binders.integerBinder";
240 }
241 }
242
243 /**
244 * The implementation for {@link Binders#decimalBinder()}
245 * @author JB Nizet
246 */
247 private static final class DecimalBinder implements Binder {
248 @Override
249 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
250 if (value instanceof String) {
251 stmt.setBigDecimal(param, new BigDecimal((String) value));
252 }
253 else {
254 stmt.setObject(param, value);
255 }
256 }
257
258 @Override
259 public String toString() {
260 return "Binders.decimalBinder";
261 }
262 }
263
264 /**
265 * The implementation for {@link Binders#timestampBinder()}
266 * @author JB Nizet
267 */
268 private static final class TimestampBinder implements Binder {
269 // the number of chars in yyyy-mm-dd hh:mm:ss
270 private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;
271
272 @Override
273 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
274 if (value instanceof Timestamp) {
275 stmt.setTimestamp(param, (Timestamp) value);
276 }
277 else if (value instanceof java.util.Date) {
278 stmt.setTimestamp(param, new Timestamp(((java.util.Date) value).getTime()));
279 }
280 else if (value instanceof Calendar) {
281 stmt.setTimestamp(param, new Timestamp(((Calendar) value).getTimeInMillis()));
282 }
283 else if (value instanceof String) {
284 String valueAsString = (String) value;
285 if (valueAsString.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {
286 stmt.setTimestamp(param, Timestamp.valueOf(valueAsString));
287 }
288 else {
289 Date valueAsDate = Date.valueOf(valueAsString);
290 stmt.setTimestamp(param, new Timestamp(valueAsDate.getTime()));
291 }
292 }
293 else {
294 stmt.setObject(param, value);
295 }
296 }
297
298 @Override
299 public String toString() {
300 return "Binders.timestampBinder";
301 }
302 }
303
304 /**
305 * The implementation for {@link Binders#dateBinder()}
306 * @author JB Nizet
307 */
308 private static final class DateBinder implements Binder {
309 @Override
310 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
311 if (value instanceof Date) {
312 stmt.setDate(param, (Date) value);
313 }
314 else if (value instanceof java.util.Date) {
315 stmt.setDate(param, new Date(((java.util.Date) value).getTime()));
316 }
317 else if (value instanceof Calendar) {
318 stmt.setDate(param, new Date(((Calendar) value).getTimeInMillis()));
319 }
320 else if (value instanceof String) {
321 stmt.setDate(param, Date.valueOf((String) value));
322 }
323 else {
324 stmt.setObject(param, value);
325 }
326 }
327
328 @Override
329 public String toString() {
330 return "Binders.dateBinder";
331 }
332 }
333
334 /**
335 * The implementation for {@link Binders#defaultBinder()}
336 * @author JB Nizet
337 */
338 private static final class DefaultBinder implements Binder {
339 @Override
340 public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
341 Object boundValue;
342 if (value instanceof Enum) {
343 boundValue = ((Enum) value).name();
344 }
345 else if (value instanceof java.util.Date) {
346 boundValue = new Timestamp(((java.util.Date) value).getTime());
347 }
348 else if (value instanceof Calendar) {
349 boundValue = new Timestamp(((Calendar) value).getTime().getTime());
350 }
351 else {
352 boundValue = value;
353 }
354 stmt.setObject(param, boundValue);
355 }
356
357 @Override
358 public String toString() {
359 return "Binders.defaultBinder";
360 }
361 }
362 }