All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bonitasoft.engine.persistence.AbstractHibernatePersistenceService Maven / Gradle / Ivy

There is a newer version: 10.2.0
Show newest version
/**
 * Copyright (C) 2019 Bonitasoft S.A.
 * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation
 * version 2.1 of the License.
 * This library 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 Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 **/
package org.bonitasoft.engine.persistence;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.sql.DataSource;

import org.bonitasoft.engine.commons.ClassReflector;
import org.bonitasoft.engine.commons.exceptions.SRetryableException;
import org.bonitasoft.engine.log.technical.TechnicalLogSeverity;
import org.bonitasoft.engine.log.technical.TechnicalLoggerService;
import org.bonitasoft.engine.sequence.SequenceManager;
import org.bonitasoft.engine.services.SPersistenceException;
import org.bonitasoft.engine.services.UpdateDescriptor;
import org.bonitasoft.engine.services.Vendor;
import org.bonitasoft.engine.sessionaccessor.STenantIdNotSetException;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleStateException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.query.Query;
import org.hibernate.stat.Statistics;

/**
 * Hibernate implementation of the persistence service
 *
 * @author Charles Souillard
 * @author Nicolas Chabanoles
 * @author Yanyan Liu
 * @author Matthieu Chaffotte
 * @author Celine Souchet
 * @author Laurent Vaills
 * @author Guillaume Rosinosky
 */
public abstract class AbstractHibernatePersistenceService extends AbstractDBPersistenceService {

    private final SessionFactory sessionFactory;

    private final Map classAliasMappings;

    private final Map cacheQueries;

    private final List> classMapping;

    private final List mappingExclusions;
    private Statistics statistics;
    private int stat_display_count;
    private QueryBuilderFactory queryBuilderFactory;

    protected AbstractHibernatePersistenceService(final SessionFactory sessionFactory,
            final List> classMapping,
            final Map classAliasMappings, final boolean enableWordSearch,
            final Set wordSearchExclusionMappings, final TechnicalLoggerService logger)
            throws Exception {
        super("TEST", logger);
        this.sessionFactory = sessionFactory;
        this.classAliasMappings = classAliasMappings;
        this.classMapping = classMapping;
        statistics = sessionFactory.getStatistics();
        cacheQueries = Collections.emptyMap();
        mappingExclusions = Collections.emptyList();
        queryBuilderFactory = new QueryBuilderFactory(OrderByCheckingMode.NONE, classAliasMappings, '#',
                enableWordSearch, wordSearchExclusionMappings);
    }

    public AbstractHibernatePersistenceService(final String name,
            final HibernateConfigurationProvider hbmConfigurationProvider,
            final Properties extraHibernateProperties,
            final TechnicalLoggerService logger,
            final SequenceManager sequenceManager, final DataSource datasource, QueryBuilderFactory queryBuilderFactory)
            throws Exception {
        super(name, sequenceManager, datasource, logger);
        hbmConfigurationProvider.bootstrap(extraHibernateProperties);
        sessionFactory = hbmConfigurationProvider.getSessionFactory();

        this.queryBuilderFactory = queryBuilderFactory;
        if (hbmConfigurationProvider.getVendor() == Vendor.SQLSERVER) {
            this.queryBuilderFactory.setOrderByBuilder(new SQLServerOrderByBuilder());
        }
        statistics = sessionFactory.getStatistics();
        classMapping = hbmConfigurationProvider.getMappedClasses();
        classAliasMappings = hbmConfigurationProvider.getClassAliasMappings();
        mappingExclusions = hbmConfigurationProvider.getMappingExclusions();
        cacheQueries = hbmConfigurationProvider.getCacheQueries();
    }

    /**
     * Log synthetic information about cache every 10.000 sessions, if hibernate.gather_statistics, is enabled.
     */
    private void logStats() {
        if (!statistics.isStatisticsEnabled() || !logger.isLoggable(this.getClass(), TechnicalLogSeverity.INFO)) {
            return;
        }
        if (stat_display_count == 10 || stat_display_count == 100 || stat_display_count == 1000
                || stat_display_count % 10000 == 0) {
            final long query_cache_hit = statistics.getQueryCacheHitCount();
            final long query_cache_miss = statistics.getQueryCacheMissCount();
            final long query_cache_put = statistics.getQueryCachePutCount();
            final long level_2_cache_hit = statistics.getSecondLevelCacheHitCount();
            final long level_2_cache_miss = statistics.getSecondLevelCacheMissCount();
            final long level_2_put = statistics.getSecondLevelCachePutCount();

            logger.log(this.getClass(), TechnicalLogSeverity.INFO, "Query Cache Ratio "
                    + (int) ((double) query_cache_hit / (query_cache_hit + query_cache_miss) * 100) + "% "
                    + query_cache_hit + " hits " + query_cache_miss
                    + " miss " + query_cache_put + " puts");
            logger.log(this.getClass(), TechnicalLogSeverity.INFO, "2nd Level Cache Ratio "
                    + (int) ((double) level_2_cache_hit / (level_2_cache_hit + level_2_cache_miss) * 100) + "% "
                    + level_2_cache_hit + " hits "
                    + level_2_cache_miss + " miss " + level_2_put + " puts");
        }
        stat_display_count++;
    }

    protected Session getSession(final boolean useTenant) throws SPersistenceException {
        logStats();
        try {
            return sessionFactory.getCurrentSession();
        } catch (final HibernateException e) {
            throw new SPersistenceException(e);
        }
    }

    void flushStatements(final boolean useTenant) throws SPersistenceException {
        final Session session = getSession(useTenant);
        session.flush();
    }

    @Override
    public void delete(final PersistentObject entity) throws SPersistenceException {
        if (logger.isLoggable(getClass(), TechnicalLogSeverity.DEBUG)) {
            logger.log(this.getClass(), TechnicalLogSeverity.DEBUG,
                    "Deleting instance of class " + entity.getClass().getSimpleName() + " with id=" + entity.getId());
        }
        final Class mappedClass = getMappedClass(entity.getClass());
        final Session session = getSession(true);
        try {
            if (session.contains(entity)) {
                session.delete(entity);
            } else {
                // Deletion must be performed on the session entity and not on a potential transitional entity.
                final Object pe = session.get(mappedClass, new PersistentObjectId(entity.getId(), 0));
                session.delete(pe);
            }
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException he) {
            throw new SPersistenceException(he);
        }
    }

    @Override
    public int update(final String updateQueryName) throws SPersistenceException {
        return update(updateQueryName, null);
    }

    @Override
    public int update(final String updateQueryName, final Map inputParameters)
            throws SPersistenceException {
        final Query query = getSession(true).getNamedQuery(updateQueryName);
        try {
            if (inputParameters != null) {
                setParameters(query, inputParameters);
            }
            return query.executeUpdate();
        } catch (final HibernateException he) {
            throw new SPersistenceException(he);
        }
    }

    @Override
    public void deleteAll(final Class entityClass) throws SPersistenceException {
        final Class mappedClass = getMappedClass(entityClass);
        final Query query = getSession(true).getNamedQuery("deleteAll" + mappedClass.getSimpleName());
        try {
            query.executeUpdate();
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException he) {
            throw new SPersistenceException(he);
        }
    }

    @Override
    public void insert(final PersistentObject entity) throws SPersistenceException {
        final Class entityClass = entity.getClass();
        checkClassMapping(entityClass);
        final Session session = getSession(true);
        setId(entity);
        try {
            session.save(entity);
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException he) {
            throw new SPersistenceException(he);
        }
    }

    @Override
    public void insertInBatch(final List entities) throws SPersistenceException {
        if (!entities.isEmpty()) {
            final Session session = getSession(true);
            for (final PersistentObject entity : entities) {
                final Class entityClass = entity.getClass();
                checkClassMapping(entityClass);
                setId(entity);
                session.save(entity);
            }
        }
    }

    @Override
    public void update(final UpdateDescriptor updateDescriptor) throws SPersistenceException {
        // FIXME: deal with disconnected objects:
        final Class entityClass = updateDescriptor.getEntity().getClass();
        checkClassMapping(entityClass);
        final PersistentObject entity = updateDescriptor.getEntity();
        final Session session = getSession(false);
        if (!session.contains(entity)) {
            throw new SPersistenceException("The object cannot be updated because it's disconnected " + entity);
        }
        for (final Map.Entry field : updateDescriptor.getFields().entrySet()) {
            setField(entity, field.getKey(), field.getValue());
        }
    }

    private void setField(final PersistentObject entity, final String fieldName, final Object parameterValue)
            throws SPersistenceException {
        Long id = null;
        try {
            id = entity.getId();
            ClassReflector.setField(entity, fieldName, parameterValue);
        } catch (final Exception e) {
            throw new SPersistenceException("Problem while updating entity: " + entity + " with id: " + id, e);
        }
    }

    @Override
    public  T selectOne(final SelectOneDescriptor selectDescriptor) throws SBonitaReadException {
        try {
            return selectOne(getSession(true), selectDescriptor);
        } catch (final SPersistenceException e) {
            throw new SBonitaReadException(e, selectDescriptor);
        }
    }

    Class getMappedClass(final Class entityClass)
            throws SPersistenceException {
        if (classMapping.contains(entityClass)) {
            return entityClass;
        }
        throw new SPersistenceException("Unable to locate class " + entityClass + " in Hibernate configuration");
    }

    private void checkClassMapping(final Class entityClass) throws SPersistenceException {
        if (!classMapping.contains(entityClass) && !mappingExclusions.contains(entityClass.getName())) {
            throw new SPersistenceException("Unable to locate class " + entityClass + " in Hibernate configuration");
        }
    }

    @Override
    public  T selectById(final SelectByIdDescriptor selectDescriptor)
            throws SBonitaReadException {
        try {
            final Session session = getSession(true);
            final T object = this.selectById(session, selectDescriptor);
            if (selectDescriptor.isReadOnly()) {
                disconnectEntityFromSession(session, object);
            }
            return object;
        } catch (final SPersistenceException e) {
            throw new SBonitaReadException(e, selectDescriptor);
        }
    }

    private static  void disconnectEntityFromSession(Session session, T object) {
        if (object != null) {
            session.evict(object);
        }
    }

    @Override
    protected void setId(PersistentObject entity) throws SPersistenceException {
        super.setId(entity);
    }

    @SuppressWarnings("unchecked")
     T selectById(final Session session, final SelectByIdDescriptor selectDescriptor)
            throws SBonitaReadException {
        Class mappedClass;
        try {
            mappedClass = getMappedClass(selectDescriptor.getEntityType());
        } catch (final SPersistenceException e) {
            throw new SBonitaReadException(e);
        }
        try {
            return (T) session.get(mappedClass, selectDescriptor.getId());
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException he) {
            throw new SBonitaReadException(he);
        }
    }

    @SuppressWarnings("unchecked")
    private  T selectOne(final Session session, final SelectOneDescriptor selectDescriptor)
            throws SBonitaReadException {
        try {
            checkClassMapping(selectDescriptor.getEntityType());
        } catch (final SPersistenceException e) {
            throw new SBonitaReadException(e);
        }
        final Query query = session.getNamedQuery(selectDescriptor.getQueryName());
        setQueryCache(query, selectDescriptor.getQueryName());
        final Map parameters = selectDescriptor.getInputParameters();
        if (parameters != null) {
            setParameters(query, parameters);
        }
        query.setMaxResults(1);
        try {
            return disconnectIfReadOnly((T) query.uniqueResult(), query, session);
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException he) {
            throw new SBonitaReadException(he);
        }
    }

    private boolean isCacheEnabled(String queryName) {
        return cacheQueries != null && cacheQueries.containsKey(queryName);
    }

    private void setQueryCache(final Query query, final String name) {
        if (isCacheEnabled(name)) {
            query.setCacheable(true);
        }
    }

    @Override
    public  List selectList(final SelectListDescriptor selectDescriptor) throws SBonitaReadException {
        try {
            final Class entityClass = selectDescriptor.getEntityType();
            checkClassMapping(entityClass);

            final Session session = getSession(true);

            org.hibernate.query.Query query = queryBuilderFactory.createQueryBuilderFor(session, selectDescriptor)
                    .tenantId(getTenantId())
                    .cache(isCacheEnabled(selectDescriptor.getQueryName()))
                    .build();

            @SuppressWarnings("unchecked")
            final List list = query.list();
            if (list != null) {
                disconnectIfReadOnly(list, query, session);
                return list;
            }
            return Collections.emptyList();
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException | SPersistenceException | STenantIdNotSetException e) {
            throw new SBonitaReadException(e, selectDescriptor);
        }
    }

    private static  void disconnectIfReadOnly(List list, Query query, Session session) {
        if (query.isReadOnly()) {
            for (T t : list) {
                disconnectEntityFromSession(session, t);
            }
        }
    }

    private static  T disconnectIfReadOnly(T object, Query query, Session session) {
        if (query.isReadOnly()) {
            disconnectEntityFromSession(session, object);
        }
        return object;
    }

    private void setParameters(final Query query, final Map inputParameters) {
        for (final Map.Entry entry : inputParameters.entrySet()) {
            final Object value = entry.getValue();
            if (value instanceof Collection) {
                query.setParameterList(entry.getKey(), (Collection) value);
            } else {
                query.setParameter(entry.getKey(), value);
            }
        }
    }

    public Map getClassAliasMappings() {
        return classAliasMappings;
    }

    @Override
    public void delete(final long id, final Class entityClass)
            throws SPersistenceException {
        final Class mappedClass = getMappedClass(entityClass);
        final Query query = getSession(true).getNamedQuery("delete" + mappedClass.getSimpleName());
        query.setParameter("id", id);
        try {
            query.executeUpdate();
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException he) {
            throw new SPersistenceException(he);
        }
    }

    @Override
    public void delete(final List ids, final Class entityClass)
            throws SPersistenceException {
        final Class mappedClass = getMappedClass(entityClass);
        final Query query = getSession(true).getNamedQuery("deleteByIds" + mappedClass.getSimpleName());
        query.setParameterList("ids", ids);
        try {
            query.executeUpdate();
        } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) {
            throw new SRetryableException(e);
        } catch (final HibernateException sse) {
            throw new SPersistenceException(sse);
        }
    }

    public void destroy() {
        logger.log(getClass(), TechnicalLogSeverity.INFO,
                "Closing Hibernate session factory of " + getClass().getName());
        sessionFactory.close();
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy