HibernateSessionFactoryProvider.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Carsten Hammer
*******************************************************************************/
package org.eclipse.jgit.storage.hibernate.config;
import java.util.Properties;
import org.eclipse.jgit.storage.hibernate.entity.GitCommitIndex;
import org.eclipse.jgit.storage.hibernate.entity.GitObjectEntity;
import org.eclipse.jgit.storage.hibernate.entity.GitPackEntity;
import org.eclipse.jgit.storage.hibernate.entity.GitRefEntity;
import org.eclipse.jgit.storage.hibernate.entity.GitReflogEntity;
import org.eclipse.jgit.storage.hibernate.entity.FilePathHistory;
import org.eclipse.jgit.storage.hibernate.entity.JavaBlobIndex;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* Provides a Hibernate {@link SessionFactory} configured for JGit database
* storage.
*/
public class HibernateSessionFactoryProvider {
private final SessionFactory sessionFactory;
/**
* Create a provider with the given configuration properties.
*
* @param properties
* Hibernate configuration properties including connection URL,
* driver, dialect, etc. Hibernate Search defaults to a
* local-filesystem Lucene backend if not configured explicitly.
* Set {@code hibernate.search.backend.directory.type} to
* {@code local-heap} for in-memory indexes (suitable for
* testing only).
*/
public HibernateSessionFactoryProvider(Properties properties) {
Configuration cfg = new Configuration();
cfg.addProperties(properties);
// Default Hibernate Search to Lucene backend
if (!properties.containsKey("hibernate.search.backend.type")) { //$NON-NLS-1$
cfg.setProperty("hibernate.search.backend.type", "lucene"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!properties
.containsKey("hibernate.search.backend.directory.type")) { //$NON-NLS-1$
cfg.setProperty("hibernate.search.backend.directory.type", //$NON-NLS-1$
"local-filesystem"); //$NON-NLS-1$
}
if (!properties
.containsKey("hibernate.search.backend.directory.root") //$NON-NLS-1$
&& "local-filesystem".equals(cfg.getProperties().get( //$NON-NLS-1$
"hibernate.search.backend.directory.type"))) { //$NON-NLS-1$
String root = System.getenv("JGIT_SEARCH_INDEX_DIR"); //$NON-NLS-1$
if (root == null || root.isEmpty()) {
root = "jgit-search-index"; //$NON-NLS-1$
}
cfg.setProperty("hibernate.search.backend.directory.root", //$NON-NLS-1$
root);
}
// Default to HikariCP connection pool if not explicitly configured
if (!properties.containsKey("hibernate.connection.provider_class")) { //$NON-NLS-1$
cfg.setProperty("hibernate.connection.provider_class", //$NON-NLS-1$
"org.hibernate.hikaricp.internal.HikariCPConnectionProvider"); //$NON-NLS-1$
}
if (!properties.containsKey("hibernate.hikari.minimumIdle")) { //$NON-NLS-1$
cfg.setProperty("hibernate.hikari.minimumIdle", "5"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!properties.containsKey("hibernate.hikari.maximumPoolSize")) { //$NON-NLS-1$
cfg.setProperty("hibernate.hikari.maximumPoolSize", "20"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!properties.containsKey("hibernate.hikari.idleTimeout")) { //$NON-NLS-1$
cfg.setProperty("hibernate.hikari.idleTimeout", "300000"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!properties.containsKey("hibernate.hikari.connectionTimeout")) { //$NON-NLS-1$
cfg.setProperty("hibernate.hikari.connectionTimeout", "20000"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!properties.containsKey("hibernate.hikari.maxLifetime")) { //$NON-NLS-1$
cfg.setProperty("hibernate.hikari.maxLifetime", "1200000"); //$NON-NLS-1$ //$NON-NLS-2$
}
// Keep connections open for the full transaction — required for
// PostgreSQL LOB access through HikariCP
if (!properties.containsKey(
"hibernate.connection.handling_mode")) { //$NON-NLS-1$
cfg.setProperty("hibernate.connection.handling_mode", //$NON-NLS-1$
"DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION"); //$NON-NLS-1$
}
// Default to second-level cache with Caffeine/JCache
if (!properties.containsKey("hibernate.cache.use_second_level_cache")) { //$NON-NLS-1$
cfg.setProperty("hibernate.cache.use_second_level_cache", //$NON-NLS-1$
"true"); //$NON-NLS-1$
}
if (!properties.containsKey("hibernate.cache.region.factory_class")) { //$NON-NLS-1$
cfg.setProperty("hibernate.cache.region.factory_class", //$NON-NLS-1$
"jcache"); //$NON-NLS-1$
}
if (!properties
.containsKey("hibernate.search.backend.analysis.configurer")) { //$NON-NLS-1$
cfg.setProperty("hibernate.search.backend.analysis.configurer", //$NON-NLS-1$
"class:org.eclipse.jgit.storage.hibernate.search.JavaSourceAnalysisConfigurer"); //$NON-NLS-1$
}
cfg.addAnnotatedClass(GitObjectEntity.class);
cfg.addAnnotatedClass(GitRefEntity.class);
cfg.addAnnotatedClass(GitPackEntity.class);
cfg.addAnnotatedClass(GitReflogEntity.class);
cfg.addAnnotatedClass(GitCommitIndex.class);
cfg.addAnnotatedClass(JavaBlobIndex.class);
cfg.addAnnotatedClass(FilePathHistory.class);
this.sessionFactory = cfg.buildSessionFactory();
}
/**
* Create a provider with an existing session factory.
*
* @param sessionFactory
* the session factory to use
*/
public HibernateSessionFactoryProvider(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Get the session factory.
*
* @return the Hibernate session factory
*/
public SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* Close the session factory and release resources.
*/
public void close() {
if (sessionFactory != null && !sessionFactory.isClosed()) {
sessionFactory.close();
}
}
}