/** * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ package org.openmrs.api.db.hibernate; import java.util.Date; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.hibernate.Criteria; import org.hibernate.FlushMode; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Property; import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.Subqueries; import org.openmrs.Concept; import org.openmrs.ConceptName; import org.openmrs.Encounter; import org.openmrs.Location; import org.openmrs.Obs; import org.openmrs.Patient; import org.openmrs.Person; import org.openmrs.User; import org.openmrs.api.db.DAOException; import org.openmrs.api.db.ObsDAO; import org.openmrs.util.OpenmrsConstants.PERSON_TYPE; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Hibernate specific Observation related functions This class should not be used directly. All * calls should go through the {@link org.openmrs.api.ObsService} methods. * * @see org.openmrs.api.db.ObsDAO * @see org.openmrs.api.ObsService */ public class HibernateObsDAO implements ObsDAO { protected final Logger log = LoggerFactory.getLogger(getClass()); protected SessionFactory sessionFactory; /** * Set session factory that allows us to connect to the database that Hibernate knows about. * * @param sessionFactory */ public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } /** * @see org.openmrs.api.ObsService#deleteObs(org.openmrs.Obs) */ @Override public void deleteObs(Obs obs) throws DAOException { sessionFactory.getCurrentSession().delete(obs); } /** * @see org.openmrs.api.ObsService#getObs(java.lang.Integer) */ @Override public Obs getObs(Integer obsId) throws DAOException { return (Obs) sessionFactory.getCurrentSession().get(Obs.class, obsId); } /** * @see org.openmrs.api.db.ObsDAO#saveObs(org.openmrs.Obs) */ @Override public Obs saveObs(Obs obs) throws DAOException { if (obs.hasGroupMembers() && obs.getObsId() != null) { // hibernate has a problem updating child collections // if the parent object was already saved so we do it // explicitly here for (Obs member : obs.getGroupMembers()) { if (member.getObsId() == null) { saveObs(member); } } } sessionFactory.getCurrentSession().saveOrUpdate(obs); return obs; } /** * @see org.openmrs.api.db.ObsDAO#getObservations(List, List, List, List, List, List, List, * Integer, Integer, Date, Date, boolean, String) */ @Override @SuppressWarnings("unchecked") public List<Obs> getObservations(List<Person> whom, List<Encounter> encounters, List<Concept> questions, List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, List<String> sortList, Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs, String accessionNumber) throws DAOException { Criteria criteria = createGetObservationsCriteria(whom, encounters, questions, answers, personTypes, locations, sortList, mostRecentN, obsGroupId, fromDate, toDate, null, includeVoidedObs, accessionNumber); return criteria.list(); } /** * @see org.openmrs.api.db.ObsDAO#getObservationCount(List, List, List, List, List, List, Integer, Date, Date, List, boolean, String) */ @Override public Long getObservationCount(List<Person> whom, List<Encounter> encounters, List<Concept> questions, List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, Integer obsGroupId, Date fromDate, Date toDate, List<ConceptName> valueCodedNameAnswers, boolean includeVoidedObs, String accessionNumber) throws DAOException { Criteria criteria = createGetObservationsCriteria(whom, encounters, questions, answers, personTypes, locations, null, null, obsGroupId, fromDate, toDate, valueCodedNameAnswers, includeVoidedObs, accessionNumber); criteria.setProjection(Projections.rowCount()); return (Long) criteria.list().get(0); } /** * A utility method for creating a criteria based on parameters (which are optional) * * @param whom * @param encounters * @param questions * @param answers * @param personTypes * @param locations * @param sortList If a field needs to be in <i>asc</i> order, <code>" asc"</code> has to be appended to the field name. For example: <code>fieldname asc</code> * @param mostRecentN * @param obsGroupId * @param fromDate * @param toDate * @param includeVoidedObs * @param accessionNumber * @return */ private Criteria createGetObservationsCriteria(List<Person> whom, List<Encounter> encounters, List<Concept> questions, List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, List<String> sortList, Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate, List<ConceptName> valueCodedNameAnswers, boolean includeVoidedObs, String accessionNumber) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Obs.class, "obs"); if (CollectionUtils.isNotEmpty(whom)) { criteria.add(Restrictions.in("person", whom)); } if (CollectionUtils.isNotEmpty(encounters)) { criteria.add(Restrictions.in("encounter", encounters)); } if (CollectionUtils.isNotEmpty(questions)) { criteria.add(Restrictions.in("concept", questions)); } if (CollectionUtils.isNotEmpty(answers)) { criteria.add(Restrictions.in("valueCoded", answers)); } if (CollectionUtils.isNotEmpty(personTypes)) { getCriteriaPersonModifier(criteria, personTypes); } if (CollectionUtils.isNotEmpty(locations)) { criteria.add(Restrictions.in("location", locations)); } if (CollectionUtils.isNotEmpty(sortList)) { for (String sort : sortList) { if (StringUtils.isNotEmpty(sort)) { // Split the sort, the field name shouldn't contain space char, so it's safe String[] split = sort.split(" ", 2); String fieldName = split[0]; if (split.length == 2 && "asc".equals(split[1])) { /* If asc is specified */ criteria.addOrder(Order.asc(fieldName)); } else { /* If the field hasn't got ordering or desc is specified */ criteria.addOrder(Order.desc(fieldName)); } } } } if (mostRecentN != null && mostRecentN > 0) { criteria.setMaxResults(mostRecentN); } if (obsGroupId != null) { criteria.createAlias("obsGroup", "og"); criteria.add(Restrictions.eq("og.obsId", obsGroupId)); } if (fromDate != null) { criteria.add(Restrictions.ge("obsDatetime", fromDate)); } if (toDate != null) { criteria.add(Restrictions.le("obsDatetime", toDate)); } if (CollectionUtils.isNotEmpty(valueCodedNameAnswers)) { criteria.add(Restrictions.in("valueCodedName", valueCodedNameAnswers)); } if (!includeVoidedObs) { criteria.add(Restrictions.eq("voided", false)); } if (accessionNumber != null) { criteria.add(Restrictions.eq("accessionNumber", accessionNumber)); } return criteria; } /** * Convenience method that adds an expression to the given <code>criteria</code> according to * what types of person objects is wanted * * @param criteria * @param personType * @return the given criteria (for chaining) */ private Criteria getCriteriaPersonModifier(Criteria criteria, List<PERSON_TYPE> personTypes) { if (personTypes.contains(PERSON_TYPE.PATIENT)) { DetachedCriteria crit = DetachedCriteria.forClass(Patient.class, "patient").setProjection( Property.forName("patientId")); criteria.add(Subqueries.propertyIn("person.personId", crit)); } if (personTypes.contains(PERSON_TYPE.USER)) { DetachedCriteria crit = DetachedCriteria.forClass(User.class, "user").setProjection(Property.forName("userId")); criteria.add(Subqueries.propertyIn("person.personId", crit)); } if (personTypes.contains(PERSON_TYPE.PERSON)) { // all observations are already on person's. Limit to non-patient and non-users here? //criteria.createAlias("Person", "person"); //criteria.add(Restrictions.eqProperty("obs.person.personId", "person.personId")); } return criteria; } /** * @see org.openmrs.api.db.ObsDAO#getObsByUuid(java.lang.String) */ @Override public Obs getObsByUuid(String uuid) { return (Obs) sessionFactory.getCurrentSession().createQuery("from Obs o where o.uuid = :uuid").setString("uuid", uuid).uniqueResult(); } /** * @see org.openmrs.api.db.ObsDAO#getRevisionObs(org.openmrs.Obs) */ @Override public Obs getRevisionObs(Obs initialObs) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Obs.class, "obs"); criteria.add(Restrictions.eq("previousVersion", initialObs)); return (Obs) criteria.uniqueResult(); } /** * @see org.openmrs.api.db.ObsDAO#getSavedStatus(org.openmrs.Obs) */ @Override public Obs.Status getSavedStatus(Obs obs) { // avoid premature flushes when this internal method is called from inside a service method Session session = sessionFactory.getCurrentSession(); FlushMode flushMode = session.getFlushMode(); session.setFlushMode(FlushMode.MANUAL); try { SQLQuery sql = session.createSQLQuery("select status from obs where obs_id = :obsId"); sql.setInteger("obsId", obs.getObsId()); return Obs.Status.valueOf((String) sql.uniqueResult()); } finally { session.setFlushMode(flushMode); } } }