001/*
002 * Copyright 2002-2017 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.util.Assert;
026import org.springframework.util.CollectionUtils;
027import org.springframework.util.NumberUtils;
028
029/**
030 * Miscellaneous utility methods for DAO implementations.
031 * Useful with any data access technology.
032 *
033 * @author Juergen Hoeller
034 * @since 1.0.2
035 */
036public abstract class DataAccessUtils {
037
038        /**
039         * Return a single result object from the given Collection.
040         * <p>Returns {@code null} if 0 result objects found;
041         * throws an exception if more than 1 element found.
042         * @param results the result Collection (can be {@code null})
043         * @return the single result object, or {@code null} if none
044         * @throws IncorrectResultSizeDataAccessException if more than one
045         * element has been found in the given Collection
046         */
047        public static <T> T singleResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
048                if (CollectionUtils.isEmpty(results)) {
049                        return null;
050                }
051                if (results.size() > 1) {
052                        throw new IncorrectResultSizeDataAccessException(1, results.size());
053                }
054                return results.iterator().next();
055        }
056
057        /**
058         * Return a single result object from the given Collection.
059         * <p>Throws an exception if 0 or more than 1 element found.
060         * @param results the result Collection (can be {@code null})
061         * @return the single result object
062         * @throws IncorrectResultSizeDataAccessException if more than one
063         * element has been found in the given Collection
064         * @throws EmptyResultDataAccessException if no element at all
065         * has been found in the given Collection
066         */
067        public static <T> T requiredSingleResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
068                if (CollectionUtils.isEmpty(results)) {
069                        throw new EmptyResultDataAccessException(1);
070                }
071                if (results.size() > 1) {
072                        throw new IncorrectResultSizeDataAccessException(1, results.size());
073                }
074                return results.iterator().next();
075        }
076
077        /**
078         * Return a unique result object from the given Collection.
079         * <p>Returns {@code null} if 0 result objects found;
080         * throws an exception if more than 1 instance found.
081         * @param results the result Collection (can be {@code null})
082         * @return the unique result object, or {@code null} if none
083         * @throws IncorrectResultSizeDataAccessException if more than one
084         * result object has been found in the given Collection
085         * @see org.springframework.util.CollectionUtils#hasUniqueObject
086         */
087        public static <T> T uniqueResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
088                if (CollectionUtils.isEmpty(results)) {
089                        return null;
090                }
091                if (!CollectionUtils.hasUniqueObject(results)) {
092                        throw new IncorrectResultSizeDataAccessException(1, results.size());
093                }
094                return results.iterator().next();
095        }
096
097        /**
098         * Return a unique result object from the given Collection.
099         * <p>Throws an exception if 0 or more than 1 instance found.
100         * @param results the result Collection (can be {@code null})
101         * @return the unique result object
102         * @throws IncorrectResultSizeDataAccessException if more than one
103         * result object has been found in the given Collection
104         * @throws EmptyResultDataAccessException if no result object at all
105         * has been found in the given Collection
106         * @see org.springframework.util.CollectionUtils#hasUniqueObject
107         */
108        public static <T> T requiredUniqueResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
109                if (CollectionUtils.isEmpty(results)) {
110                        throw new EmptyResultDataAccessException(1);
111                }
112                if (!CollectionUtils.hasUniqueObject(results)) {
113                        throw new IncorrectResultSizeDataAccessException(1, results.size());
114                }
115                return results.iterator().next();
116        }
117
118        /**
119         * Return a unique result object from the given Collection.
120         * Throws an exception if 0 or more than 1 result objects found,
121         * of if the unique result object is not convertible to the
122         * specified required type.
123         * @param results the result Collection (can be {@code null})
124         * @return the unique result object
125         * @throws IncorrectResultSizeDataAccessException if more than one
126         * result object has been found in the given Collection
127         * @throws EmptyResultDataAccessException if no result object
128         * at all has been found in the given Collection
129         * @throws TypeMismatchDataAccessException if the unique object does
130         * not match the specified required type
131         */
132        @SuppressWarnings("unchecked")
133        public static <T> T objectResult(Collection<?> results, Class<T> requiredType)
134                        throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException {
135
136                Object result = requiredUniqueResult(results);
137                if (requiredType != null && !requiredType.isInstance(result)) {
138                        if (String.class == requiredType) {
139                                result = result.toString();
140                        }
141                        else if (Number.class.isAssignableFrom(requiredType) && Number.class.isInstance(result)) {
142                                try {
143                                        result = NumberUtils.convertNumberToTargetClass(((Number) result), (Class<? extends Number>) requiredType);
144                                }
145                                catch (IllegalArgumentException ex) {
146                                        throw new TypeMismatchDataAccessException(ex.getMessage());
147                                }
148                        }
149                        else {
150                                throw new TypeMismatchDataAccessException(
151                                                "Result object is of type [" + result.getClass().getName() +
152                                                "] and could not be converted to required type [" + requiredType.getName() + "]");
153                        }
154                }
155                return (T) result;
156        }
157
158        /**
159         * Return a unique int result from the given Collection.
160         * Throws an exception if 0 or more than 1 result objects found,
161         * of if the unique result object is not convertible to an int.
162         * @param results the result Collection (can be {@code null})
163         * @return the unique int result
164         * @throws IncorrectResultSizeDataAccessException if more than one
165         * result object has been found in the given Collection
166         * @throws EmptyResultDataAccessException if no result object
167         * at all has been found in the given Collection
168         * @throws TypeMismatchDataAccessException if the unique object
169         * in the collection is not convertible to an int
170         */
171        public static int intResult(Collection<?> results)
172                        throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException {
173
174                return objectResult(results, Number.class).intValue();
175        }
176
177        /**
178         * Return a unique long result from the given Collection.
179         * Throws an exception if 0 or more than 1 result objects found,
180         * of if the unique result object is not convertible to a long.
181         * @param results the result Collection (can be {@code null})
182         * @return the unique long result
183         * @throws IncorrectResultSizeDataAccessException if more than one
184         * result object has been found in the given Collection
185         * @throws EmptyResultDataAccessException if no result object
186         * at all has been found in the given Collection
187         * @throws TypeMismatchDataAccessException if the unique object
188         * in the collection is not convertible to a long
189         */
190        public static long longResult(Collection<?> results)
191                        throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException {
192
193                return objectResult(results, Number.class).longValue();
194        }
195
196
197        /**
198         * Return a translated exception if this is appropriate,
199         * otherwise return the given exception as-is.
200         * @param rawException an exception that we may wish to translate
201         * @param pet PersistenceExceptionTranslator to use to perform the translation
202         * @return a translated persistence exception if translation is possible,
203         * or the raw exception if it is not
204         */
205        public static RuntimeException translateIfNecessary(
206                        RuntimeException rawException, PersistenceExceptionTranslator pet) {
207
208                Assert.notNull(pet, "PersistenceExceptionTranslator must not be null");
209                DataAccessException dex = pet.translateExceptionIfPossible(rawException);
210                return (dex != null ? dex : rawException);
211        }
212
213}