【问题标题】:Java Heap space with jpa native Query带有 jpa 本机查询的 Java 堆空间
【发布时间】:2018-05-27 08:18:46
【问题描述】:

我在尝试通过 jpa / hibernate EM 运行本机 SQL 查询时遇到 OOME 问题。 每包 50 个可插入数百万个。

这是我的算法代码:

private void createNewJetonsForIndividus(boolean isGlobal, List<String> entreprises, List<String> services,
                                         String user, Timestamp dateDB) {

    LocalDateTime timer = LocalDateTime.now();

    List<Object[]> MinMaxId = getMinMaxIdDroitsIndividusActifsForCreation(
            isGlobal, entreprises, services);

    if (null != MinMaxId.get(0)[0]) {

        int idStart =  ((BigInteger) MinMaxId.get(0)[0]).intValue();
        int idEnd = idStart + PAS;
        int idMax = ((BigInteger) MinMaxId.get(0)[1]).intValue();
        int nbRowsTotal = 0;
        Logger.debug("Droits Individus : ID Min {}  -  ID Max {}", idStart, idMax);


        do {
            int finalIdStart = idStart;
            int finalIdEnd = idEnd;
            callTransaction(() -> create(false, true,isGlobal, entreprises, services, finalIdStart,
                    finalIdEnd, user, dateDB));
            idStart = idEnd + 1;
            idEnd = idEnd + PAS;
        }
        while (idMax > idEnd);

    }
}

该方法用于计算我感兴趣的记录的 id 最小值和最大值。随后,我使用 create 方法,其代码如下:

int nbRowsFind;

    List<Object[]> listeDroitsIndividusActifsForCreation = getDroitsIndividusActifsForCreation(
            isGlobal, entreprises, services, idStart, idEnd);

    if (ValidationUtils.isNotEmpty(listeDroitsIndividusActifsForCreation)) {

        nbRowsFind = listeDroitsIndividusActifsForCreation.size();

        StringBuilder sbJeton = new StringBuilder();
        sbJeton.append("INSERT INTO sigs_nv_jeton VALUES ");


        StringBuilder sbDroitHasJeton = new StringBuilder();

        if (isCreateForIndiv) {
            sbDroitHasJeton.append("INSERT INTO sigs_droits_individu_has_nv_jeton VALUES ");
        }

        listeDroitsIndividusActifsForCreation.stream().forEach(object -> {
            sbJeton.append("(");
            sbDroitHasJeton.append("(");

            BigInteger idDroit = (BigInteger) object[0];

            String jetonGenerated = IdJetonGenerator.codeGenerator(idDroit.toString(), DateUtils.now());
            sbJeton.append("'").append(jetonGenerated).append("', ");

            appendDate(sbJeton, object[1]);
            appendDate(sbJeton, object[2]);
            sbJeton.append(0).append(", ");
            sbJeton.append(0).append(", ");
            sbJeton.append("'").append(dateDB).append("', ");
            sbJeton.append("'").append(user).append("'");

            sbDroitHasJeton.append(idDroit).append(",'").append(jetonGenerated).append("'");

            sbJeton.append("),");
            sbDroitHasJeton.append("),");
        });


        String requestJeton = sbJeton.toString();
        sbJeton.delete(33, sbJeton.length());
        requestJeton = requestJeton.substring(0, requestJeton.length() - 1);
        jpaApi.em().createNativeQuery(requestJeton).executeUpdate();

        String requestDroitHasJeton = sbDroitHasJeton.toString();
        sbDroitHasJeton.delete(54, sbDroitHasJeton.length());
        requestDroitHasJeton = requestDroitHasJeton.substring(0, requestDroitHasJeton.length() - 1);
        **jpaApi.em().createNativeQuery(requestDroitHasJeton).executeUpdate();**


        **jpaApi.em().flush();
        jpaApi.em().clear();**
    }

当我分析 Heap Dump 时,我注意到尽管进行了刷新和清除,但 SessionFactory 中仍然引用了查询,这是否正常? enter image description here

【问题讨论】:

    标签: java jpa heap-memory nativequery


    【解决方案1】:

    这是我的新实现:

        Statement statement = null;
        ResultSet resultSet = null;
        int nbRows = 0;
    
        try {
            statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
                    ResultSet.CONCUR_READ_ONLY);
            try {
                resultSet = statement.executeQuery(queryGetDroitsEntreprisesActifsForCreation(isGlobal, entreprises, services));
                resultSet.beforeFirst();
                while (resultSet.next()) {
                    nbRows += 1;
                    queryInsertJetonsAndLinkDroitsJeton(isCreateForEntreprise, isCreateForIndiv, connection, resultSet,
                            user, dateDB);
                }
                System.out.println(nbRows);
            } finally {
                if (resultSet != null) {
                    resultSet.close();
                }
            }
        } finally {
            if (statement != null) {
                statement.close();
            }
        }
    

    方法“queryGetDroitsEntreprisesActifsForCreation”以字符串形式返回 SQL 查询。 “queryInsertJetonsAndLinkDroitsJeton”方法使用了著名的 PreparedStatement:

    PreparedStatement psJeton = null;
        PreparedStatement psDroitJeton = null;
        try {
            String sbJeton = "INSERT INTO TABLE1 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ";
    
            psJeton = connection.prepareStatement(sbJeton);
    
            String jetonGenerated;
            if (isCreateForEntreprise) {
                jetonGenerated = IdJetonGenerator.codeGenerator(String.valueOf(resultSet.getInt("id_e")), DateUtils.now());
            } else {
                jetonGenerated = IdJetonGenerator.codeGenerator(String.valueOf(resultSet.getInt("id_i")), DateUtils.now());
            }
            psJeton.setString(1, jetonGenerated);
            psJeton.setString(2, ContextType.GD.name());
            psJeton.setString(3, JetonType.ANONYME.name());
            psJeton.setInt(4, 1);
            psJeton.setTimestamp(5, resultSet.getTimestamp("dt_debut"));
            psJeton.setTimestamp(6, resultSet.getTimestamp("dt_fin"));
            psJeton.setInt(7, 0);
            psJeton.setInt(8, 0);
            psJeton.setInt(9, 0);
            psJeton.setInt(10, 0);
            psJeton.setTimestamp(11, dateDB);
            psJeton.setString(12, user);
            psJeton.setTimestamp(13, dateDB);
            psJeton.setString(14, user);
            psJeton.executeUpdate();
    
    
            String sbDroitHasJeton = null;
            if (isCreateForEntreprise) {
                sbDroitHasJeton = "INSERT INTO sigs_dej VALUES (?, ?)";
            }
            if (isCreateForIndiv) {
                sbDroitHasJeton = "INSERT INTO sigs_dij VALUES (?, ?)";
            }
    
            psDroitJeton = connection.prepareStatement(sbDroitHasJeton);
            if(isCreateForEntreprise) {
                psDroitJeton.setInt(1, resultSet.getInt("id_e"));
            } else {
                psDroitJeton.setInt(1, resultSet.getInt("id_i"));
            }
    
            psDroitJeton.setString(2, jetonGenerated);
            psDroitJeton.executeUpdate();
    
    
        } catch (SQLException e) {
            e.printStackTrace();
    
        } finally {
            if (psJeton != null) {
                psJeton.close();
            }
            if (psDroitJeton != null) {
                psDroitJeton.close();
            }
    
        }
    

    我希望这是 PreparedStatement & Scrollable ResultSet 的最佳实现

    【讨论】:

      【解决方案2】:

      flush 正在执行在工作单元中所做的更改,而 clear 则从持久性上下文中删除实体。

      两者都与原生 SQL 查询无关。我假设本机查询缓存在其他任何地方(Hibernate、JDBC...)

      我建议您使用准备好的语句而不是动态插入语句。

      【讨论】:

      • 感谢您的回复。我无法理解的是,在插入查询的堆转储中显示查询在 SessionFactory ==> Hibernation Level 1 Cache 中得到了很好的引用?尽管每个事务都使用接近每个调用结束的实例 EM。您建议我通过准备好的语句使用 JDBC 吗?
      • 不,我的建议是使用带有占位符 (?) 的插入语句。像这样 Hibernate 会创建一个准备好的语句,并且该语句只存在一次。
      • 感谢您的解释。我借此机会使用 Scrollable ResultSet 和preparedStatement 放置我的新实现
      猜你喜欢
      • 2019-04-15
      • 2020-02-19
      • 2020-09-05
      • 2017-04-21
      • 2012-10-18
      • 1970-01-01
      • 2018-09-05
      • 2018-08-03
      • 1970-01-01
      相关资源
      最近更新 更多