Java - найти первую причину исключения
Мне нужно проверить, вызвано ли исключение какой-либо проблемой базы данных. Я получаю исключение и проверяю, содержит ли его причина строку "ORA", и возвращаю ее (что-то вроде "ORA-00001"). Проблема здесь в том, что полученное мной исключение вложено в другие исключения, поэтому, если я не выясню, является ли это исключением оракула, я должен выяснить причину этого исключения и так далее. Есть ли более чистый способ сделать это? Есть ли способ узнать первую причину (глубокое исключение) данного исключения?
Мой текущий код выглядит так:
private String getErrorOracle(Throwable e){
final String ORACLE = "ORA";
if (e.getCause() != null && e.getCause().toString().contains(ORACLE)){
return e.getCause().toString();
} else if(e.getCause() != null){
return getErrorOracle(e.getCause());
} else {
return null;
}
}
13 ответов
Просто обходите цепочку исключений, пока не получите исключение без причины, а затем просто верните это сообщение, если хотите последнее.
Ваша функция получит только первую причину, если она есть.
Возможно, вы захотите найти первую причину в своем пакете, хотя самой глубокой причиной может быть исключение оракула, что полезно, но если вы не видите, где вы создали проблему, вам будет трудно ее устранить.
Чтобы не изобретать велосипед, если вы используете Apache Commons Lang, посмотрите на ExceptionUtils.getRootCause ().
Стоит ли включать библиотеку только для этого? Возможно, нет. Но если у вас уже есть его в вашем classpath, он для вас, и обратите внимание, что он делает некоторые вещи, которые "наивная" реализация может не делать (например, иметь дело с циклами в цепочке причин... тьфу!)
Если вы уже находитесь в Guava, то Throwables.getRootCause() придет на помощь.
Вероятно, немного излишним для вашего использования, но я думаю, что это чище (и многоразового использования)
interface ThrowablePredicate {
boolean accept(Throwable t);
}
public OracleErrorThrowablePredicate implements ThrowablePredicate {
private static final ORA_ERR = "ORA";
public boolean accept(Throwable t) {
return t.toString().contains(ORA_ERR);
}
}
public class CauseFinder {
private ThrowablePredicate predicate;
public CauseFinder(ThrowablePredicate predicate) {
this.predicate = predicate;
}
Throwable findCause(Throwable t) {
Throwable cause = t.getCause();
return cause == null ? null
: predicate.accept(cause) ? cause : findCause(cause)
}
}
// Your method
private String getErrorOracle(Throwable e){
return new CauseFinder(new OracleErrorThrowablePredicate()).findCause(e);
}
Я думаю, что любая ошибка, выдаваемая оракулом, будет заключена в исключение SQLException (кто-нибудь, пожалуйста, исправьте меня, если ошибаюсь). Как только вы получите доступ к SQLException, вы сможете позвонить
getErrorCode() Получает специфический для поставщика код исключения для этого объекта SQLException.
Дайте мне знать, если это работает, как я никогда не пробовал:-)
Карл
В моем проекте Spring Boot для
getRootCause
IDEA предлагает 3 статических импорта:
Ядро пружины:
org.springframework.core.NestedExceptionUtils.getRootCause
Джексон:
com.fasterxml.jackson.databind.util.ClassUtil.getRootCause
Гуава (переходный Swagger)
com.google.common.base.Throwables.getRootCause
Самый умный (с проверкой цикла) - Spring
NestedExceptionUtils.getRootCause
. Но, если ваше исключение не имеет причины, метод возвращает
null
. В моем случае это неправильно, поэтому я сделал:
@NonNull
public static Throwable getRootCause(@NonNull Throwable t) {
Throwable rootCause = NestedExceptionUtils.getRootCause(t);
return rootCause != null ? rootCause : t;
}
Вы можете улучшить проверку кода для SQLException
import java.sql.SQLException;
private static final String ORACLE = "ORA";
public String doHandle(Throwable t) {
if (t.getClass().isAssignableFrom(SQLException.class)) {
SQLException e = (SQLException) t;
int errCode = e.getErrorCode();
String state = e.getSQLState();
String msg = e.getMessage();
if (msg.contains(ORACLE)) {
return msg;
}
} else {
if (t.getCause() != null) {
return this.doHandle(t.getCause());
}
}
return "";
}
Кроме того, я думаю, что в Oracle "errCode" содержит номер, связанный с ORA-nnnn
ExceptionUtils.getRootCause() и Throwables.getRootCause() возвращают null, если причина передаваемого исключения равна null. Следующий метод вернет исходный объект throwable, если в качестве входного параметра передается объект throwable без причины.
/**
* @return the root cause of a given throwable.
* If throwable without a cause is being passed, the original throwable will be returned
*/
public static Throwable getRootCause(@NonNull final Throwable throwable) {
List<Throwable> throwableList = ExceptionUtils.getThrowableList(throwable);
return throwableList.get(throwableList.size() - 1);
}
Однострочное решение с использованием ядра Java API:
try {
i = 1 / 0;
} catch (ArithmeticException e) {
System.out.println(new ArithmeticException().initCause(e).getCause());
}
Еще одно решение ниже также работает:
try {
i = 1 / 0;
} catch (ArithmeticException e) {
System.out.println(new Exception().initCause(e).getCause());
}
Оба они напечатают
java.lang.ArithmeticException: / по нулю
Вы можете использовать getStackTrace() из класса Throwable. Это даст вам стек StackTraceElements для работы. Вы можете перебрать StackTraceElements[], чтобы найти строку "ORA".
Дайте мне знать, если вам нужен пример.
Я хочу добавить функции расширения Kotlin, чтобы выяснить основные причины:
fun Throwable.rootCause(): Throwable {
return if (cause == null) this else cause!!.rootCause()
}
//Return null if first cause is null
fun Throwable.optRootCause(): Throwable? {
return if (cause == null) null else cause!!.rootCause()
}
28-01-2015 я не смог решить свою проблему ни с одним из вышеуказанных решений, поэтому я рекомендую использовать:
e.getMessage().toString();
PS: я использую его на Android.
Если выбрасываемое исключение всегда будет определенного типа, например OracleException, вы можете перехватить только это исключение.
Например:
try {
...
} catch(OracleException oe) {
...
}
Это применимо только в том случае, если создаются конкретные исключения Oracle. Я не очень разбираюсь в Oracle, поэтому, прежде чем пытаться это сделать, вы, вероятно, захотите узнать, происходит ли это.