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}