Java 堆 space 与 jpa 本机查询
Java Heap space with jpa native Query
我在尝试通过 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 最小值和最大值。随后,我使用代码如下的创建方法:
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
flush 正在执行工作单元中所做的更改,clear 从持久性上下文中删除实体。
两者都与本机 SQL 查询无关。我假设本机查询缓存在其他任何地方(Hibernate,JDBC...)
我建议您使用准备好的语句而不是动态插入语句。
这是我的新实现:
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" returns 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 的最佳实现
我在尝试通过 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 最小值和最大值。随后,我使用代码如下的创建方法:
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
flush 正在执行工作单元中所做的更改,clear 从持久性上下文中删除实体。
两者都与本机 SQL 查询无关。我假设本机查询缓存在其他任何地方(Hibernate,JDBC...)
我建议您使用准备好的语句而不是动态插入语句。
这是我的新实现:
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" returns 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 的最佳实现