/** * 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.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.regex.Pattern; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.SQLQuery; import org.hibernate.SessionFactory; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.hibernate.persister.entity.AbstractEntityPersister; import org.openmrs.Allergies; import org.openmrs.Allergy; import org.openmrs.Location; import org.openmrs.Patient; import org.openmrs.PatientIdentifier; import org.openmrs.PatientIdentifierType; import org.openmrs.PatientIdentifierType.UniquenessBehavior; import org.openmrs.Person; import org.openmrs.PersonAttribute; import org.openmrs.PersonName; import org.openmrs.api.context.Context; import org.openmrs.api.db.DAOException; import org.openmrs.api.db.PatientDAO; import org.openmrs.api.db.hibernate.search.LuceneQuery; import org.openmrs.collection.ListPart; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.OpenmrsUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Hibernate specific database methods for the PatientService * * @see org.openmrs.api.context.Context * @see org.openmrs.api.db.PatientDAO * @see org.openmrs.api.PatientService */ public class HibernatePatientDAO implements PatientDAO { protected final Logger log = LoggerFactory.getLogger(getClass()); /** * Hibernate session factory */ private SessionFactory sessionFactory; /** * Set session factory * * @param sessionFactory */ public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } /** * @param patientId internal patient identifier * @return patient with given internal identifier * @see org.openmrs.api.PatientService#getPatient(java.lang.Integer) */ @Override public Patient getPatient(Integer patientId) { return (Patient) sessionFactory.getCurrentSession().get(Patient.class, patientId); } /** * @param patient patient to be created or updated * @return patient who was created or updated * @see org.openmrs.api.db.PatientDAO#savePatient(org.openmrs.Patient) */ @Override public Patient savePatient(Patient patient) throws DAOException { if (patient.getPatientId() == null) { // if we're saving a new patient, just do the normal thing // and rows in the person and patient table will be created by // hibernate sessionFactory.getCurrentSession().saveOrUpdate(patient); return patient; } else { // if we're updating a patient, its possible that a person // row exists but a patient row does not. hibernate does not deal // with this correctly right now, so we must create a dummy row // in the patient table before saving // Check to make sure we have a row in the patient table already. // If we don't have a row, create it so Hibernate doesn't bung // things up insertPatientStubIfNeeded(patient); // Note: A merge might be necessary here because hibernate thinks that Patients // and Persons are the same objects. So it sees a Person object in the // cache and claims it is a duplicate of this Patient object. //patient = (Patient) sessionFactory.getCurrentSession().merge(patient); sessionFactory.getCurrentSession().saveOrUpdate(patient); return patient; } } /** * Inserts a row into the patient table This avoids hibernate's bunging of our * person/patient/user inheritance * * @param patient */ private void insertPatientStubIfNeeded(Patient patient) { boolean stubInsertNeeded = false; if (patient.getPatientId() != null) { // check if there is a row with a matching patient.patient_id String sql = "SELECT 1 FROM patient WHERE patient_id = :patientId"; Query query = sessionFactory.getCurrentSession().createSQLQuery(sql); query.setInteger("patientId", patient.getPatientId()); stubInsertNeeded = (query.uniqueResult() == null); } if (stubInsertNeeded) { //If not yet persisted if (patient.getCreator() == null) { patient.setCreator(Context.getAuthenticatedUser()); } //If not yet persisted if (patient.getDateCreated() == null) { patient.setDateCreated(new Date()); } String insert = "INSERT INTO patient (patient_id, creator, voided, date_created) VALUES (:patientId, :creator, 0, :dateCreated)"; Query query = sessionFactory.getCurrentSession().createSQLQuery(insert); query.setInteger("patientId", patient.getPatientId()); query.setInteger("creator", patient.getCreator().getUserId()); query.setDate("dateCreated", patient.getDateCreated()); query.executeUpdate(); //Without evicting person, you will get this error when promoting person to patient //org.hibernate.NonUniqueObjectException: a different object with the same identifier //value was already associated with the session: [org.openmrs.Patient#] //see TRUNK-3728 Person person = (Person) sessionFactory.getCurrentSession().get(Person.class, patient.getPersonId()); sessionFactory.getCurrentSession().evict(person); } } /** * @see org.openmrs.api.db.PatientDAO#getPatients(String, boolean, Integer, Integer) * @should return exact match first */ @Override public List<Patient> getPatients(String query, boolean includeVoided, Integer start, Integer length) throws DAOException { if (StringUtils.isBlank(query) || (length != null && length < 1)) { return Collections.emptyList(); } if (start == null || start < 0) { start = 0; } if (length == null) { length = HibernatePersonDAO.getMaximumSearchResults(); } List<Patient> patients = findPatients(query, includeVoided, start, length); return new ArrayList<>(patients); } /** * @see org.openmrs.api.db.PatientDAO#getPatients(String, Integer, Integer) */ @Override public List<Patient> getPatients(String query, Integer start, Integer length) throws DAOException { return getPatients(query, false, start, length); } private void setFirstAndMaxResult(Criteria criteria, Integer start, Integer length) { if (start != null) { criteria.setFirstResult(start); } int maximumSearchResults = HibernatePersonDAO.getMaximumSearchResults(); if (length != null && length < maximumSearchResults) { criteria.setMaxResults(length); } else { if (log.isDebugEnabled()) { log.debug("Limiting the size of the number of matching patients to " + maximumSearchResults); } criteria.setMaxResults(maximumSearchResults); } } /** * @see org.openmrs.api.db.PatientDAO#getAllPatients(boolean) */ @SuppressWarnings("unchecked") @Override public List<Patient> getAllPatients(boolean includeVoided) throws DAOException { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Patient.class); if (!includeVoided) { criteria.add(Restrictions.eq("voided", false)); } return criteria.list(); } /** * @see org.openmrs.api.PatientService#purgePatientIdentifierType(org.openmrs.PatientIdentifierType) * @see org.openmrs.api.db.PatientDAO#deletePatientIdentifierType(org.openmrs.PatientIdentifierType) */ @Override public void deletePatientIdentifierType(PatientIdentifierType patientIdentifierType) throws DAOException { sessionFactory.getCurrentSession().delete(patientIdentifierType); } /** * @see org.openmrs.api.PatientService#getPatientIdentifiers(java.lang.String, java.util.List, java.util.List, java.util.List, java.lang.Boolean) */ @SuppressWarnings("unchecked") @Override public List<PatientIdentifier> getPatientIdentifiers(String identifier, List<PatientIdentifierType> patientIdentifierTypes, List<Location> locations, List<Patient> patients, Boolean isPreferred) throws DAOException { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PatientIdentifier.class); // join with the patient table to prevent patient identifiers from patients // that already voided getting returned criteria.createAlias("patient", "patient"); criteria.add(Restrictions.eq("patient.voided", false)); criteria.add(Restrictions.eq("voided", false)); if (identifier != null) { criteria.add(Restrictions.eq("identifier", identifier)); } if (!patientIdentifierTypes.isEmpty()) { criteria.add(Restrictions.in("identifierType", patientIdentifierTypes)); } if (!locations.isEmpty()) { criteria.add(Restrictions.in("location", locations)); } if (!patients.isEmpty()) { criteria.add(Restrictions.in("patient", patients)); } if (isPreferred != null) { criteria.add(Restrictions.eq("preferred", isPreferred)); } return criteria.list(); } /** * @see org.openmrs.api.db.PatientDAO#savePatientIdentifierType(org.openmrs.PatientIdentifierType) */ @Override public PatientIdentifierType savePatientIdentifierType(PatientIdentifierType patientIdentifierType) throws DAOException { sessionFactory.getCurrentSession().saveOrUpdate(patientIdentifierType); return patientIdentifierType; } /** * @see org.openmrs.api.PatientService#deletePatient(org.openmrs.Patient) */ @Override public void deletePatient(Patient patient) throws DAOException { HibernatePersonDAO.deletePersonAndAttributes(sessionFactory, patient); } /** * @see org.openmrs.api.PatientService#getPatientIdentifierType(java.lang.Integer) */ @Override public PatientIdentifierType getPatientIdentifierType(Integer patientIdentifierTypeId) throws DAOException { return (PatientIdentifierType) sessionFactory.getCurrentSession().get(PatientIdentifierType.class, patientIdentifierTypeId); } /** * @should not return null when includeRetired is false * @should not return retired when includeRetired is false * @should not return null when includeRetired is true * @should return all when includeRetired is true * @should return ordered * @see org.openmrs.api.db.PatientDAO#getAllPatientIdentifierTypes(boolean) */ @SuppressWarnings("unchecked") @Override public List<PatientIdentifierType> getAllPatientIdentifierTypes(boolean includeRetired) throws DAOException { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PatientIdentifierType.class); if (!includeRetired) { criteria.add(Restrictions.eq("retired", false)); } else { //retired last criteria.addOrder(Order.asc("retired")); } //required first criteria.addOrder(Order.desc("required")); criteria.addOrder(Order.asc("name")); criteria.addOrder(Order.asc("patientIdentifierTypeId")); return criteria.list(); } /** * @see org.openmrs.api.db.PatientDAO#getPatientIdentifierTypes(java.lang.String, * java.lang.String, java.lang.Boolean, java.lang.Boolean) * * @should return non retired patient identifier types with given name * @should return non retired patient identifier types with given format * @should return non retired patient identifier types that are not required * @should return non retired patient identifier types that are required * @should return non retired patient identifier types that has checkDigit * @should return non retired patient identifier types that has not CheckDigit * @should return only non retired patient identifier types * @should return non retired patient identifier types ordered by required first * @should return non retired patient identifier types ordered by required and name * @should return non retired patient identifier types ordered by required name and type id * */ @SuppressWarnings("unchecked") @Override public List<PatientIdentifierType> getPatientIdentifierTypes(String name, String format, Boolean required, Boolean hasCheckDigit) throws DAOException { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PatientIdentifierType.class); if (name != null) { criteria.add(Restrictions.eq("name", name)); } if (format != null) { criteria.add(Restrictions.eq("format", format)); } if (required != null) { criteria.add(Restrictions.eq("required", required)); } if (hasCheckDigit != null) { criteria.add(Restrictions.eq("checkDigit", hasCheckDigit)); } criteria.add(Restrictions.eq("retired", false)); //required first criteria.addOrder(Order.desc("required")); criteria.addOrder(Order.asc("name")); criteria.addOrder(Order.asc("patientIdentifierTypeId")); return criteria.list(); } /** * @param attributes attributes on a Person or Patient object. similar to: [gender, givenName, * middleName, familyName] * @return list of patients that match other patients * @see org.openmrs.api.db.PatientDAO#getDuplicatePatientsByAttributes(java.util.List) */ @SuppressWarnings("unchecked") @Override public List<Patient> getDuplicatePatientsByAttributes(List<String> attributes) { List<Patient> patients = new Vector<Patient>(); List<Integer> patientIds = new Vector<Integer>(); if (!attributes.isEmpty()) { String sqlString = getDuplicatePatientsSQLString(attributes); if(sqlString != null) { SQLQuery sqlquery = sessionFactory.getCurrentSession().createSQLQuery(sqlString); patientIds = sqlquery.list(); if (!patientIds.isEmpty()) { Query query = sessionFactory.getCurrentSession().createQuery( "from Patient p1 where p1.patientId in (:ids)"); query.setParameterList("ids", patientIds); patients = query.list(); } } } sortDuplicatePatients(patients, patientIds); return patients; } private String getDuplicatePatientsSQLString(List<String> attributes) { String outerSelect = "select distinct t1.patient_id from patient t1 "; final String t5 = " = t5."; Set<String> patientFieldNames = OpenmrsUtil.getDeclaredFields(Patient.class); Set<String> personFieldNames = OpenmrsUtil.getDeclaredFields(Person.class); Set<String> personNameFieldNames = OpenmrsUtil.getDeclaredFields(PersonName.class); Set<String> identifierFieldNames = OpenmrsUtil.getDeclaredFields(PatientIdentifier.class); List<String> whereConditions = new ArrayList<>(); List<String> innerFields = new ArrayList<>(); String innerSelect = " from patient p1 "; for (String attribute : attributes) { if (attribute != null) { attribute = attribute.trim(); } if (patientFieldNames.contains(attribute)) { AbstractEntityPersister aep = (AbstractEntityPersister) sessionFactory.getClassMetadata(Patient.class); String[] properties = aep.getPropertyColumnNames(attribute); if (properties.length >= 1) { attribute = properties[0]; } whereConditions.add(" t1." + attribute + t5 + attribute); innerFields.add("p1." + attribute); } else if (personFieldNames.contains(attribute)) { if (!outerSelect.contains("person")) { outerSelect += "inner join person t2 on t1.patient_id = t2.person_id "; innerSelect += "inner join person person1 on p1.patient_id = person1.person_id "; } AbstractEntityPersister aep = (AbstractEntityPersister) sessionFactory.getClassMetadata(Person.class); if (aep != null) { String[] properties = aep.getPropertyColumnNames(attribute); if (properties != null && properties.length >= 1) { attribute = properties[0]; } } whereConditions.add(" t2." + attribute + t5 + attribute); innerFields.add("person1." + attribute); } else if (personNameFieldNames.contains(attribute)) { if (!outerSelect.contains("person_name")) { outerSelect += "inner join person_name t3 on t2.person_id = t3.person_id "; innerSelect += "inner join person_name pn1 on person1.person_id = pn1.person_id "; } //Since we are firing a native query get the actual table column name from the field name of the entity AbstractEntityPersister aep = (AbstractEntityPersister) sessionFactory .getClassMetadata(PersonName.class); if (aep != null) { String[] properties = aep.getPropertyColumnNames(attribute); if (properties != null && properties.length >= 1) { attribute = properties[0]; } } whereConditions.add(" t3." + attribute + t5 + attribute); innerFields.add("pn1." + attribute); } else if (identifierFieldNames.contains(attribute)) { if (!outerSelect.contains("patient_identifier")) { outerSelect += "inner join patient_identifier t4 on t1.patient_id = t4.patient_id "; innerSelect += "inner join patient_identifier pi1 on p1.patient_id = pi1.patient_id "; } AbstractEntityPersister aep = (AbstractEntityPersister) sessionFactory .getClassMetadata(PatientIdentifier.class); if (aep != null) { String[] properties = aep.getPropertyColumnNames(attribute); if (properties != null && properties.length >= 1) { attribute = properties[0]; } } whereConditions.add(" t4." + attribute + t5 + attribute); innerFields.add("pi1." + attribute); } else { log.warn("Unidentified attribute: " + attribute); } } if(CollectionUtils.isNotEmpty(innerFields) || CollectionUtils.isNotEmpty(whereConditions)) { String innerFieldsJoined = StringUtils.join(innerFields, ", "); String whereFieldsJoined = StringUtils.join(whereConditions, " and "); String innerWhereCondition = ""; if (!attributes.contains("includeVoided")) { innerWhereCondition = " where p1.voided = false "; } String innerQuery = "(Select " + innerFieldsJoined + innerSelect + innerWhereCondition + " group by " + innerFieldsJoined + " having count(*) > 1" + " order by " + innerFieldsJoined + ") t5"; return outerSelect + ", " + innerQuery + " where " + whereFieldsJoined + ";"; } return null; } private void sortDuplicatePatients(List<Patient> patients, List<Integer> patientIds) { Map<Integer, Integer> patientIdOrder = new HashMap<>(); int startPos = 0; for (Integer id : patientIds) { patientIdOrder.put(id, startPos++); } class PatientIdComparator implements Comparator<Patient> { private Map<Integer, Integer> sortOrder; public PatientIdComparator(Map<Integer, Integer> sortOrder) { this.sortOrder = sortOrder; } @Override public int compare(Patient patient1, Patient patient2) { Integer patPos1 = sortOrder.get(patient1.getPatientId()); if (patPos1 == null) { throw new IllegalArgumentException("Bad patient encountered: " + patient1.getPatientId()); } Integer patPos2 = sortOrder.get(patient2.getPatientId()); if (patPos2 == null) { throw new IllegalArgumentException("Bad patient encountered: " + patient2.getPatientId()); } return patPos1.compareTo(patPos2); } } Collections.sort(patients, new PatientIdComparator(patientIdOrder)); } /** * @see org.openmrs.api.db.PatientDAO#getPatientByUuid(java.lang.String) */ @Override public Patient getPatientByUuid(String uuid) { Patient p = null; p = (Patient) sessionFactory.getCurrentSession().createQuery("from Patient p where p.uuid = :uuid").setString( "uuid", uuid).uniqueResult(); return p; } @Override public PatientIdentifier getPatientIdentifierByUuid(String uuid) { return (PatientIdentifier) sessionFactory.getCurrentSession().createQuery( "from PatientIdentifier p where p.uuid = :uuid").setString("uuid", uuid).uniqueResult(); } /** * @see org.openmrs.api.db.PatientDAO#getPatientIdentifierTypeByUuid(java.lang.String) */ @Override public PatientIdentifierType getPatientIdentifierTypeByUuid(String uuid) { return (PatientIdentifierType) sessionFactory.getCurrentSession().createQuery( "from PatientIdentifierType pit where pit.uuid = :uuid").setString("uuid", uuid).uniqueResult(); } /** * This method uses a SQL query and does not load anything into the hibernate session. It exists * because of ticket #1375. * * @see org.openmrs.api.db.PatientDAO#isIdentifierInUseByAnotherPatient(org.openmrs.PatientIdentifier) */ @Override public boolean isIdentifierInUseByAnotherPatient(PatientIdentifier patientIdentifier) { boolean checkPatient = patientIdentifier.getPatient() != null && patientIdentifier.getPatient().getPatientId() != null; boolean checkLocation = patientIdentifier.getLocation() != null && patientIdentifier.getIdentifierType().getUniquenessBehavior() == UniquenessBehavior.LOCATION; // switched this to an hql query so the hibernate cache can be considered as well as the database String hql = "select count(*) from PatientIdentifier pi, Patient p where pi.patient.patientId = p.patient.patientId " + "and p.voided = false and pi.voided = false and pi.identifier = :identifier and pi.identifierType = :idType"; if (checkPatient) { hql += " and p.patientId != :ptId"; } if (checkLocation) { hql += " and pi.location = :locationId"; } Query query = sessionFactory.getCurrentSession().createQuery(hql); query.setString("identifier", patientIdentifier.getIdentifier()); query.setInteger("idType", patientIdentifier.getIdentifierType().getPatientIdentifierTypeId()); if (checkPatient) { query.setInteger("ptId", patientIdentifier.getPatient().getPatientId()); } if (checkLocation) { query.setInteger("locationId", patientIdentifier.getLocation().getLocationId()); } return !"0".equals(query.uniqueResult().toString()); } /** * @param patientIdentifierId the patientIdentifier id * @return the patientIdentifier matching the Id * @see org.openmrs.api.db.PatientDAO#getPatientIdentifier(java.lang.Integer) */ @Override public PatientIdentifier getPatientIdentifier(Integer patientIdentifierId) throws DAOException { return (PatientIdentifier) sessionFactory.getCurrentSession().get(PatientIdentifier.class, patientIdentifierId); } /** * @param patientIdentifier patientIndentifier to be created or updated * @return patientIndentifier that was created or updated * @see org.openmrs.api.db.PatientDAO#savePatientIdentifier(org.openmrs.PatientIdentifier) */ @Override public PatientIdentifier savePatientIdentifier(PatientIdentifier patientIdentifier) { sessionFactory.getCurrentSession().saveOrUpdate(patientIdentifier); return patientIdentifier; } /** * @see org.openmrs.api.PatientService#purgePatientIdentifier(org.openmrs.PatientIdentifier) * @see org.openmrs.api.db.PatientDAO#deletePatientIdentifier(org.openmrs.PatientIdentifier) */ @Override public void deletePatientIdentifier(PatientIdentifier patientIdentifier) throws DAOException { sessionFactory.getCurrentSession().delete(patientIdentifier); } /** * @param query the string to search on * @return the number of patients matching the given search phrase * @see org.openmrs.api.db.PatientDAO#getCountOfPatients(String) */ @Override public Long getCountOfPatients(String query) { return getCountOfPatients(query, false); } /** * @param query the string to search on * @param includeVoided true/false whether or not to included voided patients * @return the number of patients matching the given search phrase * @see org.openmrs.api.db.PatientDAO#getCountOfPatients(String, boolean) */ @Override public Long getCountOfPatients(String query, boolean includeVoided) { if (StringUtils.isBlank(query)) { return 0L; } query = LuceneQuery.escapeQuery(query); LuceneQuery<PatientIdentifier> identifierQuery = getPatientIdentifierLuceneQuery(query, includeVoided); PersonLuceneQuery personLuceneQuery = new PersonLuceneQuery(sessionFactory); LuceneQuery<PersonName> nameQuery = personLuceneQuery.getPatientNameQuery(query, includeVoided, identifierQuery); LuceneQuery<PersonAttribute> attributeQuery = personLuceneQuery.getPatientAttributeQuery(query, includeVoided, nameQuery); long size = identifierQuery.resultSize() + nameQuery.resultSize() + attributeQuery.resultSize(); return size; } private List<Patient> findPatients(String query, boolean includeVoided) { return findPatients(query, includeVoided, null, null); } public List<Patient> findPatients(String query, boolean includeVoided, Integer start, Integer length){ if (start == null) { start = 0; } Integer maxLength = HibernatePersonDAO.getMaximumSearchResults(); if (length == null || length > maxLength) { length = maxLength; } query = LuceneQuery.escapeQuery(query); List<Patient> patients = new LinkedList<>(); String minChars = Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_MIN_SEARCH_CHARACTERS); if (minChars == null || !StringUtils.isNumeric(minChars)) { minChars = "" + OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_MIN_SEARCH_CHARACTERS; } if (query.length() < Integer.valueOf(minChars)) { return patients; } LuceneQuery<PatientIdentifier> identifierQuery = getPatientIdentifierLuceneQuery(query, includeVoided); long identifiersSize = identifierQuery.resultSize(); if (identifiersSize > start) { ListPart<Object[]> patientIdentifiers = identifierQuery.listPartProjection(start, length, "patient.personId"); patientIdentifiers.getList().forEach(patientIdentifier -> patients.add(getPatient((Integer) patientIdentifier[0]))); length -= patientIdentifiers.getList().size(); start = 0; } else { start -= (int) identifiersSize; } if (length == 0) { return patients; } PersonLuceneQuery personLuceneQuery = new PersonLuceneQuery(sessionFactory); LuceneQuery<PersonName> nameQuery = personLuceneQuery.getPatientNameQuery(query, includeVoided, identifierQuery); long namesSize = nameQuery.resultSize(); if (namesSize > start) { ListPart<Object[]> personNames = nameQuery.listPartProjection(start, length, "person.personId"); personNames.getList().forEach(personName -> patients.add(getPatient((Integer) personName[0]))); length -= personNames.getList().size(); start = 0; } else { start -= (int) namesSize; } if (length == 0) { return patients; } LuceneQuery<PersonAttribute> attributeQuery = personLuceneQuery.getPatientAttributeQuery(query, includeVoided, nameQuery); long attributesSize = attributeQuery.resultSize(); if (attributesSize > start) { ListPart<Object[]> personAttributes = attributeQuery.listPartProjection(start, length, "person.personId"); personAttributes.getList().forEach(personAttribute -> patients.add(getPatient((Integer) personAttribute[0]))); } return patients; } private LuceneQuery<PatientIdentifier> getPatientIdentifierLuceneQuery(String query, boolean includeVoided) { query = removeIdentifierPadding(query); List<String> tokens = tokenizeIdentifierQuery(query); LuceneQuery<PatientIdentifier> luceneQuery = LuceneQuery .newQuery(PatientIdentifier.class, sessionFactory.getCurrentSession(), "identifierPhrase:(" + StringUtils.join(tokens, " OR ") + ")"); if(!includeVoided){ luceneQuery.include("voided", false); luceneQuery.include("patient.voided", false); } luceneQuery.include("patient.isPatient", true); luceneQuery.skipSame("patient.personId"); return luceneQuery; } private String removeIdentifierPadding(String query) { String regex = Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_REGEX, ""); if (Pattern.matches("^\\^.{1}\\*.*$", regex)) { String padding = regex.substring(regex.indexOf("^") + 1, regex.indexOf("*")); Pattern pattern = Pattern.compile("^" + padding + "+"); query = pattern.matcher(query).replaceFirst(""); } return query; } /** * Copied over from PatientSearchCriteria... * * I have no idea how it is supposed to work, but tests pass... * * @param query * @return * @see PatientSearchCriteria */ private List<String> tokenizeIdentifierQuery(String query) { List<String> searchPatterns = new ArrayList<String>(); String patternSearch = Context.getAdministrationService().getGlobalProperty( OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_SEARCH_PATTERN, ""); if (StringUtils.isBlank(patternSearch)) { searchPatterns.add(query); } else { // split the pattern before replacing in case the user searched on a comma // replace the @SEARCH@, etc in all elements for (String pattern : patternSearch.split(",")) { searchPatterns.add(replaceSearchString(pattern, query)); } } return searchPatterns; } /** * Copied over from PatientSearchCriteria... * * I have no idea how it is supposed to work, but tests pass... * * Puts @SEARCH@, @SEARCH-1@, and @CHECKDIGIT@ into the search string * * @param regex the admin-defined search string containing the @..@'s to be replaced * @param identifierSearched the user entered search string * @return substituted search strings. * * @see PatientSearchCriteria#replaceSearchString(String, String) */ private String replaceSearchString(String regex, String identifierSearched) { String returnString = regex.replaceAll("@SEARCH@", identifierSearched); if (identifierSearched.length() > 1) { // for 2 or more character searches, we allow regex to use last character as check digit returnString = returnString.replaceAll("@SEARCH-1@", identifierSearched.substring(0, identifierSearched.length() - 1)); returnString = returnString.replaceAll("@CHECKDIGIT@", identifierSearched .substring(identifierSearched.length() - 1)); } else { returnString = returnString.replaceAll("@SEARCH-1@", ""); returnString = returnString.replaceAll("@CHECKDIGIT@", ""); } return returnString; } /** * @see org.openmrs..api.db.PatientDAO#getAllergies(org.openmrs.Patient) */ //@Override @Override public List<Allergy> getAllergies(Patient patient) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Allergy.class); criteria.add(Restrictions.eq("patient", patient)); criteria.add(Restrictions.eq("voided", false)); return criteria.list(); } /** * @see org.openmrs.api.db.PatientDAO#getAllergyStatus(org.openmrs.Patient) */ //@Override @Override public String getAllergyStatus(Patient patient) { return (String) sessionFactory.getCurrentSession().createSQLQuery( "select allergy_status from patient where patient_id = :patientId").setInteger("patientId", patient.getPatientId()).uniqueResult(); } /** * @see org.openmrs.api.db.PatientDAO#saveAllergies(org.openmrs.Patient, * org.openmrsallergyapi.Allergies) */ @Override public Allergies saveAllergies(Patient patient, Allergies allergies) { sessionFactory.getCurrentSession().createSQLQuery( "update patient set allergy_status = :allergyStatus where patient_id = :patientId") .setInteger("patientId", patient.getPatientId()) .setString("allergyStatus", allergies.getAllergyStatus()) .executeUpdate(); for (Allergy allergy : allergies) { sessionFactory.getCurrentSession().save(allergy); } return allergies; } /** * @see org.openmrs.PatientDAO#getAllergy(Integer) */ @Override public Allergy getAllergy(Integer allergyId) { return (Allergy) sessionFactory.getCurrentSession().createQuery("from Allergy a where a.allergyId = :allergyId") .setInteger("allergyId", allergyId).uniqueResult(); } /** * @see org.openmrs.PatientDAO#getAllergyByUuid(String) */ @Override public Allergy getAllergyByUuid(String uuid) { return (Allergy) sessionFactory.getCurrentSession().createQuery("from Allergy a where a.uuid = :uuid") .setString("uuid", uuid).uniqueResult(); } /** * @see org.openmrs.api.db.PatientDAO#saveAllergy(org.openmrs.Allergy) */ @Override public Allergy saveAllergy(Allergy allergy) { sessionFactory.getCurrentSession().save(allergy); return allergy; } }