Skip to content

GH-3614: JPA outbound components delete in batch #3629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,9 @@ public void setJpaQuery(String jpaQuery) {
* @param nativeQuery The provided SQL query must neither be null nor empty.
*/
public void setNativeQuery(String nativeQuery) {

Assert.isTrue(this.namedQuery == null && this.jpaQuery == null, "You can define only one of the "
+ "properties 'jpaQuery', 'nativeQuery', 'namedQuery'");
Assert.hasText(nativeQuery, "nativeQuery must neither be null nor empty.");

this.nativeQuery = nativeQuery;
}

Expand All @@ -204,10 +202,8 @@ public void setNativeQuery(String nativeQuery) {
* @param namedQuery Must neither be null nor empty
*/
public void setNamedQuery(String namedQuery) {

Assert.isTrue(this.jpaQuery == null && this.nativeQuery == null, "You can define only one of the "
+ "properties 'jpaQuery', 'nativeQuery', 'namedQuery'");

Assert.hasText(namedQuery, "namedQuery must neither be null nor empty.");
this.namedQuery = namedQuery;
}
Expand Down Expand Up @@ -453,7 +449,12 @@ private Object executeOutboundJpaOperationOnPersistentMode(Message<?> message) {
case MERGE:
return this.jpaOperations.merge(payload, this.flushSize, this.clearOnFlush); // NOSONAR
case DELETE:
this.jpaOperations.delete(payload);
if (payload instanceof Iterable) {
this.jpaOperations.deleteInBatch((Iterable<?>) payload);
}
else {
this.jpaOperations.delete(payload);
}
if (this.flush) {
this.jpaOperations.flush();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<int:gateway default-reply-channel="studentReplyChannel"
service-interface="org.springframework.integration.jpa.outbound.StudentService" default-reply-timeout="3000">
<int:method name="deleteStudent" request-channel="deleteStudentChannel" />
<int:method name="deleteStudents" request-channel="deleteStudentChannel" />
<int:method name="getStudent" request-channel="getStudentChannel" />
<int:method name="getStudentWithException" request-channel="getStudentEndpointWithExceptionChannel"/>
<int:method name="getStudentWithParameters" request-channel="getStudentWithParametersChannel"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,22 +17,21 @@
package org.springframework.integration.jpa.outbound;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

import java.util.List;

import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.jpa.test.JpaTestUtils;
import org.springframework.integration.jpa.test.entity.StudentDomain;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.messaging.MessagingException;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.transaction.annotation.Transactional;

/**
Expand All @@ -43,8 +42,7 @@
*
* @since 2.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SpringJUnitConfig
@Transactional
@DirtiesContext
public class JpaOutboundGatewayTests {
Expand All @@ -55,116 +53,95 @@ public class JpaOutboundGatewayTests {
@Autowired
JdbcTemplate jdbcTemplate;

@After
@AfterEach
public void cleanUp() {
this.jdbcTemplate.execute("delete from Student where rollNumber > 1003");
}

@Test
public void getStudent() {
final StudentDomain student = studentService.getStudent(1001L);
StudentDomain student = studentService.getStudent(1001L);
assertThat(student).isNotNull();
}

@Test
public void getAllStudentsStartingFromGivenRecord() {
List<?> students = studentService.getAllStudentsFromGivenRecord(1);
assertThat(students).isNotNull();
assertThat(students.size()).isEqualTo(2);
assertThat(students).isNotNull().hasSize(2);
}

@Test
public void getAllStudentsWithMaxNumberOfRecords() {
List<?> students = studentService.getStudents(1);
assertThat(students).isNotNull();
assertThat(students.size()).isEqualTo(1);
assertThat(students).isNotNull().hasSize(1);
}


@Test
public void deleteNonExistingStudent() {

StudentDomain student = JpaTestUtils.getTestStudent();
student.setRollNumber(3424234234L);

try {
studentService.deleteStudent(student);
fail("IllegalArgumentException is expected");
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage()).startsWith("Removing a detached instance");
}

assertThatIllegalArgumentException()
.isThrownBy(() -> studentService.deleteStudent(student))
.withMessageStartingWith("Removing a detached instance");
}

@Test
public void getStudentWithException() {
try {
studentService.getStudentWithException(1001L);
fail("MessageHandlingException is expected");
}
catch (MessagingException e) {
assertThat(e.getMessage())
.isEqualTo("The Jpa operation returned more than 1 result for expectSingleResult mode.");
}
assertThatExceptionOfType(MessagingException.class)
.isThrownBy(() -> studentService.getStudentWithException(1001L))
.withMessage("The Jpa operation returned more than 1 result for expectSingleResult mode.");
}

@Test
public void getStudentStudentWithPositionalParameters() {

StudentDomain student = studentService.getStudentWithParameters("First Two");

assertThat(student.getFirstName()).isEqualTo("First Two");
assertThat(student.getLastName()).isEqualTo("Last Two");
}

@Test
public void getAllStudents() {
List<StudentDomain> students = studentService.getAllStudents();
assertThat(students).isNotNull();
assertThat(students.size()).isEqualTo(3);
assertThat(students).isNotNull().hasSize(3);
}

@Test
@Transactional
public void persistStudent() {

final StudentDomain studentToPersist = JpaTestUtils.getTestStudent();
StudentDomain studentToPersist = JpaTestUtils.getTestStudent();
assertThat(studentToPersist.getRollNumber()).isNull();

final StudentDomain persistedStudent = studentService.persistStudent(studentToPersist);
StudentDomain persistedStudent = studentService.persistStudent(studentToPersist);
assertThat(persistedStudent).isNotNull();
assertThat(persistedStudent.getRollNumber()).isNotNull();

}

@Test
@Transactional
public void persistStudentUsingMerge() {

final StudentDomain studentToPersist = JpaTestUtils.getTestStudent();
StudentDomain studentToPersist = JpaTestUtils.getTestStudent();
assertThat(studentToPersist.getRollNumber()).isNull();

final StudentDomain persistedStudent = studentService.persistStudentUsingMerge(studentToPersist);
StudentDomain persistedStudent = studentService.persistStudentUsingMerge(studentToPersist);
assertThat(persistedStudent).isNotNull();
assertThat(persistedStudent.getRollNumber()).isNotNull();

}

@Test
public void testRetrievingGatewayInsideChain() {
final StudentDomain student = studentService.getStudent2(1001L);
StudentDomain student = studentService.getStudent2(1001L);
assertThat(student).isNotNull();
}

@Test
@Transactional
public void testUpdatingGatewayInsideChain() {

final StudentDomain studentToPersist = JpaTestUtils.getTestStudent();
StudentDomain studentToPersist = JpaTestUtils.getTestStudent();
assertThat(studentToPersist.getRollNumber()).isNull();

final StudentDomain persistedStudent = studentService.persistStudent2(studentToPersist);
StudentDomain persistedStudent = studentService.persistStudent2(studentToPersist);
assertThat(persistedStudent).isNotNull();
assertThat(persistedStudent.getRollNumber()).isNotNull();

Expand All @@ -173,7 +150,17 @@ public void testUpdatingGatewayInsideChain() {
@Test
public void testJpaRepositoryAsService() {
List<StudentDomain> students = this.studentService.getStudentsUsingJpaRepository("F");
assertThat(students.size()).isEqualTo(2);
assertThat(students).hasSize(2);
}


@Test
@Transactional
public void testDeleteMany() {
List<StudentDomain> allStudents = this.studentService.getAllStudents();
assertThat(allStudents).hasSize(3);
this.studentService.deleteStudents(allStudents);
assertThat(this.studentService.getAllStudents()).isNull();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@
/**
* @author Amol Nayak
* @author Artem Bilan
*
* @since 2.2
*/
public interface StudentService {
Expand All @@ -33,8 +34,11 @@ public interface StudentService {
StudentDomain getStudentWithException(Long id);

StudentDomain getStudent(Long id);

StudentDomain deleteStudent(StudentDomain student);

List<StudentDomain> deleteStudents(List<StudentDomain> students);

@Payload("new java.util.Date()")
List<StudentDomain> getAllStudents();

Expand Down
2 changes: 2 additions & 0 deletions src/reference/asciidoc/jpa.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ NOTE: As of Spring Integration 3.0, payloads to `PERSIST` or `MERGE` can also be
In that case, each object returned by the `Iterable` is treated as an entity and persisted or merged using the underlying `EntityManager`.
Null values returned by the iterator are ignored.

NOTE: Starting with version 5.5.4, the `JpaOutboundGateway`, with a `JpaExecutor` configured with `PersistMode.DELETE`, can accept an `Iterable` payload to perform a batch removal persistent operation for the provided entities.

[[jpa-using-jpaql]]
==== Using JPA Query Language (JPA QL)

Expand Down
7 changes: 7 additions & 0 deletions src/reference/asciidoc/whats-new.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,10 @@ See <<./mongodb.adoc#mongodb,MongoDb Support>> for more information.
The WebSocket channel adapters based on `ServerWebSocketContainer` can now be registered and removed at runtime.

See <<./web-sockets.adoc#web-sockets,WebSockets Support>> for more information.

[[x5.5-jpa]]
==== JPA Changes

The `JpaOutboundGateway` now supports an `Iterable` message payload for a `PersistMode.DELETE`.

See <<./jpa.adoc#jpa-outbound-channel-adapter,Outbound Channel Adapter>> for more information.