通过HTTP

时间:2018-10-05 16:21:27

标签: spring postgresql hibernate spring-mvc vaadin

我正在构建一个应用程序,以通过REST API(带有Spring MVC)和PWA(带有Vaadin)从PostgreSQL数据库提供数据。

PostgreSQL数据库使用Large Objects存储高达2GB的文件(我无法控制); JDBC驱动程序通过Blob#getBinaryStream提供对其二进制内容的流式访问,因此不需要将数据完全读取到内存中。

唯一的要求是,必须在同一事务中使用来自blob的流,否则JDBC驱动程序将引发该事件。

问题在于,即使我使用事务性存储库方法检索流,Spring MVC和Vaadin的StreamResource都将在事务外使用它,因此JDBC驱动程序将抛出异​​常。

例如,给定

public interface SomeRepository extends JpaRepository<SomeEntity, Long> {

    @Transactional(readOnly = true)
    default InputStream getStream() {
        return findById(1).getBlob().getBinaryStream();
    }
}

此Spring MVC方法将失败

@RestController
public class SomeController {

    private final SomeRepository repository;

    @GetMapping
    public ResponseEntity getStream() {
        var stream = repository.getStream();
        var resource = new InputStreamResource(stream);
        return new ResponseEntity(resource, HttpStatus.OK);
    }
}

和此Vaadin StreamResource

相同
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

这意味着在读取流时事务已经关闭。

我看到了两种可能的解决方案:

  1. 在下载过程中保持交易打开;
  2. 在事务期间将流写入磁盘,然后在下载期间从磁盘提供文件。

解决方案1是一种反模式,并且存在安全风险:交易持续时间留在客户端手上,慢速阅读器或攻击者都可能阻止数据访问。

解决方案2在客户端请求和服务器响应之间造成了巨大的延迟,因为流是首先从数据库中读取并写入磁盘的。

一个想法可能是在用数据库中的数据写入文件时开始从磁盘读取数据,以便立即开始传输,但是事务处理时间与客户端下载无关。但我不知道这可能会有哪些副作用。

如何实现以安全,高效的方式为PostgreSQL大对象提供服务的目标?

2 个答案:

答案 0 :(得分:1)

我们在Spring Content中通过使用线程+管道流和特殊的输入流包装器ClosingInputStream解决了这个问题,该包装器延迟关闭连接/事务直到使用者关闭输入流。也许像this这样的东西也会对您有帮助吗?

仅供参考。我们发现与类似的数据库相比,使用Postgres的OID和大对象API的速度非常慢。

也许您还可以仅将Spring Content JPA改造为您的解决方案,并因此使用其HTTP端点(以及我刚才概述的解决方案),而不用自己创建?像这样:-

  

pom.xml

   <!-- 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>
  

SomeEntity.java

@Entity
public class SomeEntity {
   @Id
   @GeneratedValue
   private long id;

   @ContentId
   private String contentId;

   @ContentLength
   private long contentLength = 0L;

   @MimeType
   private String mimeType = "text/plain";

   ...
}
  

SomeEntityContentStore.java

@StoreRestResource(path="someEntityContent")
public interface SomeEntityContentStore extends ContentStore<SomeEntity, String> {
}

仅需获取REST端点即可使您将内容与实体SomeEntity关联。我们的示例存储库here中有一个有效的示例。

答案 1 :(得分:0)

一个选择是如您提到的,将读取数据与将响应写入客户端的方式分离。缺点是解决方案的复杂性,您需要在读取器和写入器之间进行同步。

另一种选择是首先在主事务中获取大对象ID,然后按块读取数据,每个块在单独的事务中读取。

dat <- structure(list(SitePondGpsRep = c("BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-1-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-3-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1", "BURR-1-4-1"), DateTime_local = c("2018-05-30 10:49:04", "2018-05-30 10:49:05", "2018-05-30 10:49:06", "2018-05-30 10:49:07", "2018-05-30 10:49:08", "2018-05-30 10:49:09", "2018-05-30 10:49:10", "2018-05-30 10:49:27", "2018-05-30 10:49:28", "2018-05-30 10:49:29", "2018-05-30 10:49:30", "2018-05-30 10:49:31", "2018-05-30 10:49:32", "2018-05-30 10:49:33", "2018-05-30 10:49:34", "2018-05-30 10:49:35", "2018-05-30 10:49:36", "2018-05-30 10:49:37", "2018-05-30 10:49:38", "2018-05-30 10:49:39", "2018-05-30 10:49:40", "2018-05-30 10:49:41", "2018-05-30 10:49:42", "2018-05-30 10:49:43", "2018-05-30 10:49:44", "2018-05-30 10:49:45", "2018-05-30 10:49:46", "2018-05-30 10:49:47", "2018-05-30 10:49:48", "2018-05-30 10:49:49", "2018-05-30 10:49:50", "2018-05-30 10:49:51", "2018-05-30 10:49:52", "2018-05-30 10:49:54", "2018-05-30 10:49:55", "2018-05-30 10:49:56", "2018-05-30 10:49:57", "2018-05-30 10:49:58", "2018-05-30 10:50:01", "2018-05-30 10:50:02", "2018-05-30 10:50:03", "2018-05-30 10:50:04", "2018-05-30 10:50:05", "2018-05-30 10:50:06", "2018-05-30 10:50:07", "2018-05-30 10:50:09", "2018-05-30 10:50:10", "2018-05-30 10:50:11", "2018-05-30 10:50:12", "2018-05-30 10:50:13", "2018-05-30 10:50:14", "2018-05-30 10:50:15", "2018-05-30 10:50:16", "2018-05-30 10:50:17", "2018-05-30 10:50:18", "2018-05-30 10:50:20", "2018-05-30 10:50:24", "2018-05-30 10:50:27", "2018-05-30 10:50:36", "2018-05-30 10:50:41", "2018-05-30 10:50:42", "2018-05-30 10:50:43", "2018-05-30 10:50:44", "2018-05-30 10:50:45", "2018-05-30 10:49:05", "2018-05-30 10:49:06", "2018-05-30 10:49:07", "2018-05-30 10:49:08", "2018-05-30 10:49:09", "2018-05-30 10:49:10", "2018-05-30 10:49:11", "2018-05-30 10:49:12", "2018-05-30 10:49:13", "2018-05-30 10:49:19", "2018-05-30 10:49:31", "2018-05-30 10:49:32", "2018-05-30 10:49:33", "2018-05-30 10:49:34", "2018-05-30 10:49:35", "2018-05-30 10:49:36", "2018-05-30 10:49:37", "2018-05-30 10:49:38", "2018-05-30 10:49:39", "2018-05-30 10:49:40", "2018-05-30 10:49:41", "2018-05-30 10:49:42", "2018-05-30 10:49:43", "2018-05-30 10:49:44", "2018-05-30 10:49:45", "2018-05-30 10:49:46", "2018-05-30 10:49:47", "2018-05-30 10:49:48", "2018-05-30 10:49:49", "2018-05-30 10:49:50", "2018-05-30 10:49:51", "2018-05-30 10:49:52", "2018-05-30 10:49:53", "2018-05-30 10:49:54", "2018-05-30 10:49:55", "2018-05-30 10:49:56", "2018-05-30 10:49:57", "2018-05-30 10:49:58", "2018-05-30 10:49:59", "2018-05-30 10:50:00", "2018-05-30 10:50:01", "2018-05-30 10:50:02", "2018-05-30 10:50:03", "2018-05-30 10:50:04", "2018-05-30 10:50:05", "2018-05-30 10:50:06", "2018-05-30 10:50:07", "2018-05-30 10:50:10", "2018-05-30 10:50:11", "2018-05-30 10:50:12", "2018-05-30 10:50:13", "2018-05-30 10:50:14", "2018-05-30 10:50:15", "2018-05-30 10:50:16", "2018-05-30 10:50:37", "2018-05-30 10:50:44", "2018-05-30 10:49:05", "2018-05-30 10:49:06", "2018-05-30 10:49:07", "2018-05-30 10:49:08", "2018-05-30 10:49:09", "2018-05-30 10:49:10", "2018-05-30 10:49:11", "2018-05-30 10:49:12", "2018-05-30 10:49:19", "2018-05-30 10:49:21", "2018-05-30 10:49:22", "2018-05-30 10:49:26", "2018-05-30 10:49:27", "2018-05-30 10:49:30", "2018-05-30 10:49:31", "2018-05-30 10:49:32", "2018-05-30 10:49:33", "2018-05-30 10:49:34", "2018-05-30 10:49:35", "2018-05-30 10:49:36", "2018-05-30 10:49:37", "2018-05-30 10:49:38", "2018-05-30 10:49:39", "2018-05-30 10:49:40", "2018-05-30 10:49:41", "2018-05-30 10:49:42", "2018-05-30 10:49:43", "2018-05-30 10:49:44", "2018-05-30 10:49:45", "2018-05-30 10:49:46", "2018-05-30 10:49:47", "2018-05-30 10:49:48", "2018-05-30 10:49:49", "2018-05-30 10:49:50", "2018-05-30 10:49:51", "2018-05-30 10:49:52", "2018-05-30 10:49:54", "2018-05-30 10:49:57", "2018-05-30 10:49:58", "2018-05-30 10:49:59", "2018-05-30 10:50:00", "2018-05-30 10:50:01", "2018-05-30 10:50:02", "2018-05-30 10:50:03", "2018-05-30 10:50:04", "2018-05-30 10:50:05", "2018-05-30 10:50:06", "2018-05-30 10:50:07", "2018-05-30 10:50:08", "2018-05-30 10:50:09", "2018-05-30 10:50:10", "2018-05-30 10:50:11", "2018-05-30 10:50:12", "2018-05-30 10:50:13", "2018-05-30 10:50:14", "2018-05-30 10:50:15", "2018-05-30 10:50:16"), Latitude = c(51.9851623569, 51.9851641171, 51.9851674698, 51.9851741754, 51.9851825573, 51.9851923641, 51.9852027576, 51.9853603374, 51.985360086, 51.9853615109, 51.9853631873, 51.9853626005, 51.9853596669, 51.9853546377, 51.9853501953, 51.9853491057, 51.9853499439, 51.9853510335, 51.9853526261, 51.9853537157, 51.9853544701, 51.9853550568, 51.9853562303, 51.985358661, 51.9853618462, 51.985365618, 51.9853699766, 51.9853755087, 51.9853831362, 51.9853900932, 51.9853944518, 51.9853973016, 51.9854001515, 51.9854111318, 51.9854149874, 51.9854135625, 51.985412389, 51.9854097068, 51.9853739161, 51.9853589125, 51.9853450824, 51.9853315037, 51.9853169192, 51.9853025861, 51.9852880016, 51.985260509, 51.9852461759, 51.9852311723, 51.9852169231, 51.9852023385, 51.9851880893, 51.985174343, 51.9851596747, 51.9851456769, 51.9851331878, 51.9851182681, 51.9851253927, 51.9851476047, 51.9851814676, 51.9851861615, 51.9851861615, 51.9851835631, 51.9851810485, 51.985178953, 51.9851332717, 51.9851353671, 51.9851413183, 51.9851502031, 51.9851596747, 51.9851695653, 51.9851823058, 51.9851954654, 51.9852077868, 51.9852676336, 51.9853508659, 51.9853491057, 51.9853438251, 51.9853392988, 51.9853389636, 51.985339215, 51.9853398018, 51.9853422325, 51.9853455853, 51.9853481837, 51.9853506982, 51.9853537995, 51.9853565656, 51.9853595831, 51.9853626005, 51.9853671268, 51.9853722397, 51.9853787776, 51.9853857346, 51.9853908475, 51.9853956252, 51.9854015764, 51.9854076114, 51.9854149874, 51.9854189269, 51.9854177535, 51.9854149874, 51.985412892, 51.9854062703, 51.985392943, 51.9853766821, 51.9853594154, 51.9853420649, 51.9853279833, 51.9853130635, 51.9852968026, 51.9852817152, 51.9852411468, 51.9852255564, 51.9852114748, 51.9851971418, 51.9851819705, 51.9851673022, 51.9851524662, 51.985171577, 51.9851645362, 51.9851415697, 51.9851436652, 51.9851481915, 51.9851561543, 51.9851645362, 51.9851733372, 51.9851848204, 51.9851983991, 51.9852668792, 51.9852864929, 51.9853002392, 51.9853428192, 51.9853453338, 51.9853506144, 51.9853511173, 51.9853491057, 51.9853441603, 51.9853404723, 51.9853403047, 51.9853417296, 51.9853436574, 51.9853462558, 51.985348016, 51.9853496924, 51.9853510335, 51.9853532966, 51.9853569008, 51.9853605051, 51.9853646122, 51.9853681326, 51.9853726588, 51.9853808731, 51.985391099, 51.9853972178, 51.9854009897, 51.9854052644, 51.9854188431, 51.9854213577, 51.985419346, 51.985411467, 51.9853983074, 51.9853829686, 51.9853670429, 51.9853521232, 51.9853375386, 51.9853222836, 51.9853064418, 51.9852899294, 51.9852756802, 51.9852615986, 51.9852463435, 51.9852309208, 51.9852158334, 51.9852004945, 51.9851861615, 51.9851720799, 51.9851580821), Longitude = c(-105.0767748244, -105.0767996348, -105.0768228527, -105.0768438913, -105.0768627506, -105.0768831186, -105.0768996309, -105.0768738147, -105.0768491719, -105.0768251996, -105.0768006407, -105.0767758302, -105.0767515227, -105.0767283887, -105.0767055061, -105.0766806956, -105.0766552985, -105.0766305719, -105.0766069349, -105.0765823759, -105.0765584037, -105.0765349343, -105.0765112974, -105.076487828, -105.0764643587, -105.0764413923, -105.0764174201, -105.0763948727, -105.0763716549, -105.0763481855, -105.0763243809, -105.0763000734, -105.0762776099, -105.0762347784, -105.0762113091, -105.0761888456, -105.0761658791, -105.0761429127, -105.0761163421, -105.0761119835, -105.0761065353, -105.076102512, -105.0761000812, -105.076098321, -105.0760969799, -105.0760942139, -105.0760934595, -105.0760923699, -105.0760906935, -105.0760900229, -105.0760909449, -105.0760921184, -105.0760919508, -105.0760952197, -105.0761054456, -105.0761408173, -105.0762129016, -105.0762677193, -105.0764359441, -105.0765430648, -105.0765658636, -105.0765891653, -105.0766126346, -105.0766350981, -105.0767853856, -105.0768098608, -105.076832911, -105.0768533628, -105.0768733118, -105.0768928416, -105.0769080129, -105.0769187417, -105.0769294705, -105.0769564603, -105.0768144708, -105.0767890736, -105.0767669454, -105.0767435599, -105.0767160673, -105.0766881555, -105.0766630936, -105.0766382832, -105.076612886, -105.0765872374, -105.0765617564, -105.076536946, -105.0765135605, -105.0764897559, -105.0764658675, -105.0764395483, -105.0764154922, -105.0763928611, -105.0763694756, -105.0763450842, -105.0763208605, -105.0762992352, -105.0762787834, -105.0762574095, -105.0762354489, -105.0762114767, -105.076188175, -105.0761637837, -105.0761408173, -105.0761313457, -105.0761326868, -105.076130759, -105.0761248916, -105.076120114, -105.0761155877, -105.0761135761, -105.0761135761, -105.0761101395, -105.0761072896, -105.0761061162, -105.0761060324, -105.0761067867, -105.0761083793, -105.0761087146, -105.0764583237, -105.0766065158, -105.0767834578, -105.0768087711, -105.0768324919, -105.0768561289, -105.0768786762, -105.0768972002, -105.0769114494, -105.0769231003, -105.0769605674, -105.0769288, -105.076918155, -105.0768983737, -105.0768753234, -105.076808352, -105.0767832901, -105.0767600723, -105.0767381117, -105.0767147262, -105.076689329, -105.0766637642, -105.0766387023, -105.0766154006, -105.0765913446, -105.0765661988, -105.0765423942, -105.0765190087, -105.0764952041, -105.0764719862, -105.076448014, -105.0764254667, -105.0764035899, -105.0763796177, -105.0763540529, -105.0763287395, -105.0763040129, -105.0762819685, -105.0762406457, -105.0761751831, -105.0761494506, -105.0761289988, -105.076121036, -105.0761198625, -105.0761174317, -105.0761130732, -105.076108044, -105.0761036016, -105.0761017576, -105.0761008356, -105.0760996621, -105.0760973152, -105.0760949682, -105.0760931242, -105.0760921184, -105.0760929566, -105.0760953873, -105.0760967284, -105.0760972314)), class = "data.frame", row.names = c(NA, -177L))
library(tidyverse)
library(sf)
dat %>%
  st_as_sf(coords = c("Longitude", "Latitude"), crs = 4326) %>%
  group_by(SitePondGpsRep) %>%
  summarise(geometry = st_combine(geometry)) %>%
  st_cast("POLYGON") %>%
  plot()

此解决方案简单得多,但是建立新连接的开销很大,如果您使用连接池,这没什么大不了的。