Pages

Tuesday, December 11, 2007

Business rules in adf bc ( bc4j )

As an old school oracle developer I developed my business rules in plsql. I did this with cdm ruleframe or with triggers and packages. But now I want to make a demo on the oe sample schema. In this demo I used the orders and the order_items tables and because it is a demo I want to use bc4j for my business rules.
This are the business rules I want to implement. Every order item has to update the total order amount of the order. The Line Item Id of the order items has to an autonumber 1, 2, etc and not a sequence. On the order table the order date has to be current date and the order Id must be the next value of the orders_seq sequence.
Here some pics of the order and items page



This is how I did it on the orders view. First I made two calculated attributes CalculatedOrderTotal and CalculatedOrderLines on the orders entity. These attributes I use on the orders items entity. Then I generate the impl java class of the orders entity so I can change the getters of these calculated attributes.


/**Gets the attribute value for CalculatedOrderTotal, using the alias name CalculatedOrderTotal.
*/
public Double getCalculatedOrderTotal() {

RowIterator rowItr = this.getOrderItems();
Double total = 0.0;
while(rowItr.hasNext()){
Row row = rowItr.next();
Number unitPrice = (Number)row.getAttribute("UnitPrice");
Number quantity = (Number)row.getAttribute("Quantity");
total += (unitPrice.doubleValue() * quantity.intValue());
}
return total;
}
/**Sets value as the attribute value for CalculatedOrderTotal.
*/
public void setCalculatedOrderTotal(Double value) {
setAttributeInternal(CALCULATEDORDERTOTAL, value);
}

/**Gets the attribute value for CalculatedOrderLines, using the alias name CalculatedOrderLines.
*/
public Number getCalculatedOrderLines() {
RowIterator rowItr = this.getOrderItems();
Number lineId = new Number(0);

while(rowItr.hasNext()){
Row row = rowItr.next();
Number lineIdRow = (Number)row.getAttribute("LineItemId");
// check the current Id is greater than lineId

if ( lineIdRow != null && lineIdRow.compareTo(lineId) == 1 ) {
lineId = lineIdRow;
}
}
return lineId;
}

/**Sets value as the attribute value for CalculatedOrderLines.
*/
public void setCalculatedOrderLines(Number value) {
setAttributeInternal(CALCULATEDORDERLINES, value);
}

Now we add some code to the impl of the order items. First I try to set the next number for the line item , Then I set the unit item price of the selected product ( I can do this by using the association between order items and product information )the last step is to update the total order amount of the order


protected void prepareForDML(int dml, TransactionEvent evt) {
if (dml == DML_INSERT || dml == DML_UPDATE || dml == DML_DELETE) {
super.prepareForDML(dml,evt);
businessRules( dml);
}
}

public void businessRules(int dml){
if ( dml == DML_INSERT) {
Number row = this.getOrders().getCalculatedOrderLines();
this.setLineItemId(row.add(1));
}

if ( dml == DML_INSERT || dml == DML_UPDATE) {
ProductInformationImpl product = (ProductInformationImpl) this.getProductInformation();
if(product != null){
Number listPrice = product.getListPrice();
this.setUnitPrice(listPrice);
}
}
if ( dml == DML_UPDATE) {
Number oldQuantity = (Number)getPostedAttribute(getAttributeIndexOf("Quantity"));
System.out.println("new quantity "+this.getQuantity().toString()+" old "+oldQuantity.toString());
}
try {
this.getOrders().setOrderTotal(new Number(this.getOrders().getCalculatedOrderTotal()));;
}
catch ( Exception e) {
e.printStackTrace();
}
}

If you want to check the old and new values of an attribute then you can use getPostedAttribute to get the old value of the attribute. This is the same as :old and :new in the plsql triggers.
Now we implement the business rules on the orders entity. We add the following code to the orders impl

protected void prepareForDML(int dml, TransactionEvent evt) {
if (dml == DML_INSERT || dml == DML_UPDATE || dml == DML_DELETE) {
super.prepareForDML(dml,evt);
businessRules(dml);
}
}

public void businessRules(int dml){
if ( dml == DML_INSERT) {
SequenceImpl sequence = new SequenceImpl("ORDERS_SEQ", getDBTransaction());
this.setOrderId(sequence.getSequenceNumber());
this.setOrderDate(new Date(Date.getCurrentDate()));
}
}

In this method I set a database sequence on the Order Id and the current Date in the Order Date attribute.
Here you have the sample project it works on jdeveloper 11g and on the oe sample schema

11 comments:

  1. Just want to say that it not old fashion software to let some business rules in the database model. It make sense to let some logic in the database layer instead thinking that all has to move in the middle tier. The advantages are that all rules are always applied independtly the tool that manipulate the data and that the logic applied is near the data (checks are often data intenvive consumer).

    ReplyDelete
  2. Hi,
    My name is Shadi and I'm from Iran. I workd with jdeveloper 11g with jheadstart.
    I read your blog and wonder if you can answer my question.
    my problem is:
    I have made an edit button in a form in jdeveloper 11g and I made it the way that when I push the button all the things that I want to be updated, turns to be active to update. for example you can change dropdown's value and ...
    but when I push the save button, it says that there's nothing to save.
    I think it gets refreshed and accept no changes.
    how do you think I can make my edit button work?

    Thankx in advance

    Shadi Khani

    ReplyDelete
  3. Hi,

    When you think it refreshes itself when you do a submit (save button) then you should take a look at the refresh options of the iterators in executables section of the pagedef of the jsf page.

    thanks Edwin

    ReplyDelete
  4. Hi Edwin,

    Thanks for your help, but I did not undrestand.Could you plz tell me in more details? Where is refresh options of the iterators in executables section of the pagedef of the jsf page ?

    And another question is that I could use table range in jdeveloper 10gon my tables , but in 11g I cannot see it on the tables can you tell me what should I do if I want to see table range on my tables?

    Thanks Shadi

    ReplyDelete
  5. Ok,

    when you go the adf page and press the bindings tab. The you will see in the middle the executables. just press one and in the property window you can change the rangesize and the refresh property

    the tables fetches automatically the next rows when you press the scrollbar

    thanks edwin

    ReplyDelete
  6. Hi I solved it,


    set immediate on false on the edit button. please take a look at the jsf page lifecycle to see what immediate does

    put your backing bean in adfc-config.xml , no need for faces-config.xml
    and don't use request use session because when you press the save button and make a round tip to the server , the bean will be initialized again and the boolean value will be false.
    so put this to this session en voila.

    Good luck and thanks Edwin

    ReplyDelete
  7. Thank you Edwin,
    You're a brilliant thinker.

    Thanks Shadi

    ReplyDelete
  8. Edwin can you tell me why the page stays in edition mode after the edition is done when I press the save button? Can you fix it ? I have already sent you another demo to see what happens when you go through other pages after edittion. Could you check my email plz ?


    Thanks Shadi.

    ReplyDelete
  9. Hi Shadi,

    That is because the managed bean is in session mode. So after a succesfull commit set the edit variable to false.

    What you should do is make your own setActionListener method in the bean and use this on the commit button. Then get a handle to the commit operation in the pagedef , See if there are errors on this operationbinding and after that set the edit variable on false ) .

    see this blog for the code
    http://biemond.blogspot.com/2009/03/some-handy-code-for-backing-beans-adf.html

    or something like this

    // get the binding container
    BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();


    // get an Action or MethodAction
    OperationBinding method = bindings.getOperationBinding("commit");
    method.execute();
    List errors = method.getErrors();

    // show error

    // reset edit variable

    hope this helps

    Edwin

    ReplyDelete
  10. Hi Edwin,

    I did it myself.
    Plz don't check the "MyDemo" project

    Thanx for your help.
    Thank you Shadi

    ReplyDelete
  11. Hi,

    great job, you learned something.

    thanks Edwin

    ReplyDelete