Статьи

Окончательный список запросов и советов JPA — часть 2

Эта часть продолжается с первой части серии.

JPA: NamedQuery, запросы с датами, предупреждения о методе getSingleResult

Чтобы избежать повторения кодов запросов, повысить производительность и упростить обслуживание запросов, мы можем использовать NamedQueries. NamedQuery использует JPQL в качестве синтаксиса и объявляется в классе сущностей. Легче редактировать запрос после обновления в коде класса.

Если вы хотите выполнить запрос, используя дату в качестве параметра, вы можете отправить только объект даты или передать перечисление, которое будет описывать тип даты (рекомендуется).

Ниже вы узнаете, как создать и использовать @NamedQuery и как выполнять запрос с датой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package com.model;
 
import java.util.Date;
 
import javax.persistence.*;
 
@Entity
@NamedQuery(name='Dog.FindByDateOfBirth', query='select d from Dog d where d.dateOfBirth = :dateOfBirth')
public class Dog {
 
 public static final String FIND_BY_DATE_OF_BIRTH = 'Dog.FindByDateOfBirth';
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
 // get and set
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.model;
 
import java.util.*;
 
import javax.persistence.*;
 
@Entity
@NamedQueries({
  @NamedQuery(name='Person.findByName', query='select p from Person p where p.name = :name'),
  @NamedQuery(name='Person.findByAge', query='select p from Person p where p.age = :age')})
})
public class Person {
 
 public static final String FIND_BY_NAME = 'Person.findByName';
 public static final String FIND_BY_AGE = 'Person.findByAge';
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
        // get and set
 
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.main;
 
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TemporalType;
 
import com.model.Dog;
import com.model.Person;
 
public class Page07 {
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  int age = 70;
  List<Person> personByAge = getPersonByAge(em, 70);
  System.out.println('Found ' + personByAge.size() + ' person(s) with the age of: ' + age);
 
  SimpleDateFormat formatter = new SimpleDateFormat('dd/MM/yyyy');
  Date dateOfBirth = null;
  try {
   dateOfBirth = formatter.parse('10/1/1995');
  } catch (ParseException e) {
   e.printStackTrace();
  }
 
  List<Dog> dogsByDayOfBirth = getDogsByDayOfBirth(em, dateOfBirth);
  System.out.println('Found ' + dogsByDayOfBirth.size() + ' dog with birth date of ' + formatter.format(dateOfBirth));
 
  /*
   * This queries will raise Runtime Exceptions
   *
   * em.createQuery('select p from Person p').getSingleResult(); // NonUniqueResultException
   *
   * em.createQuery('select p from Person p where p.name = 'JJJJ'').getSingleResult(); //NoResultException
   */
 
  CodeGenerator.closeConnection();
 }
 
 @SuppressWarnings('unchecked')
 private static List<Dog> getDogsByDayOfBirth(EntityManager em, Date dateOfBirth) {
  Query query = em.createNamedQuery(Dog.FIND_BY_DATE_OF_BIRTH);
  query.setParameter('dateOfBirth', dateOfBirth, TemporalType.DATE);
 
  return query.getResultList();
 }
 
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonByAge(EntityManager em, int age) {
  Query query = em.createNamedQuery(Person.FIND_BY_AGE);
  query.setParameter('age', age);
 
  return query.getResultList();
 }
}

О коде выше:

  • Если у вас есть только один запрос, вы можете использовать аннотацию @NamedQuery; если у вас более одного запроса, вы можете использовать аннотации @NamedQueries.
  • Когда вы делаете запрос с использованием объекта даты, вы также можете использовать перечисление TemporalType, чтобы детализировать тип даты. Для запросов даты вы можете использовать « java.util.Date » или « java.util.GregorianCalendar ».

getSingleResult ()

Будьте осторожны при использовании этого метода. У него есть особый способ обрабатывать два поведения, которые легко реализовать, и оба поведения вызовут исключение:

  • Найти более одного объекта из результата запроса: NonUniqueResultException
  • Не найдено результата: NoResultException

Вам всегда нужно использовать try / catch, чтобы эти исключения не возникали в производственной среде.

Если вы хотите увидеть эти исключения в режиме реального времени, в приведенном выше коде вы можете найти два прокомментированных запроса; это поднимет исключения ниже:

1
2
3
4
Exception in thread 'main' <span style='text-decoration: underline;'>javax.persistence.NonUniqueResultException</span>: result returns more than one elements
at org.hibernate.ejb.QueryImpl.getSingleResult(<span style='text-decoration: underline;'>QueryImpl.java:287</span>)
Exception in thread 'main' <span style='text-decoration: underline;'>javax.persistence.NoResultException</span>: No entity found for query
at org.hibernate.ejb.QueryImpl.getSingleResult(<span style='text-decoration: underline;'>QueryImpl.java:280</span>)

JPA: NativeQuery, NamedNativeQuery

JPA использует JPQL, который не имеет какой-либо конкретной функции базы данных. Как вы могли бы сделать запрос, вызывающий определенную функцию базы данных, когда JPQL просто предоставляет общие функции между базами данных?

« Выберите p из Person p, где p.name ~ *: name » Этот синтаксис запроса является допустимым запросом для базы данных Postgres; если вы попытаетесь выполнить этот запрос, используя NamedQuery (с JPQL), вы увидите исключение ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<strong><em>ERROR SessionFactoryImpl:422 - Error in named query: Person.FindByName</em></strong>
<strong><em>org.hibernate.QueryException: unexpected char: '~' [select p from com.model.Person p where p.name ~* :name]</em></strong>
<strong><em>at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:229)</em></strong>
<strong><em>at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)</em></strong>
<strong><em>at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:101)</em></strong>
<strong><em>at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:80)</em></strong>
<strong><em>at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:124)</em></strong>
<strong><em>at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:547)</em></strong>
<strong><em>at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:411)</em></strong>
<strong><em>at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1842)</em></strong>
<strong><em>at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:902)</em></strong>
<strong><em>at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:57)</em></strong>
<strong><em>at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48)</em></strong>
<strong><em>at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32)</em></strong>
<strong><em>at com.main.CodeGenerator.startConnection(CodeGenerator.java:27)</em></strong>
<strong><em>at com.main.Page05.main(Page05.java:12)</em></strong>
<strong><em>Exception in thread “main” javax.persistence.PersistenceException: [PersistenceUnit: JpaQuery] Unable to build EntityManagerFactory</em></strong>
<strong><em>at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:911)</em></strong>
<strong><em>at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:57)</em></strong>
<strong><em>at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48)</em></strong>
<strong><em>at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32)</em></strong>
<strong><em>at com.main.CodeGenerator.startConnection(CodeGenerator.java:27)</em></strong>
<strong><em>at com.main.Page05.main(Page05.java:12)</em></strong>
<strong><em>Caused by: org.hibernate.HibernateException: Errors in named queries: Person.FindByName</em></strong>
<strong><em>at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:424)</em></strong>
<strong><em>at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1842)</em></strong><strong><em> </em></strong>
<strong><em>at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:902)</em></strong>
<strong><em> ... 5 more</em></strong>

Решение такой ситуации заключается в использовании запроса, который не использует JPQL в качестве своего синтаксиса. С NativeQuery вы сможете выполнять запросы, используя синтаксис базы данных.

В приведенном ниже коде вы можете увидеть, как использовать NativeQueries:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.main;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
import com.model.Dog;
 
public class Page08 {
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  String nameOfFirstPerson = getFirstPersonName(em);
  System.out.println(nameOfFirstPerson);
 
  Dog dog = getTopDogDescending(em);
  System.out.println(dog.getName());
 
  CodeGenerator.closeConnection();
 }
 
 /**
  * Returns the name of the first person using a native sql
  */
 private static String getFirstPersonName(EntityManager em) {
  Query query = em.createNativeQuery('select top 1 name from person');
  return (String) query.getSingleResult();
 }
 
 /**
  * Return an object using a native sql
  */
 private static Dog getTopDogDescending(EntityManager em) {
  Query query = em.createNativeQuery('select top 1 id, name, weight from dog order by id desc', Dog.class);
  return (Dog) query.getSingleResult();
 }
}

О коде выше:

  • Обратите внимание, что мы используем собственный запрос вместо JPQL. Мы можем иметь сущность как результат собственного запроса. Метод « getTopDogDescending » возвращает объект Dog после нативного вызова запроса.

Вы также можете создать свой собственный запрос как @NamedNativeQuery. Разница между NamedNativeQuery и NativeQuery заключается в том, что NamedNativeQuery определен в его классе сущностей, и у вас может быть только один с этим именем.

Преимущества использования @NamedNativeQuery:

  • Легко поддерживать код: каждый запрос находится в классе, если класс обновит атрибут, будет проще обновить запрос.
  • Помогает повысить производительность: как только ваш запрос уже объявлен, JPA отобразит его и сохранит его синтаксис в памяти. JPA не нужно будет «анализировать» ваш запрос каждый раз, когда ваш проект использует его.
  • Повышает повторное использование кода. После объявления @NamedNativeQuery необходимо указать значение параметра «name», и это имя должно быть уникальным для области действия модуля сохраняемости (вы устанавливаете это значение в «persistence.xml», и оно используется EntityManager).

Ниже вы увидите, как объявить @NamedNativeQuery:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.model;
 
import java.util.*;
 
import javax.persistence.*;
 
@Entity
@NamedQueries({
 @NamedQuery(name='Person.findByName', query='select p from Person p where p.name = :name'),
 @NamedQuery(name='Person.findByAge', query='select p from Person p where p.age = :age')
})
 
@NamedNativeQuery(name='Person.findByNameNative', query='select id, name, age from person where name = :name')
public class Person {
 
 public static final String FIND_BY_NAME = 'Person.findByName';
 public static final String FIND_BY_AGE = 'Person.findByAge';
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
 // get and set
}

Вы сможете использовать @NamedNativeQuery так же, как @NativeQuery: « em.createNamedQuery (« Person.findByNameNative »); ».

Здесь приходит плохие новости. К сожалению, в Hibernate еще не реализован @NamedNativeQuery. Если вы попытаетесь запустить код с этой аннотацией, вы увидите исключение ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
<em>Caused by: <span style='text-decoration: underline;'>org.hibernate.cfg.NotYetImplementedException</span>: Pure native scalar queries are not yet supported</em>
<em>at org.hibernate.cfg.annotations.QueryBinder.bindNativeQuery(<span style='text-decoration: underline;'>QueryBinder.java:140</span>)</em>
<em>at org.hibernate.cfg.AnnotationBinder.bindQueries(<span style='text-decoration: underline;'>AnnotationBinder.java:339</span>)</em>
<em>at org.hibernate.cfg.AnnotationBinder.bindClass(<span style='text-decoration: underline;'>AnnotationBinder.java:548</span>)</em>
<em>at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(<span style='text-decoration: underline;'>Configuration.java:3977</span>)</em>
<em>at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(<span style='text-decoration: underline;'>Configuration.java:3931</span>)</em>
<em>at org.hibernate.cfg.Configuration.secondPassCompile(<span style='text-decoration: underline;'>Configuration.java:1368</span>)</em>
<em>at org.hibernate.cfg.Configuration.buildMappings(<span style='text-decoration: underline;'>Configuration.java:1345</span>)</em>
<em>at org.hibernate.ejb.Ejb3Configuration.buildMappings(<span style='text-decoration: underline;'>Ejb3Configuration.java:1477</span>)</em>
<em>at org.hibernate.ejb.EventListenerConfigurator.configure(<span style='text-decoration: underline;'>EventListenerConfigurator.java:193</span>)</em>
<em>at org.hibernate.ejb.Ejb3Configuration.configure(<span style='text-decoration: underline;'>Ejb3Configuration.java:1096</span>)</em>
<em>at org.hibernate.ejb.Ejb3Configuration.configure(<span style='text-decoration: underline;'>Ejb3Configuration.java:278</span>)</em>
<em>at org.hibernate.ejb.Ejb3Configuration.configure(<span style='text-decoration: underline;'>Ejb3Configuration.java:362</span>)</em>

JPA: сложные собственные запросы

Вы сможете создавать комплексы, отображающие NativeQuery; это сопоставление вернет более одного класса или значения.

Ниже вы можете увидеть, как наша карта классов дает этот комплекс результатов и как выполнить с ним запрос:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.model;
 
import java.util.*;
 
import javax.persistence.*;
 
@Entity
@NamedQueries({
  @NamedQuery(name='Person.findByName', query='select p from Person p where p.name = :name'),
  @NamedQuery(name='Person.findByAge', query='select p from Person p where p.age = :age')})
})
@SqlResultSetMappings({
 @SqlResultSetMapping(name='personAndAdress',
   entities={
    @EntityResult(entityClass=Person.class),
    @EntityResult(entityClass=Address.class,
     fields={
      @FieldResult(name='id', column='ADDRESS_ID')
     }
    )
  }),
 @SqlResultSetMapping(name='personWithDogAmount',
  entities={@EntityResult(entityClass=Person.class)},
  columns={@ColumnResult(name='dogAmount')}
 )
})
public class Person {
 
 public static final String FIND_BY_NAME = 'Person.findByName';
 public static final String FIND_BY_AGE = 'Person.findByAge';
 public static final String MAPPING_PERSON_AND_ADDRESS = 'personAndAdress';
 public static final String MAPPING_DOG_AMOUNT = 'personWithDogAmount';
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
 // get and set
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.main;
 
import java.math.BigInteger;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
import com.model.Address;
import com.model.Person;
 
public class Page09 {
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  Query query = em.createNativeQuery('select id, name, age, a.id as ADDRESS_ID, houseNumber, streetName ' +
             'from person p join address a on a.id = p.address_id where p.id = 1',
             Person.MAPPING_PERSON_AND_ADDRESS);
 
  Object[] result = (Object[]) query.getSingleResult();
 
  Person personWithAdress = (Person) result[0];
  Address address = (Address) result[1];
 
  System.out.println(personWithAdress.getName() + ' lives at ' + address.getStreetName());
 
  query = em.createNativeQuery('select p.id, p.name, count(0) as dogAmount ' +
                'from person p join dog d on p.id = d.person_id where name = 'Mark' ' +
                'group by p.id, p.name',
             Person.MAPPING_DOG_AMOUNT);
 
  result = (Object[]) query.getSingleResult();
 
  Person person = (Person) result[0];
  BigInteger total = (BigInteger) result[1];
 
  System.out.println(person.getName() + ' has ' + total + ' dogs');
 
  CodeGenerator.closeConnection();
 }
}

О коде выше:

  • С помощью @SqlResultSetMapping вы уведомите JPA, какие объекты мы хотим получить в результате. Обратите внимание, что в сопоставлении personAndAdress мы написали классы, которые будут возвращены. Мы также использовали атрибут с именем « @FieldResult ». Этот атрибут будет отображать поля запроса с одинаковыми именами, в нашем запросе мы получили идентификатор человека и идентификатор адреса. Вот почему мы использовали « @FieldResult », чтобы уведомить JPA о сопоставлении столбца ADDRESS_ID с атрибутом ID класса Address.
  • В отображении « dogAmount » мы устанавливаем атрибут «@ColumnResult», который уведомляет JPA, что у нас будет « дополнительный столбец » в результате запроса, и этот « дополнительный столбец » не принадлежит ни одному классу.

JPA: оптимизация запросов с EJB

Каждый раз, когда вы выполняете запрос внутри области транзакции, контекст постоянства будет сохранять результат « прикрепленным »; Контекст постоянства будет « наблюдать » за этими объектами на тот случай, если какой-либо из этих « прикрепленных » объектов получит какое-либо обновление. Все обновления « прикрепленных » объектов будут сохранены в базе данных.

1
2
3
4
5
6
7
8
@PersistenceContext(unitName = 'myPU')
private EntityManager em;
 
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void editPersonName(Integer personId, String personNewName){
 Person person = em.find(Person.class, personId);
 person.setName(personNewName);
}

В приведенном выше коде вы можете видеть, что нам не нужно вызывать « em.merge () » для обновления имени человека в базе данных.

Когда мы приносим коллекцию из базы данных, обычно для отображения в таблице данных или отчетах, все эти объекты будут присоединены к контексту постоянства. Процесс прикрепления этих объектов запустит несколько процессов, проверки данных и синхронизации. Чем выше число объектов, тем выше будет выделенная память для результата запроса, и чем выше будет контекст сохраняемости, чтобы сохранить все эти объекты « присоединенными ».

Какой смысл в том, чтобы все эти объекты были «прикреплены», когда их конечный пункт назначения будет отправлен на просмотр? Когда объекты покидают проект EJB и переходят в проект представления, они будут считаться «отделенными». С этим сценарием у нас есть ненужная работа, собирающая все данные из базы данных, делающая, чем «присоединенная», и отправляющая их в представление, чтобы сделать их «отсоединенными».

Существует простой способ сделать эти объекты из базы данных уже отсоединенной. Преимущество этого подхода состоит в том, что контекст постоянства никогда не будет тратить время и процессор контейнера, пытаясь синхронизировать результат запроса.

В приведенном ниже коде вы можете увидеть, как работает это решение.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.main;
 
import java.util.List;
 
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
 
import com.model.Person;
 
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class PersonDAO {
 @PersistenceContext(unitName = 'myPU')
 private EntityManager em;
 
 @TransactionAttribute(TransactionAttributeType.REQUIRED)
 public void editPersonName(Integer personId, String personNewName){
  Person person = em.find(Person.class, personId);
  person.setName(personNewName);
 }
 
 @SuppressWarnings('unchecked')
 public List<Person> listAll(){
  Query query = em.createQuery('select p from Person p');
  return query.getResultList();
 }
 
 @SuppressWarnings('unchecked')
 public List<Person> listAllWithoutDogs(){
  Query query = em.createQuery('select p from Person p where p.dogs is empty');
  return query.getResultList();
 }
}

В приведенном выше коде мы получили класс DAO, который является EJB. По умолчанию в нашем EJB отсутствует транзакция (« @TransactionAttribute (TransactionAttributeType.NOT_SUPPORTED) »), и при таком типе транзакции контекст постоянства не « присоединяет » результат запросов. Объекты, возвращаемые запросами, будут считаться « отделенными ».

Обратите внимание, что аннотация метода « editPersonName » остается прежней: « @TransactionAttribute (TransactionAttributeType.REQUIRED) ». Этот тип транзакции указывает EJB, что новая транзакция должна быть запущена, если она еще не была запущена. Вы можете установить атрибут транзакции для класса, но метод может переопределить этот атрибут, как мы это сделали в « editPersonName ». Определение транзакции метода будет иметь приоритет над определением транзакции класса.

JPA: нумерация страниц

Если вы хотите сделать пагинацию JPA, просто сделайте, как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.main;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
import com.model.Dog;
 
public class Page11 {
 
 @SuppressWarnings('unchecked')
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  Query query = em.createQuery('select d from Dog d');
 
  List<Dog> dogs = query.getResultList();
 
  System.out.println('Total of dogs found: ' + dogs.size());
 
  query.setMaxResults(5);
  query.setFirstResult(0);
 
  List<Dog> fiveFirstDogs = query.getResultList();
 
  System.out.print('Total of dogs found: ' + fiveFirstDogs.size() + ' ');
  for (Dog dog : fiveFirstDogs) {
   System.out.print(dog.getName() + ' ');
  }System.out.println();
 
  query.setMaxResults(5);
  query.setFirstResult(5);
 
  List<Dog> fiveSecondDogs = query.getResultList();
 
  System.out.print('Total of dogs found: ' + fiveSecondDogs.size() + ' ');
  for (Dog dog : fiveSecondDogs) {
   System.out.print(dog.getName() + ' ');
  }
 
  CodeGenerator.closeConnection();
 }
}

О коде выше:

  • Метод « setMaxResults » устанавливает количество результатов, которые запрос будет возвращать.
  • Метод « setFirstResult » установит первую строку, которая будет выведена.

В первом запросе мы искали все данные в базе данных.

Во втором запросе мы получили пять результатов, начиная с позиции 0.

В последнем запросе мы снова получили пять результатов, но с позиции 5.

Помните, что первая позиция всегда равна нулю, а не единице.

JPA: База данных Советы

Поставщики базы данных предоставили нам специфические функции с именем Hints. Эти советы очень полезны, потому что они оптимизируют запросы и помогают нам в решении других задач. Каждая база данных имеет свои собственные Подсказки, и эти значения не переносимы.

Ниже вы можете увидеть некоторые подсказки:

  • SQLServer: OPTION (OPTIMIZE FOR (@name = ‘Mark’, @age UNKNOWN));
  • Oracle: выберите / * + first_rows (100) * / name
  • MySQL: выберите * из индекса игнорирования пользователя (col3_index)

Каждый поставщик базы данных устанавливает правила для своих подсказок, правил, таких как синтаксис и команды выполнения.

Есть два способа определения подсказок:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.main;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
import com.model.Dog;
 
public class Page12 {
 
 @SuppressWarnings('unchecked')
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  Query query = em.createQuery('select d from Dog d');
  query.setHint('org.hibernate.timeout', 1000);
 
  List<Dog> dogs = query.getResultList();
  System.out.println('Found ' + dogs.size() + ' dogs');
 
  CodeGenerator.closeConnection();
 }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.model;
 
import java.util.*;
 
import javax.persistence.*;
 
@Entity
@NamedQueries({
  @NamedQuery(name='Person.findByName', query='select p from Person p where p.name = :name'),
  @NamedQuery(name='Person.findByAge', query='select p from Person p where p.age = :age', hints={@QueryHint(name='org.hibernate.timeout', value='1000')})
})
@SqlResultSetMappings({
 @SqlResultSetMapping(name='personAndAdress',
   entities={
    @EntityResult(entityClass=Person.class),
    @EntityResult(entityClass=Address.class,
     fields={
      @FieldResult(name='id', column='ADDRESS_ID')
     }
    )
  }),
 @SqlResultSetMapping(name='personWithDogAmount',
  entities={@EntityResult(entityClass=Person.class)},
  columns={@ColumnResult(name='dogAmount')}
 )
})
public class Person {
 
 public static final String FIND_BY_NAME = 'Person.findByName';
 public static final String FIND_BY_AGE = 'Person.findByAge';
 public static final String MAPPING_PERSON_AND_ADDRESS = 'personAndAdress';
 public static final String MAPPING_DOG_AMOUNT = 'personWithDogAmount';
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
 // get and set
}

Вы можете установить подсказку в @NamedQuery и непосредственно в Query.

Вы должны всегда помнить недостаток подсказок . После того, как вы установите подсказку на свой NamedQuery, ваш код не будет переносимым на других поставщиков баз данных. Одним из решений будет использование только Query вместо NamedQuery. Прежде чем добавить подсказку в запрос, вы можете проверить, поддерживает ли текущая база данных эту подсказку; Вы можете использовать файл свойств для определения текущей базы данных приложения.

Продолжаем третью часть серии

Ссылка: JPA Запросы и советы от нашего партнера JCG Хеберта Коэльо в блоге uaiHebert .