当我更新ManyToMany关系中的实体时,如何阻止Hibernate/JPA删除联接表记录



我想在不更新/编辑子实体的情况下更新实体。以下是有问题的实体:

@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;

//other fields omitted for brevity

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "project_user",
joinColumns = @JoinColumn(name="user_id", updatable = false),
inverseJoinColumns = @JoinColumn(name="project_id",updatable = false))
@JsonIgnore
private Set<Project> projects;

//getters and setters omitted for brevity
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id != other.id)
return false;
return true;
}

}

以下是项目实体:

@Entity
@Table(name = "project")
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;

//other fields omitted for brevity

@ManyToMany
@JoinTable(name = "project_user",
joinColumns = @JoinColumn(name="project_id"),
inverseJoinColumns = @JoinColumn(name="user_id"))
@JsonIgnoreProperties(value = {"projects", "connected", "notifications"})
private Set<User> members;
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Project other = (Project) obj;
if (id != other.getId())
return false;
return true;
}

}

以下是合并程序:

@Override
public void update(User user) {
//user is created from a dto as a new object (that has its id set manually)
entityManager.merge(user);
}

问题是从联接表中删除休眠日志。我该怎么阻止?

休眠日志

Hibernate: //login query
select
distinct authuser0_.id as id1_7_,
authuser0_.enabled as enabled8_7_,
authuser0_.password as password5_7_,
authuser0_.username as username6_7_,
roles1_.username as username1_0_0__,
roles1_.authority as authorit2_0_0__ 
from
users authuser0_ 
inner join
authorities roles1_ 
on authuser0_.username=roles1_.username 
where
authuser0_.username=?
Hibernate: //entityManager.merge() calls this to merge it
select 
user0_.id as id1_7_0_,
user0_.email as email2_7_0_,
user0_.first_name as first_na3_7_0_,
user0_.image as image7_7_0_,
user0_.last_name as last_nam4_7_0_,
user0_.password as password5_7_0_,
user0_.username as username6_7_0_ 
from
users user0_ 
where
user0_.id=?
Hibernate: //the actual update statement
update 
users 
set
email=?,
first_name=?,
image=?,
last_name=?,
password=?,
username=? 
where
id=?
Hibernate: // not wanted, need to stop this query
delete 
from
project_user 
where
user_id=?

我在网上读到,这与平等和散列码有关。然而,我似乎无法让它发挥作用。

我知道有两个选项都不是最优的:

  1. 编写一个本机查询,而不是merge方法。问题:不可扩展,每次更改都需要手动编辑
  2. 为该表创建一个新实体(因此该表将有2个实体映射到它(,而不附加到其他表。问题:重复代码和实体

感谢提前

我知道你已经接受了答案,但是。。。

那里发生的事情比你想象的要多。

之所以存在Hibernate: delete from project_user where user_id=?,是因为后面有插入。你没看见他们吗?如果为该用户清空了关系表,但以后没有插入,那么关系可能没有更新?删除也只能复制,但您应该测试您的代码,并验证是否没有适合该用户的项目。这可能意味着您没有更新合并用户中的projects字段。

因此,运行一个基本的JPA示例,让我们从创建一个起点开始解决您的问题:

em.getTransaction().begin();
Project project = new Project();
em.persist(project);
User user = new User();
Set<Project> projects = new HashSet<>();
projects.add(project);
user.setProjects(projects);
em.persist(user);
em.getTransaction().commit();

然后,清除entityManager上下文:

em.clear();

然后重现你的问题:

em.getTransaction().begin();
Project p2 = new Project();
em.persist(p2);
projects.add(p2);
User un = em.find(User.class, 1);
un.setProjects(projects);
em.merge(un);
em.getTransaction().commit();

第二部分给了我日志:

Hibernate: insert into project (id) values (default)
Hibernate: select user0_.id as id1_2_0_ from users user0_ where user0_.id=?
Hibernate: select project0_.id as id1_0_0_ from project project0_ where project0_.id=?
Hibernate: delete from project_user where user_id=?
Hibernate: insert into project_user (user_id, project_id) values (?, ?)
Hibernate: insert into project_user (user_id, project_id) values (?, ?)

现在,让我们通过更改代码并再次运行来删除有问题的delete语句:

Project p2 = new Project();
em.persist(p2);
User un = em.find(User.class, 1);
projects = un.getProjects();
projects.add(p2);
em.merge(un);
em.getTransaction().commit();

这给了我日志:

Hibernate: insert into project (id) values (default)
Hibernate: select user0_.id as id1_2_0_ from users user0_ where user0_.id=?
Hibernate: select projects0_.user_id as user_id1_1_0_, projects0_.project_id as project_2_1_0_, project1_.id as id1_0_1_ from project_user projects0_ inner join project project1_ on projects0_.project_id=project1_.id where projects0_.user_id=?
Hibernate: insert into project_user (user_id, project_id) values (?, ?)

没有删除语句。仔细看第一个";第二部分";请注意,我将新项目添加到了原来的项目集中,该项目集不再是JPA持久性上下文的一部分。在第二个示例中,我专门从user对象中检索了一组项目,该对象是JPA持久性上下文的一部分。现在,当我更新项目集并合并用户JPA时,不必重新开始用户/项目关系。

BTW:就mappedBy逻辑而言,公认的答案是准确的,但这段代码是用您的原始示例完成的。只要JPA知道你在做什么,它就会正确地处理它。当你在JPA之外做事情时,JPA会";平底船";并重新创建。

为什么两个不同的关联(User.projectsProject.users(映射到同一个联接表?

如果您想要双向关联,那么只有一方应该是拥有方,另一方应该使用mappedBy进行映射。顺便说一句,将mappedBy用于User.projects侧将解决您的问题。

最新更新