Why do you need programmatic entity and view Objects?
Consider a scenario where you may need to retrieve data from a custom data source (say using third party API for example) and may need to perform some data update later on. While realizing such use cases, you my still want to leverage the declarative development support provided by the JDeveloper for building list of values, entity caching and validation, declarative transaction support etc. Though ADF got binding support for Java services, its not as rich as ADF BC implementation. One possible option is to customize the business components (view object and entity object) to interact with third party data source by overriding its default life cycle methods. Before get in to the implementation, let me take a step back take you through life cycle of entity and view objects.
How does your view object work?
When you run a page with a table, during the 'render response' phase of the view, table may tries to get data by evaluating the EL associated with value attribute. At this stage the binding layer will identify the underlying view object for the iterator which is bound to the table, and may retrieve the data by executing the view object. The view object execution takes the following steps
Step 1: The view object executes the query by calling executeQueryForCollection(...) and the associate the result with QueryCollection object.
Step 2: Once the query is executed, view object starts the iteration over the result set(collection) obtained at 'Step 1' by calling hasNextForCollection(...) for each element in the collection
Step 3: If step2 (hasNextForCollection) return true, then call createRowFromResultSet(...) to convert the record in to ADF aware form. If the view object is based on entity object, then corresponding entity instance(s) is getting created at this stage. In this case the ViewRowImpl will act as a wrapper over the underlying entity instance. It means that if you do update/delete on ViewRowImpl, that may finally reach underlying entity instance and entity may get added to the transaction listener's dirty list.
Step 4: Repeat Step 2 and 3 for all elements in the result set(till hasNextForCollection returns false). At the end of iteration run time marks 'fetch complete' by calling setFetchCompleteForCollection(...)
Who is interested in ViewObjectImpl::getQueryHitCount(...)?
Well, some UI components like table, tree etc. which needs paginated data fetch + scrollable behavior may require this information. These folks will call count query first and then may go for view object execution.
Note: If you are building programmatic view object, without any entity back up, its really required to implement ViewObjectImpl::retrieveByKey(ViewRowSetImpl rs,Key key,int maxNumOfRows) to return row for the key from your custom data source. why? Well, when run time fails to find the row in the cache by calling findByKey(), as a next step it will try to invoke retrieveByKey() to retrieve the value by passing Key. Please see 42.1.8 Handling View Object Queries with Primary Keys Defined by Transient Attributes to learn more.
What does happen when you update some data and call commit ?
When you update some row from an entity based view object, actual update happens against the corresponding entity instance(see Step 3 in the above section). Later when you commit transaction, entity posts changes to database and commit transaction. Let us take look at the steps of the transaction commit cycle for an entity object.
Step 1: While you call commit, transaction manager starts the post cycle for the dirty entities. The first step is to validate the business data by calling validateEntity().
Step 2: Then transaction manager calls postChanges() on each dirty row(entity instance). The post changes takes multiple sub tasks to finish the job as copied below.
EntityImpl::postChanges(...) : Initiates a post operation for this Entity Object.
a) EntityImpl::lock() : Locks the row.
b) EntityImpl::prepareForDML(...) : A pre-notification for preparing rows for posting to DB.
If you need to update some dependency columns or custom history columns then this is
the place to add that logic.
c) EntityImpl::doDML(...) : Performs INSERT/UPDATE/DELETE processing for the row.
Step 3: Once the data is posted, next step is committing the transaction. The following methods gets executed before and after of transaction commit.
a) EntityImpl::beforeCommit(...) : Reports that a commit operation has initiated
b) EntityImpl::afterCommit(...) : Reports that a successful commit operation has occurred
Well, enough theory, time for action ! Let us go ahead and build programmatic entities and view objects. Though this is simple, you may need to pay attention to a couple of 'silly' points to make it working.