Close

JPA Criteria API - Selecting java.util.Map key/values/entries via MapJoin

[Updated: Dec 11, 2018, Created: Oct 8, 2018]

In JPA Criteria API the map relations can be accessed via MapJoin interface.

Following are two of the various methods of From interface (extended by Root interface) which can be used to obtain MapJoin instance:

MapJoin<X, K, V> join(MapAttribute<? super X, K, V> mapAttribute);
MapJoin<X, K, V> joinMap(String mapAttributeName);

Following is MapJoin interface snippet:

package javax.persistence.criteria;
 ....
public interface MapJoin<Z, K, V> extends PluralJoin<Z, Map<K, V>, V> {
    MapJoin<Z, K, V> on(Expression<Boolean> var1);
    MapJoin<Z, K, V> on(Predicate... var1);
    MapAttribute<? super Z, K, V> getModel();
    Path<K> key();
    Path<V> value();
    Expression<Entry<K, V>> entry();
}

Quick Example:

  CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
  CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
  Root<Customer> customer = query.from(Customer.class);
  MapJoin<Customer, String, Order> orderMap = customer.join(Customer_.orderMap);
  query.multiselect(customer.get(Customer_.name), orderMap.key(),
                orderMap.value().get(Order_.item), orderMap.value().get(Order_.qty));
  TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
  List<Tuple> list = typedQuery.getResultList();

Example

Entities

In following example we are not using Hibernate as it has a bug while using MapJoin#entry (HHH-12945). we are, instead, going to use EclipseLink as JPA provider.

@Entity
public class Customer {
  @Id
  @GeneratedValue
  private int id;
  private String name;
  @OneToMany(cascade = CascadeType.ALL)
  private Map<String, Order> orderMap;//orderType to Order map
    .............
  public void addOrder(String orderType, String itemName, int qty) {
      if (orderMap == null) {
          orderMap = new HashMap<>();
      }
      Order order = new Order();
      order.setItem(itemName);
      order.setQty(qty);
      orderMap.put(orderType, order);
  }
    .............
}
@Entity
@Table(name="CustomerOrder")
public class Order {
  @Id
  @GeneratedValue
  private long id;
  private String item;
  private int qty;
    .............
}

Using MapJoin

public class ExampleMain {

  public static void main(String[] args) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit");
      try {
          persistEntity(emf);
          findItemQty(emf);
          findOrderMapEntry(emf);
      } finally {
          emf.close();
      }
  }

  private static void persistEntity(EntityManagerFactory emf) {
      System.out.println("-- Persisting entities --");
      EntityManager em = emf.createEntityManager();

      Customer c1 = new Customer();
      c1.setName("Lindsey Craft");
      c1.addOrder("online", "XYZ Blender", 2);
      c1.addOrder("store", "ZZZ Beer Glass", 4);
      System.out.println(c1);

      Customer c2 = new Customer();
      c2.setName("Morgan Philips");
      c2.addOrder("online", "AA Glass Cleaner", 3);
      System.out.println(c2);

      em.getTransaction().begin();
      em.persist(c1);
      em.persist(c2);
      em.getTransaction().commit();
      em.close();
  }

  private static void findItemQty(EntityManagerFactory emf) {
      System.out.println("-- Finding items and keys --");
      EntityManager entityManager = emf.createEntityManager();
      CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
      CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
      Root<Customer> customer = query.from(Customer.class);
      MapJoin<Customer, String, Order> orderMap = customer.join(Customer_.orderMap);
      query.multiselect(customer.get(Customer_.name), orderMap.key(),
              orderMap.value().get(Order_.item), orderMap.value().get(Order_.qty));
      TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
      typedQuery.getResultList().forEach(
              t -> System.out.printf("Customer: %s, Order-Type: %s, Order-item: %s, Order-qty: %s%n",
                      t.get(0), t.get(1), t.get(2), t.get(3)));
  }

  private static void findOrderMapEntry(EntityManagerFactory emf) {
      System.out.println("-- Finding orderType/order entries  --");
      EntityManager entityManager = emf.createEntityManager();
      CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
      CriteriaQuery<Map.Entry> query = criteriaBuilder.createQuery(Map.Entry.class);
      Root<Customer> customer = query.from(Customer.class);
      MapJoin<Customer, String, Order> orderMap = customer.join(Customer_.orderMap);
      query.select(orderMap.entry());
      TypedQuery<Map.Entry> typedQuery = entityManager.createQuery(query);
      typedQuery.getResultList()
                .forEach(entry -> {System.out.printf("%s = %s%n", entry.getKey(), entry.getValue());});
  }
}
-- Persisting entities --
Customer{id=0, name='Lindsey Craft', orderMap={online=Order{id=0, item='XYZ Blender', qty=2}, store=Order{id=0, item='ZZZ Beer Glass', qty=4}}}
Customer{id=0, name='Morgan Philips', orderMap={online=Order{id=0, item='AA Glass Cleaner', qty=3}}}
-- Finding items and keys --
Customer: Morgan Philips, Order-Type: online, Order-item: AA Glass Cleaner, Order-qty: 3
Customer: Lindsey Craft, Order-Type: online, Order-item: XYZ Blender, Order-qty: 2
Customer: Lindsey Craft, Order-Type: store, Order-item: ZZZ Beer Glass, Order-qty: 4
-- Finding orderType/order entries --
online = Order{id=5, item='AA Glass Cleaner', qty=3}
online = Order{id=2, item='XYZ Blender', qty=2}
store = Order{id=3, item='ZZZ Beer Glass', qty=4}

See also how to do the same thing in JPQL.

Example Project

Dependencies and Technologies Used:

  • h2 1.4.197: H2 Database Engine.
  • eclipselink 2.6.5: EclipseLink build based upon Git transaction b3d05bd.
    Related JPA version: org.eclipse.persistence:javax.persistence version 2.1.1
  • org.eclipse.persistence.jpa.modelgen.processor 2.6.5: EclipseLink build based upon Git transaction b3d05bd.
  • JDK 1.8
  • Maven 3.5.4

JPA Criteria API - MapJoin Examples Select All Download
  • jpa-criteria-api-map-join
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ExampleMain.java
          • resources
            • META-INF

    See Also