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 }