What is @StaticMetamodel and SingularAttribute<Obj,Obj>?

As per documentation:

A static metamodel is a series of classes that "mirror" the entities and embeddables in the domain model and provide static access to the metadata about the mirrored class's attributes.

The static metamodel has the following properties:

  • For each managed class X in package p, a metamodel class X_ in package p is created.
  • For every persistent non-collection-valued attribute y declared by class X, where the type of y is Y, the metamodel class must contain a declaration as follows:

SingularAttribute example:

public static volatile SingularAttribute<X, Y> y;

The static metamodel is useful for creating type-safe queries with the JPA's Criteria API.

For example, let's have the following two entities, Order and Item:

@Entity
public class Order {
    @Id 
    @GeneratedValue
    Integer id;

    @ManyToOne 
    Customer customer;

    @OneToMany 
    Set<Item> items;

    BigDecimal totalCost;

    // accessors
}

and the Item entity:

@Entity  
public class Item { 
    @Id
    @GeneratedValue
    Integer id;

    int quantity;

    @ManyToOne
    Order order;

    // accessors
}

Here's a typesafe criteria query, build with the Criteria API:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
SetJoin<Order, Item> itemNode = cq.from(Order.class).join(Order_.items);
cq.where(cb.equal(itemNode.get(Item_.id), 5)).distinct(true);

Note the usage of Item_.id and Order_.item. Those access statically the static meta-model properties (which mirror the entity properties) and this way it's ensured that the query is build properly.


I've been thinking about this a lot lately as I've been trying to learn and understand JPA. I believe I have an answer to your question: Why do we need MetaModels, and why can't we just use the Entity Model?

Take a look at this entity:

@Entity  
public class Item { 
    @Id
    @GeneratedValue
    Integer id;

    int quantity;

    @ManyToOne
    Order order;

    // accessors
}

Note that none of the properties on the Entity have the keyword static. That means that in order to use them, we need to create a new Object.

When we are building queries with CriteriaBuilder, we don't need to create an object... we just want to use the properties on the Entity to generate our query. This is the reason we have MetaModels! They create static properties that we can access without having to create an object. So we can can do things like Konstantin mentioned:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
SetJoin<Order, Item> itemNode = cq.from(Order.class).join(Order_.items);
cq.where(cb.equal(itemNode.get(Item_.id), 5)).distinct(true);

Here, we aren't making an "Item" object... we just need to know the properties of it. The static properties on the MetaModel enable us to do so!