persistence icon indicating copy to clipboard operation
persistence copied to clipboard

Support for loading multiple entities by id

Open sebersole opened this issue 4 years ago • 3 comments

I propose the spec add the capability to load multiple entities by id at one time (multi-load).

Many use cases require loading multiple entities at once. Querying could definitely be an option.

EntityManager em = ...;
List<Product> products = em.createQuery( "select p from Product p where p.id in (:ids)", Product.class )
        .setParameter( "ids", Arrays.asList( 1, 2, 3, 4 )
        .getResultList();

However, that has a few drawbacks - the main one being potentially returning way too much data from the database when many ids are requested and a significant number of them are already managed in the EntityManager.

To really get this full capability with JPA today, a user would need to perform a complex series of calls. E.g.

EntityManager em = ...;
EntityManagerFactory emf = em.getFactory();

int[] ids = new int[] { 1, 2, 3, 4 }
List<Product> products = new ArrayList<>();

for ( Integer id : ids ) {
    // see if it already exists...
    //    NOTE : overly simplistic...
    Product existing = em.getReference( Product.class, id );
    
    if ( emf.getPersistenceUnitUtil().isLoaded( existing ) ) {
        products.add( existing );
    }
    else {
        // we could get fancy here and collect the ids to load from db and issue one query, 
        // but lets do the simple thing here for illustration purposes
        products.add( initializeProxy( product ) );
    }
}

One option for API in JPA would be overloads of the existing #find methods (assuming we don't want multiple #getReference loading):

interface EntityManager {
    ...
    // existing
    <T> T find(Class<T> entityType, Object id);
    <T> T find(Class<T> entityClass, Object id, LockModeType lockMode);
    <T> T find(Class<T> entityClass, Object id, LockModeType lockMode, Map<String, Object> properties);
    <T> T find(Class<T> entityClass, Object id, Map<String, Object> properties); 

    // additions
    <T> List<T> find(Class<T> entityType, Object... ids);
    <T> List<T> find(Class<T> entityClass, Map<String, Object> properties, Object... ids); 
    <T> List<T> find(Class<T> entityClass, LockModeType lockMode, Object... ids);
    <T> List<T> find(Class<T> entityClass, LockModeType lockMode, Map<String, Object> properties, Object... ids); 
}

Possibly accepting List of ids in addition to or instead of:

interface EntityManager {
    ...
    <T> List<T> find(Class<T> entityType, List<?> ids);
    <T> List<T> find(Class<T> entityClass, Map<String, Object> properties, List<?> ids); 
    <T> List<T> find(Class<T> entityClass, LockModeType lockMode, List<?> ids);
    <T> List<T> find(Class<T> entityClass, LockModeType lockMode, Map<String, Object> properties, List<?> ids); 
}

sebersole avatar May 06 '21 15:05 sebersole

This capability almost made it into #383, but with the change of direction there, it's missed out.

Nevertheless, I think we should consider this feature for inclusion in JPA 3.2.

gavinking avatar Aug 10 '23 21:08 gavinking