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();