[RESOLVIDO] Como posso usar o transaction / rollback

Tenho percebido um erro em que ao alterar uma cidade, com a quantidade da população no negativo, tipo, quando eu digito -1, ele me avisa que o valor é inválido, contudo o sistema persiste em adicionar o valor errado. Me foi instruído a resolver o problema utilizando transaction / rollback. Como deveria fazer isso?

Minha entidade:
Cidade.java

@Getter
@Setter
@Entity
@SQLDelete(sql = "UPDATE cidade SET ativo = 0 WHERE id = ?")
@Where(clause = "ativo = 1")
public class Cidade extends BaseEntity {

	private String nome;
	private String sigla;
	
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(nullable = false)
	private Estado estado;
	
	private Double populacao;
	
}

Eu até tentei fazer a mudança removendo a anotação Where como vi em outro site, mas não deu certol. Será que devo fazer uma alteração na tabela?

Meu CidadeResource:

@RestController 
@RequestMapping("/api/cidade") 
public class CidadeResource {
		
	@Autowired
	private CidadeRepository repository;
	@Autowired
	private EstadoRepository repositoryEstado;
	@Autowired
	private CidadeResourceMapper mapper;

	@GetMapping(produces = { MediaType.APPLICATION_JSON_VALUE })
	public ResponseEntity<PageDto<CidadeDTO>> getPageWithQuery(@QuerydslPredicate(root = Cidade.class ) Predicate predicate, Pageable pageble) {
		return ResponseEntity.ok(convertToPageDto(predicate, pageble));
	}
	
	@PostMapping
	public ResponseEntity<CidadeDTO> add(@Valid @RequestBody CidadeWriteDTO dto) throws Exception {
		Cidade entity = this.mapper.fromDto(dto);
		
    	entity = repository.save(entity);
    	
    	ResponseEntity<CidadeDTO> re = ResponseEntity.ok(this.convertToDto(entity));
    	
    	return re;
				
	}

	@GetMapping(value = "/{id}")
	public ResponseEntity<CidadeDTO> get(@PathVariable Integer id) {
		return ResponseEntity.ok(convertToDto(repository.findById(id).get()));
	}
		
	@PutMapping(value = "/{id}")
	public ResponseEntity<CidadeDTO> update(@Valid @RequestBody CidadeWriteDTO dto, @PathVariable Integer id) throws AppException {
		
		Optional<Cidade> entity = repository.findById(id);
		if (entity.isPresent()) {
						
			Double qtdPopulacao = 0D;
			if (entity.get().getEstado().getPopulacao() == null)
				qtdPopulacao = entity.get().getPopulacao();
			else {
				// Remove a população da cidade (antes da alteração)
				qtdPopulacao = entity.get().getEstado().getPopulacao() - entity.get().getPopulacao();
			}
			
			this.mapper.merge(entity.get(), dto);

			// Adiciona a população da cidade
			qtdPopulacao += dto.getPopulacao();
			
			// Atualiza os dados no estado
			entity.get().getEstado().setPopulacao(qtdPopulacao);
			
			// Salva a entidade no BD
			repositoryEstado.save(entity.get().getEstado());
			
			if (dto.getPopulacao() < 0)
				throw new AppException("Valor inválido da quantidade da população");

			
			Cidade mergedEntity = repository.save(entity.get());
        	
        	ResponseEntity<CidadeDTO> re = ResponseEntity.ok(this.convertToDto(mergedEntity));
        	
			return re;
		} else {
			throw new RuntimeException();
		}
		
	}	

	@DeleteMapping(value = "/{id}")
	public void delete(@PathVariable Integer id) throws Exception {
		
		repository.deleteById(id);
		
	}
	
	private PageDto<CidadeDTO> convertToPageDto(Predicate predicate, Pageable pageble) {
		Page<Cidade> pageEntity = repository.findAll(predicate, pageble);
		
		return new PageDto<>(StreamSupport.stream(pageEntity.spliterator(), false).map(this::convertToDto)
				.collect(Collectors.toList()), pageEntity.getTotalElements());
	}	   
	
	private CidadeDTO convertToDto(Cidade entity) {
		return mapper.toDto(entity);
	}
		
}

Será que devo modificar a sentença:
if (dto.getPopulacao() < 0)
throw new AppException(“Valor inválido da quantidade da população”);

Vou tentar…

https://www.devmedia.com.br/conheca-o-spring-transactional-annotations/32472

1 curtida

É só adicionar um @Transactional em cima dos métodos: add, update e delete!

Com isso caso alguma exceção seja lançada no escopo do método durante a execução do método um rollback será aplicado.

1 curtida

@Jonathan_Medeiros verifica por favor se fiz algo errado porque continua dando o erro:

        @Transactional
	    @PostMapping
	public ResponseEntity<CidadeDTO> add(@Valid @RequestBody CidadeWriteDTO dto) throws Exception {
		Cidade entity = this.mapper.fromDto(dto);
		
    	entity = repository.save(entity);
    	
    	ResponseEntity<CidadeDTO> re = ResponseEntity.ok(this.convertToDto(entity));
    	
    	return re;
				
	}

@Transactional
@PutMapping(value = "/{id}")
public ResponseEntity<CidadeDTO> update(@Valid @RequestBody CidadeWriteDTO dto, @PathVariable Integer id) throws AppException {
	
	Optional<Cidade> entity = repository.findById(id);
	if (entity.isPresent()) {
					
		Double qtdPopulacao = 0D;
		if (entity.get().getEstado().getPopulacao() == null)
			qtdPopulacao = entity.get().getPopulacao();
		else {
			// Remove a população da cidade (antes da alteração)
			qtdPopulacao = entity.get().getEstado().getPopulacao() - entity.get().getPopulacao();
		}
		
		this.mapper.merge(entity.get(), dto);

		// Adiciona a população da cidade
		qtdPopulacao += dto.getPopulacao();
		
		// Atualiza os dados no estado
		entity.get().getEstado().setPopulacao(qtdPopulacao);
		
		// Salva a entidade no BD
		repositoryEstado.save(entity.get().getEstado());
		
		if (dto.getPopulacao() < 0)
			throw new AppException("Valor inválido da quantidade da população");

		
		Cidade mergedEntity = repository.save(entity.get());
    	
    	ResponseEntity<CidadeDTO> re = ResponseEntity.ok(this.convertToDto(mergedEntity));
    	
		return re;
	} else {
		throw new RuntimeException();
	}
	
}	

@Transactional
@DeleteMapping(value = "/{id}")
public void delete(@PathVariable Integer id) throws Exception {
	
	repository.deleteById(id);
	
}

private PageDto<CidadeDTO> convertToPageDto(Predicate predicate, Pageable pageble) {
	Page<Cidade> pageEntity = repository.findAll(predicate, pageble);
	
	return new PageDto<>(StreamSupport.stream(pageEntity.spliterator(), false).map(this::convertToDto)
			.collect(Collectors.toList()), pageEntity.getTotalElements());
}	   

private CidadeDTO convertToDto(Cidade entity) {
	return mapper.toDto(entity);

Eu importei certo não? Veja: import org.springframework.transaction.annotation.Transactional;

@javaflex você me enviou esse link e te agradeço bastante cara. Mas não consegui entender como fazer o rollback.

Sim, é essa mesmo!

É importante que você valide os dados sempre antes de salvar qualquer coisa! vi que no seu código você valida algumas coisas após aplicar um save.

@Jonathan_Medeiros
Eu consegui resolver adicionando:
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
em cada método como você indicou. Por isso darei à você a resolução aqui. Eu achei esta resposta aqui!

1 curtida