Мягкое удаление из БД при определённом условие
Суть вопроса в том, что необходимо удалить категорию только тогда, когда баннеры удалены
Entity в который реализовано мягкое удаление из БД:
@Entity
@SQLDelete(sql = "UPDATE Category SET deleted = true WHERE id=?")
@Where(clause = "deleted=false")
@Table(name = "Category")
public class Category extends BaseModelEntity {
@OneToMany(mappedBy = "category")
@Getter
@Setter
private Set<Banner> banners = new HashSet<>();
@Getter
@Setter
@Column(nullable = false)
private String name;
@Getter
@Setter
@Column(nullable = false)
private String req_name;
@Getter
@Setter
private boolean deleted = Boolean.FALSE;
}
Service:
@Service
public class CategoryService {
@Autowired
private CategoryRepository categoryRepository;
public void remove(int id, Category category){
if(category.getBanners().isEmpty()) {
categoryRepository.deleteById(id);
} else {
System.out.println("не могу удалить");
}
}
}
Я использую тест для проверки, что баннер содержит категорию.
@Test
public void deleteCategory() {
Category category = new Category();
category.setName("test1");
category.setReq_name("test2");
Banner banner = new Banner(category);
banner.setName("first");
banner.setPrice(8.2);
banner.setText("");
Banner banner1 = new Banner(category);
banner1.setName("second");
banner1.setPrice(8.2);
banner1.setText("");
categoryRepository.save(category);
bannerRepository.save(banner1);
Assertions.assertFalse(category.getBanners().isEmpty());
}
}
Получаю ошибку, что категория пуста, хотя я заинжектил категория через конструктор баннера.
org.opentest4j.AssertionFailedError:
Expected :false
Actual :true
Что не так в данном условие удаления? При тестирования метода remove категория удаляется, если она содержит баннеры.
Ответы (1 шт):
Получаю ошибку, что категория пуста, хотя я заинжектил категория через конструктор баннера.
В категории нет баннеров потому, что Hibernate не обновляет ее автоматически после сохранения баннера. Чтобы баннер появился в категории после сохранения, категорию необходимо перезапросить. Тест может выглядеть как-то так:
@Test
public void deleteCategory() {
Category category = new Category();
category.setName("test1");
category.setReq_name("test2");
Banner banner = new Banner(category);
banner.setName("first");
banner.setPrice(8.2);
banner.setText("");
Banner banner1 = new Banner(category);
banner1.setName("second");
banner1.setPrice(8.2);
banner1.setText("");
category = categoryRepository.save(category);
bannerRepository.save(banner1);
category = categoryRepository.findById(category.getId())
Assertions.assertFalse(category.getBanners().isEmpty());
}
Не забывайте, что Hibernate для каждой транзакции создает собственный транзакционный кеш. Тест будет работать до тех пор, пока каждый запрос в БД выполняется в отдельной транзакции, т.е. не будет кеширования данных. Как только метод deleteCategory будет помечен с помощью @Transactional, запросы из репозиториев будут выполняться с использованием транзакционного кеша, а в нем создаваемая в методе категория не будет содержать ни одного баннера, т.к. Hibernate не обновляет связанные данные в кеше автоматически. Что бы связи между сущностями после их сохранения появились, необходимо разделять операции сохранения и запроса по разным транзакциям в тесте.
Что бы избежать сложностей с сохранением/перезапросом сущностей, можно сделать каскадное сохранение сущностей. В таком случае, при сохранении категория уже будет содержать баннеры:
@Entity
@SQLDelete(sql = "UPDATE Category SET deleted = true WHERE id=?")
@Where(clause = "deleted=false")
@Table(name = "Category")
public class Category extends BaseModelEntity {
@OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
@Getter
@Setter
private Set<Banner> banners = new HashSet<>();
@Getter
@Setter
@Column(nullable = false)
private String name;
@Getter
@Setter
@Column(nullable = false)
private String req_name;
@Getter
@Setter
private boolean deleted = Boolean.FALSE;
}
Тест упростится до
@Test
public void deleteCategory() {
Category category = new Category();
category.setName("test1");
category.setReq_name("test2");
Banner banner = new Banner(category); // Ссылка на категорию
banner.setName("first");
banner.setPrice(8.2);
banner.setText("");
Banner banner1 = new Banner(category); // Ссылка на категорию
banner1.setName("second");
banner1.setPrice(8.2);
banner1.setText("");
// Добавление баннеров в категорию для каскадного сохранения
category.setBanners(Sets.newHashSet(banner, banner1));
category = categoryRepository.save(category);
Assertions.assertFalse(category.getBanners().isEmpty());
}