Статьи

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

Есть несколько JPA «как», которые мы можем найти в Интернете, здесь, в этом блоге, который учит, как выполнять несколько задач с JPA.

Обычно я вижу, что некоторые люди задают вопросы о Queries с JPA; Обычно для ответа на такие вопросы предоставляется несколько ссылок, пытаясь найти решение вопроса.

До сегодняшнего дня я не мог найти пост в блоге, в котором собрана хорошая тема о запросах с JPA, советы по производительности / использованию, исходный код для загрузки …

Вызов принят

Сегодня мы увидим:

  • Классы моделей и класс, который будет генерировать данные базы данных
  • Найти метод; Используйте метод getReference для повышения производительности, отображая параметры запроса в консоли с помощью log4j.
  • JPQL: запросы с простыми параметрами или объектами, объединениями, упорядочением по, перемещением по связям
  • JPQL: Функции: AVG, COUNT, MAX, MIN, TRIM, SUM, UPPER, LOWER, MOD, LENGHT, SQRT; Использование HAVING, GROUP BY
  • JPQL: Условия фильтрации: LIKE, IN, DISTINCT, EMPTY, BETWEEN, NULL, MEMBER OF, EXISTS (подзапросы), ANY, ALL, SOME, CONCAT, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCATE, SIZE, SUBST
  • JPA: NamedQuery, запросы с датами, предупреждения о методе getSingleResult
  • JPA: NativeQuery, NamedNativeQuery
  • JPA: сложные собственные запросы
  • JPA: оптимизация запросов с EJB
  • JPA: нумерация страниц
  • JPA: База данных Советы
  • JPA: создание объекта из запроса
  • JPQL: массовое обновление и удаление
  • JPA: критерии

Вы увидите, что в каждом главном классе мы будем вызывать метод « CodeGenerator.generateData () ». Этот метод класса только создает данные в базе данных; с этими данными наши запросы найдут правильные результаты.

На последней странице этого поста вы найдете ссылку для скачивания исходного кода этого поста.

В этом посте мы будем использовать JPA 2.0 с Hibernate в качестве провайдера. База данных будет HSQLDB и будет прикреплена к этому проекту. Вы можете скачать исходный код и запустить проект без необходимости какой-либо дополнительной настройки. Мы не будем говорить о том, как настроить HSQLDB, потому что этот пост посвящен тому, как запрашивать данные базы данных.

Этот пост не будет использовать лучшие практики разработки в некоторых моментах. Основное внимание в этом посте будет уделено отображению работы запросов 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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package com.model;
 
import java.util.ArrayList;
import java.util.List;
 
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
 
@Entity
public class Person {
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
 private String name;
 private int age;
 
 public Person() {
 
 }
 
 public Person(String name, int age) {
  this.name = name;
  this.age = age;
 }
 
 @OneToMany(mappedBy = 'person', cascade = CascadeType.ALL)
 private List<Dog> dogs;
 
 @OneToOne(cascade = CascadeType.ALL)
 @JoinColumn(name='address_id')
 private Address address;
 
 public int getId() {
  return id;
 }
 
 public void setId(int id) {
  this.id = id;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
 
 public int getAge() {
  return age;
 }
 
 public void setAge(int age) {
  this.age = age;
 }
 
 public List<Dog> getDogs() {
  if (dogs == null) {
   dogs = new ArrayList<Dog>();
  }
 
  return dogs;
 }
 
 public void setDogs(List<Dog> dogs) {
  this.dogs = dogs;
 }
 
 public Address getAddress() {
  return address;
 }
 
 public void setAddress(Address address) {
  this.address = address;
 }
 
 @Override
 public int hashCode() {
  return getId();
 }
 
 @Override
 public boolean equals(Object obj) {
  if (obj instanceof Person) {
   Person person = (Person) obj;
   return person.getId() == getId();
  }
 
  return false;
 }
}
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.model;
 
import java.util.Date;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
 
@Entity
public class Dog {
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
 private String name;
 private double weight;
 
 @Temporal(TemporalType.TIMESTAMP)
 private Date dateOfBirth;
 
 public Dog() {
 
 }
 
 public Dog(String name, double weight, Date dateOfBirth) {
  this.name = name;
  this.weight = weight;
  this.dateOfBirth = dateOfBirth;
 }
 
 public static void main(String[] args) {
 
  System.out.println(new Date());
 }
 
 @ManyToOne
 private Person person;
 
 public int getId() {
  return id;
 }
 
 public void setId(int id) {
  this.id = id;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
 
 public double getWeight() {
  return weight;
 }
 
 public void setWeight(double weight) {
  this.weight = weight;
 }
 
 public Date getDateOfBirth() {
  return dateOfBirth;
 }
 
 public void setDateOfBirth(Date dateOfBirth) {
  this.dateOfBirth = dateOfBirth;
 }
 
 public Person getPerson() {
  return person;
 }
 
 public void setPerson(Person person) {
  this.person = person;
 }
 
 @Override
 public int hashCode() {
  return getId();
 }
 
 @Override
 public boolean equals(Object obj) {
  if (obj instanceof Dog) {
   Dog dog = (Dog) obj;
   return dog.getId() == getId();
  }
 
  return false;
 }
}
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
65
package com.model;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
 
@Entity
public class Address {
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private int id;
 
 private String streetName;
 private int houseNumber;
 
 public Address() {
 
 }
 
 public Address(String streetName, int houseNumber) {
  this.streetName = streetName;
  this.houseNumber = houseNumber;
 }
 
 public int getId() {
  return id;
 }
 
 public void setId(int id) {
  this.id = id;
 }
 
 public String getStreetName() {
  return streetName;
 }
 
 public void setStreetName(String streetName) {
  this.streetName = streetName;
 }
 
 public int getHouseNumber() {
  return houseNumber;
 }
 
 public void setHouseNumber(int houseNumber) {
  this.houseNumber = houseNumber;
 }
 
 @Override
 public int hashCode() {
  return getId();
 }
 
 @Override
 public boolean equals(Object obj) {
  if (obj instanceof Address) {
   Address address = (Address) obj;
   return address.getId() == getId();
  }
 
  return false;
 }
}

Мы получили несколько базовых классов с однонаправленными и двунаправленными отношениями. Эти отношения помогут нам манипулировать всеми типами запросов, которые мы будем выполнять.

Для генерации данных базы данных у нас есть класс ниже:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.main;
 
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
 
import com.model.Address;
import com.model.Dog;
import com.model.Person;
 
public class CodeGenerator {
 private static EntityManagerFactory emf;
 private static EntityManager em;
 
 public static final String PERSON01_NAME = 'John';
 public static final String PERSON02_NAME = 'Mary';
 public static final String PERSON03_NAME = 'Anna';
 public static final String PERSON04_NAME = 'Joseph';
 public static final String PERSON05_NAME = 'Mark';
 public static final String PERSON06_NAME = 'I will not have any relationship';
 
 public static void startConnection() {
  emf = Persistence.createEntityManagerFactory('JpaQuery');
  em = emf.createEntityManager();
  em.getTransaction().begin();
 }
 
 public static void closeConnection() {
  em.getTransaction().commit();
  emf.close();
 }
 
 public static void generateData() {
  int year = 1995;
  int month = 1;
  int day = 10;
 
  Dog dog01 = new Dog('Yellow', 3.5d, createNewDate(day, month, year));
  Dog dog02 = new Dog('Brown', 8.5d, createNewDate(++day, ++month, ++year));
  Dog dog03 = new Dog('Dark', 15.5d, createNewDate(++day, ++month, ++year));
  Dog dog04 = new Dog('Kaka', 4.3d, createNewDate(++day, ++month, ++year));
  Dog dog05 = new Dog('Pepe', 8.2d, createNewDate(++day, ++month, ++year));
  Dog dog06 = new Dog('Casillas', 6.1d, createNewDate(++day, ++month, ++year));
  Dog dog07 = new Dog('Fish', 6.7d, createNewDate(++day, ++month, ++year));
  Dog dog08 = new Dog('Lion', 3.1d, createNewDate(++day, ++month, ++year));
  Dog dog09 = new Dog('Cat', 5.5d, createNewDate(++day, ++month, ++year));
  Dog dog10 = new Dog('Java', 21.7d, createNewDate(++day, ++month, ++year));
  Dog dog11 = new Dog('JSF', 23.65d, createNewDate(++day, ++month, ++year));
  Dog dog12 = new Dog('VRaptor', 24.0d, createNewDate(++day, ++month, ++year));
  Dog dog13 = new Dog('Ferrari', 3.7d, createNewDate(++day, ++month, ++year));
  Dog dog14 = new Dog('Porshe', 1.33d, createNewDate(++day, ++month, ++year));
  Dog dog15 = new Dog('Bike', 4.44d, createNewDate(++day, ++month, ++year));
  Dog dog16 = new Dog('Rambo', 5.44d, createNewDate(++day, ++month, 2015));
  Dog dog17 = new Dog('Terminator', 3.88d, createNewDate(++day, ++month, 2016));
  Dog dog18 = new Dog('John McClan', 3.88d, createNewDate(++day, ++month, 2016));
 
  Person person01 = new Person(PERSON01_NAME, 33);
  person01.getDogs().add(dog01);
  person01.getDogs().add(dog02);
  person01.getDogs().add(dog03);
  person01.setAddress(new Address('Street A', 30));
  dog01.setPerson(person01);
  dog02.setPerson(person01);
  dog03.setPerson(person01);
 
  Person person02 = new Person(PERSON02_NAME, 27);
  person02.getDogs().add(dog04);
  person02.getDogs().add(dog05);
  person02.getDogs().add(dog06);
  person02.setAddress(new Address('Street B', 60));
  dog04.setPerson(person02);
  dog05.setPerson(person02);
  dog06.setPerson(person02);
 
  Person person03 = new Person(PERSON03_NAME, 7);
  person03.getDogs().add(dog07);
  person03.getDogs().add(dog08);
  person03.getDogs().add(dog09);
  person03.setAddress(new Address('Street B', 90));
  dog07.setPerson(person03);
  dog08.setPerson(person03);
  dog09.setPerson(person03);
 
  Person person04 = new Person(PERSON04_NAME, 43);
  person04.getDogs().add(dog10);
  person04.getDogs().add(dog11);
  person04.getDogs().add(dog12);
  person04.setAddress(new Address('Street C', 120));
  dog10.setPerson(person04);
  dog11.setPerson(person04);
  dog12.setPerson(person04);
 
  Person person05 = new Person(PERSON05_NAME, 70);
  person05.getDogs().add(dog13);
  person05.getDogs().add(dog14);
  person05.getDogs().add(dog15);
  person05.getDogs().add(dog16);
  person05.setAddress(new Address('Street D', 150));
  dog13.setPerson(person05);
  dog14.setPerson(person05);
  dog15.setPerson(person05);
  dog16.setPerson(person05);
 
  Person person06 = new Person(PERSON06_NAME, 45);
 
  em.persist(person01);
  em.persist(person02);
  em.persist(person03);
  em.persist(person04);
  em.persist(person05);
  em.persist(person06);
 
  em.persist(dog17);
  em.persist(dog18);
 
  em.flush();
 }
 
 private static Date createNewDate(int day, int month, int year) {
  SimpleDateFormat formatter = new SimpleDateFormat('dd/MM/yyyy');
  try {
   return formatter.parse('' + day + '/' + month + '/' + year);
  } catch (ParseException e) {
   e.printStackTrace();
   return null;
  }
 }
 
 public static EntityManager getEntityManager() {
  return em;
 }
}

Найти метод; Используйте метод getReference для повышения производительности, отображая параметры запроса в консоли с помощью log4j.

Метод find обычно вызывается до того, как мы выполним некоторые изменения в базе данных, такие как обновление какого-либо атрибута объекта, отношения или удаление.

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

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
package com.main;
 
import javax.persistence.EntityManager;
 
import com.model.Address;
import com.model.Person;
 
public class Page03 {
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  Person person = em.find(Person.class, 1);
 
  int addressId = 2;
 
  // usually we send an id or a detached object from the view
  setAddressToOtherPerson(em, person, addressId);
 
  int personId = 4;
 
  // usually we send an id or a detached object from the view
  deletePerson(em, personId);
 
  CodeGenerator.closeConnection();
 }
 
 private static void setAddressToOtherPerson(EntityManager em, Person person, int addressId) {
  Address address = em.find(Address.class, addressId);
  person.setAddress(address);
  em.merge(person);
  em.flush();
 }
 
 private static void deletePerson(EntityManager em, int personId) {
  Person savedPerson = em.find(Person.class, personId);
  em.remove(savedPerson);
  em.flush();
 }
}

Обратите внимание, что методы « setAddressToOtherPerson » и « deletePerson » используют метод find только для обновления ссылки или для удаления объекта.

Метод find () имеет оптимизированную функцию запроса, которая будет искать объект в контексте постоянства, если он не найдет объект, он запросит базу данных для получения данных. Если вы получили отношение, аннотированное с помощью EAGER (например: « @OneToMany (fetch = FetchType.EAGER) »), метод find доставит эти объекты из базы данных. Обратите внимание, что нет необходимости извлекать все эти данные из базы данных для простых задач, таких как удаление обновления ссылки.

EntityManager имеет специальный метод, который помогает с этими простыми задачами. EntityManager выполнит простой запрос типа « выберите идентификатор из Person p, где p.id =: personId ». У нас будет быстрый и меньший запрос.

Ниже вы можете увидеть, как мы будем использовать getReference:

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
package com.main;
 
import javax.persistence.EntityManager;
 
import com.model.Address;
import com.model.Person;
 
public class Page03 {
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  Person person = em.find(Person.class, 1);
 
  int addressId = 2;
 
  // usually we send an id or a detached object from the view
  setAddressToOtherPerson(em, person, addressId);
 
  int personId = 4;
 
  // usually we send an id or a detached object from the view
  deletePerson(em, personId);
 
  CodeGenerator.closeConnection();
 }
 
 private static void setAddressToOtherPerson(EntityManager em, Person person, int addressId) {
  Address address = em.getReference(Address.class, addressId);
  person.setAddress(address);
  em.merge(person);
  em.flush();
  System.out.println('Merged');
 }
 
 private static void deletePerson(EntityManager em, int personId) {
  // usually is find or merge
  Person savedPerson = em.getReference(Person.class, personId);
  em.remove(savedPerson);
  em.flush();
  System.out.println('Deleted');
 }
}

С помощью метода « getReference » вы будете запрашивать только идентификатор объекта, вы сэкономите часть трафика базы данных.

Ниже вы найдете конфигурацию lo4j.properties, необходимую для отображения параметров запросов JPA в консоли. Обычно, когда мы вызываем запрос, используя Hibernate, Hibernate отформатирует запрос с «?» вместо использования реальной стоимости. С кодом ниже вы сможете увидеть параметры запроса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
 
# Root logger option
log4j.rootLogger=ERROR, stdout
 
# Hibernate logging options (INFO only shows startup messages)
log4j.logger.org.hibernate=ERROR
 
# Log JDBC bind parameter runtime arguments
log4j.logger.org.hibernate.type=TRACE

Если вы хотите деактивировать журнал, вам нужно просто закомментировать последнюю строку lo4j.properties с символом # и установить для конфигурации show_log в файле «persistence.xml» значение false.

JPQL: запросы с простыми параметрами или объектами, объединениями, упорядочением по, перемещением по связям

Для выполнения базового запроса вам просто нужно выполнить команду, подобную этой: «выберите d из Dog d». Одна вещь, которую вы всегда должны помнить: для выполнения такого запроса мы используем JPQL, а не обычный SQL .

Преимущество использования JPQL состоит в том, что он очень похож на SQL и является переносимым. Вы можете использовать один и тот же запрос в каждой базе данных без проблем.

Никогда не объединяйте ваш запрос со строкой. Если вы выполните запрос, подобный следующему: «выберите p из Person p, где p.name» + person.getName (), вы можете быть уверены, что хакерам это понравится. Они используют этот тип кода для атаки «SQL-инъекция» (или JPQL-инъекция). Чтобы избежать такого рода атак, добавьте параметры к вашему запросу, как мы увидим ниже.

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

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package com.main;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
import com.model.Dog;
import com.model.Person;
 
public class Page04 {
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  List<Dog> dogs = listAllDogs(em);
 
  for (Dog dog : dogs) {
   System.out.println(dog.getName());
  }
 
  Person person03 = findPersonByName(em, CodeGenerator.PERSON03_NAME);
  System.out.println(person03.getName());
 
  Person person01 = new Person();
  person01.setId(1);
 
  Person savedPerson = findPersonByPersonObject(em, person01);
  System.out.println(savedPerson.getName());
 
  List<Dog> dogsByWeight = listAllDogsOrderingByWeight(em);
 
  for (Dog dog : dogsByWeight) {
   System.out.println(dog.getWeight());
  }
 
  String addressName = findAddressNameOfPerson(em, CodeGenerator.PERSON04_NAME);
  System.out.println('Person 04 address is: ' + addressName);
 
  Person person02 = findPersonByNameWithAllDogs(em, CodeGenerator.PERSON02_NAME);
 
  for (Dog dog : person02.getDogs()) {
   System.out.println('Person 02 Dog: ' + dog.getName());
  }
 
  Person person05 = findPersonByNameThatMayNotHaveDogs(em, CodeGenerator.PERSON06_NAME);
  System.out.println('Is the list of the Dogs from the Person 05 empty? ' + person05.getDogs().size());
 
  CodeGenerator.closeConnection();
 }
 
 /**
  * Easiest way to do a query
  */
 @SuppressWarnings('unchecked')
 private static List<Dog> listAllDogs(EntityManager em) {
  Query query = em.createQuery('select d from Dog d', Dog.class);
 
  return query.getResultList();
 }
 
 /**
  * Easiest way to do a query with parameters
  */
 private static Person findPersonByName(EntityManager em, String name) {
  Query query = em.createQuery('select p from Person p where name = :name', Person.class);
  query.setParameter('name', name);
  return (Person) query.getSingleResult();
 }
 
 /**
  * Executes a query that has as parameter an object
  */
 private static Person findPersonByPersonObject(EntityManager em, Person person) {
  Query query = em.createQuery('select p from Person p where p = :person');
  query.setParameter('person', person);
  return (Person) query.getSingleResult();
 }
 
 /**
  * Query that will list all dogs with an order
  */
 @SuppressWarnings('unchecked')
 private static List<Dog> listAllDogsOrderingByWeight(EntityManager em) {
  Query query = em.createQuery('select d from Dog d order by d.weight desc', Dog.class);
 
  return query.getResultList();
 }
 
 /**
  * Query that get only a field instead a complete class object
  */
 private static String findAddressNameOfPerson(EntityManager em, String name) {
  Query query = em.createQuery('select p.address.streetName from Person p where p.name = :name');
  query.setParameter('name', name);
  return (String) query.getSingleResult();
 }
 
 /**
  * Query that will fetch a lazy relationship Be carefull, with this kind of
  * query only those who have the relationship will come in the result
  */
 private static Person findPersonByNameWithAllDogs(EntityManager em, String name) {
  Query query = em.createQuery('select p from Person p join fetch p.dogs where p.name = :name', Person.class);
  query.setParameter('name', name);
  return (Person) query.getSingleResult();
 }
 
 /**
  * With this query will will bring results that may not have arelationship
  */
 private static Person findPersonByNameThatMayNotHaveDogs(EntityManager em, String name) {
  Query query = em.createQuery('select p from Person p left join fetch p.dogs where p.name = :name', Person.class);
  query.setParameter('name', name);
  return (Person) query.getSingleResult();
 }
}

О коде выше:

  • Каждый запрос вызывается как « em.createQuery (« HHH », HHH.class) » с определенным текстом запроса и возвращаемым классом. Вы можете определить возвращаемый класс, например Person.class. Параметр Person.class будет указывать JPA, который является возвращаемым объектом.
  • Мы можем использовать базовые атрибуты в качестве параметров запроса, таких как « p.name =: name » или объект « p =: person ». Если вы используете объект, JPA будет сравнивать по его @ID.
  • Если вы хотите заказать запрос, вам просто нужно сделать: « order by d.weight desc «. Значение заказа по умолчанию — asc, и вам не нужно его записывать.
  • Об объединении вы должны обратить внимание на два вида объединений, которые мы использовали. В методе « findPersonByNameWithAllDogs » мы просто использовали «… Person p join fetch p.dogs …», чтобы вывести список собак. Нам нужно было использовать выборку соединения, потому что список собак помечен атрибутом «lazy»; если бы мы не включили выборку соединения и не выполнили команду типа « person.getDogs () », потребовалась бы другая « поездка » в базу данных. Если вы используете этот запрос, чтобы найти человека, у которого нет собак, JPA не найдет данных в базе данных, независимо от того, есть ли в вашей базе данных человек без собак. Если вы хотите выполнить запрос, который приносит коллекцию извлекаемых собак и людей, у которых есть или нет собак, вам нужно будет использовать « … Person p left join fetch p.dogs… », как мы делали в методе: « findPersonByNameThatMayNotHaveDogs «. « Левая выборка » принесет людей с пустым списком собак.

JPQL: Функции: AVG, COUNT, MAX, MIN, TRIM, SUM, UPPER, LOWER, MOD, LENGHT, SQRT; Использование HAVING, GROUP BY

JPQL также имеет много функций, которые помогают нам с запросами. Ниже вы можете увидеть их описание:

  • AVG — среднее число
  • COUNT — подсчитывает количество записей, найденных по запросу.
  • MAX — получает более высокое значение столбца
  • MIN — получает нижнее значение столбца
  • TRIM — удаляет пробел в начале / конце текста
  • SUM — Суммирует все значения столбца
  • UPPER — изменяет весь текст столбца в верхний регистр
  • LOWER — изменяет весь текст столбца в нижний регистр
  • MOD — возвращает модуль столбца
  • ДЛИНА — возвращает размер строки
  • SQRT — возвращает квадратный корень числа

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

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.main;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
import com.model.Person;
 
public class Page05 {
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  Number average = getPersonsAgeAverage(em);
  System.out.println(average);
 
  List<Object[]> personsFilteredByDogsWeight = getPersonsWithDogsWeightHigherThan(em, 4d);
 
  for (Object[] objects : personsFilteredByDogsWeight) {
   Person person = (Person) objects[0];
   Long count = (Long) objects[1];
   System.out.println('The person : ' + person.getName() + ' has ' + count + ' dogs with the weight > 4');
  }
 
  List<Object[]> dogsMinAndMaxWeightList = getDogMinAndMaxWeight(em);
  Object[] dogMinAndMaxWeightResult = dogsMinAndMaxWeightList.get(0);
  System.out.println('Min: ' + dogMinAndMaxWeightResult[0] + ' Max: ' + dogMinAndMaxWeightResult[1]);
 
  Number sumOfAllAges = getTheSumOfAllAges(em);
  System.out.println('All summed ages are: ' + sumOfAllAges);
 
  String loweredCaseName = getLoweredCaseNameFromUpperCase(em, CodeGenerator.PERSON03_NAME);
  System.out.println(loweredCaseName);
 
  Number personAgeMod = getPersonAgeMode(em, CodeGenerator.PERSON05_NAME, 6);
  System.out.println('Person modulus age: ' + personAgeMod);
 
  Number personAgeSqrt = getPersonAgeSqrtUsingTrim(em, '        ' + CodeGenerator.PERSON04_NAME + '        ');
  System.out.println('Person modulus age: ' + personAgeSqrt);
 
  List<Object[]> personsByDogsAmount = getPersonByHavingDogAmountHigherThan(em, 3);
 
  for (Object[] objects : personsByDogsAmount) {
   Person person = (Person) objects[0];
   Long count = (Long) objects[1];
   System.out.println(person.getName() + ' has ' + count + ' dogs');
  }
 
  CodeGenerator.closeConnection();
 }
 
 /**
  * Uses the AVG sql database function
  */
 private static Number getPersonsAgeAverage(EntityManager em) {
  Query query = em.createQuery('select avg(p.age) from Person p');
  return (Number) query.getSingleResult();
 }
 
 /**
  * This query will use the count database function
  *
  * @return List<Object[]> where object[0] is a person, object [2] is a Long
  */
 @SuppressWarnings('unchecked')
 private static List<Object[]> getPersonsWithDogsWeightHigherThan(EntityManager em, double weight) {
  Query query = em.createQuery('select p, count(p) from Person p join p.dogs d where d.weight > :weight group by p');
  query.setParameter('weight', weight);
  return query.getResultList();
 }
 
 /**
  * This query will use the min and max sql database function
  *
  * @return List<Object[]> where object[0] is the min, object [2] is the max
  */
 @SuppressWarnings('unchecked')
 private static List<Object[]> getDogMinAndMaxWeight(EntityManager em) {
  Query query = em.createQuery('select min(weight), max(weight) from Dog');
  return query.getResultList();
 }
 
 /**
  * This query will use the sum sql database function
  */
 private static Number getTheSumOfAllAges(EntityManager em) {
  Query query = em.createQuery('select sum(p.age) from Person p');
  return (Number) query.getSingleResult();
 }
 
 /**
  * Method that uses the UPPER and LOWER database functions
  */
 private static String getLoweredCaseNameFromUpperCase(EntityManager em, String name) {
  Query query = em.createQuery('select lower(p.name) from Person p where UPPER(p.name) = :name');
  query.setParameter('name', name.toUpperCase());
  return (String) query.getSingleResult();
 }
 
 /**
  * Method that uses the mod database function
  */
 private static Number getPersonAgeMode(EntityManager em, String personName, int modBy) {
  Query query = em.createQuery('select mod(p.age, :modBy) from Person p where p.name = :name');
  query.setParameter('modBy', modBy);
  query.setParameter('name', personName);
  return (Number) query.getSingleResult();
 }
 
 /**
  * Method that uses the square root of a person age using the trim function in the name
  */
 private static Number getPersonAgeSqrtUsingTrim(EntityManager em, String name) {
  Query query = em.createQuery('select sqrt(p.age) from Person p where p.name = trim(:name)');
  query.setParameter('name', name);
  return (Number) query.getSingleResult();
 }
 
 /**
  * Method that uses the having comparator with count
  */
 @SuppressWarnings('unchecked')
 private static List<Object[]> getPersonByHavingDogAmountHigherThan(EntityManager em, long dogAmount) {
  Query query = em.createQuery('select p, count(p) from Person p join p.dogs group by p.id having count(p) > :dogAmount');
  query.setParameter('dogAmount', dogAmount);
  return query.getResultList();
 }
}

О коде выше:

  • В методе « getPersonsAgeAverage » мы используем функцию «avg» для вычисления среднего значения столбца age.
  • В методе « getPersonsWithDogsWeightHigherThan » мы используем функцию count, чтобы вывести количество собак с объектом person. Обратите внимание, что у нас есть два разных результата: число и объект person. Эти значения попадут внутрь массива Object [].
  • Функции LOWER и UPPER изменят ваш строковый регистр, и вы можете использовать его для изменения результата запроса (после выбора) или в условии where. Метод « getLoweredCaseNameFromUpperCase » использует функции LOWER и UPPER в обоих случаях.
  • « GetPersonAgeMode » использует параметр после слова select. С JPA мы можем использовать параметр в любом месте запроса, вам просто нужно добавить «:» с переменной. Вы можете иметь один и тот же параметр несколько раз и передавать значение с помощью метода query.setParameter.
  • В методе « getPersonByHavingDogAmountHigherThan » функция « has » вызывается с помощью функции « count ». Мы можем использовать функцию « имея », чтобы помочь нам отфильтровать результат данных запроса.

JPQL: Условия фильтрации: LIKE, IN, DISTINCT, EMPTY, BETWEEN, NULL, MEMBER OF, EXISTS (подзапросы), ANY, ALL, SOME, CONCAT, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCATE, SIZE, SUBST

Некоторые из этих функций имеют одну и ту же цель, но обрабатываются по-разному.

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

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package com.main;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
import com.model.Dog;
import com.model.Person;
 
public class Page06 {
 
 public static void main(String[] args) {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  List<Person> personByLike = getPersonByNameUsingLike(em, 'oh');
 
  for (Person person : personByLike) {
   System.out.println(person.getName());
  }
 
  List<Person> personsByAdressNumber = getPersonsByAddressNumberHigherThan(em, 90);
 
  for (Person person : personsByAdressNumber) {
   System.out.println(person.getName());
  }
 
  List<Person> personsWithoutDogs = getPersonsWithoutDogs(em);
 
  System.out.println('Total of persons without dogs: ' + personsWithoutDogs.size());
 
  List<Person> personsWithoutAddress = getPersonsWithoutAddress(em);
 
  System.out.println('Total of persons without address: ' + personsWithoutAddress.size());
 
  try {
   SimpleDateFormat formatter = new SimpleDateFormat('dd/MM/yyyy');
 
   Date startDate = formatter.parse('01/01/1996');
   Date endDate = formatter.parse('01/01/1999');
 
   List<Dog> dogsByBirth = getDogByBirthDate(em, startDate, endDate);
 
   for (Dog dog : dogsByBirth) {
    System.out.println(dog.getName() + ': ' + formatter.format(dog.getDateOfBirth()));
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 
  Dog dog = (Dog) em.createQuery('select d from Dog d where d.id = 1', Dog.class).getSingleResult();
 
  boolean belongsTo = isThisDogBelongingToAperson(em, dog, CodeGenerator.PERSON01_NAME);
 
  System.out.println('Is this Dog member of Perons01? ' + belongsTo);
 
  Person personByConcatedName = getPersonConcatingName(em, 'Ma', 'ry');
 
  System.out.println('Found the person? ' + personByConcatedName.getName());
 
  List<Person> personByLocate = getPersonByLocatingStringInTheName(em, 'Mary');
 
  System.out.println('Amount of persons found by locate: ' + personByLocate.size());
 
  String personNameBySubstring = getPersonNameBySubstring(em, CodeGenerator.PERSON06_NAME, 12, 18);
 
  System.out.println('Name substring is: ' + personNameBySubstring);
 
  List<Person> personsDogWeight = getPersonByDogWeightOnlyHigherThan(em, 20);
 
  for (Person person : personsDogWeight) {
   System.out.println(person.getName());
  }
 
  List<Person> distinctPersons = getDistinctPersonsByDogsWeight(em, 2d);
  System.out.println('With the distinct, the result size is: ' + distinctPersons.size());
 
  List<Person> personsWithDogsAmount = getPersonsWithDougsAmountOf(em, 4);
  System.out.println('Number of persons with 4 dogs: ' + personsWithDogsAmount.size());
 
  Number numberOfDogsByPerson = getDogAmountByPerson(em, CodeGenerator.PERSON04_NAME);
  System.out.println('The dog amount is to ' + CodeGenerator.PERSON04_NAME + ': ' + numberOfDogsByPerson);
 
  List<Dog> dogsBornedAfterToday = getDogsBornAfterToday(em);
  System.out.println('The amount of dogs borned after today is: ' + dogsBornedAfterToday.size());
 
  CodeGenerator.closeConnection();
 }
 
 /**
  * This methods compares a value with LIKE
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonByNameUsingLike(EntityManager em, String name) {
  Query query = em.createQuery('select p from Person p where p.name like :name');
  query.setParameter('name', '%' + name + '%');
  return query.getResultList();
 }
 
 /**
  * This methods show several ways to do a query that checks if a part of a collection is inside another
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonsByAddressNumberHigherThan(EntityManager em, int houseNumber) {
  Query query = em.createQuery('select p from Person p where p.address in (select a from Address a where a.houseNumber > :houseNumber)');
  // Query query = em.createQuery('select p from Person p where (select a from Address a where a.houseNumber > :houseNumber and p.address = a) > 0');
  // Query query = em.createQuery('select p from Person p where p.address = any (select a from Address a where a.houseNumber > :houseNumber)');
  // Query query = em.createQuery('select p from Person p where p.address = some (select a from Address a where a.houseNumber > :houseNumber)');
  // Query query = em.createQuery('select p from Person p where exists (select a from p.address a where a.houseNumber > :houseNumber)');
  query.setParameter('houseNumber', houseNumber);
  return query.getResultList();
 }
 
 /**
  * This methods show how to check if a collection is empty
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonsWithoutDogs(EntityManager em) {
  Query query = em.createQuery('select p from Person p where p.dogs is empty');
  return query.getResultList();
 }
 
 /**
  * This method shows two ways to check if a relationship @OneToOne is empty
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonsWithoutAddress(EntityManager em) {
  Query query = em.createQuery('select p from Person p where p.address is null');
  // Query query = em.createQuery('select p from Person p where p.address is empty');
  return query.getResultList();
 }
 
 /**
  * Method that uses the between comparation
  */
 @SuppressWarnings('unchecked')
 private static List<Dog> getDogByBirthDate(EntityManager em, Date startDate, Date endDate) {
  Query query = em.createQuery('select d from Dog d where d.dateOfBirth between :startDate and :endDate');
  query.setParameter('startDate', startDate);
  query.setParameter('endDate', endDate);
  return query.getResultList();
 }
 
 /**
  * Method that uses the member of comparation to check if an object belogs to a collection
  */
 private static boolean isThisDogBelongingToAperson(EntityManager em, Dog dog, String name) {
  Query query = em.createQuery('select p from Person p where :dog member of p.dogs and p.name = :name');
  query.setParameter('dog', dog);
  query.setParameter('name', name);
 
  try {
   return query.getSingleResult() != null;
  } catch (Exception e) {
   return false;
  }
 }
 
 /**
  * Methods that concats Strings
  */
 private static Person getPersonConcatingName(EntityManager em, String firstWord, String secondWord) {
  Query query = em.createQuery('select p from Person p where p.name = concat(:firstWord, :secondWord)', Person.class);
  query.setParameter('firstWord', firstWord);
  query.setParameter('secondWord', secondWord);
  return (Person) query.getSingleResult();
 }
 
 /**
  * Method that locates a string inside another
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonByLocatingStringInTheName(EntityManager em, String valueToBeLocated) {
  Query query = em.createQuery('select p from Person p where locate(p.name, :value) > 0', Person.class);
  query.setParameter('value', valueToBeLocated);
  return query.getResultList();
 }
 
 /**
  * Methods that uses the ALL comparator
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonByDogWeightOnlyHigherThan(EntityManager em, double weight) {
  Query query = em.createQuery('select p from Person p where p.dogs is not empty and :weight < all (select d.weight from p.dogs d)');
  query.setParameter('weight', weight);
 
  return query.getResultList();
 }
 
 /**
  * Method that uses the distinct to remove any repetetition
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getDistinctPersonsByDogsWeight(EntityManager em, double weight) {
  Query query = em.createQuery('select distinct p from Person p join p.dogs d where d.weight > :weight');
  query.setParameter('weight', weight);
  return query.getResultList();
 }
 
 /**
  * Method that uses the substring to get just a position of chars inside the string
  */
 private static String getPersonNameBySubstring(EntityManager em, String personName, int startPosition, int endPosition) {
  Query query = em.createQuery('select substring(p.name, :startPosition, :endPosition) from Person p where p.name = :personName');
  query.setParameter('personName', personName);
  query.setParameter('startPosition', startPosition);
  query.setParameter('endPosition', endPosition);
  return (String) query.getSingleResult();
 }
 
 /**
  * Method that checks the size of a collection
  */
 @SuppressWarnings('unchecked')
 private static List<Person> getPersonsWithDougsAmountOf(EntityManager em, int dogAmount) {
  Query query = em.createQuery('select p from Person p where size(p.dogs) = :dogAmount');
  query.setParameter('dogAmount', dogAmount);
  return query.getResultList();
 }
 
 /**
  * Method that gets the size of a collection
  */
 private static Number getDogAmountByPerson(EntityManager em, String personName) {
  Query query = em.createQuery('select size(p.dogs) from Person p where p.name = :personName');
  query.setParameter('personName', personName);
  return (Number) query.getSingleResult();
 }
 
 /**
  * Methods that uses the current database server date/time
  */
 @SuppressWarnings('unchecked')
 private static List<Dog> getDogsBornAfterToday(EntityManager em) {
  Query query = em.createQuery('select d from Dog d where d.dateOfBirth > CURRENT_DATE');
  return query.getResultList();
 }
}

О коде выше:

  • Вы можете добавить слово «НЕ» в свои запросы. Если вы используете « IS EMPTY », вы будете искать коллекцию без значений; Если вы используете « НЕ ПУСТО », вы будете искать заполненную коллекцию.
  • « GetPersonsByAddressNumberHigherThan » показывает, как выполнить один и тот же запрос с разными функциями. Все закомментированные командные строки принесут одинаковый результат. В / Any / Some / Exists есть близкий синтаксис. Согласно Pro EJB3 книга « Некоторые » является псевдонимом «Любой».
  • Компаратор « IS EMPTY » может использоваться для проверки коллекции (например, @OneToMany) или класса отношений (например, @OneToOne). Компаратор « IS NULL » не может проверить коллекцию, но вы можете использовать его для проверки атрибута не коллекции (например, @OneToOne).
  • Компаратор « MEMBER OF » проверит, принадлежит ли данный параметр коллекции.
  • Функция «CONCAT» может использоваться как компаратор условий или как результат запроса. В приведенном выше коде он использовался просто как компаратор, но вы можете использовать его так: «выберите concat (firstName, lastName) из Person p»
  • В методе « getPersonByDogWeightOnlyHigherThan » мы используем оператор ALL. Этот оператор вернет true, только если все элементы условия (« : weight> ALL» ) вернут true. В этом методе он вернет истину, только если вес всех собак-людей был больше, чем «: вес», если только одна из собак получила вес с меньшим значением, компаратор возвратил бы ложь. Вы должны знать, если список пуст, компаратор вернет true . Чтобы избежать такого поведения, вам нужно проверить, является ли список пустым, как это было сделано в методе: « p.dogs не пуст ».
  • « Отличная » функция удалит дублирующиеся объекты. В методе « getDistinctPersonsByDogsWeight » « отличная » функция удаляет дублированных лиц.
  • Функция « SUBSTRING » извлекает значение из заданной строки. Вы установите начало и конец значения, которое будет извлечено из исходного значения. Вы можете использовать эту функцию в качестве компаратора также.
  • Функция « SIZE » возвращает количество элементов внутри коллекции. Вы можете использовать в качестве компаратора или получить значение.
  • В приведенном выше коде мы используем функцию « CURRENTE_DATE » для сравнения даты, вы также можете использовать « CURRENT_TIME, CURRENT_TIMESTAMP ». В спецификации JPA говорится, что функции текущей даты могут использоваться только в качестве компаратора. JPA пока не поддерживает никакие функции для извлечения текущей даты базы данных, поскольку эта функция не является переносимой базой данных ( 4.6.16. Функциональные выражения — окончательный выпуск JSR-000220 Enterprise JavaBeans 3.0 (постоянство) ). Если вы хотите запросить дату в базе данных, вы можете использовать NativeQuery, чтобы получить это значение.
  • Я всегда должен помнить, что вы не можете перемещаться внутри коллекции. Вы не можете выполнить следующую команду: «person.dogs.name». Вы можете получить доступ к имени собаки с помощью команды, такой как: выберите p из Person p, вызовите соединение p.dogs d, где d.name = » .

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

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