389 * convenience methods do so.</p> |
390 * convenience methods do so.</p> |
390 * |
391 * |
391 * The type argument {@link S} of the containing {@link OperationGroup} must |
392 * The type argument {@link S} of the containing {@link OperationGroup} must |
392 * be a supertype of {@link TransactionOutcome}. |
393 * be a supertype of {@link TransactionOutcome}. |
393 * |
394 * |
394 * @param trans the TransactionEnd that determines whether the Operation does a |
395 * @param trans the TransactionCompletion that determines whether the Operation does a |
395 * database commit or a database rollback. |
396 database commit or a database rollback. |
396 * @return an {@link Operation} that will end the database transaction. |
397 * @return an {@link Operation} that will end the database transaction. |
397 * @throws IllegalStateException if this {@link OperationGroup} has been |
398 * @throws IllegalStateException if this {@link OperationGroup} has been |
398 * submitted and is not held or is parallel. |
399 * submitted and is not held or is parallel. |
399 */ |
400 */ |
400 public Operation<TransactionOutcome> endTransactionOperation(TransactionEnd trans); |
401 public Operation<TransactionOutcome> endTransactionOperation(TransactionCompletion trans); |
401 |
402 |
402 /** |
403 /** |
403 * Convenience method that creates and submits a endTransaction |
404 * Convenience method that creates and submits a endTransaction |
404 * {@link Operation} that commits by default but can be set to rollback by |
405 * {@link Operation} that commits by default but can be set to rollback by |
405 * calling {@link TransactionEnd#setRollbackOnly}. The endTransaction Operation |
406 * calling {@link TransactionCompletion#setRollbackOnly}. The endTransaction Operation |
406 * is never skipped. |
407 * is never skipped. |
407 * |
408 * |
408 * @param trans the TransactionEnd that determines whether the {@link Operation} is a |
409 * @param trans the TransactionCompletion that determines whether the {@link Operation} is a |
409 * database commit or a database rollback. |
410 * database commit or a database rollback. |
410 * @return a {@link CompletionStage} that is completed with the outcome of the |
411 * @return a {@link CompletionStage} that is completed with the outcome of the |
411 * transaction |
412 * transaction |
412 * @throws IllegalStateException if this {@link OperationGroup} has been |
413 * @throws IllegalStateException if this {@link OperationGroup} has been |
413 * submitted and is not held or is parallel. |
414 * submitted and is not held or is parallel. |
414 */ |
415 */ |
415 public default CompletionStage<TransactionOutcome> commitMaybeRollback(TransactionEnd trans) { |
416 public default CompletionStage<TransactionOutcome> commitMaybeRollback(TransactionCompletion trans) { |
416 catchErrors(); |
417 catchErrors(); |
417 return this.endTransactionOperation(trans).submit().getCompletionStage(); |
418 return this.endTransactionOperation(trans).submit().getCompletionStage(); |
418 } |
419 } |
419 |
420 |
420 /** |
421 /** |
459 * @param logger used by the implementation to log significant events |
460 * @param logger used by the implementation to log significant events |
460 * @return this {@link OperationGroup} |
461 * @return this {@link OperationGroup} |
461 */ |
462 */ |
462 public OperationGroup<S, T> logger(Logger logger); |
463 public OperationGroup<S, T> logger(Logger logger); |
463 |
464 |
464 // Covariant overrides |
465 /** |
|
466 * Returns a {@code String} enclosed in single quotes. Any occurrence of a |
|
467 * single quote within the string will be replaced by two single quotes. |
|
468 * |
|
469 * <blockquote> |
|
470 * <table class="striped"> |
|
471 * <caption>Examples of the conversion:</caption> |
|
472 * <thead> |
|
473 * <tr><th scope="col">Value</th><th scope="col">Result</th></tr> |
|
474 * </thead> |
|
475 * <tbody style="text-align:center"> |
|
476 * <tr> <th scope="row">Hello</th> <td>'Hello'</td> </tr> |
|
477 * <tr> <th scope="row">G'Day</th> <td>'G''Day'</td> </tr> |
|
478 * <tr> <th scope="row">'G''Day'</th> |
|
479 * <td>'''G''''Day'''</td> </tr> |
|
480 * <tr> <th scope="row">I'''M</th> <td>'I''''''M'</td> |
|
481 * </tr> |
|
482 * |
|
483 * </tbody> |
|
484 * </table> |
|
485 * </blockquote> |
|
486 * |
|
487 * @implNote JDBC driver implementations may need to provide their own |
|
488 * implementation of this method in order to meet the requirements of the |
|
489 * underlying datasource. |
|
490 * @param val a character string. Not null |
|
491 * @return A string enclosed by single quotes with every single quote |
|
492 * converted to two single quotes. Not null |
|
493 * @throws NullPointerException if {@code val} is {@code null} |
|
494 * @throws IllegalArgumentException if {@code val} cannot be enquoted |
|
495 */ |
|
496 default String enquoteLiteral(String val) { |
|
497 return "'" + val.replace("'", "''") + "'"; |
|
498 } |
|
499 |
|
500 /** |
|
501 * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier: |
|
502 * <ul> |
|
503 * <li>Return the original value if {@code alwaysQuote} is {@code false}</li> |
|
504 * <li>Return a delimited identifier if {@code alwaysQuote} is |
|
505 * {@code true}</li> |
|
506 * </ul> |
|
507 * |
|
508 * If {@code identifier} is not a simple SQL identifier, {@code identifier} |
|
509 * will be enclosed in double quotes if not already present. If the datasource |
|
510 * does not support double quotes for delimited identifiers, the identifier |
|
511 * should be enclosed by the string returned from |
|
512 * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource does |
|
513 * not support delimited identifiers, a |
|
514 * {@code SQLFeatureNotSupportedException} should be thrown. |
|
515 * <p> |
|
516 * A {@code SQLException} will be thrown if {@code identifier} contains any |
|
517 * characters invalid in a delimited identifier or the identifier length is |
|
518 * invalid for the datasource. |
|
519 * |
|
520 * @implSpec The default implementation uses the following criteria to |
|
521 * determine a valid simple SQL identifier: |
|
522 * <ul> |
|
523 * <li>The string is not enclosed in double quotes</li> |
|
524 * <li>The first character is an alphabetic character from a through z, or |
|
525 * from A through Z</li> |
|
526 * <li>The name only contains alphanumeric characters or the character |
|
527 * "_"</li> |
|
528 * </ul> |
|
529 * |
|
530 * The default implementation will throw a {@code SQLException} if: |
|
531 * <ul> |
|
532 * <li>{@code identifier} contains a {@code null} character or double quote |
|
533 * and is not a simple SQL identifier.</li> |
|
534 * <li>The length of {@code identifier} is less than 1 or greater than 128 |
|
535 * characters |
|
536 * </ul> |
|
537 * <blockquote> |
|
538 * <table class="striped" > |
|
539 * <caption>Examples of the conversion:</caption> |
|
540 * <thead> |
|
541 * <tr> |
|
542 * <th scope="col">identifier</th> |
|
543 * <th scope="col">alwaysQuote</th> |
|
544 * <th scope="col">Result</th></tr> |
|
545 * </thead> |
|
546 * <tbody> |
|
547 * <tr> |
|
548 * <th scope="row">Hello</th> |
|
549 * <td>false</td> |
|
550 * <td>Hello</td> |
|
551 * </tr> |
|
552 * <tr> |
|
553 * <th scope="row">Hello</th> |
|
554 * <td>true</td> |
|
555 * <td>"Hello"</td> |
|
556 * </tr> |
|
557 * <tr> |
|
558 * <th scope="row">G'Day</th> |
|
559 * <td>false</td> |
|
560 * <td>"G'Day"</td> |
|
561 * </tr> |
|
562 * <tr> |
|
563 * <th scope="row">"Bruce Wayne"</th> |
|
564 * <td>false</td> |
|
565 * <td>"Bruce Wayne"</td> |
|
566 * </tr> |
|
567 * <tr> |
|
568 * <th scope="row">"Bruce Wayne"</th> |
|
569 * <td>true</td> |
|
570 * <td>"Bruce Wayne"</td> |
|
571 * </tr> |
|
572 * <tr> |
|
573 * <th scope="row">GoodDay$</th> |
|
574 * <td>false</td> |
|
575 * <td>"GoodDay$"</td> |
|
576 * </tr> |
|
577 * <tr> |
|
578 * <th scope="row">Hello"World</th> |
|
579 * <td>false</td> |
|
580 * <td>SQLException</td> |
|
581 * </tr> |
|
582 * <tr> |
|
583 * <th scope="row">"Hello"World"</th> |
|
584 * <td>false</td> |
|
585 * <td>SQLException</td> |
|
586 * </tr> |
|
587 * </tbody> |
|
588 * </table> |
|
589 * </blockquote> |
|
590 * @implNote JDBC driver implementations may need to provide their own |
|
591 * implementation of this method in order to meet the requirements of the |
|
592 * underlying datasource. |
|
593 * @param identifier a SQL identifier. Not null |
|
594 * @param alwaysQuote indicates if a simple SQL identifier should be returned |
|
595 * as a quoted identifier |
|
596 * @return A simple SQL identifier or a delimited identifier. Not null |
|
597 * @throws NullPointerException if identifier is {@code null} |
|
598 * @throws IllegalArgumentException if {@code identifier} can not be converted |
|
599 * to a valid identifier |
|
600 */ |
|
601 default String enquoteIdentifier(String identifier, boolean alwaysQuote) { |
|
602 int len = identifier.length(); |
|
603 if (len < 1 || len > 128) { |
|
604 throw new IllegalArgumentException("Invalid name"); |
|
605 } |
|
606 if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches()) { |
|
607 return alwaysQuote ? "\"" + identifier + "\"" : identifier; |
|
608 } |
|
609 if (identifier.matches("^\".+\"$")) { |
|
610 identifier = identifier.substring(1, len - 1); |
|
611 } |
|
612 if (Pattern.compile("[^\u0000\"]+").matcher(identifier).matches()) { |
|
613 return "\"" + identifier + "\""; |
|
614 } |
|
615 else { |
|
616 throw new IllegalArgumentException("Invalid name"); |
|
617 } |
|
618 } |
|
619 |
|
620 /** |
|
621 * Retrieves whether {@code identifier} is a simple SQL identifier. |
|
622 * |
|
623 * @implSpec The default implementation uses the following criteria to |
|
624 * determine a valid simple SQL identifier: |
|
625 * <ul> |
|
626 * <li>The string is not enclosed in double quotes</li> |
|
627 * <li>The first character is an alphabetic character from a through z, or |
|
628 * from A through Z</li> |
|
629 * <li>The string only contains alphanumeric characters or the character |
|
630 * "_"</li> |
|
631 * <li>The string is between 1 and 128 characters in length inclusive</li> |
|
632 * </ul> |
|
633 * |
|
634 * <blockquote> |
|
635 * <table class="striped" > |
|
636 * <caption>Examples of the conversion:</caption> |
|
637 * <thead> |
|
638 * <tr> |
|
639 * <th scope="col">identifier</th> |
|
640 * <th scope="col">Simple Identifier</th> |
|
641 * </thead> |
|
642 * |
|
643 * <tbody> |
|
644 * <tr> |
|
645 * <th scope="row">Hello</th> |
|
646 * <td>true</td> |
|
647 * </tr> |
|
648 * <tr> |
|
649 * <th scope="row">G'Day</th> |
|
650 * <td>false</td> |
|
651 * </tr> |
|
652 * <tr> |
|
653 * <th scope="row">"Bruce Wayne"</th> |
|
654 * <td>false</td> |
|
655 * </tr> |
|
656 * <tr> |
|
657 * <th scope="row">GoodDay$</th> |
|
658 * <td>false</td> |
|
659 * </tr> |
|
660 * <tr> |
|
661 * <th scope="row">Hello"World</th> |
|
662 * <td>false</td> |
|
663 * </tr> |
|
664 * <tr> |
|
665 * <th scope="row">"Hello"World"</th> |
|
666 * <td>false</td> |
|
667 * </tr> |
|
668 * </tbody> |
|
669 * </table> |
|
670 * </blockquote> |
|
671 * @implNote JDBC driver implementations may need to provide their own |
|
672 * implementation of this method in order to meet the requirements of the |
|
673 * underlying datasource. |
|
674 * @param identifier a SQL identifier. Not null |
|
675 * @return true if a simple SQL identifier, false otherwise |
|
676 * @throws NullPointerException if identifier is {@code null} |
|
677 */ |
|
678 default boolean isSimpleIdentifier(String identifier) { |
|
679 int len = identifier.length(); |
|
680 return len >= 1 && len <= 128 |
|
681 && Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches(); |
|
682 } |
|
683 |
|
684 /** |
|
685 * Returns a {@code String} representing a National Character Set Literal |
|
686 * enclosed in single quotes and prefixed with a upper case letter N. Any |
|
687 * occurrence of a single quote within the string will be replaced by two |
|
688 * single quotes. |
|
689 * |
|
690 * <blockquote> |
|
691 * <table class="striped"> |
|
692 * <caption>Examples of the conversion:</caption> |
|
693 * <thead> |
|
694 * <tr> |
|
695 * <th scope="col">Value</th> |
|
696 * <th scope="col">Result</th> |
|
697 * </tr> |
|
698 * </thead> |
|
699 * <tbody> |
|
700 * <tr> <th scope="row">Hello</th> <td>N'Hello'</td> </tr> |
|
701 * <tr> <th scope="row">G'Day</th> <td>N'G''Day'</td> </tr> |
|
702 * <tr> <th scope="row">'G''Day'</th> |
|
703 * <td>N'''G''''Day'''</td> </tr> |
|
704 * <tr> <th scope="row">I'''M</th> <td>N'I''''''M'</td> |
|
705 * <tr> <th scope="row">N'Hello'</th> <td>N'N''Hello'''</td> </tr> |
|
706 * |
|
707 * </tbody> |
|
708 * </table> |
|
709 * </blockquote> |
|
710 * |
|
711 * @implNote JDBC driver implementations may need to provide their own |
|
712 * implementation of this method in order to meet the requirements of the |
|
713 * underlying datasource. An implementation of enquoteNCharLiteral may accept |
|
714 * a different set of characters than that accepted by the same drivers |
|
715 * implementation of enquoteLiteral. |
|
716 * @param val a character string. Not null |
|
717 * @return the result of replacing every single quote character in the |
|
718 * argument by two single quote characters where this entire result is then |
|
719 * prefixed with 'N'. Not null. |
|
720 * @throws NullPointerException if {@code val} is {@code null} |
|
721 * @throws IllegalArgumentException if {@code val} cannot be enquoted |
|
722 */ |
|
723 default String enquoteNCharLiteral(String val) { |
|
724 return "N'" + val.replace("'", "''") + "'"; |
|
725 } |
|
726 |
|
727 // Covariant overrides |
465 @Override |
728 @Override |
466 public OperationGroup<S, T> timeout(Duration minTime); |
729 public OperationGroup<S, T> timeout(Duration minTime); |
467 |
730 |
468 @Override |
731 @Override |
469 public OperationGroup<S, T> onError(Consumer<Throwable> handler); |
732 public OperationGroup<S, T> onError(Consumer<Throwable> handler); |