Статьи

Тест JPQL / HQL без развертывания

Вы когда-нибудь хотели протестировать свой JPQL / HQL без полного развертывания своего приложения?
Сегодня мы увидим простое решение, которое работает для любой реализации JPA: Hibernate, OpenJPA, EclipseLink и других.

Базовый исходный код, приведенный в этом посте , взят из этой книги: « Pro JPA 2: освоение API персистентности Java ™ – Майк Кит, Меррик Шинкариол ». Этот пост добавит в исходный код: параметры запроса и тест NamedQuery.

Модельные классы и генерация данных

Ниже приведены коды модельных классов:

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
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")})
})
 
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;
 
 private String name;
 private int age;
 
 public Person() {
 
 }
 
 public Person(int id) {
  this.id = id;
 }
 
 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;
 }
 
 @Override
 public String toString() {
  return "Person name: " + name;
 }
}
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
package com.model;
 
import java.util.Date;
 
import javax.persistence.*;
 
@Entity
@NamedQueries(value={@NamedQuery(name="Dog.FindByDateOfBirth", query="select d from Dog d where d.dateOfBirth = :dateOfBirth"),
  @NamedQuery(name="Dog.FindByPerson", query="select d from Dog d where d.person = :personObject")})
public class Dog {
 
 public static final String FIND_BY_DATE_OF_BIRTH = "Dog.FindByDateOfBirth";
 
 @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;
 }
 
 @Override
 public String toString() {
  return "Dog name: " + name;
 }
}
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
package com.model;
 
import javax.persistence.*;
 
@Entity
@NamedQuery(name="Address.FindAll", query="select a from Address a")
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;
 }
 
 @Override
 public String toString() {
  return "Adress street name: " + streetName;
 }
}

В приведенном выше коде мы получили несколько отношений JPA в качестве примера.

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

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
package com.main;
 
import java.text.*;
import java.util.Date;
 
import javax.persistence.*;
 
import com.model.*;
 
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("QueryTester");
  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;
 }
}

Файл «persistence.xml» находится в папке «src / META-INF» с кодом ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
 
<persistence version="2.0"
 
 <persistence-unit name="QueryTester"
  transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
 
  <properties>
   <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
   <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:." />
   <property name="javax.persistence.jdbc.user" value="sa" />
   <property name="javax.persistence.jdbc.password" value="" />
   <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
   <property name="hibernate.connection.shutdown" value="true" />
   <property name="hibernate.hbm2ddl.auto" value="update" />
   <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.format_sql" value="false"/>
  </properties>
 </persistence-unit>
</persistence>

Абстрактный тестовый класс

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
package com.main;
 
import javax.persistence.Query;
 
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
 
import com.model.Person;
 
/**
 * @author Pro JPA 2 book
 * @Empowered by uaiHebert
 *
 */
public abstract class AbstractQueryTester {
 
 protected static void populateParameters(Query query, String parameters) {
  for (String parameter : parameters.split(";")) {
   String parameterKey = parameter.split("-")[0];
   String parameterValue = parameter.split("-")[1];
   String parameterType = parameter.split("-")[2];
 
   query.setParameter(parameterKey, configureParameterValue(parameterValue, parameterType));
  }
 }
 
 private static Object configureParameterValue(String parameterValue, String parameterType) {
  if (parameterType.equalsIgnoreCase("integer")) {
   try {
    return Integer.parseInt(parameterValue);
   } catch (Exception e) {
    throw new IllegalArgumentException("Invalid parameter value as number: " + parameterValue);
   }
  }
 
  if (parameterType.equalsIgnoreCase("string")) {
   return parameterValue;
  }
 
  if (parameterType.equalsIgnoreCase("person")) {
   int personId;
 
   try {
    personId = Integer.valueOf(parameterValue);
   } catch (Exception e) {
    throw new IllegalArgumentException("Invalid parameter value as number: " + parameterValue);
   }
 
   return new Person(personId);
  }
 
  throw new IllegalArgumentException("Invalid parameter type: " + parameterType);
 }
 
 protected static void printResult(Object result) throws Exception {
  if (result == null) {
   System.out.print("NULL");
  } else if (result instanceof Object[]) {
   Object[] row = (Object[]) result;
   System.out.print("[");
   for (int i = 0; i < row.length; i++) {
    printResult(row[i]);
   }
   System.out.print("]");
  } else if (result instanceof Long || result instanceof Double || result instanceof String) {
   System.out.print(result.getClass().getName() + ": " + result);
  } else {
   System.out.print(ReflectionToStringBuilder.toString(result, ToStringStyle.SHORT_PREFIX_STYLE));
  }
 
  System.out.println();
 }
}

О приведенном выше коде:

  • Метод populateParameters заполнит все параметры запроса автоматически. Консоль Eclipse потребует, чтобы разработчик ввел информацию о запросе и параметры запроса, если они необходимы. Параметры Query должны соответствовать требованиям синтаксиса ниже:
    • 0 параметров: просто нажмите «Enter»
    • 1 параметр: id-4-целое число
    • 2 или более параметра: имя-Джон-строка; возраст-33-целое число
    • Разработчик может использовать класс в качестве параметра: dog-33-Dog
  • Метод configureParameterValue будет «приводить» значение параметраValue к правильному типу, необходимому для запроса. Это позволяет примитивные значения и классы.
  • Метод printResult отобразит результат запроса.

Тест динамического запроса

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
package com.main;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
/**
 * @author Pro JPA 2 book
 * @Empowered by uaiHebert
 *
 */
public class DynamicQueryTester extends AbstractQueryTester {
 public static void main(String[] args) throws IOException {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 
  for (;;) {
   System.out.print("Type your JPQL and press Enter > ");
   String dynamicQuery = reader.readLine();
 
   if (dynamicQuery.equals("quit")) {
    break;
   }
 
   if (dynamicQuery.length() == 0) {
    continue;
   }
 
   System.out.println("Type the namedQuery parameters.");
   System.out.println("All paramters should be like: id-2-integer;name-John-string");
   System.out.println("Or just press enter for 0 parameters");
   String parameters = reader.readLine();
 
   try {
    Query query = em.createQuery(dynamicQuery);
 
    if (parameters.length() > 0) {
     populateParameters(query, parameters);
    }
 
    @SuppressWarnings("rawtypes")
    List result = query.getResultList();
 
    if (result.size() > 0) {
     int count = 0;
     for (Object o : result) {
      System.out.print(++count + " ");
      printResult(o);
     }
    } else {
     System.out.println("0 results returned");
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
 
  CodeGenerator.closeConnection();
 }
}

Приведенный выше код запускает транзакцию, создает данные базы данных в оперативной памяти и позволяет разработчику тестировать любые динамические запросы. Чтобы протестировать JPQL / HQL, разработчику нужно просто набрать код Query в консоли.

Чтобы завершить цикл, не вводите данные запроса, просто наберите «quit» и нажмите «Enter».

Тест NamedQuery
 

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
package com.main;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
/**
 * @author Pro JPA 2 book
 * @Empowered by uaiHebert
 *
 */
public class NamedQueryTester extends AbstractQueryTester {
 public static void main(String[] args) throws IOException {
  CodeGenerator.startConnection();
 
  CodeGenerator.generateData();
 
  EntityManager em = CodeGenerator.getEntityManager();
 
  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 
  for (;;) {
   System.out.print("Type the NamedQuery name > ");
   String namedQueryName = reader.readLine();
 
   if (namedQueryName.equals("quit")) {
    break;
   }
 
   if (namedQueryName.length() == 0) {
    continue;
   }
 
   System.out.println("Type the namedQuery parameters.");
   System.out.println("Press enter for 0 parameters");
   System.out.println("Or type all paramters like: id-2,name-4");
   String parameters = reader.readLine();
 
   try {
    Query query = em.createNamedQuery(namedQueryName);
 
    if (parameters.length() > 0) {
     populateParameters(query, parameters);
    }
 
    @SuppressWarnings("rawtypes")
    List result = query.getResultList();
 
    if (result.size() > 0) {
     int count = 0;
     for (Object o : result) {
      System.out.print(++count + " ");
      printResult(o);
     }
    } else {
     System.out.println("0 results returned");
    }
   } catch (Exception e) {
    System.err.println(e.getClass() + e.getMessage());
   }
  }
 
  CodeGenerator.closeConnection();
 }
}

Приведенный выше код запустит транзакцию, создаст данные базы данных в оперативной памяти и разрешит тестирование NamedQueries, настроенных в модельных классах. Чтобы проверить NamedQueries, просто введите их имя в консоли.

Чтобы завершить цикл, не вводите имя NamedQuery, просто введите «quit» и нажмите «Enter».

Запуск приложения

Запустите класс DynamicQueryTester и введите в консоли следующий текст: « выберите p из Person p ». Дважды нажмите клавишу ввода, и в консоли появится следующий текст:

При первом нажатии клавиши «Ввод» будет отображено сообщение с просьбой указать параметры, если они есть.

Введите в консоли: « выберите p из Person p, где p.age>: age » и нажмите «Enter». Введите параметр: « возраст-69-целое ». Нажмите ввод, и результат будет показан ниже:

Чтобы завершить код, который выполняется, введите слово «выход» и нажмите кнопку «Ввод».

Запустите сейчас код класса NamedQueryTester.

Введите имя NamedQuery «Dog.FindByPerson» в консоли и нажмите ввод. Введите параметр «personObject-1-person», и результат будет показан ниже:

Используя этот почтовый индекс с кодом вашего приложения

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

Добавьте свой модельный класс в проект этого поста :

  • Скопируйте класс модели в пакет «com.model».
  • Настройте файл persistence.xml для доступа к базе данных.

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

  • Установите постоянство, найденное в классе CodeGenerator.
  • Используйте библиотеку Apache, находящуюся в папке libs: « commons-lang3-3-1.jar ».

Для обоих подходов требуется шаг ниже:

  • Отредактируйте метод « AbstractQueryTester.configureParameterValue », чтобы он принимал все значения атрибутов / типы / классы, которые будут использоваться с запросами.
  • Измените конфигурацию « hibernate.hbm2ddl.auto » на «none» или «validate». Эта конфигурация находится в файле « persistence.xml ».
  • Вызывайте только методы для запуска / закрытия соединения в классе CodeGenerator.

Предложения

Ниже вы найдете несколько предложений, касающихся этого кода:

  • Используйте технику Reflection для создания классов в методе « AbstractQueryTester.configureParameterValue ».
  • Примените Jenkins для проверки правильности синтаксиса некоторых JPQL после коммита.

Конец

Я надеюсь, что этот пост может помочь вам.

Нажмите здесь, чтобы загрузить исходный код этого поста .

Если у вас есть какие-либо сомнения / предложения, просто опубликуйте их.

Ссылка: Как проверить ваш JPQL / HQL без Deploy от нашего партнера по JCG Хеберта Коэльо в блоге uaiHebert .