View Javadoc
1   /*
2    * Copyright (c) 2012-2023, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.jdbc;
31  
32  import java.sql.ResultSet;
33  import java.sql.SQLException;
34  import java.sql.Statement;
35  import java.util.Date;
36  import java.util.UUID;
37  import lombok.EqualsAndHashCode;
38  import lombok.RequiredArgsConstructor;
39  import lombok.ToString;
40  
41  /**
42   * Outcome that returns first column in the first row.
43   *
44   * <p>Use it when you need the first column in the first row:
45   *
46   * <pre> Long id = new JdbcSession(source)
47   *   .sql("SELECT id FROM user WHERE name = ?")
48   *   .set("Jeff Lebowski")
49   *   .select(new SingleOutcome&lt;Long&gt;(Long.class));</pre>
50   *
51   * <p>Supported types are: {@link String}, {@link Long}, {@link Boolean},
52   * {@link Byte}, {@link Date}, {@link UUID}, and {@link Utc}.
53   *
54   * <p>By default, the outcome throws {@link SQLException} if no records
55   * are found in the {@link ResultSet}. You can change this behavior by using
56   * a two-arguments constructor ({@code null} will be returned if
57   * {@link ResultSet} is empty):
58   *
59   * <pre> String name = new JdbcSession(source)
60   *   .sql("SELECT name FROM user WHERE id = ?")
61   *   .set(555)
62   *   .select(new SingleOutcome&lt;Long&gt;(Long.class), true);
63   * if (name == null) {
64   *   // such a record wasn't found in the database
65   * }</pre>
66   *
67   * @param <T> Type of items
68   * @since 0.1.8
69   */
70  @ToString
71  @EqualsAndHashCode(of = {"mapping", "silently"})
72  @RequiredArgsConstructor
73  public final class SingleOutcome<T> implements Outcome<T> {
74  
75      /**
76       * The type.
77       */
78      private final Mapping<? extends T> mapping;
79  
80      /**
81       * Silently return NULL if no row found.
82       */
83      private final boolean silently;
84  
85      /**
86       * Public ctor.
87       *
88       * @param tpe The type to convert to
89       */
90      public SingleOutcome(final Class<T> tpe) {
91          this(tpe, false);
92      }
93  
94      /**
95       * Public ctor.
96       *
97       * @param tpe The type to convert to
98       * @param slnt Silently return NULL if there is no row
99       */
100     @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors")
101     public SingleOutcome(final Class<T> tpe, final boolean slnt) {
102         this(
103             tpe,
104             Outcome.DEFAULT_MAPPINGS,
105             slnt
106         );
107     }
108 
109     /**
110      * Public ctor.
111      *
112      * @param tpe The type to convert to
113      * @param mps The mappings
114      * @param slnt Silently return NULL if there is no row
115      */
116     public SingleOutcome(final Class<T> tpe, final Mappings mps, final boolean slnt) {
117         this(mps.forType(tpe), slnt);
118     }
119 
120     @Override
121     public T handle(final ResultSet rset, final Statement stmt)
122         throws SQLException {
123         T result = null;
124         if (rset.next()) {
125             result = this.fetch(rset);
126         } else if (!this.silently) {
127             throw new SQLException("No records found");
128         }
129         return result;
130     }
131 
132     /**
133      * Fetch the value from result set.
134      *
135      * @param rset Result set
136      * @return The result
137      * @throws SQLException If some error inside
138      */
139     private T fetch(final ResultSet rset) throws SQLException {
140         return this.mapping.map(rset);
141     }
142 }