Hibernate – 有一种使用 orphanremoval = true 通过子项 ID 删除子项的方法吗?

huangapple go评论81阅读模式
英文:

Hibernate - there is a way of deleting child by id with orphanremoval = true?

问题

下面是翻译好的内容:

子实体:

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;

    // ... 其他方法和属性
}

父实体:

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<>();

    // ... 其他方法和属性
}

删除方法:

@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"));

System.out.println(accountDao.findByUsername("person").orElse(new Account()).getTasks());
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;
}

以上是你提供的代码的翻译。如果你有其他问题或需要进一步帮助,请随时提问。

英文:

I am trying to delete a child entity by id in many to one relationship with spring-data-jpa but when I try to look at the child's from the parent perspective its still there but its not in the actual database

the child entity:

package com.yoav.todolist.models;

import com.fasterxml.jackson.annotation.JsonIgnore;

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

@Entity
@Table(name = &quot;tasks&quot;)
public class Task {

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

    @Column(name = &quot;task&quot;)
    private String task;

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

    @Temporal(TemporalType.DATE)
    @Column(name = &quot;date_of_creation_task&quot;)
    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 &quot;Task{&quot; +
                &quot;id=&quot; + id +
                &quot;, task=&#39;&quot; + task + &#39;\&#39;&#39; +
                &#39;}&#39;;
    }

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

}

the parent entity:

package com.yoav.todolist.models;

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

@Entity
@Table(name = &quot;accounts&quot;)
public class Account {

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

    @Column(name = &quot;username&quot;)
    private String username;

    @Column(name = &quot;password&quot;)
    private String password;

    @OneToMany(
            mappedBy = &quot;account&quot;,
            orphanRemoval = true,
            cascade = CascadeType.ALL
    )
    private List&lt;Task&gt; tasks = new ArrayList&lt;&gt;();

    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&lt;Task&gt; getTasks() {
        return tasks;
    }

    public void setTasks(List&lt;Task&gt; tasks) {
        this.tasks.forEach(i -&gt; i.setAccount(null));
        this.tasks.clear();

        tasks.forEach(i -&gt; {
            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 &quot;Account{&quot; +
                &quot;id=&quot; + id +
                &quot;, username=&#39;&quot; + username + &#39;\&#39;&#39; +
                &quot;, password=&#39;&quot; + password + &#39;\&#39;&#39; +
                &#39;}&#39;;
    }
}

the delete by id method: (in the JpaRepository)

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

the test(that prove my problem):

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

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

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

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



        // here is the main problem here I get the both child&#39;s
        // account.addTask(new Task(&quot;java&quot;));
        // account.addTask(new Task(&quot;spring&quot;));
        System.out.println(accountDao.findByUsername(&quot;person&quot;).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(&quot;person&quot;).orElse(new Account()).getTasks()).hasSize(1);

the controller:

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

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

答案1

得分: 1

// 这里是主要的问题,我在这里得到了两个子节点
// 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()
英文:
// here is the main problem here I get the both child&#39;s
// account.addTask(new Task(&quot;java&quot;));
// account.addTask(new Task(&quot;spring&quot;));
System.out.println(accountDao.findByUsername(&quot;person&quot;).orElse(new Account()).getTasks());

Is your test transactional?

If so, then the Account that is being returned here is already loaded into the persistence context, so you're getting the exact same instance you got from the previous calls to accountDao.findByUsername(&quot;person&quot;), which is also the exact same instance you persisted using accountDao.add(account) earlier on.

Try injecting EntityManager into your test and calling entityManager.flush() followed by entityManager.clear() right after deleting the task.

huangapple
  • 本文由 发表于 2020年5月5日 00:34:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/61597132.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定