001/*
002 * Copyright 2002-2014 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.orm.hibernate4;
018
019import java.lang.reflect.Method;
020import javax.sql.DataSource;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024import org.hibernate.HibernateException;
025import org.hibernate.JDBCException;
026import org.hibernate.NonUniqueObjectException;
027import org.hibernate.NonUniqueResultException;
028import org.hibernate.ObjectDeletedException;
029import org.hibernate.PersistentObjectException;
030import org.hibernate.PessimisticLockException;
031import org.hibernate.PropertyValueException;
032import org.hibernate.QueryException;
033import org.hibernate.QueryTimeoutException;
034import org.hibernate.Session;
035import org.hibernate.SessionFactory;
036import org.hibernate.StaleObjectStateException;
037import org.hibernate.StaleStateException;
038import org.hibernate.TransientObjectException;
039import org.hibernate.UnresolvableObjectException;
040import org.hibernate.WrongClassException;
041import org.hibernate.dialect.lock.OptimisticEntityLockException;
042import org.hibernate.dialect.lock.PessimisticEntityLockException;
043import org.hibernate.engine.spi.SessionFactoryImplementor;
044import org.hibernate.exception.ConstraintViolationException;
045import org.hibernate.exception.DataException;
046import org.hibernate.exception.JDBCConnectionException;
047import org.hibernate.exception.LockAcquisitionException;
048import org.hibernate.exception.SQLGrammarException;
049import org.hibernate.service.spi.Wrapped;
050
051import org.springframework.dao.CannotAcquireLockException;
052import org.springframework.dao.DataAccessException;
053import org.springframework.dao.DataAccessResourceFailureException;
054import org.springframework.dao.DataIntegrityViolationException;
055import org.springframework.dao.DuplicateKeyException;
056import org.springframework.dao.IncorrectResultSizeDataAccessException;
057import org.springframework.dao.InvalidDataAccessApiUsageException;
058import org.springframework.dao.InvalidDataAccessResourceUsageException;
059import org.springframework.dao.PessimisticLockingFailureException;
060import org.springframework.jdbc.datasource.DataSourceUtils;
061import org.springframework.util.ClassUtils;
062import org.springframework.util.ReflectionUtils;
063
064/**
065 * Helper class featuring methods for Hibernate Session handling.
066 * Also provides support for exception translation.
067 *
068 * <p>Used internally by {@link HibernateTransactionManager}.
069 * Can also be used directly in application code.
070 *
071 * @author Juergen Hoeller
072 * @since 3.1
073 * @see HibernateExceptionTranslator
074 * @see HibernateTransactionManager
075 */
076public abstract class SessionFactoryUtils {
077
078        /**
079         * Order value for TransactionSynchronization objects that clean up Hibernate Sessions.
080         * Returns {@code DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100}
081         * to execute Session cleanup before JDBC Connection cleanup, if any.
082         * @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
083         */
084        public static final int SESSION_SYNCHRONIZATION_ORDER =
085                        DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
086
087        static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
088
089        /**
090         * Bridging between the different ConnectionProvider package location in 4.0-4.2 vs 4.3.
091         */
092        private static final Method getConnectionProviderMethod =
093                        ClassUtils.getMethodIfAvailable(SessionFactoryImplementor.class, "getConnectionProvider");
094
095
096        /**
097         * Determine the DataSource of the given SessionFactory.
098         * @param sessionFactory the SessionFactory to check
099         * @return the DataSource, or {@code null} if none found
100         * @see org.hibernate.engine.spi.SessionFactoryImplementor#getConnectionProvider
101         */
102        public static DataSource getDataSource(SessionFactory sessionFactory) {
103                if (getConnectionProviderMethod != null && sessionFactory instanceof SessionFactoryImplementor) {
104                        Wrapped cp = (Wrapped) ReflectionUtils.invokeMethod(getConnectionProviderMethod, sessionFactory);
105                        if (cp != null) {
106                                return cp.unwrap(DataSource.class);
107                        }
108                }
109                return null;
110        }
111
112        /**
113         * Perform actual closing of the Hibernate Session,
114         * catching and logging any cleanup exceptions thrown.
115         * @param session the Hibernate Session to close (may be {@code null})
116         * @see org.hibernate.Session#close()
117         */
118        public static void closeSession(Session session) {
119                if (session != null) {
120                        try {
121                                session.close();
122                        }
123                        catch (HibernateException ex) {
124                                logger.debug("Could not close Hibernate Session", ex);
125                        }
126                        catch (Throwable ex) {
127                                logger.debug("Unexpected exception on closing Hibernate Session", ex);
128                        }
129                }
130        }
131
132        /**
133         * Convert the given HibernateException to an appropriate exception
134         * from the {@code org.springframework.dao} hierarchy.
135         * @param ex HibernateException that occurred
136         * @return the corresponding DataAccessException instance
137         * @see HibernateExceptionTranslator#convertHibernateAccessException
138         * @see HibernateTransactionManager#convertHibernateAccessException
139         */
140        public static DataAccessException convertHibernateAccessException(HibernateException ex) {
141                if (ex instanceof JDBCConnectionException) {
142                        return new DataAccessResourceFailureException(ex.getMessage(), ex);
143                }
144                if (ex instanceof SQLGrammarException) {
145                        SQLGrammarException jdbcEx = (SQLGrammarException) ex;
146                        return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
147                }
148                if (ex instanceof QueryTimeoutException) {
149                        QueryTimeoutException jdbcEx = (QueryTimeoutException) ex;
150                        return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
151                }
152                if (ex instanceof LockAcquisitionException) {
153                        LockAcquisitionException jdbcEx = (LockAcquisitionException) ex;
154                        return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
155                }
156                if (ex instanceof PessimisticLockException) {
157                        PessimisticLockException jdbcEx = (PessimisticLockException) ex;
158                        return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
159                }
160                if (ex instanceof ConstraintViolationException) {
161                        ConstraintViolationException jdbcEx = (ConstraintViolationException) ex;
162                        return new DataIntegrityViolationException(ex.getMessage()  + "; SQL [" + jdbcEx.getSQL() +
163                                        "]; constraint [" + jdbcEx.getConstraintName() + "]", ex);
164                }
165                if (ex instanceof DataException) {
166                        DataException jdbcEx = (DataException) ex;
167                        return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
168                }
169                if (ex instanceof JDBCException) {
170                        return new HibernateJdbcException((JDBCException) ex);
171                }
172                // end of JDBCException (subclass) handling
173
174                if (ex instanceof QueryException) {
175                        return new HibernateQueryException((QueryException) ex);
176                }
177                if (ex instanceof NonUniqueResultException) {
178                        return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
179                }
180                if (ex instanceof NonUniqueObjectException) {
181                        return new DuplicateKeyException(ex.getMessage(), ex);
182                }
183                if (ex instanceof PropertyValueException) {
184                        return new DataIntegrityViolationException(ex.getMessage(), ex);
185                }
186                if (ex instanceof PersistentObjectException) {
187                        return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
188                }
189                if (ex instanceof TransientObjectException) {
190                        return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
191                }
192                if (ex instanceof ObjectDeletedException) {
193                        return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
194                }
195                if (ex instanceof UnresolvableObjectException) {
196                        return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
197                }
198                if (ex instanceof WrongClassException) {
199                        return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
200                }
201                if (ex instanceof StaleObjectStateException) {
202                        return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
203                }
204                if (ex instanceof StaleStateException) {
205                        return new HibernateOptimisticLockingFailureException((StaleStateException) ex);
206                }
207                if (ex instanceof OptimisticEntityLockException) {
208                        return new HibernateOptimisticLockingFailureException((OptimisticEntityLockException) ex);
209                }
210                if (ex instanceof PessimisticEntityLockException) {
211                        if (ex.getCause() instanceof LockAcquisitionException) {
212                                return new CannotAcquireLockException(ex.getMessage(), ex.getCause());
213                        }
214                        return new PessimisticLockingFailureException(ex.getMessage(), ex);
215                }
216
217                // fallback
218                return new HibernateSystemException(ex);
219        }
220
221}