Начиная с версии 5.0 собственный Hibernate Criteria API признан устаревшим и не развивается. Вместо него рекомендуется использовать JPA Criteria API.
Начиная с версии 5.2 Hibernate Criteria API объявлен deprecated и не рекомендуется к использованию
Несмотря на вышесказанное страшное предупреждение, родной (устаревший) Criteria API мне нравится больше и кажется более выразительным (и менее типобезопасным, да).
Criteria API это механизм, позволяющий описывать запросы к базе данных используя java код и java объекты. Тем самым это решает проблему генерации запросов исходя из состояния программы.Например, мы заранее не знаем, захочет пользователь фильтровать клиенов по возрасту или не захочет. В случае использования HQL или SQL запросов мы вынуждены будем либо писать два варианта запроса, либо модифицировать код запроса (то есть править текст) во время исполнения. В Criteria API это решается простым условным вызовом метода.
Простые запросы
Все запросы в данном примере основываются на модели данных использованной ранее в примерах HQL и SQL.
Для начала запросим список объектов какого-нибудь класса:
1
2
3
4
|
session.createCriteria(Person.class)
.setMaxResults(10)
.list()
.forEach(System.out::println);
|
1
|
Person{firstName='Test', lastName='Testoff', dob=2016-07-19, passport=Passport{series='AS', no='123456', issueDate=2016-07-19, validity=P20Y, owner=Testoff}, primaryAddress=Address{city='Kickapoo', street='Main street', building='1', tenants=Test}, workingPlaces=[Company{name='Acme Ltd', workers=Test}]}
|
Запрос выше полностью аналогичен запросу HQL "from Person". С Criteria также работают и все те вещи, которые работают и с Query: пейджинг, таймауты и т.д.
Разумеется, в Criteria запросах можно и нужно накладывать условия, по которым объекты будут отбираться:
1
2
3
4
|
session.createCriteria(Person.class)
.add(Restrictions.eq("lastName", "Testoff"))
.list()
.forEach(System.out::println);
|
1
|
Person{firstName='Test', lastName='Testoff', dob=2016-07-19, passport=Passport{series='AS', no='123456', issueDate=2016-07-19, validity=P20Y, owner=Testoff}, primaryAddress=Address{city='Kickapoo', street='Main street', building='1', tenants=Test}, workingPlaces=[Company{name='Acme Ltd', workers=Test}]}
|
Существует множество вариантов ограничений, аналогичных тем, что встречаются в SQL: .eq(), .like() , .between(), .in(), .ge() и т.д. Кроме того, вызовы .not() , .and() и .or() (и более сложные, вроде .disjunction()) позволяют делать логические операции над ограничениями:
1
2
3
4
5
6
7
|
session.createCriteria(Person.class)
.add(Restrictions.or(
Restrictions.like("lastName", "Te%"),
Restrictions.eq("firstName", "John")
))
.list()
.forEach(System.out::println);
|
1
|
Person{firstName='Test', lastName='Testoff', dob=2016-07-19, passport=Passport{series='AS', no='123456', issueDate=2016-07-19, validity=P20Y, owner=Testoff}, primaryAddress=Address{city='Kickapoo', street='Main street', building='1', tenants=Test}, workingPlaces=[Company{name='Acme Ltd', workers=Test}]}
|
И поскольку задание условий запроса реализуется просто вызовами функций, речь уже не идёт о поддержки параметризованных запросов. Вы просто передаёте значения в функции, а Hibernate делает всё остальное сам.
Связи и коллекции
Сущности в Hibernate могут иметь связи и отношения друг с другом. И на этим связи и отношения можно ссылаться в Criteria API:
1
2
3
4
5
|
session.createCriteria(Passport.class)
.createCriteria("owner")
.add(Restrictions.eq("lastName", "Testoff"))
.list()
.forEach(System.out::println);
|
1
|
Passport{series='AS', no='123456', issueDate=2016-07-19, validity=P20Y, owner=Testoff}
|
Так в примере выше выбираю все сущности класса Passport и накладываю условие на его поле owner, которое ссылается на класс Person, поле lastName которого и ограничивается условием.
Точно так же можно обратиться и к другой стороне связи, той, где используется коллекция:
1
2
3
4
5
|
session.createCriteria(Person.class)
.createCriteria("workingPlaces")
.add(Restrictions.eq("name", "Acme Ltd"))
.list()
.forEach(System.out::println);
|
1
|
Person{firstName='Test', lastName='Testoff', dob=2016-07-19, passport=Passport{series='AS', no='123456', issueDate=2016-07-19, validity=P20Y, owner=Testoff}, primaryAddress=Address{city='Kickapoo', street='Main street', building='1', tenants=Test}, workingPlaces=[Company{name='Acme Ltd', workers=Test}]}
|
Запросы по примеру
Запросы по примеру выбирают объекты, которые аналогичны заданному:
1
2
3
4
5
6
|
Passport p = new Passport();
p.setSeries("as");
session.createCriteria(Passport.class)
.add(Example.create(p).ignoreCase())
.list()
.forEach(System.out::println);
|
1
|
Passport{series='AS', no='123456', issueDate=2016-07-19, validity=P20Y, owner=Testoff}
|
При формировании непосредственного запроса в базу у объекта образца игнорируются null поля, поля с @Id, поля ассоциаций, поле serialVersionUUID. Дополнительно можно задать, как в примере выше, игнорирование регистра строк, использование like для сравнения строк, список дополнительно исключаемых полей и т.д.
DetachedCriteria
Обычно Criteria создаётся в рамках какой-то конкретной сесии и существует в пределах её persistence context. Однако можно создать Criteria, которая не будет привязана к конкретной Session и не присоединена (detached) к persistence context и может быть, в дальнейшем, использована с любым объектом Session. Отчасти таковое поведение напоминает именованные запросы.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
session.getTransaction().commit();
session.close();
DetachedCriteria dc = DetachedCriteria.forClass(Person.class)
.createCriteria("passport")
.add(Restrictions.eq("series", "AS"));
session = sessionFactory.openSession();
session.beginTransaction();
dc.getExecutableCriteria(session)
.list()
.forEach(System.out::println);
session.getTransaction().commit();
session.close();
|
1
|
Person{firstName='Test', lastName='Testoff', dob=2016-07-19, passport=Passport{series='AS', no='123456', issueDate=2016-07-19, validity=P20Y, owner=Testoff}, primaryAddress=Address{city='Kickapoo', street='Main street', building='1', tenants=Test}, workingPlaces=[Company{name='Acme Ltd', workers=Test}]}
|
DetachedCriteria определяется так же, как обычная Criteria, с той лишь разницей, что её нельзя исполнить. Затем, когда появляется подходящая Session вызовом getExecutableCriteria() DetachedCriteria преобразуется в обычную Criteria, связанную с этой Session и исполняется.
Код примера доступен на github.