Hibernate EntityManager.remove(实体)从数据库和关联中删除记录,但不从实体集合

时间:2016-08-24 07:33:30

标签: java hibernate jpa

所以,我有2个实体项目& 任务计划,在项目实体内部, @ManyToMany TaskPlan 实体关联

@Entity
@Table(name = "project")
public class Project {     
@Column(name = "descriptions")
private String descriptions;

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "project_has_task_plan",
        joinColumns = {@JoinColumn(name = "project_id")},
        inverseJoinColumns = {@JoinColumn(name = "task_plan_id")})
private Set<TaskPlan> taskPlans;

public Set<TaskPlan> getTaskPlans() {
    return taskPlans;
}

public void setTaskPlans(Set<TaskPlan> taskPlans) {
    this.taskPlans = taskPlans;
}

....
@Entity
@Table(name = "task_plan")
public class TaskPlan {

@GeneratedValue(generator = "idIncrementor")
@GenericGenerator(name = "idIncrementor", strategy = "increment")
@Id
private Long id;

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

@Column(name = "description")
private String description;...

我使用DAO方法与两个实体交互,我有一个 @PersitenceContext(type = PersistenceContextType.EXTENDED)。我也使用泛型来通过id获取实体

  public T getById (Long id) {
    if (id==null) {
        return null;

    } else {

        Query query = this.entityManager.createQuery("from " + type.getName() + " where id=:id");
        query.setParameter("id", id);
        List<T> result = query.getResultList();
        if (!result.isEmpty()) {
                return result.get(0);

        }

        throw new IllegalArgumentException("Not found!");
    }

}

我正在使用以下方法更新Project实体上的taskplans集合,并向 taskPlans Set

添加新元素
//ProjectService class
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseBody
public void updateProject(@RequestBody Project project) throws Exception {
    this.projectFacade.updateOne(project);
}
 //ProjectFacade class
@Override
public void updateOne(Project project) {
    Project itemFromDbs = this.projectDao.getById(project.getId());
    if (itemFromDbs != null) {
        itemFromDbs.setName(project.getName());
        itemFromDbs.setDescriptions(project.getDescriptions());
        if (project.getTaskPlans() != null) {
            project.getTaskPlans().stream().forEach(taskPlan -> {
                TaskPlan taskPlanToAdd = taskplanDao.getById(taskPlan.getId());
                if (taskPlanToAdd == null) {
                    itemFromDbs.getTaskPlans().add(taskPlan);
                }
            });
        }
        this.projectDao.updateProject(itemFromDbs);
    }
}
....
 //ProjectDao class
 @Transactional
public void updateProject(Project project) {
    Project itemFromDbs = this.getById(project.getId());
    if (itemFromDbs != null) {
        itemFromDbs.setTaskPlans(project.getTaskPlans());
        entityManager.getEntityManagerFactory().getCache().evictAll();
        entityManager.persist(itemFromDbs);

        entityManager.flush();
        entityManager.clear();
    }
}

目前一切正常,当我尝试从 TaskPlan 实体中移除任务计划时,会发生坏事。 .entitymanager.remove()工作正常,实体将从 TaskPlan表和关联的 project_has_task_plan 表中删除,但是当我刷新页面并 getById 项目实体触发 TaskPlan 实体中已删除的项目仍在设置任务计划 ...

我必须提到二级缓存已完全禁用 clear() evictAll()方法无效。< / p>

以下是删除代码:

 //TaskPlanService class
@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
@ResponseBody
public void deleteTaskPlan(@PathVariable("id") Long taskPlanId) throws Exception {
    this.taskplanFacade.deleteOne(taskPlanId);
}

 //TaskPlanFacade class
 public void deleteOne(Long taskPlanId) {
    taskplanDao.deleteById(taskPlanId);
}
 //TaskPlanDao class
@Transactional
@Override
public void deleteById(Long taskPlanId) {
    TaskPlan taskPlanFromDbs=this.getById(taskPlanId);
    if(taskPlanFromDbs!=null){
        taskPlanFromDbs.getTasks().stream().forEach(task -> {//this things
                      //are associated with the                            
                     //taskplan entity but for the moment i don't interract   
                    //with them
            task.getBadges().stream().forEach(badge -> {
                entityManager.remove(badge);
            });
            entityManager.remove(task);
        });
        taskPlanFromDbs.getBadges().stream().forEach(badge -> {
            entityManager.remove(badge);
        });
        entityManager.remove(taskPlanFromDbs);
        entityManager.flush();

        entityManager.clear();

    }
}

3 个答案:

答案 0 :(得分:2)

作为开发人员,您负责维护实体的所有关系。因此,如果删除TaskPlan,则必须注意将其从与其他实体的任何关联中删除。

JPA 2.1 Specification 2.9 EntityRelationships

  

请注意,应用程序负责维护运行时关系的一致性 - 例如,确保双向关系的“一”和“多”方在应用程序更新时保持一致在运行时的关系。

答案 1 :(得分:0)

[披露:我没有调试过,试图执行甚至深度阅读你的代码;我不知道如何创建EntityManager或者你正在使用哪个@Transactional

Hibernate(以及其他JPA提供程序)通常在获取实体时不会修改实体 - 没有魔法附加到您的对象,您仍然可以自由使用自定义集合等。没有任何理由可以从中删除或出现在你的收藏中。

使用JPA的概念基于“工作单元”模式,该模式要求将操作拆分为单独的“业务逻辑块”,并在单独的EntityManager中运行每个块。只有当完成整个工作并且完成工作时,EntityManager关闭(至少在mental model中;实现工作方式不同),才会与数据库同步。

通过使用扩展的实体管理器,您展示的代码会将猴子扳手扔进机器中。消息来源似乎滥用了Hibernate,并且可能存在很多问题,既逻辑又与并发相关。通过使用普通的事务性实体管理器,可以很容易地修复和简化它。

答案 2 :(得分:0)

解决  我设置了@PersitenceContext(type = PersistenceContextType.TRANSACTION) 然后在ProjectDao类中:

@Transactional
public void updateProject(Project project) {
    Project itemFromDbs = this.getById(project.getId());
    if (itemFromDbs != null) {
        itemFromDbs.setTaskPlans(project.getTaskPlans());
        entityManager.merge(itemFromDbs);//so i wont persist a detached entity
        entityManager.flush();
    }
}

改变了项目&amp; TaskPlan实体asociation fetchType选项,所以我不会得到一个延迟的初始化错误:

@Entity
@Table(name = "project")
public class Project {
@GeneratedValue(generator = "idIncrementor")
@GenericGenerator(name = "idIncrementor", strategy = "increment")
@Id
private Long id;

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

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

@ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinTable(name = "project_has_task_plan",
        joinColumns = {@JoinColumn(name = "project_id")},
        inverseJoinColumns = {@JoinColumn(name = "task_plan_id")})
private Set<TaskPlan> taskPlans;

谢谢@fdreger:)