Downloads Documentation Community Contribute Demo






Show Sidebar
Login | Register

root/openmrs/trunk/src/api/org/openmrs/api/db/hibernate/HibernatePatientDAO.java

Revision 5381, 21.2 kB (checked in by bwolfe, 1 day ago)

Readded additional patient identifier search back to the generic person dwr searches - #689

  • Property svn:eol-style set to CRLF
Line 
1 /**
2  * The contents of this file are subject to the OpenMRS Public License
3  * Version 1.0 (the "License"); you may not use this file except in
4  * compliance with the License. You may obtain a copy of the License at
5  * http://license.openmrs.org
6  *
7  * Software distributed under the License is distributed on an "AS IS"
8  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9  * License for the specific language governing rights and limitations
10  * under the License.
11  *
12  * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
13  */
14 package org.openmrs.api.db.hibernate;
15
16 import java.lang.reflect.Field;
17 import java.sql.Connection;
18 import java.sql.PreparedStatement;
19 import java.sql.SQLException;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.Vector;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.hibernate.Criteria;
28 import org.hibernate.Hibernate;
29 import org.hibernate.Query;
30 import org.hibernate.SessionFactory;
31 import org.hibernate.criterion.Expression;
32 import org.hibernate.criterion.LogicalExpression;
33 import org.hibernate.criterion.MatchMode;
34 import org.hibernate.criterion.Order;
35 import org.hibernate.criterion.Restrictions;
36 import org.openmrs.Location;
37 import org.openmrs.Patient;
38 import org.openmrs.PatientIdentifier;
39 import org.openmrs.PatientIdentifierType;
40 import org.openmrs.Person;
41 import org.openmrs.PersonName;
42 import org.openmrs.Tribe;
43 import org.openmrs.api.AdministrationService;
44 import org.openmrs.api.context.Context;
45 import org.openmrs.api.db.DAOException;
46 import org.openmrs.api.db.PatientDAO;
47 import org.openmrs.util.OpenmrsConstants;
48
49 /**
50  * Hibernate specific database methods for the PatientService
51  *
52  * @see org.openmrs.api.context.Context
53  * @see org.openmrs.api.db.PatientDAO
54  * @see org.openmrs.api.PatientService
55  */
56 public class HibernatePatientDAO implements PatientDAO {
57
58         protected final Log log = LogFactory.getLog(getClass());
59
60         /**
61          * Hibernate session factory
62          */
63         private SessionFactory sessionFactory;
64        
65         /**
66          * Set session factory
67          *
68          * @param sessionFactory
69          */
70         public void setSessionFactory(SessionFactory sessionFactory) {
71                 this.sessionFactory = sessionFactory;
72         }
73        
74         /**
75          * @see org.openmrs.api.PatientService#getPatient(java.lang.Long)
76          */
77         public Patient getPatient(Integer patientId) {
78                 return (Patient) sessionFactory.getCurrentSession()
79                                                .get(Patient.class, patientId);
80         }
81        
82
83         /**
84          * @see org.openmrs.api.db.PatientDAO#savePatient(org.openmrs.Patient)
85          */
86         public Patient savePatient(Patient patient) throws DAOException {
87                 if (patient.getPatientId() == null) {
88                         // if we're saving a new patient, just do the normal thing
89                         // and rows in the person and patient table will be created by
90                         // hibernate
91                         sessionFactory.getCurrentSession().saveOrUpdate(patient);
92                         return patient;
93                 } else {
94                         // if we're updating a patient, its possible that a person
95                         // row exists but a patient row does not. hibernate does not deal
96                         // with this correctly right now, so we must create a dummy row
97                         // in the patient table before saving
98                        
99                         // Check to make sure we have a row in the patient table already.
100                         // If we don't have a row, create it so Hibernate doesn't bung
101                         // things up
102                         Object obj = sessionFactory.getCurrentSession()
103                                                    .get(Patient.class,
104                                                         patient.getPatientId());
105                         if (!(obj instanceof Patient)) {
106                                 insertPatientStub(patient);
107                         }
108                        
109                         // TODO: look at UserService.saveUser(User) to see how to do it without
110                         //              it requiring a merge
111                         // do a merge here because of the previous get 2 lines up
112                         patient = (Patient) sessionFactory.getCurrentSession()
113                                                           .merge(patient);
114
115                         return patient;
116                 }
117         }
118
119         /**
120          * Inserts a row into the patient table
121          *
122          * This avoids hibernate's bunging of our person/patient/user inheritance
123          *
124          * @param patient
125          */
126         private void insertPatientStub(Patient patient) {
127                 Connection connection = sessionFactory.getCurrentSession().connection();
128                 try {
129                         PreparedStatement ps = connection.prepareStatement("INSERT INTO patient (patient_id, creator, date_created) VALUES (?, ?, ?)");
130                        
131                         ps.setInt(1, patient.getPatientId());
132                         ps.setInt(2, patient.getCreator().getUserId());
133                         ps.setDate(3, new java.sql.Date(patient.getDateCreated().getTime()));
134        
135                         ps.executeUpdate();
136                 } catch (SQLException e) {
137                         log.warn("SQL Exception while trying to create a patient stub", e);
138                 }
139                
140                 sessionFactory.getCurrentSession().flush();
141         }
142
143         /**
144      * @see org.openmrs.api.db.PatientDAO#getPatients(java.lang.String, java.lang.String, java.util.List, boolean)
145      */
146         @SuppressWarnings("unchecked")
147     public List<Patient> getPatients(String name, String identifier,
148             List<PatientIdentifierType> identifierTypes, boolean matchIdentifierExactly) throws DAOException {
149            
150         Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Patient.class);
151        
152         criteria.createAlias("names", "name");
153                 criteria.addOrder(Order.asc("name.givenName"));
154                 criteria.addOrder(Order.asc("name.middleName"));
155                 criteria.addOrder(Order.asc("name.familyName"));
156        
157                 // get only distinct patients
158                 criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
159                
160         if (name != null) {
161                 // TODO simple name search to start testing, will need to make "real"
162                 // name search
163                 // i.e. split on whitespace, guess at first/last name, etc
164                 // TODO return the matched name instead of the primary name
165                 // possible solution: "select new" org.openmrs.PatientListItem and
166                 // return a list of those
167
168                 name = name.replaceAll("  ", " ");
169                 name = name.replace(", ", " ");
170                 String[] names = name.split(" ");
171                
172                         // TODO add junit test for searching on voided patient names
173                
174                 String nameSoFar = names[0];
175                 for (int i=0 ; i < names.length ; i++) {
176                         String n = names[i];
177                         if (n != null && n.length() > 0) {
178                                 LogicalExpression oneNameSearch = getNameSearch(n);
179                                 LogicalExpression searchExpression = oneNameSearch;
180                                 if(i>0){
181                                         nameSoFar += " " + n;
182                                         LogicalExpression fullNameSearch = getNameSearch(nameSoFar);
183                                         searchExpression = Expression.or(oneNameSearch, fullNameSearch);
184                                 }
185                                 criteria.add(searchExpression);
186                         }
187                 }
188                
189         }
190        
191         // do the restriction on either identifier string or types
192         if (identifier != null || identifierTypes.size() > 0) {
193        
194                 // TODO add junit test for searching on voided identifiers
195
196                 // add the join on the identifiers table
197                 criteria.createAlias("identifiers", "ids");
198                 criteria.add(Expression.eq("ids.voided", false));
199                
200                 // do the identifier restriction
201                 if (identifier != null) {
202                                 AdministrationService adminService = Context.getAdministrationService();
203                                 String regex = adminService.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_REGEX, "");
204                                
205                                 // if the user wants an exact search, match on that.
206                         if (matchIdentifierExactly) {
207                                         criteria.add(Expression.eq("ids.identifier", identifier));
208                                 }
209                                 // if the regex is empty, default to a simple "like" search or if
210                                 // we're in hsql world, also only do the simple like search (because
211                                 // hsql doesn't know how to deal with 'regexp'
212                                 else if (regex.equals("") || HibernateUtil.isHSQLDialect(sessionFactory)) {
213                                 String prefix = adminService.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_PREFIX, "");
214                                         String suffix = adminService.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_SUFFIX, "%");
215                                 StringBuffer likeString = new StringBuffer(prefix).append(identifier)
216                                                                                   .append(suffix);
217                                 criteria.add(Expression.like("ids.identifier", likeString.toString()));
218                                 }
219                                 // if the regex is present, search on that
220                                 else {
221                                         regex = regex.replace("@SEARCH@", identifier);
222                                 criteria.add(Restrictions.sqlRestriction("identifier regexp ?", regex, Hibernate.STRING));
223                                 }
224                         }
225                
226                 // TODO add a junit test for patientIdentifierType restrictions
227                
228                 // do the type restriction
229                 if (identifierTypes.size() > 0) {
230                         criteria.add(Expression.in("ids.identifierType", identifierTypes));
231                         }
232                 }
233                
234                 // TODO add junit test for searching on voided patients
235
236         // make sure the patient object isn't voided
237         criteria.add(Expression.eq("voided", false));
238                
239         // restricting the search to the max search results value
240                 criteria.setFirstResult(0);
241                 criteria.setMaxResults(getMaximumSearchResults());
242                
243                 return criteria.list();
244         }
245
246         /**
247      * Auto generated method comment
248      *
249      * @param name
250      * @return
251          */
252     private LogicalExpression getNameSearch(String name){
253         // this criteria is essentially:
254                 // where voided = false && name in [familyName, middleName, givenName]
255                 return Expression.and(Expression.eq("name.voided", false),
256                                                              Expression.or(Expression.like("name.familyName", name, MatchMode.START),
257                                                                            Expression.or(Expression.like("name.middleName", name, MatchMode.START),
258                                                                                          Expression.like("name.givenName", name, MatchMode.START))));
259         }
260
261         /**
262      * @see org.openmrs.api.db.PatientDAO#getAllPatients(boolean)
263          */
264         @SuppressWarnings("unchecked")
265     public List<Patient> getAllPatients(boolean includeVoided)
266             throws DAOException {
267         Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Patient.class);
268        
269         if (includeVoided == false)
270                 criteria.add(Expression.eq("voided", false));
271        
272         return criteria.list();
273     }
274                
275         /**
276          * @see org.openmrs.api.PatientService#purgePatientIdentifierType(org.openmrs.PatientIdentifierType)
277      * @see org.openmrs.api.db.PatientDAO#deletePatientIdentifierType(org.openmrs.PatientIdentifierType)
278      */
279     public void deletePatientIdentifierType(
280             PatientIdentifierType patientIdentifierType) throws DAOException {
281         sessionFactory.getCurrentSession().delete(patientIdentifierType);
282         }
283        
284         /**
285      * @see org.openmrs.api.db.PatientDAO#getPatientIdentifiers(java.lang.String, java.util.List, java.util.List, java.util.List, java.lang.Boolean)
286      * @see org.openmrs.api.PatientService#getPatientIdentifiers(java.lang.String, java.util.List, java.util.List, java.util.List, java.lang.Boolean)
287          */
288         @SuppressWarnings("unchecked")
289     public List<PatientIdentifier> getPatientIdentifiers(String identifier,
290             List<PatientIdentifierType> patientIdentifierTypes,
291             List<Location> locations, List<Patient> patients,
292             Boolean isPreferred)
293             throws DAOException {
294         Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PatientIdentifier.class);
295        
296         // TODO add junit test for not getting voided
297         // make sure the patient object isn't voided
298         criteria.add(Expression.eq("voided", false));
299        
300         // TODO add junit test for getting by identifier (and for not getting by partial here)
301         if (identifier != null)
302                 criteria.add(Expression.eq("identifier", identifier));
303        
304         // TODO add junit test for getting by identifier type
305         if (patientIdentifierTypes.size() > 0)
306                         criteria.add(Expression.in("identifierType", patientIdentifierTypes));
307        
308         // TODO add junit test for getting by patients
309         if (patients.size() > 0)
310                         criteria.add(Expression.in("patient", patients));
311        
312         // TODO add junit test for getting by null/true/false isPreferred
313         if (isPreferred != null)
314                 criteria.add(Expression.eq("preferred", isPreferred));
315                
316         return criteria.list();
317         }
318        
319         /**
320      * @see org.openmrs.api.db.PatientDAO#savePatientIdentifierType(org.openmrs.PatientIdentifierType)
321          */
322     public PatientIdentifierType savePatientIdentifierType(
323             PatientIdentifierType patientIdentifierType) throws DAOException {
324         sessionFactory.getCurrentSession().saveOrUpdate(patientIdentifierType);
325         return patientIdentifierType;
326         }
327        
328         /**
329          * @see org.openmrs.api.PatientService#deletePatient(org.openmrs.Patient)
330          */
331         public void deletePatient(Patient patient) throws DAOException {
332                 HibernatePersonDAO.deletePersonAndAttributes(sessionFactory, patient);
333         }
334                
335         /**
336          * @see org.openmrs.api.PatientService#getPatientIdentifierType(java.lang.Integer)
337          */
338         public PatientIdentifierType getPatientIdentifierType(
339                 Integer patientIdentifierTypeId) throws DAOException {
340                 return (PatientIdentifierType) sessionFactory.getCurrentSession()
341                                                              .get(PatientIdentifierType.class,
342                                                                   patientIdentifierTypeId);
343         }
344        
345         /**
346          * @see org.openmrs.api.db.PatientDAO#getAllPatientIdentifierTypes(boolean)
347          */
348         @SuppressWarnings("unchecked")
349     public List<PatientIdentifierType> getAllPatientIdentifierTypes(boolean includeRetired)
350                 throws DAOException {
351                
352                 // TODO test this method
353                
354                 Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PatientIdentifierType.class);
355                 criteria.addOrder(Order.asc("name"));
356                
357                 if (includeRetired == false)
358                         criteria.add(Expression.eq("retired", false));
359
360                 return criteria.list();
361         }       
362
363         /**
364      * @see org.openmrs.api.db.PatientDAO#getPatientIdentifierTypes(java.lang.String, java.lang.String, java.lang.Boolean, java.lang.Boolean)
365          */
366         @SuppressWarnings("unchecked")
367     public List<PatientIdentifierType> getPatientIdentifierTypes(String name,
368             String format, Boolean required, Boolean hasCheckDigit)
369             throws DAOException {
370         // TODO test this method
371                
372                 Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PatientIdentifierType.class);
373                 criteria.addOrder(Order.asc("name"));
374                
375                 if (name != null)
376                         criteria.add(Expression.eq("name", name));
377                
378                 if (format != null)
379                         criteria.add(Expression.eq("format", format));
380                
381                 if (required != null)
382                         criteria.add(Expression.eq("required", required));
383                
384                 if (hasCheckDigit!= null)
385                         criteria.add(Expression.eq("checkDigit", hasCheckDigit));
386                
387                 criteria.add(Expression.eq("retired", false));
388                
389                 return criteria.list();
390         }
391
392         /**
393          * @see org.openmrs.api.PatientService#getTribe()
394          * @deprecated tribe will be moved to patient attribute
395          */
396         public Tribe getTribe(Integer tribeId) throws DAOException {
397                 Tribe tribe = (Tribe) sessionFactory.getCurrentSession()
398                                                     .get(Tribe.class, tribeId);
399                
400                 return tribe;
401         }
402        
403         /**
404          * @see org.openmrs.api.PatientService#getTribes()
405          * @deprecated tribe will be moved to patient attribute
406          */
407         @SuppressWarnings("unchecked")
408         public List<Tribe> getTribes() throws DAOException {
409                 List<Tribe> tribes = sessionFactory.getCurrentSession()
410                                                    .createQuery("from Tribe t order by t.name asc")
411                                                    .list();
412                
413                 return tribes;
414         }
415        
416         /**
417          * @see org.openmrs.api.PatientService#findTribes()
418          * @deprecated tribe will be moved to patient attribute
419          */
420         @SuppressWarnings("unchecked")
421         public List<Tribe> findTribes(String s) throws DAOException {
422                 Criteria crit = sessionFactory.getCurrentSession()
423                                               .createCriteria(Tribe.class);
424                 crit.add(Expression.like("name", s, MatchMode.START));
425                 crit.addOrder(Order.asc("name"));
426                
427                 return crit.list();
428         }
429        
430         /**
431          * @see org.openmrs.api.db.PatientDAO#getDuplicatePatientsByAttributes(java.util.List)
432          */
433         @SuppressWarnings("unchecked")
434         public List<Patient> getDuplicatePatientsByAttributes(List<String> attributes) {
435                 List<Patient> patients = new Vector<Patient>();
436                
437                 if (attributes.size() > 0) {
438                         String select = "select distinct p1 from Patient p1, Patient p2";
439                         String where  = " where p1 <> p2 ";
440                         String orderBy= " order by ";
441                        
442                         Class patient = Patient.class;
443                         Set<String> patientFieldNames = new HashSet<String>(patient.getDeclaredFields().length);
444                         for (Field f : patient.getDeclaredFields()){
445                                 patientFieldNames.add(f.getName());
446                                 log.debug(f.getName());
447                         }
448                        
449                         Class person = Person.class;
450                         Set<String> personFieldNames = new HashSet<String>(person.getDeclaredFields().length);
451                         for (Field f : person.getDeclaredFields()){
452                                 personFieldNames.add(f.getName());
453                                 log.debug(f.getName());
454                         }
455                        
456                         Class personName = PersonName.class;
457                         Set<String> personNameFieldNames = new HashSet<String>(personName.getDeclaredFields().length);
458                         for (Field f : personName.getDeclaredFields()){
459                                 personNameFieldNames.add(f.getName());
460                                 log.debug(f.getName());
461                         }
462                        
463                         Class identifier = PatientIdentifier.class;
464                         Set<String> identifierFieldNames = new HashSet<String>(identifier.getDeclaredFields().length);
465                         for (Field f : identifier.getDeclaredFields()){
466                                 identifierFieldNames.add(f.getName());
467                                 log.debug(f.getName());
468                         }
469                        
470                         if (!attributes.contains("includeVoided"))
471                                 where += "and p1.voided = false and p2.voided = false ";
472                        
473                         for (String s : attributes) {
474                                 if (patientFieldNames.contains(s)) {
475                                         where += " and p1." + s + " = p2." + s;
476                                         orderBy += "p1." + s + ", ";
477                                 } else if (personFieldNames.contains(s)) {
478                                         if (!select.contains("Person ")) {
479                                                 select += ", Person person1, Person person2";
480                                                 where += " and p1.patientId = person1.personId and p2.patientId = person2.personId ";
481                                         }
482                                         where += " and person1." + s + " = person2." + s;
483                                         orderBy += "person1." + s + ", ";
484                                 } else if (personNameFieldNames.contains(s)) {
485                                         if (!select.contains("PersonName")) {
486                                                 select += ", PersonName pn1, PersonName pn2";
487                                                 where += " and p1 = pn1.person and p2 = pn2.person ";
488                                         }
489                                         where += " and pn1." + s + " = pn2." + s;
490                                         orderBy += "pn1." + s + ", ";
491                                 } else if (identifierFieldNames.contains(s)) {
492                                         if (!select.contains("PatientIdentifier")) {
493                                                 select += ", PatientIdentifier pi1, PatientIdentifier pi2";
494                                                 where += " and p1 = pi1.patient and p2 = pi2.patient ";
495                                         }
496                                         where += " and pi1." + s + " = pi2." + s;
497                                         orderBy += "pi1." + s + ", ";
498                                 } else
499                                         log.warn("Unidentified attribute: " + s);
500                         }
501
502                         int index = orderBy.lastIndexOf(", ");
503                         orderBy = orderBy.substring(0, index);
504                        
505                         select = select + where + orderBy;
506                        
507                         Query query = sessionFactory.getCurrentSession()
508                                                     .createQuery(select);
509                
510                         patients = query.list();
511                 }
512                
513                 /*
514                  * if (attributes.size() > 0) { String select = "select p from Patient
515                  * p"; String where = " where 1=1 "; String groupBy= " group by ";
516                  * String having = " having count(p.patientId) > 1";
517                  *
518                  * Class patient = Patient.class; Set<String> patientFieldNames = new
519                  * HashSet<String>(patient.getDeclaredFields().length); for (Field f :
520                  * patient.getDeclaredFields()){ patientFieldNames.add(f.getName());
521                  * log.debug(f.getName()); }
522                  *
523                  * Class patientName = PersonName.class; Set<String>
524                  * patientNameFieldNames = new HashSet<String>(patientName.getDeclaredFields().length);
525                  * for (Field f : patientName.getDeclaredFields()){
526                  * patientNameFieldNames.add(f.getName()); log.debug(f.getName()); }
527                  *
528                  * Class identifier = PatientIdentifier.class; Set<String>
529                  * identifierFieldNames = new HashSet<String>(identifier.getDeclaredFields().length);
530                  * for (Field f : identifier.getDeclaredFields()){
531                  * identifierFieldNames.add(f.getName()); log.debug(f.getName()); }
532                  *
533                  * for (String s : attributes) { if (patientFieldNames.contains(s)) {
534                  * groupBy += "p." + s + ", "; } else if
535                  * (patientNameFieldNames.contains(s)) { if
536                  * (!select.contains("PersonName")) { select += ", PersonName pn"; where +=
537                  * "and p = pn.patient "; } groupBy += "pn." + s + ", "; } else if
538                  * (identifierFieldNames.contains(s)) { if
539                  * (!select.contains("PatientIdentifier")) { select += ",
540                  * PatientIdentifier pi"; where += "and p = pi.patient "; } groupBy +=
541                  * "pi." + s + ", "; } else log.warn("Unidentified attribute: " + s); }
542                  *
543                  * int index = groupBy.lastIndexOf(", "); groupBy = groupBy.substring(0,
544                  * index);
545                  *
546                  * select = select + where + groupBy + having;
547                  *
548                  * Query query = session.createQuery(select);
549                  *
550                  * patients = query.list(); }
551                 */
552                
553                 return patients;
554         }
555        
556         /**
557          * Fetch the max results value from the global properties table
558          *
559          * @return Integer value for the patient search max results global property
560          */
561         private Integer getMaximumSearchResults() {
562                 try {
563                         return Integer.valueOf(Context.getAdministrationService()
564                                                       .getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_SEARCH_MAX_RESULTS,
565                                                                          "1000"));
566                 } catch (Exception e) {
567                         log.warn("Unable to convert the global property "
568                                 + OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_SEARCH_MAX_RESULTS
569                                 + "to a valid integer. Returning the default 1000");
570                 }
571                
572                 return 1000;
573         }
574 }
Note: See TracBrowser for help on using the browser.