# ManyToManyCycleLoader.java

    package org.codefilarete.stalactite.engine.runtime.cycle;

    import java.util.Map;
    import java.util.Set;

    import org.codefilarete.stalactite.engine.EntityPersister;
    import org.codefilarete.stalactite.engine.configurer.onetomany.FirstPhaseCycleLoadListener;
    import org.codefilarete.stalactite.engine.runtime.SecondPhaseRelationLoader;
    import org.codefilarete.stalactite.sql.result.BeanRelationFixer;

    /**
     * Loader in case of same entity type present in entity graph as child of itself : A.b -> B.c -> C.a
     * Sibling is not the purpose of this class.
     *
     * Implemented as such :
     * - very first query reads cycling entity type identifiers
     * - a secondary query is executed to load subgraph with above identifiers
     *
     * @param <SRC> type of root entity graph
     * @param <TRGT> cycling entity type (the one to be loaded)
     * @param <TRGTID> cycling entity identifier type
     *
     */
    public class ManyToManyCycleLoader<SRC, TRGT, TRGTID> extends AbstractCycleLoader<SRC, TRGT, TRGTID> {

    	public ManyToManyCycleLoader(EntityPersister<TRGT, TRGTID> targetPersister) {
    		super(targetPersister);
    	}

    	/**
    	 * Implemented to read very first identifiers of source type
    	 */
    	public FirstPhaseCycleLoadListener<SRC, TRGTID> buildRowReader(String relationName) {
    		return (src, targetId) -> {
    			if (!SecondPhaseRelationLoader.isDefaultValue(targetId)) {
    				ManyToManyCycleLoader.this.currentRuntimeContext.get().addRelationToInitialize(relationName, src, targetId);
    			}
    		};
    	}

    	@Override
    	protected void applyRelationToSource(EntityRelationStorage<SRC, TRGTID> relationStorage,
    										 BeanRelationFixer<SRC, TRGT> beanRelationFixer,
    										 Map<TRGTID, TRGT> targetPerId) {
    		relationStorage.getEntitiesToFulFill().forEach(src -> {
    			Set<TRGTID> trgtids = relationStorage.getRelationToInitialize(src);
    			if (trgtids != null) {
    				trgtids.forEach(targetId -> {
    					// because we loop over all source instances of a recursion process (invoking SQL until no more entities need to be loaded),
    					// and whereas this method is called only for one iteration of the whole process, we may encountered source that are not concerned
    					// by given targetPerId, so we need to check for not null matching, else we would get a null element in target collection
    					if (targetPerId.get(targetId) != null) {
    						beanRelationFixer.apply(src, targetPerId.get(targetId));
    					}
    				});
    			}
    		});
    	}
    }