天天看点

量数据导出到JSON文件中

有时,您需要将大量数据导出到JSON文件中。可能是“将所有数据导出到JSON”,或者是GDPR“可移植性的权利”,您实际上需要这样做。

与任何大型数据集一样,您不能将其全部放入内存中并将其写入文件。这需要一段时间,它从数据库中读取了很多条目,您需要小心,不要使这种导出重载整个系统,或者耗尽内存。

幸运的是,在杰克逊的帮助下,这样做很简单​<code>​SequenceWriter​</code>​和可选的管道流。下面是它的样子:

<col>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

​<code>​private​</code>​​ ​<code>​ObjectMapper jsonMapper = ​</code>​​<code>​new​</code>​​ ​<code>​ObjectMapper();​</code>​

​<code>​private​</code>​​ ​<code>​ExecutorService executorService = Executors.newFixedThreadPool(​</code>​​<code>​5​</code>​​<code>​);​</code>​

​<code>​@Async​</code>​

​<code>​public​</code>​​ ​<code>​ListenableFuture&lt;Boolean&gt; export(UUID customerId) {​</code>​

​<code>​try​</code>​​ ​<code>​(PipedInputStream in = ​</code>​​<code>​new​</code>​​ ​<code>​PipedInputStream();​</code>​

​<code>​PipedOutputStream pipedOut = ​</code>​​<code>​new​</code>​​ ​<code>​PipedOutputStream(in);​</code>​

​<code>​GZIPOutputStream out = ​</code>​​<code>​new​</code>​​ ​<code>​GZIPOutputStream(pipedOut)) {​</code>​

​<code>​Stopwatch stopwatch = Stopwatch.createStarted();​</code>​

​<code>​ObjectWriter writer = jsonMapper.writer().withDefaultPrettyPrinter();​</code>​

​<code>​try​</code>​​<code>​(SequenceWriter sequenceWriter = writer.writeValues(out)) {​</code>​

​<code>​sequenceWriter.init(​</code>​​<code>​true​</code>​​<code>​);​</code>​

​<code>​Future&lt;?&gt; storageFuture = executorService.submit(() -&gt;​</code>​

​<code>​storageProvider.storeFile(getFilePath(customerId), in));​</code>​

​<code>​int​</code>​​ ​<code>​batchCounter = ​</code>​​<code>​0​</code>​​<code>​;​</code>​

​<code>​while​</code>​​ ​<code>​(​</code>​​<code>​true​</code>​​<code>​) {​</code>​

​<code>​List&lt;Record&gt; batch = readDatabaseBatch(batchCounter++);​</code>​

​<code>​for​</code>​​ ​<code>​(Record record : batch) {​</code>​

​<code>​sequenceWriter.write(entry);​</code>​

​<code>​}​</code>​

​<code>​if​</code>​​ ​<code>​(batch.isEmpty()) {​</code>​

​<code>​// if there are no more batches, stop.​</code>​

​<code>​break​</code>​​<code>​;​</code>​

​<code>​// wait for storing to complete​</code>​

​<code>​storageFuture.get();​</code>​

​<code>​// send the customer a notification and a download link​</code>​

​<code>​notifyCustomer(customerId);​</code>​

​<code>​}  ​</code>​

​<code>​logger.info(​</code>​​<code>​"Exporting took {} seconds"​</code>​​<code>​, stopwatch.stop().elapsed(TimeUnit.SECONDS));​</code>​

​<code>​return​</code>​​ ​<code>​AsyncResult.forValue(​</code>​​<code>​true​</code>​​<code>​);​</code>​

​<code>​} ​</code>​​<code>​catch​</code>​​ ​<code>​(Exception ex) {​</code>​

​<code>​logger.error(​</code>​​<code>​"Failed to export data"​</code>​​<code>​, ex);​</code>​

​<code>​return​</code>​​ ​<code>​AsyncResult.forValue(​</code>​​<code>​false​</code>​​<code>​);​</code>​

代码做了几件事:

​​http://www.itangyuan.com/book/16245130.html​​

​​https://www.wenjuan.com/s/UZBZJvTYoO/​​

使用SequenceWriter连续写入记录。它是用OutputStream初始化的,所有内容都写入到OutputStream中。这可能是一个简单的FileOutputStream,或者如下所述的管道流。注意这里的命名有点误导-​<code>​writeValues(out)​</code>​听起来,您现在正在指示作者编写一些东西;相反,它将其配置为稍后使用特定的流。

这个​<code>​SequenceWriter​</code>​​初始化为​<code>​true​</code>​,意思是“在数组中包装”。您正在编写许多相同的记录,因此它们应该在最终的JSON中表示一个数组。

使用​<code>​PipedOutputStream​</code>​​和​<code>​PipedInputStream​</code>​​链接​<code>​SequenceWriter​</code>​​转到​<code>​InputStream​</code>​​然后传递给存储服务。如果我们显式地处理文件,就不需要这样做--只需传递一个​<code>​FileOutputStream​</code>​就行了。但是,您可能希望以不同的方式存储文件,例如在AmazonS 3中,并且在那里,putObject调用需要一个InputStream来读取数据并将其存储在S3中。因此,实际上,您正在写入一个OutputStream,它直接写到InputStream,当被攻击时,InputStream将所有的内容都写入另一个OutputStream

存储文件是在一个单独的线程中调用的,这样对文件的写入不会阻塞当前线程,该线程的目的是从数据库中读取。同样,如果使用了简单的FileOutputStream,则不需要这样做。

整个方法被标记为@异步(Spring),这样它就不会阻止执行--它将被调用并在准备就绪时结束(使用内部SpringExecutor服务和一个有限的线程池)

此处不显示数据库批处理读取代码,因为它根据数据库的不同而有所不同。关键是,您应该分批获取数据,而不是从X中选择*。

OutputStream封装在GZIPOutputStream中,因为具有重复元素的文本文件(如JSON)从压缩中明显受益

主要的工作是由杰克逊的SequenceWriter完成的,(有点明显的)要点是--不要假设您的数据会被存储在内存中。它几乎从来没有这样做过,所以每件事都是分批和增量写的