使用executeBatch()获取插入(或现有)ID

时间:2015-07-27 09:27:38

标签: java postgresql jdbc

我正在尝试向数据库插入一些单词,如果该单词已存在于数据库中,则返回新插入的id或现有id。

我发现我可以使用PreparedStatement并使用Statement.RETURN_GENERATED_KEYS执行此操作。但是PreparedStatement非常缓慢。我需要一次插入5000个单词。我可以通过在for循环中运行单个查询来实现它的另一种方法:

public ArrayList<Integer> addWords(ArrayList<String[]> allTermsForTag) {
    ArrayList ids = new ArrayList<Integer>();
    ResultSet rs = null;
    try{
        Statement st = connection.createStatement();
        for (String[] articleTerms: allTermsForTag) {
            for(String term: articleTerms) {
                String query = "WITH a AS (INSERT INTO tag (name) SELECT '"+term+"' WHERE NOT EXISTS (SELECT name FROM tag WHERE name = '"+term+"') " +
                        "RETURNING id) SELECT id FROM a UNION SELECT id FROM tag WHERE name = '"+term+"'";
                rs = st.executeQuery(query);
                while (rs.next())
                {
                    int id = rs.getInt(1);
                    ids.add(id);
                    System.out.printf("id: "+id);

                }
            }
        }
        rs.close();
        st.close();
    }catch(SQLException e){
        System.out.println("SQL exception was raised while performing SELECT: "+e);
    }
    return ids;


}

这很适合我需要的东西,但这也太慢了。

我编写的另一种方法使用executeBatch(),但它不会返回ids:

public ArrayList<Integer> addWords(ArrayList<String[]> allTermsForTag){
        ResultSet rs = null;
        ArrayList ids = new ArrayList<Integer>();

        try{
            Statement st = connection.createStatement();
            for (String[] articleTerms: allTermsForTag) {
                for(String term: articleTerms) {
                    String query = "WITH a AS (INSERT INTO tag (name) SELECT '"+term+"' WHERE NOT EXISTS (SELECT name FROM tag WHERE name = '"+term+"') " +
                            "RETURNING id) SELECT id FROM a UNION SELECT id FROM tag WHERE name = '"+term+"'";
                    st.addBatch(query);
                }
                st.executeBatch();
                rs = st.getGeneratedKeys();
                while (rs.next()) {
                    int id = rs.getInt(1);
                    ids.add(id);
                }
            }
            st.close();
            return ids;
        }catch (SQLException e){
            System.out.println("SQL exception was raised while performing batch INSERT: "+e.getNextException());
            System.out.println("dub");
        }
        return null;
    }

所以问题是 - 如何在使用executeBatch()时获取ID,或者如果这不可能,如何解决这个问题?我需要它尽可能快地工作,因为会有大量INSERT操作和大量数据。 谢谢!

2 个答案:

答案 0 :(得分:1)

executeBatch可以在最新的PgJDBC版本中返回生成的密钥。请参阅issue 195pull 204。您必须使用the prepareStatement variant that takes a String[] of returned column names

然而......退后一步。解决方案不是循环。解决方案几乎从不循环。

在这种情况下,您几乎肯定会COPY通过the PgJDBC CopyManager APICOPY数据用于TEMPORARY表格。然后执行INSERT INTO ... SELECT ... RETURNING ...将临时表的内容插入到最终表中并返回任何生成的字段。您还可以执行SELECT加入临时表以返回任何已存在的表。这基本上是bulk upsert或密切相关的批量插入 - 如果不存在。

如果由于某种原因你无法做到这一点,那么下一个最佳选项可能是具有大INSERT列表的多值VALUES,但这需要一些丑陋的动态SQL。由于如果行已经存在,您需要现有值,您可能也需要可写的CTE。实际上,只需使用COPY和查询来进行表合并。

答案 1 :(得分:0)

Set set = new HashSet();
    try {
        PreparedStatement ps = cn.prepareStatement("delete  from myTable where... ", 
        Statement.RETURN_GENERATED_KEYS);
        ps.setInt(1,200);
        ps.setInt(2,262);
        ps.setString(3, "108gf99");
        ps.addBatch();
        ps.setInt(1,200);
        ps.setInt(2,250);
        ps.setString(3, "hgfha");
        ps.addBatch();
        ps.executeBatch();
        ResultSet rs = ps.getGeneratedKeys();
        while (rs.next()){

            set.addAll(Collections.singleton(rs.getLong(1)));
        }

        System.out.println(set);

    } catch (SQLException e) {
        e.printStackTrace();
    }