O Bean Validation so funciona com jpa?

Ola,

Iniciei um pequeno projeto spring-boot, porem resolvi usar JdbcTemplate ao inves de JPA pois é em um banco de terceiros. Porem percebi que as validacoes nao vem quando eu inicio o projeto spring boot com jdbc, mas se eu colocar o jpa funciona normalmente.

É isso mesmo?

Desconheço uma implementação do beans validation para JDBC, pois funciona com JPA pois é o hibernate que provê uma implementação da especificação para ser usada.

1 curtida

Usa o “Bean Validation” do Spring Boot. Funciona com qualquer POJO, independente do que estiver usando para persistência.

Exemplo:

		<dependency>
			<groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
package com.example.demo;

import javax.validation.constraints.NotEmpty;

public class Teste {
	@NotEmpty(message = "Nome é requerido")
	private String nome;
	
	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}
}
package com.example.demo;

import java.util.HashMap;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.HttpStatus;

@RestController
@RequestMapping("exemplo")
public class ExemploController {
   @PostMapping
   public ResponseEntity<String> post(@RequestBody @Valid Teste teste) {
       return ResponseEntity.ok("ok");
   }
   
   @ResponseStatus(HttpStatus.BAD_REQUEST)
   @ExceptionHandler(MethodArgumentNotValidException.class)
   public Map<String, String> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
       Map<String, String> errors = new HashMap<>();
    
       ex.getBindingResult().getFieldErrors().forEach(error -> 
           errors.put(error.getField(), error.getDefaultMessage()));
        
       return errors;
   }   
}
1 curtida

eu fiz isso. Aparece as anotações de validação, porém não valida nada.

Do jeito que o @javaflex passou, ele vai validar na camada REST, mas acho que não serve para jdbcTemplate.

1 curtida

exato, eu nao estou usando rest. ter que fazer na mao mesmo ou ativar a jpa e usar o jdbctemplate

Foi só um exemplo para aplicação web, que é o mais comum hoje. Mas a validação pode funcionar em qualquer situação dentro do Java. Um exemplo sem depender do tipo de aplicação:

	   Teste teste = new Teste();
	   teste.setNome("");
	   
	   Set<ConstraintViolation<Teste>> constraintViolation = validator.validate(teste);
	   
	   if (!constraintViolation.isEmpty()) {
		   throw new ConstraintViolationException(constraintViolation);
	   }

Isso pode ficar no ServiceBase. Não precisa amarrar a um mecanismo de persistência ou trazer overhead do JPA. Tem muitas formas de usar o Bean Validation, é o que pude exemplificar, mas dá uma pesquisada geral sobre Spring com Bean Validation.

1 curtida

eu agora ja estou achando que eh problema do projeto. pois criei um entity jpa aqui e nao ta validando tb.

Com o último exemplo que passei é só explorar o constraintViolation que nao terá mistérios, sem overhead de JPA/Hibernate pode validar qualquer POJO no Service ou onde desejar.

2 curtidas

Classe completa caso tenha dúvida sobre as referencias:

package com.example.demo;

import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;

public class QualquerClasse {
	@Autowired
	Validator validator;
	
	public void qualquerMetodo() {
	   Teste teste = new Teste();
	   teste.setNome("");
	   
	   Set<ConstraintViolation<Teste>> constraintViolation = validator.validate(teste);
	   
	   if (!constraintViolation.isEmpty()) {
		   throw new ConstraintViolationException(constraintViolation);
	   }	   
	}
}
1 curtida

Ta tudo certinho aqui. Pior que acho que ja passei por isso antes, e nao sei o que foi. Mas é verdade que o BeanValidation é para funcionar independente de persistencia, pois ele é chamado na hora que recebo os dados no controller e so depois da validacao que o fluxo continua.

public class Secao {

	private Long id;

	@Size(min = 3, max = 30, message = "O campo NOME deve ter entre 3 e 30 caracteres.")
	@NotEmpty(message = "O campo NOME deve ser preenchido.")
	private String nome;

	public Secao() {
	}

	public Secao(Long id) {
		super();
		this.id = id;
	}

	public Secao(Long id, String nome) {
		super();
		this.id = id;
		this.nome = nome;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome.toUpperCase();
	}

	public boolean isNovo() {
		return id == null;
	}
}
@Controller
@RequestMapping("/secoes")
public class SecaoController {

	@Autowired
	SecaoService secaoService;

	@GetMapping
	public ModelAndView listar() {
		ModelAndView mv = new ModelAndView("secao/PesquisaSecao");
		mv.addObject("secoes", secaoService.findAll());
		return mv;
	}

	@GetMapping("/cadastrar")
	public ModelAndView cadastrar(Secao secao) {
		return new ModelAndView("secao/CadastroSecao");
	}

	@RequestMapping(value = { "/cadastrar", "{\\d+}" }, method = RequestMethod.POST)
	public ModelAndView salvar(@Valid Secao secao, BindingResult result, RedirectAttributes attributes) {
		if (result.hasErrors()) {
			cadastrar(secao);
		}
		secaoService.saveOrUpdate(secao);
		attributes.addFlashAttribute("mensagem", "Seção cadastrada com sucesso!");
		return new ModelAndView("redirect:/secoes");
	}

	@GetMapping("/{id}")
	public ModelAndView editar(@PathVariable("id") Long id) {
		Secao secao = secaoService.findById(id);
		ModelAndView mv = cadastrar(secao);
		mv.addObject(secao);
		return mv;
	}

	@DeleteMapping("/{id}")
	public @ResponseBody ResponseEntity<?> excluir(@PathVariable("id") Long id) {
		secaoService.delete(id);
		return ResponseEntity.ok().build();
	}
	
}
<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.2.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>nz.net.ultraq.thymeleaf</groupId>
			<artifactId>thymeleaf-layout-dialect</artifactId>
		</dependency>
		<dependency>
			<groupId>com.github.mxab.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-data-attribute</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity5</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

Aparentemente voce está usando Spring MVC. Então pesquisa algum tutorial sobre como configurar Bean Validation com Spring MVC. Exemplo:

https://docs.spring.io/spring/docs/4.1.x/spring-framework-reference/html/validation.html

1 curtida

eu sei usar validacao, ja uso faz anos. A questao foi so pq eu criei um projeto e ela nao entrou, quando coloquei recurso, simplesmente nao funcionou e por incrivel que pareca acabei de criar um projeto novo, e da mesma forma que o anterior com as mesmas versoes e pacotes e funcionou. O que é mais estranho.

o erro de nao estar validando era uma merda…

@RequestMapping(value = { "/cadastrar", "{\\d+}" }, method = RequestMethod.POST)
	public ModelAndView salvar(@Valid Secao secao, BindingResult result, RedirectAttributes attributes) {
		if (result.hasErrors()) {

             // ESTAVA FALTANDO UM RETURN AQUI
			RETURN cadastrar(secao);

		}
		secaoService.saveOrUpdate(secao);
		attributes.addFlashAttribute("mensagem", "Seção cadastrada com sucesso!");
		return new ModelAndView("redirect:/secoes");
	}