Skip to main content

Creating Custom History Types

While dealing with business data, 'history' has its own role to play. The 'history' here means, data specific to a point in time. Most of the enterprise application may capture audit information when the user commits a business transaction. These audit data may includes previous value of some sensitive attributes, user who modified the data, modified date etc. ADF BC framework ships with a number of history types, but you can also create your own on need basis.

Life cycle of a 'history type' attribute

Question here is, who does update these attributes (and when)? Well, this job is done by the ADF BC run time at prepareForDML phase of a transaction post cycle. When you commit/post a transaction, Transaction Manager would take each entity object through a transaction post cycle as shown in the following diagram.


prepareForDML - Issues pre-doDML ntofication for all entities, this is used to assign history values before an entity row is saved. Run time uses attribute definition to identify the nature of each attribute and takes appropriate actions.

doDML - Performs the appropriate SQL Data Manipulation Language operations on the database to reflect an update, delete or insert operation on an Entity Object beforeCommit

beforeCommit - This is invoked on each modified entity instance after the changes have been posted to the database, but before they are committed. Last chance for you to validate your business data(if you have some data validation logic built using stored procedure, you can hook that call here). This is also a place for invoking third party services passing data from the current entity instance.

afterCommit - Send notifications about a change to an entity object's state. This is invoked on the entity instance after committing data to DB.

History types are very much extensible in ADF, you can build your own history type without much effort. This part is very well documented in Fusion Developer's Guide, please see topic 38.10 Creating New History Types to learn more. In this post, I'm sharing simple example built using custom history type.

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS3 + HR Schema]

A glance at the implementation

This example captures the old value of DepartmentName attribute using DepartmentNameAudit field whenever DepartmentName changes( DepartmentNameAudit is a custom 'history type'). I'm highlighting the steps for creating custom history type used in this example - 'modified value'.

1. Define a new history type(modified value) using the editor( Tools->Preferences->Business Components->History Types)


2. Provide implementation for the custom history type by overriding EntityImpl::getHistoryContextForAttribute(...)

@Override
protected Object getHistoryContextForAttribute(AttributeDefImpl attr) {

  if (attr.getHistoryKind() == MODIFIED_VALUE_HISTORY_TYPE) {
  // your logic goes here
  // return the value for the history type attribute
  }
  return super.getHistoryContextForAttribute(attr);
}


3. Mark the desired attribute using newly defined history type



How to run this sample?

1. Download the source and extract this to you local host.

2. This example uses HR schema. The attribute DepartmentNameAudit in Departments entity is mapped to OLD_DEPARTMENT_NAME column in DEPARTMENTS table. If you want to run the sample, then please add this extra column to DEPARTMENTS table.

ALTER TABLE DEPARTMENTS ADD OLD_DEPARTMENT_NAME varchar2(30);

3. Run main.jspx

4. Modify Department Name field and commit the changes. Please see the corresponding record in you database table(DEPARTMENTS). The column OLD_DEPARTMENT_NAME may have the previous value of DEPARTMENT_NAME.


Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Comments

Anonymous said…
Thank you very much. Till now, we were setting history column values, during each save, explicitly.
Tuttu said…
Hi Jobinesh,
I have a problem when using the history column. when i update a row twice(update > save > update > save) i get error "Another user has changed the row with primary key oracle.jbo.Key". So I set "refresh after update" to true in entity object. Now the error is minimized to insert and then update on same row(insert > save > update > save). If i try to set "refresh after insert" also to true, db fires null exception for history column.
I am working on 11.1.1.2. Is there any settings that can solve my issue?
Also if after closing the message, if i try to save, then it works.(insert > save > update > save > save).
Jobinesh said…
Tuttu,
Do you have db trigger for updating history columns? if yes, you dont need to use ADF history types + mark them as non mandatory
Tuttu said…
Actually the problem was with the application I was working on. I tried it on a fresh test application and it works. Also the refresh after update was actually not working. It also resets the value before saving(during updates). But since refresh after insert was not selected, the value will be set during insert. So no change was visible.
I have solved the problem by executing the query once again in my application(the problematic one).
Thanks for the suggestion.
Tuttu

Popular posts from this blog

How to set Bind Variable Values at runtime ?

In this post I'm sharing a couple of approaches for programmatically setting bind variables values at run time. This post is an attempt to explain 'When to use what ?'[ In case if you are familiar with 'Bind Variables' in ADF BC, please refer Section 5.10, Working with Bind Variables in Fusion Developer's Guide ]

1. Set the Bind Variable value using RowSet::setNamedWhereClauseParam(...)

You can use use the setNamedWhereClauseParam(...) method on the ViewObject interface (which extends oracle.jbo.RowSet) to set the value for bind variables. Please note this sets the value on default RowSet. In other words, this doesn't have any effect on the secondary RowSets that you/system generates.
ViewObject vo = am.findViewObject("EmployeesView1"); vo.setNamedWhereClauseParam("bindVarDeptId", new Number(10)); vo.executeQuery();
2. Set the Bind Variable value using ViewObject's VariableValueManager::setVariableValue(...)

VariableValueManager Ma…