Spring-Data-Jpa与EmbeddedId的双向关联。合并时外键为空

时间:2019-06-12 16:37:14

标签: spring hibernate jpa spring-data-jpa

我正在运行具有两个实体的最新版本的Hibernate:ProjectProjectShare,它们具有双向设置的一对多关系。

ProjectShare由包含project_iduser_id的复合ID唯一标识。除了键之外,ProjectShare包含一个布尔型标志,无论用户对项目具有读取或写入访问权限。

@Entity
@Table(name = "projects")
public class Project {

    @Id
    @GeneratedValue // UUID generator
    @Column(name = "project_id")
    private String id;

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

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<ProjectShare> shares = new ArrayList<>();

    public Project(String name) {
        this.name = name;
    }

    public void addShare(ProjectShare share) {
        shares.add(share);
        share.setProject(this);
    }

    public void removeShare(ProjectShare share) {
        shares.remove(share);
        share.setProject(null);
    }
}

@Entity
@Table(name = "project_shares")
public class ProjectShare {

    @EmbeddedId
    private ProjectShareId id;

    @Column(name = "has_write_access")
    private boolean hasWriteAccess;

    @MapsId("projectId")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "project_id", nullable = false, updatable = false)
    private Project project;

    public ProjectShare(ProjectShareId id, boolean hasWriteAccess) {
        this.id = id;
        this.hasWriteAccess = hasWriteAccess;
    }

    public void setProject(Project project) {
        this.project = project;
    }
}

@Embeddable
public class ProjectShareId implements Serializable {

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

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

    public ProjectShareId(String userId) {
        this.userId = userId;
    }

    // equals and hashCode go here...
}

如果我创建一个新的Project并为其分配新的ProjectShare关联,则一切正常:

Project project = new Project("my_project");
project.addShare(new ProjectShare(new ProjectShareId("user1"), false));
project.addShare(new ProjectShare(new ProjectShareId("user2"), false));
projectRepository.save(project); // assume project id is '123'

由于所有对象都是新的并且尚未持久化,因此它将为Project本身执行1次插入,并为每个ProjectShare执行1次插入。假设插入的项目ID为“ 123”。

现在,如果我加载此现有项目,并向其添加新的ProjectShares,则会出现问题:

Project project = projectRepository.findById("123");
project.addShare(new ProjectShare(new ProjectShareId("user2"), true));
project.addShare(new ProjectShare(new ProjectShareId("user3"), true));
projectRepository.save(project);

对于每个ProjectShare,此操作将对ProjectShareId(project_id和user_id)中的值执行SELECT,然后执行INSERT UPDATE(取决于是否找到记录)。这是Hibernate的基本合并策略,这就是我们想要的。

期望的结果应该是:

  • 未更改user1的ProjectShare
  • 更新user2的ProjectShare(从falsetrue
  • user3创建一个新的ProjectShare

但是,当对ProjectShare执行SELECT时,外键project_id始终为空。这意味着将永远找不到现有记录,尝试使用INSERT代替UPDATE,并触发数据库级约束违反。

我应该如何解决此问题?我应该手动浏览project.getShares()集合,查找现有记录并更新它们吗?我希望Hibernate通过其合并策略来做到这一点。

或者这可能是Hibernate中与嵌入ID中的外键关联相关的错误吗?

0 个答案:

没有答案