JPA (Java Persistance API) — JPA — это спецификация Java, которая используется для сопоставления объектов Java с реляционной базой данных, выступая в качестве моста между ними. Он не выполняет операции сам по себе, но требует реализации с помощью инструментов ORM, таких как Hibernate.

Реляционное сопоставление объектов (ORM) — это сопоставление объектов Java с таблицами базы данных.

JPA используется для непосредственной работы с объектами, а не для выполнения операций с помощью операторов SQL. Метаданные постоянства (определяемые аннотациями XML-файла) определяют сопоставление между объектами Java и таблицами базы данных.

JPA определяет SQL-подобный язык запросов для реализации статических и динамических запросов. JPA JPQL — это объектно-ориентированный язык запросов, используемый для выполнения операций с базой данных. Он использует объект сущности для выполнения операций с записями базы данных. Затем запросы JPQL преобразуются в SQL с использованием реализации JPA. Это можно использовать с базами данных любого типа, такими как MySQL, H2, Oracle и т. д.

Выполнение

Стек технологий

Весенний загрузочный фреймворк

Мавен 3.6.3

MySQL 8.0.19

Настройка проекта

  1. Перейти на https://start.spring.io/
  2. Выберите тип проекта в зависимости от того, какой инструмент сборки вы хотите использовать. Мы выбрали maven в качестве инструмента сборки.
  3. Выберите версию Spring Boot
  4. Заполните информацию о метаданных проекта.
  5. Добавьте зависимости. В случае с этим проектом нам потребуются Spring Web, Spring Data JPA, драйвер MySQL.
  6. Создайте проект и импортируйте его в предпочитаемый вами редактор.

Настройка базы данных

  • Создайте базу данных employees в MySQL.
  • Создайте таблицу employee
CREATE DATABASE employees;

USE employees;

CREATE TABLE employee(
id int not null auto_increment primary key,
name varchar(50),
gender varchar(10),
experience_in_years int,
experience_in_months int,
experience_in_days int
);

Модель сотрудника будет содержать следующие атрибуты: id, имя, пол, опыт в годах, опыт в месяцах, опыт в днях.

Добавить конфигурацию базы данных с помощью application.properties

Конфигурация источника данных добавляется в application.properties.

spring.datasource.url указывает URL базы данных

spring.datasource.username и spring.datasource.password указывают имя пользователя и пароль используемой базы данных.

spring.jpa.hibernate.ddl-auto – это свойство Spring Data JPA, которое управляет преобразованием схемы базы данных при запуске приложения. Возможные значения: create (создает новую схему при каждом запуске приложения), create-drop (схема удаляется после остановки приложения и создается только после приложение запускается снова), update(обновляет схему в случае каких-либо изменений)и validate(только проверяет схему, не внося никаких изменений ).

Свойство spring.jpa.properties.hibernate.dialect позволяет hibernate генерировать лучший SQL для базы данных

spring.jpa.show-sql свойство, если установлено значение true, регистрирует SQL-запросы в консоли.

spring.datasource.url = jdbc:mysql://localhost:3306/employees
spring.datasource.username = 
spring.datasource.password = 
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql=true

Определить объект

Базовой единицей персистентности в JPA является сущность . Это класс Java, который использует метаданные, описанные в виде аннотаций или определенные в файле XML (в этом примере используются аннотации), чтобы сопоставить класс с таблицей базы данных.

Аннотация @Entity используется для всех классов, которые необходимо сохранить в базе данных. Экземпляр класса сохраняется как строка в таблице базы данных.

Имя таблицы базы данных можно определить специально с помощью аннотации @Table(name="______"). В противном случае таблица принимает имя соответствующего класса.

@Id используется для определения первичного ключа

Первичный ключ может быть автоматически сгенерирован в базе данных с помощью аннотации @GeneratedValue.

Поля в Entity сохраняются как столбцы в таблице базы данных. Имя столбца можно определить специально с помощью аннотации @Column(name="______"). В противном случае столбец принимает имя соответствующего поля.

Если поле не нужно сохранять в базе данных как столбец, его можно аннотировать с помощью @Transient .

Различные способы выполнения запроса с использованием JPQL

Прежде чем приступить к реализации запросов, важно получить экземпляр EntityManager.

EntityManager может быть управляемым контейнером или управляемым приложением.

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

В этом примере мы используем управляемый контейнером экземпляр EntityManager, где контейнер отвечает за создание экземпляра из EntityManagerFactory для нас, начало транзакции, ее фиксацию или откат.

@PersistenceContext
EntityManager entityManager;

Запросы JPQL могут выполняться по-разному.

  • создать запрос

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

Запрос извлекает соответствующую запись из таблицы базы данных, а также сопоставляет ее с объектом Entity.

private final String GET_EMPLOYEE_QUERY = "SELECT employee FROM Employee";
@Transactional
public List<Employee> getAllEmployees() {
    List<Employee> employees = new ArrayList<>();
    Query getAllQuery =
        entityManager.createQuery(GET_EMPLOYEE_QUERY);
    employees = (List<Employee>) getAllQuery.getResultList();
    return employees;
}

Метод getAllEmployees извлекает список сотрудников с помощью метода createQuery(String jpql). Запрос JPQL определен в GET_EMPLOYEE_QUERY. Вместо упоминания имени таблицы (employee) в запросе упоминается имя сущности (Employee). Поскольку JPA не может определить тип результата, нам нужно преобразовать тип результата перед его возвратом. Метод getResultList() используется для получения списка результатов после выполнения запроса JPQL.

Типизированный запрос

JPA также предоставляет специальный подтип Query, известный как TypedQuery, в качестве решения JPA, который не может определить, какой будет тип результата. Это хорошее решение, когда тип результата известен заранее, а также помогает избежать приведения исключений.

private final String GET_EMPLOYEE_BY_YEARS_OF_EXPERIENCE = "SELECT employee FROM Employee employee WHERE experienceInYears=?1";
@Transactional
public List<Employee> getEmployeeByYearsOfExperience(int years) {
    List<Employee> employees = new ArrayList<>()
    TypedQuery<Employee> getQueryByYear = entityManager
.createQuery(GET_EMPLOYEE_BY_YEARS_OF_EXPERIENCE,      Employee.class).setParameter(1, years);
    employees = getQueryByYear.getResultList();
    return employees;
}

Метод getEmployeesByYearsOfExperience извлекает список сотрудников с указанным опытом работы с использованием метода createQuery(String jpql). Запрос JPQL определен в GET_EMPLOEES_BY_YEARS_OF_EXPERIENCE.

Позиционный параметр использовался для указания года опыта.

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

Позиционные параметры объявляются со знаком вопроса (?), за которым следует числовая позиция параметра в запросе. Мы начинаем с 1 и увеличиваем позицию с каждым параметром. Метод Query.setParameter(integer position, Object value) используется для установки значений параметров. Мы также можем использовать один и тот же параметр более одного раза в одном запросе.

Мы можем выполнять запросы, используя следующие методы интерфейса запросов:

executeUpdate() — этот метод выполняет операцию обновления и удаления.

getFirstResult() — этот метод возвращает первый позиционированный результат, на получение которого был настроен объект запроса.

getMaxResults() — этот метод возвращает максимальное количество результатов, на получение которых был настроен объект запроса.

getResultList() — этот метод возвращает список результатов в виде нетипизированного списка.

getSingleResult() — этот метод возвращает один нетипизированный результат.

  • создатьИменованныйЗапрос

Метод createNamedQuery используется для выполнения статических запросов. Запросы можно определить в метаданных с помощью аннотации NamedQuery в самом классе Entity. Элемент имени @NamedQuery указывает имя запроса, которое будет использоваться с методом createNamedQuery. Все NamedQueries должны иметь уникальное имя.

@Transactional
public List<Employee> getEmployeeByMonthsOfExperience(int months) {
    List<Employee> employees = new ArrayList<>();
    Query getQueryByMonth = entityManager.createNamedQuery("findEmployeeByMonthsOfExperience")
.setParameter(1, months); 
    employees = (List<Employee>) getQueryByMonth.getResultList();
    return employees;
}

Метод getEmployeesByMonthsOfExperience извлекает список сотрудников, имеющих опыт работы в указанных месяцах, с помощью метода createNamedQuery(String name). Имя NamedQuery указывается в метаданных.

@Entity
@Table(name = "employee")
@NamedQuery(name = "findEmployeeByMonthsOfExperience", query = "SELECT employee FROM Employee employee WHERE experienceInMonths=?1")
public class Employee {
    //
    //
    //
}
  • создатьNativeQuery

Метод createNativeQuery используется для выполнения собственных/чистых запросов SQL.

NativeQuery — это просто SQL-запрос. Это позволяет нам использовать функции, недоступные в синтаксисе JPQL. Однако мы теряем переносимость базы данных приложения.

private final String GET_EMPLOYEE_BY_DAYS_OF_EXPERIENCE = "SELECT * FROM employee WHERE experience_in_days=?1";
@Transactional
public List<Employee> getEmployeeByDaysOfExperience(int days) {
    List<Employee> employees = new ArrayList<>();
    Query getQueryByDay =  entityManager.createNativeQuery(GET_EMPLOYEE_BY_DAYS_OF_EXPERIENCE).setParameter(1, days);
    employees = (List<Employee>) getQueryByDay.getResultList();
    return employees;
}

Метод getEmployeesByDaysOfExperience извлекает список сотрудников с указанным опытом работы с использованием метода createNativeQuery(String sql). Запрос SQL определен в GET_EMPLOEES_BY_DAYS_OF_EXPERIENCE. Позиционные параметры также можно использовать с собственными запросами SQL.

Некоторые дополнительные методы -:

а) СОЗДАТЬ

Метод persist объекта EntityManager используется для сохранения экземпляра Employee в базе данных.

@Transactional
public void createEmployee(Employee employee) {
    entityManager.persist(employee);
}

б) ОБНОВИТЬ И УДАЛИТЬ

Обновление и удаление экземпляра Employee с помощью метода createQuery объекта EntityManager и метода executeUpdate интерфейса Query.

private final String UPDATE_EMPLOYEE_QUERY = "UPDATE Employee SET name=?1,gender=?2,experienceInYears=?3,experienceInMonths=?4,experienceInDays=?5 WHERE id=?6";
private final String DELETE_EMPLOYEE_QUERY = "DELETE FROM Employee WHERE id=?1";
@Transactional
public void updateEmployee(Employee employee, int id) {
    Query updateQuery = entityManager.createQuery(UPDATE_EMPLOYEE_QUERY)
.setParameter(1, employee.getName())
.setParameter(2, employee.getGender())
.setParameter(3, employee.getExperienceInYears())
.setParameter(4, employee.getExperienceInMonths())
.setParameter(5, employee.getExperienceInDays())
.setParameter(6, id);
    updateQuery.executeUpdate();
}
@Transactional
public void deleteEmployee(int id) {
    Query deleteQuery = entityManager.createQuery(DELETE_EMPLOYEE_QUERY)
.setParameter(1, id);
    deleteQuery.executeUpdate();
}

в) ПОЛУЧИТЬ ПО ID

Этот метод извлекает запись о сотруднике на основе переданного идентификатора.

private final String GET_EMPLOYEE_BY_ID_QUERY = "SELECT employee FROM Employee employee WHERE id =?1";
@Transactional
public Employee getEmployeeById(int id) {
    Employee employee = new Employee();
    Query getQueryById = entityManager.createQuery(GET_EMPLOYEE_BY_ID_QUERY)
.setParameter(1, id);
    employee = (Employee) getQueryById.getSingleResult();
    return employee;
}

г) МЕЖДУ… И…

Запрос JPQL для получения списка сотрудников на основе диапазона указанных значений выполняется следующим методом.

private final String GET_EMPLOYEE_BETWEEN_YEARS_OF_EXPERIENCE = "SELECT employee FROM Employee employee WHERE experience_in_years BETWEEN ?1 and ?2";
@Transactional
public List<Employee> getEmployeeBetweenYearsOfExperience(int start, int end) {
    List<Employee> employees = new ArrayList<>();
    Query getQueryBetweenYear = entityManager.createQuery(GET_EMPLOYEE_BETWEEN_YEARS_OF_EXPERIENCE)
.setParameter(1, start).setParameter(2, end);
    employees = (List<Employee>)getQueryBetweenYear.getResultList();
    return employees;
}

e) COUNT, MAX И MIN

Запросы JPQL для выполнения методов агрегирования, таких как получение количества сотрудников, максимального опыта работы и минимального опыта работы, выполняются в следующем методе.

private final String GET_EMPLOYEE_COUNT = "SELECT COUNT(employee) FROM Employee employee";
private final String GET_MAXIMUM_EXPERIENCE_IN_YEARS = "SELECT MAX(experienceInYears) FROM Employee";
private final String GET_MINIMUM_EXPERIENCE_IN_YEARS = "SELECT MIN(experienceInYears) FROM Employee";
@Transactional
public Map<String, Object> getEmployeeStats() {
    Map<String, Object> employeeStats = new HashMap<>();
    Query employeeCount = entityManager.createQuery(GET_EMPLOYEE_COUNT);
    Query maximumExperienceInYear = entityManager.createQuery(GET_MAXIMUM_EXPERIENCE_IN_YEARS);
    Query minimumExperienceInYear = entityManager.createQuery(GET_MINIMUM_EXPERIENCE_IN_YEARS);
    employeeStats.put("employeeCount", (Long)    employeeCount.getSingleResult());
    employeeStats.put("maximumExperienceInYear", (Integer) maximumExperienceInYear.getSingleResult());
    employeeStats.put("minimumExperienceInYear", (Integer) minimumExperienceInYear.getSingleResult());
    return employeeStats;
}

е) СОРТИРОВКА

Запрос JPQL для получения записей в отсортированном порядке. Следующий метод извлекает записи, отсортированные в убывающем порядке на основе многолетнего опыта.

private final String GET_EMPLOYEE_SORTED_BY_YEAR = "SELECT employee FROM Employee employee ORDER BY experienceInYears DESC";
@Transactional
public List<Employee> getEmployeeSortedByYearExperience() {
    List<Employee> employees = new ArrayList<>();
    Query getQuerySortedByYear = entityManager.createQuery(GET_EMPLOYEE_SORTED_BY_YEAR);
    employees = (List<Employee>) getQuerySortedByYear.getResultList();
    return employees;
}

Найдите реализованный код здесь.