All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.internet2.middleware.grouperClient.jdbc.tableSync.GcGrouperSyncMemberDao Maven / Gradle / Ivy

package edu.internet2.middleware.grouperClient.jdbc.tableSync;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import edu.internet2.middleware.grouperClient.jdbc.GcDbAccess;
import edu.internet2.middleware.grouperClient.jdbc.GcPersist;
import edu.internet2.middleware.grouperClient.jdbc.GcPersistableField;
import edu.internet2.middleware.grouperClient.util.GrouperClientUtils;

public class GcGrouperSyncMemberDao {

  /**
   * 
   * @param gcGrouperSyncMemberId
   * @return
   */
  public GcGrouperSyncMember memberRetrieveByIdFromCache(String gcGrouperSyncMemberId) {
    return this.internalCacheSyncMembersById.get(gcGrouperSyncMemberId);
  }
  

  /**
   * keep an internal cache of members by member id
   */
  @GcPersistableField(persist = GcPersist.dontPersist)
  private Map internalCacheSyncMembers = new HashMap();
  /**
   * keep an internal cache of members by uuid
   */
  @GcPersistableField(persist = GcPersist.dontPersist)
  private Map internalCacheSyncMembersById = new HashMap();

  public GcGrouperSyncMemberDao() {
  }

  /**
   * select grouper sync member by member id.  Note: this doesnt store to db yet, you do that at the end
   * @param connectionName
   * @param memberId
   * @return the member
   */
  public GcGrouperSyncMember memberCreateByMemberId(String memberId) {
    GcGrouperSyncMember gcGrouperSyncMember = this.internal_memberCreateByMemberIdHelper(memberId);
    this.internal_memberStore(gcGrouperSyncMember);
    this.gcGrouperSync.addObjectCreatedCount(1);
    this.internal_memberCacheAdd(gcGrouperSyncMember);
    return gcGrouperSyncMember;
  }

  /**
   * create grouper sync member by member id.  Note: this doesnt store to db yet, you do that at the end
   * @param connectionName
   * @param memberId
   * @return the member
   */
  public GcGrouperSyncMember internal_memberCreateByMemberIdHelper(String memberId) {
    
    GcGrouperSyncMember gcGrouperSyncMember = internal_memberRetrieveFromDbByMemberNameFromMemberId(memberId);
    
    if (gcGrouperSyncMember != null) {
      gcGrouperSyncMember.setMemberId(memberId);
      return gcGrouperSyncMember;
    }
    
    gcGrouperSyncMember = new GcGrouperSyncMember();
    gcGrouperSyncMember.setGrouperSync(this.getGcGrouperSync());
    gcGrouperSyncMember.setMemberId(memberId);
    return gcGrouperSyncMember;
  }

  
  /**
   * select grouper sync member by member id
   * @param connectionName
   * @param memberId
   * @return the group
   */
  public GcGrouperSyncMember internal_memberRetrieveFromDbByMemberNameFromMemberId(String memberId) {
    
    List gcGrouperSyncMembers = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
        .sql("select gsm.* from grouper_sync_member gsm, grouper_members gm where gsm.grouper_sync_id = ? and gm.id = ? and gm.subject_id = gsm.subject_id and gsm.source_id = gm.subject_source")
          .addBindVar(this.getGcGrouperSync().getId()).addBindVar(memberId).selectList(GcGrouperSyncMember.class);
    
    if (GrouperClientUtils.length(gcGrouperSyncMembers) == 0) {
      return null;
    }
    
    if (GrouperClientUtils.length(gcGrouperSyncMembers) == 1) {
      return gcGrouperSyncMembers.iterator().next();
    }
    
    gcGrouperSyncMembers.sort(new Comparator() {

      @Override
      public int compare(GcGrouperSyncMember o1, GcGrouperSyncMember o2) {
        return o2.getLastUpdated().compareTo(o1.getLastUpdated());
      }
    });
    
    return gcGrouperSyncMembers.iterator().next();
    
  }

  /**
   * delete batch
   * @param gcGrouperSyncMembers
   * @return rows deleted (members and logs)
   */
  public int memberDelete(Collection gcGrouperSyncMembers, boolean deleteMemberships, boolean deleteLogs) {
    int count = 0;
  
    if (GrouperClientUtils.length(gcGrouperSyncMembers) == 0) {
      return 0;
    }
    
    List> batchBindVars = new ArrayList>();
    
    Set memberSyncIds = new HashSet();
    
    for (GcGrouperSyncMember gcGrouperSyncMember : gcGrouperSyncMembers) {
      
      List currentBindVarRow = new ArrayList();
      currentBindVarRow.add(gcGrouperSyncMember.getId());
      batchBindVars.add(currentBindVarRow);
      
      memberSyncIds.add(gcGrouperSyncMember.getId());
      this.internal_memberCacheDelete(gcGrouperSyncMember);
    }
  
    String connectionName = gcGrouperSyncMembers.iterator().next().getConnectionName();
    
    if (deleteLogs) {
      count += this.getGcGrouperSync().getGcGrouperSyncLogDao().internal_logDeleteBatchByOwnerIds(memberSyncIds);
    }
    
    if (deleteMemberships) {
      count += this.getGcGrouperSync().getGcGrouperSyncMembershipDao().membershipDeleteBySyncMemberIds(memberSyncIds, deleteLogs);
    }

    int[] rowDeleteCounts = new GcDbAccess().connectionName(connectionName).sql("delete from grouper_sync_member where id = ?")
      .batchBindVars(batchBindVars).batchSize(this.getGcGrouperSync().batchSize()).executeBatchSql();
  
    for (int rowDeleteCount : rowDeleteCounts) {
      count += rowDeleteCount;
    }
  
    return count;
    
  
  }

  /**
   * delete sync member
   * @param gcGrouperSyncMember
   * @return rows deleted (members and logs)
   */
  public int memberDelete(GcGrouperSyncMember gcGrouperSyncMember, boolean deleteMemberships, boolean deleteLogs ) {
    
    if (gcGrouperSyncMember == null) {
      return 0;
    }
    
    this.internal_memberCacheDelete(gcGrouperSyncMember);

    int count = 0;
    
    if (deleteLogs) {
      count += this.getGcGrouperSync().getGcGrouperSyncLogDao().logDeleteByOwnerId(gcGrouperSyncMember.getId());
    }

    if (deleteMemberships) {
      count += this.getGcGrouperSync().getGcGrouperSyncMembershipDao().membershipDeleteBySyncMemberId(gcGrouperSyncMember.getId(), deleteLogs);
    }

    int rowDeleteCount = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName()).sql("delete from grouper_sync_member where id = ?")
      .bindVars(gcGrouperSyncMember.getId()).executeSql();
    
    count += rowDeleteCount;
      
    return count;

  }

  /**
   * delete all members for a sync
   * @param deleteMemberships true if delete memberships and logs for memberships too
   * @param deleteLogs delete logs too
   * @return the syncs
   */
  public int memberDeleteAll(boolean deleteMemberships, boolean deleteLogs) {
    this.internalCacheSyncMembers.clear();
    this.internalCacheSyncMembersById.clear();
    
    int rowDeleteCount = 0;
    
    if (deleteLogs) {
      rowDeleteCount += new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName()).sql(
        "delete from grouper_sync_log where grouper_sync_owner_id in ( select id from grouper_sync_member gsg where gsg.grouper_sync_id = ?)")
        .bindVars(this.getGcGrouperSync().getId()).executeSql();
    }
    
    if (deleteMemberships) {
      if (deleteLogs) {
        rowDeleteCount += new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName()).sql(
            "delete from grouper_sync_log where grouper_sync_owner_id in ( select id from grouper_sync_membership gsm where gsm.grouper_sync_id = ?)")
            .bindVars(this.getGcGrouperSync().getId()).executeSql();
      }
      rowDeleteCount += new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName()).sql(
          "delete from grouper_sync_membership where grouper_sync_id = ?")
          .bindVars(this.getGcGrouperSync().getId()).executeSql();
    }
    rowDeleteCount += new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName()).sql(
        "delete from grouper_sync_member where grouper_sync_id = ?")
        .bindVars(this.getGcGrouperSync().getId()).executeSql();
    
    return rowDeleteCount;
  }

  /**
   * select grouper sync member by sync id and member id
   * @param grouperSyncId
   * @param provisionerName
   * @return the syncs
   */
  public List memberRetrieveAll() {
    if (!this.memberRetrievedAllObjectsFromDb) {
      for (GcGrouperSyncMember gcGrouperSyncMember : this.internal_memberRetrieveFromDbAll()) {
        this.internal_memberCacheAdd(gcGrouperSyncMember);
      }
      this.memberRetrievedAllObjectsFromDb = true;
    }
    return new ArrayList(this.internalCacheSyncMembers.values());
  }

  /**
   * select grouper sync member by member id
   * @param connectionName
   * @param memberId
   * @return the member
   */
  public GcGrouperSyncMember memberRetrieveByMemberId(String memberId) {
    GcGrouperSyncMember gcGrouperSyncMember = this.internalCacheSyncMembers.get(memberId);
    if (gcGrouperSyncMember == null) {
      gcGrouperSyncMember = internal_memberRetrieveFromDbByMemberId(memberId);
    }
    return gcGrouperSyncMember;
  }

  /**
   * select grouper sync member by sync id and member id
   * @param grouperSyncId
   * @param grouperMemberIdsCollection
   * @param provisionerName
   * @return the memberId to syncMember map
   */
  public Map memberRetrieveByMemberIds(
      Collection grouperMemberIdsCollection) {
  
    Map result = new HashMap();
    
    Set memberIdsToGetFromDb = new HashSet();
    
    // try from cache
    for (String memberId : GrouperClientUtils.nonNull(grouperMemberIdsCollection)) {
      GcGrouperSyncMember gcGrouperSyncMember = this.internalCacheSyncMembers.get(memberId);
      if (gcGrouperSyncMember != null) {
        result.put(memberId, gcGrouperSyncMember);
      } else {
        memberIdsToGetFromDb.add(memberId);
      }
    }
    
    // or else get from db
    if (memberIdsToGetFromDb.size() > 0) {
      Map fromDb = internal_memberRetrieveFromDbByMemberIds(memberIdsToGetFromDb);
      result.putAll(fromDb);
    }
    
    return result;
    
  }

  /**
   * select grouper sync member by member id
   * @param gcGrouperSyncMemberId
   * @return the member
   */
  public GcGrouperSyncMember memberRetrieveById(String gcGrouperSyncMemberId) {
    GcGrouperSyncMember gcGrouperSyncMember = this.internalCacheSyncMembersById.get(gcGrouperSyncMemberId);
    if (gcGrouperSyncMember == null) {
      gcGrouperSyncMember = internal_memberRetrieveFromDbById(gcGrouperSyncMemberId);
    }
    return gcGrouperSyncMember;
  }
  
  /**
   * select grouper sync members by ids
   * @param gcGrouperSyncMemberIds
   * @return map of ids to gcGrouperSyncMembers
   */
  public Map memberRetrieveByIds(Collection gcGrouperSyncMemberIds) {
    
    Map result = new HashMap();
    
    Set memberIdsToGetFromDb = new HashSet();
    
    // try from cache
    for (String gcGrouperSyncMemberId : GrouperClientUtils.nonNull(gcGrouperSyncMemberIds)) {
      GcGrouperSyncMember gcGrouperSyncMember = this.internalCacheSyncMembersById.get(gcGrouperSyncMemberId);
      if (gcGrouperSyncMember != null) {
        result.put(gcGrouperSyncMemberId, gcGrouperSyncMember);
      } else {
        memberIdsToGetFromDb.add(gcGrouperSyncMemberId);
      }
    }
    
    // or else get from db
    if (memberIdsToGetFromDb.size() > 0) {
      Map fromDb = internal_memberRetrieveFromDbByIds(memberIdsToGetFromDb);
      result.putAll(fromDb);
    }
    
    return result;
  }

  /**
   * select grouper sync member by member id.  Note: this doesnt store to db yet, you do that at the end
   * @param connectionName
   * @param memberId
   * @return the member
   */
  public GcGrouperSyncMember memberRetrieveOrCreateByMemberId(String memberId) {
    GcGrouperSyncMember gcGrouperSyncMember = this.memberRetrieveByMemberId(memberId);
    if (gcGrouperSyncMember == null) {
      gcGrouperSyncMember = this.memberCreateByMemberId(memberId);
    }
    return gcGrouperSyncMember;
  }

  /**
   * select grouper sync member by member id.  note, this does not store the members to the database, do that later
   * @param grouperSyncId
   * @param grouperMemberIdsCollection
   * @param provisionerName
   * @return the memberId to syncMember map
   */
  public Map memberRetrieveOrCreateByMemberIds(
      Collection grouperMemberIdsCollection) {
  
    Map result = this.memberRetrieveByMemberIds(grouperMemberIdsCollection);
  
    // if done, return
    if (GrouperClientUtils.length(grouperMemberIdsCollection) == 0 || grouperMemberIdsCollection.size() == result.size()) {
      return result;
    }
    
    Set memberIdsToCreate = new HashSet(grouperMemberIdsCollection);
    memberIdsToCreate.removeAll(result.keySet());
    
    Set syncMembersToStore = new HashSet();
    
    for (String memberIdToCreate : memberIdsToCreate) {
      GcGrouperSyncMember gcGrouperSyncMember = this.internal_memberCreateByMemberIdHelper(memberIdToCreate);
      result.put(memberIdToCreate, gcGrouperSyncMember);
      syncMembersToStore.add(gcGrouperSyncMember);
    }
    
    int changes = this.internal_memberStore(syncMembersToStore);
    this.gcGrouperSync.addObjectCreatedCount(changes);

    for (GcGrouperSyncMember gcGrouperSyncMember : syncMembersToStore) {
      this.internal_memberCacheAdd(gcGrouperSyncMember);
    }

    return result;
    
  }

  /**
   * 
   * @param gcGrouperSyncMemberId
   * @return log
   */
  public GcGrouperSyncLog memberCreateLog(GcGrouperSyncMember gcGrouperSyncMember) {
    return this.gcGrouperSync.getGcGrouperSyncLogDao().logCreateByOwnerId(gcGrouperSyncMember.getId());
  }

  /**
   * 
   * @param gcGrouperSyncMember
   */
  private void internal_memberCacheAdd(GcGrouperSyncMember gcGrouperSyncMember) {
    if (gcGrouperSyncMember.getMemberId() != null) {
      this.internalCacheSyncMembers.put(gcGrouperSyncMember.getMemberId(), gcGrouperSyncMember);
    }
    if (gcGrouperSyncMember.getId() != null) { 
      this.internalCacheSyncMembersById.put(gcGrouperSyncMember.getId(), gcGrouperSyncMember);
    }
  }

  /**
   * 
   * @param gcGrouperSyncMember
   */
  public void internal_memberCacheDelete(GcGrouperSyncMember gcGrouperSyncMember) {
    if (gcGrouperSyncMember.getMemberId() != null) {
      this.internalCacheSyncMembers.remove(gcGrouperSyncMember.getMemberId());
    }
    if (gcGrouperSyncMember.getId() != null) {
      this.internalCacheSyncMembersById.remove(gcGrouperSyncMember.getId());
    }
    
  }

  /**
   * select grouper sync member by sync id and member id
   * @param grouperSyncId
   * @param provisionerName
   * @return the syncs
   */
  public List internal_memberRetrieveFromDbAll() {
    
    // clear the cache
    this.internalCacheSyncMembers.clear();
    this.internalCacheSyncMembersById.clear();
    
    List gcGrouperSyncMemberList = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
        .sql("select * from grouper_sync_member where grouper_sync_id = ?").addBindVar(this.getGcGrouperSync().getId()).selectList(GcGrouperSyncMember.class);
    
    for (GcGrouperSyncMember gcGrouperSyncMember : gcGrouperSyncMemberList) {
      gcGrouperSyncMember.setGrouperSync(this.getGcGrouperSync());
      this.internal_memberCacheAdd(gcGrouperSyncMember);
  
    }
    return gcGrouperSyncMemberList;
  }
  
  public List internal_memberRetrieveFromDbDeletables() {
    
    List gcGrouperSyncMemberList = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
        .sql("select * from grouper_sync_member gsm where grouper_sync_id = ? and provisionable = 'F' and in_target = 'F' and not exists "
            + "( select  1 from grouper_sync_membership gsmem where gsmem.grouper_sync_member_id = gsm.id and (gsmem.in_target is null or gsmem.in_target = 'F') ) ").addBindVar(this.getGcGrouperSync().getId()).selectList(GcGrouperSyncMember.class);
    
    for (GcGrouperSyncMember gcGrouperSyncMember : gcGrouperSyncMemberList) {
      gcGrouperSyncMember.setGrouperSync(this.getGcGrouperSync());
    }
    return gcGrouperSyncMemberList;
    
  }

  /**
   * select grouper sync member by member id
   * @param connectionName
   * @param memberId
   * @return the member
   */
  public GcGrouperSyncMember internal_memberRetrieveFromDbByMemberId(String memberId) {
    
    GcGrouperSyncMember gcGrouperSyncMember = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
        .sql("select * from grouper_sync_member where grouper_sync_id = ? and member_id = ?")
          .addBindVar(this.getGcGrouperSync().getId()).addBindVar(memberId).select(GcGrouperSyncMember.class);
    if (gcGrouperSyncMember != null) {
      gcGrouperSyncMember.setGrouperSync(this.getGcGrouperSync());
      this.internal_memberCacheAdd(gcGrouperSyncMember);
    }
    return gcGrouperSyncMember;
    
  }

  /**
   * select grouper sync member by sync id and member id
   * @param grouperSyncId
   * @param grouperMemberIdsCollection
   * @param provisionerName
   * @return the memberId to syncMember map
   */
  public Map internal_memberRetrieveFromDbByMemberIds(Collection grouperMemberIdsCollection) {
    
    Map result = new HashMap();
    
    if (GrouperClientUtils.length(grouperMemberIdsCollection) == 0) {
      return result;
    }
    
    List memberIdsList = new ArrayList(grouperMemberIdsCollection);
    
    int batchSize = this.getGcGrouperSync().maxBindVarsInSelect();
    int numberOfBatches = GrouperClientUtils.batchNumberOfBatches(memberIdsList, batchSize);
    
    for (int batchIndex = 0; batchIndex batchOfMemberIds = GrouperClientUtils.batchList(memberIdsList, batchSize, batchIndex);
      
      String sql = "select * from grouper_sync_member where grouper_sync_id = ? and member_id in ( " 
          + GrouperClientUtils.appendQuestions(batchOfMemberIds.size()) + ")";
      GcDbAccess gcDbAccess = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
          .sql(sql).addBindVar(this.getGcGrouperSync().getId());
      for (String memberId : batchOfMemberIds) {
        gcDbAccess.addBindVar(memberId);
      }
      
      List gcGrouperSyncMembers = gcDbAccess.selectList(GcGrouperSyncMember.class);
      
      for (GcGrouperSyncMember gcGrouperSyncMember : GrouperClientUtils.nonNull(gcGrouperSyncMembers)) {
        result.put(gcGrouperSyncMember.getMemberId(), gcGrouperSyncMember);
        gcGrouperSyncMember.setGrouperSync(this.getGcGrouperSync());
        this.internal_memberCacheAdd(gcGrouperSyncMember);
      }
      
    }
    return result;
  }

  /**
   * select grouper sync member by gcGrouperSyncMemberId id
   * @param gcGrouperSyncMemberId
   * @return the gcGrouperSyncMember
   */
  public GcGrouperSyncMember internal_memberRetrieveFromDbById(String gcGrouperSyncMemberId) {
    
    GcGrouperSyncMember gcGrouperSyncMember = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
        .sql("select * from grouper_sync_member where id = ?")
          .addBindVar(gcGrouperSyncMemberId).select(GcGrouperSyncMember.class);
    if (gcGrouperSyncMember != null) {
      gcGrouperSyncMember.setGrouperSync(this.getGcGrouperSync());
      this.internal_memberCacheAdd(gcGrouperSyncMember);
    }
    return gcGrouperSyncMember;
    
  }

  /**
   * select grouper sync member by sync id and member id
   * @param grouperSyncId
   * @param syncMemberIdsCollection
   * @param provisionerName
   * @return the id to syncMember map
   */
  public Map internal_memberRetrieveFromDbByIds(Collection syncMemberIdsCollection) {
    
    Map result = new HashMap();
    
    if (GrouperClientUtils.length(syncMemberIdsCollection) == 0) {
      return result;
    }
    
    List syncIdsList = new ArrayList(syncMemberIdsCollection);
    
    int batchSize = this.getGcGrouperSync().maxBindVarsInSelect();
    int numberOfBatches = GrouperClientUtils.batchNumberOfBatches(syncIdsList, batchSize);
    
    for (int batchIndex = 0; batchIndex batchOfSyncIds = GrouperClientUtils.batchList(syncIdsList, batchSize, batchIndex);
      
      String sql = "select * from grouper_sync_member where grouper_sync_id = ? and id in ( " 
          + GrouperClientUtils.appendQuestions(batchOfSyncIds.size()) + ")";
      GcDbAccess gcDbAccess = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
          .sql(sql).addBindVar(this.getGcGrouperSync().getId());
      for (String memberId : batchOfSyncIds) {
        gcDbAccess.addBindVar(memberId);
      }
      
      List gcGrouperSyncMembers = gcDbAccess.selectList(GcGrouperSyncMember.class);
      
      for (GcGrouperSyncMember gcGrouperSyncMember : GrouperClientUtils.nonNull(gcGrouperSyncMembers)) {
        result.put(gcGrouperSyncMember.getId(), gcGrouperSyncMember);
        gcGrouperSyncMember.setGrouperSync(this.getGcGrouperSync());
        this.internal_memberCacheAdd(gcGrouperSyncMember);
      }
      
    }
    return result;
  }

  /**
   * 
   */
  private GcGrouperSync gcGrouperSync;
  /**
   * if all objects have been retrieved from db
   */
  @GcPersistableField(persist=GcPersist.dontPersist)
  private boolean memberRetrievedAllObjectsFromDb = false;
  
  
  
  /**
   * 
   * @return
   */
  public GcGrouperSync getGcGrouperSync() {
    return gcGrouperSync;
  }

  /**
   * 
   * @param gcGrouperSync
   */
  public void setGcGrouperSync(GcGrouperSync gcGrouperSync) {
    this.gcGrouperSync = gcGrouperSync;
  }

  /**
   * 
   * @return number of members stored
   */
  public int internal_memberStoreAll() {
    return this.internal_memberStore(this.internalCacheSyncMembers.values());
  }
  
  /**
   * store batch, generally call this from store all objects from GcGrouperSync
   * @param gcGrouperSyncMembers
   * @return number of changes
   */
  public int internal_memberStore(Collection gcGrouperSyncMembers) {
  
    if (GrouperClientUtils.length(gcGrouperSyncMembers) == 0) {
      return 0;
    }
  
    int batchSize = this.getGcGrouperSync().batchSize();
  
    List gcGrouperSyncMembersList = new ArrayList(gcGrouperSyncMembers);
    
    for (GcGrouperSyncMember gcGrouperSyncMember : GrouperClientUtils.nonNull(gcGrouperSyncMembers)) {
      gcGrouperSyncMember.storePrepare();
    }
  
    int changes = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName()).storeBatchToDatabase(gcGrouperSyncMembersList, batchSize);
    
    for (GcGrouperSyncMember gcGrouperSyncMember : GrouperClientUtils.nonNull(gcGrouperSyncMembers)) {
      this.internal_memberCacheAdd(gcGrouperSyncMember);
    }
    return changes;
  }

  /**
   * store batch, generally call this from store all objects from GcGrouperSync
   * @param gcGrouperSyncMembers
   */
  public void internal_memberStore(GcGrouperSyncMember gcGrouperSyncMember) {
  
    gcGrouperSyncMember.storePrepare();
  
    new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName()).storeToDatabase(gcGrouperSyncMember);
  
  }

  /**
   * get member ids with errors after error timestamp
   * @param errorTimestampCheckFrom if null get all
   * @return member ids
   */
  public List retrieveMemberIdsWithErrorsAfterMillis(Timestamp errorTimestampCheckFrom) {
    // don't pull errors unless the entity is in the target or the entity is provisionable or entity has at least one membership in a provsionable group 
    GcDbAccess gcDbAccess = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
        .sql("select member_id from grouper_sync_member gsm where grouper_sync_id = ? and error_code is not null " + (errorTimestampCheckFrom == null ? " and error_timestamp is not null" : " and error_timestamp >= ? and"
            + " (in_target = 'T' or provisionable = 'T' or exists (select 1 from grouper_sync_membership gsmem where gsmem.grouper_sync_member_id = gsm.id and gsmem.error_code is null ) )"))
        .addBindVar(this.getGcGrouperSync().getId());
    if (errorTimestampCheckFrom != null) {
      gcDbAccess.addBindVar(errorTimestampCheckFrom);
    }
    List memberIds = gcDbAccess.selectList(String.class);
    return memberIds;
  }
  
  /**
   * get count of rows per error code
   * @return
   */
  public Map retrieveErrorCountByCode() {
    
    GcDbAccess gcDbAccess = new GcDbAccess().connectionName(this.getGcGrouperSync().getConnectionName())
        .sql("select error_code, count(*) from grouper_sync_member where grouper_sync_id = ? and error_code is not null group by error_code")
        .addBindVar(this.getGcGrouperSync().getId());
    Map errorCount = gcDbAccess.selectMapMultipleRows(String.class, Integer.class);
    return errorCount;
  }

}