Estou convivendo com erro, gostaria da vossa ajuda, desenvolvendo uma aplicação em java com spring boot, preciso popular combobox de forma dinâmica com ajax:
Tenho um formulário, em que ao selecionar a província preciso disparar outro combox de municípios desta província selecionada.
O código do meu script:
<script type="text/javascript">
$(document).ready(function(){
$('#comboboxProvincia').on('change', function(){
var idProvincia = $(this).val();
$.ajax({
type: 'GET',
url: 'loadMunicipioByProvincia/' + idProvincia,
success: function(result) {
var result = JSON.parse(result);
var s = '';
for(var i = 0; i < result.length; i++) {
s += '<option value="' + result[i].idMunicipio + '">' + result[i].descricaoMuni + '</option>';
}
$('#comboboxMunicipio').html(s);
}
});
});
});
</script>
O código do meu controller
@ResponseBody
@RequestMapping(value = "loadMunicipioByProvincia/{idProvincia}", method = RequestMethod.GET)
public String loadStatesByCountry(@PathVariable("idProvincia") int idProvincia) {
List<Municipio> listaNew = municipioService.listaMuniPorProv(idProvincia);
for (int i = 0; i < listaNew.size(); i++) {
System.out.println(listaNew.get(i).getDescricaoMuni());
}
Gson gson = new Gson();
return gson.toJson(municipioService.listaMuniPorProv(idProvincia));
}
O Erro da console.log “no front”:
GET http://localhost:8080/loadMunicipioByProvincia/93 500
Erro no Spring Too:
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:244) ~[gson-2.9.0.jar:na]
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:1086) ~[gson-2.9.0.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.9.0.jar:na]
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97) ~[gson-2.9.0.jar:na]
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61) ~[gson-2.9.0.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.9.0.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:126) ~[gson-2.9.0.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:244) ~[gson-2.9.0.jar:na]
… com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97) ~[gson-2.9.0.jar:na]
Isso mesmo. Apenas tenha cuidado com os relacionamentos bidirecionais, que podem dá erro na serialização.
Eu teria feito algo assim:
@GetMapping("/municipios")
public List<MunicipioDto> filtrarMunicipios(
@RequestParam("provincia") int idProvincia
) {
return municipioService
.findByProvincia(idProvincia)
.stream()
.map(m -> new MunicipioDto(m))
.collect(Collectors.toList());
}
Repare que o resultado é transformado (map) num DTO antes de retornar. Isso é uma boa prática, pois não é legal retornar a entidade diretamente. Isso facilita algumas coisas na medida em que o código vai evoluindo. Dá mais flexibilidade.
E repare que a provincia foi alterada de @PathVariable para `@RequestParam, pois fica mais flexível caso vc queira filtrar os municípios por algum outro campo no futuro.
Outra forma de fazer esse endpoint que retorna os municípios de uma província usando @PathVariable, poderia ser assim:
// ProvinciaController.java
@GetMapping("/provincias/{idProvincia}/municipios")
public List<MunicipioDto> getMunicipios(
@PathVariable("idProvincia") int idProvincia
) {
return municipioService
.findByProvincia(idProvincia)
.stream()
.map(m -> new MunicipioDto(m))
.collect(Collectors.toList());
}
é um assunto bem polêmico em relação ao DTO ou ViewModel ou Modelo de passagem, eu particularmente acredito que não tem vantagem e nem desvantagem, falar que enviar um modelo de entidade para algum tipo de retorno, fico pensando aqui com meus botões as informações são as mesmas os dados computados são os mesmo o retorno é uma copia da Entidade sei lá, é como eu disse não tem problema e também não há problema retornar em DTO.
Talvez um prática, um modo de programar e dizer que é bom, estou mesmo querendo entender até aonde é bom, porque é mais um classe.
Por exemplo, já tive cenários onde foi necesśario juntar dados de mais de uma entidade para retornar num endpoint (um recurso disponibilizado num endpoint não necessariamente se refere à uma tabela de banco de dados, pois não há essa relação, porém, muitas vezes acaba acontecendo, mas não é uma regra).
Outro caso é que, nem sempre vc precisa retornar todos os dados num primeiro momento, ou há dados que nem podem ser expostos, sem falar do problema de relacionamentos bidirecionais.
Ter essa transformação no meio acaba ajudando em todos esse pontos e em até outros que posso não estar lembrando agora.
Relaxa, sempre vejo que suas respostas aqui no fórum são sempre ótimas e que sempre agrega.
Pois é @Lucas_Camara não é ruim fazer um DTO ou ViewModel ou modelo de passagem, tem a sua empregabilidade, mas, na maioria são retornos comuns e ai eu vou criar um retorno que já existe? é nesse aspecto que eu realmente fico na duvida porque no final das contas é o mesmo tipo de dado.
Cara sério é um debate saudavel, não sou contra, mas, acho totalmente desnecessário repetir classes e também acho necessário pra alguma parte do projeto. E também acho que o cara é livre pra programar dessa forma com DTO para mostrar resultados.
É um assunto muito interessante porque cai naquele ponto “Boas práticas”!
Você tem que verificar o retorno com F12 no navegador na parte de redes clicando em cima da rota estabelecida para ver o que o back end envia para o front end. Tem que verificar isso, porque pelo visto agora deu certo o retorno, falta a parte do front end