/**
   * INTERNAL: Return the value of the field from the row or a value holder on the query to obtain
   * the object. Check for batch + aggregation reading.
   */
  @Override
  public Object valueFromRow(
      AbstractRecord row,
      JoinedAttributeManager joinManager,
      ObjectBuildingQuery sourceQuery,
      AbstractSession executionSession)
      throws DatabaseException {
    // If any field in the foreign key is null then it means there are no referenced objects
    for (Enumeration enumeration = getFields().elements(); enumeration.hasMoreElements(); ) {
      DatabaseField field = (DatabaseField) enumeration.nextElement();
      if (row.get(field) == null) {
        return getIndirectionPolicy().nullValueFromRow();
      }
    }

    if (getTypeField() != null) {
      // If the query used batched reading, return a special value holder,
      // or retrieve the object from the query property.
      if (sourceQuery.isReadAllQuery()
          && (((ReadAllQuery) sourceQuery).isAttributeBatchRead(getDescriptor(), getAttributeName())
              || shouldUseBatchReading())) {
        return batchedValueFromRow(row, ((ReadAllQuery) sourceQuery));
      }

      // If the field is empty we cannot load the object because we do not know what class it will
      // be
      if (row.get(getTypeField()) == null) {
        return getIndirectionPolicy().nullValueFromRow();
      }
      Class implementerClass =
          (Class) getImplementorForType(row.get(getTypeField()), executionSession);
      ReadObjectQuery query = (ReadObjectQuery) getSelectionQuery().clone();
      query.setReferenceClass(implementerClass);
      query.setSelectionCriteria(getSelectionCriteria());
      query.setDescriptor(null); // Must set to null so the right descriptor is used

      if (sourceQuery.isObjectLevelReadQuery()
          && (sourceQuery.shouldCascadeAllParts()
              || (sourceQuery.shouldCascadePrivateParts() && isPrivateOwned())
              || (sourceQuery.shouldCascadeByMapping() && this.cascadeRefresh))) {
        query.setShouldRefreshIdentityMapResult(sourceQuery.shouldRefreshIdentityMapResult());
        query.setCascadePolicy(sourceQuery.getCascadePolicy());
        query.setShouldMaintainCache(sourceQuery.shouldMaintainCache());
        // For flashback.
        if (((ObjectLevelReadQuery) sourceQuery).hasAsOfClause()) {
          query.setAsOfClause(((ObjectLevelReadQuery) sourceQuery).getAsOfClause());
        }

        // CR #4365 - used to prevent infinit recursion on refresh object cascade all
        query.setQueryId(sourceQuery.getQueryId());
      }

      return getIndirectionPolicy().valueFromQuery(query, row, executionSession);
    } else {
      return super.valueFromRow(row, joinManager, sourceQuery, executionSession);
    }
  }
 /** Read employee and clear the cache, test database read. */
 public void test() throws Exception {
   ReadObjectQuery query = new ReadObjectQuery(Employee.class);
   ExpressionBuilder employee = new ExpressionBuilder();
   query.setSelectionCriteria(
       employee
           .get("firstName")
           .equal("Brendan")
           .and(employee.get("salary").equal(100000))
           .and(employee.get("address").get("city").like("%pean%"))
           .and(employee.anyOf("phoneNumbers").get("type").equal("Home")));
   query.checkCacheOnly();
   Employee result = (Employee) getSession().executeQuery(query);
 }