Spring JPA 将分页添加到@ManyToMany

Spring JPA add pagination to @ManyToMany

在我当前的项目中,我使用 JPA 和 Hibernate 来处理我的数据库层。

我的一个实体与其自身具有多对多关系,使用 @ManyToMany@JoinTable 注释效果很好。

我的问题是系统要求我为实体添加分页支持。我在网上搜索了一个解决方案,但我找到的最接近解决方案的东西适用于不同的用例,其中关系存在于 2 个实体之间(而不是一个实体本身)。

这是实体的重要部分 class:

package iob.data;

import iob.data.primarykeys.InstancePrimaryKey;
import iob.logic.exceptions.instance.InvalidBindingOperationException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;


@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@RequiredArgsConstructor
@Entity
@Table(name = "INSTANCES")
@IdClass(InstancePrimaryKey.class)
public class InstanceEntity {
    //<editor-fold desc="Primary key">
    @Id
    // The index would be generated from a sequence named "INSTANCES_SEQUENCE"
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INSTANCES_SEQUENCE")
    // Here we create that sequence, and define it to be updated every insertion (allocationSize = 1).
    @SequenceGenerator(name = "INSTANCES_SEQUENCE", sequenceName = "INSTANCES_SEQUENCE", allocationSize = 1)
    private long id;
    @Id
    @NonNull
    private String domain;
    //</editor-fold>

    //<editor-fold desc="Many to Many relation">
    // Define a new table that would store the references
    @JoinTable(name = "INSTANCES_TO_INSTANCES",
            // The main columns for this attribute are the parent's primary key which is constructed from domain and id
            inverseJoinColumns = {@JoinColumn(name = "PARENT_ID", referencedColumnName = "id"),
                    @JoinColumn(name = "PARENT_DOMAIN", referencedColumnName = "domain")},

            // The referenced columns for this attribute are the child's primary key which is also constructed from domain and id
            joinColumns = {@JoinColumn(name = "CHILD_ID", referencedColumnName = "id"),
                    @JoinColumn(name = "CHILD_DOMAIN", referencedColumnName = "domain")})
    // Declare the parent's side of the relation
    @ManyToMany(fetch = FetchType.LAZY)
    private Set<InstanceEntity> parentInstances = new HashSet<>();

    // Declare the children's size of the relation, and define that it is related to the parentInstances
    @ManyToMany(mappedBy = "parentInstances", fetch = FetchType.LAZY)
    private Set<InstanceEntity> childInstances = new HashSet<>();
    //</editor-fold>

    public void addParent(InstanceEntity parent) {
        if (this.equals(parent))
            throw new InvalidBindingOperationException("Cannot assign parent to himself");
        parentInstances.add(parent);
    }

    public void addChild(InstanceEntity child) {
        if (this.equals(child))
            throw new InvalidBindingOperationException("Cannot assign child to himself");
        childInstances.add(child);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, domain);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        InstanceEntity entity = (InstanceEntity) o;
        return id == entity.id && domain.equals(entity.domain);
    }
}

到目前为止,我尝试(可能天真地)为 parentInstance 编写 getter 并传递给它一个 Pagable 对象(因为它适用于 derived-queries,所以值得shot ) 但显然它什么也没做。

我能想到的唯一解决方案是删除关系,然后手动创建它(使用另一个实体创建 table 并添加一个新的 PagingAndSortingRepository 来检索相关实例,然后转换他们在服务中 InstanceEntity

那么,如何为 parentInstanceschildInstances 添加分页支持?

我在一些 中找到了解决方案。和分页无关,但还是解决了我的问题

所以我的解决方案是将以下功能添加到我的 PagingAndSortingRepository 界面:

Page<InstanceEntity> findDistinctByParentInstancesIn(@Param("parent_instances") Set<InstanceEntity> parentInstances, Pageable pageable);

然后调用它是:

InstanceEntity ie = new InstanceEntity();
ie.setDomain(parentDomain);
ie.setId(Long.parseLong(parentId));
instancesDao.findDistinctByParentInstancesIn(Collections.singleton(ie), PageRequest.of(page, size));

老实说,我不确定 how/why 它是否有效。 IntelliJ IDEA 在存储库界面 Expected parameter types: Collection<Set<InstanceEntity>>.

中给我一个错误

所以如果有人有解释,我会很高兴听到。