Как фильтровать объекты ассоциации в таблице сопоставления с помощью Spring Data JPA (Hibernate)?

Есть ли способ отфильтровать мягко удаленную ассоциацию «многие ко многим» с использованием @OneToMany и @ManyToOne через промежуточный объект (таблицу сопоставления)?

product и product_option_group находятся в отношении N:M. Я реализую мягкое удаление, используя столбец disabled_datetime, и хочу отфильтровать набор ProductOptionGroup из объекта Product. В этом сообщении для этого используются @ManyToMany и @Where. Я последовал за ним, и это сработало (отфильтрованы отключенные product_option_group). Обратите внимание @Where на ProductOptionGroup класс.

// `product` <-> `product-product_option_group` <-> `product_option_group`

@Entity
@Table(name = "product")
public class Product implements Serializable {
    ...

    @ManyToMany
    @JoinTable(name = "product-product_option_group",
            joinColumns = @JoinColumn(name = "product_id"),
            inverseJoinColumns = @JoinColumn(name = "product_option_group_id"))
    private final Set<ProductOptionGroup> productOptionGroups = new HashSet<>();

    ...
}

@Entity
@Table(name = "product_option_group")
@Where(clause = "disabled_datetime is null")
public class ProductOptionGroup implements Serializable {
    ...

    @Column(name = "disabled_datetime")
    private ZonedDateTime disabledDatetime;

    ...
}

но я хочу использовать @OneToMany для таблицы product-product_option_group, вот так.

@Entity
@Table(name = "product")
public class Product implements Serializable {
    ...

    @OneToMany(mappedBy = "id.product")
    private final Set<ProductProductOptionGroup> productProductOptionGroups = new HashSet<>();

    ...
}

@Entity
@Table(name = "`product-product_option_group`")
public class ProductProductOptionGroup implements Serializable {
    @EmbeddedId
    private final ProductProductOptionGroupId id = new ProductProductOptionGroupId();

    ...
}

@Embeddable
public class ProductProductOptionGroupId implements Serializable {
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id", referencedColumnName = "id")
    private Product product;

    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @JoinColumn(name = "product_option_group_id", referencedColumnName = "id")
    @Where(clause = "disabled_datetime is null")
    private ProductOptionGroup productOptionGroup;
}

@Entity
@Table(name = "product_option_group")
@Where(clause = "disabled_datetime is null")
public class ProductOptionGroup implements Serializable {
    ...
}

Но тогда аннотации @Where больше не будут работать, поэтому также выбираются отключенные product_option_group. Как это решить?


person user2652379    schedule 18.06.2021    source источник


Ответы (2)


Я хотел бы добавить обходные пути, ожидая ответов.

Во-первых, я могу добавить представление в качестве фильтра.

CREATE VIEW `product-product_option_group-enabled` AS
SELECT *
FROM `product-product_option_group` ppog
JOIN product_option_group pog ON ppog.product_option_group_id = pog.id
    AND pog.disabled_datetime IS NULL;
@Entity
@Table(name = "`product-product_option_group-enabled`") // using the view as a filter
public class ProductProductOptionGroupEnabled implements Serializable {
    @EmbeddedId
    private final ProductProductOptionGroupId id = new ProductProductOptionGroupId();

    ...
}

Тогда использование ProductProductOptionGroupEnabled для чтения и ProductProductOptionGroup для любой модификации данных может решить проблему. Мне это не нравится.

  • Несколько коллекций объекта. Нужно быть осторожным при его изменении.
  • 2 разных объекта в одной таблице. Оркестрировать их может быть сложно (equals() и hashCode()).
  • Добавляет представление. Лучше быть осторожным, когда схема изменяется.

Возможно, я смогу использовать обновляемые и вставляемые представления, но Мне все равно понадобится ProductProductOptionGroup для выбора каждой строки.


Во-вторых, я могу фильтровать коллекцию из Java.

@Entity
@Table(name = "product")
public class Product implements Serializable {
    ...

    @OneToMany(mappedBy = "id.product")
    private final Set<ProductProductOptionGroup> productProductOptionGroups = new HashSet<>();

    public Set<ProductProductOptionGroup> getProductProductOptionGroups() {
        return productProductOptionGroups.stream()
                .filter(o -> o.getId().getProductOptionGroup().getDisabledDatetime() == null)
                .collect(Collectors.toSet());
    }

    public Set<ProductProductOptionGroup> getProductProductOptionGroupsAll() {
        return productProductOptionGroups;
    }
    
    ...
}

Затем используйте getProductProductOptionGroups(), чтобы получить отфильтрованную коллекцию. Мне это тоже не нравится.

  • Он выберет каждую строку из таблицы product-product_option_group независимо от disabled_datetime.
  • Создает другую коллекцию. Нужно быть осторожным при его изменении.
  • javax.persistence.EntityNotFoundException: Unable to find with id xxx может случиться. Это можно решить, join fetchпозвонив беспокоящему объекту.
person user2652379    schedule 21.06.2021

Предложение WHERE добавляет возможности фильтрации в структуру FROM-SELECT. В любом запросе JPQL важно извлекать выбранные объекты из базы данных. на мой взгляд, вам следует проверить правильность реализации @where, см. следующую reference< /а>.

person carleetho    schedule 05.07.2021
comment
Я использую org.hibernate.annotations.Where для аннотации @Where. Другой реализации не нашел. Какова правильная реализация @Where? - person user2652379; 06.07.2021
comment
К сожалению, org.hibernate.annotations.Where принимает только SQL, а не JPQL. - person user2652379; 06.07.2021