Triggers are the mechanism that is best suited for maintaining semantic integrity in a database (if we allow for a closed-world assumption). Triggers are well known from SQL. Most relational database management systems implement trigger mechanisms.
Let us assume that we have stored several album
documents
within our encyclopedia collection. In addition, we have stored several
review
documents that relate to some of those album documents.
When we delete an album
document, we want to make sure that the
review
elements relating to the deleted album document are also
removed from the database.
We can achieve this by defining an onDelete
trigger to the
album
node of the album
document type.
This trigger is called immediately before the album
node is
deleted.
<xs:element name = "album" > <xs:annotation> <xs:appinfo> <tsd:elementInfo> <tsd:logical> <tsd:trigger> <tsd:onDelete type = action> encyclopedia_album.deleteRelatedReviews </tsd:onDelete> </tsd:trigger> </tsd:logical> </tsd:elementInfo> </xs:appinfo> </xs:annotation> ... </xs:element>
Sample trigger code is shown in the following example. The deleted
album
document node is passed to the method
deleteRelatedReviews
, which implements the trigger. This allows us
to extract the album/@albumNo
attribute value, and then to delete
any review instances that specify this value in their
review/album/@albumNo
node. These cascading delete
operations are performed via server extension callbacks.
package tamino.SXS.jazz.encyclopedia.album; import com.softwareag.ino.sxs.ASXJBase; public class encyclopedia_album extends ASXJBase { /** * The default constructor * (No other constructor allowed.) */ public encyclopedia_album() { } /** * Delete all reviews relating to this album instance (used as trigger function) * @param object_id parameter * @param element_id parameter * @param document parameter */ public void deleteRelatedReviews( StringBuffer collection, StringBuffer doctype, StringBuffer inoId, String document) throws java.lang.Exception { // We rely on the fact that albumNo is an attribute of the root element // (the official way would be to employ a parser) int aPos = document.indexOf("albumNo"); if (aPos < 0) throw new Exception("No albumNo attribute"); int qaPos = document.indexOf('"', aPos + 8); int qePos = document.indexOf('"', qaPos + 1); String albumNo = document.substring(qaPos, qePos + 1); // create Delete query string for the callback String xmlQuery = "review[album/@albumNo=" + albumNo + "]"; // create new string buffer for call back results StringBuffer response = new StringBuffer(); // callback: delete all documents matching this query int ret = SxsXMLDelete("encyclopedia", xmlQuery, response); if (ret != 0) processError(); } // Here we dive deep into the rather complex error // handling for server extensions callbacks. private void processError() throws Exception { // identify the callback error int msgNo = SxsGetMsgNo(); switch (msgNo) { case INO_ERROR : StringBuffer msgBuf = new StringBuffer(); // Get the Tamino Server message number int inoMsgNo = SxsGetInoMsgNo(); // Get the Tamino Server messageline int ret = SxsXMLGetMessage(msgBuf); throw (new Exception("INO_ERROR: " + inoMsgNo + " " + msgBuf)); default : // Get the SXS message corresponding to callback error String msg = SxsGetMsgText(); throw (new Exception("SXS Error: " + msgNo + " " + msg)); } } }
Note that triggers can be cascaded. We could, for example, equip
review
documents with an onDelete
trigger that
removes any critic
instances that are only referred to by the
deleted review
instance. The deletion of an album
document could thus cause the deletion of a critic
document. This
is conceptually debatable: both reviews and critics are first class business
objects, so their existence should not depend on other business objects.