JBO-25030: Detail entity X with row key Y cannot find or invalidate its owning entity

  • Problem Description

    You tried to create a new child object row of type X row without providing the necessary context information to identify its owning parent object. At the moment of child row creation the correct owning parent context must be provided, otherwise the new child row created would be an "orphan".

    For example, consider a custom object named TroubleTicket that has a child object named Activity. The following script that tries to create a new activity would generate this error:

    def activityVO = newView('Activity')
    // PROBLEM: Attempting to create a new child activity row
    // -------  without providing context about which owning
    //          TroubleTicket row this activity belongs to.
    def newActivity = activityVO.createRow()

    This generates a JBO-25030: Detail entity Activity with row key null cannot find or invalidate its owning entity exception because the script is trying to create a new Activity row without providing the context that allows that new row to know which TroubleTicket it should belong to.

  • Resolution

    There are two ways to provide the appropriate parent object context when creating a new child object row. The first approach is to get the owning parent row to which you want to add a new child row and use the parent row's child collection attribute to perform the createRow() and insertRow() combination. For example, to create a new Activity row in the context of TroubleTicket with an Id of 100000000272002 you can do the following, using the helper function mentioned in Finding an Object by Id. When you use this approach, the parent object context is implicit since you're performing the action on the parent row's child collection.

    def idParent = 100000000272002
    def ttVO = newView('TroubleTicket')
    def parent = adf.util.findRowByKey(ttVO,idParent)
    if (parent != null) {
      // Access the collection of Activity child rows for 
      // this TroubleTicket parent object
      def activities = parent.ActivityCollection
      // Use this child collection to create/insert the new row
      def newActivity = activities.createRow()
      activities.insertRow(newActivity);
      // Set other field values of the new activity here...
    } 

    The second approach you can use is to pass the context that identifies the id of the parent TroubleTicket row when you create the child row. You do that using an alternative function named createAndInitRow() as shown below. In this case, you don't need to have the parent row in hand or even use the parent TroubleTicket view object. Providing the id to the owning parent row at the moment of child activity row creation is good enough.

    def idParent = 100000000272002
    // Create an name/value pairs object to pass the parent id
    def parentAttrs = new oracle.jbo.NameValuePairs()
    parentAttrs.setAttribute('Id',idParent)
    // Use this name/value pairs object to pass the parent
    // context information while creating the new child row
    def activityVO = newView('Activity')
    def newActivity = activityVO.createAndInitRow(parentAttrs)
    activityVO.insertRow(newActivity);
    // Set other field values of the new activity here...