Saturday, February 11, 2012

ADF - Create and Populate new Rows on ViewObject with Multiple Updateable Dependent Entity Objects


This is my first post on my new Blog and I would like to share with you the solution for a very common ADF BC case study: Create and Populate new Rows on ViewObjectwith Multiple Updateable Dependent Entity Objects.



Image that you need to build a form page thatallows users to provide the data for creating both a new Supplier and its “Principal Contact”. To do this, you can build a single view object that joinsboth the supplier and contact entity object where you can find a SupplierId column thatrepresents the foreign key with supplier table.

I'm not going to talk about creation of entityobject and view object - I take for granted you have built your ViewObjectcorrectly and that you can query it in the Application Module tester.

A basic step is enabling the secondary entity object (ContactEOin our example) to be updatable by selecting it in the Selected list of theEntity Objects page of the view object overview editor and:

  Deselecting the Reference checkbox

  Selecting the Updatable checkbox

Nowyou can start by editing SupplierVORowImpl.java class, you need override the framework create()method as shown in the following code:
/** [In file: SupplierVORowImpl.java]
 *
*/
  
 @Override
    protected void create(AttributeList attributeList) {
     SupplierEOImpl newSupplier = this.getSupplierEO();
     ContactEOImpl newContact = this.getContactEO();
          try {
             newSupplier.create(attributeList);
             newContact.create(newSupplier);            
          }
          catch (JboException ex) {
            newSupplier.revert();
            newContact.revert();  
           
            throw ex;
          }
          catch (Exception otherEx) {
              newSupplier.revert();
              newContact.revert();  
                    
            throw new RowCreateException(true /* EO Row? */,
                                  "Supplier" /* EO Name */,
                               otherEx      /* Details */);
          }      
   }


The above code dosen’t work untilwe override the create() method in SupplierEOImpl and ContactEOImpl class. TheBC4J framework base class for entity objects, oracle.jbo.server.EntityImpl,has the create() method with protected member access so if yourentity object is in another package compared to view object, you need to widenthe access of the create() method to public. The overridden create()methods class looks like this: 

/** [In file: SupplierEOImpl.java]
 *
*/
  protected void create(AttributeList attributeList) {
    super.create(attributeList);
  }

/** [In file: ContactEOImpl.java]
 *
*/
  protected void create(AttributeList attributeList) {
    super.create(attributeList);
    DBSequence supplierId = (DBSequence)attributeList.getAttribute("Id");
    if (supplierId!= null) {
      setSupplierId(supplierId.getSequenceNumber());
    }
  }

Pay attention to the create() method ofContactEOImpl where we retrieve the DBSequence value of Id Attribute from the newSupplierEOImpl instance passed-in, and the way we set the "SupplierId"foreign key attribute of the contact with this temporary value. In next steps overriding refreshFKInNewContainees()and postChanges() method of SupplierEOImpl class we force the new supplier topost first and refresh temporary foreign key value to the final(database-trigger-assigned) sequence number.

If the primary key of SupplierEOImpl is notdefined as DBSequence Attribute - but as Number for example - you don’t need to override refreshFKInNewContainees()and postChanges() method, but you can retrieve the database sequence in create()method of SupplierEOImpl using SequenceImpl class, and then you can use thisvalue in create() method of ContactEOImpl.java to populate the foreign keyattribute.


The following code represent the refreshFKInNewContainees()and postChanges() method of SupplierEOImpl class:

/** [In file: SupplierEOImpl.java]
  *
*/
  private RowIterator newContactBeforePost = null;
 
 
  public void postChanges(TransactionEvent e) {
   
    if (getPostState() == STATUS_NEW) {
      newContactBeforePost = getContacts();
    }
    super.postChanges(e);
  }
 
 
 protected void refreshFKInNewContainees() {
     Number newSupplierId = getId().getSequenceNumber();
     if (newContactBeforePost!= null) {
       while (newContactBeforePost.hasNext()) {
         ContactEOImpl curContact = (ContactEOImpl) newContactBeforePost.next();
         curContact.setSupplierId(newSupplierId);
       }
       newContactBeforePost = null;
     }
   }


The code is now ok, you should beable to use the BC4J Tester to test your application module and to create a newSupplier with it Principal Contact!!


I advise you to read the following post because I found It very useful to write mine: HOWTO: Implementing a ViewObject with Multiple Updateable Dependent Entity Objects or  you can read the official Oracle Documentation about this issue here.


I hope you appreciated my post. Feel free to comment it.
bye
Emilio



2 comments:

Anonymous said...

I wanted to implement gapless sequence number. But, when i try to to implement it then (DBSequence)attributeList.getAttribute("Id");

returns null.

and then jbo.null object reference exception is trhown.

Do you know how I can implement gapless sequence number using this approach.

Anonymous said...

I wanted to implement gapless sequence number. But, when i try to to implement it then (DBSequence)attributeList.getAttribute("Id");

returns null.

and then jbo.null object reference exception is trhown.

Do you know how I can implement gapless sequence number using this approach.