Hibernate - есть способ удаления ребенка по id с помощью orphanremoval = true? - PullRequest
0 голосов
/ 04 мая 2020

Я пытаюсь удалить дочернюю сущность по идентификатору в отношениях «один ко многим к одному» с spring-data-jpa, но когда я пытаюсь посмотреть на дочернюю сущность с точки зрения родителя, она все еще там, но ее нет в реальной базе данных

дочерняя сущность:

package com.yoav.todolist.models;

import com.fasterxml.jackson.annotation.JsonIgnore;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "tasks")
public class Task {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;

    @Column(name = "task")
    private String task;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnore
    private Account account;

    @Temporal(TemporalType.DATE)
    @Column(name = "date_of_creation_task")
    private Date date;

    public Task(String task) {
        this.date = new Date();
        this.task = task;
    }

    public Task() {
        this.date = new Date();
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTask() {
        return task;
    }

    public void setTask(String task) {
        this.task = task;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public boolean equals(Object task) {
        return ((Task)task).getId() == this.id;
    }

    @Override
    public String toString() {
        return "Task{" +
                "id=" + id +
                ", task='" + task + '\'' +
                '}';
    }

    @Override
    public int hashCode() {
        return 31;
    }

}

родительская сущность:

package com.yoav.todolist.models;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "accounts")
public class Account {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    private String password;

    @OneToMany(
            mappedBy = "account",
            orphanRemoval = true,
            cascade = CascadeType.ALL
    )
    private List<Task> tasks = new ArrayList<>();

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public Account() {
    }

    public void removeTask(Task task) {
        tasks.remove(task);
        task.setAccount(null);
    }

    public void addTask(Task task) {
        tasks.add(task);
        task.setAccount(this);
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks.forEach(i -> i.setAccount(null));
        this.tasks.clear();

        tasks.forEach(i -> {
            i.setAccount(this);
            addTask(i);
        });
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object account) {
        return ((Account)account).getUsername().equals(this.username);
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

метод удаления по идентификатору: (в JpaRepository)

@Query(value = "DELETE FROM tasks WHERE tasks.id = ?1", nativeQuery = true)
@Transactional
@Modifying
@Override
void deleteById(int id);

тест (который доказывает мою проблему):

        Account account = new Account("person", "person");
        account.addTask(new Task("java"));
        account.addTask(new Task("spring"));
        accountDao.add(account);

        MockHttpSession session = new MockHttpSession();
        session.setAttribute("isAdmin", true);

        System.out.println(accountDao.findByUsername("person").orElse(new Account()).getTasks());
        assertThat(accountDao.findByUsername("person").orElse(new Account()).getTasks()).hasSize(2);

        mockMvc.
                perform(post("/admin/person").session(session).
                        param(
                                "deleteTask",
                                String.valueOf(accountDao.
                                        findByUsername("person").orElse(new Account()).getTasks().get(0).getId())
                        )).
                andExpect(status().is3xxRedirection()).
                andExpect(redirectedUrl("/admin/person"));



        // here is the main problem here I get the both child's
        // account.addTask(new Task("java"));
        // account.addTask(new Task("spring"));
        System.out.println(accountDao.findByUsername("person").orElse(new Account()).getTasks());



        // but here i get only one child and i dont see the child that i removed 
        System.out.println(taskDao.getAll());



        assertThat(accountDao.findByUsername("person").orElse(new Account()).getTasks()).hasSize(1);

контроллер:

@RequestMapping(value = "/admin/{usernameOfAccount}", method = RequestMethod.POST, params = "deleteTask")
    public String postDeleteTasksOfUsername(
            @RequestParam String deleteTask,
            @PathVariable String usernameOfAccount,
            HttpSession session) {

        if (session.getAttribute("isAdmin") == null) return "unauthorized";
        int idOfDeletingTask = Integer.parseInt(deleteTask);
        taskService.deleteById(idOfDeletingTask);
        return "redirect:/admin/" + usernameOfAccount; // redirecting back to the list of tasks of the specifying username
    }

1 Ответ

1 голос
/ 05 мая 2020
// here is the main problem here I get the both child's
// account.addTask(new Task("java"));
// account.addTask(new Task("spring"));
System.out.println(accountDao.findByUsername("person").orElse(new Account()).getTasks());

Является ли ваша тестовая транзакция?

Если это так, то возвращаемый здесь Account уже загружен в контекст постоянства, поэтому вы получаете точно такой же экземпляр вы получили от предыдущих вызовов accountDao.findByUsername("person"), что также точно такой же экземпляр , который вы сохраняли, используя accountDao.add(account) ранее.

Попробуйте ввести EntityManager в свой тест и вызвать entityManager.flush(), а затем entityManager.clear() сразу после удаления задачи.

...