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.orm.jpa.vendor;
018
019import java.util.HashMap;
020import java.util.Map;
021import javax.persistence.EntityManager;
022import javax.persistence.EntityManagerFactory;
023import javax.persistence.spi.PersistenceProvider;
024import javax.persistence.spi.PersistenceUnitInfo;
025import javax.persistence.spi.PersistenceUnitTransactionType;
026
027import org.hibernate.cfg.Environment;
028import org.hibernate.dialect.DB2Dialect;
029import org.hibernate.dialect.DerbyDialect;
030import org.hibernate.dialect.H2Dialect;
031import org.hibernate.dialect.HSQLDialect;
032import org.hibernate.dialect.InformixDialect;
033import org.hibernate.dialect.MySQL5Dialect;
034import org.hibernate.dialect.Oracle9iDialect;
035import org.hibernate.dialect.PostgreSQLDialect;
036import org.hibernate.dialect.SQLServer2008Dialect;
037
038/**
039 * {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate
040 * EntityManager. Developed and tested against Hibernate 3.6, 4.2/4.3 as well as 5.x.
041 * <b>Hibernate 4.2+ is strongly recommended for use with Spring 4.0+.</b>
042 *
043 * <p>Exposes Hibernate's persistence provider and EntityManager extension interface,
044 * and adapts {@link AbstractJpaVendorAdapter}'s common configuration settings.
045 * Also supports the detection of annotated packages (through
046 * {@link org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo#getManagedPackages()}),
047 * e.g. containing Hibernate {@link org.hibernate.annotations.FilterDef} annotations,
048 * along with Spring-driven entity scanning which requires no {@code persistence.xml}
049 * ({@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#setPackagesToScan}).
050 *
051 * <p>Note that the package location of Hibernate's JPA support changed from 4.2 to 4.3:
052 * from {@code org.hibernate.ejb.HibernateEntityManager(Factory)} to
053 * {@code org.hibernate.jpa.HibernateEntityManager(Factory)}. As of Spring 4.0,
054 * we're exposing the correct, non-deprecated variant depending on the Hibernate
055 * version encountered at runtime, in order to avoid deprecation log entries.
056 *
057 * <p><b>A note about {@code HibernateJpaVendorAdapter} vs native Hibernate settings:</b>
058 * Some settings on this adapter may conflict with native Hibernate configuration rules
059 * or custom Hibernate properties. For example, specify either {@link #setDatabase} or
060 * Hibernate's "hibernate.dialect_resolvers" property, not both. Also, be careful about
061 * Hibernate's connection release mode: This adapter prefers {@code ON_CLOSE} behavior,
062 * aligned with {@link HibernateJpaDialect#setPrepareConnection}, at least for non-JTA
063 * scenarios; you may override this through corresponding native Hibernate properties.
064 *
065 * @author Juergen Hoeller
066 * @author Rod Johnson
067 * @since 2.0
068 * @see HibernateJpaDialect
069 * @see org.hibernate.ejb.HibernatePersistence
070 * @see org.hibernate.ejb.HibernateEntityManager
071 */
072public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
073
074        private final HibernateJpaDialect jpaDialect = new HibernateJpaDialect();
075
076        private final PersistenceProvider persistenceProvider;
077
078        private final Class<? extends EntityManagerFactory> entityManagerFactoryInterface;
079
080        private final Class<? extends EntityManager> entityManagerInterface;
081
082
083        @SuppressWarnings("unchecked")
084        public HibernateJpaVendorAdapter() {
085                ClassLoader cl = HibernateJpaVendorAdapter.class.getClassLoader();
086                Class<? extends EntityManagerFactory> emfIfcToUse;
087                Class<? extends EntityManager> emIfcToUse;
088                Class<?> providerClass;
089                PersistenceProvider providerToUse;
090                try {
091                        try {
092                                // Try Hibernate 4.3/5.0's org.hibernate.jpa package in order to avoid deprecation warnings
093                                emfIfcToUse = (Class<? extends EntityManagerFactory>) cl.loadClass("org.hibernate.jpa.HibernateEntityManagerFactory");
094                                emIfcToUse = (Class<? extends EntityManager>) cl.loadClass("org.hibernate.jpa.HibernateEntityManager");
095                                providerClass = cl.loadClass("org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider");
096                        }
097                        catch (ClassNotFoundException ex) {
098                                // Fall back to Hibernate 3.6-4.2 org.hibernate.ejb package
099                                emfIfcToUse = (Class<? extends EntityManagerFactory>) cl.loadClass("org.hibernate.ejb.HibernateEntityManagerFactory");
100                                emIfcToUse = (Class<? extends EntityManager>) cl.loadClass("org.hibernate.ejb.HibernateEntityManager");
101                                providerClass = cl.loadClass("org.springframework.orm.jpa.vendor.SpringHibernateEjbPersistenceProvider");
102                        }
103                        providerToUse = (PersistenceProvider) providerClass.newInstance();
104                }
105                catch (Exception ex) {
106                        throw new IllegalStateException("Failed to determine Hibernate PersistenceProvider", ex);
107                }
108                this.persistenceProvider = providerToUse;
109                this.entityManagerFactoryInterface = emfIfcToUse;
110                this.entityManagerInterface = emIfcToUse;
111        }
112
113
114        /**
115         * Set whether to prepare the underlying JDBC Connection of a transactional
116         * Hibernate Session, that is, whether to apply a transaction-specific
117         * isolation level and/or the transaction's read-only flag to the underlying
118         * JDBC Connection.
119         * <p>See {@link HibernateJpaDialect#setPrepareConnection(boolean)} for details.
120         * This is just a convenience flag passed through to {@code HibernateJpaDialect}.
121         * <p>On Hibernate 5.1/5.2, this flag remains {@code true} by default like against
122         * previous Hibernate versions. The vendor adapter manually enforces Hibernate's
123         * new connection handling mode {@code DELAYED_ACQUISITION_AND_HOLD} in that case
124         * unless a user-specified connection handling mode property indicates otherwise;
125         * switch this flag to {@code false} to avoid that interference.
126         * <p><b>NOTE: For a persistence unit with transaction type JTA e.g. on WebLogic,
127         * the connection release mode will never be altered from its provider default,
128         * i.e. not be forced to {@code DELAYED_ACQUISITION_AND_HOLD} by this flag.</b>
129         * Alternatively, set Hibernate 5.2's "hibernate.connection.handling_mode"
130         * property to "DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION" or even
131         * "DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT" in such a scenario.
132         * @since 4.3.1
133         * @see PersistenceUnitInfo#getTransactionType()
134         * @see #getJpaPropertyMap(PersistenceUnitInfo)
135         * @see HibernateJpaDialect#beginTransaction
136         */
137        public void setPrepareConnection(boolean prepareConnection) {
138                this.jpaDialect.setPrepareConnection(prepareConnection);
139        }
140
141
142        @Override
143        public PersistenceProvider getPersistenceProvider() {
144                return this.persistenceProvider;
145        }
146
147        @Override
148        public String getPersistenceProviderRootPackage() {
149                return "org.hibernate";
150        }
151
152        @Override
153        public Map<String, Object> getJpaPropertyMap(PersistenceUnitInfo pui) {
154                return buildJpaPropertyMap(this.jpaDialect.prepareConnection &&
155                                pui.getTransactionType() != PersistenceUnitTransactionType.JTA);
156        }
157
158        @Override
159        public Map<String, Object> getJpaPropertyMap() {
160                return buildJpaPropertyMap(this.jpaDialect.prepareConnection);
161        }
162
163        private Map<String, Object> buildJpaPropertyMap(boolean connectionReleaseOnClose) {
164                Map<String, Object> jpaProperties = new HashMap<String, Object>();
165
166                if (getDatabasePlatform() != null) {
167                        jpaProperties.put(Environment.DIALECT, getDatabasePlatform());
168                }
169                else if (getDatabase() != null) {
170                        Class<?> databaseDialectClass = determineDatabaseDialectClass(getDatabase());
171                        if (databaseDialectClass != null) {
172                                jpaProperties.put(Environment.DIALECT, databaseDialectClass.getName());
173                        }
174                }
175
176                if (isGenerateDdl()) {
177                        jpaProperties.put(Environment.HBM2DDL_AUTO, "update");
178                }
179                if (isShowSql()) {
180                        jpaProperties.put(Environment.SHOW_SQL, "true");
181                }
182
183                if (connectionReleaseOnClose) {
184                        // Hibernate 5.1/5.2: manually enforce connection release mode ON_CLOSE (the former default)
185                        try {
186                                // Try Hibernate 5.2
187                                Environment.class.getField("CONNECTION_HANDLING");
188                                jpaProperties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_HOLD");
189                        }
190                        catch (NoSuchFieldException ex) {
191                                // Try Hibernate 5.1
192                                try {
193                                        Environment.class.getField("ACQUIRE_CONNECTIONS");
194                                        jpaProperties.put("hibernate.connection.release_mode", "ON_CLOSE");
195                                }
196                                catch (NoSuchFieldException ex2) {
197                                        // on Hibernate 5.0.x or lower - no need to change the default there
198                                }
199                        }
200                }
201
202                return jpaProperties;
203        }
204
205        /**
206         * Determine the Hibernate database dialect class for the given target database.
207         * @param database the target database
208         * @return the Hibernate database dialect class, or {@code null} if none found
209         */
210        @SuppressWarnings("deprecation")
211        protected Class<?> determineDatabaseDialectClass(Database database) {
212                switch (database) {
213                        case DB2: return DB2Dialect.class;
214                        case DERBY: return DerbyDialect.class;  // DerbyDialect deprecated in 4.x
215                        case H2: return H2Dialect.class;
216                        case HSQL: return HSQLDialect.class;
217                        case INFORMIX: return InformixDialect.class;
218                        case MYSQL: return MySQL5Dialect.class;
219                        case ORACLE: return Oracle9iDialect.class;
220                        case POSTGRESQL: return PostgreSQLDialect.class;  // PostgreSQLDialect deprecated in 4.x
221                        case SQL_SERVER: return SQLServer2008Dialect.class;
222                        case SYBASE: return org.hibernate.dialect.SybaseDialect.class;  // SybaseDialect deprecated in 3.6 but not 4.x
223                        default: return null;
224                }
225        }
226
227        @Override
228        public HibernateJpaDialect getJpaDialect() {
229                return this.jpaDialect;
230        }
231
232        @Override
233        public Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() {
234                return this.entityManagerFactoryInterface;
235        }
236
237        @Override
238        public Class<? extends EntityManager> getEntityManagerInterface() {
239                return this.entityManagerInterface;
240        }
241
242}