ch.systemsx.cisd.openbis.generic.server.dataaccess.db.ExternalDataDAO.java Source code

Java tutorial

Introduction

Here is the source code for ch.systemsx.cisd.openbis.generic.server.dataaccess.db.ExternalDataDAO.java

Source

/*
 * Copyright 2008 ETH Zuerich, CISD
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ch.systemsx.cisd.openbis.generic.server.dataaccess.db;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.hibernate.FetchMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

import ch.systemsx.cisd.common.logging.LogCategory;
import ch.systemsx.cisd.common.logging.LogFactory;
import ch.systemsx.cisd.common.utilities.MethodUtils;
import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExternalDataDAO;
import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolder;
import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
import ch.systemsx.cisd.openbis.generic.shared.dto.CodeConverter;
import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE;
import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
import ch.systemsx.cisd.openbis.generic.shared.dto.TableNames;
import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;

/**
 * Implementation of {@link IExternalDataDAO} for databases.
 * 
 * @author Christian Ribeaud
 */
final class ExternalDataDAO extends AbstractGenericEntityDAO<ExternalDataPE> implements IExternalDataDAO {
    private final static Class<ExternalDataPE> ENTITY_CLASS = ExternalDataPE.class;

    private final static Class<DataPE> ENTITY_SUPER_CLASS = DataPE.class;

    private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, ExternalDataDAO.class);

    private static final String TABLE_NAME = ENTITY_CLASS.getSimpleName();

    ExternalDataDAO(final SessionFactory sessionFactory, final DatabaseInstancePE databaseInstance) {
        super(sessionFactory, databaseInstance, ENTITY_CLASS);
    }

    //
    // IExternalDataDAO
    //

    public boolean hasExternalData(SamplePE sample) throws DataAccessException {
        final DetachedCriteria criteria = DetachedCriteria.forClass(ExternalDataPE.class);
        criteria.add(Restrictions.eq("sampleInternal", sample));
        criteria.setProjection(Projections.rowCount());
        Integer count = (Integer) getHibernateTemplate().findByCriteria(criteria).get(0);
        return count > 0;
    }

    public final List<ExternalDataPE> listRelatedExternalData(final IEntityInformationHolder entity)
            throws DataAccessException {
        assert entity != null : "Unspecified entity.";

        final String entityName = entity.getEntityKind().toString().toLowerCase();
        final String query = String.format("from %s e " + "left join fetch e.experimentInternal "
                + "left join fetch e.sampleInternal " + "left join fetch e.parents "
                + "left join fetch e.dataSetProperties " + "where e.%sInternal.id = ?", TABLE_NAME, entityName);
        final List<ExternalDataPE> list = cast(getHibernateTemplate().find(query, toArray(entity.getId())));

        // distinct does not work properly in HQL for left joins
        distinct(list);
        if (operationLog.isDebugEnabled()) {
            operationLog
                    .debug(String.format("%d external data have been found for [entity=%s].", list.size(), entity));
        }
        return list;
    }

    public final List<ExternalDataPE> listExternalData(final SamplePE sample) throws DataAccessException {
        assert sample != null : "Unspecified sample.";

        final String query = String
                .format("from %s e " + "left join fetch e.experimentInternal " + "left join fetch e.parents "
                        + "left join fetch e.dataSetProperties " + "where e.sampleInternal = ?", TABLE_NAME);
        final List<ExternalDataPE> list = cast(getHibernateTemplate().find(query, toArray(sample)));

        // distinct does not work properly in HQL for left joins
        distinct(list);
        if (operationLog.isDebugEnabled()) {
            operationLog
                    .debug(String.format("%d external data have been found for [sample=%s].", list.size(), sample));
        }
        return list;
    }

    public final List<ExternalDataPE> listExternalData(final DataStorePE dataStore) throws DataAccessException {
        assert dataStore != null : "Unspecified data store.";
        final DetachedCriteria criteria = DetachedCriteria.forClass(ExternalDataPE.class);
        criteria.add(Restrictions.eq("dataStore", dataStore));
        final List<ExternalDataPE> list = cast(getHibernateTemplate().findByCriteria(criteria));
        if (operationLog.isDebugEnabled()) {
            operationLog.debug(String.format("%s(%s): %d data set(s) have been found.",
                    MethodUtils.getCurrentMethod().getName(), dataStore, list.size()));
        }
        return list;
    }

    public final List<ExternalDataPE> listExternalData(final ExperimentPE experiment) throws DataAccessException {
        assert experiment != null : "Unspecified experiment.";

        final String query = String
                .format("from %s e " + "left join fetch e.experimentInternal " + "left join fetch e.parents "
                        + "left join fetch e.dataSetProperties " + "where e.experimentInternal = ?", TABLE_NAME);
        final List<ExternalDataPE> list = cast(getHibernateTemplate().find(query, toArray(experiment)));

        // distinct does not work properly in HQL for left joins
        distinct(list);
        if (operationLog.isDebugEnabled()) {
            operationLog.debug(String.format("%d external data have been found for [experiment=%e].", list.size(),
                    experiment));
        }
        return list;
    }

    private void distinct(List<ExternalDataPE> list) {
        Set<ExternalDataPE> set = new TreeSet<ExternalDataPE>(list);
        list.clear();
        list.addAll(set);
    }

    public DataPE tryToFindDataSetByCode(String dataSetCode) {
        assert dataSetCode != null : "Unspecified data set code.";

        String name = ENTITY_SUPER_CLASS.getSimpleName();
        String hql = String.format("select e from %s e where e.code = ?", name);
        String normalizedCode = CodeConverter.tryToDatabase(dataSetCode);
        final List<DataPE> list = cast(getHibernateTemplate().find(hql, toArray(normalizedCode)));
        final DataPE entity = tryFindEntity(list, "data set");
        if (operationLog.isDebugEnabled()) {
            String methodName = MethodUtils.getCurrentMethod().getName();
            operationLog.debug(String.format("%s(%s): '%s'.", methodName, dataSetCode, entity));
        }
        return entity;
    }

    public ExternalDataPE tryToFindFullDataSetByCode(String dataSetCode, boolean withPropertyTypes) {
        assert dataSetCode != null : "Unspecified data set code";

        final String mangledCode = CodeConverter.tryToDatabase(dataSetCode);
        final Criterion codeEq = Restrictions.eq("code", mangledCode);

        final DetachedCriteria criteria = DetachedCriteria.forClass(ENTITY_CLASS);
        criteria.add(codeEq);
        if (withPropertyTypes) {
            criteria.setFetchMode("dataSetType.dataSetTypePropertyTypesInternal", FetchMode.JOIN);
        }
        criteria.setResultTransformer(DetachedCriteria.DISTINCT_ROOT_ENTITY);
        final List<ExternalDataPE> list = cast(getHibernateTemplate().findByCriteria(criteria));
        final ExternalDataPE entity = tryFindEntity(list, "data set");
        if (operationLog.isDebugEnabled()) {
            operationLog
                    .debug(String.format("External data '%s' found for data set code '%s'.", entity, dataSetCode));
        }
        return entity;
    }

    public void createDataSet(DataPE dataset) {
        assert dataset != null : "Unspecified data set.";

        dataset.setCode(CodeConverter.tryToDatabase(dataset.getCode()));
        final HibernateTemplate template = getHibernateTemplate();
        template.save(dataset);
        template.flush();
        if (operationLog.isInfoEnabled()) {
            operationLog.info(String.format("ADD: data set '%s'.", dataset));
        }
    }

    public void updateDataSet(ExternalDataPE externalData) {
        assert externalData != null : "Given external data can not be null.";
        validatePE(externalData);

        final HibernateTemplate hibernateTemplate = getHibernateTemplate();
        externalData.setCode(CodeConverter.tryToDatabase(externalData.getCode()));
        Long id = HibernateUtils.getId(externalData);
        final DataPE loaded = (DataPE) hibernateTemplate.load(ENTITY_CLASS, id);
        // This just means that we do not have any entry in 'EXTERNAL_DATA' table for this id. It
        // might happen when we work with placeholder data.
        if (loaded instanceof ExternalDataPE == false) {
            String location = externalData.getLocation();
            Long locatorTypeID = externalData.getLocatorType().getId();
            Long fileFormatTypeID = externalData.getFileFormatType().getId();
            char complete = externalData.getComplete().name().charAt(0);
            Long storageFormatTermID = externalData.getStorageFormatVocabularyTerm().getId();
            executeUpdate(
                    "insert into " + TableNames.EXTERNAL_DATA_TABLE
                            + " (data_id, location, loty_id, ffty_id, is_complete, cvte_id_stor_fmt) "
                            + "values (?, ?, ?, ?, ?, ?)",
                    id, location, locatorTypeID, fileFormatTypeID, complete, storageFormatTermID);
            hibernateTemplate.evict(loaded);
        }
        hibernateTemplate.update(externalData);
        hibernateTemplate.flush();
        if (operationLog.isInfoEnabled()) {
            operationLog.info(String.format("UPDATE: external data '%s'.", externalData));
        }
    }

    @Override
    public void delete(ExternalDataPE entity) throws DataAccessException {
        assert entity != null : "entity unspecified";
        if (entity.getChildren().size() > 0) {
            throw new DataIntegrityViolationException(
                    String.format("External Data '%s' cannot be deleted because children datasets are connected.",
                            entity.getCode()));
        }
        super.delete(entity);
    }

    @SuppressWarnings("unchecked")
    public Set<TechId> findParentIds(final Collection<TechId> dataSetIds) {
        // Native SQL query is used to be able to query on 'many-to-many association table -
        // - 'data_set_relationships' without join with 'data' table involved in this association.
        // Drawback of this solution is that instead of list of Long values we get list of
        // BigIntegers and so a transformation is needed.

        final String query = "select data_id_parent from data_set_relationships where data_id_child in (:ids)";
        final List<? extends Number> results = (List<? extends Number>) getHibernateTemplate()
                .execute(new HibernateCallback() {

                    //
                    // HibernateCallback
                    //

                    public final Object doInHibernate(final Session session) {
                        // we could remove this transformation if we choose to pass Long values
                        final List<Long> longIds = transformTechIds2Longs(dataSetIds);
                        return session.createSQLQuery(query).setParameterList("ids", longIds).list();
                    }
                });
        return transformNumbers2TechIds(results);
    }

    // data set relationship helper methods

    private List<Long> transformTechIds2Longs(Collection<TechId> techIds) {
        final List<Long> result = new ArrayList<Long>(techIds.size());
        for (TechId techId : techIds) {
            result.add(techId.getId());
        }
        return result;
    }

    private Set<TechId> transformNumbers2TechIds(Collection<? extends Number> numbers) {
        final Set<TechId> result = new HashSet<TechId>();
        for (Number number : numbers) {
            result.add(new TechId(number));
        }
        return result;
    }

}