edu.harvard.med.screensaver.db.ScreenResultsDAOImpl.java Source code

Java tutorial

Introduction

Here is the source code for edu.harvard.med.screensaver.db.ScreenResultsDAOImpl.java

Source

// $HeadURL$
// $Id$

// Copyright  2006, 2010, 2011, 2012 by the President and Fellows of Harvard College.

// Screensaver is an open-source project developed by the ICCB-L and NSRB labs
// at Harvard Medical School. This software is distributed under the terms of
// the GNU General Public License.

package edu.harvard.med.screensaver.db;

import java.util.List;
import java.util.Map;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.Transformer;
import org.apache.log4j.Logger;
import org.hibernate.CacheMode;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;

import edu.harvard.med.screensaver.db.Criterion.Operator;
import edu.harvard.med.screensaver.db.hqlbuilder.HqlBuilder;
import edu.harvard.med.screensaver.db.hqlbuilder.JoinType;
import edu.harvard.med.screensaver.model.DataModelViolationException;
import edu.harvard.med.screensaver.model.libraries.LibraryWellType;
import edu.harvard.med.screensaver.model.libraries.Reagent;
import edu.harvard.med.screensaver.model.libraries.Well;
import edu.harvard.med.screensaver.model.libraries.WellKey;
import edu.harvard.med.screensaver.model.screenresults.AnnotationType;
import edu.harvard.med.screensaver.model.screenresults.AnnotationValue;
import edu.harvard.med.screensaver.model.screenresults.AssayWell;
import edu.harvard.med.screensaver.model.screenresults.DataColumn;
import edu.harvard.med.screensaver.model.screenresults.DataType;
import edu.harvard.med.screensaver.model.screenresults.ResultValue;
import edu.harvard.med.screensaver.model.screenresults.ScreenResult;
import edu.harvard.med.screensaver.model.screens.Screen;
import edu.harvard.med.screensaver.model.screens.ScreenType;
import edu.harvard.med.screensaver.util.CollectionUtils;

public class ScreenResultsDAOImpl extends AbstractDAO implements ScreenResultsDAO {
    private static Logger log = Logger.getLogger(ScreenResultsDAOImpl.class);

    private GenericEntityDAO _dao;

    /**
     * @motivation for CGLIB dynamic proxy creation
     */
    public ScreenResultsDAOImpl() {
    }

    public ScreenResultsDAOImpl(GenericEntityDAO dao) {
        _dao = dao;
    }

    public Map<WellKey, ResultValue> findResultValuesByPlate(final Integer plateNumber, final DataColumn col) {
        List<ResultValue> result = runQuery(new edu.harvard.med.screensaver.db.Query() {
            public List<?> execute(Session session) {
                // NOTE: added fetch of the library into the session to fix lazy update problem when calling rv.isEdgeWell()->well.isEdgeWell() which needs the library see: [#1376]- sde4
                String hql = "select r from ResultValue r " + "left join fetch r.well w join fetch w.library l "
                        + "where r.dataColumn.id = :colId and w.id >= :firstWellInclusive "
                        + "and w.id < :lastWellExclusive";
                Query query = session.createQuery(hql);
                query.setParameter("colId", col.getEntityId());
                query.setParameter("firstWellInclusive", new WellKey(plateNumber, 0, 0).toString());
                query.setParameter("lastWellExclusive", new WellKey(plateNumber + 1, 0, 0).toString());
                return query.list();
            }
        });
        Map<WellKey, ResultValue> result2 = CollectionUtils.indexCollection(result,
                // note: calling rv.getWell().getWellId() does *not* require a db hit, since
                // a proxy can return its ID w/o forcing Hibernate to access the db;
                // so we use the id to instantiate the WellKey
                new Transformer() {
                    public Object transform(Object rv) {
                        return new WellKey(((ResultValue) rv).getWell().getWellId());
                    }
                }, WellKey.class, ResultValue.class);
        return result2;
    }

    public List<DataColumn> findMutualPositiveColumns(final ScreenResult screenResult) {
        return findMutualPositiveColumns(new HqlBuilderCallback() {
            @Override
            public void apply(HqlBuilder hql) {
                hql.where("aw1", "screenResult", Operator.EQUAL, screenResult).where("aw2", "screenResult",
                        Operator.NOT_EQUAL, screenResult); // ignore data columns from the screen result passed in
            }
        });
    }

    public List<DataColumn> findMutualPositiveColumns(final HqlBuilderCallback hqlBuilderCallback) {

        edu.harvard.med.screensaver.db.Query query = new edu.harvard.med.screensaver.db.Query() {
            public List<?> execute(Session session) {
                HqlBuilder hql = new HqlBuilder().select("dc").distinctProjectionValues()
                        .from(AssayWell.class, "aw1").from(AssayWell.class, "aw2")
                        .from("aw2", AssayWell.screenResult, "sr2", JoinType.INNER)
                        .from("sr2", ScreenResult.dataColumns, "dc", JoinType.INNER)
                        .where("aw1", "libraryWell", Operator.EQUAL, "aw2", "libraryWell")
                        .where("aw1", "positive", Operator.EQUAL, Boolean.TRUE)
                        .where("aw2", "positive", Operator.EQUAL, Boolean.TRUE)
                        .whereIn("dc", "dataType", Sets.newHashSet(DataType.POSITIVE_INDICATOR_BOOLEAN,
                                DataType.POSITIVE_INDICATOR_PARTITION));
                hqlBuilderCallback.apply(hql);
                return hql.toQuery(session, true).list();
            }
        };

        return runQuery(query);
    }

    public void deleteScreenResult(ScreenResult screenResultIn) {
        Session session = getHibernateSession();
        ScreenResult screenResult = (ScreenResult) session.get(ScreenResult.class, screenResultIn.getEntityId());

        log.info("delete Assay Wells");
        Query query = session.createQuery("delete AssayWell a where a.screenResult.id = :screenResultId");
        query.setParameter("screenResultId", screenResult.getScreenResultId());
        int rows = query.executeUpdate();
        log.info("deleted " + rows + " AssayWells for " + screenResult);
        screenResult.getAssayWells().clear();

        log.info("delete Assay Plates to be orphaned (i.e., without library screenings)");
        query = session
                .createQuery("delete AssayPlate ap where ap.screen.id = :screenId and ap.libraryScreening is null");
        query.setParameter("screenId", screenResult.getScreen().getScreenId());
        rows = query.executeUpdate();
        log.info("deleted " + rows + " AssayPlates for " + screenResult);

        log.info("delete ResultValues");
        int cumRows = 0;
        query = session.createQuery("delete ResultValue v where v.dataColumn.id = :col");
        for (DataColumn col : screenResult.getDataColumns()) {
            query.setParameter("col", col.getDataColumnId());
            rows = query.executeUpdate();
            cumRows += rows;
            log.debug("deleted " + rows + " result values for " + col);
            col.getResultValues().clear();
        }
        log.info("deleted a total of " + cumRows + " result values");
        //screenResult.getPlateNumbers().clear();
        // dissociate ScreenResult from Screen
        screenResult.getScreen().clearScreenResult();
        session.delete(screenResult);
        flush();
        log.info("deleted " + screenResult);
    }

    //TODO: move to report class
    public int createScreenedReagentCounts(final ScreenType screenType, Screen study,
            AnnotationType positiveAnnotationType, AnnotationType overallAnnotationType) {
        // Break this into two separate queries because of Hibernate bug (http://opensource.atlassian.com/projects/hibernate/browse/HHH-1615):
        // when using the "group by" clause with a full object (as opposed to an attribute of the object/table),
        // Hibernate is requiring that every attribute of the object be specified in a "group by" and not 
        // just the object itself.  so the workaround is to query once to get the id's then once again to 
        // get the objects.

        log.info("1. get the reagent id's for the positive counts");
        ScrollableResults sr = runScrollQuery(new edu.harvard.med.screensaver.db.ScrollQuery() {
            public ScrollableResults execute(Session session) {
                HqlBuilder builder = new HqlBuilder();
                builder.select("r", "id").selectExpression("count(*)").from(AssayWell.class, "aw")
                        .from("aw", AssayWell.libraryWell, "w", JoinType.INNER)
                        .from("w", Well.latestReleasedReagent, "r", JoinType.INNER)
                        .from("w", Well.library, "l", JoinType.INNER)
                        .where("l", "screenType", Operator.EQUAL, screenType)
                        .where("w", "libraryWellType", Operator.EQUAL, LibraryWellType.EXPERIMENTAL);
                builder.where("aw", "positive", Operator.EQUAL, Boolean.TRUE);
                builder.groupBy("r", "id");
                log.debug("hql: " + builder.toHql());
                return builder.toQuery(session, true).setCacheMode(CacheMode.IGNORE)
                        .scroll(ScrollMode.FORWARD_ONLY);
            }
        });

        Map<Integer, Long> positivesMap = Maps.newHashMap();
        while (sr.next()) {
            Object[] row = sr.get();
            positivesMap.put((Integer) row[0], (Long) row[1]);
        }

        log.info("2. get the reagent id's for the overall counts");
        sr = runScrollQuery(new edu.harvard.med.screensaver.db.ScrollQuery() {
            public ScrollableResults execute(Session session) {
                HqlBuilder builder = new HqlBuilder();
                builder.select("r", "id").selectExpression("count(*)").from(AssayWell.class, "aw")
                        .from("aw", AssayWell.libraryWell, "w", JoinType.INNER)
                        .from("w", Well.library, "l", JoinType.INNER)
                        .from("w", Well.latestReleasedReagent, "r", JoinType.INNER)
                        .where("l", "screenType", Operator.EQUAL, screenType)
                        .where("w", "libraryWellType", Operator.EQUAL, LibraryWellType.EXPERIMENTAL)
                        .groupBy("r", "id");
                log.debug("hql: " + builder.toHql());
                return builder.toQuery(session, true).setCacheMode(CacheMode.IGNORE)
                        .scroll(ScrollMode.FORWARD_ONLY);
            }
        });

        Map<Integer, Long> overallMap = Maps.newHashMap();
        while (sr.next()) {
            Object[] row = sr.get();
            overallMap.put((Integer) row[0], (Long) row[1]);
        }

        log.info("3. get the Reagents");
        sr = runScrollQuery(new edu.harvard.med.screensaver.db.ScrollQuery() {
            public ScrollableResults execute(Session session) {
                HqlBuilder builder = new HqlBuilder();
                builder.select("r").distinctProjectionValues().from(AssayWell.class, "aw")
                        .from("aw", AssayWell.libraryWell, "w", JoinType.INNER)
                        .from("w", Well.library, "l", JoinType.INNER)
                        .from("w", Well.latestReleasedReagent, "r", JoinType.INNER)
                        .where("l", "screenType", Operator.EQUAL, screenType)
                        .where("w", "libraryWellType", Operator.EQUAL, LibraryWellType.EXPERIMENTAL);
                log.debug("hql: " + builder.toHql());
                return builder.toQuery(session, true).setCacheMode(CacheMode.IGNORE)
                        .scroll(ScrollMode.FORWARD_ONLY);
            }
        });

        log.info("4. build the Study: positives: " + positivesMap.size() + ", reagents: " + overallMap.size());
        int count = 0;
        while (sr.next()) {
            Reagent r = (Reagent) sr.get()[0];

            AnnotationValue av = new AnnotationValue(overallAnnotationType, r, null,
                    (double) overallMap.get(r.getReagentId()).intValue());
            _dao.saveOrUpdateEntity(av);
            Long positiveCount = positivesMap.get(r.getReagentId());
            if (positiveCount != null) {
                av = new AnnotationValue(positiveAnnotationType, r, null, (double) positiveCount.intValue());
                _dao.saveOrUpdateEntity(av);
            }
            // Note: due to memory performance, we will build the study_reagent_link later
            if (count++ % ROWS_TO_CACHE == 0) {
                log.debug("flushing");
                _dao.flush();
                _dao.clear();
            }
            if (count % 10000 == 0) {
                log.info("" + count + " reagents processed");
            }
        }

        log.info("save the study");
        _dao.saveOrUpdateEntity(study);
        _dao.flush();
        log.info("populateStudyReagentLinkTable");
        int reagentCount = populateStudyReagentLinkTable(study.getScreenId());
        log.info("done: positives: " + positivesMap.size() + ", reagents: " + overallMap.size());
        return reagentCount;
    }

    public AssayWell findAssayWell(ScreenResult screenResult, WellKey wellKey) {

        List result = getHibernateSession()
                .createQuery("select a from AssayWell a where a.screenResult.id = ? and a.libraryWell.id = ?")
                .setParameter(0, screenResult.getEntityId()).setParameter(1, wellKey.toString()).list();
        if (result.size() == 0) {
            return null;
        }
        if (result.size() > 1) {
            throw new DataModelViolationException("multiple assay wells found");
        }
        return (AssayWell) result.get(0);
    }

    /**
     * Use SQL to populate the Study-to-Reagent link table: <br>
     * Reagents could be added to the study in java-hibernate, however, this
     * poses memory performance issues.
     */
    //TODO: move this to the report class
    private int populateStudyReagentLinkTable(int screenId) {
        String sql = "insert into study_reagent_link " + "(study_id,reagent_id) " + "select :studyId as study_id, "
                + "reagent_id from " + "(select distinct(reagent_id) " + "from reagent "
                + "join annotation_value using(reagent_id) " + "join annotation_type using(annotation_type_id) "
                + "where study_id = :studyId ) a";

        log.debug("sql: " + sql);

        javax.persistence.Query query = getEntityManager().createNativeQuery(sql);
        query.setParameter("studyId", screenId);
        int rows = query.executeUpdate();
        log.info("study_reagent_link updated: " + rows);
        return rows;
    }

    public ScreenResult getLatestScreenResult() {
        List<ScreenResult> result = new HqlBuilder().select("sr").distinctProjectionValues()
                .from(ScreenResult.class, "sr").orderBy("sr", SortDirection.DESCENDING)
                .toQuery(getHibernateSession(), true).list();

        if (result == null || result.isEmpty())
            return null;
        return result.get(0);
    }

}