null

Использование Metamodel API для создания Criteria API запросов

Статья о том, как выполнять typesafe Criteria API запросы при помощи файлов метамодели. 

Зачастую, при работе с Criteria API программисты обращаются к столбцам напрямую через строку название столбца, например order.get(“name”). Такие запросы как-либо статично проверяться компилятором на корректность, поэтому любая опечатка может “сломать” запрос из-за отсутствия колонки с таким названием в момент исполнения запроса . Хорошо еще, если программист в этот момент тестировал именно этот запрос, благодаря чему мог сразу заметить и исправить ошибку. В худшем случае, например, при смене названия колонки таблицы, заметить в недрах кода захардкоженное название столбца в запросе не так-то просто.

Поэтому при использовании Criteria API для обращения к столбцам таблицы используются файлы метамодели - серии классов, которые «отражают» сущности и обеспечивают статический доступ к метаданным об атрибутах зеркального класса. Эти файлы генерируются hibernate’ом при компиляции на основе вашей предметной модели и отличить их можно по символу _ в конце названия класса. Например, для таблицы order и entity класса Order, hibernate автоматически сгенерирует абстрактный класс с описанием модели таблицы Order с названием Order_.class. В этом классе будут описания названий всех полей и их типы.
 

Пример: 

Объект, описывающий таблицу заказа

@Entity
@Table(name = "order")
public class Order {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private Double price;
    private Integer quantityOfGoods;

    // Getters and setters code
}

Сгенерированный класс метамодели:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Order.class)
public abstract class Order_ {
    public static volatile SingularAttribute<Order, String> name;
    public static volatile SingularAttribute<Order, Double> price;
    public static volatile SingularAttribute<Order, Integer> quantityOfGoods;

    public static final String NAME = "name";
    public static final String PRICE = "price";
    public static final String QUANTITY_OF_GOODS = "quantityOfGoods";

}

Query Roots для конкретного объекта Criteria Query равносилен FROM языков запроса - это объект, к которому применяются все join’ы и из которого происходит выборка полей сущности. Для запросов к таблице order: query root - это Order.class. 

Предикат WHERE для точного поиска по имени в самом простом виде выглядит так:

criteriaBuilder.equal(order.get("name"), "Вася Пупкин")

Именно в этом месте и происходят ошибки. Если столбец name будет переименован или программист отпечатается, компилятор успешно скомпилирует программу и не укажет на отсутствие столбца. Такая проверка просто не производится. Все будет идти своим чередом до прямого выполнения этого запроса, после чего может произойти ошибка выполнения запроса. Для избежания таких ошибок проверки существования столбцов можно использовать файлы метамодели.

В общем случае, использование метамодели позволяет обращаться к полям сущности (столбцам таблицы):

criteriaBuilder.equal(order.get(Order_.name), "Вася Пупкин")

- в этом случае будет проводится статическая проверка и на уровне вашего средства разработки и в момент компиляции. 

Также класс метамодели можно создать и программным путем:

EntityManager em = ...;
Metamodel m = em.getMetamodel();
EntityType<Order> Order_ = m.entity(Order.class);
Root<Order> order = cq.from(Order_);