我有一个 Spring Boot 3.0 应用程序,它从 Spring 数据存储库获取 Blob:
@Service
public class CarService {
private final CarRepository carRepository;
public Set<CarDto> getValuations(LocalDate date) {
Blob reportData = carRepository.getReportData("sedan,truck", date);
try {
byte[] b= reportData.getBytes(1, (int) reportData.length());
log.debug("connection is closed by Spring Data JPA");
//...
}
public interface CarRepository extends Repository<Car, Long> {
@Procedure(value="pValuation", outputParameterName="reportData")
Blob getReportData(String carTypes, LocalDate tradeDate);
不幸的是,Spring Data 在从
Blob
方法返回 getReportData
后关闭连接。当我尝试从 Blob
获取字节时,这会导致以下错误:
SQLException:com.microsoft.sqlserver.jdbc.SQLServerException:连接已关闭。连接已关闭。
我可以通过使
getValuations
事务化(即将方法注释为 @Transactional
)来保持连接打开,但随后数据库会因为锁定问题而挂起。
如何告诉 Spring Data 在没有事务的情况下保持连接打开,以便我可以从 Blob 检索字节?
注意:我不能使用 byte[] 作为
getReportData
的返回类型,因为数据将被截断为 8000 字节。
注意:我的程序
pValuation
是只读的。
更新
我可以在不使用 Spring Data JPA(并且没有事务)的情况下调用此存储过程,如下所示:
@Service
@RequiredArgsConstructor
@Slf4j
public class CarService {
private final CarRepository carRepository;
private final EntityManager em;
public Set<CarDto> getValuations(LocalDate date) {
StoredProcedureQuery q = em.createStoredProcedureQuery("pValuation");
q.registerStoredProcedureParameter("carTypes", String.class, ParameterMode.IN);
q.registerStoredProcedureParameter("tradeDate", LocalDate.class, ParameterMode.IN);
q.registerStoredProcedureParameter("reportData", Blob.class, ParameterMode.OUT);
q.setParameter("carTypes", "sedan,truck");
q.setParameter("tradeDate", date);
q.execute();
Blob reportData = (Blob) q.getOutputParameterValue("reportData");
log.debug("got Blob");
try {
byte[] b= reportData.getBytes(1, (int) reportData.length());
log.debug("got bytes");
return carRepository.getCarValuations(b);
}
catch (SQLException convertBlobToBytesException) {
log.error(convertBlobToBytesException.toString());
}
}
此代码之所以有效,是因为在读取字节时连接保持打开状态。据我所知,Spring Data 在存储库方法调用后关闭连接,除非我有事务(我不能使用事务,因为 DB SP 挂起)。
您可以查看
EntityManager
,而不是将 createStoredProcedureQuery
与
SimpleJdbcCall
一起使用。 API 非常相似,您可以定义一次(作为 bean 或在构造函数中)并重复使用该定义。额外的好处是提取结果的回调在当前连接上运行,因此它保持打开状态。
Spring Boot 自动配置一个
JdbcTemplate
,您可以重复使用它来创建 SimpleJdbcCall
。
SimpleJdbcCall sp = new SimpleJdbcCall(jdbcTemplate);
sp.declareParameters(new SqlParameter("carTypes", Types.VARCHAR),
new SqlParameter("tradeDate", Types.DATE), new SqlOutParameter("reportData", Types.BLOB, new RowMapper())