Friday, December 16, 2011

Showing A Message Before The Page Is Rendered.

One of my client's requirement was to show a message on the UI if the rows to be rendered on the UI are more than 150. This was required to inform the user that "For better performance, download the records in an excel". In this blog, I am writing my findings about how I did it.

For this blog, I am using SCOTT.EMP table, and will show the warning if the row count is greater than 10 instead of 150. The final outcome will look like this:



Here is how it can be done. Consider the following project:


Here the model is pretty simple, created a simple VO based on SCOTT.EMP table to include all the employee records. Included the EmpVO in the application module. In the view project, highlighted the files used:

  1. baseTaskFlow.xml - Its the bounded task flow which contains the activities to excute EmpVO, a method activity to render the message, and then the view activity to render the table showing the employees records.
  2. BasePage.jspx - This page shows the baseTaskFlow as a region.
  3. EmployeesView.jsff - Contains the table to render employees records.
  4. EmployeesViewBean.java - The request scope bean that contains the method to render the message.

Here is how the baseTaskFlow looks like:



Here, executeEmpVO simply executes the EmpVO. Its a method written in AppModuleAMImpl and here is the code of AmmModuleAMImpl:

package model;

import model.common.AppModule;

import oracle.jbo.server.ApplicationModuleImpl;
import oracle.jbo.server.ViewObjectImpl;

public class AppModuleImpl extends ApplicationModuleImpl implements AppModule {
    /**
     * This is the default constructor (do not remove).
     */
    public AppModuleImpl() {
    }

    /**
     * Container's getter for EmpVO1.
     * @return EmpVO1
     */
    public ViewObjectImpl getEmpVO1() {
        return (ViewObjectImpl)findViewObject("EmpVO1");
    }
  
    public void executeEmpVO(){
        getEmpVO1().executeQuery();
    }
}

The renderMessage is a method-call activity that calls the renderRowsExceededPopupMessage() method of the EmployeesViewBean, and here is its complete code:

package view;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCIteratorBinding;
import oracle.binding.BindingContainer;

public class EmployeesViewBean {
    public EmployeesViewBean() {
    }
  
    public void renderRowsExceededPopupMessage(){
      
      BindingContext bindingctx = BindingContext.getCurrent();
      BindingContainer bindings = null;
      bindings = bindingctx.getCurrentBindingsEntry();
      DCBindingContainer bindingsImpl = (DCBindingContainer) bindings;
      DCIteratorBinding dciter = null;
      //access the iterator by its ID value in the PageDef file.
      dciter = bindingsImpl.findIteratorBinding("EmpVO1Iterator");
      long estRowCount = dciter.getEstimatedRowCount();
    
      if(estRowCount> 10){

        FacesContext fctx = FacesContext.getCurrentInstance();
        FacesMessage message = new FacesMessage("For the current selection criteria, the estimated row count is " + estRowCount + ". We recommend to export data to excel for row count greater that 10.");
        fctx.addMessage(null, message);

      }
    }
}

For this method to work, you need an iterator binding to EmpVO, that we are rendering in the EmployeesView view activity.

For that, create the page definition of the method-call activity, and add an iterator binding of EmpVO in the executable section, as shown below:



This is the important step. Rest all is simple.

Next, the EmployeesView.jsff is a simple page fragment having EmpVO rendered as a table.
After completing the task flow, drop the task flow as a region to BasePage.jspx and run it.
You will get an output like show in first figure.
That's all.



Tuesday, December 6, 2011

DVT Graphs Event Handlers

While writing event handling code for graphs, we need to take care of the following hierarchy:



That's because different graphs click events return different type of ComponentHandle references.
For example to handle Pie Graph's click event, we need to write the code similar to the following code snippet:

    public void pieClick(ClickEvent event) {
        // Add event code here...
        ComponentHandle handle = event.getComponentHandle();
        String seriesName = null;
        String seriesValue = null;
        Long clickedValue = null;
       
        if (handle instanceof DataComponentHandle){
                DataComponentHandle dhandle = (DataComponentHandle)handle;
                clickedValue = (Long)dhandle.getValue(DataComponentHandle.UNFORMATTED_VALUE);
                // Get the series attributes
                Attributes [] seriesInfo = dhandle.getSeriesAttributes();
                if(seriesInfo != null)
                {
                    for(Attributes attrs: seriesInfo)
                    {
                        seriesName = attrs.getValue(Attributes.LABEL_ATTRIBUTE).toString();
                        if(seriesName.equals("Status"))
                            seriesValue = attrs.getValue(Attributes.LABEL_VALUE).toString();   
                       
                    }
                }
        }
    . . .
    }   

Whereas while handling Line Graph's click event, the event handler looks like :

    public void graphClicked(ClickEvent clickEvent) {
          // Add event code here...
          ComponentHandle handle = clickEvent.getComponentHandle();
          if (handle instanceof SeriesComponentHandle) {
                  // Get the series attributes
                  Attributes [] seriesInfo = ((SeriesComponentHandle)handle).getSeriesAttributes();
                  String data = "";
                  if(seriesInfo != null) {
                          for(Attributes attrs: seriesInfo) {
                                  data += "Series value: " + attrs.getValue(Attributes.LABEL_VALUE);
                                  data += " Series name: " + attrs.getValue(Attributes.LABEL_ATTRIBUTE);
                                  data += " Series value id: " + attrs.getValue(Attributes.ID_VALUE);
                                  data += " Series name id: " + attrs.getValue(Attributes.ID_ATTRIBUTE);
                          }
                  }
          }
      . . .
    }

For Pie Graph, we look for DataComponentHandle to fetch desired attribute values, whereas for Line Graph, we look for SeriesComponentHandle.

Event handlers for other graphs are listed in the above image.

Wednesday, November 30, 2011

Viewing ADF Log Messages on Standalone/Production Servers

If you have used the ADF Logging capabilities ( eg. as mentioned in the blog of Duncan Mills: http://blogs.oracle.com/groundside/entry/adventures_in_adf_logging_part ), then you will figure out that its very easy to test and use in JDeveloper IDE, but most guides does not tell anything about how to enable/disable them on standalone/production WLS. I pasted the same question on forum post and thanks to Timo who pointed that for doing the same on standalone/production WLS, we need to install Enterprise Manager as well. Here is that forum post:

https://forums.oracle.com/forums/message.jspa?messageID=10009360

If the EM is installed, then after selecting the managed server, from the WebLogic Server menu select Log Configuration, and change the logging level for your AM, as shown below:



After that, the log messages will be visible in the diagnostic log files.

Monday, November 21, 2011

Working With The Switcher Component.

This blog shows how we can use Switcher component. Switcher is used for conditional layout changes. For example, when used within a table, using switcher you can render different component for each row based on some values present in the rows. In this sample, I am using SCOTT.EMP table to render different images based on different 'Deptno' values for each emp row. The logic to use switcher is simple. A switcher contains with in it (one or many, based on the requirement) facets, and a switcher has a property called facetName which should point to the facet we want to render.

Here are the screen shots to explain this:

Fig 1:



The above figure shows that we want to render the facet whose name matches the value of Deptno for the current row.

Fig 2:



Since I know the Deptno has values like 10, 20, or 30, I have named the facet accordingly:

Fig 3:




Fig 4:



Fig 5:



The above 3 figure shows what we want to render in each facet.

Fig 6:




The above figure shows how the EmpView page looks like after adding the switcher.

Fig 7:


The above figure shows the output when the EmpView page is run.

Tuesday, October 25, 2011

Formatting Numbers On DVT Pie.

Using numberFormatString property of dvt:pieGraph->dvt:sliceLabel->dvt:numberFormat tag, you can change the format of the number rendering on Pie graph from :


to


Here, I set the numberFormatSting to FM999,999,999,999,999 to get the desired output, as shown below:



Since numberFormat tag is deprecated now, the same view can be achieved by using af:converNumber tag instead of dvt:numberFormat tag, and by setting scaling to none, and AutoPrecision to off on dvt:sliceLabel tag.

Thursday, October 13, 2011

Case-Insensitive Filtering for ADF Tables.

The FilterFeatures property of af:column component can have 2 values: "caseSensitive" or "caseInsensitive". This property is used to configure if a filter needs to be case-sensitive or case-insensitive. It was not working earlier but now it works fine.

Refer to the following forum post for details:

https://forums.oracle.com/forums/thread.jspa?messageID=3697884&#3697884

Monday, October 10, 2011

Glasspane Rendering JavaScript's Issue on IE 7.

If you refer to following link:

http://www.oracle.com/technetwork/developer-tools/jdev/1-2011-javascript-302460.pdf

You will find that the JavaScript listed to render glasspane is as follows:

<af:resource type="javascript">
function enforcePreventUserInput(evt){
        var popup = AdfPage.PAGE.findComponentByAbsoluteId('p1');
        if (popup != null){
                AdfPage.PAGE.addBusyStateListener(popup,handleBusyState);
                evt.preventUserInput();
        }
}
//JavaScript callback handler
function handleBusyState(evt){
        var popup = AdfPage.PAGE.findComponentByAbsoluteId('p1');
        if(popup!=null){
                if (evt.isBusy()){
                        popup.show();
                }
                else{
                        popup.hide();
                        //remove busy state listener (important !!!)
                        AdfPage.PAGE.removeBusyStateListener(popup, handleBusyState);
                }
        }
}
</af:resource>

This JavaScript does not work well when called from a column of a table (for example, if you are calling a PL/SQL function/procedure) on IE 7. If you first select a row in a table, and then click the link to call a PL/SQL function/procedure to render glasspane, then it works fine but if you straight away calls the PL/SQL function/procedure the glasspane does not render at all.

However, after working with Oracle Support, figured out that the fix of this problem is pretty simple. We need to modify function handleBusyState() as follows:

function handleBusyState(evt) {
        var popup = AdfPage.PAGE.findComponentByAbsoluteId('p1');
        if (popup != null) {
                if (evt.isBusy()) {
                        popup.show();
                }
                else if (popup.isPopupVisible()) {
                        popup.hide();
                        AdfPage.PAGE.removeBusyStateListener(popup, handleBusyState);
                }
        }
}

The reason for this is explained in the SR as follows:

"
In this usecase on the action event of a command link you have a client listener that registers a busy state listener. The callback for the busy state listener shows the popup when the busy state is true or hides the popup and unregisters the busy state listener. Registered busy state listeners are invoked before and after every PPR regardless of the events sent to the server. In this scenario, when you register the busy state listener from the action handler, there is already a table selection event in flight. The first invocation of this listener reflects a false busy state. This is correct as it marks the completion of the table row selection event. The logic in the busy state callback hides and unregisters the busy state handler before the action event is even sent to the server. This is why the popup is never shown. The key here is that the busy state listener is not specifically targeted at a component. It reflects any component event sent to the server. The first parameter for the "addBusyStateListener" and "removeBusyStateListener" is just the context that the callback should be invoked on. A simple fix to this issue is to just check to see if the popup is visible before hiding and unregistering the listener.
"

Informed Frank about this fix to update the code corner PDF, but other PDFs are not in synch.
So, pasting this in here for future reference.

For details, refer:

Forum Post: https://forums.oracle.com/forums/thread.jspa?threadID=2246139&start=0&tstart=0
Service Request: SR 3-3985745581: Glasspane rendering in ADF
Bug 12834664: SHOW GLASSPANE WITH JAVASCRIPT CODE DOESN'T WORK FOR IE

 

Friday, September 30, 2011

Fixing: oracle.jbo.RowInconsistentException: JBO-25014: Another user has changed the row with primary key oracle.jbo.Key

To fix this issue, we need to make sure that:

  • Change indicator and history columns are mapped correctly. Change indicator column can be a version number column or last update date column.
  • All those columns that are getting updated by a database trigger or a PL/SQL call, are enabled for 'Refersh On Insert' (for insert case ) or 'Refresh On Update' (for update case) or both.
Remember that LastUpdateDate column can also work and Change Indicator.


References:
https://forums.oracle.com/forums/thread.jspa?threadID=214411
http://radio-weblogs.com/0118231/stories/2004/03/24/whyDoIGetOraclejborowinconsistentexception.html

Wednesday, September 28, 2011

ADF Library Feature & Security

JDev Version 11.1.2.

As we can break big application into small work-spaces and projects, I was wondering how we can implement security in that case. For example, consider and project, say Producer, which has all the task flows required by project, say Consumer. If Consumer project has enabled ADF Security, and the requirement is to make sure that a column of a table rendered in the Producer's task flow should be read only based on the roles defined in the jazn of Consumer project. In this case, how should we implement security.
This blog is about that.

For this, I am using the same project as I used for Effective Dates entities as Producer (to save time), as shown below:



The components relevant for this blog are highlighted above.
The FirstTaskFlow has only one view activity, as shown below:


And that view activity contains just one table created from DatedVO->DatedEO, as shown below:


I created 'ADF Library' deployment profile for View Controller project and deployed the project so that consumer applications can create a File System connection to use the task flow available in the ViewController project. Since the ViewController project is dependent on Model project as well, so the deployed jar automatically contains the BC4J components as well.

Then created a separate workspace, JarConsumerApp, as consumer application which will use the FirstTaskFlow of the producer application. Created a new File System Connection to point to the location of ViewController jar. Then later on added that ViewController jar to ConsumerViewController project as shown below:



As soon as we add the library, the AM entry gets visible in the Data Control section, and in the Project Properties->Libraries and Classpath section, the jar's entry gets added in ADF Libray section, as shown below:




Now, to add FirstTaskFlow to First.jspx, you first need to see that task flow in the Application Navigator. For this, select 'Show Libraries' as shown below:


Then, expand ADF Library->WEB-INF and drop the task flow to First.jspx page as region, as shown below:


After running, the First.jspx looks like as shown below:



It has only 1 row, and that is editable.
Now, I want to try two things:

1. The SysEffectiveDate column should be read-only on the UI for Non-Admin users.

For this, we need to enable ADF Security and do some setup on Consumer project only, ie. JarConsumerApp. After running the ADF Security wizard, I did the following:
  1. Added two new roles: Admin & NonAdmin.
  2. Created to new users: Admin & NonAdmin. Assigned Admin user to Admin role. Similarly assigned NonAdmin user to NonAdmin role, as shown below:



Then, in the Resource Grants section, select 'Resource Type' as Task Flow, and check the checkbox for 'Show task flows imported from ADF libraries' option.
Now, assign the view action on FirstTaskFlow to both Admin and NonAdmin roles, as shown below:




Similarly, do the same for First.jspx page as well, as shown below:




Now, to make SysEffectiveDate read-only, open FirstView.jspx of Producer project, and modify the input text field as shown below:




Now, redeploy the project, and run First.jspx again, this time logging in as NonAdmin user. The following screen appears:



As you can see, the SysEffectiveDate screen appears read-only, which means that security context of the producer project was able to figure out the roles defined in the consumer project.
Try logging in as Admin user, and then the following screen appears:



So, the EL expression is working fine.


2. Only Admin should be able to read DatedEO.

For this, we need to define at entity level that read operation is secured, as shown below:



Then, we need to modify jazn to tell which role can read DatedEO data, in our case its Admin, as shown below:



Now, run First.jspx again using NonAdmin user, the following screen comes up:



However, if logged in as Admin user, the following screen comes up:




As only Admin role is configured to perform 'read' action, on then data is displayed else not.
That's it.

Sunday, September 18, 2011

Understanding Effective Dated Entities Behavior - Difference Between Dated and Effective Dated Entity Types

JDev Version: 11.1.2.

The Dev Guide says that:  "The primary difference between the effective dated entity type and the dated entity type is that the dated entity does not cause row splits during update and delete."

It says primary difference, but does not explain exactly what other differences are there.
Posted the question of forum post as well, but nothing is available:

https://forums.oracle.com/forums/message.jspa?messageID=9860255#9860255

I tried to compare the behavior of Dated Entities with Effective Dated Entities, and this is what I found out:
(For the setup, and project details, please refer to the earlier blog: 'Understanding Effective Dated Entities Behavior - Creation/Update/Delete').

For Create Operation

When I created a row for DatedVO (Dated Entity) with EffectiveDate as 2001-09-09, after commit, the following data was visible in the database:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First09-SEP-011Y

EndDate is not populated, but StartDate is populated with the Effective Date.
However, other attributes are populated like Sequence and Sequence Flag. Not sure why the behavior is like that.

When I created a row for MultiChangesSingleDayVO (Effective Dated Entity) with EffectiveDate as 2001-09-09, after commit, the following data was visible in the database:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First09-SEP-0131-DEC-121Y

All the Effected Dated attributes are populated.
So this is one difference.

For Update Operation

As discussed in the earlier blogs, there are multiple flags available in the oracle.jbo.Row interface for row update behavior. Let's compare the behavior of Dated Entity with Effective Dated entity for the following flag:

Row.EFFDT_UPDATE_MODE: When an effective dated row is updated in "update" mode, the modified row is end dated on the effective date and a new row is created with the changed values.

Used the following method to update the DatedVO row:

    public void updateDatedVODatedVORow( String text, Date startDate, Date effectiveDate, int mode){
       
        if (effectiveDate != null) {
            ApplicationModuleImpl rootAM = this.getRootApplicationModule();
            rootAM.setProperty(EFF_DT_PROPERTY_STR, effectiveDate);
        }
                ViewObjectImpl vo = this.getDatedVO1();
        Row row = null;
        row = (vo.findByKey(new Key(new Object[]{ 1, null , null, startDate}), 1))[0];
        if(mode==1)
                row.setEffectiveDateMode(Row.EFFDT_UPDATE_MODE     );
        else if(mode==2)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_CHANGE_INSERT_MODE       );
        else if(mode==3)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_CORRECTION        );
        else if(mode==4)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_NEW_EARLIEST_CHANGE_MODE        );       
        else if(mode==5){
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_OVERRIDE_MODE         ); 
        }
        else if(mode==6)
            row.setEffectiveDateMode(Row.EFFDT_NONE_MODE         ); 
        
        row.setAttribute("Text", text);
    
        this.getDBTransaction().commit();
    }

For DatedVO, consider the following row in the table:
Before Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-111Y

Parameter Passed:

text = MODE_1
effectiveDate = 2012-01-01(YYYY-MM-DD)
mode = 1

After Update:


IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1MODE_101-JAN-111Y

As we can see, only text attribute is updated, everything else is unchanged.
So, seems like after row creation, framework logic does not update the effective dated attributes for Dated entities.
In the effective dated entities, this operation causes a new row to be inserted.

So, only this difference is documented in the Dev Guide, but other small differences are documented. Not sure why.

Understanding Effective Dated Entities Behavior - Delete

JDev Version: 11.1.2.

In this blog, I have documented the results of the tests I did for Effective Dated Entities delete behavior. For the setup, and project details, please refer to the earlier blog: 'Understanding Effective Dated Entities Behavior - Creation/Update'.

The following modes in the oracle.jbo.Row interface are available to delete Effective Dated rows:

  • Row.EFFDT_DELETE_MODE: When an effective dated row is deleted in "delete" mode, the end date of the row is set to the row's effective date and all the future rows for the same key values are deleted.
  • Row.EFFDT_DELETE_FUTURE_CHANGE_MODE: When an effective dated row is deleted in "delete future change" mode, the end date of the row is set to the end of time and all the future rows for the same key values are deleted.
  • Row.EFFDT_DELETE_NEXT_CHANGE_MODE: When an effective dated row is deleted in "delete next change" mode, the end date of the row is set to the end date of adjoining row and the adjoining row is deleted.
  • Row.EFFDT_DELETE_ZAP_MODE: When an effective dated row is deleted in "zap" mode, all the effective dated rows with the same key values are deleted.
  • Row.EFFDT_DELETE_THIS_CHANGE_MODE: When an effective dated row is deleted in "delete this change" mode, the current row is removed.
I am using the following method to delete the rows of MultiChangesSingleDayVO:

    public void deleteMultiChangesSingleDayEffDatedVORow( Date effectiveDate, int mode){
       
        if (effectiveDate != null) {
            ApplicationModuleImpl rootAM = this.getRootApplicationModule();
            rootAM.setProperty(EFF_DT_PROPERTY_STR, effectiveDate);
        }

        ViewObjectImpl vo = this.getMultiChangesSingeDayVO1();
        Row row = null;
        row = (vo.findByKey(new Key(new Object[]{ 1, null, null, "Y"}), 1))[0];
        if(mode==1)
            row.seteffectiveDateMode(Row.EFFDT_DELETE_MODE      );
        else if(mode==2)
            row.seteffectiveDateMode(Row.EFFDT_DELETE_FUTURE_CHANGE_MODE        );
        else if(mode==3)
            row.seteffectiveDateMode(Row.EFFDT_DELETE_NEXT_CHANGE_MODE        );
        else if(mode==4)
            row.seteffectiveDateMode(Row.EFFDT_DELETE_ZAP_MODE          );   
        else if(mode==5)
            row.seteffectiveDateMode(Row.EFFDT_DELETE_THIS_CHANGE_MODE          );          

    row.remove();

        this.getDBTransaction().commit();
    }

As you can see, I am using the following mode value to identify which mode to use:

1 = Row.EFFDT_DELETE_MODE
2 = Row.EFFDT_DELETE_FUTURE_CHANGE_MODE
3 = Row.EFFDT_DELETE_NEXT_CHANGE_MODE 
4 = Row.EFFDT_DELETE_ZAP_MODE         
5 = Row.EFFDT_DELETE_THIS_CHANGE_MODE 

Testing Effective Dated Table Delete:

Documenting some of results I got when I tested the Effective Dated entities delete using all the above flags:

Here it the initial table content used for all the delete test cases:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1Second01-JAN-0531-DEC-081Y
1Third01-JAN-0931-DEC-121Y

Test Cases on the above data:


1.


Parameter Passed:
       
effectiveDate = 2006-01-01       
mode = 1       

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1Second01-JAN-0501-JAN-061Y

Recalling the definition of Row.EFFDT_DELETE_MODE: When an effective dated row is deleted in "delete" mode, the end date of the row is set to the row's effective date and all the future rows for the same key values are deleted.
Perfect.

2.

Parameter Passed:
       
effectiveDate = 2006-01-01       
mode = 2       


Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1Second01-JAN-0531-DEC-121Y

Recalling the definition of Row.EFFDT_DELETE_FUTURE_CHANGE_MODE: When an effective dated row is deleted in "delete future change" mode, the end date of the row is set to the end of time and all the future rows for the same key values are deleted.
Perfect.

3.

Parameter Passed:   
       
effectiveDate = 2002-01-01       
mode = 3       

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-081Y
1Third01-JAN-0931-DEC-121Y

Recalling the definition of Row.EFFDT_DELETE_NEXT_CHANGE_MODE: When an effective dated row is deleted in "delete next change" mode, the end date of the row is set to the end date of adjoining row and the adjoining row is deleted.
Perfect.

4.

Parameter Passed:
       
effectiveDate = 2006-01-01
mode = 4

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG

No records.

Recalling the definition of Row.EFFDT_DELETE_ZAP_MODE: When an effective dated row is deleted in "zap" mode, all the effective dated rows with the same key values are deleted.
Perfect.

5.

Parameter Passed:
       
effectiveDate = 2006-01-01
mode = 5

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-081Y
1Third01-JAN-0931-DEC-121Y

Recalling the definition of Row.EFFDT_DELETE_THIS_CHANGE_MODE: When an effective dated row is deleted in "delete this change" mode, the current row is removed.
Perfect again.

So, I found understanding delete test cases easier than understanding the update test cases, could be because I found the documentation of delete test cases clearer than update scenarios.

Saturday, September 17, 2011

Understanding Effective Dated Entities Behavior - Update

JDev Version: 11.1.2.

In this blog, I have documented the results of the tests I did for Effective Dated Entities update behavior. For the setup, and project details, please refer to the earlier blog: 'Understanding Effective Dated Entities Behavior - Creation'.

The following modes in the oracle.jbo.Row interface are available to update Effective Dated rows:

  • Row.EFFDT_UPDATE_MODE: When an effective dated row is updated in "update" mode, the modified row is end dated on the effective date and a new row is created with the changed values.
  • Row.EFFDT_UPDATE_CHANGE_INSERT_MODE: When an effective dated row is updated in "change insert" mode, the modified row is end dated on the effective date and a new row is inserted that fits between the effective date and the start date of the next row in the effective date time line.
  • Row.EFFDT_UPDATE_CORRECTION: When an effective dated row is updated in "correction" mode, the effective start date and effective end date is left unchanged.
  • Row.EFFDT_UPDATE_NEW_EARLIEST_CHANGE_MODE: Updating in "new earliest change" mode is supported only in Multiple Changes Per Day.
  • Row.EFFDT_UPDATE_OVERRIDE_MODE: When an effective dated row is updated in "override" mode, the modified row is end dated on the effective date and the start date of the next row in the effective date time line is set to effective date + 1 day.   
  • Row.EFFDT_NONE_MODE: Default state of the effective date mode on the row.

I am using the following method to update the rows of MultiChangesSingleDayVO:

    public void updateMultiChangesSingleDayEffDatedVORow( String text, Date startDate, Date endDate, Date effectiveDate, int mode){
      
        if (effectiveDate != null) {
            ApplicationModuleImpl rootAM = this.getRootApplicationModule();
            rootAM.setProperty(EFF_DT_PROPERTY_STR, effectiveDate);
        }


        ViewObjectImpl vo = this.getMultiChangesSingeDayVO1();
        Row row = null;
        row = (vo.findByKey(new Key(new Object[]{ 1, null, null, "Y"}), 1))[0];
        if(mode==1)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_MODE     );
        else if(mode==2)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_CHANGE_INSERT_MODE       );
        else if(mode==3)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_CORRECTION        );
        else if(mode==4)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_NEW_EARLIEST_CHANGE_MODE        );      
        else if(mode==5){
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_OVERRIDE_MODE         );
        }
        else if(mode==6)
            row.setEffectiveDateMode(Row.EFFDT_NONE_MODE         );
       
        row.setAttribute("Text", text);
        if(startDate!=null)
            row.setAttribute("StartDate", startDate);
        if(endDate!=null)
            row.setAttribute("EndDate", endDate);
   
        this.getDBTransaction().commit();
    }

As you can see, I am using the following mode value to identify which mode to use:

  
1 = Row.EFFDT_UPDATE_MODE
 
2 = Row.EFFDT_UPDATE_CHANGE_INSERT_MODE
 
3 = Row.EFFDT_UPDATE_CORRECTION
 
4 = Row.EFFDT_UPDATE_NEW_EARLIEST_CHANGE_MODE
 
5 = Row.EFFDT_UPDATE_OVERRIDE_MODE
 
6 = Row.EFFDT_NONE_MODE

Testing Effective Dated Table Update:

Documenting some of results I got when I tested the Effective Dated entities update using all the above flags:

Started experimenting by updates on single row. Here is the data used for all the below cases:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First11-SEP-1131-DEC-121Y

Test Cases on the above data:

1.

Parameter Passed:

text = MODE_1
effectiveDate = 2011-09-11(yyyy-mm-dd)
mode =1

Other parameters are null. These parameters are passed as shown below:



After giving the above specified input to updateMultiChangesSingleDayEffDatedVORow method, got the follout output and table contents:

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1MODE_111-SEP-1131-DEC-122Y
1First11-SEP-1111-SEP-111N

We can see that when EFFDT_UPDATE_MODE mode is used, the single row is split into two rows, where the initial row is end dated on the effective date passed as parameter.

Recalling the definition of Row.EFFDT_UPDATE_MODE: When an effective dated row is updated in "update" mode, the modified row is end dated on the effective date and a new row is created with the changed values. This is exactly what has happened.

In addition, in the JDev log, I saw that the following query is fired for the findByKey operation:

SELECT MultiChangesSingleDayEO.ID,         MultiChangesSingleDayEO.TEXT,         MultiChangesSingleDayEO.START_DATE,         MultiChangesSingleDayEO.END_DATE,         MultiChangesSingleDayEO.SEQUENCE,         MultiChangesSingleDayEO.SEQUENCE_FLAG
FROM SCOTT.MULTI_CHANGES_SINGLE_DAY_TABLE MultiChangesSingleDayEO
WHERE (MultiChangesSingleDayEO.ID = :fbkKy__0 AND MultiChangesSingleDayEO.SEQUENCE_FLAG = :fbkKy__3)
AND (:SysEffectiveDateBindVar BETWEEN MultiChangesSingleDayEO.START_DATE AND MultiChangesSingleDayEO.END_DATE) AND (MultiChangesSingleDayEO.SEQUENCE_FLAG = 'Y')


2.

Parameter Passed:

text = MODE_2
effectiveDate = 2011-09-11(yyyy-mm-dd)
mode = 2

Output:
(oracle.jbo.JboException) JBO-27132: Current operation cannot be performed in UPDATE_CHANGE_INSERT mode because no future rows exist. key=oracle.jbo.Key[1 2011-09-11 1 Y ]

Recalling the definition of Row.EFFDT_UPDATE_CHANGE_INSERT_MODE: When an effective dated row is updated in "change insert" mode, the modified row is end dated on the effective date and a new row is inserted that fits between the effective date and the start date of the next row in the effective date time line.

As there is no next row, this operation returns an error. Table contents are unchanged because of this.

3.

Parameter Passed:

text =MODE_3
effectiveDate =2011-09-11(yyyy-mm-dd)
mode =3

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1MODE_311-SEP-1131-DEC-121Y

Recalling the definition of Row.EFFDT_UPDATE_CORRECTION: When an effective dated row is updated in "correction" mode, the effective start date and effective end date is left unchanged.

So, none of the effective dated attibutes are touched in this mode.

4.

Parameter Passed:

text = MODE_4
effectiveDate = 2011-09-11(yyyy-mm-dd)
mode = 4

Output:
(oracle.jbo.JboException) JBO-27133: Current operation cannot be performed in UPDATE_CHANGE_INSERT mode because no previous rows exist. key=oracle.jbo.Key[1 2011-09-11 1 Y ]

Recalling the definition of Row.EFFDT_UPDATE_NEW_EARLIEST_CHANGE_MODE: Updating in "new earliest change" mode is supported only in Multiple Changes Per Day.

This definition is somewhat not stating what it does, but it actually captures the scenarios somewhat opposite of Row.EFFDT_UPDATE_CHANGE_INSERT_MODE.

5.

Parameter Passed:

text = MODE_5
effectiveDate = 2011-09-11(yyyy-mm-dd)
mode = 5

Output:
(oracle.jbo.JboException) JBO-27130: Current mode UPDATE_OVERRIDE is not supported in Multiple Changes Per Day.

Recalling the definition of Row.EFFDT_UPDATE_OVERRIDE_MODE: When an effective dated row is updated in "override" mode, the modified row is end dated on the effective date and the start date of the next row in the effective date time line is set to effective date + 1 day. 

Discussed later in the blog.

6.

Parameter Passed:

text = MODE_6
effectiveDate = 2011-09-11(yyyy-mm-dd)
mode = 6

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1MODE_611-SEP-1131-DEC-121Y

Recalling the definition of Row.EFFDT_NONE_MODE: Default state of the effective date mode on the row.
Just the Text attribute passed as parameter is updated.

So out of 6 test cases, 3 failed. Let's move to testing the Effective Dated feature with multiple records.

Now consider the following records used for all the below cases:

Table Content:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1Second01-JAN-0531-DEC-071Y
1Third01-JAN-0831-DEC-121Y

These records are created by first inserting a record using effective date 01-JAN-2001, and then breaking it using Row.EFFDT_UPDATE_MODE using 2 separate effective dates: 01-JAN-2005, and 01-JAN-2008. So this covers our updates for multiple dates scenario as well.

Now we will try to modify the second record for all the different flags:

Test Cases:

1.

Parameter Passed:

text = MODE_1
effectiveDate = 2005-01-01(yyyy-mm-dd)
mode = 1


Output:
(oracle.jbo.JboException) JBO-27127: The effective date operation for Entity MultiChangesSingleDayEO will introduce gaps or overlaps. key=oracle.jbo.Key[1 2005-01-01 1 Y ]

Recalling the definition of Row.EFFDT_UPDATE_MODE: When an effective dated row is updated in "update" mode, the modified row is end dated on the effective date and a new row is created with the changed values.

As this mode inserts a new row with changed values, I guess this mode only works with last (corner) row.
Basically, it can't insert a row in between when already a future row exists.


2.

Parameter Passed:

text = MODE_2
effectivedate = 2005-01-01(yyyy-mm-dd)
mode = 2

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1Second01-JAN-0501-JAN-051N
1Third01-JAN-0831-DEC-121Y
1MODE_201-JAN-0531-DEC-072Y


Recalling the definition of Row.EFFDT_UPDATE_CHANGE_INSERT_MODE: When an effective dated row is updated in "change insert" mode, the modified row is end dated on the effective date and a new row is inserted that fits between the effective date and the start date of the next row in the effective date time line.

Perfect.

3.

Parameter Passed:

text = MODE_3
effectiveDate = 2005-01-01(yyyy-mm-dd)
mode = 3

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1MODE_301-JAN-0531-DEC-071Y
1Third01-JAN-0831-DEC-121Y

Recalling the definition of Row.EFFDT_UPDATE_CORRECTION: When an effective dated row is updated in "correction" mode, the effective start date and effective end date is left unchanged.

So none of the effective dated attibutes are touched in this mode.

4.

Parameter Passed:

text = MODE_4
effectiveDate = 2005-01-01(yyyy-mm-dd)
mode = 4

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1second01-JAN-0531-DEC-072Y
1Third01-JAN-0831-DEC-121Y
1MODE_401-JAN-0501-JAN-051N

Recalling the definition of Row.EFFDT_UPDATE_NEW_EARLIEST_CHANGE_MODE: Updating in "new earliest change" mode is supported only in Multiple Changes Per Day.

This definition is somewhat not stating what it does, but it actually captures the scenarios somewhat opposite of Row.EFFDT_UPDATE_CHANGE_INSERT_MODE.


5.

Parameter Passed:

text = MODE_5
effectiveDate = 2005-01-01(yyyy-mm-dd)
mode = 5

Output:
(oracle.jbo.JboException) JBO-27130: Current mode UPDATE_OVERRIDE is not supported in Multiple Changes Per Day.

Recalling the definition of Row.EFFDT_UPDATE_OVERRIDE_MODE: When an effective dated row is updated in "override" mode, the modified row is end dated on the effective date and the start date of the next row in the effective date time line is set to effective date + 1 day. 

Discussed later in the blog.

6.

Parameter Passed:

text =MODE_6
effectiveDate =2005-01-01(yyyy-mm-dd)
mode = 6

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATESEQUENCESEQUENCE_FLAG
1First01-JAN-0131-DEC-041Y
1MODE_601-JAN-0531-DEC-071Y
1Third01-JAN-0831-DEC-121Y

Recalling the definition of Row.EFFDT_NONE_MODE: Default state of the effective date mode on the row.
Just the Text attribute passed as parameter is updated.

Apart from Row.EFFDT_UPDATE_OVERRIDE_MODE, all other modes are clear till here. For Row.EFFDT_UPDATE_OVERRIDE_MODE mode, posted this on the forum post:

https://forums.oracle.com/forums/thread.jspa?threadID=2281030&tstart=0

And John Stegemen pointed out that it works only for those entities where multiple changes on a single day is not allowed.

Lets try this on the entity (SingleChangeSingleDayVO) not configured for multiple changes in a single day. Using the following method to update SingleChangeSingleDayVO row:

    public void updateSingleChangeSingleDayEffDatedVORow( String text, Date startDate, Date effectiveDate, int mode){
      
        if (effectiveDate != null) {
            ApplicationModuleImpl rootAM = this.getRootApplicationModule();
            rootAM.setProperty(EFF_DT_PROPERTY_STR, effectiveDate);
        }
      
      
        ViewObjectImpl vo = this.getSingleChangeSingleDayVO1();
        Row row = null;
        row = (vo.findByKey(new Key(new Object[]{ 1, startDate, null }), 1))[0];
        if(mode==1)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_MODE     );
        else if(mode==2)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_CHANGE_INSERT_MODE       );
        else if(mode==3)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_CORRECTION        );
        else if(mode==4)
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_NEW_EARLIEST_CHANGE_MODE        );      
        else if(mode==5){
            row.setEffectiveDateMode(Row.EFFDT_UPDATE_OVERRIDE_MODE         );
        }
        else if(mode==6)
            row.setEffectiveDateMode(Row.EFFDT_NONE_MODE         );
       
        row.setAttribute("Text", text);
   
        this.getDBTransaction().commit();
    }

Initial Table Content:

IDTEXTSTART_DATEEND_DATE
1First01-JAN-1131-DEC-12

Test Cases:

1.

Parameter Passed:

text = MODE_5
effectiveDate = 2011-01-01(yyyy-mm-dd)
startDate = 2011-01-01(yyyy-mm-dd)
mode =5


Output:
(oracle.jbo.JboException) JBO-27128: The effective date update operation for Entity SingleChangeSingleDayEO failed because an existing row has the effective start date that matches the effective date. key=oracle.jbo.Key[1 2011-01-01 4712-12-31 ]

So, it fails again when StartDate is equal to EffectiveDate. This same test case works with Row.EFFDT_UPDATE_MODE so dont care if it fails with Row.EFFDT_UPDATE_OVERRIDE_MODE.

Now, lets try with effective date later than start date.


2.

Parameter Passed:

text = MODE_5
effectiveDate = 2012-01-01(yyyy-mm-dd)
startDate = 2011-01-01(yyyy-mm-dd)
mode = 5

Output: Success

Table Content After Method Call:

IDTEXTSTART_DATEEND_DATE
1MODE_501-JAN-1231-DEC-12
1First01-JAN-1131-DEC-11

So, MODE_5 works only for entities not configured for multiple changes in a single day.



Understanding Effective Dated Entities Behavior - Creation

JDev Version 11.1.2.

Effective dated entities are used to implement the functionality required to view data related to a specific point in time. Within Effective dated entities, there are two variations: One that allows multiple changes in a single day, and the other one which does not. To make an entity object effective dated, you need to mark the entity object as Effective Dated, as shown below:


In addition, you need to add 4 extra attributes to allow the framework to implement the effective dated functionality. Those attributes are:

  • StartDate attribute: An attribute with Date data type, which StartDate property selected.
  • EndDate attribute: An attribute with Date data type, which EndDate property selected.
  • Sequence attribute: An attribute with Sequence property selected. I have used String data type for this attribute.
  • SequenceFlag attribute: An attribute with Sequence Flag property selected. I have used String data type for this attribute.

The Sequence and SequenceFlag attribute are required for Effective Dated entities only if you want to enable multiple changes in a single day. Otherwise, only StartDate and EndDate attributes are required.

When you create an effective dated entity object, JDeveloper creates a transient attribute called SysEffectiveDate to store the effective date for the row. This attribute is used to find/match the records to update, as the following type of where clause is appended to the query at runtime:

:SysEffectiveDateBindVar BETWEEN MultiChangesSingleDayEO.START_DATE AND MultiChangesSingleDayEO.END_DATE

At runtime, you can pass the value for the SysEffectiveDate attribute by setting the AM's property EFF_DT_PROPERTY_STR, as shown below:

            ApplicationModuleImpl rootAM = this.getRootApplicationModule();
            rootAM.setProperty(EFF_DT_PROPERTY_STR, effectiveDate);

At runtime, the bind value for the query is obtained from a property of the root application module.
The default value of effective date is current date. So if EFF_DT_PROPERTY_STR property of AM is not set, current date is used as effective date. The Effective Dated VOs are created using Effective Dated EOs.

I am using the following App for this blog:



To test all the scenarios, I have created the followings Effected Dated VOs:

MultiChangesSingleDayVO: To capture multiple changes in a single day.
SingleChangeSingleDayVO: To allow only single change in a day.
DatedVO: Its is a Dated VO, used in the blog 'Difference Between Dated and Effective Dated Entity Types'.


MultiChangesSingleDayVO and DatedVO are based on the following table:

CREATE TABLE MULTI_CHANGES_SINGLE_DAY_TABLE (
  id            NUMBER(4,0)  NOT NULL,
  text          VARCHAR2(10) NOT NULL,
  start_date    DATE         NOT NULL,
  end_date      DATE         NULL,
  "SEQUENCE"      VARCHAR2(10) NOT NULL,
  sequence_flag VARCHAR2(1)  NOT NULL
)
/

ALTER TABLE MULTI_CHANGES_SINGLE_DAY_TABLE
  ADD CONSTRAINT MCSDT_PK1 PRIMARY KEY (
    id,
    start_date,
    "SEQUENCE",
    sequence_flag
  )
/

SingleChangeSingleDayVO is based on the following table:

CREATE TABLE SINGLE_CHANGE_SINGLE_DAY_TABLE (
  id         NUMBER(4,0)  NOT NULL,
  text       VARCHAR2(10) NOT NULL,
  start_date DATE         NOT NULL,
  end_date   DATE         NOT NULL
)
  PCTUSED    0
/

ALTER TABLE SINGLE_CHANGE_SINGLE_DAY_TABLE
  ADD CONSTRAINT SCSDT_PK1 PRIMARY KEY (
    id,
    start_date,
    end_date
  )
/

I have created a composite Primary key for both the tables because without it, Effective Dates VOs throw TooManyObjects exceptions for row split operations. It's not documented so posted this question on the following forum post to confirm:

https://forums.oracle.com/forums/thread.jspa?threadID=2280616&tstart=0

Effective Dated Entities Creation Behavior

When we create a new row for an Effective Dated Entity, the framework automatically takes care for the default values of the four Effective Dated attributes: StartDate, EndDate, Sequence & SequenceFlag. Sequence & SequenceFlag attributes are used to track multiple changes in a single day. Sequence shows the update count, whereas SequenceFlag is set to Y for the recently updated record.
I think these two attribute should not be modified manually. However, for StartDate, and EndDate we can provide values while creation.
For example, see the following figure:



Here we have only specified the values for 3 attributes, ID, Text, & transient attribute SysEffectiveDate. On commit, other attributes are automatically populated, as shown below:



Similarly, you can provide values for StartDate and EndDate as well, as shown below:




On commit, remaining attributes are automatically populated, as shown below:


Monday, September 5, 2011

Understanding ADF Transactions Behavior

Important forums:

https://forums.oracle.com/forums/thread.jspa?threadID=2211040
https://forums.oracle.com/forums/thread.jspa?threadID=2270308

and excellent blogs to understand transaction behavior by Chris Muir:

http://one-size-doesnt-fit-all.blogspot.com/2011/05/jdev-11g-task-flows-adf-bc-one-root.html
http://one-size-doesnt-fit-all.blogspot.com/2011/05/jdev-11g-task-flows-adf-bc-always-use.html
http://one-size-doesnt-fit-all.blogspot.com/2011/07/task-flows-sayonara-automated-nesting.html
http://one-size-doesnt-fit-all.blogspot.com/2011/08/task-flows-sayonara-auto-am-nesting-in.html

Key Points:

1. The "No Controller Transaction" option enables the ADF BC layer manage its own transactions. The other task flow transaction options allow the developer to create and reuse transactions at a higher level of abstraction for relating web pages, page fragments and other task flow activities.

2. Shared data control scope we set in the task flows has no use when the No Controller Transaction option is used.

3. Steve said before 11.1.2: In a future release we will likely be changing that implementation detail so that the AMs are always used from their own AM pool, however they will share a transaction/connection. It's done in 11.1.2.

4. Connection has 1-to-1 relationship with a transaction.

5. No Controller Transaction: Perfect for scenarios where you are committing data on any/every page within a flow. If you are in a wizard/train the standard use case is to save all the data at the end or do not saving anything, then you can think of other tranaction options available on the task flow.

Saturday, September 3, 2011

Business Events Support In Case Of Composition Scenarios

This blog is in continuation of my previous blog:
http://adfblogs.blogspot.com/2011/08/adf-integration-with-soa-1-business.html

The following is what Dev Guide says about composition scenarios:

To support composition scenarios (such as a purchase order with line items), a child entity can raise events defined on the parent entity, and events defined on the child entity can include attributes from the parent entity. When a child entity raises an event on a parent entity, only a single event is raised for a particular top-level entity  per transaction, regardless of how many times the child entity raises it.

So, we can't have a single payload which contains all the data for child and parent entities.
However, from a transaction point of view, all the changes are handled in one transaction.
For details, refer to forum post:

https://forums.oracle.com/forums/message.jspa?messageID=9838631#9838631

Here is how I tested the composition scenario.

I have used SCOTT.EMP and SCOTT.DEPT tables where DEPT is the master table, and created the model project as shown below:





Defined and published a Department update event as shown below:




Defined a create event on Employee entity, and included Department attributes in the event definition, as shown below:




Here is how EmpEO looks like after the changes:




Then, created a new page, BusinessEventTest.jspx and dragged both Emp and Dept VOs to create tables, as shown below:




After running, the page looks like this:


Then, updated the location of first Department record, and created 2 new Employee records, as shown below:



After commit, ran the following query and figured out that business events are raised successfully:

SELECT * FROM AQ$EDN_EVENT_QUEUE_TABLE;

The above query returns 6 records, which means that 3 events are raised, 1 for Department update, and 2 other for each Employee record. To see the payload of each event without consuming the event in SOA composite, you can run the following query that returns 3 record (one for each event):

SELECT E.USER_DATA.EVENT.PAYLOAD FROM EDN_EVENT_QUEUE_TABLE E;

The above query is composed because of the following data model:

CREATE TABLE EDN_EVENT_QUEUE_TABLE (
  Q_NAME            VARCHAR2(30)   NULL,
  MSGID             RAW(16)        NOT NULL,
  CORRID            VARCHAR2(128)  NULL,
  PRIORITY          NUMBER         NULL,
  STATE             NUMBER         NULL,
  "DELAY"           TIMESTAMP(6)   NULL,
  EXPIRATION        NUMBER         NULL,
  TIME_MANAGER_INFO TIMESTAMP(6)   NULL,
  LOCAL_ORDER_NO    NUMBER         NULL,
  CHAIN_NO          NUMBER         NULL,
  CSCN              NUMBER         NULL,
  DSCN              NUMBER         NULL,
  ENQ_TIME          TIMESTAMP(6)   NULL,
  ENQ_UID           VARCHAR2(30)   NULL,
  ENQ_TID           VARCHAR2(30)   NULL,
  DEQ_TIME          TIMESTAMP(6)   NULL,
  DEQ_UID           VARCHAR2(30)   NULL,
  DEQ_TID           VARCHAR2(30)   NULL,
  RETRY_COUNT       NUMBER         NULL,
  EXCEPTION_QSCHEMA VARCHAR2(30)   NULL,
  EXCEPTION_QUEUE   VARCHAR2(30)   NULL,
  STEP_NO           NUMBER         NULL,
  RECIPIENT_KEY     NUMBER         NULL,
  DEQUEUE_MSGID     RAW(16)        NULL,
  SENDER_NAME       VARCHAR2(30)   NULL,
  SENDER_ADDRESS    VARCHAR2(1024) NULL,
  SENDER_PROTOCOL   NUMBER         NULL,
  USER_DATA         EDN_EVENT_DATA NULL,
  USER_PROP         ANYDATA        NULL
);

CREATE OR REPLACE TYPE EDN_EVENT_DATA AS OBJECT (
    EVENT EDN_BUSINESS_EVENT,
    PUBLISH_IMPL CHAR,
    SUBJECT_INFO VARCHAR2(256),
    TARGET VARCHAR2(1024)
);

CREATE OR REPLACE TYPE EDN_BUSINESS_EVENT AS OBJECT (
    NAMESPACE VARCHAR2(256),
    LOCAL_NAME VARCHAR2(80),
    PAYLOAD SYS.XMLTYPE,
    COMPRESSED_EVENT DECIMAL(10),
    DECOMP_METHOD VARCHAR2(64)
);

Here is how the Deparment payload looks like:




Here is how first Employee record's payload looks like:




Here is the payload of the second Employee record:




You can see that both the child record's payload contains the new value for the parent entity, as specified in the child entity event definition earlier.

That's all.

JDev Release 11.1.1.4