org.dbunit.DatabaseUnitRuntimeException: org.dbunit.dataset.NoSuchColumnException: OBS.STATUS - (Non-uppercase input column: status) in ColumnNameToIndexes cache map.

Hi everyone,

I am trying to migrate the Obs class from Hibernate XML mapping to annotation-based mapping in openmrs-core (module 3).

After making all the required changes, I am getting the following error when running: mvn clean package

**Error Stacktrace **

[ERROR] org.openmrs.AllergiesTest.addAll2_shouldAllowNonDuplicateNonCodedAllergen
Time elapsed: 0.130 s <<< ERROR!

org.dbunit.DatabaseUnitRuntimeException:
org.dbunit.dataset.NoSuchColumnException: OBS.STATUS -
(Non-uppercase input column: status) in ColumnNameToIndexes cache map.
Note that the map’s column names are NOT case sensitive.

at org.openmrs.test.jupiter.BaseContextSensitiveTest.executeDataSet(BaseContextSensitiveTest.java:847)
at org.openmrs.test.jupiter.BaseContextSensitiveTest.executeDataSet(BaseContextSensitiveTest.java:734)
at org.openmrs.test.jupiter.BaseContextSensitiveTest.baseSetupWithStandardDataAndAuthentication(BaseContextSensitiveTest.java:969)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)

Caused by: org.dbunit.dataset.NoSuchColumnException: OBS.STATUS -
(Non-uppercase input column: status) in ColumnNameToIndexes cache map.
at org.dbunit.dataset.AbstractTableMetaData.getColumnIndex(AbstractTableMetaData.java:117)
at org.dbunit.operation.AbstractOperation.getOperationMetaData(AbstractOperation.java:89)
at org.dbunit.operation.RefreshOperation.execute(RefreshOperation.java:101)
at org.openmrs.test.jupiter.BaseContextSensitiveTest.executeDataSet(BaseContextSensitiveTest.java:840)

Update I Have Done

  • Module: openmrs-core (module 3)

  • Converted org.openmrs.Obs from Hibernate .hbm.xml mapping to annotations

@Entity
@Table(name = "obs")
@BatchSize(size = 25)
@Audited
public class Obs extends BaseFormRecordableOpenmrsData {
	
	/**
	 * @since 2.1.0
	 */
	public enum Interpretation {
		NORMAL, ABNORMAL, CRITICALLY_ABNORMAL, NEGATIVE, POSITIVE, CRITICALLY_LOW, LOW, HIGH, CRITICALLY_HIGH, VERY_SUSCEPTIBLE, SUSCEPTIBLE, INTERMEDIATE, RESISTANT, SIGNIFICANT_CHANGE_DOWN, SIGNIFICANT_CHANGE_UP, OFF_SCALE_LOW, OFF_SCALE_HIGH
	}
	
	/**
	 * @since 2.1.0
	 */
	public enum Status {
		PRELIMINARY, FINAL, AMENDED
	}
	
    .
    .
    .
	
	private static final Logger log = LoggerFactory.getLogger(Obs.class);

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "obs_id")
	protected Integer obsId;

	@ManyToOne
	@JoinColumn(name = "concept_id", nullable = false)
	protected Concept concept;

	@Column(name = "obs_datetime", nullable = false)
	@Temporal(TemporalType.TIMESTAMP)
	protected Date obsDatetime;

	@Column(name = "accession_number")
	protected String accessionNumber;
	
	/**
	 * The "parent" of this obs. It is the grouping that brings other obs together. note:
	 * obsGroup.getConcept().isSet() should be true This will be non-null if this obs is a member of
	 * another groupedObs
	 * 
	 * @see #isObsGrouping() (??)
	 */
	@ManyToOne
	@JoinColumn(name = "obs_group_id")
	protected Obs obsGroup;
	
	/**
	 * The list of obs grouped under this obs.
	 */
	@OneToMany(mappedBy = "obsGroup", cascade = CascadeType.REMOVE, orphanRemoval = true)
	.
	.
	.

	@Column(name = "interpretation", length = 32)
	@Enumerated(EnumType.STRING)
	private Interpretation interpretation;

	@Column(name = "status", length = 16, nullable = false)
	@Enumerated(EnumType.STRING)
	private Status status = Status.FINAL;

	@OneToOne(mappedBy = "obs", cascade = CascadeType.ALL)
	private ObsReferenceRange referenceRange;
}
  • Deleted the old Hibernate XML mapping file
<?xml version="1.0"?>
<!--

    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.

-->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.openmrs">

	<class name="Obs" table="obs" batch-size="25">

		<id name="obsId" type="int" column="obs_id" unsaved-value="null">
			<generator class="identity">
				<param name="sequence">obs_obs_id_seq</param>
			</generator>
		</id>

		

		<property name="uuid" type="java.lang.String"
			column="uuid" length="38" unique="true" />

		<property name="personId" type="java.lang.Integer" length="11" 
			column="person_id" not-null="true" insert="false" update="false" />
			
		<property name="obsDatetime" type="java.util.Date"
			column="obs_datetime" not-null="true" length="19" />
					
		<property name="valueGroupId" type="java.lang.Integer"
			column="value_group_id" length="11" />
		<property name="valueDatetime" type="java.util.Date"
			column="value_datetime" length="19" />
		<property name="valueNumeric" type="double"
			column="value_numeric" length="22" />
		<property name="valueModifier" type="java.lang.String"
			column="value_modifier" length="2" />
		<property name="valueText" type="text"
			column="value_text" length="65535" />
		<property name="valueComplex" type="java.lang.String"
		    column="value_complex" length="1000" />
			
		<property name="comment" type="java.lang.String"
			column="comments" length="255" />
		<property name="accessionNumber" type="java.lang.String"
			column="accession_number" length="255" />
			
				
		<property name="dateCreated" type="java.util.Date"
			column="date_created" not-null="true" length="19" />
		<property name="voided" type="java.lang.Boolean" column="voided"
			length="1" not-null="true" />
		<property name="dateVoided" type="java.util.Date"
			column="date_voided" length="19" />
		<property name="voidReason" type="java.lang.String"
			column="void_reason" length="255" />
		<property name="formNamespaceAndPath" type="java.lang.String"
			column="form_namespace_and_path" length="255" access="field" />
		<property name="status" column="status" length="16" not-null="true">
			<type name="org.hibernate.type.EnumType">
				<param name="enumClass">org.openmrs.Obs$Status</param>
				<param name="useNamed">true</param>
			</type>
		</property>
		<property name="interpretation" column="interpretation" length="32">
			<type name="org.hibernate.type.EnumType">
				<param name="enumClass">org.openmrs.Obs$Interpretation</param>
				<param name="useNamed">true</param>
			</type>
		</property>
		
		<!-- Associations -->

		<many-to-one name="person" class="Person" not-null="true" column="person_id" />
		<many-to-one name="concept" class="Concept" not-null="true" column="concept_id" />
		<many-to-one name="valueCoded" class="Concept" column="value_coded" />
		<many-to-one name="valueCodedName" class="ConceptName" column="value_coded_name_id" />
		<many-to-one name="valueDrug" class="Drug" column="value_drug" />
		<many-to-one name="order" class="Order" column="order_id" cascade="none" />
		<many-to-one name="location" class="Location" not-null="false" column="location_id" />
		<many-to-one name="encounter" class="Encounter" column="encounter_id" />
		<many-to-one name="creator" class="User" not-null="true" />
		<many-to-one name="voidedBy" class="User" column="voided_by" />
		
		<!-- many-to-one association from this obs to its parent grouping obs -->
		<many-to-one name="obsGroup" class="Obs" column="obs_group_id" />
		
		<!-- one-to-many association to all obs in this grouping -->
		<set name="groupMembers" inverse="true" cascade="delete" order-by="obs_id"
		access = "field" batch-size="25">
			<key column="obs_group_id" />
			<one-to-many class="Obs" />
		</set> 
		<many-to-one name="previousVersion" class="Obs" column="previous_version" unique="true" />

		<one-to-one name="referenceRange" class="org.openmrs.ObsReferenceRange" cascade="all"  />
	</class>
	
</hibernate-mapping>

  • Added annotated class explicitly in tests, for example:
@Test
public void saveOrder_shouldFailIfTheJavaTypeOfThePreviousOrderDoesNotMatch() throws Exception {

    HibernateSessionFactoryBean sessionFactoryBean = (HibernateSessionFactoryBean) applicationContext
       .getBean("&sessionFactory");
    Configuration configuration = sessionFactoryBean.getConfiguration();

    HibernateAdministrationDAO adminDAO = (HibernateAdministrationDAO) applicationContext.getBean("adminDAO");
    StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
       .configure().applySettings(configuration.getProperties()).build();

    Metadata metaData = new MetadataSources(standardRegistry).addAnnotatedClass(Obs.class).getMetadataBuilder().build();

Is there anything else required when migrating a core domain class (Obs) from XML mapping to annotations that might affect DbUnit tests?

The DbUnit error

org.dbunit.dataset.NoSuchColumnException: OBS.STATUS - (Non-uppercase input column: status)

gives us a hint of the cause of the error which is not really a Hibernate runtime error but DbUnit complaining that the column status in your test dataset does not match what Hibernate expects.

This is what you wrote:

In this case, Hibernate will map the enum Status as a string in the status column which is write but DbUnit dataset XMLs used in core tests still refer to OBS.STATUS. In other words, the DbUnit can’t match STATUS (uppercase) in XML to status (lowercase) in the annotation. To solve this change the column name to match the XML dataset.

Here logs state that column names are not case sensitive.

while debugging from IDE debugger i found that in DB unit file , at the time of retrieval of column, status key is not in that hashmap which retrieve from connection.getDataSet().getMetadata()

Thanks for the clarity. This shows that DbUnit is already configured to handle STATUS vs status, so case is not the real issue as I previously thought. Debugging shows that the status key is missing from the metadata hashmap. Could you kindly confirm whether DbUnit is not seeing the column in the dataset/metadata at all?

In cache HashMap (which maps their column index value with column name) few keys are not present including status

Okay! the problem is that your DbUnit dataset XML doesn’t include the status column, which DbUnit expects because your new Obs class has a status field mapped to the status column.

In addition to all the above, it would be great to share a pull request with your current changes.

dataset has the status column but still it does not found in cache.

sure!

here is the Pull Request @jwnasambu @dkayiwa

@kmvipin i have put a comment on your pull request. Try it out and see how it goes.

thanks @dkayiwa, this may works but after adding 3 new columns in dataset now it will throw OutOfMemory Java Heap error. I am trying to finding the root cause of it.