Spring JPA @Procedure 无法正确处理多个 OUT 子句

Spring JPA @Procedure not working correctly with multiple OUT clause

无法使用 Spring JPA 为以下设置映射或获得所需结果。 我的存储过程如下:

CREATE PROCEDURE [dbo].[sp_name] AS BEGIN
SET NOCOUNT ON;
MERGE Products AS TARGET
USING UpdatedProducts AS SOURCE 
ON (TARGET.ProductID = SOURCE.ProductID) 
--When records are matched, update the records if there is any change
WHEN MATCHED AND TARGET.ProductName <> SOURCE.ProductName OR TARGET.Rate <> SOURCE.Rate 
THEN UPDATE SET TARGET.ProductName = SOURCE.ProductName, TARGET.Rate = SOURCE.Rate 
--When no records are matched, insert the incoming records from source table to target table
WHEN NOT MATCHED BY TARGET 
THEN INSERT (ProductID, ProductName, Rate) VALUES (SOURCE.ProductID, SOURCE.ProductName, SOURCE.Rate)
--When there is a row that exists in target and same record does not exist in source then delete this record target
WHEN NOT MATCHED BY SOURCE 
THEN DELETE 
--$action specifies a column of type nvarchar(10) in the OUTPUT clause that returns 
--one of three values for each row: 'INSERT', 'UPDATE', or 'DELETE' according to the action that was performed on that row
OUTPUT 
DELETED.ProductID AS TargetProductID, 
INSERTED.ProductID AS SourceProductID
END;
GO

我的@Repository class 看起来像:

@Procedure(procedureName = "sp_name")
Map<String, Integer> callingSP();

低于异常:

Type cannot be null; nested exception is java.lang.IllegalArgumentException: Type cannot be null

请问哪里出了问题?

对于不以 table 形式存在的结果集(因此在某处具有 @Entity 修饰的 class 定义),技巧似乎是使用 interface 用于在您的存储库中声明的 @Query-修饰方法的结果。

给定存储过程的 SQL 设置...

use master;
go
create database Whosebug;
go
use Whosebug;
go
create table dbo.Products(
    ProductID int not null,
    ProductName nvarchar(50),
    Rate float
);
go
create table dbo.UpdatedProducts(
    ProductID int not null,
    ProductName nvarchar(50),
    Rate float
);
go
insert dbo.Products (ProductID, ProductName, Rate) values
    (10, 'Ten', 10.10),
    (20, 'Twenty', 20.20);
insert dbo.UpdatedProducts (ProductID, ProductName, Rate) values
    (20, 'Twenty', 20),
    (30, 'Thirty', 30);
go
select * from dbo.Products;
select * from dbo.UpdatedProducts;
go

产生...

ProductID ProductName Rate
10 Ten 10.1
20 Twenty 20.199999999999999
ProductID ProductName Rate
20 Twenty 20.0
30 Thirty 30.0

然后,在 Java 我们有...

// MergeResult.java
public interface MergeResult {
  Integer getSourceProductID();
  Integer getTargetProductID();
}
// ProductsRepository.java
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductsRepository extends JpaRepository<Products, Integer> {
    @Query(nativeQuery = true, value = "EXEC dbo.sp_name")
    List<MergeResult> callingSP();
}
// FooJpaApplication.java
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class FooJpaApplication {

  private static final Logger log = LoggerFactory.getLogger(FooJpaApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(FooJpaApplication.class);
  }

  @Bean
  public CommandLineRunner demo(ProductsRepository repository) {
    return (args) -> {
      log.info("Executing sp_name()");
      log.info("-------------------");
      List<MergeResult> results = repository.callingSP();
      for (MergeResult r : results) {
        String line = String.format("TargetProductID=%d; SourceProductID=%d", r.getTargetProductID(),
            r.getSourceProductID());
        log.info(line);
      }
    };
  }
}

产生日志输出...

...
2021-02-25 20:30:01.136  INFO 84285 --- [           main] c.e.a.FooJpaApplication        : Started FooJpaApplication in 3.076 seconds (JVM running for 3.388)
2021-02-25 20:30:01.138  INFO 84285 --- [           main] c.e.a.FooJpaApplication        : Executing sp_name()
2021-02-25 20:30:01.138  INFO 84285 --- [           main] c.e.a.FooJpaApplication        : -------------------
2021-02-25 20:30:01.238  INFO 84285 --- [           main] c.e.a.FooJpaApplication        : TargetProductID=null; SourceProductID=30
2021-02-25 20:30:01.238  INFO 84285 --- [           main] c.e.a.FooJpaApplication        : TargetProductID=10; SourceProductID=null
2021-02-25 20:30:01.238  INFO 84285 --- [           main] c.e.a.FooJpaApplication        : TargetProductID=20; SourceProductID=20
2021-02-25 20:30:01.243  INFO 84285 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
...