private BeanPropertyAssocMany<?>[] getMany2Many() {
    ArrayList<BeanPropertyAssocMany<?>> list = new ArrayList<BeanPropertyAssocMany<?>>();
    for (int i = 0; i < manys.size(); i++) {
      BeanPropertyAssocMany<?> prop = manys.get(i);
      if (prop.isManyToMany()) {
        list.add(prop);
      }
    }

    return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]);
  }
  /** This combines the sql from named/positioned parameters and expressions. */
  private void prepare(boolean buildSql, boolean parseRaw, DeployParser deployParser) {

    buildBindWhereRawSql(buildSql, parseRaw, deployParser);
    buildBindHavingRawSql(buildSql, parseRaw, deployParser);

    SpiExpressionList<?> whereExp = query.getWhereExpressions();
    if (whereExp != null) {
      DefaultExpressionRequest whereReq = new DefaultExpressionRequest(request, deployParser);
      whereExprBindValues = whereExp.buildBindValues(whereReq);
      if (buildSql) {
        whereExprSql = whereExp.buildSql(whereReq);
      }
    }

    BeanPropertyAssocMany<?> manyProperty = request.getManyProperty();
    if (manyProperty != null) {
      OrmQueryProperties chunk = query.getDetail().getChunk(manyProperty.getName(), false);
      SpiExpressionList<?> filterMany = chunk.getFilterMany();
      if (filterMany != null) {
        DefaultExpressionRequest filterReq = new DefaultExpressionRequest(request, deployParser);
        filterManyExprBindValues = filterMany.buildBindValues(filterReq);
        if (buildSql) {
          filterManyExprSql = filterMany.buildSql(filterReq);
        }
      }
    }

    // having expression
    SpiExpressionList<?> havingExpr = query.getHavingExpressions();
    if (havingExpr != null) {
      DefaultExpressionRequest havingReq = new DefaultExpressionRequest(request, deployParser);
      havingExprBindValues = havingExpr.buildBindValues(havingReq);
      if (buildSql) {
        havingExprSql = havingExpr.buildSql(havingReq);
      }
    }

    if (buildSql) {
      parsePropertiesToDbColumns(deployParser);
    }
  }
  private BeanPropertyAssocMany<?>[] getMany(Mode mode) {
    ArrayList<BeanPropertyAssocMany<?>> list = new ArrayList<BeanPropertyAssocMany<?>>();
    for (int i = 0; i < manys.size(); i++) {
      BeanPropertyAssocMany<?> prop = manys.get(i);

      switch (mode) {
        case Save:
          if (prop.getCascadeInfo().isSave()
              || prop.isManyToMany()
              || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) {
            // Note ManyToMany always included as we always 'save'
            // the relationship via insert/delete of intersection table
            // REMOVALS means including PrivateOwned relationships
            list.add(prop);
          }
          break;
        case Delete:
          if (prop.getCascadeInfo().isDelete()
              || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) {
            // REMOVALS means including PrivateOwned relationships
            list.add(prop);
          }
          break;
        default:
          break;
      }
    }

    return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]);
  }
  /** There is a many property so we need to make sure the ordering is appropriate. */
  private String deriveOrderByWithMany(BeanPropertyAssocMany<?> manyProp) {

    if (manyProp == null) {
      return parseOrderBy();
    }

    String orderBy = parseOrderBy();

    BeanDescriptor<?> desc = request.getBeanDescriptor();
    String orderById = desc.getDefaultOrderBy();

    if (orderBy == null) {
      orderBy = orderById;
    }

    // check for default ordering on the many property...
    String manyOrderBy = manyProp.getFetchOrderBy();
    if (manyOrderBy != null) {
      orderBy = orderBy + ", " + CQueryBuilder.prefixOrderByFields(manyProp.getName(), manyOrderBy);
    }

    if (request.isFindById()) {
      // only one master bean so should be fine...
      return orderBy;
    }

    if (orderBy.startsWith(orderById)) {
      return orderBy;
    }

    // more than one top level row may be returned so
    // we need to make sure their is an order by on the
    // top level first (to ensure master/detail construction).

    int manyPos = orderBy.indexOf(manyProp.getName());
    int idPos = orderBy.indexOf(" " + orderById);

    if (manyPos == -1) {
      // no ordering of the many
      if (idPos == -1) {
        // append the orderById so that master level objects are ordered
        // even if the orderBy is not unique for the master object
        return orderBy + ", " + orderById;
      }
      // orderById is already in the order by clause
      return orderBy;
    }

    if (idPos <= -1 || idPos >= manyPos) {
      if (idPos > manyPos) {
        // there was an error with the order by...
        String msg =
            "A Query on ["
                + desc
                + "] includes a join to a 'many' association ["
                + manyProp.getName();
        msg += "] with an incorrect orderBy [" + orderBy + "]. The id property [" + orderById + "]";
        msg += " must come before the many property [" + manyProp.getName() + "] in the orderBy.";
        msg += " Ebean has automatically modified the orderBy clause to do this.";

        logger.warn(msg);
      }

      // the id needs to come before the manyPropName
      orderBy = orderBy.substring(0, manyPos) + orderById + ", " + orderBy.substring(manyPos);
    }

    return orderBy;
  }