Tuesday, September 10, 2013

Entities Association - not a composition

master detail relations in relational databases can be described either as a composite or not a composite relationships.

a Master - detail composite means that the detail cannot exist on it own without a master.
a Master - detail non-composite means that the detail can exist in the database without a master.

in ADF this can be configured in the Assocaiton between two entities ,relationship tab under the behavior sub-header, by checking the composition association checkbox.

   1)this will have some effects on the behavior of the framework, some of these are: the order of posting changes to the database, the master will be posted first.
   2) if a detail is changed then the master will be validated again.
   3)if the ids are assigned by triggers, the foreign key in the detail will be changed to the new id of the master assigned by the trigger.


but what will happen if the relationship is not a composition?
this might causes some issues, specially since the framework posts the new records to the database in the order of their creation.
so consider the following scenario: you create a detail record , create a master record, assign the detail to be a child record of the master record then commit.
here is a code sample that does the above scenario:
        System.out.println(Test.class+".main>>starting");
        ApplicationModuleImpl module=(ApplicationModuleImpl)Configuration.createRootApplicationModule("model.businessObjects.applicationMod.AppModule","AppModuleLocal");
        System.out.println(Test.class+".main>>after getting application module");

        ViewObject deptVO= module.findViewObject("DepartmentsView1");
        ViewObject empVO= module.findViewObject("EmployeesView3");

        EmployeesViewRowImpl empRow=(EmployeesViewRowImpl)empVO.createRow();
        DepartmentsViewRowImpl deptRow=(DepartmentsViewRowImpl)deptVO.createRow();

        empRow.setLastName("zamer");
        empRow.setEmail("ahmad.alzamer@gmail.com");
        empRow.setHireDate(new Date());
        empRow.setJobId("AD_VP");
        empRow.setDepartmentId(deptRow.getDepartmentId().getSequenceNumber());
        
        deptRow.setDepartmentName("testing");

        module.getTransaction().commit();
        System.out.println(Test.class+".main>>emp:"+empRow.getEmployeeId());
        System.out.println(Test.class+".main>>dept:"+deptRow.getDepartmentId());

the above code is built on the HR schema and the association between the two entities is not a composite one. For the record, I overrode the doDml method in both entities to print to the output console the name of the class
if we run the above code we will get the following:
class model.testing.Test.main>>after getting application module
class model.businessObjects.entity.EmployeesImpl.doDML>>
Exception in thread "main" oracle.jbo.DMLConstraintException: JBO-26048: Constraint "EMP_DEPT_FK" is violated during post operation "Insert" using SQL statement "BEGIN INSERT INTO EMPLOYEES(EMPLOYEE_ID,LAST_NAME,EMAIL,HIRE_DATE,JOB_ID,DEPARTMENT_ID) VALUES (:1,:2,:3,:4,:5,:6) RETURNING EMPLOYEE_ID INTO :7; END;".
at oracle.jbo.server.OracleSQLBuilderImpl.doEntityDML(OracleSQLBuilderImpl.java:568)
at oracle.jbo.server.EntityImpl.doDMLWithLOBs(EntityImpl.java:8535)
at oracle.jbo.server.EntityImpl.doDML(EntityImpl.java:8467)
at model.businessObjects.entity.EmployeesImpl.doDML(EmployeesImpl.java:25)
at oracle.jbo.server.EntityImpl.postChanges(EntityImpl.java:6733)
at model.businessObjects.entity.EmployeesImpl.postChanges(EmployeesImpl.java:33)
at oracle.jbo.server.DBTransactionImpl.doPostTransactionListeners(DBTransactionImpl.java:3286)
at oracle.jbo.server.DBTransactionImpl.postChanges(DBTransactionImpl.java:3089)
at oracle.jbo.server.DBTransactionImpl.commitInternal(DBTransactionImpl.java:2093)
at oracle.jbo.server.DBTransactionImpl.commit(DBTransactionImpl.java:2374)
at model.testing.Test.main(Test.java:35)
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (HR.EMP_DEPT_FK) violated - parent key not found
ORA-06512: at line 1

as you can see framework tried to post the employee record first; since it was created first.
in order to change this behavior, we should override postChanges to ensure that the master record is posted first.
here is the code to override the postChanges in the EmployeesImpl class:
    public void postChanges(TransactionEvent transactionEvent){
        if(getPostState()==STATUS_NEW||getPostState()==STATUS_MODIFIED){
            if(getBelongToDepartment().getPostState()==STATUS_NEW){
                getBelongToDepartment().postChanges(transactionEvent);
            }
        }
        super.postChanges(transactionEvent);
    }
the output after the run is:
class model.testing.Test.main>>after getting application module
class model.businessObjects.entity.DepartmentsImpl.doDML>>
class model.businessObjects.entity.EmployeesImpl.doDML>>
Exception in thread "main" oracle.jbo.DMLConstraintException: JBO-26048: Constraint "EMP_DEPT_FK" is violated during post operation "Insert" using SQL statement "BEGIN INSERT INTO EMPLOYEES(EMPLOYEE_ID,LAST_NAME,EMAIL,HIRE_DATE,JOB_ID,DEPARTMENT_ID) VALUES (:1,:2,:3,:4,:5,:6) RETURNING EMPLOYEE_ID INTO :7; END;".
at oracle.jbo.server.OracleSQLBuilderImpl.doEntityDML(OracleSQLBuilderImpl.java:568)
at oracle.jbo.server.EntityImpl.doDMLWithLOBs(EntityImpl.java:8535)
at oracle.jbo.server.EntityImpl.doDML(EntityImpl.java:8467)
at model.businessObjects.entity.EmployeesImpl.doDML(EmployeesImpl.java:25)
at oracle.jbo.server.EntityImpl.postChanges(EntityImpl.java:6733)
at model.businessObjects.entity.EmployeesImpl.postChanges(EmployeesImpl.java:35)
at oracle.jbo.server.DBTransactionImpl.doPostTransactionListeners(DBTransactionImpl.java:3286)
at oracle.jbo.server.DBTransactionImpl.postChanges(DBTransactionImpl.java:3089)
at oracle.jbo.server.DBTransactionImpl.commitInternal(DBTransactionImpl.java:2093)
at oracle.jbo.server.DBTransactionImpl.commit(DBTransactionImpl.java:2374)
at model.testing.Test.main(Test.java:35)
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (HR.EMP_DEPT_FK) violated - parent key not found
ORA-06512: at line 1

we still get integrity constraint error, since the id of the master is changed -by the trigger- but the foreign key is not.
to fix this error we override two functions in the departmentsImpl class:
    RowSet empsInDept=null;
    @Override
    public void postChanges(TransactionEvent transactionEvent){
        if(getPostState()==STATUS_NEW){
            empsInDept=(RowSet)getEmployeesInDept();
        }
        super.postChanges(transactionEvent);
    }

    @Override
    protected void refreshFKInNewContainees(){
        if(empsInDept!=null){
            while(empsInDept.hasNext()){
                EmployeesImpl employee=(EmployeesImpl)empsInDept.next();
                employee.setDepartmentId(getDepartmentId().getSequenceNumber());
            }
            empsInDept.closeRowSet();
        }
        
        super.refreshFKInNewContainees();
    }
we override the functions:
   1)postChanges : to save a rowSet temporarily ,so that we have a way to access the child record after posting the current row to the database. since after posting the row will have a new id and we will not be able to fetch the child records.
   2)refreshFKInNewContainees: this function is called by the framework to refresh the values of the foreign keys in child records with the new value assigned by the triggers, but this function will only affect composite relations; so we override it to update the values in a non-composite relation.

the output after the run is:
class model.testing.Test.main>>after getting application module
class model.businessObjects.entity.DepartmentsImpl.doDML>>
class model.businessObjects.entity.EmployeesImpl.doDML>>
class model.testing.Test.main>>emp:4
class model.testing.Test.main>>dept:6

1 comment: