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 }