001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.dao.support; 018 019import java.util.Collection; 020 021import org.springframework.dao.DataAccessException; 022import org.springframework.dao.EmptyResultDataAccessException; 023import org.springframework.dao.IncorrectResultSizeDataAccessException; 024import org.springframework.dao.TypeMismatchDataAccessException; 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027import org.springframework.util.CollectionUtils; 028import org.springframework.util.NumberUtils; 029 030/** 031 * Miscellaneous utility methods for DAO implementations. 032 * Useful with any data access technology. 033 * 034 * @author Juergen Hoeller 035 * @since 1.0.2 036 */ 037public abstract class DataAccessUtils { 038 039 /** 040 * Return a single result object from the given Collection. 041 * <p>Returns {@code null} if 0 result objects found; 042 * throws an exception if more than 1 element found. 043 * @param results the result Collection (can be {@code null}) 044 * @return the single result object, or {@code null} if none 045 * @throws IncorrectResultSizeDataAccessException if more than one 046 * element has been found in the given Collection 047 */ 048 @Nullable 049 public static <T> T singleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException { 050 if (CollectionUtils.isEmpty(results)) { 051 return null; 052 } 053 if (results.size() > 1) { 054 throw new IncorrectResultSizeDataAccessException(1, results.size()); 055 } 056 return results.iterator().next(); 057 } 058 059 /** 060 * Return a single result object from the given Collection. 061 * <p>Throws an exception if 0 or more than 1 element found. 062 * @param results the result Collection (can be {@code null} 063 * but is not expected to contain {@code null} elements) 064 * @return the single result object 065 * @throws IncorrectResultSizeDataAccessException if more than one 066 * element has been found in the given Collection 067 * @throws EmptyResultDataAccessException if no element at all 068 * has been found in the given Collection 069 */ 070 public static <T> T requiredSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException { 071 if (CollectionUtils.isEmpty(results)) { 072 throw new EmptyResultDataAccessException(1); 073 } 074 if (results.size() > 1) { 075 throw new IncorrectResultSizeDataAccessException(1, results.size()); 076 } 077 return results.iterator().next(); 078 } 079 080 /** 081 * Return a single result object from the given Collection. 082 * <p>Throws an exception if 0 or more than 1 element found. 083 * @param results the result Collection (can be {@code null} 084 * and is also expected to contain {@code null} elements) 085 * @return the single result object 086 * @throws IncorrectResultSizeDataAccessException if more than one 087 * element has been found in the given Collection 088 * @throws EmptyResultDataAccessException if no element at all 089 * has been found in the given Collection 090 * @since 5.0.2 091 */ 092 @Nullable 093 public static <T> T nullableSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException { 094 // This is identical to the requiredSingleResult implementation but differs in the 095 // semantics of the incoming Collection (which we currently can't formally express) 096 if (CollectionUtils.isEmpty(results)) { 097 throw new EmptyResultDataAccessException(1); 098 } 099 if (results.size() > 1) { 100 throw new IncorrectResultSizeDataAccessException(1, results.size()); 101 } 102 return results.iterator().next(); 103 } 104 105 /** 106 * Return a unique result object from the given Collection. 107 * <p>Returns {@code null} if 0 result objects found; 108 * throws an exception if more than 1 instance found. 109 * @param results the result Collection (can be {@code null}) 110 * @return the unique result object, or {@code null} if none 111 * @throws IncorrectResultSizeDataAccessException if more than one 112 * result object has been found in the given Collection 113 * @see org.springframework.util.CollectionUtils#hasUniqueObject 114 */ 115 @Nullable 116 public static <T> T uniqueResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException { 117 if (CollectionUtils.isEmpty(results)) { 118 return null; 119 } 120 if (!CollectionUtils.hasUniqueObject(results)) { 121 throw new IncorrectResultSizeDataAccessException(1, results.size()); 122 } 123 return results.iterator().next(); 124 } 125 126 /** 127 * Return a unique result object from the given Collection. 128 * <p>Throws an exception if 0 or more than 1 instance found. 129 * @param results the result Collection (can be {@code null} 130 * but is not expected to contain {@code null} elements) 131 * @return the unique result object 132 * @throws IncorrectResultSizeDataAccessException if more than one 133 * result object has been found in the given Collection 134 * @throws EmptyResultDataAccessException if no result object at all 135 * has been found in the given Collection 136 * @see org.springframework.util.CollectionUtils#hasUniqueObject 137 */ 138 public static <T> T requiredUniqueResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException { 139 if (CollectionUtils.isEmpty(results)) { 140 throw new EmptyResultDataAccessException(1); 141 } 142 if (!CollectionUtils.hasUniqueObject(results)) { 143 throw new IncorrectResultSizeDataAccessException(1, results.size()); 144 } 145 return results.iterator().next(); 146 } 147 148 /** 149 * Return a unique result object from the given Collection. 150 * Throws an exception if 0 or more than 1 result objects found, 151 * of if the unique result object is not convertible to the 152 * specified required type. 153 * @param results the result Collection (can be {@code null} 154 * but is not expected to contain {@code null} elements) 155 * @return the unique result object 156 * @throws IncorrectResultSizeDataAccessException if more than one 157 * result object has been found in the given Collection 158 * @throws EmptyResultDataAccessException if no result object 159 * at all has been found in the given Collection 160 * @throws TypeMismatchDataAccessException if the unique object does 161 * not match the specified required type 162 */ 163 @SuppressWarnings("unchecked") 164 public static <T> T objectResult(@Nullable Collection<?> results, @Nullable Class<T> requiredType) 165 throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException { 166 167 Object result = requiredUniqueResult(results); 168 if (requiredType != null && !requiredType.isInstance(result)) { 169 if (String.class == requiredType) { 170 result = result.toString(); 171 } 172 else if (Number.class.isAssignableFrom(requiredType) && Number.class.isInstance(result)) { 173 try { 174 result = NumberUtils.convertNumberToTargetClass(((Number) result), (Class<? extends Number>) requiredType); 175 } 176 catch (IllegalArgumentException ex) { 177 throw new TypeMismatchDataAccessException(ex.getMessage()); 178 } 179 } 180 else { 181 throw new TypeMismatchDataAccessException( 182 "Result object is of type [" + result.getClass().getName() + 183 "] and could not be converted to required type [" + requiredType.getName() + "]"); 184 } 185 } 186 return (T) result; 187 } 188 189 /** 190 * Return a unique int result from the given Collection. 191 * Throws an exception if 0 or more than 1 result objects found, 192 * of if the unique result object is not convertible to an int. 193 * @param results the result Collection (can be {@code null} 194 * but is not expected to contain {@code null} elements) 195 * @return the unique int result 196 * @throws IncorrectResultSizeDataAccessException if more than one 197 * result object has been found in the given Collection 198 * @throws EmptyResultDataAccessException if no result object 199 * at all has been found in the given Collection 200 * @throws TypeMismatchDataAccessException if the unique object 201 * in the collection is not convertible to an int 202 */ 203 public static int intResult(@Nullable Collection<?> results) 204 throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException { 205 206 return objectResult(results, Number.class).intValue(); 207 } 208 209 /** 210 * Return a unique long result from the given Collection. 211 * Throws an exception if 0 or more than 1 result objects found, 212 * of if the unique result object is not convertible to a long. 213 * @param results the result Collection (can be {@code null} 214 * but is not expected to contain {@code null} elements) 215 * @return the unique long result 216 * @throws IncorrectResultSizeDataAccessException if more than one 217 * result object has been found in the given Collection 218 * @throws EmptyResultDataAccessException if no result object 219 * at all has been found in the given Collection 220 * @throws TypeMismatchDataAccessException if the unique object 221 * in the collection is not convertible to a long 222 */ 223 public static long longResult(@Nullable Collection<?> results) 224 throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException { 225 226 return objectResult(results, Number.class).longValue(); 227 } 228 229 230 /** 231 * Return a translated exception if this is appropriate, 232 * otherwise return the given exception as-is. 233 * @param rawException an exception that we may wish to translate 234 * @param pet the PersistenceExceptionTranslator to use to perform the translation 235 * @return a translated persistence exception if translation is possible, 236 * or the raw exception if it is not 237 */ 238 public static RuntimeException translateIfNecessary( 239 RuntimeException rawException, PersistenceExceptionTranslator pet) { 240 241 Assert.notNull(pet, "PersistenceExceptionTranslator must not be null"); 242 DataAccessException dae = pet.translateExceptionIfPossible(rawException); 243 return (dae != null ? dae : rawException); 244 } 245 246}