java 如何让Spring Data JPA在没有transaction的情况下保持连接打开?

g6baxovj  于 2023-10-14  发布在  Java
关注(0)|答案(1)|浏览(127)

我有一个Sping Boot 3.0应用程序,它从Spring Data repository获取一个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在从getReportData方法返回Blob后关闭了连接。当我尝试从Blob获取字节时,这会导致以下错误:
SQLException:com.microsoft.sqlserver.jdbc.SQLServerException:连接已关闭。连接已关闭。
我可以通过使getValuations成为transactional(即,将该方法注解为@Transactional)来保持连接打开,但是由于锁定问题,数据库挂起。
我如何告诉Spring Data在没有transaction的情况下保持连接打开,以便我可以从Blob中检索字节?

  • 注意:我不能使用byte[]作为getReportData的返回类型,因为数据将被截断到8000字节。
  • 注意:我的过程pValuation是只读的。*
    更新

我可以在不使用Spring Data JPA的情况下调用这个存储过程(并且没有transaction),如下所示:

@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在repository方法调用之后关闭连接,除非我有一个transaction(我不能使用它,因为DB SP挂起了B/c)。

ecbunoof

ecbunoof1#

如果不将EntityManagercreateStoredProcedureQuery一起使用,您可以看看SimpleJdbcCall。API非常相似,您可以定义一次(作为bean或在构造函数中)并重用该定义。额外的好处是,提取结果的回调是在当前连接上操作的,因此它保持打开状态。
Sping 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, (rs, row) -> { Blob blob = rs.getBlob("reportData"); return blob.getBytes(1, (int) blob.length());})

sp.executeObject(byte[].class, "sedan,truck", date);

沿着这些路线的东西应该会起作用。不确定代码是否完全正常,因为我是从我的头顶编码的。

相关问题