
org.nuiton.topia.persistence.internal.HibernateProvider Maven / Gradle / Ivy
package org.nuiton.topia.persistence.internal;
/*
* #%L
* ToPIA Extension :: API
* %%
* Copyright (C) 2018 - 2022 Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.dialect.Oracle10gDialect;
import org.hibernate.dialect.PostgreSQL9Dialect;
import org.hibernate.dialect.SQLServer2012Dialect;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.Service;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.service.spi.Stoppable;
import org.nuiton.topia.persistence.HibernateAvailableSettings;
import org.nuiton.topia.persistence.TopiaConfiguration;
import org.nuiton.topia.persistence.TopiaMisconfigurationException;
import org.nuiton.topia.persistence.jdbc.JdbcConfigurationBuilder;
import org.nuiton.topia.persistence.support.TopiaServiceSupport;
import org.nuiton.topia.persistence.util.TopiaUtil;
import java.util.Collection;
import java.util.Properties;
import java.util.Set;
/**
* @author Arnaud Thimel (Code Lutin)
*/
public class HibernateProvider {
private static final Logger log = LogManager.getLogger(HibernateProvider.class);
static final String HIKARI_CP_HIBERNATE_CONNECTION_PROVIDER_CLASS_NAME = "org.hibernate.hikaricp.internal.HikariCPConnectionProvider";
static final String C3P0_HIBERNATE_CONNECTION_PROVIDER_CLASS_NAME = "org.hibernate.c3p0.internal.C3P0ConnectionProvider";
protected SessionFactory hibernateSessionFactory;
protected Configuration hibernateConfiguration;
protected TopiaConfiguration topiaConfiguration;
protected TopiaServiceSupport topiaServiceSupport;
protected TopiaHibernateSessionRegistry sessionRegistry;
/**
* List of persistent classes (TopiaEntity or not)
*/
protected Set> persistenceClasses;
protected Metadata metaData;
public HibernateProvider(TopiaConfiguration topiaConfiguration,
TopiaServiceSupport topiaServiceSupport,
TopiaHibernateSessionRegistry sessionRegistry,
Iterable> persistenceClasses) {
this.topiaConfiguration = topiaConfiguration;
this.topiaServiceSupport = topiaServiceSupport;
this.sessionRegistry = sessionRegistry;
this.persistenceClasses = ImmutableSet.copyOf(persistenceClasses);
}
/**
* Get the current Hibernate Configuration. The Configuration instance is lazy-initialized using the
* {@link #newHibernateConfiguration()} method. The returned instance is always initialized and mapping are built.
*
* @return the Hibernate Configuration instance with built mappings.
*/
public Configuration getHibernateConfiguration() {
if (hibernateConfiguration == null) {
hibernateConfiguration = newHibernateConfiguration();
TopiaUtil.warnOnAutomaticSchemaOperationRisk(hibernateConfiguration);
}
return hibernateConfiguration;
}
/**
* Creates a new Configuration instance. The instance is create but mappings are not built yet to avoid unwanted
* database access.
*
* @return a new Hibernate Configuration instance without built mappings.
*/
public Configuration newHibernateConfiguration() {
Configuration newHibernateConfiguration = new Configuration();
for (Class> entityClass : persistenceClasses) {
newHibernateConfiguration.addClass(entityClass);
}
Properties properties = new Properties();
// JDBC
properties.put(AvailableSettings.URL, topiaConfiguration.getJdbcConnectionUrl());
properties.put(AvailableSettings.DRIVER, topiaConfiguration.getJdbcDriverClass().getName());
properties.put(AvailableSettings.USER, topiaConfiguration.getJdbcConnectionUser());
properties.put(AvailableSettings.PASS, topiaConfiguration.getJdbcConnectionPassword());
// dialect
properties.put(AvailableSettings.DIALECT, getHibernateDialect(topiaConfiguration));
// using c3p0 with default configuration
if (topiaConfiguration.isUseHikariForJdbcConnectionPooling()) {
properties.put(AvailableSettings.CONNECTION_PROVIDER, HIKARI_CP_HIBERNATE_CONNECTION_PROVIDER_CLASS_NAME);
} else {
properties.put(AvailableSettings.CONNECTION_PROVIDER, C3P0_HIBERNATE_CONNECTION_PROVIDER_CLASS_NAME);
properties.put(AvailableSettings.C3P0_MIN_SIZE, 5);
properties.put(AvailableSettings.C3P0_MAX_SIZE, 20);
properties.put(AvailableSettings.C3P0_TIMEOUT, 1800);
properties.put(AvailableSettings.C3P0_MAX_STATEMENTS, 50);
}
// schema validation
if (topiaConfiguration.isValidateSchema()) {
properties.put(AvailableSettings.HBM2DDL_AUTO, "validate");
}
properties.put(HibernateAvailableSettings.NAMING_STRATEGY, org.hibernate.cfg.ImprovedNamingStrategy.class.getName());
properties.put(AvailableSettings.FORMAT_SQL, true);
properties.put(AvailableSettings.USE_SQL_COMMENTS, true);
// user specific configuration
properties.putAll(topiaConfiguration.getHibernateExtraConfiguration());
if (log.isInfoEnabled()) {
log.info("will start hibernate with configuration " + properties);
}
newHibernateConfiguration.setProperties(properties);
return newHibernateConfiguration;
}
/**
* Get Hibernate {@link org.hibernate.dialect.Dialect} to use for given {@link TopiaConfiguration}.
*
* Prefer user defined dialect over dialect guessed by ToPIA;
* Warn user if dialect declared seems wrong (H2 dialect for a PostgreSQL database)
*
* @throws TopiaMisconfigurationException if user must add dialect to its configuration (because it can not be guessed)
*/
public static String getHibernateDialect(TopiaConfiguration topiaConfiguration) {
String jdbcConnectionUrl = topiaConfiguration.getJdbcConnectionUrl();
String guessedDialect = guessHibernateDialect(jdbcConnectionUrl);
String userDefinedDialect = topiaConfiguration.getHibernateExtraConfiguration().get(AvailableSettings.DIALECT);
String dialect;
if (guessedDialect == null) {
if (userDefinedDialect == null) {
String message = String.format(
"unable to guess Hibernate dialect to use for JDBC URL %s please patch ToPIA or configure Hibernate manually using %s",
jdbcConnectionUrl,
HibernateAvailableSettings.DIALECT);
throw new TopiaMisconfigurationException(message, topiaConfiguration);
} else {
dialect = userDefinedDialect;
}
} else {
if (userDefinedDialect == null) {
dialect = guessedDialect;
} else {
dialect = userDefinedDialect;
if (guessedDialect.equals(userDefinedDialect)) {
log.info("configuration defined hibernate dialect " + userDefinedDialect + " but ToPIA could have guessed it (you can remove the configuration directive safely)");
} else {
log.warn("not sure if " + dialect + " is suitable for " + jdbcConnectionUrl);
}
}
}
return dialect;
}
/**
* @deprecated Hibernate can do a much better job at guessing the dialect by calling JDBC and discovering the actual DBMS version and the dialect to use.
*/
@Deprecated
public static String guessHibernateDialect(String jdbcConnectionUrl) {
JdbcConfigurationBuilder jdbcConfigurationBuilder = new JdbcConfigurationBuilder();
String guessedHibernateDialect = null;
// DB2
if (jdbcConfigurationBuilder.isDb2Url(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl) && jdbcConnectionUrl.contains("MODE=DB2")) {
guessedHibernateDialect = DB2Dialect.class.getName();
// Derby
} else if (jdbcConfigurationBuilder.isDerbyUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl) && jdbcConnectionUrl.contains("MODE=Derby")) {
guessedHibernateDialect = DerbyDialect.class.getName();
// HSQLDB
} else if (jdbcConfigurationBuilder.isHsqlDbUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl) && jdbcConnectionUrl.contains("MODE=HSQLDB")) {
guessedHibernateDialect = HSQLDialect.class.getName();
// MySQL
} else if (jdbcConfigurationBuilder.isMysqlUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isMariaDbUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isGoogleAppEngineUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl) && jdbcConnectionUrl.contains("MODE=MySQL")) {
guessedHibernateDialect = MySQL5Dialect.class.getName();
// Oracle
} else if (jdbcConfigurationBuilder.isOracleUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl) && jdbcConnectionUrl.contains("MODE=Oracle")) {
guessedHibernateDialect = Oracle10gDialect.class.getName();
// PostgreSQL
} else if (jdbcConfigurationBuilder.isPostgreSqlUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl) && jdbcConnectionUrl.contains("MODE=PostgreSQL")) {
guessedHibernateDialect = PostgreSQL9Dialect.class.getName();
// MS SQLServer
} else if (jdbcConfigurationBuilder.isJtdsUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isSqlServerUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl) && jdbcConnectionUrl.contains("MODE=MSSQLServer")) {
guessedHibernateDialect = SQLServer2012Dialect.class.getName();
// SQLite
} else if (jdbcConfigurationBuilder.isSqliteUrl(jdbcConnectionUrl)
|| jdbcConfigurationBuilder.isSqlDroidUrl(jdbcConnectionUrl)) {
guessedHibernateDialect = null;
// H2
} else if (jdbcConfigurationBuilder.isH2Url(jdbcConnectionUrl)) {
guessedHibernateDialect = H2Dialect.class.getName();
} else {
if (log.isWarnEnabled()) {
log.warn("unable to guess Hibernate dialect for JDBC URL " + jdbcConnectionUrl + ". Please, submit a patch!");
}
}
return guessedHibernateDialect;
}
/**
* Method to extract from the given Hibernate SessionFactory a working instance of StandardServiceRegistry
*
* IMPORTANT : As much as possible, prefer using the
* {@link #getSessionFactoryServiceRegistry(org.hibernate.SessionFactory)} mthod instead of the current one because
* the SessionFactoryServiceRegistry is a child of the StandardServiceRegistry
*
* NB: This method is static to make sure it does not depend on the current instance
*
* @param sessionFactory the Hibernate's SessionFactory instance
* @return the StandardServiceRegistry instance used by the given SessionFactory
*/
public static StandardServiceRegistry getStandardServiceRegistry(SessionFactory sessionFactory) {
// AThimel 03/04/14 The next two lines are the good way to get the StandardServiceRegistry in Hibernate 4.3
SessionFactoryOptions sessionFactoryOptions = sessionFactory.getSessionFactoryOptions();
return sessionFactoryOptions.getServiceRegistry();
}
/**
* Method to extract from the given Hibernate SessionFactory a working instance of SessionFactoryServiceRegistry
*
* IMPORTANT : If possible, prefer using this method instead of
* {@link #getStandardServiceRegistry(org.hibernate.SessionFactory)} because the SessionFactoryServiceRegistry is a
* child of the StandardServiceRegistry
*
* NB: This method is static to make sure it does not depend on the current instance
*
* @param sessionFactory the Hibernate's SessionFactory instance
* @return the SessionFactoryServiceRegistry instance used by the given SessionFactory
*/
protected static SessionFactoryServiceRegistry getSessionFactoryServiceRegistry(SessionFactory sessionFactory) {
// AThimel 03/04/14 The next two lines are the good way to get the SessionFactoryServiceRegistry in Hibernate 4.3
SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor) sessionFactory;
return (SessionFactoryServiceRegistry) sessionFactoryImplementor.getServiceRegistry();
}
/**
* Method to get an Hibernate service instance from a given Hibernate SessionFactory
*
* NB: This method is static to make sure it does not depend on the current instance
*
* @param sessionFactory the Hibernate's SessionFactory instance
* @param serviceClass the expected service class
* @param type of service
* @return the found service instance
* @throws org.hibernate.service.UnknownServiceException Indicates the service was not known.
* @see org.hibernate.service.ServiceRegistry#getService(Class)
*/
public static S getHibernateService(SessionFactory sessionFactory, Class serviceClass) {
// Hibernate 4.3.x : prefer using the SessionFactoryServiceRegistry method instead of StandardServiceRegistry
// because SessionFactoryServiceRegistry is a child of the StandardServiceRegistry
ServiceRegistry serviceRegistry = getSessionFactoryServiceRegistry(sessionFactory);
return serviceRegistry.getService(serviceClass);
}
public void close() {
metaData = null;
if (hibernateSessionFactory != null) {
Preconditions.checkState(!hibernateSessionFactory.isClosed());
// close connection provider if possible (http://nuiton.org/issues/2757)
ConnectionProvider service = getHibernateService(hibernateSessionFactory, ConnectionProvider.class);
if (service instanceof Stoppable) {
Stoppable stoppable = (Stoppable) service;
stoppable.stop();
}
hibernateSessionFactory.close();
}
}
public SessionFactory getSessionFactory() {
if (hibernateSessionFactory == null) {
Configuration effectiveHibernateConfiguration = getHibernateConfiguration();
hibernateSessionFactory = TopiaUtil.newSessionFactory(effectiveHibernateConfiguration);
}
return hibernateSessionFactory;
}
public SessionFactory newSessionFactory(Configuration effectiveHibernateConfiguration) {
return TopiaUtil.newSessionFactory(effectiveHibernateConfiguration);
}
public Metadata getMetaData() {
if (metaData == null) {
metaData = newMetaData(getHibernateConfiguration(), getSessionFactory());
}
return metaData;
}
public Metadata newMetaData(Configuration configuration, SessionFactory sessionFactory) {
return newMetaData(configuration, sessionFactory, persistenceClasses);
}
public Metadata newMetaData(Configuration configuration, SessionFactory sessionFactory, Collection> persistenceClasses) {
StandardServiceRegistry standardServiceRegistry = getStandardServiceRegistry(sessionFactory);
MetadataSources sources = new MetadataSources(standardServiceRegistry);
for (Class> persistanceClass : persistenceClasses) {
String hbmXmlFile = persistanceClass.getName().replace('.', '/') + ".hbm.xml";
sources.addResource(hbmXmlFile);
configuration.addClass(persistanceClass);
}
MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
return metadataBuilder.build();
}
}