Changeset 8130


Ignore:
Timestamp:
Mar 21, 2023, 11:10:53 AM (7 months ago)
Author:
Nicklas Nordborg
Message:

References #2296: Improve performance when loading many any-to-any links

Added AnyToAny.getFastLoader() which returns a FastLinkLoader instance for loading any-to-any links with fixed link name between source and target item types.

The fast loaded need to be pre-loaded with items before it can be used. The simplest way is to use FastLinkLoader.preloadAll() but there is also an alternative that allows a subset of the links to be pre-loaded.

Location:
branches/3.19-stable/src/core/net/sf/basedb/core
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/3.19-stable/src/core/net/sf/basedb/core/AnyToAny.java

    r7853 r8130  
    2525import java.util.ArrayList;
    2626import java.util.Collections;
     27import java.util.HashMap;
     28import java.util.Iterator;
    2729import java.util.List;
     30import java.util.Map;
    2831
    2932import org.hibernate.mapping.PersistentClass;
     
    3134
    3235import net.sf.basedb.core.data.AnyToAnyData;
     36import net.sf.basedb.core.data.BasicData;
    3337import net.sf.basedb.core.hibernate.TypeWrapper;
    3438import net.sf.basedb.core.query.Expressions;
    3539import net.sf.basedb.core.query.Hql;
     40import net.sf.basedb.core.query.IdListRestriction;
     41import net.sf.basedb.core.query.Restriction;
    3642import net.sf.basedb.core.query.Restrictions;
    3743import net.sf.basedb.core.signal.ThreadSignalHandler;
     
    253259    );
    254260    return query;
     261  }
     262 
     263  /**
     264    Create an optimized implementation for loading the target item of a given named link from
     265    multiple source items. When the source item type, target item type and link name is known
     266    this implementation can be used to load the target items much more quickly than when loaded
     267    one by one. The {@link FastLinkLoader#get(BasicItem)} is equivalent to
     268    {@link AnyToAny#getByName(DbControl, BasicItem, String)#getTo()}.
     269   
     270    @since 3.19.8
     271  */
     272  public static <S extends BasicItem, T extends BasicItem> FastLinkLoader<S, T> getFastLoader(DbControl dc, Item fromType, String linkName, Item toType)
     273  {
     274    return new FastLinkLoader<>(dc, fromType, linkName, toType);
    255275  }
    256276 
     
    742762  }
    743763
    744  
     764  /**
     765    Helper class for faster loading of any-to-any links between items.
     766    A single instance have a fixed source item type, link name and target
     767    item type. Before the loader can be used at least one of the <code>preload()</code>
     768    methods should be used.
     769   
     770    @since 3.19.8
     771  */
     772  public static class FastLinkLoader<S extends BasicItem, T extends BasicItem>
     773  {
     774   
     775    private final DbControl dc;
     776    private final Item fromType;
     777    private final Item toType;
     778    private final String linkName;
     779   
     780    private final Map<Integer, AnyToAnyData> links;
     781    private final Map<Integer, BasicData> targets;
     782   
     783    FastLinkLoader(DbControl dc, Item fromType, String linkName, Item toType)
     784    {
     785      this.dc = dc;
     786      this.fromType = fromType;
     787      this.toType = toType;
     788      this.linkName = linkName;
     789      this.links = new HashMap<>();
     790      this.targets = new HashMap<>();
     791    }
     792   
     793    /**
     794      Pre-load all links between source and target item type with the
     795      given link name.
     796      @return This instance
     797    */
     798    public FastLinkLoader<S, T> preloadAll()
     799    {
     800      preload(null, null);
     801      return this;
     802    }
     803   
     804   
     805    /**
     806      Pre-load links between source and target item type with the
     807      given link name and optionally extra restrictions for the any-to-any
     808      link and/or the target item type.
     809      @return This instance
     810    */
     811    public FastLinkLoader<S, T> preload(Restriction linkRestriction, Restriction targetRestriction)
     812    {
     813      // Load all AnyToAny links with the specified linkName between source and target item types
     814      // We store the mapping between source item id and target item id
     815      Map<Integer, Integer> idMap = new HashMap<>();
     816      DataQuery<AnyToAnyData> linkQuery = new DataQuery<>(AnyToAnyData.class, null);
     817      linkQuery.restrict(Restrictions.eq(Hql.property("fromType"), Expressions.integer(fromType.getValue())));
     818      linkQuery.restrict(Restrictions.eq(Hql.property("toType"), Expressions.integer(toType.getValue())));
     819      linkQuery.restrict(Restrictions.eq(Hql.property("name"), Expressions.string(linkName)));
     820      if (linkRestriction != null) linkQuery.restrict(linkRestriction);
     821      Iterator<AnyToAnyData> linkIterator = linkQuery.iterate(dc);
     822      while (linkIterator.hasNext())
     823      {
     824        AnyToAnyData a = linkIterator.next();
     825        idMap.put(a.getFromId(), a.getToId());
     826        links.put(a.getFromId(), a);
     827      }
     828     
     829      // Load the *Data objects of all target items that was found in the previous step
     830      // Store the mapping between target item id and *Data object
     831      Map<Integer, BasicData> targetMap = new HashMap<>(idMap.size());
     832      DataQuery<? extends BasicData> targetQuery = new DataQuery<>(toType.getDataClass(), null);
     833      targetQuery.restrict(new IdListRestriction("id", idMap.values()));
     834      if (targetRestriction != null) targetQuery.restrict(targetRestriction);
     835      Iterator<? extends BasicData> targetIterator = targetQuery.iterate(dc);
     836      while (targetIterator.hasNext())
     837      {
     838        BasicData target = targetIterator.next();
     839        targetMap.put(target.getId(), target);
     840      }
     841     
     842      // Populate the linked items which is a map from source item id to target *Data object
     843      for (Map.Entry<Integer, Integer> e : idMap.entrySet())
     844      {
     845        BasicData target = targetMap.get(e.getValue());
     846        if (target != null) targets.put(e.getKey(), target);
     847      }
     848     
     849      return this;
     850    }
     851   
     852    /**
     853      The source item type.
     854    */
     855    public Item getFromType()
     856    {
     857      return fromType;
     858    }
     859   
     860    /**
     861      The target item type.
     862    */
     863    public Item getToType()
     864    {
     865      return toType;
     866    }
     867   
     868    /**
     869      The link name.
     870    */
     871    public String getLinkName()
     872    {
     873      return linkName;
     874    }
     875   
     876    /**
     877      Number of items that matches.
     878    */
     879    public int size()
     880    {
     881      return targets.size();
     882    }
     883   
     884    /**
     885      Get the any-to-any link.
     886    */
     887    public AnyToAny getLink(S item)
     888    {
     889      AnyToAnyData link = links.get(item.getId());
     890      return link == null ? null : dc.getItem(AnyToAny.class, link);
     891    }
     892   
     893    /**
     894      Get the target item that is linked from the source item by the named link.
     895    */
     896    @SuppressWarnings("unchecked")
     897    public T getTo(S item)
     898    {
     899      BasicData target = targets.get(item.getId());
     900      return target == null ? null : (T)dc.getItem(toType.getItemClass(), target);
     901    }
     902   
     903  }
    745904}
  • branches/3.19-stable/src/core/net/sf/basedb/core/query/IdListRestriction.java

    r7912 r8130  
    5050  public IdListRestriction()
    5151  {
    52     this(false, new HashSet<>());
     52    this("id", false, new HashSet<>());
    5353  }
    5454 
     
    5858  public IdListRestriction(boolean notIn)
    5959  {
    60     this(notIn, new HashSet<>());
     60    this("id", notIn, new HashSet<>());
    6161  }
    6262 
    6363  public IdListRestriction(Set<Integer> idList)
    6464  {
    65     this(false, idList);
     65    this("id", false, idList);
    6666  }
    6767 
     68  /**
     69    @since 3.19.8
     70  */
     71  public IdListRestriction(String idProperty, Collection<Integer> idList)
     72  {
     73    this(idProperty, false, idList);
     74  }
     75
    6876  /**
    6977    @since 3.18
     
    7179  public IdListRestriction(boolean notIn, Set<Integer> idList)
    7280  {
     81    this("id", notIn, idList);
     82  }
     83
     84  /**
     85    @since 3.19.8
     86  */
     87  public IdListRestriction(String idProperty, boolean notIn, Collection<Integer> idList)
     88  {
    7389    this.notIn = notIn;
    7490    this.idList = new HashSet<>(idList);
    75     this.idExpression = Hql.property("id");
     91    this.idExpression = Hql.property(idProperty);
    7692  }
     93
    7794 
    7895  @Override
Note: See TracChangeset for help on using the changeset viewer.