Правильные отношения @ManyToMany
НЕТ FetchType.EAGER
Сначала мы должны взглянуть на реализацию ваших отношений @ManyToMany
. Как подробно объяснил Влад Михальча в , эта статья , FetchType.EAGER
не является хорошим выбором. Это приводит к большому количеству ненужных запросов к базе данных.
NO CascadeType.REMOVE
Кроме того, каскадирование всех изменений может привести к серьезным проблемам. Удаление определенного объекта может быть автоматически связано с удалением множества других объектов. Как описано в этой статье (прокрутите немного вниз), следует избегать CascadeType.REMOVE
для отношений @ManyToMany
. CascadeType.ALL
включает REMOVE
. Вместо этого вы должны использовать @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
Другой пример отношения @ManyToMany
можно найти в этом ответе Stackoverflow .
Инициализация ленивых отношений
Ленивые отношения (например, упомянутые «хорошие» @ManyToMany
) не инициализируются Hibernate (ORM, используемый Spring Data автоматически). В соответствующей переменной можно найти только прокси с соответствующим первичным ключом. Полный объект автоматически загружается из базы данных, когда кто-то пытается получить доступ к переменной. Это приводит к дополнительному запросу к базе данных.
Если вы уже заранее знаете, что объект понадобится (переменная, к которой осуществляется доступ), вы должны инициализировать отношение при запросе родителя из базы данных (когда вы запрашиваете * 1032). * из базы данных следует также загрузить ее idPhoto
.
В SQL это означает объединение этих разных таблиц. В JPA объединение не означает автоматически, что данные загружаются. Следовательно,Вы должны использовать выборочное соединение или граф сущностей для инициализации отложенных аннотаций. Эта статья дает хороший обзор того, как использовать эти различные способы инициализации.
Пример с Criteria API
В JPA существуют различные способы реализации запросов к базе данных: JPQL как язык запросов и API Criteria (проще для рефакторинга, меньше подвержено ошибкам). Так как я фанат API Criteria, я покажу вам пример того, какотношения могут быть инициализированы с ним и использованием графа сущностей.
public Optional<ToolSet> findById(long id) {
EntityGraph<ToolSet> graph = entityManager.createEntityGraph(ToolSet.class);
graph.addAttributeNodes("assignedTo");
Subgraph<Contractor> subgraph = graph.addSubgraph("assignedTo");
subgraph.addAttributeNodes("idPhoto");
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ToolSet> cq = cb.createQuery(ToolSet.class);
Root<ToolSet> root = cq.from(ToolSet.class);
cq.where(cb.equal(root.get("id"), id)); // Assumption of id as PK
TypedQuery<ToolSet> typedQuery = entityManager.createQuery(cq);
typedQuery.setHint("javax.persistence.fetchgraph", graph);
ToolSet response = null;
try {
response = typedQuery.getSingleResult();
}
catch(NoResultException nre) {}
return Optional.ofNullable(response);
}
В этом примере я предполагаю, что ваш первичный ключ для ToolSet
называется id
. Чтобы увидеть ошибки еще лучше во время компиляции, взгляните на hibernate-jpamodelgen. Затем вы можете написать что-то вроде cq.where(cb.equal(root.get(ToolSet_.ID), id))
.
Выше я использую Optional
. Это не должно быть использовано. В любом случае, вы можете заново изучить преимущества его использования.