|
28 | 28 | import java.sql.PreparedStatement; |
29 | 29 | import java.sql.Statement; |
30 | 30 | import java.sql.Types; |
| 31 | +import java.time.LocalDateTime; |
| 32 | +import java.time.format.DateTimeFormatter; |
| 33 | +import java.time.format.DateTimeParseException; |
31 | 34 | import java.util.ArrayList; |
32 | 35 | import java.util.Date; |
33 | 36 | import java.util.HashMap; |
@@ -243,6 +246,52 @@ public PreparedStatement createFindByParamsStatementWithLimit(final Connection c |
243 | 246 | return prepareStatement; |
244 | 247 | } |
245 | 248 |
|
| 249 | + public String getColumnSQLName(final Class<? extends AbstractEntity> entityClass, |
| 250 | + final String column) { |
| 251 | + final String tableName = entityMappingHolder.tableToEntityNameMap.inverse() |
| 252 | + .get(entityClass.getSimpleName()); |
| 253 | + final BiMap<String, String> entityNameToDBNameMapping = |
| 254 | + entityMappingHolder.columnMappingPerTable.get(tableName).inverse(); |
| 255 | + |
| 256 | + final String columnName = entityNameToDBNameMapping.get(column); |
| 257 | + checkNotNull(columnName, String |
| 258 | + .format("Found field '%s' but expected %s", column, |
| 259 | + entityNameToDBNameMapping.keySet())); |
| 260 | + |
| 261 | + return columnName; |
| 262 | + } |
| 263 | + |
| 264 | + public String createFindColumnByParamsStatementWithLimitQuery( |
| 265 | + final Class<? extends AbstractEntity> entityClass, final String column, |
| 266 | + final Predicate predicate, final Long limit, final Long offset) { |
| 267 | + final String tableName = entityMappingHolder.tableToEntityNameMap.inverse() |
| 268 | + .get(entityClass.getSimpleName()); |
| 269 | + final BiMap<String, String> entityNameToDBNameMapping = |
| 270 | + entityMappingHolder.columnMappingPerTable.get(tableName).inverse(); |
| 271 | + |
| 272 | + final String columnName = entityNameToDBNameMapping.get(column); |
| 273 | + checkNotNull(columnName, String |
| 274 | + .format("Found field '%s' but expected %s", column, |
| 275 | + entityNameToDBNameMapping.keySet())); |
| 276 | + |
| 277 | + final StringBuilder sqlBuilder = new StringBuilder(String.format( |
| 278 | + "SELECT %s FROM %s", columnName, tableName)); |
| 279 | + |
| 280 | + if(predicate != null) { |
| 281 | + final StringBuilder whereClause = new StringBuilder(" WHERE "); |
| 282 | + createWhereClause(entityNameToDBNameMapping, predicate, whereClause); |
| 283 | + sqlBuilder.append(whereClause); |
| 284 | + } |
| 285 | + if (limit != null) { |
| 286 | + sqlBuilder.append(" LIMIT ").append(limit); |
| 287 | + } |
| 288 | + if (offset != null) { |
| 289 | + sqlBuilder.append(" OFFSET ").append(offset); |
| 290 | + } |
| 291 | + return sqlBuilder.toString(); |
| 292 | + } |
| 293 | + |
| 294 | + |
246 | 295 | public PreparedStatement createCountStatement(final Connection connection, final @Nullable Predicate predicate, |
247 | 296 | final Class<? extends AbstractEntity> entityClass) throws Exception { |
248 | 297 | final String tableName = |
@@ -363,12 +412,137 @@ private void generateWhereClause(final BiMap<String, String> entityNameToDBNameM |
363 | 412 | } |
364 | 413 | } |
365 | 414 |
|
| 415 | + private void createWhereClause(final BiMap<String, String> entityNameToDBNameMapping, |
| 416 | + final Predicate predicate, final StringBuilder whereClause) { |
| 417 | + String columnName = null; |
| 418 | + |
| 419 | + if (predicate.getLhs() != null) { |
| 420 | + columnName = entityNameToDBNameMapping.get(predicate.getLhs()); |
| 421 | + checkNotNull(columnName, String |
| 422 | + .format("Found field '%s' but expected %s", predicate.getLhs(), |
| 423 | + entityNameToDBNameMapping.keySet())); |
| 424 | + } |
| 425 | + |
| 426 | + switch (predicate.getOper()) { |
| 427 | + case AND: |
| 428 | + case OR: |
| 429 | + whereClause.append("("); |
| 430 | + String delim = ""; |
| 431 | + for (final Predicate childPredicate : predicate.getChildPredicates()) { |
| 432 | + whereClause.append(delim); |
| 433 | + createWhereClause(entityNameToDBNameMapping, childPredicate, whereClause); |
| 434 | + delim = " " + predicate.getOper().toString() + " "; |
| 435 | + } |
| 436 | + whereClause.append(")"); |
| 437 | + break; |
| 438 | + case EQ: |
| 439 | + if (predicate.getRhs() == null) { |
| 440 | + whereClause.append(columnName).append(" IS NULL "); |
| 441 | + } else { |
| 442 | + // duplicated code with NEQ and LIKE/GT/GE/... - ok for the moment, this needs to be migrated to JOOQ anyway |
| 443 | + whereClause.append(columnName).append(" ").append(predicate.getOper().toString()) |
| 444 | + .append(" ").append(getPredicateValStr(predicate.getRhs())); |
| 445 | + } |
| 446 | + break; |
| 447 | + case NEQ: |
| 448 | + if (predicate.getRhs() == null) { |
| 449 | + whereClause.append(columnName).append(" IS NOT NULL "); |
| 450 | + } else { |
| 451 | + whereClause.append(columnName).append(" ").append(predicate.getOper().toString()) |
| 452 | + .append(" ").append(getPredicateValStr(predicate.getRhs())); |
| 453 | + } |
| 454 | + break; |
| 455 | + case LIKE: |
| 456 | + case GT: |
| 457 | + case LT: |
| 458 | + case LE: |
| 459 | + case GE: |
| 460 | + whereClause.append(columnName).append(" ").append(predicate.getOper().toString()) |
| 461 | + .append(" ").append(getPredicateValStr(predicate.getRhs())); |
| 462 | + break; |
| 463 | + case IN: |
| 464 | + Object rhs = predicate.getRhs(); |
| 465 | + if (rhs != null) { |
| 466 | + if (!rhs.getClass().isArray()) { |
| 467 | + rhs = rhs.toString().split(","); |
| 468 | + } |
| 469 | + whereClause.append(columnName).append(" ").append(Predicate.OPER.IN) |
| 470 | + .append("("); |
| 471 | + delim = ""; |
| 472 | + final int length = Array.getLength(rhs); |
| 473 | + if (length > 0) { |
| 474 | + for (int i = 0; i < length; i++) { |
| 475 | + whereClause.append(delim).append(getPredicateValStr(Array.get(rhs, i))); |
| 476 | + delim = ","; |
| 477 | + } |
| 478 | + } else { |
| 479 | + whereClause.append("null"); |
| 480 | + } |
| 481 | + whereClause.append(")"); |
| 482 | + } |
| 483 | + break; |
| 484 | + case BETWEEN: |
| 485 | + final ImmutablePair<Object, Object> pair = (ImmutablePair<Object, Object>) predicate.getRhs(); |
| 486 | + whereClause.append(columnName).append(predicate.getOper().toString()) |
| 487 | + .append(getPredicateValStr(pair.getLeft())) |
| 488 | + .append(" AND ") |
| 489 | + .append(getPredicateValStr(pair.getRight())); |
| 490 | + break; |
| 491 | + default: |
| 492 | + throw new RuntimeException("Unsupported predicate type:" + predicate.getOper()); |
| 493 | + } |
| 494 | + } |
| 495 | + |
| 496 | + private String getPredicateValStr(Object val) { |
| 497 | + if (checkIfValidBoolean(val)) { |
| 498 | + return val.toString(); |
| 499 | + } |
| 500 | + else if (val instanceof String || checkIfValidDateTime(val)) { |
| 501 | + return "'" + val + "'"; |
| 502 | + } else { |
| 503 | + return val.toString(); |
| 504 | + } |
| 505 | + } |
| 506 | + |
| 507 | + private boolean checkIfValidDateTime(Object val) { |
| 508 | + // List of possible patterns |
| 509 | + final String[] patterns = { |
| 510 | + "yyyy-MM-dd HH:mm:ss.SSS", // e.g. 2025-02-17 17:45:06.493 |
| 511 | + "yyyy-MM-dd HH:mm:ss.SS", // e.g. 2020-02-17 23:30:00.28 |
| 512 | + "yyyy-MM-dd HH:mm:ss.S", // e.g. 2020-02-17 23:30:00.0 |
| 513 | + "yyyy-MM-dd HH:mm:ss", // e.g. 2025-02-17 17:45:06 |
| 514 | + "yyyy-MM-dd'T'HH:mm:ss.SSS", // e.g. 2025-02-17T17:45:06.493 |
| 515 | + "yyyy-MM-dd'T'HH:mm:ss.SS", // e.g. 2025-02-17T17:45:06.49 |
| 516 | + "yyyy-MM-dd'T'HH:mm:ss.S", // e.g. 2020-02-17T23:30:00.0 |
| 517 | + "yyyy-MM-dd'T'HH:mm:ss", // e.g. 2025-02-17T17:45:06 |
| 518 | + "yyyy/MM/dd HH:mm:ss", // e.g. 2025/02/17 17:45:06 |
| 519 | + "yyyy/MM/dd", // e.g. 2025/02/17 |
| 520 | + "MM/dd/yyyy HH:mm:ss" // e.g. 02/17/2025 17:45:06 |
| 521 | + }; |
| 522 | + |
| 523 | + for (String pattern : patterns) { |
| 524 | + try { |
| 525 | + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); |
| 526 | + LocalDateTime.parse(val.toString(), formatter); |
| 527 | + return true; // If parsing is successful with any pattern |
| 528 | + } catch (DateTimeParseException e) { |
| 529 | + // If parsing fails, continue with next pattern |
| 530 | + } |
| 531 | + } |
| 532 | + |
| 533 | + return false; |
| 534 | + } |
| 535 | + |
| 536 | + private boolean checkIfValidBoolean(Object val) { |
| 537 | + return "true".equalsIgnoreCase(val.toString()) || "false".equalsIgnoreCase(val.toString()); |
| 538 | + } |
| 539 | + |
366 | 540 | public PreparedStatement createStatementFromSQL(final Connection connection, String parameterizedSQL, |
367 | 541 | final Map<String, Object> parameterMap, final Class<? extends AbstractEntity> entityClass) |
368 | 542 | throws Exception { |
369 | 543 | final String tableName = |
370 | 544 | entityMappingHolder.tableToEntityNameMap.inverse().get(entityClass.getSimpleName()); |
371 | | - parameterizedSQL = "select * from " + tableName + " " + parameterizedSQL; |
| 545 | + parameterizedSQL = "select " + tableName + ".* from " + tableName + " " + parameterizedSQL; |
372 | 546 | parameterizedSQL = parameterizedSQL.replace(entityClass.getSimpleName(), tableName); |
373 | 547 | final StringBuilder psSql = new StringBuilder(); |
374 | 548 | final List<String> paramNames = new ArrayList<>(); |
|
0 commit comments