스프링 부츠 e MongoDB
Aqui faremosóo 백엔드 da aplica ão e futuramente desenvolveremoso 전단.
Criando a aplica ão 스프링 부츠
Para isso vamos usar oSpring Initalizr,entrando na página escolhemos como queremos iniciar o projeto,aqui eu irei usar o Spring Web Para poder fazer requisiçes Rest,também estou usando Lombok e Spring DevTools mas s s s sao mais pela facilidade que o Lombok fornece quando criarmos nossos POJOs e o DevTools Para podermos usar em desenvolvimento e termos live reload da aplicaão.
Então fica mais ou menos assim o projeto:
Após isso também precisamos adicionar ao projeto a Dependentência do google service maps,como estou usando Maven
<!-- https://mvnrepository.com/artifact/com.google.maps/google-maps-services -->
<dependency>
<groupId>com.google.maps</groupId>
<artifactId>google-maps-services</artifactId>
<version>0.11.0</version>
</dependency>
Também adicione o driver do Mongo ao pom.<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</dependency>
설명
Vamos emular uma rede entregas de alimentos,onde os estabellecimentos estão cadastrados e quando um usuário digitar o seu endereço e ele iráexibir os mais próximos dele.
모델
Vamos começar o nosso model com o que seria então estabellecimento ele possuiránome,email e a sua localizaão.então vou começar criando a classe Localizacao.
package com.challenge.geolocation.model;
import java.util.List;
import lombok.Data;
@Data
public class Localizacao {
private String endereco;
private List<Double> coordinates;
private String type = "Point";
}
Aqui temos o endere o mas também temos dois atributos que podem parecer um pouco estranhos o 좌표 e o 유형.Os dois são necesários quando estamos trabalhando com geologisazaão com o Mongo,o primeiro valor coordinateséuma lista de double contendo a lations e longitude o type diz respeito a um ponto no mapa,podemos ter outros type como Polygon.Agora criando a nossa classe do estabellecimento propriamente dita.
package com.challenge.geolocation.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Data;
package com.challenge.geolocation.model;
import org.
import lombok.Data;
@Datapublic class Estabelecimento
private ObjectId id;
private String nome;
private String email;
private Localizacao localizacao;
}
Temos aqui a classe estabellecimento composta pela classe Localizacao e com os atributos id lombok nos ajuda a reduzir um pouco a verbozidade.코딩기
Agora temos o Model criado mas precisamos fazer de alguma forma pra que a nossa aplicaão se comunique com o Mongo.Aíentraos 인코더,ele vai ser o response á vel por fazer tanto o envio como o recebimento dos objetos do Mongo.
Então vamos criar a classe estabellecimentocodec que implementa a interface CollectibleCodec do tipo estabellecimento:
package com.challenge.geolocation.codec;
import org.bson.BsonReader;
import org.bson.BsonValue;
import org.bson.BsonWriter;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import com.challenge.geolocation.model.Estabelecimento;
public class EstabelecimentoCodec implements CollectibleCodec<Estabelecimento>{
@Override
public void encode(BsonWriter writer, Estabelecimento value, EncoderContext encoderContext) {
// TODO Auto-generated method stub
}
@Override
public Class<Estabelecimento> getEncoderClass() {
// TODO Auto-generated method stub
return null;
}
@Override
public Estabelecimento decode(BsonReader reader, DecoderContext decoderContext) {
// TODO Auto-generated method stub
return null;
}
@Override
public Estabelecimento generateIdIfAbsentFromDocument(Estabelecimento document) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean documentHasId(Estabelecimento document) {
// TODO Auto-generated method stub
return false;
}
@Override
public BsonValue getDocumentId(Estabelecimento document) {
// TODO Auto-generated method stub
return null;
}
}
Agora precisamos começar a implementar o codec a nossa maneira para ele poder fazer o encod e o decode,para isso vamos adicionar a classe codec do pacote bson que nos ajuda,vamos tipa la como um Document e vamos adicioná-lo ao construtor para ele ficar como dependenceência do nosso codec:import org.bson.Document;
import org.bson.codecs.Codec;
import com.challenge.geolocation.model.Estabelecimento;
public class EstabelecimentoCodec implements CollectibleCodec<Estabelecimento>{
private Codec<Document> codec;
public EstabelecimentoCodec(Codec<Document> codec) {
this.codec = codec;
}
Agora vamos 실현자 o método respons á vel por fazer o encode.Aquiéonde dizemos como serão salvo os nossos objetos em Java para um objeto do Mongo:@Override
public void encode(BsonWriter writer, Estabelecimento estabelecimento, EncoderContext encoder) {
Document document = new Document();
document.put("_id", estabelecimento.getId());
document.put("nome", estabelecimento.getNome());
document.put("email", estabelecimento.getEmail());
Localizacao localizacao = estabelecimento.getLocalizacao();
List<Double> coordinates = new ArrayList<>();
localizacao.getCoordinates().forEach(coordinates::add);
document.put("localizacao", new Document()
.append("endereco", localizacao.getEndereco())
.append("coordinates", coordinates)
.append("type", localizacao.getType()));
codec.encode(writer, document, encoder);
}
E aquiéonde impletamos o decode,como o Java vai interpretar o objeto returnado do Mongo:@Override
public Estabelecimento decode(BsonReader reader, DecoderContext decoderContext) {
Document document = codec.decode(reader, decoderContext);
Estabelecimento estabelecimento = new Estabelecimento();
estabelecimento.
estabelecimento.setNome(document.getString("nome"));
estabelecimento.setEmail(document.getString("email"));
Document localizacao = (Document) document.get("localizacao");
if(localizacao != null) {
String endereco = localizacao.getString("endereco");
@SuppressWarnings("unchecked")
List<Double> coordinates = (List<Double>) localizacao.get("coordinates");
Localizacao localizacaoEntity = new Localizacao();
localizacaoEntity.setEndereco(endereco);
localizacaoEntity.setCoordinates(coordinates);
estabelecimento.setLocalizacao(localizacaoEntity);
}
return estabelecimento;
}
E temos os outros métodos que implementamos para que o codec consiga fazer a gerência dos objetos:@Override
public Class<Estabelecimento> getEncoderClass() {
return Estabelecimento.class;
}
@Override
public Estabelecimento generateIdIfAbsentFromDocument(Estabelecimento estabelecimento) {
return documentHasId(estabelecimento) ? estabelecimento.generateId() : estabelecimento;
}
@Override
public boolean documentHasId(Estabelecimento estabelecimento) {
return estabelecimento.getId() == null;
}
@Override
public BsonValue getDocumentId(Estabelecimento estabelecimento) {
if (!documentHasId(estabelecimento)) {
throw new IllegalStateException("This Document do not have a id");
}
return new BsonString(estabelecimento.getId().toHexString());
}
Aúnica coisa aqui A ressattle foi A criaão do método generateId no model estabellecimento que fica assim:package com.challenge.geolocation.model;
import org.
import lombok.Data;
@Datapublic class Estabelecimento
private ObjectId id;
private String nome;
private String email;
private Localizacao localizacao;
public Estabelecimento generateId() {
this
return this;
}
}
저장소
Agora temos o Model e o Codec Agora podemos criar o nosso Repository que iráfazer o acesso ao banco e ficaráresponsável por toda a gerência no Mongo.
package com.challenge.geolocation.repository;
import org.springframework.stereotype.Repository;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
@Repository
public class EstabelecimentoRepository {
private MongoClient client;
private MongoDatabase mongoDataBase;
Aqui Crei a classe EstabelecimentoRepository e utilizei a annotation@Repository que diz ao Spring que essa classe faráa administraço com o banco,Aqui jáadicionei o MongoClient que faráo registro do Codec e faráa conexãao banco e o MongoDatabase queéquem seráresponseável por nos trazer a inst–ncia onde poderemos buscar nas nossos collections e fazer as transaçes.Agora vamos abrir a conexão com o banco de dados:
@Value("${host}")
private String host;
@Value("${port}")
private String port;
@Value("${database}")
private String database;
@Value("${collection.estabelecimento}")
private String estabelecimento;
private MongoCollection<Estabelecimento> openConnetion() {
Codec<Document> codec = MongoClient.getDefaultCodecRegistry().get(Document.class);
EstabelecimentoCodec estCodec = new EstabelecimentoCodec(codec);
CodecRegistry registry = CodecRegistries.fromRegistries(MongoClient.getDefaultCodecRegistry(),
CodecRegistries.fromCodecs(estCodec));
MongoClientOptions options = MongoClientOptions.builder().codecRegistry(registry).build();
this.client = new MongoClient(host + ":" + port, options);
this.mongoDataBase = client.getDatabase(database);
return this.mongoDataBase.getCollection(this.estabelecimento, Estabelecimento.class);
}
private void closeConnection() {
this.client.close();
}
Fazendo uso da annotation@Valeu do Spring conseguimos reciperar o valor que est á no 응용 프로그램.ymlcontendohost, 하나의 포트, o 데이터베이스 e o nome da 집합.Então basicamente registramos o Codec e conectamos no Mongo e jápegamos a nossa collection e jádeixamos pronto o método para fechar a conexão.
Agora vamos ao método que vai fazer a busca e agregaço desses dados se baseando na proximidade.
public List<Estabelecimento> searchByGeolocation(Filter filter) {
try {
MongoCollection<Estabelecimento> estabelecimentoCollection = openConnetion();
estabelecimentoCollection.createIndex(Indexes.geo2dsphere("localizacao"));
Point referencePoint = new Point(new Position(filter.getLat(), filter.getLng()));
MongoCursor<Estabelecimento> resultados = estabelecimentoCollection
.find(Filters.nearSphere("localizacao", referencePoint, filter.getDistance(), 0.0)).limit(filter.getLimit()).iterator();
List<Estabelecimento> estabelecimentos = fillEstabelecimento(resultados);
return estabelecimentos;
} finally {
closeConnection();
}
}
private List<Estabelecimento> fillEstabelecimento(MongoCursor<Estabelecimento> resultados) {
List<Estabelecimento> estabelecimentos = new ArrayList<>();
while (resultados.hasNext()) {
estabelecimentos.add(resultados.next());
}
return estabelecimentos;
}
Então aqui abrimos a conexão com o método openConnetion()que nos devove a nossa collection e como queremos fazer uma busca por proximidade adicionamos umíndice e dizemos que o campo localizacaoédo tipo 2dsphere se tivessemos usando Mongo ficaria assim:db.estabelcimento.createIndex({
localizacao : "2dsphere"
})
Isso o que fizemoséo que o Mongo nos obriga a fazer se quisermos fazer a busca por geologizao.Em seguida criamos uma classe Point que recebe a lation e a longitude e que seráa baseado no nosso endereço quando passarmos.코모 페가 아나사 위도 e경도?Nãse preocupe que iremos ver mais a seguir no momento sóentenda que teremos esses valores poiséassim que poderemos trabalhar com geologizao.
Agora podemos Disperor a nossa pesquisa:
MongoCursor<Estabelecimento> resultados = estabelecimentoCollection
.find(Filters.nearSphere("localizacao", referencePoint, filter.getDistance(), 0.0)).limit(filter.getLimit()).iterator();
Aqui estáa nossa busca,temos a nossa collection e usamos o método find sóque passamos dentro dele os nossos filtros para a agregaço com a classe Filter e seu método estático nearSphere que ele recebe o campo onde ele vai fazer a busca que no casoélocalizacao ponto de referencecia que o nosso referencePoint que nós criamos com a lation e longitude,a máxima dist–ncia,em metros,da nossa pesquisa e a mínima dist–ncia;também passamos o limit de resultados e chamamos o iterator para podermos percorres o que nos voltar do Mongo.Com um Iterator de resultados em mão podemos então percorre o resultado,aqui separai no método fillEstabelecimento que devolve uma lista de estabellecimento:
private List<Estabelecimento> fillEstabelecimento(MongoCursor<Estabelecimento> resultados) {
List<Estabelecimento> estabelecimentos = new ArrayList<>();
while (resultados.hasNext()) {
estabelecimentos.add(resultados.next());
}
return estabelecimentos;
}
서비스
Agora faremos a classe de serviço,que iráexecutar a nossa consulta ao banco através do Repository e que iráser chamada pela nossa Controller que nos passaráo endereço e nós iremos fazer a transformaço dele em lation e longitude,para isso usaremos a depencia Google Maps mas antes disso teremos que nos registrator no GCP-Google Cloud Platform,para isso ser á neces á rio criar um conta lá, n ãse preocupe com isso pois o Google dar á um valor em créditos caso seja seu primeiro registro e após isso n ão cobrar á nada sem seu approvisimento prévio, após criar a conta acesseo Console habilitar a API 지리 인코딩 API:
Após issoénecessário criar uma Apikey para que a sua aplicaão possa se communicar com o serviço que foi habilitado:
Agora a nossa apikey em mão jápodmos começar a criar a CLASS E EstabelecimentoService:
package com.challenge.geolocation.service;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.challenge.geolocation.dto.EstabelecimentoDTO;
import com.challenge.geolocation.filter.Filter;
import com.challenge.geolocation.model.Estabelecimento;
import com.challenge.geolocation.repository.EstabelecimentoRepository;
import com.google.maps.GeoApiContext;
import com.google.maps.GeocodingApi;
import com.google.maps.GeocodingApiRequest;
import com.google.maps.errors.ApiException;
import com.google.maps.model.GeocodingResult;
import com.google.maps.model.Geometry;
import com.google.maps.model.LatLng;
@Service
public class EstabelecimentoService {
@Autowired
private EstabelecimentoRepository repository;
@Value("${apikey}")
private String apikey;
public List<EstabelecimentoDTO> procuraEstabelecimentosProximoAMim(String endereco, String distance, String limit) {
GeoApiContext context = new GeoApiContext.Builder().apiKey(apikey).build();
GeocodingApiRequest request = GeocodingApi.newRequest(context).address(endereco);
try {
GeocodingResult[] results = request.await();
GeocodingResult resultado = results[0];
Geometry geometry = resultado.geometry;
LatLng location = geometry.location;
List<Estabelecimento> estabelecimentoList = repository.searchByGeolocation(
Filter.toFilter(location.lat, location.lng, Double.valueOf(distance), Integer.valueOf(limit)));
List<EstabelecimentoDTO> dtoList = estabelecimentoList.stream().map(estabelecimento -> {
return EstabelecimentoDTO.toDTO(estabelecimento);
}).collect(Collectors.toList());
return dtoList;
} catch (ApiException | InterruptedException | IOException e) {
e.printStackTrace();
}
return List.of();
}
}
Criamos o método procuraEstabelecimentosProximoAMim que recebe o endereco,a distance e o limit,em seguida Criamos GeoApiContext usando a nossa apikey fazemos a nossa requisiço para a o serviço do Google passando nosso endereço como estamos fazendo tudo isso de forma síncrona ficamos esperando returno do serviço externo,isso por si sópode ocasionar muitos problem a então todo o método wait lança Exceptions que aquinão vamos nos aprofundar tratando as.Após o returno pegamos o resultado e navegamos no objeto de returno atéchegarmos onde queremos queéna location queéonde ele guarda a lation e a longitude e agora podemos chamar o nosso Repository que iráexecutar a nossa busca.
필터
Um ponto de observaão,vocêdeveter notado a classe Filter e o método toFilter na nossa Service e na Repository o Filter com getLat,getLng,getDistance e getLimit.Ele nos ajuda a não passar um valor muito grande variáveis na chamada de um método,segue:
@Getter
public class Filter {
private Filter() {}
private double lat;
private double lng;
private double distance;
private int limit;
public static Filter toFilter(double latitude, double longitude, double distance, int limit) {
Filter filter = new Filter();
filter.lat = latitude;
filter.lng = longitude;
filter.distance = distance == 0 ? 1000.0 : distance;
filter.limit = limit == 0 ? 10 : limit;
return filter;
}
}
컨트롤러
Agora iremos criar nossa 컨트롤러 onde receberemos a requisião e devolveremos o resultado da pesquisa.
package com.challenge.geolocation.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.challenge.geolocation.dto.EstabelecimentoDTO;
import com.challenge.geolocation.service.EstabelecimentoService;
@RestController
@RequestMapping("api")
public class EstabalecimentoController {
@Autowired
private EstabelecimentoService service;
@GetMapping("estabelecimento")
public List<EstabelecimentoDTO> pegaEstabelecimentosProximosPeloEndereco(
@RequestParam(name = "limit", defaultValue = "10") String limit,
@RequestParam(name = "distancia", defaultValue = "1000.00") String distancia,
@RequestParam("endereco") String endereco) {
return service.procuraEstabelecimentosProximoAMim(endereco, distancia, limit);
}
}
Temos aqui a nossa EstabalecimentoController com unm método pegaEstabelecimentosProximosPeloEndereco e os par–metros limit com um valor default de 10 caso não seja specíficado na url,distancea com o valor de 1000.00,valor em metros e endereco.Uma coisa interestsanteéque a nossa Controller não devolve o nosso Model pois nãoéconsiderado Uma boa prática e atémesmo um falha de segurança dependedo da situaço então que devolvemos?Devolvemos um DTO(데이터 전송 대상) queéum objeto POJO que só trafega dados como no examplo:
package com.challenge.geolocation.dto;
import com.challenge.geolocation.model.Estabelecimento;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import lombok.Getter;
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
@Getter
public class EstabelecimentoDTO {
private EstabelecimentoDTO() {}
private String nome;
private String email;
private String endereco;
public static EstabelecimentoDTO toDTO(Estabelecimento estabelecimento) {
EstabelecimentoDTO dto = new EstabelecimentoDTO();
dto.nome = estabelecimento.getNome();
dto.email = estabelecimento.getEmail();
dto.endereco = estabelecimento.getLocalizacao().getEndereco();
return dto;
}
}
Aúnica responsibilidade desse DTOétransformar um estabellecimento em um DTO para ser returnado para A Controller e issoéfeito dentro da nossa classe de Service no returno da nossa Repository.최종 결과
테스트 전 준비 작업에는 벽면 보호기, 벽면 보호기 없음:
{
"nome" : "Mercado I",
"email" : "[email protected]",
"localizacao" : {
"endereco" : "Rua Brigadeiro Tobias n 780",
"coordinates" : [
-23.53624,
-46.63395
],
"type" : "Point"
}
}
{
"nome" : "Estabelecimento II",
"email" : "[email protected]",
"localizacao" : {
"endereco" : "R. Brg. Tobias, 206 - Santa Ifigênia, São Paulo - SP, 01032-000",
"coordinates" : [
-23.54165,
-46.63583
],
"type" : "Point"
}
}
{
"nome" : "Estabelecimento III",
"email" : "[email protected]",
"localizacao" : {
"endereco" : "Av. Cásper Líbero, 42 - Centro Histórico De São Paulo, São Paulo - SP, 01033-000",
"coordinates" : [
-23.54132,
-46.63643
],
"type" : "Point"
}
}
{
"nome" : "Estabelecimento IV",
"email" : "[email protected]",
"localizacao" : {
"endereco" : "Av. Rio Branco, 630 - República, São Paulo - SP, 01205-000",
"coordinates" : [
-23.53984,
-46.64008
],
"type" : "Point"
}
}
{
"nome" : "Estabelecimento V",
"email" : "[email protected]",
"localizacao" : {
"endereco" : "Alameda Barão de Limeira, 425 - Campos Elíseos, São Paulo - SP, 01202-900",
"coordinates" : [
-23.53386,
-46.6482
],
"type" : "Point"
}
}
{
"nome" : "Estabelecimento VI",
"email" : "[email protected]",
"localizacao" : {
"endereco" : "R. Canuto do Val, 41 - Santa Cecilia, São Paulo - SP, 01224-040",
"coordinates" : [
-23.54062,
-46.65114
],
"type" : "Point"
}
}
Agora vamos fazer um teste manual usando 불면증 um client para requisi es HTTP,mas vocè usar qualquer um de sua prefència,PostMan,postwomer,cUrle 등.엔토 파잔도 아르쿠시온http://localhost:8080/api/estabelecimento?endereco=R.베어링토바이어스, 247
Temos a reposta:
[
{
"nome": "Estabelecimento II",
"email": "[email protected]",
"endereco": "R. Brg. Tobias, 206 - Santa Ifigênia, São Paulo - SP, 01032-000"
},
{
"nome": "Estabelecimento III",
"email": "[email protected]",
"endereco": "Av. Cásper Líbero, 42 - Centro Histórico De São Paulo, São Paulo - SP, 01033-000"
},
{
"nome": "Mercado I",
"email": "[email protected]",
"endereco": "Rua Brigadeiro Tobias n 780"
},
{
"nome": "Estabelecimento IV",
"email": "[email protected]",
"endereco": "Av. Rio Branco, 630 - República, São Paulo - SP, 01205-000"
}
]
종목 결승전
O 프로젝트 최종vocêpode encontrar aquiaqui
Reference
이 문제에 관하여(스프링 부츠 e MongoDB), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/guilhermegarcia86/spring-boot-e-mongodb-4350텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)