Menu

Целостность данных форм с загрузкой файлов

2018-03-26
2018-04-05
  • Roman Zakharov

    Roman Zakharov - 2018-03-26

    Исходные данные:
    Форма (создания или редактирования) сущности с полями загрузки файлов.

    Системная часть спроектирована таким образом, что вначале происходить создание записи в БД, затем отдельный севрлет загружает файл в таблицу по созданному первичному ключу.

    Проблема:
    Если во время загрузки файла произошла ошибка, то запись будет создана, но файла не будет. В лучшем случае пользователь сможет дополнить запись, но если такая возможность не заложена в ПО, то остается битая запись с нарушенной целостностью данных.

    Возможные решения:
    1. После успешного сохранения данных и загрузки файлов подтверждать сущность (с помощью флага или статуса). Логика формы разбивается на следующие части: создание сущности, загрузка файлов, подтверждение сохранения.
    Минусы:
    На прикладному уровне необходима сборка мусора в случае, когда во время загрузки файлов произошла ошибка:
    - либо сразу удалять созданные данные (сущность и успешно загруженные файлы).
    - либо по расписанию удалять неподтвержденные записи.
    Изменения в системной части не требуются.
    2. Создавать связанные данные в одной транзакции, а именно загружать файлы вместе с данными:
    2.1 Передавать CLOB/BLOB, как параметры в DB-функции. В этом решении, вероятно, будет проблемы с производительностью. Требуется доп. анализ с замерами.
    2.2 Совместить реализацию DAO и JepUploadServlet севрлета. Требуется доп. анализ.

     

    Last edit: Roman Zakharov 2018-03-26
  • Alexander Lapygin

    Думаю, нужны 2 решения - для быстрой загрузки и для долгой загрузки.
    Для быстрой - делать в одной транзакции.
    Для долгой - сложное решение с откатами типа п.1.

     
    • Roman Zakharov

      Roman Zakharov - 2018-03-26

      Для быстрой, 2.1 или 2.2?

       
  • ViTr

    ViTr - 2018-03-26

    Поддержу Сашино предложение: возможно имеет смысл иметь две возможности, причем возможно для этого потребуется определить некоторую верхнюю границу (threshold), превышая которую будет целесообразнее разделять создание записи БД с загрузкой в LOB-поле. Например для JDBC метода setBinaryOutputstream или setBLOB не думаю, что будет высокой нагрузка, например, файлов размером до 2-5 мб. А вот если размер выше, то однозначно надо разделять загрузку. Исходя из этих соображений, прикладной разработчик будет иметь возможность выбора необходимого поведения.
    P. S. Вместе с этим тогда следует сделать JepUploadServlet асинхронным, используя setWriteListener.

     

    Last edit: ViTr 2018-03-26
  • Romanov

    Romanov - 2018-03-26

    Думаю, нужно загружать по принципу "чем больше -- тем лучше", зачем вообще удалять данные? У нас обычно клобы/блобы -- это второстепенная информация, без которой сама запись со своими полями имеет право существовать.

    а) если запись создалась, но не загрузилось фото -- пусть остаётся запись без фото (в интерфейсе это будет видно на форме просмотра, котрая обычно всплывает после сохранения + ошибка "фото не загрузилось").

    б) Аналогично, если запись создалась, и успешно загрузилось 1 из 2 фото -- пусть остаётся запись с одним фото (в интерфейсе это будет видно на форме просмотра, котрая обычно всплывает после сохранения + ошибка "не все фото загрузились").

    Если блоб совсем намертво привязан к записи, и последняя не имеет смысла без него, то поддерживаю создание транзакций.

     

    Last edit: Romanov 2018-03-26
    • Roman Zakharov

      Roman Zakharov - 2018-03-26

      Проблема актуальна для обоих случаев. Если пользователь хотел добавить файлы, но оказался на форме просмотра, то получается неудобно. У него должна быть возможность повторно сохранить свои файл или убрать его с формы (если он необязательный) и сохранить форму еще раз.

       
  • Alexander Lapygin

    Я думаю, что в случае быстрой загрузки управление транзакцией с уровня DAO нужно перенести в JepDataServiceServlet.update/create. Сделать что-то типа:

    @Override
    public JepRecord update(FindConfig updateConfig) throws ApplicationException {
    ...

    START TRANSACTION
    dao.update(record, getOperatorId());
    updateLobFields(record);
    COMMIT TRANSACTION
    
    ...
    
    logger.trace("END update(" + resultRecord + ")");
    return resultRecord;
    

    }

     
  • ViTr

    ViTr - 2018-03-26

    Саш, этот код как раз не поможет..загрузка файла происходит в отдельном потоке и передается в класс-сервлет. Имеет смысл там ее открывать, а закрывать в UploadServlet

     
  • ViTr

    ViTr - 2018-03-26

    Напрашивается сделать сквозную функциональность TransactionManager с возможностью декларативным определением точек начал и концовки транзакции. Сейчас дело омрачает создание новой транзакции в классе BinaryFileUploadImpl

     
  • Roman Zakharov

    Roman Zakharov - 2018-03-26

    Попробую немного подытожить:
    1. Потребуется доработка JepFileField, чтобы файлы и основные данные формы были в одном запросе для обработки в одной транзакции. Для небольших файлов (3-5 МБ) можно воспользоваться 2.1, для больших 2.2;
    2. Потребуется доработка UploadServlert для поддрежания асинхронной загрузки (setWriteListener).

     
  • Roman Zakharov

    Roman Zakharov - 2018-04-05

    Решать проблему с помощью развития менеджера управления транзакциями, открывать и заканчивать транзакцию в нужных местах бизнес-логики. Рефакторинг классов TextFileUploadImpl и BinaryFileUploadImpl.

     

    Last edit: Roman Zakharov 2018-04-05

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.