Двунаправленная связь Spring-Data-Jpa с EmbeddedId. Внешний ключ является нулевым при объединении - PullRequest
0 голосов
/ 12 июня 2019

Я использую последнюю версию Hibernate с двумя сущностями: Project и ProjectShare, которые имеют двунаправленное отношение «один ко многим».

A ProjectShare однозначно определяетсясоставной идентификатор, содержащий project_id и user_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'

Поскольку все объекты являются новыми и еще не сохранены, выполняется 1 вставка для самого проекта и 1 вставка для каждого ProjectShare.Предположим, что проект вставлен с идентификатором '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это выполняет SELECT для значений в ProjectShareId (project_id и user_id), после чего следует либо INSERT of UPDATE, в зависимости от того, была ли найдена запись.Это базовая стратегия слияния в Hibernate, и именно этого мы и хотим.

Желаемым результатом этого должно быть:

  • Оставить ProjectShare на user1нетронутым
  • Обновление ProjectShare для user2false до true)
  • Создание нового ProjectShare для user3

Однако, когдаSELECT выполняется для ProjectShare, внешнего ключа project_id всегда равно нулю .Это означает, что существующие записи никогда не будут найдены, вместо INSATE и INSERT предпринята попытка INSERT, и будет инициировано нарушение ограничения уровня DB.

Как мне решить эту проблему?Должен ли я вручную просмотреть коллекцию project.getShares(), найти существующие записи и обновить их?Я надеялся, что Hibernate сделает это с помощью своей стратегии слияния.

Или это может быть ошибка в Hibernate, связанная с ассоциациями с внешними ключами в встроенных идентификаторах?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...