Showing posts with label Transaction. Show all posts
Showing posts with label Transaction. Show all posts

29 Oct 2017

Checking ADF BC Transaction Status

There is a very common use case when we need to programmatically check if the current transaction is dirty and notify a user about that. The most common approach is to get an instance of the current data control frame or a data control and check their isTransactionDirty() and isTransactionModified() methods.

For example, like this:

    private boolean isTransactionDirty() {
        BindingContext context = BindingContext.getCurrent();
        DataControlFrame dcFrame = context.dataControlFrame();               
        return dcFrame.isTransactionDirty();
    }

Or like this:

    private boolean isTransactionDirty() {
        BindingContext context = BindingContext.getCurrent();
        DCBindingContainer binding = (DCBindingContainer) context.getCurrentBindingsEntry();
        DCDataControl dc = binding.getDataControl();      

        return dc.isTransactionDirty();       

        //or       

        return dc.isTransactionModified();       

        //or       

        return dc.isTransactionDirty() || dc.isTransactionModified();       
    }

Note, that in the second example both isTransactionDirty() and isTransactionModified() methods are used. In the good old days, when people worked with 11g, the isTransactionDirty() method checked the underlying model if it was dirty (basically if ADF BC transaction was dirty). The isTransactionModified() has never done that, it's been always checking its internal flag only which is useful when it comes to a non-transactional data control (e.g. been data control). Having those both methods was nice as it gave some flexibility and you could use either of them (or both) depending on what you were actually checking.

Nowadays (12cisTransactionDirty() is combined with isTransactionModified(), so it checks the internal flag (which is set up whenever any data bound value is changed) and the underlying model transaction and returns true if either of them is true. Having said that, you are not able anymore to use isTransactionDirty() to check if ADF BC transaction is dirty.

Let's say there is a transient view object and you use it on your screen for some temporary values (e.g. implementing custom filtering or a form with input values for a business service method). Since those values are data bound (even though they have nothing to do with ADF BC entity cache) the framework will mark the internal data control flag as dirty whenever the values are changed. So, isTransactionDirty() method in 12c is going to return true. The user didn't do anything bad yet, and we are scaring them with the notification about dirty transaction.

The solution is to override the method in the data control. You can see how to tell the framework to use a custom data control here. So, in our custom data control we are going to override isTransactionDirty() method:

    //We consider transaction as dirty only if BC transaction is dirty
    //all manipulations with transient VOs/attributes should not matter
    @Override
    public boolean isTransactionDirty()  {
       ApplicationModule am = getApplicationModule();
       return (am != null
                  && am.getTransaction() != null
                  && am.getTransaction().isDirty());
    }


That's it!




30 Apr 2016

Application Modules and Entity Cache

Any ADF developer with some basic knowledge of ADF Business Components would be familiar with the following diagram:


It represents the core building blocks of ADF Business Components at run-time. There is an instance of a root application module containing view object instances. View object instances might be backed up by entity objects that are stored in entity collection or in other words entity cache. A root application module may also contain nested application modules which in their turn my contain their own view object instances. This is very important that all view object instances and nested application modules within a single root AM share the same entity cache. The question is How? 

The diagram above represents a very simple case. There is only one user session and it is assumed that there is only one root application module in the application. However, ADF BC suppose that each user session has its own entity cache. So, what links my application module to my and only my entity cache?
And here is where DB Transaction object comes to the scene:


It is an internal framework object that actually contains entity cache and provides it to all application modules registered with this DB Transaction object. Furthermore DB Transaction object contains a DB connection and it provides all jdbc-related services such as creating and executing callable statements. Many developers think that actually application module is responsible for containing entity cache, holding DB connection and interacting with database. That's not actually true. An application module is just attached to DB Transaction object consuming entity cache and DB connection from it. The word "attached" means that there could be many root application modules referring to the same DB Transaction object.


In that case the transaction is called "shared". Each application module attached to it consumes the same DB Connection and the same entity cache. There is a common myth that any instance of a root application module always requires a dedicated DB connection. Obviously, that's not always the case.

This feature is based on jbo.shared.txn AM property. It means that all root application modules with the same value of this property will share the same DB Transaction object and therefore the same DB connection and entity cache. The "shared transaction" feature is commonly used for shared application modules so that by setting the same string value for their jbo.shared.txn property we can force them to share the same DB connection.

That's it!