diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java b/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
index 6c6f54d12..dff41bfa1 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java
@@ -28,6 +28,7 @@ import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
+import net.sf.jsqlparser.statement.merge.Merge;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
@@ -58,6 +59,7 @@ import org.springframework.util.StringUtils;
*
* @author Diego Krupitza
* @author Greg Turnquist
+ * @author Geoffrey Deremetz
* @since 2.7.0
*/
public class JSqlParserQueryEnhancer implements QueryEnhancer {
@@ -92,6 +94,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;
}
@@ -483,10 +487,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/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java
index 440778bc7..ba1303592 100644
--- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java
@@ -107,6 +107,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")
@@ -2810,6 +2811,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/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java
index 5424d84b9..bdacbcc9f 100644
--- a/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java
@@ -38,6 +38,7 @@ import org.springframework.data.jpa.domain.JpaSort;
* Unit tests for {@link QueryEnhancer}.
*
* @author Diego Krupitza
+ * @author Geoffrey Deremetz
*/
class QueryEnhancerUnitTests {
@@ -922,6 +923,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/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java
index 3e514d18c..c9f48f1ba 100644
--- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java
+++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java
@@ -55,6 +55,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 {
@@ -693,6 +694,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();