Велосипед для извлечения данных

Извлечение данных


Каждому кто занимается промышленной разработкой кровавым энтерпрайзом не раз приходилось сталкиваться с написанием слоя работы с базой данных. С этим столкнулись и мы.


Наш проект построен на финском фреймворке 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 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. Хотелось бы получить оценку решения велосипеда и конструктивных предложений и замечаний.

Комментарии (0)

© Habrahabr.ru