Spring Data JPA метод deleteById не удаляет сущность
Метод deleteById Spring Data Jpa не удаляет объект из БД, хотя отрабатывает.
Читал, что это из-за того, что объект имеет связь ManyToOne и ссылка на него остается в родительской сущности, которая хранит список этих объектов, и сначала надо удалить объект из этого списка. Но как это сделать, чтобы не подгружать весь список объектов, которых могут быть тысячи?
Рылся в PgAdmin (графический интерфейс управления БД PostgreSQL): там я вообще не нашел таблиц связей (вроде entity1_entity2) между основными таблицами.
CascadeType у меня почему-то тоже не работает.
Именно не получается удалить объект Comment, который связан отношением ManyToOne к сущностям Person и Task.
Вот две родительские сущности Person и Task:
@Entity
@Table(name = "people")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
@Email
private String email;
@Size(min = 5, message = "Пароль должен содержать минимум 5 символов")
private String password;
private transient String passwordConfirm;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "author")
private List<Task> createdTasks;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "executor")
private List<Task> executableTasks;
@CreatedDate
private LocalDateTime registrationDate;
public Person(){}
// getters, setters
@Entity
@Table(name = "tasks")
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Заголовок не может быть пустым")
private String title;
@NotBlank(message = "Описание не может быть пустым")
private String description;
@NotNull(message = "У задачи должен быть статус")
@Enumerated(EnumType.STRING)
private Status status;
@NotNull(message = "У задачи должен быть приоритет")
@Enumerated(EnumType.STRING)
private Priority priority;
@ManyToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name = "author_id", referencedColumnName = "id")
private Person author;
@ManyToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name = "executor_id", referencedColumnName = "id")
private Person executor;
@NotNull(message = "У задачи должен быть исполнитель")
private transient long executorId;
@OneToMany(mappedBy = "task")
private List<Comment> comments;
@CreatedDate
private LocalDateTime createdAt;
private LocalDateTime editedAt;
public Task(){}
// getters, setters
Дочерняя сущность Comment:
@Entity
@Table(name = "comments")
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Комментарий не может быть пустым")
private String body;
@ManyToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name = "task_id", referencedColumnName = "id")
private Task task;
private transient long taskId;
@ManyToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name = "author_id", referencedColumnName = "id")
private Person author;
@CreatedDate
private LocalDateTime createdAt;
private LocalDateTime editedAt;
public Comment(){}
// getters, setters
Метод контроллера:
@DeleteMapping("/comment/delete/confirm/{id}")
public String deleteComment(@PathVariable long id, Model model, @AuthenticationPrincipal PersonDetails pd) {
Comment comment = commentService.findById(id);
if (pd == null || comment.getAuthor().getId() != pd.getPerson().getId())
throw new MismatchIdentifierException("ID автора комментария и Ваш не совпадают. Похоже, что Вы не являетесь автором.");
commentService.deleteById(id);
System.out.println("\n\n\n\ndeleting succesful");
model.addAttribute("task", comment.getTask());
return showTask(comment.getTask().getId(), model, pd);
}
В результате выводится сообщение "deleting succesful".
Метод сервиса:
public void deleteById(long id) {
commentRepository.deleteById(id);
}
Репозиторий:
@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query(value = "SELECT * from comments WHERE task_id = ?1", nativeQuery = true)
List<Comment> findByTask(long id);
@Query(value = "SELECT * from comments WHERE author_id = ?1", nativeQuery = true)
List<Comment> findByAuthor(long id);
Page<Comment> findByAuthor_id(long id, Pageable pageable);
}
Ответы (1 шт):
В общем решением было во-первых повесить аннотацию @Transactional над методом в сервисе (так как я выложил не весь код и не упомянул, что у меня над классом сервиса висит аннотация @Transactional(readOnly = true)):
@Transactional(readOnly = false)
public void deleteById(long id) {
commentRepository.deleteById(id);
}
А во-вторых явно объявить метод удаления с нативным SQL запросом в репозитории:
@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query(value = "SELECT * from comments WHERE task_id = ?1", nativeQuery = true)
List<Comment> findByTask(long id);
@Query(value = "SELECT * from comments WHERE author_id = ?1", nativeQuery = true)
List<Comment> findByAuthor(long id);
Page<Comment> findByAuthor_id(long id, Pageable pageable);
@Modifying
@Query(value = "DELETE FROM comments WHERE id = ?1", nativeQuery = true)
void deleteById(long id);
}
Теперь удаление работает.
Каскадирование не отключал - я думаю, там все правильно, тем более что при проверочном удалении объекта Comment объекты Task и Person не удалились.