Coverage Summary for Class: Session (net.sf.persism)
| Class | Method, % | Line, % |
|---|---|---|
| Session | 100% (21/21) | 94.4% (354/375) |
| Session$1 | 100% (1/1) | 100% (1/1) |
| Total | 100% (22/22) | 94.4% (355/376) |
1 package net.sf.persism; 2 3 import net.sf.persism.annotations.NotTable; 4 import net.sf.persism.annotations.View; 5 6 import java.lang.reflect.Method; 7 import java.math.BigDecimal; 8 import java.sql.*; 9 import java.util.ArrayList; 10 import java.util.Arrays; 11 import java.util.List; 12 import java.util.Map; 13 14 import static net.sf.persism.Util.isRecord; 15 16 /** 17 * Performs various read and write operations in the database. 18 * 19 * @author Dan Howard 20 * @since 1/8/2021 21 */ 22 public final class Session implements AutoCloseable { 23 24 private static final Log log = Log.getLogger(Session.class); 25 26 private final Connection connection; 27 28 private MetaData metaData; 29 30 private Reader reader; 31 private Converter converter; 32 33 /** 34 * Default constructor for a Session object 35 * 36 * @param connection db connection 37 * @throws PersismException if something goes wrong 38 */ 39 public Session(Connection connection) throws PersismException { 40 this.connection = connection; 41 init(connection, null); 42 } 43 44 /** 45 * Constructor for Session where you want to specify the Session Key. 46 * 47 * @param connection db connection 48 * @param sessionKey Unique string to represent the connection URL if it is not available on the Connection metadata. 49 * This string should start with the jdbc url string to indicate the connection type. 50 * <code> 51 * <br> jdbc:h2 = h2 52 * <br> jdbc:sqlserver = MS SQL 53 * <br> jdbc:oracle = Oracle 54 * <br> jdbc:sqlite = SQLite 55 * <br> jdbc:derby = Derby 56 * <br> jdbc:mysql = MySQL/MariaDB 57 * <br> jbc:postgresql = PostgreSQL 58 * <br> jdbc:firebirdsql = Firebird (Jaybird) 59 * <br> jdbc:hsqldb = HSQLDB 60 * <br> jdbc:ucanaccess = MS Access 61 * <br> jdbc:informix = Informix 62 * </code> 63 * @throws PersismException if something goes wrong 64 */ 65 public Session(Connection connection, String sessionKey) throws PersismException { 66 this.connection = connection; 67 init(connection, sessionKey); 68 } 69 70 /** 71 * Close the connection 72 */ 73 @Override 74 public void close() { 75 if (connection != null) { 76 try { 77 connection.close(); 78 } catch (SQLException e) { 79 log.warn(e.getMessage(), e); 80 } 81 } 82 } 83 84 private void init(Connection connection, String sessionKey) { 85 // place any DB specific properties here. 86 try { 87 metaData = MetaData.getInstance(connection, sessionKey); 88 } catch (SQLException e) { 89 throw new PersismException(e.getMessage(), e); 90 } 91 92 converter = new Converter(); 93 reader = new Reader(this); 94 } 95 96 /** 97 * Function block of database operations to group together in one transaction. 98 * This method will set autocommit to false then execute the function, commit and set autocommit back to true. 99 * <pre>{@code 100 * session.withTransaction(() -> { 101 * Contact contact = getContactFromSomewhere(); 102 * 103 * contact.setIdentity(randomUUID); 104 * session.insert(contact); 105 * 106 * contact.setContactName("Wilma Flintstone"); 107 * 108 * session.update(contact); 109 * session.fetch(contact); 110 * }); 111 * }</pre> 112 * 113 * @param transactionBlock Block of operations expected to run as a single transaction. 114 * @throws PersismException in case of SQLException where the transaction is rolled back. 115 */ 116 public void withTransaction(Runnable transactionBlock) { 117 try { 118 connection.setAutoCommit(false); 119 transactionBlock.run(); 120 connection.commit(); 121 } catch (Exception e) { 122 Util.rollback(connection); 123 throw new PersismException(e.getMessage(), e); 124 } finally { 125 try { 126 connection.setAutoCommit(true); 127 } catch (SQLException e) { 128 log.warn(e.getMessage()); 129 } 130 } 131 } 132 133 /** 134 * Updates the data object in the database. 135 * 136 * @param object data object to update. 137 * @return usually 1 to indicate rows changed via JDBC. 138 * @throws PersismException Indicating the upcoming robot uprising. 139 */ 140 public <T> Result<T> update(T object) throws PersismException { 141 142 checkIfOkForWriteOperation(object, "Update"); 143 144 List<String> primaryKeys = metaData.getPrimaryKeys(object.getClass(), connection); 145 if (primaryKeys.size() == 0) { 146 throw new PersismException(Messages.TableHasNoPrimaryKeys.message("UPDATE", metaData.getTableName(object.getClass()))); 147 } 148 149 PreparedStatement st = null; 150 try { 151 152 String updateStatement = null; 153 try { 154 updateStatement = metaData.getUpdateStatement(object, connection); 155 } catch (NoChangesDetectedForUpdateException e) { 156 log.info("No properties changed. No update required for Object: " + object + " class: " + object.getClass().getName()); 157 return new Result<>(0, object); 158 } 159 160 st = connection.prepareStatement(updateStatement); 161 162 // These keys should always be in sorted order. 163 Map<String, PropertyInfo> allProperties = metaData.getTableColumnsPropertyInfo(object.getClass(), connection); 164 Map<String, PropertyInfo> changedProperties; 165 if (object instanceof Persistable) { 166 changedProperties = metaData.getChangedProperties((Persistable<?>) object, connection); 167 } else { 168 changedProperties = allProperties; 169 } 170 171 List<Object> params = new ArrayList<>(primaryKeys.size()); 172 List<ColumnInfo> columnInfos = new ArrayList<>(changedProperties.size()); 173 174 Map<String, ColumnInfo> columns = metaData.getColumns(object.getClass(), connection); 175 176 for (String column : changedProperties.keySet()) { 177 ColumnInfo columnInfo = columns.get(column); 178 179 if (primaryKeys.contains(column)) { 180 log.info("Session update: skipping column " + column); 181 } else { 182 Object value = allProperties.get(column).getter.invoke(object); 183 params.add(value); 184 columnInfos.add(columnInfo); 185 } 186 } 187 188 for (String column : primaryKeys) { 189 params.add(allProperties.get(column).getter.invoke(object)); 190 columnInfos.add(metaData.getColumns(object.getClass(), connection).get(column)); 191 } 192 assert params.size() == columnInfos.size(); 193 for (int j = 0; j < params.size(); j++) { 194 if (params.get(j) != null) { 195 params.set(j, converter.convert(params.get(j), columnInfos.get(j).columnType.getJavaType(), columnInfos.get(j).columnName)); 196 } 197 } 198 setParameters(st, params.toArray()); 199 int rows = st.executeUpdate(); 200 201 if (object instanceof Persistable) { 202 // Save this object state to later detect changed properties 203 ((Persistable<?>) object).saveReadState(); 204 } 205 206 return new Result<>(rows, object); 207 208 } catch (Exception e) { 209 Util.rollback(connection); 210 throw new PersismException(e.getMessage(), e); 211 212 } finally { 213 Util.cleanup(st, null); 214 } 215 } 216 217 /** 218 * Inserts the data object in the database refreshing with autoinc and other defaults that may exist. 219 * 220 * @param <T> Type of the inserted object 221 * @param object the data object to insert. 222 * @return usually 1 to indicate rows changed via JDBC. 223 * @throws PersismException When planet of the apes starts happening. 224 */ 225 public <T> Result<T> insert(T object) throws PersismException { 226 227 checkIfOkForWriteOperation(object, "Insert"); 228 229 Object returnObject = object; 230 231 String insertStatement = metaData.getInsertStatement(object, connection); 232 233 PreparedStatement st = null; 234 ResultSet rs = null; 235 236 try { 237 // These keys should always be in sorted order. 238 Map<String, PropertyInfo> properties = metaData.getTableColumnsPropertyInfo(object.getClass(), connection); 239 Map<String, ColumnInfo> columns = metaData.getColumns(object.getClass(), connection); 240 241 List<String> generatedKeys = new ArrayList<>(1); 242 for (ColumnInfo column : columns.values()) { 243 if (column.autoIncrement) { 244 generatedKeys.add(column.columnName); 245 } else if (metaData.getConnectionType() == ConnectionTypes.PostgreSQL && column.primary && column.hasDefault) { 246 generatedKeys.add(column.columnName); 247 } 248 } 249 250 if (generatedKeys.size() > 0) { 251 String[] keyArray = generatedKeys.toArray(new String[0]); 252 st = connection.prepareStatement(insertStatement, keyArray); 253 } else { 254 st = connection.prepareStatement(insertStatement); 255 } 256 257 boolean refreshAfterInsert = false; 258 259 List<Object> params = new ArrayList<>(columns.size()); 260 List<ColumnInfo> columnInfos = new ArrayList<>(columns.size()); 261 262 for (ColumnInfo columnInfo : columns.values()) { 263 264 PropertyInfo propertyInfo = properties.get(columnInfo.columnName); 265 if (propertyInfo.getter == null) { 266 throw new PersismException(Messages.ClassHasNoGetterForProperty.message(object.getClass(), propertyInfo.propertyName)); 267 } 268 if (!columnInfo.autoIncrement) { 269 270 if (columnInfo.hasDefault) { 271 // Do not include if this column has a default and no value has been 272 // set on it's associated property. 273 if (propertyInfo.getter.getReturnType().isPrimitive()) { 274 log.warnNoDuplicates(Messages.PropertyShouldBeAnObjectType.message(propertyInfo.propertyName, columnInfo.columnName, object.getClass())); 275 } 276 277 if (propertyInfo.getter.invoke(object) == null) { 278 279 if (columnInfo.primary) { 280 // This is supported with PostgreSQL but otherwise throw this an exception 281 if (!(metaData.getConnectionType() == ConnectionTypes.PostgreSQL)) { 282 throw new PersismException(Messages.NonAutoIncGeneratedNotSupported.message()); 283 } 284 } 285 286 refreshAfterInsert = true; 287 continue; 288 } 289 } 290 291 Object value = propertyInfo.getter.invoke(object); 292 293 params.add(value); 294 columnInfos.add(columnInfo); 295 } 296 } 297 298 // https://forums.oracle.com/forums/thread.jspa?threadID=879222 299 // http://download.oracle.com/javase/1.4.2/docs/guide/jdbc/getstart/statement.html 300 //int ret = st.executeUpdate(insertStatement, Statement.RETURN_GENERATED_KEYS); 301 assert params.size() == columnInfos.size(); 302 for (int j = 0; j < params.size(); j++) { 303 if (params.get(j) != null) { 304 params.set(j, converter.convert(params.get(j), columnInfos.get(j).columnType.getJavaType(), columnInfos.get(j).columnName)); 305 } 306 } 307 308 setParameters(st, params.toArray()); 309 st.execute(); 310 int ret = st.getUpdateCount(); 311 312 log.debug("insert return count after insert: %s", ret); 313 314 List<Object> primaryKeyValues = new ArrayList<>(); 315 if (generatedKeys.size() > 0) { 316 rs = st.getGeneratedKeys(); 317 PropertyInfo propertyInfo; 318 for (String column : generatedKeys) { 319 if (rs.next()) { 320 321 propertyInfo = properties.get(column); 322 323 Method setter = propertyInfo.setter; 324 Object value; 325 if (setter != null) { 326 value = getTypedValueReturnedFromGeneratedKeys(setter.getParameterTypes()[0], rs); 327 setter.invoke(object, value); 328 } else { 329 // Set read-only property by field ONLY FOR NON-RECORDS. 330 value = getTypedValueReturnedFromGeneratedKeys(propertyInfo.field.getType(), rs); 331 if (!isRecord(object.getClass())) { 332 propertyInfo.field.setAccessible(true); 333 propertyInfo.field.set(object, value); 334 propertyInfo.field.setAccessible(false); 335 log.debug("insert %s generated %s", column, value); 336 } 337 } 338 339 primaryKeyValues.add(value); 340 } 341 } 342 } 343 344 // If it's a record we can't assign the autoinc so we need a refresh 345 if (generatedKeys.size() > 0 && isRecord(object.getClass())) { 346 refreshAfterInsert = true; 347 } 348 349 if (refreshAfterInsert) { 350 // Read the full object back to update any properties which had defaults 351 if (isRecord(object.getClass())) { 352 returnObject = fetch(object.getClass(), metaData.getSelectStatement(object.getClass(), connection), primaryKeyValues.toArray()); 353 } else { 354 fetch(object); 355 returnObject = object; 356 } 357 } 358 359 if (object instanceof Persistable) { 360 // Save this object new state to later detect changed properties 361 ((Persistable<?>) object).saveReadState(); 362 } 363 364 //noinspection unchecked 365 return new Result<>(ret, (T) returnObject); 366 } catch (Exception e) { 367 Util.rollback(connection); 368 throw new PersismException(e.getMessage(), e); 369 } finally { 370 Util.cleanup(st, rs); 371 } 372 } 373 374 375 /** 376 * Deletes the data object from the database. 377 * 378 * @param object data object to delete 379 * @return usually 1 to indicate rows changed via JDBC. 380 * @throws PersismException Perhaps when asteroid 1999 RQ36 hits us? 381 */ 382 public <T> Result<T> delete(T object) throws PersismException { 383 384 checkIfOkForWriteOperation(object, "Delete"); 385 386 List<String> primaryKeys = metaData.getPrimaryKeys(object.getClass(), connection); 387 if (primaryKeys.size() == 0) { 388 throw new PersismException(Messages.TableHasNoPrimaryKeys.message("DELETE", metaData.getTableName(object.getClass()))); 389 } 390 391 PreparedStatement st = null; 392 try { 393 String deleteStatement = metaData.getDeleteStatement(object, connection); 394 st = connection.prepareStatement(deleteStatement); 395 396 // These keys should always be in sorted order. 397 Map<String, PropertyInfo> columns = metaData.getTableColumnsPropertyInfo(object.getClass(), connection); 398 399 List<Object> params = new ArrayList<>(primaryKeys.size()); 400 List<ColumnInfo> columnInfos = new ArrayList<>(columns.size()); 401 for (String column : primaryKeys) { 402 params.add(columns.get(column).getter.invoke(object)); 403 columnInfos.add(metaData.getColumns(object.getClass(), connection).get(column)); 404 } 405 406 for (int j = 0; j < params.size(); j++) { 407 if (params.get(j) != null) { 408 params.set(j, converter.convert(params.get(j), columnInfos.get(j).columnType.getJavaType(), columnInfos.get(j).columnName)); 409 } 410 } 411 setParameters(st, params.toArray()); 412 int rows = st.executeUpdate(); 413 return new Result<>(rows, object); 414 415 } catch (Exception e) { 416 Util.rollback(connection); 417 throw new PersismException(e.getMessage(), e); 418 419 } finally { 420 Util.cleanup(st, null); 421 } 422 } 423 424 // For unit tests only for now. 425 void execute(String sql, Object... parameters) { 426 427 log.debug("execute: %s params: %s", sql, Arrays.asList(parameters)); 428 429 Statement st = null; 430 try { 431 432 if (parameters.length == 0) { 433 st = connection.createStatement(); 434 st.execute(sql); 435 } else { 436 st = connection.prepareStatement(sql); 437 PreparedStatement pst = (PreparedStatement) st; 438 setParameters(pst, parameters); 439 pst.execute(); 440 } 441 442 } catch (Exception e) { 443 Util.rollback(connection); 444 throw new PersismException(e.getMessage(), e); 445 } finally { 446 Util.cleanup(st, null); 447 } 448 } 449 450 /** 451 * Query for all rows in a table or view. 452 * 453 * @param objectClass class of objects to return. 454 * @param <T> Return type 455 * @return a list of objects of the specified class 456 * @throws PersismException If something goes wrong you get a big stack trace. 457 */ 458 public <T> List<T> query(Class<T> objectClass) throws Exception { 459 if (objectClass.getAnnotation(NotTable.class) != null) { 460 throw new PersismException(Messages.OperationNotSupportedForNotTableQuery.message(objectClass, "QUERY")); 461 } 462 String sql = metaData.getSelectStatement(objectClass, connection); 463 sql = sql.substring(0, sql.indexOf(" WHERE")); 464 return query(objectClass, sql); 465 } 466 467 /** 468 * Query for a list of objects of the specified class using the specified SQL query and parameters. 469 * The type of the list can be Data Objects or native Java Objects or primitives. 470 * 471 * @param objectClass class of objects to return. 472 * @param sql query string to execute. 473 * @param parameters parameters to the query. 474 * @param <T> Return type 475 * @return a list of objects of the specified class using the specified SQL query and parameters. 476 * @throws PersismException If something goes wrong you get a big stack trace. 477 */ 478 public <T> List<T> query(Class<T> objectClass, String sql, Object... parameters) throws PersismException { 479 List<T> list = new ArrayList<T>(32); 480 481 // If we know this type it means it's a primitive type. Not a DAO so we use a different rule to read those 482 boolean isPOJO = Types.getType(objectClass) == null; 483 boolean isRecord = isPOJO && isRecord(objectClass); 484 485 if (isPOJO && objectClass.getAnnotation(NotTable.class) == null) { 486 // Make sure columns are initialized if this is a table. 487 metaData.getTableColumnsPropertyInfo(objectClass, connection); 488 } 489 490 JDBCResult result = new JDBCResult(); 491 try { 492 // we don't check parameter types here? Nope - we don't know anything at this point. 493 executeQuery(result, sql, parameters); 494 495 while (result.rs.next()) { 496 if (isRecord) { 497 list.add(reader.readRecord(objectClass, result.rs)); 498 } else if (isPOJO) { 499 T t = objectClass.getDeclaredConstructor().newInstance(); 500 list.add(reader.readObject(t, result.rs)); 501 } else { 502 list.add(reader.readColumn(result.rs, 1, objectClass)); 503 } 504 } 505 506 } catch (Exception e) { 507 Util.rollback(connection); 508 throw new PersismException(e.getMessage(), e); 509 } finally { 510 Util.cleanup(result.st, result.rs); 511 } 512 513 return list; 514 515 } 516 517 /** 518 * Fetch an object from the database by it's primary key(s). 519 * You should instantiate the object and set the primary key properties before calling this method. 520 * 521 * @param object Data object to read from the database. 522 * @return true if the object was found by the primary key. 523 * @throws PersismException if something goes wrong. 524 */ 525 public boolean fetch(Object object) throws PersismException { 526 Class<?> objectClass = object.getClass(); 527 528 // If we know this type it means it's a primitive type. This method cannot be used for primitives 529 boolean readPrimitive = Types.getType(objectClass) != null; 530 if (readPrimitive) { 531 // For unit tests 532 throw new PersismException(Messages.CannotReadThisType.message("primitive")); 533 } 534 535 if (isRecord(objectClass)) { 536 throw new PersismException(Messages.CannotReadThisType.message("Record")); 537 } 538 539 List<String> primaryKeys = metaData.getPrimaryKeys(objectClass, connection); 540 if (primaryKeys.size() == 0) { 541 throw new PersismException(Messages.TableHasNoPrimaryKeys.message("FETCH", metaData.getTableName(object.getClass()))); 542 } 543 544 Map<String, PropertyInfo> properties = metaData.getTableColumnsPropertyInfo(object.getClass(), connection); 545 List<Object> params = new ArrayList<>(primaryKeys.size()); 546 List<ColumnInfo> columnInfos = new ArrayList<>(properties.size()); 547 Map<String, ColumnInfo> cols = metaData.getColumns(objectClass, connection); 548 JDBCResult JDBCResult = new JDBCResult(); 549 try { 550 for (String column : primaryKeys) { 551 PropertyInfo propertyInfo = properties.get(column); 552 params.add(propertyInfo.getter.invoke(object)); 553 columnInfos.add(cols.get(column)); 554 } 555 assert params.size() == columnInfos.size(); 556 557 String sql = metaData.getSelectStatement(objectClass, connection); 558 log.debug("FETCH %s PARAMS: %s", sql, params); 559 for (int j = 0; j < params.size(); j++) { 560 if (params.get(j) != null) { 561 params.set(j, converter.convert(params.get(j), columnInfos.get(j).columnType.getJavaType(), columnInfos.get(j).columnName)); 562 } 563 } 564 executeQuery(JDBCResult, sql, params.toArray()); 565 566 if (JDBCResult.rs.next()) { 567 reader.readObject(object, JDBCResult.rs); 568 return true; 569 } 570 return false; 571 572 } catch (Exception e) { 573 Util.rollback(connection); 574 throw new PersismException(e.getMessage(), e); 575 } finally { 576 Util.cleanup(JDBCResult.st, JDBCResult.rs); 577 } 578 } 579 580 /** 581 * Fetch an object of the specified type from the database. The type can be a Data Object or a native Java Object or primitive. 582 * 583 * @param objectClass Type of returned value 584 * @param sql query - this would usually be a select OR a select of a single column if the type is a primitive. 585 * If this is a primitive type then this method will only look at the 1st column in the result. 586 * @param parameters parameters to the query. 587 * @param <T> Return type 588 * @return value read from the database of type T or null if not found 589 * @throws PersismException Well, this is a runtime exception so actually it could be anything really. 590 */ 591 public <T> T fetch(Class<T> objectClass, String sql, Object... parameters) throws PersismException { 592 // If we know this type it means it's a primitive type. Not a DAO so we use a different rule to read those 593 boolean isPOJO = Types.getType(objectClass) == null; 594 boolean isRecord = isPOJO && isRecord(objectClass); 595 596 JDBCResult result = new JDBCResult(); 597 try { 598 599 executeQuery(result, sql, parameters); 600 601 if (result.rs.next()) { 602 if (isRecord) { 603 return reader.readRecord(objectClass, result.rs); 604 } else if (isPOJO) { 605 T t = objectClass.getDeclaredConstructor().newInstance(); 606 return reader.readObject(t, result.rs); 607 } else { 608 return reader.readColumn(result.rs, 1, objectClass); 609 } 610 } 611 612 return null; 613 614 } catch (Exception e) { 615 Util.rollback(connection); 616 throw new PersismException(e.getMessage(), e); 617 } finally { 618 Util.cleanup(result.st, result.rs); 619 } 620 } 621 622 MetaData getMetaData() { 623 return metaData; 624 } 625 626 Converter getConverter() { 627 return converter; 628 } 629 630 Connection getConnection() { 631 return connection; 632 } 633 /* 634 Private methods 635 */ 636 637 private void executeQuery(JDBCResult result, String sql, Object... parameters) throws SQLException { 638 if (sql.trim().toLowerCase().startsWith("select ")) { 639 if (metaData.getConnectionType() == ConnectionTypes.Firebird) { 640 // https://stackoverflow.com/questions/935511/how-can-i-avoid-resultset-is-closed-exception-in-java 641 result.st = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); 642 } else { 643 result.st = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); 644 } 645 646 PreparedStatement pst = (PreparedStatement) result.st; 647 setParameters(pst, parameters); 648 result.rs = pst.executeQuery(); 649 } else { 650 if (!sql.trim().toLowerCase().startsWith("{call")) { 651 sql = "{call " + sql + "} "; 652 } 653 // Don't need If Firebird here. Firebird would call a selectable stored proc with SELECT anyway 654 result.st = connection.prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); 655 656 CallableStatement cst = (CallableStatement) result.st; 657 setParameters(cst, parameters); 658 result.rs = cst.executeQuery(); 659 } 660 } 661 662 private void checkIfOkForWriteOperation(Object object, String operation) { 663 Class<?> objectClass = object.getClass(); 664 665 if (objectClass.getAnnotation(View.class) != null) { 666 throw new PersismException(Messages.OperationNotSupportedForView.message(objectClass, operation)); 667 } 668 if (objectClass.getAnnotation(NotTable.class) != null) { 669 throw new PersismException(Messages.OperationNotSupportedForNotTableQuery.message(objectClass, operation)); 670 } 671 if (Types.getType(objectClass) != null) { 672 throw new PersismException(Messages.OperationNotSupportedForJavaType.message(objectClass, operation)); 673 } 674 } 675 676 private <T> T getTypedValueReturnedFromGeneratedKeys(Class<T> objectClass, ResultSet rs) throws SQLException { 677 678 Object value = null; 679 Types type = Types.getType(objectClass); 680 681 if (type == null) { 682 log.warn(Messages.UnknownTypeForPrimaryGeneratedKey.message(objectClass)); 683 return (T) rs.getObject(1); 684 } 685 686 switch (type) { 687 688 case integerType: 689 case IntegerType: 690 value = rs.getInt(1); 691 break; 692 693 case longType: 694 case LongType: 695 value = rs.getLong(1); 696 break; 697 698 default: 699 value = rs.getObject(1); 700 } 701 return (T) value; 702 } 703 704 void setParameters(PreparedStatement st, Object[] parameters) throws SQLException { 705 if (log.isDebugEnabled()) { 706 log.debug("setParameters PARAMS: %s", Arrays.asList(parameters)); 707 } 708 709 int n = 1; 710 for (Object param : parameters) { 711 712 if (param != null) { 713 714 Types type = Types.getType(param.getClass()); 715 if (type == null) { 716 log.warn(Messages.UnknownTypeInSetParameters.message(param.getClass())); 717 type = Types.ObjectType; 718 } 719 720 Object value; // used for conversions 721 722 switch (type) { 723 724 case booleanType: 725 case BooleanType: 726 st.setBoolean(n, (Boolean) param); 727 break; 728 729 case byteType: 730 case ByteType: 731 st.setByte(n, (Byte) param); 732 break; 733 734 case shortType: 735 case ShortType: 736 st.setShort(n, (Short) param); 737 break; 738 739 case integerType: 740 case IntegerType: 741 st.setInt(n, (Integer) param); 742 break; 743 744 case longType: 745 case LongType: 746 st.setLong(n, (Long) param); 747 break; 748 749 case floatType: 750 case FloatType: 751 st.setFloat(n, (Float) param); 752 break; 753 754 case doubleType: 755 case DoubleType: 756 st.setDouble(n, (Double) param); 757 break; 758 759 case BigDecimalType: 760 st.setBigDecimal(n, (BigDecimal) param); 761 break; 762 763 case BigIntegerType: 764 st.setString(n, "" + param); 765 break; 766 767 case StringType: 768 st.setString(n, (String) param); 769 break; 770 771 case characterType: 772 case CharacterType: 773 st.setObject(n, "" + param); 774 break; 775 776 case SQLDateType: 777 st.setDate(n, (java.sql.Date) param); 778 break; 779 780 case TimeType: 781 st.setTime(n, (Time) param); 782 break; 783 784 case TimestampType: 785 st.setTimestamp(n, (Timestamp) param); 786 break; 787 788 case LocalTimeType: 789 value = converter.convert(param, Time.class, "Parameter " + n); 790 st.setObject(n, value); 791 break; 792 793 case UtilDateType: 794 case LocalDateType: 795 case LocalDateTimeType: 796 value = converter.convert(param, Timestamp.class, "Parameter " + n); 797 st.setObject(n, value); 798 break; 799 800 case OffsetDateTimeType: 801 case ZonedDateTimeType: 802 case InstantType: 803 log.warn(Messages.UnSupportedTypeInSetParameters.message(type)); 804 st.setObject(n, param); 805 // todo ZonedDateTime, OffsetDateTimeType and MAYBE Instant 806 break; 807 808 case byteArrayType: 809 case ByteArrayType: 810 // Blob maps to byte array 811 st.setBytes(n, (byte[]) param); 812 break; 813 814 case ClobType: 815 case BlobType: 816 // Clob is converted to String Blob is converted to byte array 817 // so this should not occur unless they were passed in by the user. 818 // We are most probably about to fail here. 819 log.warn(Messages.ParametersDoNotUseClobOrBlob.message(), new Throwable()); 820 st.setObject(n, param); 821 break; 822 823 case EnumType: 824 if (metaData.getConnectionType() == ConnectionTypes.PostgreSQL) { 825 st.setObject(n, param.toString(), java.sql.Types.OTHER); 826 } else { 827 st.setString(n, param.toString()); 828 } 829 break; 830 831 case UUIDType: 832 if (metaData.getConnectionType() == ConnectionTypes.PostgreSQL) { 833 // PostgreSQL does work with setObject but not setString unless you set the connection property stringtype=unspecified 834 st.setObject(n, param); 835 } else { 836 st.setString(n, param.toString()); 837 } 838 break; 839 840 default: 841 // Usually SQLite with util.date - setObject works 842 // Also if it's a custom non-standard type. 843 log.info("setParameters using setObject on parameter: " + n + " for " + param.getClass()); 844 st.setObject(n, param); 845 } 846 847 } else { 848 // param is null 849 st.setObject(n, param); 850 } 851 852 n++; 853 } 854 } 855 856 }