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'
...
无法使用 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'
...