public void addToBatch(Object key, String sql, Expectation expectation) {
   checkConsistentBatchKey(key);
   if (sql == null) {
     throw new IllegalArgumentException("sql must be non-null.");
   }
   notifyObserversImplicitExecution();
   try {
     final PreparedStatement statement = getStatements().get(sql);
     final int rowCount = statement.executeUpdate();
     expectation.verifyOutcome(rowCount, statement, 0);
   } catch (SQLException e) {
     log.error("sqlexception escaped proxy", e);
     throw getSqlExceptionHelper().convert(e, "could not execute batch statement", sql);
   }
 }
  protected int doUpdateRows(
      Serializable id, PersistentCollection collection, SessionImplementor session)
      throws HibernateException {

    // we finish all the "removes" first to take care of possible unique
    // constraints and so that we can take better advantage of batching

    try {
      int count = 0;
      if (isRowDeleteEnabled()) {
        boolean useBatch = true;
        PreparedStatement st = null;
        // update removed rows fks to null
        try {
          int i = 0;

          Iterator entries = collection.entries(this);
          int offset = 1;
          Expectation expectation = Expectations.NONE;
          while (entries.hasNext()) {

            Object entry = entries.next();
            if (collection.needsUpdating(
                entry, i, elementType)) { // will still be issued when it used to be null
              if (st == null) {
                String sql = getSQLDeleteRowString();
                if (isDeleteCallable()) {
                  expectation = Expectations.appropriateExpectation(getDeleteCheckStyle());
                  useBatch = expectation.canBeBatched();
                  st =
                      useBatch
                          ? session.getBatcher().prepareBatchCallableStatement(sql)
                          : session.getBatcher().prepareCallableStatement(sql);
                  offset += expectation.prepare(st);
                } else {
                  st = session.getBatcher().prepareBatchStatement(getSQLDeleteRowString());
                }
              }
              int loc = writeKey(st, id, offset, session);
              writeElementToWhere(st, collection.getSnapshotElement(entry, i), loc, session);
              if (useBatch) {
                session.getBatcher().addToBatch(expectation);
              } else {
                expectation.verifyOutcome(st.executeUpdate(), st, -1);
              }
              count++;
            }
            i++;
          }
        } catch (SQLException sqle) {
          if (useBatch) {
            session.getBatcher().abortBatch(sqle);
          }
          throw sqle;
        } finally {
          if (!useBatch) {
            session.getBatcher().closeStatement(st);
          }
        }
      }

      if (isRowInsertEnabled()) {
        Expectation expectation = Expectations.appropriateExpectation(getInsertCheckStyle());
        boolean callable = isInsertCallable();
        boolean useBatch = expectation.canBeBatched();
        String sql = getSQLInsertRowString();
        PreparedStatement st = null;
        // now update all changed or added rows fks
        try {
          int i = 0;
          Iterator entries = collection.entries(this);
          while (entries.hasNext()) {
            Object entry = entries.next();
            int offset = 1;
            if (collection.needsUpdating(entry, i, elementType)) {
              if (useBatch) {
                if (st == null) {
                  if (callable) {
                    st = session.getBatcher().prepareBatchCallableStatement(sql);
                  } else {
                    st = session.getBatcher().prepareBatchStatement(sql);
                  }
                }
              } else {
                if (callable) {
                  st = session.getBatcher().prepareCallableStatement(sql);
                } else {
                  st = session.getBatcher().prepareStatement(sql);
                }
              }

              offset += expectation.prepare(st);

              int loc = writeKey(st, id, offset, session);
              if (hasIndex && !indexContainsFormula) {
                loc = writeIndexToWhere(st, collection.getIndex(entry, i, this), loc, session);
              }

              writeElementToWhere(st, collection.getElement(entry), loc, session);

              if (useBatch) {
                session.getBatcher().addToBatch(expectation);
              } else {
                expectation.verifyOutcome(st.executeUpdate(), st, -1);
              }
              count++;
            }
            i++;
          }
        } catch (SQLException sqle) {
          if (useBatch) {
            session.getBatcher().abortBatch(sqle);
          }
          throw sqle;
        } finally {
          if (!useBatch) {
            session.getBatcher().closeStatement(st);
          }
        }
      }

      return count;
    } catch (SQLException sqle) {
      throw getFactory()
          .getSQLExceptionHelper()
          .convert(
              sqle,
              "could not update collection rows: "
                  + MessageHelper.collectionInfoString(this, id, getFactory()),
              getSQLInsertRowString());
    }
  }