Запрос JPA на deleteById для составного ключа, объявленного с использованием IdClass

1

Вопрос

Если я объявил свой (составной) первичный ключ с помощью @IdClass, как мне написать, @Queryчтобы иметь возможность выдавать DELETEзапрос с помощью Collection<MyIdClass>?

Вторичный вопрос

Будет ли CASCADE действительно запускать удаление связанного, AnotherEntityнесмотря на использование @Query?

Текущая модель

@Entity
@Table(name = "myentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@IdClass(MyIdClass.class)
public class MyEntity {

    @Id
    @Column(updatable = false)
    private String foo;

    @Id
    @Column(updatable = false)
    private String bar;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "my_foreign_key", referencedColumnName = "external_pk")
    private AnotherEntity anotherEntity;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MyIdClass implements Serializable {

    private String foo;
    private String bar;
}
@Entity
@Table(name = "anotherentity")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class AnotherEntity {

    @Id
    @Column(name = "external_pk", nullable = false, updatable = false)
    private String externalPk;
}

Что я читал

Несколько ресурсов:

  1. https://www.baeldung.com/spring-data-jpa-query
  2. https://www.baeldung.com/spring-data-jpa-delete
  3. https://stackoverflow.com/a/36765129/9768291

И я также нашел этот вопрос SO, который казался очень близким к тому, что я ищу, но, к сожалению, нет ответов.

Цель

Что-то подобное:

@Repository
public interface MyCRUDRepository extends CrudRepository<MyEntity, MyIdClass> {

    @Modifying
    @Query("DELETE FROM myentity m WHERE m IN ?1") // how do I write this?
    void deleteAllWithIds(Collection<MyIdClass> ids);
}

В конечном итоге я хочу сделать это для пакетной обработки моих запросов DELETE для повышения производительности.

Ловушки, которых я пытаюсь избежать

Я знаю, что есть, deleteAll(Iterable<? extends MyEntity>)но тогда мне действительно нужно иметь эти объекты для начала, что потребует дополнительных вызовов БД.

Также есть deleteById(MyIdClass), но на самом деле это всегда вызывает findByIdперед отправкой одного оператора DELETE в качестве транзакции: плохо для производительности!

Потенциально несущественная точность

Я не уверен, что это может помочь, но мой провайдер JPA поможет EclipseLink. Насколько я понимаю, есть свойства для пакетной обработки запросов, и это, в конечном счете, то, что я собираюсь использовать.

Однако я не совсем уверен, каковы внутренние требования для выполнения этой пакетной обработки. Например, если бы я сделал deleteByIdв a for-loop, будут ли операторы чередования SELECTи DELETEпредотвращать выполнение пакетной обработки? Документации по этому поводу довольно мало.

3
  • Есть несколько приемов, которые вы можете попробовать, например, собственный запрос, в котором вы используете строковый параметр для представления данных в списке stackoverflow.com/a/14375413/496099, но он не будет работать с привязкой параметров. Другие должны использовать внутренние запросы на удаление EclipseLink - EclipseLink может пакетировать свой запрос DeleteObject, и для этого не требуется извлекать объект. Однако JPA не имеет поддержки IN для конструкций - я не думал, что все базы данных поддерживают.
    Chris
    2 дня назад
  • @Chris: как мне использовать «внутренние запросы на удаление EclipseLink»? Я изо всех сил пытался понять, как заставить EclipseLink пакетировать мои запросы (похоже, не так много документации по предварительным условиям для пакетной обработки).
    payne
    2 дня назад
  • Пакетное написание EclipseLink зависит от поддержки драйвера, но оно настраивается с помощью флага единицы сохранения состояния: eclipse.org/eclipselink/documentation/2.4/jpa/extensions/… . Это позволяет ему группировать все операторы, с которыми он сталкивается в транзакции при записи SQL (при сбросе или фиксации). Запросы EclipseLink задокументированы, но это не JPA api, поэтому найти его немного сложнее. Простейшей отправной точкой может быть получение «UnitOfWork uow = entityManager.unwrap (UnitOfWork.class);» затем попробуйте использовать deleteObject для экземпляров только с pk.
    Chris
    22 часов назад
2

Если вы уверены, что IdClass - лучший выбор, чем EmbeddedId в вашей ситуации , вы можете добавить дополнительное сопоставление в MyEntity:

@Embedded
@AttributeOverrides({
  @AttributeOverride(name = "foo",
    column = @Column(name = "foo", insertable = false, updatable = false)),
  @AttributeOverride(name = "bar",
    column = @Column(name = "bar", insertable = false, updatable = false))})
private MyIdClass id;

и используйте его в своем репозитории:

@Modifying
@Query("DELETE FROM MyEntity me WHERE me.id in (:ids)")
void deleteByIdIn(@Param("ids") Collection<MyIdClass> ids);

Это сгенерирует один запрос:, в delete from myentity where bar=? and foo=? [or bar=? and foo=?]...результате чего этот тест будет пройден (со следующими записями в таблице insert into myentity(foo,bar) values ('foo1', 'bar1'),('foo2', 'bar2'),('foo3', 'bar3'),('foo4', 'bar4');):

@Test
@Transactional
void deleteByInWithQuery_multipleIds_allDeleted() {
  assertEquals(4, ((Collection<MyEntity>) myEntityRepository.findAll()).size());

  MyIdClass id1 = new MyIdClass("foo1", "bar1");
  MyIdClass id2 = new MyIdClass("foo2", "bar2");

  assertDoesNotThrow(() -> myEntityRepository.deleteByIdIn(List.of(id1, id2)));
  assertEquals(2, ((Collection<MyEntity>) myEntityRepository.findAll()).size());
}
11
  • Это кажется очень близким. Internal Exception: org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of com.path.to.MyIdClass. Use setObject() with an explicit Types value to specify the type to use.После просмотра фактического запроса, созданного для пакета из 5 объектов, которые нужно удалить, я получаю :Call: DELETE FROM myentity WHERE ((?, ?) IN (?,?,?,?,?))
    payne
    вчера
  • И будет ли предложенное @Queryраспространять удаление в соответствии с определением @CascadeType?
    payne
    вчера
  • Проверьте эту реализацию кода . Все тесты проходят. Итак, есть либо критическое изменение в версиях зависимостей, которые мы используем, либо проблема с отображением. вчера
  • И чтобы ответить на ваш второй вопрос, взгляните на это . tl; dr аннотация запроса отключает интеллектуальную часть JPA. Если вы проверите тесты по моей ссылке на github, вы увидите, что последний фактически каскадируется, поскольку аннотации нет, но она генерирует запрос для каждого отдельного действия. вчера
  • Спасибо, что поделились репо! Очень интересно, что работает ваше репо, а не мое. Например, мне пришлось добавить @Embeddableна MyIdClassеще я бы получить « The Entity class [class com.path.to.MyEntity] has an embedded attribute [id] of type [class com.path.to.MyIdClass] which is NOT an Embeddable class.».
    payne
    вчера
0

Я думаю, вы ищете что-то, что будет генерировать такой запрос

delete from myentity where MyIdClass in (? , ? , ?)

Вы можете попробовать из этого сообщения , это может вам помочь.

Новый участник
Nirmal Kumar is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
2
  • Да, я действительно связал это в своем вопросе. Я недостаточно хорошо разбираюсь в запросах JPA, чтобы знать, как правильно писать строку, которая входит в @Query("here"). Ответы в ссылке только описывают, как работать со списком примитивов; Я имею дело со списком составных ключей.
    payne
    2 дня назад
  • Плохо, я забыл предоставить эту ссылку: - baeldung.com/jpa-composite-primary-keys . Я думаю, это объясняет, что вам нужно. 2 дня назад