Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/396.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从HashMap高效地批量插入/复制到表中_Java_Postgresql - Fatal编程技术网

Java 从HashMap高效地批量插入/复制到表中

Java 从HashMap高效地批量插入/复制到表中,java,postgresql,Java,Postgresql,任务: 给定这个HashMap结构:Map mainMap=newhashmap 我想将内部映射的每个值插入或复制到数据库中它自己的单元格中 主地图的大小(如果50000)。 内部贴图的大小为50。 要插入的表有50列。 每个列的标题都是内部映射的键。 编辑:最初用户上载一个包含50列中35列的大型电子表格。然后,我用各种格式清理数据,并为每个mainMap条目向innerMap添加我自己的15个新对。如果不进行清理/格式化/添加,我无法直接从用户的源文件复制到我的数据库 一旦我完成了对电子表格

任务:

给定这个HashMap结构:Map mainMap=newhashmap

我想将内部映射的每个值插入或复制到数据库中它自己的单元格中

主地图的大小(如果50000)。 内部贴图的大小为50。 要插入的表有50列。 每个列的标题都是内部映射的键。 编辑:最初用户上载一个包含50列中35列的大型电子表格。然后,我用各种格式清理数据,并为每个mainMap条目向innerMap添加我自己的15个新对。如果不进行清理/格式化/添加,我无法直接从用户的源文件复制到我的数据库

一旦我完成了对电子表格的迭代和mainMap的构建,我就需要高效地将其插入到数据库表中

研究:

我知道拷贝是最初批量填充表的最佳方法,但是我仍然无法确定我的需求是否支持该命令

声明Postgres对查询的预处理语句参数限制为34464

我假设总共需要50 x 50000=2500000个参数。 这相当于约73个单独的查询

问题:

在这里复制正确的方法而不是所有这些参数吗? 如果是这样,我是否将HashMap值转换为.sql文件,将其保存在web app服务器的磁盘上,然后在COPY命令中引用该值,然后删除临时文件?或者我可以直接将连接的字符串传递给它,而不必冒SQL注入的风险? 此命令将经常发生,因此需要进行优化

我找不到任何将Java对象转换为兼容的Postgres文本文件格式的例子,因此任何反馈都有帮助

你将如何处理这个问题

其他信息:

“我的表”是预先存在的,无法删除,因为它是我的webapp的后端,并且在任何给定时间都连接了多个用户

我知道在使用复制之前临时删除索引可以提高性能,但我只要求一次最多插入或复制50000行,而不是数百万行


StackExchange让我在这里提问。

虽然Java肯定不是进行此类ETL的最佳选择,但使用标准INSERT语句和准备好的查询肯定是可行的,而且开销很小:

conn.setAutoCommitfalse; PreparedStatement stmt=conn.prepareStatement 在我的表格中插入列a,列b。。。 +值。。。; int batchSize=1000; int行=0; 对于贴图值:mainMap.values{ int i=0; stmt.setString++i,values.getcol\u a; stmt.setString++i,values.getcol_b; // ... stmt.addBatch;//将行添加到批中 如果++行%batchSize==0{ //批量调整:执行。。。 stmt.executeBatch; } } 如果行%batchSize!=0 { //如有必要,最后一次执行。。。 stmt.executeBatch; } conn.commit;//原子操作-如果任何记录失败,整个导入将失败
或者,您可以将映射写入一个文件并使用,但我严重怀疑这会比使用成批插入更快,但对于数百万行来说,这会有所不同。

尽管Java肯定不是进行此类ETL的最佳选择,使用标准INSERT语句和准备好的查询当然是可能的,而且开销很小:

conn.setAutoCommitfalse; PreparedStatement stmt=conn.prepareStatement 在我的表格中插入列a,列b。。。 +值。。。; int batchSize=1000; int行=0; 对于贴图值:mainMap.values{ int i=0; stmt.setString++i,values.getcol\u a; stmt.setString++i,values.getcol_b; // ... stmt.addBatch;//将行添加到批中 如果++行%batchSize==0{ //批量调整:执行。。。 stmt.executeBatch; } } 如果行%batchSize!=0 { //如有必要,最后一次执行。。。 stmt.executeBatch; } conn.commit;//原子操作-如果任何记录失败,整个导入将失败 或者,您可以将映射写入一个文件并使用,但我严重怀疑这会比批量插入更快,但对于数百万行来说,这可能会有所不同。

可能确实是初始批量上载的推荐方式,但考虑到初始数据存储在Java映射的内存中,存在一些限制:

首先,它希望从服务器本地加载文件,并由其用户读取,或者再次从服务器本地或通过STDIN执行的程序加载。这些选项对JDBC连接都不是特别友好的。 其次,假设您在同一台机器上准备这样一个文件,即使您可以准备该格式的数据,例如,您仍然需要将存储在Java内存中的数据转换为复制所需的格式。这种处理方式可能不值得使用COPY。 我会创建一个PreparedStatement来插入您的50列,然后 然后对mainMap.values中的每个映射执行准备好的语句,即每次执行50列

你可以使用它来提高速度。也就是说,我不会在一批中执行所有50000个,而是分批执行

我会这样做:

    int BATCH_SIZE = 100;

    List<String> keyNames = new ArrayList<>();

    int i = 0;
    try (PreparedStatement ps = conn
            .prepareStatement("INSERT INTO xyz (col1, col2, ...) VALUES (?, ?, ...)")) {
        for (Map<String, String> rowMap : mainMap.values()) {
            int j = 1;
            // You need the keynames to be in the same order as the columns
            // they match.
            for (String key : keyNames) {
                ps.setString(j, rowMap.get(key));
                j++;
            }
            ps.addBatch();

            if (i > 0 && i % BATCH_SIZE == 0) {
                ps.executeBatch();
            }
            i++;
        }
        if (i % BATCH_SIZE != 1) {
            // More batches to execute since the last time it was done.
            ps.executeBatch();
        }
    }
这可能确实是初始批量上传的推荐方式,但考虑到初始数据存储在Java映射的内存中,存在一些限制:

首先,它希望从服务器本地加载文件,并由其用户读取,或者再次从服务器本地或通过STDIN执行的程序加载。这些选项对JDBC连接都不是特别友好的。 其次,假设您在同一台机器上准备这样一个文件,即使您可以准备该格式的数据,例如,您仍然需要将存储在Java内存中的数据转换为复制所需的格式。这种处理方式可能不值得使用COPY。 相反,我会创建一个PreparedStatement来插入50列,然后迭代执行mainMap.values中每个映射的准备好的语句,即每次50列

你可以使用它来提高速度。也就是说,我不会在一批中执行所有50000个,而是分批执行

我会这样做:

    int BATCH_SIZE = 100;

    List<String> keyNames = new ArrayList<>();

    int i = 0;
    try (PreparedStatement ps = conn
            .prepareStatement("INSERT INTO xyz (col1, col2, ...) VALUES (?, ?, ...)")) {
        for (Map<String, String> rowMap : mainMap.values()) {
            int j = 1;
            // You need the keynames to be in the same order as the columns
            // they match.
            for (String key : keyNames) {
                ps.setString(j, rowMap.get(key));
                j++;
            }
            ps.addBatch();

            if (i > 0 && i % BATCH_SIZE == 0) {
                ps.executeBatch();
            }
            i++;
        }
        if (i % BATCH_SIZE != 1) {
            // More batches to execute since the last time it was done.
            ps.executeBatch();
        }
    }


问题是对象从何而来?好的,您可以处理用户的数据并在单个请求中将其持久化到数据库中吗?这将消除主地图和大批量处理的需要。一旦用户上传电子表格,我就必须逐行迭代。例如,在我迭代第一行之后,在处理下一行之前,我应该如何处理清理后的数据?我不知道如何在不发送昂贵的INSERT查询的情况下,将单行数据持久化到数据库中。这就是我将其存储在RAM中的原因,因此在所有迭代完成后,我将使用最少的查询进行批量插入/复制。我不明白什么?如果我理解正确,mainMap代表一个用户的一个电子表格,并统计50k行,每行50列?不,用户上载的电子表格最多有50000行数据,但只有35列。然后我需要逐行迭代,以不同的方式清理数据的每个单元格。根据该行的数据,我将自己的另外15对添加到内部映射中。因此目标表有50列。在完全组合mainMap时,它基本上是目标表应该具有的相同数据复制。问题是对象来自何处?好的,您可以在单个请求中处理用户的数据并将其保留在数据库中吗?这将消除主地图和大批量处理的需要。一旦用户上传电子表格,我就必须逐行迭代。例如,在我迭代第一行之后,在处理下一行之前,我应该如何处理清理后的数据?我不知道如何在不发送昂贵的INSERT查询的情况下,将单行数据持久化到数据库中。这就是我将其存储在RAM中的原因,因此在所有迭代完成后,我将使用最少的查询进行批量插入/复制。我不明白什么?如果我理解正确,mainMap代表一个用户的一个电子表格,并统计50k行,每行50列?不,用户上载的电子表格最多有50000行数据,但只有35列。然后我需要逐行迭代,以不同的方式清理数据的每个单元格。根据该行的数据,我将自己的另外15对添加到内部映射中。因此目标表有50列。在完全合成mainMap时,它实际上是目标表应该具有的相同数据复制。这看起来很有希望。我会回来报到的。谢谢,我觉得我不得不接受你的回答,因为它是第一个、正确的、有凝聚力的,而且还提供了有关conn.commit的有趣信息,我不知道。谢谢你的支持+这看起来很有希望。我会回来报到的。谢谢,我觉得我不得不接受你的回答,因为它是第一个、正确的、有凝聚力的,而且还提供了有关conn.commit的有趣信息,我不知道。谢谢你的支持+1汉克·布鲁诺,我现在花了一些时间来真正理解你们的答案,然后再进一步回答。我不得不接受另一个答案,因为它是第一个也是正确的,但你的答案非常相似,所以谢谢你。你证实了我对在这里使用COPY的担忧,并教了我一些有趣的花边新闻+1汉克·布鲁诺,我现在花了一些时间来真正理解你们的答案,然后再进一步回答。我不得不接受另一个答案,因为它是第一个也是正确的,但你的答案非常相似,所以谢谢你。你证实了我对在这里使用COPY的担忧,并教了我一些有趣的花边新闻+1.