Coverage Report - com.jcabi.jdbc.SingleOutcome
 
Classes in this File Line Coverage Branch Coverage Complexity
SingleOutcome
51%
19/37
16%
10/62
6.25
SingleOutcome$AjcClosure1
100%
1/1
N/A
6.25
 
 1  2
 /**
 2  
  * Copyright (c) 2012-2015, 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 com.jcabi.aspects.Immutable;
 33  
 import com.jcabi.aspects.Loggable;
 34  
 import java.sql.ResultSet;
 35  
 import java.sql.SQLException;
 36  
 import java.sql.Statement;
 37  
 import java.util.Date;
 38  
 import lombok.EqualsAndHashCode;
 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}, 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  
  * @author Yegor Bugayenko (yegor@teamed.io)
 68  
  * @version $Id: 9599c667333df25f925aff89825d2ae9a234f614 $
 69  
  * @since 0.1.8
 70  
  * @param <T> Type of items
 71  
  */
 72  
 @Immutable
 73  1
 @ToString
 74  0
 @EqualsAndHashCode(of = { "type", "silently" })
 75  
 public final class SingleOutcome<T> implements Outcome<T> {
 76  
 
 77  
     /**
 78  
      * The type name.
 79  
      */
 80  
     private final transient String type;
 81  
 
 82  
     /**
 83  
      * Silently return NULL if no row found.
 84  
      */
 85  
     private final transient boolean silently;
 86  
 
 87  
     /**
 88  
      * Public ctor.
 89  
      * @param tpe The type to convert to
 90  
      */
 91  
     public SingleOutcome(final Class<T> tpe) {
 92  3
         this(tpe, false);
 93  1
     }
 94  
 
 95  
     /**
 96  
      * Public ctor.
 97  
      * @param tpe The type to convert to
 98  
      * @param slnt Silently return NULL if there is no row
 99  
      */
 100  3
     public SingleOutcome(final Class<T> tpe, final boolean slnt) {
 101  
         //@checkstyle BooleanExpressionComplexity (3 lines)
 102  3
         if (tpe.equals(String.class) || tpe.equals(Long.class)
 103  
             || tpe.equals(Boolean.class) || tpe.equals(Byte.class)
 104  
             || tpe.equals(Date.class) || tpe.equals(Utc.class)
 105  
             || byte[].class.equals(tpe)
 106  
         ) {
 107  1
             this.type = tpe.getName();
 108  
         } else {
 109  2
             throw new IllegalArgumentException(
 110  
                 String.format("type %s is not supported", tpe.getName())
 111  
             );
 112  
         }
 113  1
         this.silently = slnt;
 114  1
     }
 115  
 
 116  
     @Override
 117  
     @Loggable(Loggable.DEBUG)
 118  
     public T handle(final ResultSet rset, final Statement stmt)
 119  
         throws SQLException {
 120  2
         T result = null;
 121  1
         if (rset.next()) {
 122  1
             result = this.fetch(rset);
 123  0
         } else if (!this.silently) {
 124  0
             throw new SQLException("no records found");
 125  
         }
 126  1
         return result;
 127  
     }
 128  
 
 129  
     /**
 130  
      * Fetch the value from result set.
 131  
      * @param rset Result set
 132  
      * @return The result
 133  
      * @throws SQLException If some error inside
 134  
      */
 135  
     @SuppressWarnings("unchecked")
 136  
     private T fetch(final ResultSet rset) throws SQLException {
 137  
         final Object result;
 138  
         Class<T> tpe;
 139  
         try {
 140  1
             tpe = (Class<T>) Class.forName(this.type);
 141  1
             if (tpe.equals(String.class)) {
 142  1
                 result = rset.getString(1);
 143  0
             } else if (tpe.equals(Long.class)) {
 144  0
                 result = rset.getLong(1);
 145  0
             } else if (tpe.equals(Boolean.class)) {
 146  0
                 result = rset.getBoolean(1);
 147  0
             } else if (tpe.equals(Byte.class)) {
 148  0
                 result = rset.getByte(1);
 149  0
             } else if (tpe.equals(Date.class)) {
 150  0
                 result = rset.getDate(1);
 151  0
             } else if (tpe.equals(Utc.class)) {
 152  0
                 result = new Utc(Utc.getTimestamp(rset, 1));
 153  0
             } else if (byte[].class.equals(tpe)) {
 154  0
                 result = rset.getBytes(1);
 155  
             } else {
 156  0
                 throw new IllegalStateException(
 157  
                     String.format("type %s is not allowed", tpe.getName())
 158  
                 );
 159  
             }
 160  0
         } catch (final ClassNotFoundException ex) {
 161  0
             throw new IllegalArgumentException(
 162  
                 String.format("Unknown type: %s", this.type), ex
 163  
             );
 164  1
         }
 165  1
         return tpe.cast(result);
 166  
     }
 167  
 
 168  
 }