0

In @OneToMany bidirectional, When EntityManger update the ProjectValue which has the collection of Task, EntityManger cannot delete child Task which remove by the program in memory. Will delete that Task by using other function?

DataStructure : 

    In ProjectPlan Table :
        PRPJ0001, First Poject
    In Task Table :
        AAA, AAA, PRPJ0001
        BBB, BBB, PRPJ0001
        CCC, CCC, PRPJ0001
    In Memory :
        PRPJ0001, First Poject
            AAA, AAA, PRPJ0001          -->  No Changes
            BBB, Change Name, PRPJ0001  -->  Need to update
                                        -->  Need to delete CCC
            NEW, NEW, PRPJ0001          -->  Need to add 

    Problem : Entity Manger cannot delete CCC;

    @Entity
    public class ProjectPlan {
        @Id
        private String id;
        private String name;
        @OneToMany(mappedBy = "projectPlan", targetEntity = Task.class, cascade = {CascadeType.ALL})
        private List<Task> taskList;

        public ProjectPlan(){}

        public ProjectPlan(String id, String name, List<Task> taskList) {
            this.id = id;
            this.name = name;
            this.taskList = taskList;
        }
        //getter & setter
    }

    @Entity
    public class Task {
        @Id
        private String id;
        private String name;
        @ManyToOne(targetEntity = ProjectPlan.class, cascade = {CascadeType.REFRESH})
        @JoinColumn(name = "projectId", referencedColumnName = "ID")
        private ProjectPlan projectPlan;

        public Task(){
        }

        public Task(String id, String name, ProjectPlan projPlan) {
            this.id = id;
            this.name = name;
            this.projectPlan = projPlan;
        }
        // getter & setter
    }

    public class Test {
        public static void main(String[] args) {
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("JPA");
            EntityManager em = emf.createEntityManager();
            /*
             Existing Data in Database
                List<Task> taskList = new ArrayList<Task>();
                ProjectPlan plan = new ProjectPlan("PRPJ0001", "First Poject", taskList);
                Task task1 = new Task("AAA","AAA", plan);
                Task task2 = new Task("BBB","BBB", plan);
                Task task3 = new Task("CCC","CCC", plan);
                taskList.add(task1);
                taskList.add(task2);
                taskList.add(task3);
            */

            em.getTransaction().begin();
            List<Task> taskList = new ArrayList<Task>();
            ProjectPlan plan = em.find(ProjectPlan.class, "PRPJ0001");

            /*Task "AAA" no changes*/
            Task task1 = new Task("AAA","AAA", plan);

            /*Task "BBB" updated info*/
            Task task2 = new Task("BBB","Change Name", plan);

            /*Task "CCC" removed*/

            /*Task "NEW" newly created*/
            Task task3 = new Task("NEW","NEW", plan);
            taskList.add(task1);
            taskList.add(task2);
            taskList.add(task3);

            plan.setTaskList(taskList);

            em.merge(plan);
            em.getTransaction().commit();
            em.close();
        }
    }

If Task have another Sub Task List instance(Recursive Relationship), Task also will be face this situation. I have to check and retrieve the task which is need to delete pragmatically? Do you think JPA should support this feature?

Zaw Than oo
  • 9,651
  • 13
  • 83
  • 131

2 Answers2

1

Use attribute orphanRemoval on annotation OneToMany.

Explanation:

When a target entity in one-to-one or one-to-many relationship is removed from the relationship, it is often desirable to cascade the remove operation to the target entity. Such target entities are considered “orphans,” and the orphanRemoval attribute can be used to specify that orphaned entities should be removed.

@Entity
public class ProjectPlan {
    @Id
    private String id;
    private String name;
    @OneToMany(mappedBy = "projectPlan", cascade = {CascadeType.ALL}, orphanRemoval=true)   // --> new attribute
    private List<Task> taskList;

    public ProjectPlan(){}

    public ProjectPlan(String id, String name, List<Task> taskList) {
        this.id = id;
        this.name = name;
        this.taskList = taskList;
    }
    //getter & setter
}

@Entity
public class Task {
    @Id
    private String id;
    private String name;
    @ManyToOne(cascade = {CascadeType.ALL})
    @JoinColumn(name = "projectId", referencedColumnName = "ID")
    private ProjectPlan projectPlan;

    public Task(){
    }

    public Task(String id, String name, ProjectPlan projPlan) {
        this.id = id;
        this.name = name;
        this.projectPlan = projPlan;
    }
    // getter & setter
}
Jhonathan
  • 1,611
  • 2
  • 13
  • 24
  • @CycDemo View this post http://stackoverflow.com/questions/4329577/jpa-2-0-orphanremoval-true-vs-on-delete-cascade – Jhonathan Sep 26 '12 at 15:01
0

A bidirectional association has an owner side, and an inverse side. Hibernate looks at the owner side to know if an association exists. The owner side is the one which doesn't have the mappedBy attribute. So, in this case, the owner side is Task.

So, if you want to detach the task CCC from its project plan, you must set its projectPlan field to null. If you want to delete the task CCC, you must delete it, using em.remove().

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • If `Task` have another `Sub Task List` instance(Recursive Relationship), Task also will be face this situation. I have to check and retrieve the task which is need to delete pragmatically? Do you think `JPA` should support this feature? – Zaw Than oo Sep 21 '12 at 11:31
  • which feature are you talking about? If you want to delete a specifc task, you just need to call `em.remove(em.getReference(Task.class, taskId))`. How is that a problem? – JB Nizet Sep 21 '12 at 11:44