为什么@Transactional 不回滚在此集成测试中保存实体?
Why is @Transactional not rolling back saving an entity in this integration test?
我有一个控制器可以接收一些表单数据,并且应该
- 将一些实体保存到数据库中
- 将提交的所有图像保存到文件系统
控制器用 @Transactional
注释(虽然我读过在控制器级别上使用这个注释不是一个好主意......)用 rollbackFor = Exception.class
,因为如果发生任何异常,我想回滚对任何实体所做的更改。
当我 运行 测试并检查我期望消失的实体是否存在时,它仍然存在。所以,@Transactional
似乎没有像我预期的那样工作。
ClassifiedController.java,在src/main/java/com/example/controllers:
package com.example.controllers;
import com.example.services.DefaultImageManipulationService;
import com.example.services.ImageManipulationService;
import com.example.entities.Classified;
import com.example.entities.Place;
import com.example.inbound.ClassifiedFormData;
import com.example.repositories.ClassifiedRepository;
import com.example.repositories.PlaceRepository;
import com.example.services.StorageService;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.awt.*;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class ClassifiedController {
private final ClassifiedRepository classifiedRepository;
private final PlaceRepository placeRepository;
private final StorageService storageService;
private final ImageManipulationService imageManipulationService;
public ClassifiedController(ClassifiedRepository classifiedRepository,
PlaceRepository placeRepository,
StorageService storageService,
DefaultImageManipulationService imageManipulationService) {
this.classifiedRepository = classifiedRepository;
this.placeRepository = placeRepository;
this.storageService = storageService;
this.imageManipulationService = imageManipulationService;
}
@Transactional(rollbackFor = Exception.class)
@PostMapping(path = "/classifieds", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public void addClassified(@RequestPart(name="data") ClassifiedFormData classifiedFormData,
@RequestPart(name="images") MultipartFile[] images) {
/* The end goal here is to get a classified and a place into the DB.
If anything goes wrong, the transaction should be rolled back, and any saved images and thumbnails
should be deleted. */
List<String> filePaths = null;
Path pathToImagesForThisClassified = null;
String thumbnailPath = null;
Path pathToThumbnailsForThisClassified = null;
try {
Classified classified = new Classified();
classified.setSummary(classifiedFormData.getSummary());
classified.setDescription(classifiedFormData.getDescription());
classified.setPrice(classifiedFormData.getPrice());
classified.setCurrency(classifiedFormData.getCurrency());
classifiedRepository.save(classified);
if (true) {
throw new Exception("The saved Classified should be deleted because of the @Transactional annotation");
}
String idAsStr = String.valueOf(classified.getId());
pathToImagesForThisClassified = Paths.get("images", idAsStr);
filePaths = storageService.storeAll(pathToImagesForThisClassified, images);
File thumbnail = imageManipulationService.resize(filePaths.get(classifiedFormData.getThumbnailIndex()),
new Dimension(255, 255));
pathToThumbnailsForThisClassified = Paths.get("thumbnails", idAsStr);
thumbnailPath = storageService.store(pathToThumbnailsForThisClassified, thumbnail);
classified.setImagePaths(filePaths);
classified.setThumbnailImagePath(thumbnailPath);
classifiedRepository.save(classified);
Place place = new Place(classified);
place.setCountry(classifiedFormData.getCountry());
place.setLabel(classifiedFormData.getLabel());
place.setLatitude(Double.valueOf(classifiedFormData.getLat()));
place.setLongitude(Double.valueOf(classifiedFormData.getLon()));
placeRepository.save(place);
} catch (Exception e) {
e.printStackTrace();
storageService.deleteRecursively(pathToImagesForThisClassified);
storageService.deleteRecursively(pathToThumbnailsForThisClassified);
}
}
}
ClassifiedControllerTest.java 在 src/test/java/com/example/controllers:
package com.example.controllers;
import com.example.entities.Classified;
import com.example.entities.Place;
import com.example.inbound.ClassifiedFormData;
import com.example.repositories.ClassifiedRepository;
import com.example.repositories.PlaceRepository;
import com.example.services.StorageService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.TestConstructor;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@DisplayName("ClassifiedController")
public class ClassifiedControllerTest {
private final MockMvc mvc;
private final ClassifiedRepository classifiedRepository;
private final PlaceRepository placeRepository;
private final StorageService storageService;
public ClassifiedControllerTest(MockMvc mvc, ClassifiedRepository classifiedRepository,
PlaceRepository placeRepository, StorageService storageService) {
this.mvc = mvc;
this.classifiedRepository = classifiedRepository;
this.placeRepository = placeRepository;
this.storageService = storageService;
}
@DisplayName("Any saved entities and files are deleted if an exception is encountered")
@Test
public void givenInvalidFormData_whenPosted_thenStatus400AndClean() throws Exception {
// GIVEN
ClassifiedFormData classifiedFormData = new ClassifiedFormData();
classifiedFormData.setCountry("Spain");
classifiedFormData.setCurrency("EUR");
classifiedFormData.setSummary("Test");
classifiedFormData.setDescription("Test");
classifiedFormData.setLabel("Test");
classifiedFormData.setPrice(32.45);
classifiedFormData.setThumbnailIndex((byte)1);
classifiedFormData.setLat("42.688630");
classifiedFormData.setLon("-2.945620");
MockMultipartFile classified = new MockMultipartFile("data", "", "application/json",
("{\"summary\":\"feefwfewfew\",\"description\":\"fewfewfewfewfwe\",\"price\":\"34\"," +
"\"currency\":\"CAD\",\"thumbnailIndex\":0,\"lat\":\"52.2460367\",\"lon\":\"0.7125173\"," +
"\"label\":\"Bury St Edmunds, Suffolk, East of England, England, IP33 1BZ, United Kingdom\"," +
"\"country\":\"United Kingdom\"}").getBytes());
byte[] image1Bytes = getClass().getClassLoader().getResourceAsStream("test_image.jpg").readAllBytes();
byte[] image2Bytes = getClass().getClassLoader().getResourceAsStream("test_image.jpg").readAllBytes();
String image1Filename = "image1.jpg";
String image2Filename = "image2.jpg";
MockMultipartFile image1 =
new MockMultipartFile("images", image1Filename,"image/jpeg", image1Bytes);
MockMultipartFile image2 =
new MockMultipartFile("images", image2Filename, "image/jpeg", image2Bytes);
Path expectedImagePath = Paths.get("images", "5");
Path expectedThumbnailPath = Paths.get("thumbnails", "5");
// WHEN-THEN
mvc.perform(MockMvcRequestBuilders.multipart("/classifieds")
.file(classified)
.file(image1)
.file(image2)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
.andExpect(status().isOk());
Optional<Classified> classifiedOptional = classifiedRepository.findById((long)5);
assertFalse(classifiedOptional.isPresent()); // This is the assertion that is failing
Optional<Place> placeOptional = placeRepository.findByClassifiedId(5);
assertFalse(placeOptional.isPresent());
Resource image1AsResource = storageService.loadAsResource(expectedImagePath, image1Filename);
Resource image2AsResource = storageService.loadAsResource(expectedImagePath, image2Filename);
Resource thumbnailAsResource = storageService.loadAsResource(expectedThumbnailPath, "thumbnail.jpg");
assertFalse(image1AsResource.exists());
assertFalse(image2AsResource.exists());
assertFalse(thumbnailAsResource.exists());
}
}
测试结果:
java.lang.Exception: The saved Classified should be deleted because of the @Transactional annotation
at com.example.controllers.ClassifiedController.addClassified(ClassifiedController.java:67)
at com.example.controllers.ClassifiedController$$FastClassBySpringCGLIB$50f537.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
[some lines omitted for brevity]
expected: <false> but was: <true>
org.opentest4j.AssertionFailedError: expected: <false> but was: <true>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertFalse.assertFalse(AssertFalse.java:40)
at org.junit.jupiter.api.AssertFalse.assertFalse(AssertFalse.java:35)
at org.junit.jupiter.api.Assertions.assertFalse(Assertions.java:210)
at com.example.controllers.ClassifiedControllerTest.givenInvalidFormData_whenPosted_thenStatus400AndClean(ClassifiedControllerTest.java:148)
该方法从不抛出异常,因此 Spring 没有理由回滚事务。
如果它确实抛出异常(例如通过在 catch 块的末尾添加 throw new RuntimeException(e);
),那么 Spring 将回滚事务。
您正在 addClassified(@RequestPart(name="data") 方法的 catch 块中捕获冒泡异常。
你必须在catch块中抛出异常或者移除catch块,这样spring的拦截器才能知道抛出异常并回滚事务。
抛出的异常
if (true) {
throw new Exception("The saved Classified should be deleted because of the *@Transactional* annotation");
}
被抓住:
} catch (Exception e) {
e.printStackTrace();
// ...
}
并且不离开 addClassified 方法,即不会传播异常。因此,Spring不会做任何事情。
在高层次上,@transactional 注释将您的代码包装成如下形式:
UserTransaction utx = entityManager.getTransaction();
try {
utx.begin();
addClassified(); // your actual method invocation
utx.commit();
} catch (Exception ex) {
utx.rollback();
throw ex;
}
TL;DR:您可以删除 try-catch 或(重新)在您的 catch 块中抛出一个新异常。
@Actully 你不应该处理异常,因为 spring 不知道异常所以不能回滚
并且您有要求,如果发生异常,则必须删除文件。比你喜欢的要多。
public void addClassified(@RequestPart(name="data") ClassifiedFormData classifiedFormData, @RequestPart(name="images") MultipartFile[] images) {
// to delete file
boolean flag = true;
try {
Classified classified = new Classified();
classified.setSummary(classifiedFormData.getSummary());
classified.setDescription(classifiedFormData.getDescription());
classified.setPrice(classifiedFormData.getPrice());
classified.setCurrency(classifiedFormData.getCurrency());
classifiedRepository.save(classified);
if (true) {
throw new Exception("The saved Classified should be deleted because of the @Transactional annotation");
}
String idAsStr = String.valueOf(classified.getId());
pathToImagesForThisClassified = Paths.get("images", idAsStr);
filePaths = storageService.storeAll(pathToImagesForThisClassified, images);
File thumbnail = imageManipulationService.resize(filePaths.get(classifiedFormData.getThumbnailIndex()),
new Dimension(255, 255));
pathToThumbnailsForThisClassified = Paths.get("thumbnails", idAsStr);
thumbnailPath = storageService.store(pathToThumbnailsForThisClassified, thumbnail);
classified.setImagePaths(filePaths);
classified.setThumbnailImagePath(thumbnailPath);
classifiedRepository.save(classified);
Place place = new Place(classified);
place.setCountry(classifiedFormData.getCountry());
place.setLabel(classifiedFormData.getLabel());
place.setLatitude(Double.valueOf(classifiedFormData.getLat()));
place.setLongitude(Double.valueOf(classifiedFormData.getLon()));
placeRepository.save(place);
flag = false;
} finally {
if(flag){
storageService.deleteRecursively(pathToImagesForThisClassified);
storageService.deleteRecursively(pathToThumbnailsForThisClassified);
}
}
}
我有一个控制器可以接收一些表单数据,并且应该
- 将一些实体保存到数据库中
- 将提交的所有图像保存到文件系统
控制器用 @Transactional
注释(虽然我读过在控制器级别上使用这个注释不是一个好主意......)用 rollbackFor = Exception.class
,因为如果发生任何异常,我想回滚对任何实体所做的更改。
当我 运行 测试并检查我期望消失的实体是否存在时,它仍然存在。所以,@Transactional
似乎没有像我预期的那样工作。
ClassifiedController.java,在src/main/java/com/example/controllers:
package com.example.controllers;
import com.example.services.DefaultImageManipulationService;
import com.example.services.ImageManipulationService;
import com.example.entities.Classified;
import com.example.entities.Place;
import com.example.inbound.ClassifiedFormData;
import com.example.repositories.ClassifiedRepository;
import com.example.repositories.PlaceRepository;
import com.example.services.StorageService;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.awt.*;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class ClassifiedController {
private final ClassifiedRepository classifiedRepository;
private final PlaceRepository placeRepository;
private final StorageService storageService;
private final ImageManipulationService imageManipulationService;
public ClassifiedController(ClassifiedRepository classifiedRepository,
PlaceRepository placeRepository,
StorageService storageService,
DefaultImageManipulationService imageManipulationService) {
this.classifiedRepository = classifiedRepository;
this.placeRepository = placeRepository;
this.storageService = storageService;
this.imageManipulationService = imageManipulationService;
}
@Transactional(rollbackFor = Exception.class)
@PostMapping(path = "/classifieds", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public void addClassified(@RequestPart(name="data") ClassifiedFormData classifiedFormData,
@RequestPart(name="images") MultipartFile[] images) {
/* The end goal here is to get a classified and a place into the DB.
If anything goes wrong, the transaction should be rolled back, and any saved images and thumbnails
should be deleted. */
List<String> filePaths = null;
Path pathToImagesForThisClassified = null;
String thumbnailPath = null;
Path pathToThumbnailsForThisClassified = null;
try {
Classified classified = new Classified();
classified.setSummary(classifiedFormData.getSummary());
classified.setDescription(classifiedFormData.getDescription());
classified.setPrice(classifiedFormData.getPrice());
classified.setCurrency(classifiedFormData.getCurrency());
classifiedRepository.save(classified);
if (true) {
throw new Exception("The saved Classified should be deleted because of the @Transactional annotation");
}
String idAsStr = String.valueOf(classified.getId());
pathToImagesForThisClassified = Paths.get("images", idAsStr);
filePaths = storageService.storeAll(pathToImagesForThisClassified, images);
File thumbnail = imageManipulationService.resize(filePaths.get(classifiedFormData.getThumbnailIndex()),
new Dimension(255, 255));
pathToThumbnailsForThisClassified = Paths.get("thumbnails", idAsStr);
thumbnailPath = storageService.store(pathToThumbnailsForThisClassified, thumbnail);
classified.setImagePaths(filePaths);
classified.setThumbnailImagePath(thumbnailPath);
classifiedRepository.save(classified);
Place place = new Place(classified);
place.setCountry(classifiedFormData.getCountry());
place.setLabel(classifiedFormData.getLabel());
place.setLatitude(Double.valueOf(classifiedFormData.getLat()));
place.setLongitude(Double.valueOf(classifiedFormData.getLon()));
placeRepository.save(place);
} catch (Exception e) {
e.printStackTrace();
storageService.deleteRecursively(pathToImagesForThisClassified);
storageService.deleteRecursively(pathToThumbnailsForThisClassified);
}
}
}
ClassifiedControllerTest.java 在 src/test/java/com/example/controllers:
package com.example.controllers;
import com.example.entities.Classified;
import com.example.entities.Place;
import com.example.inbound.ClassifiedFormData;
import com.example.repositories.ClassifiedRepository;
import com.example.repositories.PlaceRepository;
import com.example.services.StorageService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.TestConstructor;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@DisplayName("ClassifiedController")
public class ClassifiedControllerTest {
private final MockMvc mvc;
private final ClassifiedRepository classifiedRepository;
private final PlaceRepository placeRepository;
private final StorageService storageService;
public ClassifiedControllerTest(MockMvc mvc, ClassifiedRepository classifiedRepository,
PlaceRepository placeRepository, StorageService storageService) {
this.mvc = mvc;
this.classifiedRepository = classifiedRepository;
this.placeRepository = placeRepository;
this.storageService = storageService;
}
@DisplayName("Any saved entities and files are deleted if an exception is encountered")
@Test
public void givenInvalidFormData_whenPosted_thenStatus400AndClean() throws Exception {
// GIVEN
ClassifiedFormData classifiedFormData = new ClassifiedFormData();
classifiedFormData.setCountry("Spain");
classifiedFormData.setCurrency("EUR");
classifiedFormData.setSummary("Test");
classifiedFormData.setDescription("Test");
classifiedFormData.setLabel("Test");
classifiedFormData.setPrice(32.45);
classifiedFormData.setThumbnailIndex((byte)1);
classifiedFormData.setLat("42.688630");
classifiedFormData.setLon("-2.945620");
MockMultipartFile classified = new MockMultipartFile("data", "", "application/json",
("{\"summary\":\"feefwfewfew\",\"description\":\"fewfewfewfewfwe\",\"price\":\"34\"," +
"\"currency\":\"CAD\",\"thumbnailIndex\":0,\"lat\":\"52.2460367\",\"lon\":\"0.7125173\"," +
"\"label\":\"Bury St Edmunds, Suffolk, East of England, England, IP33 1BZ, United Kingdom\"," +
"\"country\":\"United Kingdom\"}").getBytes());
byte[] image1Bytes = getClass().getClassLoader().getResourceAsStream("test_image.jpg").readAllBytes();
byte[] image2Bytes = getClass().getClassLoader().getResourceAsStream("test_image.jpg").readAllBytes();
String image1Filename = "image1.jpg";
String image2Filename = "image2.jpg";
MockMultipartFile image1 =
new MockMultipartFile("images", image1Filename,"image/jpeg", image1Bytes);
MockMultipartFile image2 =
new MockMultipartFile("images", image2Filename, "image/jpeg", image2Bytes);
Path expectedImagePath = Paths.get("images", "5");
Path expectedThumbnailPath = Paths.get("thumbnails", "5");
// WHEN-THEN
mvc.perform(MockMvcRequestBuilders.multipart("/classifieds")
.file(classified)
.file(image1)
.file(image2)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
.andExpect(status().isOk());
Optional<Classified> classifiedOptional = classifiedRepository.findById((long)5);
assertFalse(classifiedOptional.isPresent()); // This is the assertion that is failing
Optional<Place> placeOptional = placeRepository.findByClassifiedId(5);
assertFalse(placeOptional.isPresent());
Resource image1AsResource = storageService.loadAsResource(expectedImagePath, image1Filename);
Resource image2AsResource = storageService.loadAsResource(expectedImagePath, image2Filename);
Resource thumbnailAsResource = storageService.loadAsResource(expectedThumbnailPath, "thumbnail.jpg");
assertFalse(image1AsResource.exists());
assertFalse(image2AsResource.exists());
assertFalse(thumbnailAsResource.exists());
}
}
测试结果:
java.lang.Exception: The saved Classified should be deleted because of the @Transactional annotation
at com.example.controllers.ClassifiedController.addClassified(ClassifiedController.java:67)
at com.example.controllers.ClassifiedController$$FastClassBySpringCGLIB$50f537.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
[some lines omitted for brevity]
expected: <false> but was: <true>
org.opentest4j.AssertionFailedError: expected: <false> but was: <true>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertFalse.assertFalse(AssertFalse.java:40)
at org.junit.jupiter.api.AssertFalse.assertFalse(AssertFalse.java:35)
at org.junit.jupiter.api.Assertions.assertFalse(Assertions.java:210)
at com.example.controllers.ClassifiedControllerTest.givenInvalidFormData_whenPosted_thenStatus400AndClean(ClassifiedControllerTest.java:148)
该方法从不抛出异常,因此 Spring 没有理由回滚事务。
如果它确实抛出异常(例如通过在 catch 块的末尾添加 throw new RuntimeException(e);
),那么 Spring 将回滚事务。
您正在 addClassified(@RequestPart(name="data") 方法的 catch 块中捕获冒泡异常。
你必须在catch块中抛出异常或者移除catch块,这样spring的拦截器才能知道抛出异常并回滚事务。
抛出的异常
if (true) {
throw new Exception("The saved Classified should be deleted because of the *@Transactional* annotation");
}
被抓住:
} catch (Exception e) {
e.printStackTrace();
// ...
}
并且不离开 addClassified 方法,即不会传播异常。因此,Spring不会做任何事情。
在高层次上,@transactional 注释将您的代码包装成如下形式:
UserTransaction utx = entityManager.getTransaction();
try {
utx.begin();
addClassified(); // your actual method invocation
utx.commit();
} catch (Exception ex) {
utx.rollback();
throw ex;
}
TL;DR:您可以删除 try-catch 或(重新)在您的 catch 块中抛出一个新异常。
@Actully 你不应该处理异常,因为 spring 不知道异常所以不能回滚 并且您有要求,如果发生异常,则必须删除文件。比你喜欢的要多。
public void addClassified(@RequestPart(name="data") ClassifiedFormData classifiedFormData, @RequestPart(name="images") MultipartFile[] images) {
// to delete file
boolean flag = true;
try {
Classified classified = new Classified();
classified.setSummary(classifiedFormData.getSummary());
classified.setDescription(classifiedFormData.getDescription());
classified.setPrice(classifiedFormData.getPrice());
classified.setCurrency(classifiedFormData.getCurrency());
classifiedRepository.save(classified);
if (true) {
throw new Exception("The saved Classified should be deleted because of the @Transactional annotation");
}
String idAsStr = String.valueOf(classified.getId());
pathToImagesForThisClassified = Paths.get("images", idAsStr);
filePaths = storageService.storeAll(pathToImagesForThisClassified, images);
File thumbnail = imageManipulationService.resize(filePaths.get(classifiedFormData.getThumbnailIndex()),
new Dimension(255, 255));
pathToThumbnailsForThisClassified = Paths.get("thumbnails", idAsStr);
thumbnailPath = storageService.store(pathToThumbnailsForThisClassified, thumbnail);
classified.setImagePaths(filePaths);
classified.setThumbnailImagePath(thumbnailPath);
classifiedRepository.save(classified);
Place place = new Place(classified);
place.setCountry(classifiedFormData.getCountry());
place.setLabel(classifiedFormData.getLabel());
place.setLatitude(Double.valueOf(classifiedFormData.getLat()));
place.setLongitude(Double.valueOf(classifiedFormData.getLon()));
placeRepository.save(place);
flag = false;
} finally {
if(flag){
storageService.deleteRecursively(pathToImagesForThisClassified);
storageService.deleteRecursively(pathToThumbnailsForThisClassified);
}
}
}