Spring 通过HTTP为PostgreSQL大型对象提供服务
我正在构建一个应用程序,通过RESTAPI(使用SpringMVC)和PWA(使用Vaadin)从PostgreSQL数据库提供数据服务 PostgreSQL数据库使用存储高达2GB的文件(我无法控制);JDBC驱动程序通过提供对二进制内容的流式访问,因此数据不需要完全读取到内存中 唯一的要求是blob中的流必须在同一事务中使用,否则JDBC驱动程序将抛出 问题是,即使我在事务存储库方法中检索流,Spring MVC和Vaadin的Spring 通过HTTP为PostgreSQL大型对象提供服务,spring,postgresql,hibernate,spring-mvc,vaadin,Spring,Postgresql,Hibernate,Spring Mvc,Vaadin,我正在构建一个应用程序,通过RESTAPI(使用SpringMVC)和PWA(使用Vaadin)从PostgreSQL数据库提供数据服务 PostgreSQL数据库使用存储高达2GB的文件(我无法控制);JDBC驱动程序通过提供对二进制内容的流式访问,因此数据不需要完全读取到内存中 唯一的要求是blob中的流必须在同一事务中使用,否则JDBC驱动程序将抛出 问题是,即使我在事务存储库方法中检索流,Spring MVC和Vaadin的StreamResource都将在事务之外使用它,因此JDBC驱
StreamResource
都将在事务之外使用它,因此JDBC驱动程序抛出
例如,给定
public interface SomeRepository extends JpaRepository<SomeEntity, Long> {
@Transactional(readOnly = true)
default InputStream getStream() {
return findById(1).getBlob().getBinaryStream();
}
}
对于这个VaadinStreamResource
public class SomeView extends VerticalLayout {
public SomeView(SomeRepository repository) {
var resource = new StreamResource("x", repository::getStream);
var anchor = new Anchor(resource, "Download");
add(anchor);
}
}
除此之外:
org.postgresql.util.PSQLException: ERROR: invalid large-object descriptor: 0
这意味着在读取流时事务已经关闭
我认为有两种可能的解决办法:
如何实现以安全、高效的方式为PostgreSQL大型对象提供服务的目标?一个选项是,如您所述,将读取与数据库分离,并将响应写入客户端。缺点是解决方案的复杂性,您需要在读写器之间进行同步 另一种选择是首先在主事务中获取大对象id,然后在块中读取数据,每个块在单独的事务中读取
byte[] getBlobChunk(Connection connection, long lobId, long start, long chunkSize) throws SQLException {
Blob blob = PgBlob(connection, lobId);
InputStream is = blob.getBinaryStream(start, chunkSize);
return IOUtils.toByteArray(is);
}
此解决方案简单得多,但有建立新连接的开销,如果您使用连接池,这应该不是什么大问题。我们在中通过使用线程+管道流和一个特殊的inputstream包装器ClosingInputStream
来解决此问题,该包装器延迟关闭连接/事务,直到使用者关闭关闭输入流。也许类似的东西也能帮你
仅供参考。我们发现,与类似的数据库相比,使用Postgres的OID和大型对象API的速度非常慢
也许您也可以将SpringContentJPA改装到您的解决方案中,从而使用它的http端点(以及我刚才概述的解决方案),而不是创建自己的端点?类似这样的:
pom.xml
SomeEntityContentStore.java
@StoreRestResource(path=“someEntityContent”)
公共接口SomeEntityContentStore扩展ContentStore{
}
只需获取REST端点,即可将内容与实体关联
SomeEntity
。我们的示例回购协议中有一个工作示例。谢谢您的回答。我更喜欢在单个事务中读取流,因为在多个事务之间,流可能会改变。
byte[] getBlobChunk(Connection connection, long lobId, long start, long chunkSize) throws SQLException {
Blob blob = PgBlob(connection, lobId);
InputStream is = blob.getBinaryStream(start, chunkSize);
return IOUtils.toByteArray(is);
}
<!-- Java API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-jpa-boot-starter</artifactId>
<version>0.4.0</version>
</dependency>
<!-- REST API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>0.4.0</version>
</dependency>
@Entity
public class SomeEntity {
@Id
@GeneratedValue
private long id;
@ContentId
private String contentId;
@ContentLength
private long contentLength = 0L;
@MimeType
private String mimeType = "text/plain";
...
}
@StoreRestResource(path="someEntityContent")
public interface SomeEntityContentStore extends ContentStore<SomeEntity, String> {
}