Search code examples
javasqloracle-databaseoracle-adfjdeveloper

Reload ADF table after button click in a Fusion Web application


I recently started working on my first ADF project using JDeveloper 12c.

So, I have a Fusion Web Application that's connected to an Oracle database. On one of the jsf pages there are two ADF tables, that display data from the database. Below is a button that sends a "DELETE"-statement to the database to delete selected entries. For reasons both tables have to be refreshed after this (the deletion affects the shown entries of both tables).

After I was already really happy that the tables were displaying the correct data and the button did its thing too I quickly realized that if the database changes, the tables in the jsf page will not get refreshed automatically. I searched around the web a bit for a a good beginner level tutorial of how to refresh elements of a jsf page in an ADF Fusion application. Sadly I didn't found anything that gave me the key to it so far.

I found this article on the Oracle HQ page, but it also has the habit of using many propietary namings and is written in flowing text, so there is no code sample or snippet or similar in it, which made it difficult to follow for a rookie.

This is a snippet from my Managed java bean, where I store my button functionality:

    public String update() {
    getDBConnection c = new DBConnection();
    Connection conn = c.getConn();
    
    RowKeySet selectedEntries = getDetailTable().getSelectedRowKeys();   
    Iterator selectedEntryIter = selectedEntries.iterator();
    DCBindingContainer bindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
    DCIteratorBinding entryIter = bindings.findIteratorBinding("my_iterator");
    RowSetIterator rSIter = entryIter.getRowSetIterator();
    try {
        PreparedStatement pstmt1 = conn.prepareStatement("DELETE ...");
        PreparedStatement pstmt2 = conn.prepareStatement("DELETE ...");
        while(selectedEntryIter.hasNext()){
            Key key = (Key)((List)selectedEntryIter.next()).get(0);
            Row currentRow = rSIter.getRow(key);
            BigDecimal barcode = (BigDecimal) currentRow.getAttribute("id");
            BigDecimal field1 = (BigDecimal) currentRow.getAttribute("field1");
            BigDecimal field2 = (BigDecimal) currentRow.getAttribute("field2");
                          
            pstmt1.setBigDecimal(1, id);
            pstmt1.setBigDecimal(2, field1);
            pstmt2.setBigDecimal(1, id);
            pstmt2.setBigDecimal(2, field2);
            pstmt1.executeUpdate();
            pstmt2.executeUpdate();
            
        }
        conn.commit();
        //i guess here i have to trigger to refresh the tables but I have pretty to no clue of how to do that
        //where do I have to set the functionality? I read sth about creating another bean in the "session" package 
        //but somehow i have to access the jsf i want to have refreshed. Where do I create that connection?
        //even a simple example or a good reference to a  tutorial would be helpful for me
        
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return null;
}

Probably this question is a duplicate to an already existing one and i'm just too stupid to find it, but I will give it a try anyways. Thanks in advance!


Solution

  • In your case you can simply add a entryIter.executeQuery(); right after your conn.commit(); (You should also avoid running a direct sql delete and use the standard ADF BC DELETE https://o7planning.org/11489/create-update-and-delele-data-using-adf-form-in-adf-bc#a9769791)

    But to answer the question title for future queries, below is an exemple of a simple refresh table button, easily reusable, that i usually add on my clients tables toolbar :

    //in your jsff
    <af:panelCollection id="pc" >
        <f:facet name="menus"/> 
        <f:facet name="statusbar"/>
        <f:facet name="toolbar">
            <af:toolbar id="t1" flex="5">
                <af:group id="g1">
                    <af:commandImageLink shortDesc="Reload" partialSubmit="true" actionListener="#{YOUR_SCOPE.YOUR_BEAN.refreshTable}"
                                         icon="#{resource['images:YOUR_RELOAD_ICON.png']}">
                        <f:attribute name="tableIdToRefresh" value="YOUR_TABLE_ID"/>
                    </af:commandImageLink>
                </af:group>
            </af:toolbar>
            </f:facet>
            <af:table id="YOUR_TABLE_ID" value="#{bindings.YOUR_VO.collectionModel}" var="row" rows="#{bindings.YOUR_VO.rangeSize}"
                      selectedRowKeys="#{bindings.YOUR_VO.collectionModel.selectedRow}" selectionListener="#{bindings.YOUR_VO.collectionModel.makeCurrent}" rowSelection="single"
                      fetchSize="#{bindings.YOUR_VO.rangeSize}" filterModel="#{bindings.XxcnVieIntSearchVCQuery.queryDescriptor}"
                      queryListener="#{bindings.XxcnVieIntSearchVCQuery.processQuery}" varStatus="vs" >
                
                <!--  COLUMNS -->
            </af:table>
    </af:panelCollection>
    
    
    //in your bean #{YOUR_SCOPE.YOUR_BEAN.refreshTable}
    public void refreshTable(ActionEvent actionEvent) {
        //Get the attribute tableIdToRefresh value. I like to have it as a jsff attribute so i can easily reuse the button elsewhere
        String tableToRefreshId = "" + ((RichCommandImageLink)actionEvent.getSource()).getAttributes().get("tableIdToRefresh");
        if (sValeurCode != null) {
            addPprToComponentById(tableToRefreshId);
            //If it doesn't suffice (see below) you can use this : 
            //refreshTableIterator(tableToRefreshId);
        }
    }
    
    public static void addPprToComponentById(String id) {
        Object component = findComponentInRoot(id); //from the great JSFUtils library
        if (component != null) {
            AdfFacesContext.getCurrentInstance().addPartialTarget((UIComponent)component);
            AdfFacesContext.getCurrentInstance().partialUpdateNotify((UIComponent)component);
        }
    }
    
    /**
     * Locate an UIComponent in view root with its component id. Use a recursive way to achieve this.
     * @param id UIComponent id
     * @return UIComponent object
     */
    public static UIComponent findComponentInRoot(String id) {
        UIComponent component = null;
        if (id != null) {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            if (facesContext != null) {
                UIComponent root = facesContext.getViewRoot();
                if (root != null) {
                    component = findComponent(root, id);
                }
            }
        }
        return component;
    }
    

    You can also use this addPPR logic for other components.

    If a simple ppr doesn't suffice. You can force the table query execution with those :

    public static void refreshTableIterator(String tableId) {
        RichTable table = findComponentInRoot(tableId);
        DCIteratorBinding treeIterator = null;
        if (table != null) {
            treeIterator = getTableIterator(table);
            if (treeIterator != null) {
                RowSetIterator rsi = treeIterator.getRowSetIterator();
                treeIterator.executeQuery();
                rsi.closeRowSetIterator();
            }
        }
    }
    
    public static DCIteratorBinding getTableIterator(RichTable table) {
        DCIteratorBinding treeIterator = null;
        CollectionModel model = (CollectionModel)table.getValue();
        if (model != null) {
            JUCtrlHierBinding treeBinding = (JUCtrlHierBinding)model.getWrappedData();
            if (treeBinding != null) {
                treeIterator = treeBinding.getDCIteratorBinding();
            }
        }
        return treeIterator;
    }