1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.cfg;
26
27 import java.io.Serializable;
28 import java.sql.Connection;
29 import java.sql.DatabaseMetaData;
30 import java.sql.ResultSet;
31 import java.sql.SQLException;
32 import java.util.Map;
33 import java.util.Properties;
34
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import org.hibernate.ConnectionReleaseMode;
39 import org.hibernate.EntityMode;
40 import org.hibernate.HibernateException;
41 import org.hibernate.bytecode.BytecodeProvider;
42 import org.hibernate.cache.QueryCacheFactory;
43 import org.hibernate.cache.RegionFactory;
44 import org.hibernate.cache.impl.NoCachingRegionFactory;
45 import org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge;
46 import org.hibernate.connection.ConnectionProvider;
47 import org.hibernate.connection.ConnectionProviderFactory;
48 import org.hibernate.dialect.Dialect;
49 import org.hibernate.dialect.resolver.DialectFactory;
50 import org.hibernate.exception.SQLExceptionConverter;
51 import org.hibernate.exception.SQLExceptionConverterFactory;
52 import org.hibernate.hql.QueryTranslatorFactory;
53 import org.hibernate.jdbc.BatcherFactory;
54 import org.hibernate.jdbc.BatchingBatcherFactory;
55 import org.hibernate.jdbc.NonBatchingBatcherFactory;
56 import org.hibernate.jdbc.util.SQLStatementLogger;
57 import org.hibernate.transaction.TransactionFactory;
58 import org.hibernate.transaction.TransactionFactoryFactory;
59 import org.hibernate.transaction.TransactionManagerLookup;
60 import org.hibernate.transaction.TransactionManagerLookupFactory;
61 import org.hibernate.util.PropertiesHelper;
62 import org.hibernate.util.ReflectHelper;
63 import org.hibernate.util.StringHelper;
64
65 /**
66 * Reads configuration properties and builds a {@link Settings} instance.
67 *
68 * @author Gavin King
69 */
70 public class SettingsFactory implements Serializable {
71 private static final Logger log = LoggerFactory.getLogger( SettingsFactory.class );
72 private static final long serialVersionUID = -1194386144994524825L;
73
74 public static final String DEF_CACHE_REG_FACTORY = NoCachingRegionFactory.class.getName();
75
76 protected SettingsFactory() {
77 }
78
79 public Settings buildSettings(Properties props) {
80 Settings settings = new Settings();
81
82 //SessionFactory name:
83
84 String sessionFactoryName = props.getProperty(Environment.SESSION_FACTORY_NAME);
85 settings.setSessionFactoryName(sessionFactoryName);
86
87 //JDBC and connection settings:
88
89 ConnectionProvider connections = createConnectionProvider(props);
90 settings.setConnectionProvider(connections);
91
92 //Interrogate JDBC metadata
93
94 boolean metaSupportsScrollable = false;
95 boolean metaSupportsGetGeneratedKeys = false;
96 boolean metaSupportsBatchUpdates = false;
97 boolean metaReportsDDLCausesTxnCommit = false;
98 boolean metaReportsDDLInTxnSupported = true;
99 Dialect dialect = null;
100
101 // 'hibernate.temp.use_jdbc_metadata_defaults' is a temporary magic value.
102 // The need for it is intended to be alleviated with future developement, thus it is
103 // not defined as an Environment constant...
104 //
105 // it is used to control whether we should consult the JDBC metadata to determine
106 // certain Settings default values; it is useful to *not* do this when the database
107 // may not be available (mainly in tools usage).
108 boolean useJdbcMetadata = PropertiesHelper.getBoolean( "hibernate.temp.use_jdbc_metadata_defaults", props, true );
109 if ( useJdbcMetadata ) {
110 try {
111 Connection conn = connections.getConnection();
112 try {
113 DatabaseMetaData meta = conn.getMetaData();
114 log.info( "RDBMS: " + meta.getDatabaseProductName() + ", version: " + meta.getDatabaseProductVersion() );
115 log.info( "JDBC driver: " + meta.getDriverName() + ", version: " + meta.getDriverVersion() );
116
117 dialect = DialectFactory.buildDialect( props, conn );
118
119 metaSupportsScrollable = meta.supportsResultSetType( ResultSet.TYPE_SCROLL_INSENSITIVE );
120 metaSupportsBatchUpdates = meta.supportsBatchUpdates();
121 metaReportsDDLCausesTxnCommit = meta.dataDefinitionCausesTransactionCommit();
122 metaReportsDDLInTxnSupported = !meta.dataDefinitionIgnoredInTransactions();
123 metaSupportsGetGeneratedKeys = meta.supportsGetGeneratedKeys();
124 }
125 catch ( SQLException sqle ) {
126 log.warn( "Could not obtain connection metadata", sqle );
127 }
128 finally {
129 connections.closeConnection( conn );
130 }
131 }
132 catch ( SQLException sqle ) {
133 log.warn( "Could not obtain connection to query metadata", sqle );
134 dialect = DialectFactory.buildDialect( props );
135 }
136 catch ( UnsupportedOperationException uoe ) {
137 // user supplied JDBC connections
138 dialect = DialectFactory.buildDialect( props );
139 }
140 }
141 else {
142 dialect = DialectFactory.buildDialect( props );
143 }
144
145 settings.setDataDefinitionImplicitCommit( metaReportsDDLCausesTxnCommit );
146 settings.setDataDefinitionInTransactionSupported( metaReportsDDLInTxnSupported );
147 settings.setDialect( dialect );
148
149 //use dialect default properties
150 final Properties properties = new Properties();
151 properties.putAll( dialect.getDefaultProperties() );
152 properties.putAll( props );
153
154 // Transaction settings:
155
156 TransactionFactory transactionFactory = createTransactionFactory(properties);
157 settings.setTransactionFactory(transactionFactory);
158 settings.setTransactionManagerLookup( createTransactionManagerLookup(properties) );
159
160 boolean flushBeforeCompletion = PropertiesHelper.getBoolean(Environment.FLUSH_BEFORE_COMPLETION, properties);
161 log.info("Automatic flush during beforeCompletion(): " + enabledDisabled(flushBeforeCompletion) );
162 settings.setFlushBeforeCompletionEnabled(flushBeforeCompletion);
163
164 boolean autoCloseSession = PropertiesHelper.getBoolean(Environment.AUTO_CLOSE_SESSION, properties);
165 log.info("Automatic session close at end of transaction: " + enabledDisabled(autoCloseSession) );
166 settings.setAutoCloseSessionEnabled(autoCloseSession);
167
168 //JDBC and connection settings:
169
170 int batchSize = PropertiesHelper.getInt(Environment.STATEMENT_BATCH_SIZE, properties, 0);
171 if ( !metaSupportsBatchUpdates ) batchSize = 0;
172 if (batchSize>0) log.info("JDBC batch size: " + batchSize);
173 settings.setJdbcBatchSize(batchSize);
174 boolean jdbcBatchVersionedData = PropertiesHelper.getBoolean(Environment.BATCH_VERSIONED_DATA, properties, false);
175 if (batchSize>0) log.info("JDBC batch updates for versioned data: " + enabledDisabled(jdbcBatchVersionedData) );
176 settings.setJdbcBatchVersionedData(jdbcBatchVersionedData);
177 settings.setBatcherFactory( createBatcherFactory(properties, batchSize) );
178
179 boolean useScrollableResultSets = PropertiesHelper.getBoolean(Environment.USE_SCROLLABLE_RESULTSET, properties, metaSupportsScrollable);
180 log.info("Scrollable result sets: " + enabledDisabled(useScrollableResultSets) );
181 settings.setScrollableResultSetsEnabled(useScrollableResultSets);
182
183 boolean wrapResultSets = PropertiesHelper.getBoolean(Environment.WRAP_RESULT_SETS, properties, false);
184 log.debug( "Wrap result sets: " + enabledDisabled(wrapResultSets) );
185 settings.setWrapResultSetsEnabled(wrapResultSets);
186
187 boolean useGetGeneratedKeys = PropertiesHelper.getBoolean(Environment.USE_GET_GENERATED_KEYS, properties, metaSupportsGetGeneratedKeys);
188 log.info("JDBC3 getGeneratedKeys(): " + enabledDisabled(useGetGeneratedKeys) );
189 settings.setGetGeneratedKeysEnabled(useGetGeneratedKeys);
190
191 Integer statementFetchSize = PropertiesHelper.getInteger(Environment.STATEMENT_FETCH_SIZE, properties);
192 if (statementFetchSize!=null) log.info("JDBC result set fetch size: " + statementFetchSize);
193 settings.setJdbcFetchSize(statementFetchSize);
194
195 String releaseModeName = PropertiesHelper.getString( Environment.RELEASE_CONNECTIONS, properties, "auto" );
196 log.info( "Connection release mode: " + releaseModeName );
197 ConnectionReleaseMode releaseMode;
198 if ( "auto".equals(releaseModeName) ) {
199 releaseMode = transactionFactory.getDefaultReleaseMode();
200 }
201 else {
202 releaseMode = ConnectionReleaseMode.parse( releaseModeName );
203 if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT && !connections.supportsAggressiveRelease() ) {
204 log.warn( "Overriding release mode as connection provider does not support 'after_statement'" );
205 releaseMode = ConnectionReleaseMode.AFTER_TRANSACTION;
206 }
207 }
208 settings.setConnectionReleaseMode( releaseMode );
209
210 //SQL Generation settings:
211
212 String defaultSchema = properties.getProperty(Environment.DEFAULT_SCHEMA);
213 String defaultCatalog = properties.getProperty(Environment.DEFAULT_CATALOG);
214 if (defaultSchema!=null) log.info("Default schema: " + defaultSchema);
215 if (defaultCatalog!=null) log.info("Default catalog: " + defaultCatalog);
216 settings.setDefaultSchemaName(defaultSchema);
217 settings.setDefaultCatalogName(defaultCatalog);
218
219 Integer maxFetchDepth = PropertiesHelper.getInteger(Environment.MAX_FETCH_DEPTH, properties);
220 if (maxFetchDepth!=null) log.info("Maximum outer join fetch depth: " + maxFetchDepth);
221 settings.setMaximumFetchDepth(maxFetchDepth);
222 int batchFetchSize = PropertiesHelper.getInt(Environment.DEFAULT_BATCH_FETCH_SIZE, properties, 1);
223 log.info("Default batch fetch size: " + batchFetchSize);
224 settings.setDefaultBatchFetchSize(batchFetchSize);
225
226 boolean comments = PropertiesHelper.getBoolean(Environment.USE_SQL_COMMENTS, properties);
227 log.info( "Generate SQL with comments: " + enabledDisabled(comments) );
228 settings.setCommentsEnabled(comments);
229
230 boolean orderUpdates = PropertiesHelper.getBoolean(Environment.ORDER_UPDATES, properties);
231 log.info( "Order SQL updates by primary key: " + enabledDisabled(orderUpdates) );
232 settings.setOrderUpdatesEnabled(orderUpdates);
233
234 boolean orderInserts = PropertiesHelper.getBoolean(Environment.ORDER_INSERTS, properties);
235 log.info( "Order SQL inserts for batching: " + enabledDisabled( orderInserts ) );
236 settings.setOrderInsertsEnabled( orderInserts );
237
238 //Query parser settings:
239
240 settings.setQueryTranslatorFactory( createQueryTranslatorFactory(properties) );
241
242 Map querySubstitutions = PropertiesHelper.toMap(Environment.QUERY_SUBSTITUTIONS, " ,=;:\n\t\r\f", properties);
243 log.info("Query language substitutions: " + querySubstitutions);
244 settings.setQuerySubstitutions(querySubstitutions);
245
246 boolean jpaqlCompliance = PropertiesHelper.getBoolean( Environment.JPAQL_STRICT_COMPLIANCE, properties, false );
247 settings.setStrictJPAQLCompliance( jpaqlCompliance );
248 log.info( "JPA-QL strict compliance: " + enabledDisabled( jpaqlCompliance ) );
249
250 // Second-level / query cache:
251
252 boolean useSecondLevelCache = PropertiesHelper.getBoolean(Environment.USE_SECOND_LEVEL_CACHE, properties, true);
253 log.info( "Second-level cache: " + enabledDisabled(useSecondLevelCache) );
254 settings.setSecondLevelCacheEnabled(useSecondLevelCache);
255
256 boolean useQueryCache = PropertiesHelper.getBoolean(Environment.USE_QUERY_CACHE, properties);
257 log.info( "Query cache: " + enabledDisabled(useQueryCache) );
258 settings.setQueryCacheEnabled(useQueryCache);
259
260 // The cache provider is needed when we either have second-level cache enabled
261 // or query cache enabled. Note that useSecondLevelCache is enabled by default
262 settings.setRegionFactory( createRegionFactory( properties, ( useSecondLevelCache || useQueryCache ) ) );
263
264 boolean useMinimalPuts = PropertiesHelper.getBoolean(
265 Environment.USE_MINIMAL_PUTS, properties, settings.getRegionFactory().isMinimalPutsEnabledByDefault()
266 );
267 log.info( "Optimize cache for minimal puts: " + enabledDisabled(useMinimalPuts) );
268 settings.setMinimalPutsEnabled(useMinimalPuts);
269
270 String prefix = properties.getProperty(Environment.CACHE_REGION_PREFIX);
271 if ( StringHelper.isEmpty(prefix) ) prefix=null;
272 if (prefix!=null) log.info("Cache region prefix: "+ prefix);
273 settings.setCacheRegionPrefix(prefix);
274
275 boolean useStructuredCacheEntries = PropertiesHelper.getBoolean(Environment.USE_STRUCTURED_CACHE, properties, false);
276 log.info( "Structured second-level cache entries: " + enabledDisabled(useStructuredCacheEntries) );
277 settings.setStructuredCacheEntriesEnabled(useStructuredCacheEntries);
278
279 if (useQueryCache) settings.setQueryCacheFactory( createQueryCacheFactory(properties) );
280
281 //SQL Exception converter:
282
283 SQLExceptionConverter sqlExceptionConverter;
284 try {
285 sqlExceptionConverter = SQLExceptionConverterFactory.buildSQLExceptionConverter( dialect, properties );
286 }
287 catch(HibernateException e) {
288 log.warn("Error building SQLExceptionConverter; using minimal converter");
289 sqlExceptionConverter = SQLExceptionConverterFactory.buildMinimalSQLExceptionConverter();
290 }
291 settings.setSQLExceptionConverter(sqlExceptionConverter);
292
293 //Statistics and logging:
294
295 boolean showSql = PropertiesHelper.getBoolean(Environment.SHOW_SQL, properties);
296 if (showSql) log.info("Echoing all SQL to stdout");
297 // settings.setShowSqlEnabled(showSql);
298
299 boolean formatSql = PropertiesHelper.getBoolean(Environment.FORMAT_SQL, properties);
300 // settings.setFormatSqlEnabled(formatSql);
301
302 settings.setSqlStatementLogger( new SQLStatementLogger( showSql, formatSql ) );
303
304 boolean useStatistics = PropertiesHelper.getBoolean(Environment.GENERATE_STATISTICS, properties);
305 log.info( "Statistics: " + enabledDisabled(useStatistics) );
306 settings.setStatisticsEnabled(useStatistics);
307
308 boolean useIdentifierRollback = PropertiesHelper.getBoolean(Environment.USE_IDENTIFIER_ROLLBACK, properties);
309 log.info( "Deleted entity synthetic identifier rollback: " + enabledDisabled(useIdentifierRollback) );
310 settings.setIdentifierRollbackEnabled(useIdentifierRollback);
311
312 //Schema export:
313
314 String autoSchemaExport = properties.getProperty(Environment.HBM2DDL_AUTO);
315 if ( "validate".equals(autoSchemaExport) ) settings.setAutoValidateSchema(true);
316 if ( "update".equals(autoSchemaExport) ) settings.setAutoUpdateSchema(true);
317 if ( "create".equals(autoSchemaExport) ) settings.setAutoCreateSchema(true);
318 if ( "create-drop".equals(autoSchemaExport) ) {
319 settings.setAutoCreateSchema(true);
320 settings.setAutoDropSchema(true);
321 }
322
323 EntityMode defaultEntityMode = EntityMode.parse( properties.getProperty( Environment.DEFAULT_ENTITY_MODE ) );
324 log.info( "Default entity-mode: " + defaultEntityMode );
325 settings.setDefaultEntityMode( defaultEntityMode );
326
327 boolean namedQueryChecking = PropertiesHelper.getBoolean( Environment.QUERY_STARTUP_CHECKING, properties, true );
328 log.info( "Named query checking : " + enabledDisabled( namedQueryChecking ) );
329 settings.setNamedQueryStartupCheckingEnabled( namedQueryChecking );
330
331 // String provider = properties.getProperty( Environment.BYTECODE_PROVIDER );
332 // log.info( "Bytecode provider name : " + provider );
333 // BytecodeProvider bytecodeProvider = buildBytecodeProvider( provider );
334 // settings.setBytecodeProvider( bytecodeProvider );
335
336 return settings;
337
338 }
339
340 protected BytecodeProvider buildBytecodeProvider(String providerName) {
341 if ( "javassist".equals( providerName ) ) {
342 return new org.hibernate.bytecode.javassist.BytecodeProviderImpl();
343 }
344 else if ( "cglib".equals( providerName ) ) {
345 return new org.hibernate.bytecode.cglib.BytecodeProviderImpl();
346 }
347 else {
348 log.debug( "using cglib as bytecode provider by default" );
349 return new org.hibernate.bytecode.cglib.BytecodeProviderImpl();
350 }
351 }
352
353 private static String enabledDisabled(boolean value) {
354 return value ? "enabled" : "disabled";
355 }
356
357 protected QueryCacheFactory createQueryCacheFactory(Properties properties) {
358 String queryCacheFactoryClassName = PropertiesHelper.getString(
359 Environment.QUERY_CACHE_FACTORY, properties, "org.hibernate.cache.StandardQueryCacheFactory"
360 );
361 log.info("Query cache factory: " + queryCacheFactoryClassName);
362 try {
363 return (QueryCacheFactory) ReflectHelper.classForName(queryCacheFactoryClassName).newInstance();
364 }
365 catch (Exception cnfe) {
366 throw new HibernateException("could not instantiate QueryCacheFactory: " + queryCacheFactoryClassName, cnfe);
367 }
368 }
369
370 protected RegionFactory createRegionFactory(Properties properties, boolean cachingEnabled) {
371 String regionFactoryClassName = PropertiesHelper.getString( Environment.CACHE_REGION_FACTORY, properties, null );
372 if ( regionFactoryClassName == null && cachingEnabled ) {
373 String providerClassName = PropertiesHelper.getString( Environment.CACHE_PROVIDER, properties, null );
374 if ( providerClassName != null ) {
375 // legacy behavior, apply the bridge...
376 regionFactoryClassName = RegionFactoryCacheProviderBridge.class.getName();
377 }
378 }
379 if ( regionFactoryClassName == null ) {
380 regionFactoryClassName = DEF_CACHE_REG_FACTORY;
381 }
382 log.info( "Cache region factory : " + regionFactoryClassName );
383 try {
384 return ( RegionFactory ) ReflectHelper.classForName( regionFactoryClassName )
385 .getConstructor( new Class[] { Properties.class } )
386 .newInstance( new Object[] { properties } );
387 }
388 catch ( Exception e ) {
389 throw new HibernateException( "could not instantiate RegionFactory [" + regionFactoryClassName + "]", e );
390 }
391 }
392
393 protected QueryTranslatorFactory createQueryTranslatorFactory(Properties properties) {
394 String className = PropertiesHelper.getString(
395 Environment.QUERY_TRANSLATOR, properties, "org.hibernate.hql.ast.ASTQueryTranslatorFactory"
396 );
397 log.info("Query translator: " + className);
398 try {
399 return (QueryTranslatorFactory) ReflectHelper.classForName(className).newInstance();
400 }
401 catch (Exception cnfe) {
402 throw new HibernateException("could not instantiate QueryTranslatorFactory: " + className, cnfe);
403 }
404 }
405
406 protected BatcherFactory createBatcherFactory(Properties properties, int batchSize) {
407 String batcherClass = properties.getProperty(Environment.BATCH_STRATEGY);
408 if (batcherClass==null) {
409 return batchSize==0 ?
410 (BatcherFactory) new NonBatchingBatcherFactory() :
411 (BatcherFactory) new BatchingBatcherFactory();
412 }
413 else {
414 log.info("Batcher factory: " + batcherClass);
415 try {
416 return (BatcherFactory) ReflectHelper.classForName(batcherClass).newInstance();
417 }
418 catch (Exception cnfe) {
419 throw new HibernateException("could not instantiate BatcherFactory: " + batcherClass, cnfe);
420 }
421 }
422 }
423
424 protected ConnectionProvider createConnectionProvider(Properties properties) {
425 return ConnectionProviderFactory.newConnectionProvider(properties);
426 }
427
428 protected TransactionFactory createTransactionFactory(Properties properties) {
429 return TransactionFactoryFactory.buildTransactionFactory(properties);
430 }
431
432 protected TransactionManagerLookup createTransactionManagerLookup(Properties properties) {
433 return TransactionManagerLookupFactory.getTransactionManagerLookup(properties);
434 }
435
436 }