Skip to main content

Case versioning

Info

When updating case objects, the change history is automatically created. However, when the case type changes, which is often the case in rapidly developing systems that change the data storage model, a new version of the case is created. The action occurs automatically when (using business layer services) we save the case (existing) and when a type change is detected, i.e. a new field is added, a field type change is detected. Since version 3.0.1.0.2, the version change action can be performed "manually" by invoking the appropriate sequence of saves, which we will discuss at the end of the article.

Case version change​

As described in the introduction, most often the case version change occurs automatically when the case type (definition describing the case) changes. This is because it is difficult to clearly assess the intention of the system that saves an existing case, which simultaneously changes its definition, and to identify why the type was changed. It seems that the best solution is that when the algorithm recognizing the case type identifies a change in its definition, a new case is added, with a new type definition, maintaining a consistent image of the data of the updated object. Saving together with the case version change causes:

  1. The previous image of the case is saved with the status Z (closed), without changing the remaining parameter values ​​(fields).
  2. A new case (new object instance) is created with the status A (active) with a new type definition and new, updated field values.

It is very important that the previous version of the case can be easily found. Therefore, the following fields have been defined in the case header to support searching for previous versions:

  • previousVersionId - a field containing the value of the previous version of the case identifier
  • rootVersionId - a field containing the value of the first version of the case identifier

The dependencies between individual case versions can be described in the following relation (example data of case identifier values):

Case version descriptioncaseIdpreviousVersionIdrootVersionId
First version12345null12345
Second version123461234512345
Third version123471234612345
Fourth version123481234712345
...

As you can see from the table above, the rootVersionId value always indicates the first version of the case, and previousVersionId indicates the case in the previous version.

tip

Remember that only one of the case versions (the last one) can be active, i.e. have a status other than Z (closed). In the example given in the table, the status field will have the following values ​​(see also the article devoted to CaseHeader case header, where you will find information about the status field):

Case version descriptionstatus - allowed values ​​
First versionZ
Second versionZ
Third versionZ
Fourth versionA, O, N, Z

Thanks to this data organization, we can always quickly find the necessary data about individual versions. For example, we can quickly find all versions of a case using a Lucene query (example using the CaseBusinessAction.searchLuceneByQueryXML service):

<query>mrc_rootVersionId:118904</query>

image2020-9-14_10-36-13

Consequences of changing the case version​

We can always engage in an academic discussion about whether changing the case version when changing the type definition is right and whether the implementation, such and not another behavior of Mercury DB (HgDB) is correct. When implementing a solution, we should always be guided by the rule of the golden mean, compromise, and as we know, compromise makes everyone unhappy. Therefore, it is worth presenting the pros and cons of such and not another universal solution:

ConsPros
The direct link between the case for which the version is changed and another parent case in the definition of the complex case is lost. The change causes the new version to no longer be related, because the link is based on case identifiers. The parent case still remains related to the old version of the case.The link between the previous version of the case and another parent case is maintained, and thus the consistency of the definition of the entire complex case object is maintained. Remember that we are working with objects - changing the definition of a fragment of an object changes the definition of the whole - the adopted solution prevents such events.
The new definition of the case type may be completely different than the previous one - we prevent data loss, which would result from the lack of the possibility of skillful, automatic conversion of different types of cases.
No possibility of updating previous versions of cases.Old versions of cases are still available in the system and can be used in READ-ONLY mode.
There is no need to carry out data migration processes when the data model used by applications storing data in Mercury DB (HgDB) changes

Preventing changes to case versions​

The most common problem is UNCONSCIOUS, unintentional change of the case version.

Remember!

The case data model is dynamic and is verified every time a case is saved. The most common error is incorrect interpretation of the String and Text type fields. Fields of these types are very similar and if this is not explicitly presented in the case save request, the transmitted values ​​may be overinterpreted. An example would be sending a field named name with a length of less than 64 characters. Mercury DB (HgDB) will interpret such a field as String. If we later update this field to a string longer than 64 characters, it will be interpreted as Text - and thus the field definition will change, and consequently the entire case metadata definition (case type), which may lead to the creation of a new case version. So to avoid such a situation, if you know that the field may contain more than 64 characters, it is always worth setting the type attribute to Text.

How to minimize the risk of UNCONSCIOUS changes to case versions?

Send as much type definition data as possible with the case​

First of all, you should send (IF POSSIBLE) as much case type definition data as possible to the system with the case. For example, when using SOAP services in the sent XML of the case, it is worth adding the "type" and "position" attributes - both attributes are optional, but they are very helpful in unambiguous interpretation of the case field definition (example):

GoodBetter
<dictionaryName>TEST</dictionaryName>
<name>Test Name3</name>
<code>Test Code3</code>
<dictionaryName position="1" type="String">TEST</dictionaryName>
<name position="2" type="String">Test Name3</name>
<code position="3" type="String">Test Code3</code>

Set a configuration parameter that blocks the creation of new versions​

Another option is to use the server configuration parameter that blocks the change of the case version and the request parameter sent with the context during the save. Since version 3.0.2.x, a Mercury DB (HgDB) server configuration parameter called mercury.saveRequestContext.forceChangeType.default has been introduced. In the mercury.properties configuration file, you should insert:

mercury.saveRequestContext.forceChangeType.default=false

Such a configuration will block changes to the case versions during their update at the save operation level. However, this does not mean that we are blocking this operation permanently. The parameter value is the default value to be applied during the save. Its value can be overwritten by the appropriate value of the parameter sent in the context of the save request itself. Just add the saveRequestContext.forceChangeType parameter with the appropriate value (true or false) to the sent context object to enable or disable automatic case version change, e.g.:

<requestProperties>
<entry>
<key>saveRequestContext.forceChangeType</key>
<value>true</value>
</entry>
</requestProperties>

Case version change and the Case Catalog​

As mentioned earlier ("Consequences of case version change"), one of the disadvantages of the solution is that in the case of case version change, the link between the parent case and the latest version of the child case is lost. The dependency issues of composite cases (complex objects) are described in the article Case vs. object. Another thing is the organization of the Case Catalog. The case catalog (based on the Case2Case entity) is a tree-like relationship between cases (regardless of their definition). They are created based on the value of the rootVersionId field, which means that the created relationship always indicates a relationship between the last versions of the given cases.

Other use of the case versioning mechanism​

The trend these days is the electronicization of all types of documents. Mercury DB (HgDB) is an ideal tool for storing such data, providing a rich API for storing and searching for data. A very good example of using the system is storing data related to all types of contracts, with which the addresses of the parties containing them are related. Of course, we can define the contract object in such a way that the address field does not constitute a separate, subordinate case, but then we are exposed to data duplication, during the entry of which we are also exposed to errors. Therefore, most often, address objects are stored as separate cases and a link is created between the contract case and this address. And a dilemma arises, what to do when the street name changes? If we operate on an address instance, its change, through the relationship, also affects the contract, and such a situation is unacceptable from a business point of view, because changing the address requires an annex (another contract) to the basic contract.

To maintain business consistency, you can use the mechanism for creating new versions of cases so that the contract is linked to the old version of the address, and the annex to the new one. And what was previously seen as a disadvantage turns out to be an advantage.

We wrote earlier that a new version of the case is created when its type is changed, but since version 3.0.1.0.2 you can force the creation of a new version of the case while maintaining the same definition of the case metadata. To do this, follow these steps:

  1. Download the case whose version you want to change (original).
  2. Make a copy of its object (copy).
  3. In the original, we change:
    1. the status header field to Z (closed),
    2. the dirty header field to true
  4. In the copy, we set:
    1. the previousVersionId header field to the value from the caseId field
    2. the caseId header field to an empty value
    3. the dirty header field to true
    4. We change the parameters that are to be changed
  5. We create a list of cases:
    1. we set the original to the first position
    2. we set the copy to the second position
  6. We send the list for writing

This will create a new version of the case.

Changing the case version using the hgdb-client-open (Java) client​

Below is an example of implementing a case version change:


MrcObject mrcObject;
MrcObject mrcObjectCopy;

SaveRequestContext.FORCE_CHANGE_TYPE.setContextValue(context, "false");
ICaseBusiness caseBusiness = (ICaseBusiness) getRegistry().getContextBean(ICaseBusiness.class);
/* Get the original case from the database */
mrcObject = caseBusiness.find(context, /*caseId */ 12345L);
assertNotNull(mrcObject);
/* Get the header instance of the retrieved case */

/* create a copy of the original case - START */
mrcObjectCopy = mrcObject instanceof MrcObjectWithRequiredPosition ? new MrcObjectWithRequiredPosition()
: new MrcObject();
mrcObjectCopy.copy(context, mrcObject);

assertTrue("Objects should be the same", mrcObject.equals(mrcObjectCopy));
/* create a copy of the original case - END */

/* Old version of the case - START */
MrcCaseHeader origHeader = (MrcCaseHeader) mrcObject.getPropertyValue("mrcCaseHeader");
/* Get the original case identifier */
Long origCaseId = origHeader.getCaseId();
/* set the old case to closed */
origHeader.setStatus(Case.State.Z);
origHeader.setDirty(Boolean.TRUE);
/* Old version of the case - END */

/* New version of the case - START */
/* Getting the instance of the copy header */
MrcCaseHeader copygHeader = (MrcCaseHeader) mrcObjectCopy.getPropertyValue("mrcCaseHeader");
/* I set the new version of the case to null */
copygHeader.setCaseId(null);
/* I set the new version to the previous one */
copygHeader.setPreviousVersionId(origCaseId);
copygHeader.setDirty(Boolean.TRUE);
/* New version of the case - END */

/** I create a list of cases to save - START */
/* the first position on the list will be the old version of the case, the second will be the new one */
MrcObjectMetadata metadata = new MrcObjectMetadata();
metadata.setClassName(copygHeader.getTypeCode());
metadata.setStatus(MrcCaseStatus.ALL);
MrcList saveList = new MrcList(metadata);
saveList.addArrayData(mrcObject);
saveList.addArrayData(mrcObjectCopy);
/** Creating a list of cases to save - END */

/* Saving the list of cases */
MrcList savedList = caseBusiness.saveList(context, saveList, /* forceAddStore2Type */ true);

Kroki zmiany wersji sprawy za pomocą usług SoapUI​

Below are illustrations of the invocation of the individual steps of changing the case version in the SoapUI project. The CaseBusinessAction service was used, the WSDL of which is available at the address of the installed Mercury DB (HgDB) product:

[https|https]://<server_name>[:port]/mercury-ws-app/services/CaseBusinessAction?wsdl
  • Retrieving an existing case using the findXML method:

image2020-9-14_14-14-22

  • I copy the resulting XML to Notepad and create a list of cases for saving by making the following changes:
    • (original case) line 8: change to <status type="String">Z</status>
    • (original case) line 24: change to <dirty type="Boolean">true</dirty>
    • (case copy) line 33: change to <caseId type="Integer"></caseId>
    • (case copy) line 38: add, set <previousVersionId type="Integer">118906</previousVersionId>
    • (case copy) line 54: change to <dirty type="Boolean">true</dirty>
<variable    type="TestSimpleDictionary[]">
<item type="TestSimpleDictionary" withRequiredPosition="true">
<mrcCaseHeader type="MrcCaseHeader">
<caseId type="Integer">118906</caseId>
<groupId type="Integer">54504</groupId>
<typeId type="Integer">54404</typeId>
<typeCode type="String">TestSimpleDictionary</typeCode>
<status type="String">Z</status>
<rootVersionId type="Integer">118906</rootVersionId>
<priceValue type="Decimal">0.0</priceValue>
<storeCount type="Integer">1</storeCount>
<storeId type="Integer">52404</storeId>
<createDate type="Date">2020-09-14 14:13:10</createDate>
<createdBy type="String">slawas</createdBy>
<lastModifyDate type="Date">2020-09-14 14:13:10</lastModifyDate>
<lastModifiedBy type="String">slawas</lastModifiedBy>
<modifyComment type="String">SOAP request</modifyComment>
<createdByRoleName type="String">CKBPM-Team</createdByRoleName>
<lastModifiedByRoleName type="String">CKBPM-Team</lastModifiedByRoleName>
<className type="String">TestSimpleDictionary</className>
<objectID type="String">?.TestSimpleDictionary</objectID>
<rootVersionContextID type="String">SoapUI.001</rootVersionContextID>
<version type="String">4509</version>
<dirty type="Boolean">true</dirty>
<pkPropertyName type="String">dictionaryName||'.'||code</pkPropertyName>
</mrcCaseHeader>
<dictionaryName position="1" type="String">TEST</dictionaryName>
<name position="2" type="String">Test Name4</name>
<code position="3" type="String">Test Code4</code>
</item>
<item type="TestSimpleDictionary" withRequiredPosition="true">
<mrcCaseHeader type="MrcCaseHeader">
<caseId type="Integer"></caseId>
<groupId type="Integer">54504</groupId>
<typeId type="Integer">54404</typeId>
<typeCode type="String">TestSimpleDictionary</typeCode>
<status type="String">A</status>
<previousVersionId type="Integer">118906</previousVersionId>
<rootVersionId type="Integer">118906</rootVersionId>
<priceValue type="Decimal">0.0</priceValue>
<storeCount type="Integer">1</storeCount>
<storeId type="Integer">52404</storeId>
<createDate type="Date">2020-09-14 14:13:10</createDate>
<createdBy type="String">slawas</createdBy>
<lastModifyDate type="Date">2020-09-14 14:13:10</lastModifyDate>
<lastModifiedBy type="String">slawas</lastModifiedBy>
<modifyComment type="String">SOAP request</modifyComment>
<createdByRoleName type="String">CKBPM-Team</createdByRoleName>
<lastModifiedByRoleName type="String">CKBPM-Team</lastModifiedByRoleName>
<className type="String">TestSimpleDictionary</className>
<objectID type="String">?.TestSimpleDictionary</objectID>
<rootVersionContextID type="String">SoapUI.001</rootVersionContextID>
<version type="String">4509</version>
<dirty type="Boolean">true</dirty>
<pkPropertyName type="String">dictionaryName||'.'||code</pkPropertyName>
</mrcCaseHeader>
<dictionaryName position="1" type="String">TEST</dictionaryName>
<name position="2" type="String">Test Name4</name>
<code position="3" type="String">Test Code4</code>
</item>
</variable>
  • Sending prepared data for saving via the saveXML method:

image2020-9-14_14-30-56

  • As the example shows, a new version of the case object instance has been created.