Skip to content

Commit

Permalink
TRUNK-6202 Replace Hibernate Criteria API with JPA Criteria API for H…
Browse files Browse the repository at this point in the history
…ibernateAdministrationDAO (#4449)
  • Loading branch information
k4pran committed Nov 15, 2023
1 parent 8b41ee3 commit 9ecc5a6
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 25 deletions.
Expand Up @@ -9,21 +9,22 @@
*/
package org.openmrs.api.db.hibernate;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.sql.Statement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.MappingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.jdbc.Work;
import org.hibernate.mapping.Column;
Expand Down Expand Up @@ -93,56 +94,89 @@ public String getGlobalProperty(String propertyName) throws DAOException {

return gp.getPropertyValue();
}

/**
* @see org.openmrs.api.db.AdministrationDAO#getGlobalPropertyObject(java.lang.String)
*/
@Override
public GlobalProperty getGlobalPropertyObject(String propertyName) {
Session session = sessionFactory.getCurrentSession();

if (isDatabaseStringComparisonCaseSensitive()) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class);
return (GlobalProperty) criteria.add(Restrictions.eq(PROPERTY, propertyName).ignoreCase())
.uniqueResult();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<GlobalProperty> query = cb.createQuery(GlobalProperty.class);
Root<GlobalProperty> root = query.from(GlobalProperty.class);

Predicate condition = (propertyName != null)
? cb.equal(cb.lower(root.get(PROPERTY)), propertyName.toLowerCase())
: cb.isNull(root.get(PROPERTY));

query.where(condition);

return session.createQuery(query).uniqueResult();
} else {
return (GlobalProperty) sessionFactory.getCurrentSession().get(GlobalProperty.class, propertyName);
return session.get(GlobalProperty.class, propertyName);
}
}

@Override
public GlobalProperty getGlobalPropertyByUuid(String uuid) throws DAOException {

return (GlobalProperty) sessionFactory.getCurrentSession()
.createQuery("from GlobalProperty t where t.uuid = :uuid").setString("uuid", uuid).uniqueResult();
return HibernateUtil.getUniqueEntityByUUID(sessionFactory, GlobalProperty.class, uuid);
}

/**
* @see org.openmrs.api.db.AdministrationDAO#getAllGlobalProperties()
*/
@Override
@SuppressWarnings("unchecked")
public List<GlobalProperty> getAllGlobalProperties() throws DAOException {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class);
return criteria.addOrder(Order.asc(PROPERTY)).list();
Session session = sessionFactory.getCurrentSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<GlobalProperty> query = cb.createQuery(GlobalProperty.class);
Root<GlobalProperty> root = query.from(GlobalProperty.class);

query.orderBy(cb.asc(root.get(PROPERTY)));

return session.createQuery(query).getResultList();
}

/**
* @see org.openmrs.api.db.AdministrationDAO#getGlobalPropertiesByPrefix(java.lang.String)
*/
@Override
@SuppressWarnings("unchecked")
public List<GlobalProperty> getGlobalPropertiesByPrefix(String prefix) {
return sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class)
.add(Restrictions.ilike(PROPERTY, prefix, MatchMode.START)).list();
if (prefix == null) {
log.warn("Attempted to get global properties with a null prefix");
return Collections.emptyList();
}

Session session = sessionFactory.getCurrentSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<GlobalProperty> query = cb.createQuery(GlobalProperty.class);
Root<GlobalProperty> root = query.from(GlobalProperty.class);

query.where(cb.like(cb.lower(root.get(PROPERTY)), MatchMode.START.toCaseInsensitivePattern(prefix)));

return session.createQuery(query).getResultList();
}

/**
* @see org.openmrs.api.db.AdministrationDAO#getGlobalPropertiesBySuffix(java.lang.String)
*/
@Override
@SuppressWarnings("unchecked")
public List<GlobalProperty> getGlobalPropertiesBySuffix(String suffix) {
return sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class)
.add(Restrictions.ilike(PROPERTY, suffix, MatchMode.END)).list();
if (suffix == null) {
log.warn("Attempted to get global properties with a null suffix");
return Collections.emptyList();
}

Session session = sessionFactory.getCurrentSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<GlobalProperty> query = cb.createQuery(GlobalProperty.class);
Root<GlobalProperty> root = query.from(GlobalProperty.class);

query.where(cb.like(cb.lower(root.get(PROPERTY)), MatchMode.END.toCaseInsensitivePattern(suffix)));

return session.createQuery(query).getResultList();
}

/**
Expand Down
15 changes: 15 additions & 0 deletions api/src/main/java/org/openmrs/api/db/hibernate/HibernateUtil.java
Expand Up @@ -9,13 +9,17 @@
*/
package org.openmrs.api.db.hibernate;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.DetachedCriteria;
Expand All @@ -28,6 +32,7 @@
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.proxy.HibernateProxy;
import org.openmrs.Location;
import org.openmrs.api.db.DAOException;
import org.openmrs.attribute.AttributeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -188,4 +193,14 @@ public static <T> T getRealObjectFromProxy(T persistentObject) {

return persistentObject;
}

public static <T> T getUniqueEntityByUUID(SessionFactory sessionFactory, Class<T> entityClass, String uuid) throws DAOException {
Session session = sessionFactory.getCurrentSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<T> query = cb.createQuery(entityClass);
Root<T> root = query.from(entityClass);

query.where(cb.equal(root.get("uuid"), uuid));
return session.createQuery(query).uniqueResult();
}
}
43 changes: 43 additions & 0 deletions api/src/main/java/org/openmrs/api/db/hibernate/MatchMode.java
@@ -0,0 +1,43 @@
/**
* 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;

public enum MatchMode {
START,
END,
ANYWHERE,
EXACT;

public String toCaseSensitivePattern(String str) {
return toPatternInternal(str, false);
}

public String toCaseInsensitivePattern(String str) {
return toPatternInternal(str, true);
}

private String toPatternInternal(String str, boolean caseInsensitive) {
if (str == null) {
return null;
}
String processedStr = caseInsensitive ? str.toLowerCase() : str;
switch (this) {
case START:
return processedStr + "%";
case END:
return "%" + processedStr;
case ANYWHERE:
return "%" + processedStr + "%";
case EXACT:
default:
return processedStr;
}
}
}
3 changes: 2 additions & 1 deletion api/src/test/java/org/openmrs/OpenmrsTestsTest.java
Expand Up @@ -29,6 +29,7 @@
import org.junit.Ignore;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.openmrs.annotation.OpenmrsProfileExcludeFilterWithModulesJUnit4Test;
import org.openmrs.annotation.StartModuleAnnotationJUnit4Test;
import org.openmrs.annotation.StartModuleAnnotationReuseJUnit4Test;
Expand Down Expand Up @@ -98,7 +99,7 @@ public void shouldHaveTestAnnotationWhenStartingWithShould() {

// make sure every should___ method has an @Test annotation
if (methodName.startsWith("should") || methodName.contains("_should")) {
assertTrue(method.getAnnotation(Test.class) != null || method.getAnnotation(org.junit.Test.class) != null, currentClass.getName() + "#" + methodName + " does not have the @Test annotation on it even though the method name starts with 'should'");
assertTrue(method.getAnnotation(Test.class) != null || method.getAnnotation(org.junit.Test.class) != null || method.getAnnotation(ParameterizedTest.class) != null, currentClass.getName() + "#" + methodName + " does not have the @Test annotation on it even though the method name starts with 'should'");
}
}
}
Expand Down
70 changes: 69 additions & 1 deletion api/src/test/java/org/openmrs/api/AdministrationServiceTest.java
Expand Up @@ -291,6 +291,57 @@ public void getGlobalPropertiesByPrefix_shouldReturnAllRelevantGlobalPropertiesI
assertTrue(property.getPropertyValue().startsWith("correct-value"));
}
}

@Test
public void getGlobalPropertiesByInvalidPrefix_shouldReturnEmptyList() {
executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml");

String invalidPrefix = "non.existing.prefix.";
List<GlobalProperty> properties = adminService.getGlobalPropertiesByPrefix(invalidPrefix);

assertTrue(properties.isEmpty());
}

@Test
public void getGlobalPropertiesByPrefix_shouldReturnEmptyWhenPrefixIsNull() {
executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml");
List<GlobalProperty> properties = adminService.getGlobalPropertiesByPrefix(null);

assertNotNull(properties);
assertTrue(properties.isEmpty());
}

@Test
public void getGlobalPropertiesBySuffix_shouldReturnAllRelevantGlobalPropertiesInTheDatabase() {
executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml");

List<GlobalProperty> properties = adminService.getGlobalPropertiesBySuffix(".abcd");

assertNotNull(properties);
assertTrue(properties.size() > 0);
for (GlobalProperty property : properties) {
assertTrue(property.getProperty().endsWith(".abcd"));
}
}

@Test
public void getGlobalPropertiesByInvalidSuffix_shouldReturnEmptyList() {
executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml");

String invalidSuffix = "non.existing.suffix.";
List<GlobalProperty> properties = adminService.getGlobalPropertiesBySuffix(invalidSuffix);

assertTrue(properties.isEmpty());
}

@Test
public void getGlobalPropertiesBySuffix_shouldReturnEmptyWhenSuffixIsNull() {
executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml");
List<GlobalProperty> properties = adminService.getGlobalPropertiesBySuffix(null);

assertNotNull(properties);
assertTrue(properties.isEmpty());
}

@Test
public void getAllowedLocales_shouldNotFailIfNotGlobalPropertyForLocalesAllowedDefinedYet() {
Expand Down Expand Up @@ -349,7 +400,24 @@ public void getAllGlobalProperties_shouldReturnAllGlobalPropertiesInTheDatabase(
executeDataSet(ADMIN_INITIAL_DATA_XML);
assertEquals(allGlobalPropertiesSize + 9, adminService.getAllGlobalProperties().size());
}


@Test
public void getAllGlobalProperties_shouldReturnPropertiesInAscendingOrder() {
executeDataSet(ADMIN_INITIAL_DATA_XML);
List<GlobalProperty> properties = adminService.getAllGlobalProperties();

assertFalse(properties.isEmpty(), "The list of global properties should not be empty");

// Verify the properties are in ascending order
for (int i = 0; i < properties.size() - 1; i++) {
String currentProperty = properties.get(i).getProperty();
String nextProperty = properties.get(i + 1).getProperty();

assertTrue(currentProperty.compareTo(nextProperty) <= 0,
"The global properties should be in ascending order by the property name");
}
}

@Test
public void getAllowedLocales_shouldReturnAtLeastOneLocaleIfNoLocalesDefinedInDatabaseYet() {
assertTrue(adminService.getAllowedLocales().size() > 0);
Expand Down
44 changes: 44 additions & 0 deletions api/src/test/java/org/openmrs/api/db/hibernate/MatchModeTest.java
@@ -0,0 +1,44 @@
/**
* 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 static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Arrays;
import java.util.stream.Stream;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

public class MatchModeTest {

static Stream<Object[]> data() {
return Arrays.stream(new Object[][]{
{MatchMode.START, "TEST", true, "test%"},
{MatchMode.END, "TeST", true, "%test"},
{MatchMode.ANYWHERE, "test", true, "%test%"},
{MatchMode.EXACT, "TEst", true, "test"},
{MatchMode.START, "TEST", false, "TEST%"},
{MatchMode.END, "TeST", false, "%TeST"},
{MatchMode.ANYWHERE, "test", false, "%test%"},
{MatchMode.EXACT, "TEst", false, "TEst"},
});
}

@ParameterizedTest
@MethodSource("data")
public void shouldMatchPatternCorrectly(MatchMode matchMode, String input, boolean caseInsensitive, String expectedPattern) {
if (caseInsensitive) {
assertEquals(expectedPattern, matchMode.toCaseInsensitivePattern(input));
} else {
assertEquals(expectedPattern, matchMode.toCaseSensitivePattern(input));
}
}
}

0 comments on commit 9ecc5a6

Please sign in to comment.