Каждому кто занимается промышленной разработкой кровавым энтерпрайзом не раз приходилось сталкиваться с написанием слоя работы с базой данных. С этим столкнулись и мы.
Наш проект построен на финском фреймворке Vaadin и чистым JDBC в основе слоя работы с базой данных. Без опыта работы с JDBC мы нагородили достаточно большой слой спагетти кода, а потом доблестно с ним разобрались.
О том как мы с этим боролись и какой велосипед изобрели под катом.
Что привело к такому решению
В Vaadin отображать данные в UI компоненты можно с помощью BeanItem Container
, можно почитать здесь.
Коротко о предметной области
Некоторое количество сущностей, которые показываются пользователю и пользователь должен их редактировать: удалять, изменять, добавлять.
Сущности описывались с помощью спецификации Bean
:
public class Element implements Serializable{ private Integer id = 0; private String name = ""; private Float price = 0.0F; // getter and setter for property }
Контейнер для такого элемента создается просто:
BeanItemContainer<Element> container = new BeanItemContainer<>(Element.class);
И дальше этот контейнер подставляется в UI с помощью методов setContainerDataSource(...)
.
Чтобы получить этот контейнер, его нужно предварительно разобрать на экземпляры класса Element
из ResultSet
полученного с помощью запроса к базе данных.
Изначально решение получилось такого вида:
public Element(ResutlSet rs){ try { id = rs.getInt("id"); }catch(SqlException e){ id = 0; e.printStackTrace(); } try { name = rs.getString("name"); }catch(SqlException e){ name = ""; e.printStackTrace(); } try { price = rs.getFloat("price"); }catch(SqlException e){ price = 0.0f; e.printStackTrace(); } }
И тут на меня должны быть направлены гневные взгляды гуру Java и карма полететь в минусы.
Но концепция такая: если во время разбора данных из ResultSet
поле вызвало ошибку, то система не должна вывалиться с Exception
и продолжить работать и записать логи ошибок (запись логов здесь и дальше описывать не будет тема для отдельной статьи).
Но признаюсь такой код ужасный, а после прочтения небезызвестной книги дядюшки Боба, появилось непреодолимое желание исправить этот код.
В результате написали библиотеку, которая берет на себя получение данных из ResultSet
.
Реализация
Используя паттерн Декоратор создали маленькую библиотеку Executor.
Эта библиотека расширяет функционал ResultSet
, добавляя безопасные методы для получения данных.
public Integer getInt(String columnName) { Integer i = 0; try { i = rs.getInt(columnName); } catch (SQLException ignored) { } return i; } public Integer getIntNull(String columnName) { Integer i = 0; try { i = rs.getInt(columnName); if (rs.wasNull()) return null; } catch (SQLException ignored) { } return i; } public String getString(String columnName) { String s = ""; try { s = rs.getString(columnName); if (s == null) return ""; } catch (SQLException ignored) { } return s; }
Таким образом 100% гарантия получения результата и работа без Exception
при обработке данных, которые в Vaadin выглядят чуть-чуть пугающе.
На вопрос, а если нужен Exception
, ответ: в планах добавить конструктор для Executor
с типом вызываемого Exception
, потому что простой SqlExecption
не информативен, и реализовать методы для корректной работы.
Планируется API следующего вида:
public Executor(ResultSet rs, Class exceptionClass){ this.rs = rs; this.exceptionClass = exceptionClass; } public Integer getIntThrow(String columnName) { Integer i = 0; try { i = rs.getInt(columnName); if (rs.wasNull()) return null; } catch (SQLException ex) { throw (RuntimeExceptoion) exceptionClass.newInstance(); } return i; }
И вариант использования
public Element(ResutlSet rs){ Executor ex = new Executor(rs, CreateElementException.class); id = ex.getIntThrow("id"); name = ex.getStringThrow("name"); price = ex.getFloatThrow("price"); } private class CreateElementException extends RuntimeException{ private String message = ""; public CreateElementException(String message){ this.message = message; } @Override public String getMessage(){ return "Exception with access to column with name " + this.message; } }
Примеры использования
Что получается с кодом, после использования данной библиотеки. Конструктор для класса Element
изменился на следующий:
public Element(ResutlSet rs){ Executor ex = new Executor(rs); id = ex.getInt("id"); name = ex.getString("name"); price = ex.getFloat("price"); }
В результате:
- кода стало меньше.
- уменьшение объемов кодовой базы, при наличие большого количества классов описывающих сущности.
- код становится понятнее для восприятия и места для возникновения ошибок становится меньше.
- проще писать тесты.
- появление дополнительной библиотеки в зависимостях.
Заключение
Сделан еще один велосипед велосипеды нужно тоже уметь делать, который обеспечивает универсальный и безопасный доступ к получению данных из БД. Люди желающие воспользоваться библиотекой исходники добро пожаловать на GitHub. Хотелось бы получить оценку решения велосипеда и конструктивных предложений и замечаний.
ссылка на оригинал статьи https://habrahabr.ru/post/318740/
Добавить комментарий