Ранее мы рассматривали загрузку файлов в web приложении на основе Spring MVC с использованием форм. Но, на механизме загрузки процесс на самом деле не заканчивается. Необходимо обеспечить корректность его работы. Чтобы было соответствие между файлами и хранящимися на сервере и записями в базе данных, которые содержат информацию о них.
Согласитесь, что если на сервере будет храниться файл, так или иначе связанный с бизнес логикой приложения, о котором оно «ничего не знает» или когда в базе данных есть записи о файле, но самого файла на жёстком диске физически нет, это неправильно. И чтобы предотвратить возникновение подобных ситуации, мы усовершенствуем рассмотренный ранее механизм загрузки при помощи паттерна «Сценарий транзакции».
В теоретические подробности вдаваться не будем, так как они достаточно хорошо описаны в литературе [1], а сразу приступим к практике.
Сразу отметим, что вследствие разнообразия возможных ситуации реализация данного паттерна для того или иного конкретного случая может сильно отличаться, но принцип, всегда один и тот же.
За основу мы возьмём пример, который рассматривался нами в статье «Работа с формами в Spring MVC». Напомним, что представлял собой метод контроллера, который обрабатывал данные формы при загрузке файлов:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
protected String addManPost(MansForm mansForm) throws UnsupportedEncodingException { String manName = man.setName(mansForm.getName()); if (mansForm.getAvatar() != null) { MultipartFile file = mansForm.getAvatar(); try { String fileName = basePath + file.getOriginalFilename(); file.transferTo(new File(fileName)); } catch (IOException ex) { Logger.getLogger(mansController.class.getName()).log(Level.SEVERE, null, ex); } } return "redirect:/addman"; } |
В этом методе уже заложена основа для реализации паттерна «Сценарий транзакции», так как блок try-catch позволяет отследить факт успешного сохранения файла. Но для того, чтобы реализовать его полностью нам требуется контролировать не только сохранение файла, но и соответствующей записи в базе данных.
Вначале объединим оба процесса в одном методе. Для разнообразия «аватар» и предыдущего примера переименуем в «фотографию».
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
protected String addManPost(MansForm mansForm) throws UnsupportedEncodingException { Mans man = new Mans(); man.setName(mansForm.getName()); if (mansForm.getPhoto() != null) { MultipartFile file = mansForm.getPhoto(); try { // Сохранение файла String fileName = basePath + file.getOriginalFilename(); file.transferTo(new File(fileName)); man.setPhoto(fileName); // Сохранение записи ва БД mansDao.add(man); } catch (IOException ex) { Logger.getLogger(indexController.class.getName()).log(Level.SEVERE, null, ex); } } return "redirect:/addman"; } |
Подобная конструкция позволяет обеспечить корректность процесса сохранения в целом, но не для отдельных его этапов.
Добавим проверку существования файла фотографии при помощи стандартного класса File.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
protected String addManPost(MansForm mansForm/ throws UnsupportedEncodingException { Mans man = new Mans(); man.setName(mansForm.getName()); if (mansForm.getPhoto() != null) { MultipartFile file = mansForm.getPhoto(); try { // Сохранение файла String fileName = basePath + file.getOriginalFilename(); file.transferTo(new File(fileName)); man.setPhoto(fileName); // Сохранение записи ва БД File photoFile = new File(fileName); if (photoFile.exists()) { mansDao.add(man); } } catch (IOException ex) { Logger.getLogger(indexController.class.getName()).log(Level.SEVERE, null, ex); } } return "redirect:/addman"; } |
Теперь запись в будет сохраняться в базе данных только если файл фотографии успешно загружен. Но, это только половина задачи, так как подобная проверка не препятствует наличию файлов, записи о которых по различным причинам в базе данных не сохранились.
Для устранения этого недостатка введём проверку на сохранение в базе данных при помощи блока try-catch и в случае возникновения исключения при сохранении записи будем удалять файл.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
protected String addManPost(MansForm mansForm) throws UnsupportedEncodingException { Mans man = new Mans(); man.setName(mansForm.getName()); if (mansForm.getPhoto() != null) { MultipartFile file = mansForm.getPhoto(); try { // Сохранение файла String fileName = basePath + file.getOriginalFilename(); file.transferTo(new File(fileName)); man.setPhoto(fileName); // Сохранение записи ва БД File photoFile = new File(fileName); if (photoFile.exists()) { try { mansDao.add(man); } catch (Exception ex) { Logger.getLogger(indexController.class.getName()).log(Level.SEVERE, null, ex); photoFile.delete(); } } } catch (IOException ex) { Logger.getLogger(indexController.class.getName()).log(Level.SEVERE, null, ex); } } return "redirect:/addman"; } |
Таким образом мы добились того, что при сохранении записи в базе данных обязательно будет сохраняться связанный с ней файл и наоборот. Тем самым используя паттерн «Сценарий транзакции» мы обеспечили корректность работы приложения.
Добавить комментарий