diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
index 4b5d64fc5..48cdd1873 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
@@ -26,6 +26,7 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
+import net.sf.jsqlparser.statement.merge.Merge;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
@@ -57,6 +58,7 @@ import org.springframework.util.StringUtils;
*
* @author Diego Krupitza
* @author Greg Turnquist
+ * @author Geoffrey Deremetz
* @since 2.7.0
*/
public class JSqlParserQueryEnhancer implements QueryEnhancer {
@@ -91,6 +93,8 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
return ParsedType.DELETE;
} else if (statement instanceof Select) {
return ParsedType.SELECT;
+ } else if (statement instanceof Merge) {
+ return ParsedType.MERGE;
} else {
return ParsedType.SELECT;
}
@@ -479,10 +483,11 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer {
*
{@code ParsedType.UPDATE}: means the top level statement is {@link Update}
* {@code ParsedType.SELECT}: means the top level statement is {@link Select}
* {@code ParsedType.INSERT}: means the top level statement is {@link Insert}
+ * {@code ParsedType.MERGE}: means the top level statement is {@link Merge}
*
*/
enum ParsedType {
- DELETE, UPDATE, SELECT, INSERT;
+ DELETE, UPDATE, SELECT, INSERT, MERGE;
}
}
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java
index 4f88fe896..955dc32da 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java
@@ -83,6 +83,7 @@ import org.springframework.transaction.annotation.Transactional;
* @author Diego Krupitza
* @author Daniel Shuy
* @author Simon Paradies
+ * @author Geoffrey Deremetz
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:application-context.xml")
@@ -3002,6 +3003,22 @@ public class UserRepositoryTests {
.contains("Gierke", "Arrasz", "Matthews", "raymond", testLastName);
}
+ @Test // GH-2641
+ void mergeWithNativeStatement() {
+
+ flushTestUsers();
+
+ Optional byIdUser = repository.findById(firstUser.getId());
+ assertThat(byIdUser).isPresent().map(User::getAge).get().isEqualTo(28);
+
+ // when
+ repository.mergeNativeStatement();
+
+ // then
+ Optional afterUpdate = repository.findById(firstUser.getId());
+ assertThat(afterUpdate).isPresent().map(User::getAge).get().isEqualTo(30);
+ }
+
private Page executeSpecWithSort(Sort sort) {
flushTestUsers();
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java
index bc9f20010..8aa76ffc8 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java
@@ -36,6 +36,7 @@ import org.springframework.data.jpa.domain.JpaSort;
* Unit tests for {@link QueryEnhancer}.
*
* @author Diego Krupitza
+ * @author Geoffrey Deremetz
*/
class QueryEnhancerUnitTests {
@@ -920,6 +921,18 @@ class QueryEnhancerUnitTests {
assertThat(queryEnhancer.hasConstructorExpression()).isFalse();
}
+ @Test // GH-2641
+ void mergeStatementWorksWithJSqlParser() {
+ String query = "merge into a using (select id, value from b) query on (a.id = query.id) when matched then update set a.value = value";
+ StringQuery stringQuery = new StringQuery(query, true);
+ QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery);
+
+ assertThat(queryEnhancer.getJoinAliases()).isEmpty();
+ assertThat(queryEnhancer.detectAlias()).isNull();
+ assertThat(queryEnhancer.getProjection()).isEmpty();
+ assertThat(queryEnhancer.hasConstructorExpression()).isFalse();
+ }
+
public static Stream insertStatementIsProcessedSameAsDefaultSource() {
return Stream.of( //
Arguments.of("INSERT INTO FOO(A) VALUES('A')"), //
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java
index b7bd9b5b9..df67e8beb 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java
@@ -43,6 +43,7 @@ import org.springframework.transaction.annotation.Transactional;
* @author Greg Turnquist
* @author Simon Paradies
* @author Diego Krupitza
+ * @author Geoffrey Deremetz
*/
public interface UserRepository
extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom {
@@ -681,6 +682,16 @@ public interface UserRepository
nativeQuery = true)
void insertNewUserWithParamNativeQuery(@Param("lastname") String lastname);
+ // GH-2641
+ @Modifying(clearAutomatically = true)
+ @Query(value = "merge into sd_user " +
+ "using (select id from sd_user where age < 30) request " +
+ "on (sd_user.id = request.id) " +
+ "when matched then " +
+ " update set sd_user.age = 30",
+ nativeQuery = true)
+ int mergeNativeStatement();
+
interface RolesAndFirstname {
String getFirstname();