Рубрики
Без рубрики

Простая реализация тегов с помощью JPA

Узнайте, как вы можете расширить свой проект JPA с помощью реализации тегов

Автор оригинала: baeldung.

1. Обзор

Теги-это стандартный шаблон проектирования, который позволяет нам классифицировать и фильтровать элементы в нашей модели данных.

В этой статье мы реализуем тегирование с помощью Spring и JPA. Для выполнения этой задачи мы будем использовать данные Spring. Кроме того, эта реализация будет полезна, если вы хотите использовать Hibernate.

Это вторая статья из серии, посвященная внедрению тегов. Чтобы узнать, как реализовать его с помощью Elasticsearch, перейдите сюда .

2. Добавление тегов

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

@Entity
public class Student {
    // ...

    @ElementCollection
    private List tags = new ArrayList<>();

    // ...
}

Обратите внимание на использование аннотации ElementCollection в нашем новом поле. Поскольку мы работаем перед хранилищем данных, нам нужно рассказать ему, как хранить наши теги.

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

Это создает отношение “Один ко многим” между нашей сущностью и тегами! Здесь мы реализуем простейшую версию тегирования. Из-за этого у нас потенциально будет много повторяющихся тегов (по одному для каждой сущности, у которой он есть). Мы поговорим об этой концепции позже.

3. Построение запросов

Теги позволяют нам выполнять некоторые интересные запросы к нашим данным. Мы можем искать объекты с определенным тегом, фильтровать сканирование таблицы или даже ограничивать результаты, возвращаемые в конкретном запросе. Давайте рассмотрим каждый из этих случаев.

3.1. Поиск тегов

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

Вот как мы ищем объект, содержащий определенный тег:

@Query("SELECT s FROM Student s JOIN s.tags t WHERE t = LOWER(:tag)")
List retrieveByTag(@Param("tag") String tag);

Поскольку теги хранятся в другой таблице, нам нужно ОБЪЕДИНИТЬ их в нашем запросе – это вернет все объекты Student с соответствующим тегом.

Во-первых, давайте настроим некоторые тестовые данные:

Student student = new Student(0, "Larry");
student.setTags(Arrays.asList("full time", "computer science"));
studentRepository.save(student);

Student student2 = new Student(1, "Curly");
student2.setTags(Arrays.asList("part time", "rocket science"));
studentRepository.save(student2);

Student student3 = new Student(2, "Moe");
student3.setTags(Arrays.asList("full time", "philosophy"));
studentRepository.save(student3);

Student student4 = new Student(3, "Shemp");
student4.setTags(Arrays.asList("part time", "mathematics"));
studentRepository.save(student4);

Затем давайте проверим его и убедимся, что он работает:

// Grab only the first result
Student student2 = studentRepository.retrieveByTag("full time").get(0);
assertEquals("name incorrect", "Larry", student2.getName());

Мы вернем первого студента в хранилище с тегом full time . Это именно то, чего мы хотели.

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

List students = studentRepository.retrieveByTag("full time");
assertEquals("size incorrect", 2, students.size());

С помощью небольшого рефакторинга мы можем изменить репозиторий, чтобы использовать несколько тегов в качестве фильтра, чтобы мы могли еще больше улучшить наши результаты.

3.2. Фильтрация запроса

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

Поскольку нам также нужно фильтровать другие поиски, давайте рассмотрим пример:

@Query("SELECT s FROM Student s JOIN s.tags t WHERE s.name = LOWER(:name) AND t = LOWER(:tag)")
List retrieveByNameFilterByTag(@Param("name") String name, @Param("tag") String tag);

Мы видим, что этот запрос почти идентичен приведенному выше. Тег | – это не что иное, как еще одно ограничение для использования в нашем запросе.

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

Student student2 = studentRepository.retrieveByNameFilterByTag(
  "Moe", "full time").get(0);
assertEquals("name incorrect", "moe", student2.getName());

Следовательно, мы можем применить тег filter к любому запросу на эту сущность. Это дает пользователю много возможностей в интерфейсе, чтобы найти точные данные, которые им нужны.

4. Расширенная маркировка

Наша простая реализация тегов-отличное место для начала. Но из-за отношений “Один ко многим” мы можем столкнуться с некоторыми проблемами.

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

Кроме того, наша модель Tag не очень надежна. Что делать, если мы хотим отслеживать, когда тег был изначально создан? В нашей нынешней реализации у нас нет возможности сделать это.

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

Отношения “Многие ко многим” решат большинство наших проблем. Чтобы узнать, как использовать аннотацию @manytomany , ознакомьтесь с этой статьей (поскольку это выходит за рамки данной статьи).

5. Заключение

Тегирование-это простой и понятный способ запроса данных, и в сочетании с API сохранения Java у нас есть мощная функция фильтрации, которая легко реализуется.

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

Как всегда, код, используемый в этой статье, можно найти на на GitHub .