Cloud HIMS is a JSF/JPA (EclipseLink 2.3)/MySQL application designed to capture data in Health Care Centres across Sri Lanka. We've recently migrated to ObjectDB to improve performance. Despite the migration, we're experiencing general performance issues across all transactions. As requested in a seperate thread, I'm isolating a specific issue to illustrate the broader problem.
Method: listPatientsByIDsWithBasicData
Duration: Takes around 3 seconds to search
Search Method
public List<ClientBasicData> listPatientsByIDsWithBasicData(String ids) { Long st = new Date().getTime(); System.out.println("listPatientsByIDsWithBasicData - start - " + st); List<ClientBasicData> cs = new ArrayList<>(); if (ids == null || ids.trim().equals("")) { return cs; } String jpql; Map m; m = new HashMap(); jpql = "select new lk.gov.health.phsp.pojcs.ClientBasicData(" + "c.id, " + "c.phn, " + "c.person.name, " + "c.person.nic, " + "c.person.phone1, " + "c.person.address " + ") " + " from Client c " + " where c.retired=false " + " and " + " (" + " lower(c.phn)=:q " + " or " + " lower(c.person.localReferanceNo)=:q " + " or " + " lower(c.person.ssNumber)=:q " + " or " + " c.person.phone1=:q " + " or " + " c.person.phone2=:q " + " or " + " lower(c.person.nic)=:q " + " ) "; m.put("q", ids.trim().toLowerCase()); System.out.println("m = " + m); System.out.println("j = " + jpql); cs = (List<ClientBasicData>) getFacade().findLightsByJpql(jpql, m); System.out.println("cs size = " + cs.size()); Long ed = new Date().getTime(); System.out.println("listPatientsByIDsWithBasicData - end - " + ed); System.out.println("listPatientsByIDsWithBasicData - Duration - " + (ed - st)); return cs; }
Search Method Output
listPatientsByIDsWithBasicData - start - 1695401112698]] m = {q=0715812399}]] j = select new lk.gov.health.phsp.pojcs.ClientBasicData(c.id, c.phn, c.person.name, c.person.nic, c.person.phone1, c.person.address ) from Client c where c.retired=false and ( lower(c.phn)=:q or lower(c.person.localReferanceNo)=:q or lower(c.person.ssNumber)=:q or c.person.phone1=:q or c.person.phone2=:q or lower(c.person.nic)=:q ) ]] cs size = 7]] listPatientsByIDsWithBasicData - end - 1695401115663]] listPatientsByIDsWithBasicData - Duration - 2965]] listPatientsByIDsWithBasicData - start - 1695401637568]] m = {q=0715812399}]] j = select new lk.gov.health.phsp.pojcs.ClientBasicData(c.id, c.phn, c.person.name, c.person.nic, c.person.phone1, c.person.address ) from Client c where c.retired=false and ( lower(c.phn)=:q or lower(c.person.localReferanceNo)=:q or lower(c.person.ssNumber)=:q or c.person.phone1=:q or c.person.phone2=:q or lower(c.person.nic)=:q ) ]] cs size = 7]] listPatientsByIDsWithBasicData - end - 1695401640561]] listPatientsByIDsWithBasicData - Duration - 2993]]
Entity Classes
Client
package lk.gov.health.phsp.entity; import java.io.Serializable; import java.util.Date; import javax.jdo.annotations.Index; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Temporal; import lk.gov.health.phsp.pojcs.Identifiable; @Entity @Table public class Client implements Serializable , Identifiable { // <editor-fold defaultstate="collapsed" desc="Attributes"> @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private static final long serialVersionUID = 1L; @Index @OneToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER) private Person person; @Index private String phn; private String comments; /* Create Properties */ @ManyToOne(fetch = FetchType.LAZY) private WebUser createdBy; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date createdAt; @Temporal(javax.persistence.TemporalType.DATE) private Date createdOn; @ManyToOne(fetch = FetchType.LAZY) private Institution createInstitution; @ManyToOne(fetch = FetchType.LAZY) private Institution poiInstitution; /* Last Edit Properties */ @ManyToOne(fetch = FetchType.LAZY) private WebUser lastEditBy; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date lastEditeAt; /* Retire Reversal Properties */ @ManyToOne(fetch = FetchType.LAZY) private WebUser retiredReversedBy; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date retiredReversedAt; /* Retire Properties */ private boolean retired; private boolean reservedClient; @ManyToOne(fetch = FetchType.LAZY) private WebUser retiredBy; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date retiredAt; private String retireComments; // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Overrides"> @Override public int hashCode() { int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { if (!(object instanceof Client)) { return false; } Client other = (Client) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return "Client{" + "phn=" + phn + '}'; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Getters & Setters"> public static long getSerialVersionUID() { return serialVersionUID; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Person getPerson() { if (person == null) { person = new Person(); } return person; } public void setPerson(Person person) { this.person = person; } public WebUser getCreatedBy() { return createdBy; } public void setCreatedBy(WebUser createdBy) { this.createdBy = createdBy; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public WebUser getLastEditBy() { return lastEditBy; } public void setLastEditBy(WebUser lastEditBy) { this.lastEditBy = lastEditBy; } public Date getLastEditeAt() { return lastEditeAt; } public void setLastEditeAt(Date lastEditeAt) { this.lastEditeAt = lastEditeAt; } public boolean isRetired() { return retired; } public void setRetired(boolean retired) { this.retired = retired; } public WebUser getRetiredBy() { return retiredBy; } public void setRetiredBy(WebUser retiredBy) { this.retiredBy = retiredBy; } public Date getRetiredAt() { return retiredAt; } public void setRetiredAt(Date retiredAt) { this.retiredAt = retiredAt; } public String getRetireComments() { return retireComments; } public void setRetireComments(String retireComments) { this.retireComments = retireComments; } public WebUser getRetiredReversedBy() { return retiredReversedBy; } public void setRetiredReversedBy(WebUser retiredReversedBy) { this.retiredReversedBy = retiredReversedBy; } public Date getRetiredReversedAt() { return retiredReversedAt; } public void setRetiredReversedAt(Date retiredReversedAt) { this.retiredReversedAt = retiredReversedAt; } public String getPhn() { return phn; } public void setPhn(String phn) { this.phn = phn; } // </editor-fold> public Institution getCreateInstitution() { return createInstitution; } public void setCreateInstitution(Institution createInstitution) { this.createInstitution = createInstitution; } public String getComments() { return comments; } public void setComments(String comments) { this.comments = comments; } public boolean isReservedClient() { return reservedClient; } public void setReservedClient(boolean reservedClient) { this.reservedClient = reservedClient; } public Date getCreatedOn() { return createdOn; } public void setCreatedOn(Date createdOn) { this.createdOn = createdOn; } public Institution getPoiInstitution() { return poiInstitution; } public void setPoiInstitution(Institution poiInstitution) { this.poiInstitution = poiInstitution; } }
Person Class
package lk.gov.health.phsp.entity; import java.io.Serializable; import java.util.Date; import javax.jdo.annotations.Index; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.Transient; import lk.gov.health.phsp.pojcs.Identifiable; import org.joda.time.LocalDate; import org.joda.time.Period; import org.joda.time.PeriodType; /** * * @author Dr. M. H. B. Ariyaratne, MBBS, PGIM Trainee for MSc(Biomedical * Informatics) */ @Entity @Table public class Person implements Serializable, Identifiable { // <editor-fold defaultstate="collapsed" desc="Persistant Attributes"> static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.EAGER) private Item title; private String name; @ManyToOne(fetch = FetchType.EAGER) private Item sex; @ManyToOne(fetch = FetchType.EAGER) private Item citizenship; @ManyToOne(fetch = FetchType.EAGER) private Item ethinicGroup; @ManyToOne(fetch = FetchType.EAGER) private Item religion; @ManyToOne(fetch = FetchType.EAGER) private Item mariatalStatus; @ManyToOne(fetch = FetchType.EAGER) private Item educationStatus; private String occupation; @Index @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date dateOfBirth; private boolean dobIsAnApproximation; @Lob private String address; @Index private String phone1; @Index private String phone2; @Index private String email; @Index private String nic; @Index private String passportNumber; @Index private String localReferanceNo; @Index private String ssNumber; private String website; private String drivingLicenseNumber; @ManyToOne(fetch = FetchType.EAGER) private Area gnArea; @ManyToOne(fetch = FetchType.EAGER) private Area dsArea; @ManyToOne(fetch = FetchType.EAGER) private Area phmArea; @ManyToOne(fetch = FetchType.EAGER) private Area mohArea; @ManyToOne(fetch = FetchType.EAGER) private Area district; @ManyToOne(fetch = FetchType.EAGER) private Area province; //Created Properties @ManyToOne(fetch = FetchType.LAZY) private WebUser createdBy; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date createdAt; @ManyToOne(fetch = FetchType.LAZY) private WebUser editer; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date editedAt; //Retairing properties private boolean retired; @ManyToOne(fetch = FetchType.LAZY) private WebUser retiredBy; @Temporal(javax.persistence.TemporalType.TIMESTAMP) private Date retiredAt; private String retireComments; // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Transient Attributes"> @Transient private boolean ageCalculated = false; @Transient private int ageMonths; @Transient private int ageDays; @Transient private int ageYears; @Transient private String age; @Transient private long ageInDays; @Transient private int serealNumber; @Transient private String transPhoneNumbers; // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Functions"> public void calAgeFromDob() { ageCalculated = true; setAge(""); setAgeInDays(0l); setAgeMonths(0); setAgeDays(0); setAgeYears(0); if (getDateOfBirth() == null) { return; } LocalDate dob = new LocalDate(getDateOfBirth()); LocalDate date = new LocalDate(new Date()); Period period = new Period(dob, date, PeriodType.yearMonthDay()); setAgeYears(period.getYears()); setAgeMonths(period.getMonths()); setAgeDays(period.getDays()); if (getAgeYears() > 12) { setAge(period.getYears() + " years."); } else if (getAgeYears() > 0) { setAge(period.getYears() + " years and " + period.getMonths() + " months."); } else { setAge(period.getMonths() + " months and " + period.getDays() + " days."); } period = new Period(dob, date, PeriodType.days()); setAgeInDays((long) period.getDays()); } public String getAge() { if (!ageCalculated) { calAgeFromDob(); } return age; } public Long getAgeInDays() { if (!ageCalculated) { calAgeFromDob(); } return ageInDays; } public int getAgeMonths() { if (!ageCalculated) { calAgeFromDob(); } return ageMonths; } public int getAgeDays() { if (!ageCalculated) { calAgeFromDob(); } return ageDays; } public int getAgeYears() { if (!ageCalculated) { calAgeFromDob(); } return ageYears; } public String getNameWithTitle() { String temT; if (getTitle() != null) { temT = getTitle().name + " " + getName(); } else { temT = getName(); } return temT; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Getters & Setters"> /** * @return the gnArea */ public Area getGnArea() { return gnArea; } /** * @param gnArea the gnArea to set */ public void setGnArea(Area gnArea) { this.gnArea = gnArea; } /** * @return the dsArea */ public Area getDsArea() { return dsArea; } /** * @param dsArea the dsArea to set */ public void setDsArea(Area dsArea) { this.dsArea = dsArea; } /** * @return the phmArea */ public Area getPhmArea() { return phmArea; } /** * @param phmArea the phmArea to set */ public void setPhmArea(Area phmArea) { this.phmArea = phmArea; } /** * @return the mohArea */ public Area getMohArea() { return mohArea; } /** * @param mohArea the mohArea to set */ public void setMohArea(Area mohArea) { this.mohArea = mohArea; } /** * @return the district */ public Area getDistrict() { return district; } /** * @param district the district to set */ public void setDistrict(Area district) { this.district = district; } /** * @return the province */ public Area getProvince() { return province; } /** * @param province the province to set */ public void setProvince(Area province) { this.province = province; } /** * @param ageMonths the ageMonths to set */ public void setAgeMonths(int ageMonths) { this.ageMonths = ageMonths; } /** * @param ageDays the ageDays to set */ public void setAgeDays(int ageDays) { this.ageDays = ageDays; } /** * @param ageYears the ageYears to set */ public void setAgeYears(int ageYears) { this.ageYears = ageYears; } /** * @param age the age to set */ public void setAge(String age) { this.age = age; } /** * @param ageInDays the ageInDays to set */ public void setAgeInDays(long ageInDays) { this.ageInDays = ageInDays; } public Item getSex() { return sex; } public void setSex(Item sex) { this.sex = sex; } public String getRetireComments() { return retireComments; } public void setRetireComments(String retireComments) { this.retireComments = retireComments; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public WebUser getEditer() { return editer; } public void setEditer(WebUser editer) { this.editer = editer; } public Date getEditedAt() { return editedAt; } public void setEditedAt(Date editedAt) { this.editedAt = editedAt; } public WebUser getCreatedBy() { return createdBy; } public void setCreatedBy(WebUser createdBy) { this.createdBy = createdBy; } public String getPhone2() { return phone2; } public void setPhone2(String phone2) { this.phone2 = phone2; } public String getName() { return name; } public void setName(String name) { this.name = name.toUpperCase(); } public boolean isRetired() { return retired; } public void setRetired(boolean retired) { this.retired = retired; } public Date getRetiredAt() { return retiredAt; } public void setRetiredAt(Date retiredAt) { this.retiredAt = retiredAt; } public WebUser getRetiredBy() { return retiredBy; } public void setRetiredBy(WebUser retiredBy) { this.retiredBy = retiredBy; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address.toUpperCase(); } public String getPassportNumber() { return passportNumber; } public void setPassportNumber(String passportNumber) { this.passportNumber = passportNumber; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getWebsite() { return website; } public void setWebsite(String website) { this.website = website; } public String getDrivingLicenseNumber() { return drivingLicenseNumber; } public void setDrivingLicenseNumber(String drivingLicenseNumber) { this.drivingLicenseNumber = drivingLicenseNumber; } public Item getTitle() { return title; } public void setTitle(Item title) { this.title = title; } public Date getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(Date dateOfBirth) { ageCalculated = false; this.dateOfBirth = dateOfBirth; } public String getPhone1() { return phone1; } public void setPhone1(String phone1) { this.phone1 = phone1; } public String getNic() { return nic; } public void setNic(String nic) { this.nic = nic; } public int getSerealNumber() { return serealNumber; } public void setSerealNumber(int serealNumber) { this.serealNumber = serealNumber; } public Item getCitizenship() { return citizenship; } public void setCitizenship(Item citizenship) { this.citizenship = citizenship; } public Item getEthinicGroup() { return ethinicGroup; } public void setEthinicGroup(Item ethinicGroup) { this.ethinicGroup = ethinicGroup; } public Item getReligion() { return religion; } public void setReligion(Item religion) { this.religion = religion; } public Item getMariatalStatus() { return mariatalStatus; } public void setMariatalStatus(Item mariatalStatus) { this.mariatalStatus = mariatalStatus; } public Item getEducationStatus() { return educationStatus; } public void setEducationStatus(Item educationStatus) { this.educationStatus = educationStatus; } // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Over-rides"> @Override public int hashCode() { int hash = 0; hash += (getId() != null ? getId().hashCode() : 0); return hash; } @Override public boolean equals(Object object) { if (!(object instanceof Person)) { return false; } Person other = (Person) object; if ((this.getId() == null && other.getId() != null) || (this.getId() != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return getName(); } // </editor-fold> public boolean isDobIsAnApproximation() { return dobIsAnApproximation; } public void setDobIsAnApproximation(boolean dobIsAnApproximation) { this.dobIsAnApproximation = dobIsAnApproximation; } public String getOccupation() { return occupation; } public void setOccupation(String occupation) { this.occupation = occupation; } public String getTransPhoneNumbers() { boolean phoneOneNotBlank = false; boolean phoneTwoNotBlank = false; if (phone1 != null && !phone1.trim().equals("")) { phoneOneNotBlank = true; } if (phone2 != null && !phone2.trim().equals("")) { phoneTwoNotBlank = true; } if (phoneOneNotBlank && phoneTwoNotBlank) { transPhoneNumbers = phone1 + ", " + phone2; } else if (phoneOneNotBlank) { transPhoneNumbers = phone1; } else if (phoneTwoNotBlank) { transPhoneNumbers = phone2; } else { transPhoneNumbers = ""; } return transPhoneNumbers; } public boolean isAgeCalculated() { return ageCalculated; } public void setAgeCalculated(boolean ageCalculated) { this.ageCalculated = ageCalculated; } public String getLocalReferanceNo() { return localReferanceNo; } public void setLocalReferanceNo(String localReferanceNo) { this.localReferanceNo = localReferanceNo; } public String getSsNumber() { return ssNumber; } public void setSsNumber(String ssNumber) { this.ssNumber = ssNumber; } }
Other Java Classes
package lk.gov.health.phsp.pojcs; import java.io.Serializable; import java.util.Date; import lk.gov.health.phsp.bean.CommonController; /** * * @author buddhika */ public class ClientBasicData implements Serializable { private String phn; private String gnArea; private String createdInstitution; private String phone; private String name; private Date dataOfBirth; private Date createdAt; private String sex; private int ageInYears; private Long id; private String nic; private String address; public ClientBasicData() { } public ClientBasicData(String phn, String gnArea, String createdInstitution, Date dataOfBirth, String sex) { this.phn = phn; this.gnArea = gnArea; this.createdInstitution = createdInstitution; this.dataOfBirth = dataOfBirth; this.sex = sex; } public ClientBasicData(String phn, String gnArea, String createdInstitution, Date dataOfBirth, Date createdAt, String sex) { this.phn = phn; this.gnArea = gnArea; this.createdInstitution = createdInstitution; this.dataOfBirth = dataOfBirth; this.createdAt = createdAt; this.sex = sex; } public ClientBasicData(String phn,String name, String nic, String address, String phone, String gnArea, String createdInstitution, Date dataOfBirth, Date createdAt, String sex) { this.phn = phn; this.name = name; this.nic = nic; this.address = address; this.phone = phone; this.gnArea = gnArea; this.createdInstitution = createdInstitution; this.dataOfBirth = dataOfBirth; this.createdAt = createdAt; this.sex = sex; } public ClientBasicData(Long id, String phn, String gnArea, String createdInstitution, Date dataOfBirth, Date createdAt, String sex, String nic) { this.id = id; this.phn = phn; this.gnArea = gnArea; this.createdInstitution = createdInstitution; this.dataOfBirth = dataOfBirth; this.createdAt = createdAt; this.sex = sex; this.nic = nic; } public ClientBasicData(Long id, String phn, String gnArea, String createdInstitution, Date dataOfBirth, Date createdAt, String sex, String nic, String name) { this.id = id; this.phn = phn; this.gnArea = gnArea; this.createdInstitution = createdInstitution; this.dataOfBirth = dataOfBirth; this.createdAt = createdAt; this.sex = sex; this.nic = nic; this.name = name; } public ClientBasicData(Long id, String phn, String name, String sex, String nic, String phone, String address) { this.phn = phn; this.phone = phone; this.name = name; this.sex = sex; this.id = id; this.nic = nic; this.address = address; } public ClientBasicData(Long id, String phn, String name, String nic, String phone, String address) { this.phn = phn; this.phone = phone; this.name = name; this.id = id; this.nic = nic; this.address = address; } public String getPhn() { return phn; } public void setPhn(String phn) { this.phn = phn; } public String getGnArea() { return gnArea; } public void setGnArea(String gnArea) { this.gnArea = gnArea; } public String getCreatedInstitution() { return createdInstitution; } public void setCreatedInstitution(String createdInstitution) { this.createdInstitution = createdInstitution; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDataOfBirth() { return dataOfBirth; } public void setDataOfBirth(Date dataOfBirth) { this.dataOfBirth = dataOfBirth; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAgeInYears() { ageInYears = CommonController.ageFromDob(dataOfBirth); return ageInYears; } public void setAgeInYears(int ageInYears) { this.ageInYears = ageInYears; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNic() { return nic; } public void setNic(String nic) { this.nic = nic; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
Persistence.xml file
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="hmisPU"> <description>cHIMS</description> <provider>com.objectdb.jpa.Provider</provider> <properties> <property name="javax.persistence.jdbc.url" value="objectdb://192.168.1.155/chims.odb"/> <property name="javax.persistence.jdbc.user" value="admin"/> <property name="javax.persistence.jdbc.password" value="admin"/> <property name="javax.persistence.schema-generation.database.action" value="create-or-extend-tables"/> </properties> </persistence-unit> </persistence>
Method in Abstract Facade
public List<?> findLightsByJpql(String jpql, Map<String, Object> parameters) { Query qry = getEntityManager().createQuery(jpql); Set<Map.Entry<String, Object>> entries = parameters.entrySet(); for (Map.Entry<String, Object> entry : entries) { String paramName = entry.getKey(); Object paramValue = entry.getValue(); if (paramValue instanceof Date) { qry.setParameter(paramName, (Date) paramValue, TemporalType.DATE); } else { qry.setParameter(paramName, paramValue); } } List<?> resultList; try { resultList = qry.getResultList(); } catch (Exception e) { resultList = new ArrayList<>(); } return resultList; }