Здесь есть несколько проблем, которые играют роль.
Первая причина NPE:
java.lang.NullPointerException
at org.tranql.connector.jdbc.ConnectionHandle.connectionError(ConnectionHandle.java:103)
Это явно ошибка в драйвере TranQL JDBC. Во время метода ConnectionHandle#connectionError()
произошел неожиданный нулевой указатель, поэтому он не смог обработать фактическую ошибку соединения. Чтобы исправить эту конкретную проблему (чтобы у вас не было NPE, но есть более понятный и специфичный для драйвера SQLException
), либо обновите драйвер, либо замените его более подходящим (я никогда раньше не слышал о TranQL) ).
Что касается истинной причины всей проблемы; Судя по трассировке стека, похоже, что нет активного соединения. Судя по не поточнобезопасному коду, похоже, что подготовленное состояние уже использовалось ранее и каким-то образом неявно было закрыто / отсоединено от соединения.
По крайней мере, он явно сводится к тому, что у вас есть не-потокобезопасный и небезопасный код JDBC. Обычная идиома JDBC заключается в том, что вы всегда должны получить и , чтобы закрыть Connection
, PreparedStatement
и ResultSet
в кратчайшей области действия . Таким образом, уже внутри того же (нестатического!) Метода блока. Утверждение и набор результатов никогда не должны быть разделены между потоками. Связь может быть, но вы не должны брать ее в свои руки. Используйте для этого приличный API пула соединений, например, C3P0.
Вот пример идеальной идиомы JDBC:
public String recoverPassword(Long userId, String answer) throws SQLException {
Connection connection = null;
PreparedStatement verifyAnswer = null;
ResultSet resultSet = null;
String newPassword = null;
PreparedStatement resetPassword = null;
try {
connection = database.getConnection();
verifyAnswer = connection.prepareStatement(SQL_VERIFY_ANSWER);
verifyAnswer.setLong(1, userId);
verifyAnswer.setString(2, answer);
resultSet = statement.executeQuery();
if (resultSet.next()) {
SecureRandom random = new SecureRandom();
newPassword = new BigInteger(130, random).toString(32);
resetPassword = connection.prepareStatement(SQL_RESET_PASSWORD);
resetPassword.setString(1, newPassword);
resetPassword.setLong(2, userId);
resetPassword.executeUpdate();
}
} finally {
// Always free resources in reversed order.
if (resetPassword != null) try { resetPassword.close(); } catch (SQLException logOrIgnore) {}
if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
if (verifyAnswer != null) try { verifyAnswer.close(); } catch (SQLException logOrIgnore) {}
if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
}
return newPassword;
}
Обратите внимание, что я намекнул userId
на Long
. Вы действительно не хотите, чтобы в вашем коде и модели данных были String
идентификаторы. Вы можете найти более подробную информацию, советы и другие примеры написания твердого кода JDBC здесь .
Тем не менее, следующая часть стека трассировки
at com.ibm.lims.LimsHandler.recoverpassword(LimsHandler.java:940)
at org.apache.jsp.recoverpasswordresult_jsp._jspService(recoverpasswordresult_jsp.java:78)
подразумевает, что вы пишете необработанный код Java в файле JSP с использованием старомодных скриптлетов. Чтобы избежать будущих проблем с обслуживанием и отладкой, я настоятельно рекомендую вам не делать этого, а просто использовать для этого класс Servlet.