- 09 Aug 2008 -

When writing a dbunit test for the CohortServiceTest, I encountered this non-informative error:

org.dbunit.dataset.NoPrimaryKeyException: COHORT_MEMBER

In the Cohort mapping file the cohort_member table is mapped like any hibernate collection:

<set name="memberIds" cascade="none" lazy="true" table="cohort_member">
    <key column="cohort_id" />
    <element column="patient_id" type="integer" />

It looks normal enough.  Cohort.memberIds is a list of integers defined by the cohort_member table.

Hibernate automatically creates the schema in our test hsqldb because we have hbm2ddl set to “auto”.  The problem arose here because hibernate wasn’t creating a dual primary key on the member_ids table.  Digging through the code, I found that Hibernate only assumes the primary keys are the columns that set to “not null”.

The corrected mapping with both columns set to be not-null:

<set name="memberIds" cascade="none" lazy="true" table="cohort_member">
    <key column="cohort_id" not-null="true"/>
    <element column="patient_id" type="integer" not-null="true"/>

Now hibernate creates the table with primary keys and hsql is happy again.

This is in the openmrs category tagged as , , , ,


I’m going to post this here as a reminder to other OpenMRS developers (and myself) and to hopefully save a few hours/days of debugging. The key to getting transactions to work correctly in the webapp is to, well, tell Spring that we’re in a transaction. You can do this by simply putting an @Transactional annotation in the *Service java interface.

Without that key piece of text, the service and its methods will still work. However, they’ll work in a readonly kind of way. All data written to the database will be rolled back when the body of work has completed. This little error manifested itself recently after our reporting code-a-thon. During the event we had to create a new API service, which is only done maybe a few times a year. The service didn’t get the @Transactional tag and so therefore all database editing failed that went through the ReportService. The unit tests set up for the object passed, but that is because a unit test happens all within a single transaction — the rollback is always done after the completion of the test anyway.

I added a note to http://openmrs.org/wiki/OpenMRS_API about the requirement, but I’m sure there will be an OpenMRS developer in the future struggling with their object not saving or updating in the database and be completely baffled by it.

This is in the openmrs category tagged as , , ,

Add a comment »