top of page

Search

81 itens encontrados para ""

  • Introdução ao Lombok

    O projeto Lombok é uma biblioteca Java que permite criar um código Java utilizando anotações que permitem substituir a criação de getters e setters, métodos equals, builders e entre outros. Para os exemplos a seguir, iremos utilizar como build tool o Maven. Maven org.projectlombok lombok 1.18.16 provided Estamos utilizando o escopo do tipo provided que significa que o Lombok será configurado em tempo de compilação. Vamos criar uma classe chamada Customer e nela iremos criar alguns campos, construtores, métodos getters e setters e um builder. Tudo isso de forma manual. E em seguida vamos recriar esta classe utilizando os recurso do Lombok. Classe Customer import java.util.Date; public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; public Customer(){ } public Customer(Long id, String firstName, String lastName, Date birthDate){ this.id = id; this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } } Entendendo a classe Customer Na classe Customer criamos: 4 campos 2 Construtores, sendo 1 default métodos getters e setters para cada campo Perceba que uma quantidade razoável de código foi criado. Recriando a classe Customer utilizando Lombok import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; @AllArgsConstructor @NoArgsConstructor @Data public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; } Agora perceba a diferença entre o primeiro e este último. Muito mais limpo e elegante. Entendendo as anotações utilizadas @AllArgsConstructor Esta anotação permite substituir a criação de um construtor como por exemplo: public Customer(Long id, String firstName, String lastName, Date birthDate){ this.id = id; this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; } Assim, caso seja necessário adicionar um novo campo, não é necessário declará-lo no construtor. O Lombok faz este trabalho. @NoArgsConstructor Já está anotação, substitui o construtor default, ou seja, não te obriga a passar campos como parâmetros no construtor ao criar um novo objeto. Por exemplo: public Customer(){} @Data Esta é uma das anotações mais interessantes. Ela permite a não necessidade da criação do métodos getters e setters, como foi criado no primeiro exemplo de código da classe Customer. Criando a classe Customer utilizando Builder de forma tradicional O Builder é um padrão de projeto (padrão de criação) utilizado bastante em código Java para a criação de objetos. No próximo código vamos implementar um builder de forma manual. import java.util.Date; public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; private Customer(){} public static class Builder { private Long id; private String firstName; private String lastName; private Date birthDate; public Builder id(Long id){ this.id = id; return this; } public Builder firstName(String firstName){ this.firstName = firstName; return this; } public Builder lastName(String lastName){ this.lastName = lastName; return this; } public Builder birthDate(Date birthDate){ this.birthDate = birthDate; return this; } public Customer build(){ Customer customer = new Customer(); customer.id = this.id; customer.firstName = this.firstName; customer.lastName = this.lastName; customer.birthDate = this.birthDate; return customer; } } @Override public String toString() { return "id = " + this.id + " FirstName = " + this.firstName + " LastName = " + this.lastName + " BirthDate = " + this.birthDate; } } Aplicando o Builder na prática import java.util.Date; public class Main { public static void main(String[] args) { Customer customer = new Customer .Builder() .id(1L) .firstName("Jones") .lastName("Paul") .birthDate(new Date()) .build(); System.out.println(customer); } } Resultado id = 1 FirstName = Jones LastName = Paul BirthDate = Sun Dez 28 11:48:55 BRT 2020 Perceba mais uma vez, um código verboso que poderia ser facilmente substituído pelo Lombok. Agora vamos recriar o código acima utilizando o Lombok. Recriando a classe Customer utilizando Builder do Lombok import lombok.Builder; import java.util.Date; @Builder public class Customer { private Long id; private String firstName; private String lastName; private Date birthDate; @Override public String toString() { return "id = " + this.id + " FirstName = " + this.firstName + " LastName = " + this.lastName + " BirthDate = " + this.birthDate; } } Perceba agora a diferença em termos de quantidade de código escrito. Apenas criamos os mesmos campos, reutilizamos o método toString() e adicionamos a anotação @Builder responsável pela criação e substituição de todo aquele código criado anteriormente no primeiro exemplo. Aplicando o Builder utilizando Lombok na prática import java.util.Date; public class Main { public static void main(String[] args) { Customer customer = Customer .builder() .id(1L) .firstName("Jones") .lastName("Paul") .birthDate(new Date()) .build(); System.out.println(customer); } } Perceba que houve uma pequena mudança para executar o código acima. Neste caso não foi preciso criar um objeto utilizando a palavra chave new, apenas invocamos o método builder() e os campos da classe para atribuição. Resultado id = 1 FirstName = Jones LastName = Paul BirthDate = Sun Dez 28 11:48:55 BRT 2020 Prático e útil, certo? É isso, curtiu? Até mais!

  • Escopos do Maven

    Conforme a documentação do próprio Maven, os escopos possuem um papel em limitar as dependências transitivas e o controle de quando um dependência pode ser incluída no classpath do projeto. O Maven provê alguns escopos que podem ser utilizados para configurar as dependências do seu projeto. Neste post, vamos falar de forma resumida dos 5 principais escopos. São eles: compile: Escopo padrão, quando não se especifica nenhum outro escopo. Dependências configuradas com este escopo são disponibilizadas para todo o classpath de um projeto. provided: Basicamente utilizado para dependências que serão adicionadas ao classpath em tempo de compilação e testes. Mas o ponto forte da utilização deste escopo é quando existe uma necessidade em adicionar uma ou mais dependências já existentes dentro de um contexto específico. Na maioria dos casos, isso acontece quando estamos fazendo um deploy de uma aplicação web dentro de um container que já possui dependências em comum. runtime: Esta escopo fala por si. O Maven inclui dependências configuradas como runtime no classpath em tempo de execução (runtime) e testes. test: Escopo utilizado para execução de testes. Normalmente utilizado para bibliotecas como JUnit e Mockito. system: Este escopo é similar ao provided mas com uma diferença. Para a utilização do escopo system, é necessário especificar o caminho do Jar. Lembrando que este escopo está depreciado. Material de estudo Se quer aprender mais sobre o assunto e alcançar um alto nível de conhecimento, recomendo fortemente a leitura do(s) seguinte(s) livro(s): Maven: The Definitive Guide (English Edition) é um livro escrito em inglês que vale muito a pena ter no seu acervo caso você queira entender mais sobre o assunto. Maven é hoje o principal gerenciador de dependências em projetos Java encontrado no mercado, em qualquer projeto seja ele nacional ou internacional, Maven vai estar presente facilitando a instalação de bibliotecas e dando suporte aos builds dos projetos. Recomendo também os seguintes materiais para complementar seus estudos: Use a cabeça: Java é um clássico onde qualquer iniciante no assunto deve ler e estudar. Neste livro você aprenderá sobre os principais pilares de Java como, orientação a objetos, criação de classes, interfaces e seu funcionamento, controlar exceções, objetos genéricos e muito mais. Se quer entrar de cabeça neste mundo, é uma excelente opção! Padrões de Projeto é um livro no qual recomendamos após alcançar uma certa maturidade em programação em geral. Escrito por grandes nomes do assunto e traduzido para o português, Padrões de Projeto ou Design patterns como também são conhecidos, são padrões e estratégias que te ajudará a resolver diferentes desafios arquiteturais, aplicando soluções de forma elegante. São estratégias em que desenvolvedores de todo o mundo tem adotado em seus projetos. Imagina se você precisa atuar em um projeto internacional, caso este projetos tem adotado estratégias de Padrões de projeto, você não terá nenhuma grande dificuldade em entender a dinâmica e a arquitetura do código ou caso contrário, você mesmo poderá aplicar os conhecimentos adquiridos através deste livro. O mais interessante que Padrões de Projeto independem de linguagem de programação, é possível aplicar em qualquer outra linguagem, ou seja, é um conteúdo 100% reutilizável. Java Efetivo escrito por Joshua Bloch e traduzido para o português, é um livro excelente para aqueles que possuem um pouco mais de experiência com Java mas que precisam aprofundar em detalhes mais específicos da linguagem como, quando e como usar específicos Padrões de Projeto ou Design Patterns, utilização de Lambdas e Streams, concorrências e serializações. Para aqueles que possuem mais de experiência, é um excelente material de consulta.

  • Git: Desfazendo commits

    Quando precisamos desfazer algum commit feito de forma incorreta ou por outro motivo. A seguir alguns comandos Git que vão te ajudar a resolver este problema. Utilizando o modo --soft como argumento git reset --soft HEAD~ Neste caso o último commit será desfeito na atual branch (HEAD). O arquivo não será alterado de acordo com as alterações anteriores ao commit. O que vai acontecer é que o arquivo ficará disponível para um novo commit. Se você executar o comando git status Será possível ver o(s) arquivo(s) na seção "changes to be committed", ou seja, o commit foi desfeito e está disponível para ser comitado. Podemos melhorar este comando passando o número de commits em que você deseja desfazer. Por ex: git reset --soft HEAD~3 O número 3 fará com que os últimos 3 commits sejam desfeitos. É bem simples. Utilizando o modo --hard como argumento git reset --hard HEAD~ O modo --hard possibilita que além de desfazer o commit, o arquivo seja alterado com base nas alterações anteriores ao commit. Caso precise desfazer um ou mais commits conforme mostrado no primeiro exemplo, a regra é a mesma. Existem outros modos de reset, veja a documentação oficial do Git

  • Spring Boot com Spring Data + JPA

    O Spring Data é um dos vários projetos do ecossistema Spring que visa facilitar acesso aos dados através da utilização de modelos e sem a necessidade de escrever códigos SQL. Qualquer manipulação de dados pode ser feita através de interfaces Java resultando em um código muito mais compreensível e mais limpo. Criando o projeto Para este projeto vamos usar as seguintes dependências: Spring Data JPA H2 Database Lombok Acesse o site https://start.spring.io selecionando as dependências conforme abaixo Escolha a versão Java que você utiliza e clique em Generate para gerar os arquivos e em seguida faça a importação do arquivo pom.xml para a sua IDE. Maven 4.0.0 org.springframework.boot spring-boot-starter-parent 2.4.4 com.blog.spring.data spring-data 0.0.1-SNAPSHOT spring-data Demo project for Spring Boot 8 org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok Sobre o projeto Neste projeto vamos trabalhar com base na classe Employee. Faremos uma carga inicial para o H2 que é um banco de dados em memória e em seguida vamos explorar mais o Spring Data criando algumas consultas utilizando apenas uma Interface Java. Classe Employee A classe Employee representará o modelo principal do projeto package com.blog.spring.data.springdata.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Data @AllArgsConstructor @NoArgsConstructor @Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; private String position; @Override public String toString() { return String.format("Employee [ID: %o," + "FistName: %s," + "LastName: %s," + "Position: %s]", getId(), getFirstName(), getLastName(), getPosition()); } } Observações sobre a classe acima: Anotação @Data A anotação @Data é um recurso do Lombok que facilita bastante a criação de modelos deixando o código menos verboso. Perceba que não foi necessário criar os getters e setters das propriedades graças ao uso do Lombok. @AllArgsConstructor e @NoArgsConstructor Também são recursos do Lombok @AllArgsConstructor: Possibilita criar um construtor de forma implícita. @NoArgsConstructor: Não obriga a passagem de argumentos para o construtor. @Entity Recurso do JPA que possibilita identificar a classe Employee como uma entidade de banco de dados. @Id Recurso do JPA utilizado para identificar um campo que deverá ser um identificar único no modelo de dados. Em resumo, a classe Employee por ter sido anotada como @Entity representará uma entidade a nível de banco de dados/tabela para a aplicação, e o @Id permite criar o campo id como um identificador único, ou seja, uma chave primária da entidade Employee. @GeneratedValue(strategy = GenerationType.AUTO) Recurso que permite controlar a geração dos valores únicos para o campo Id. Perceba que é necessário escolher a estratégia para gerar estes valores. Isso depende de cada engine de banco de dados, neste caso estamos utilizando o H2, mas caso utilizamos Oracle, a estratégia seria GenerationType.SEQUENCE. Criando o repositório EmployeeRepository No contexto de projeto Java, utilizamos algumas convenções de nomes para identificar recursos, neste campo vamos criar uma classe que servirá como repositório de dados para a entidade Employee. Para trabalhar com Spring Data, os repositórios são bem importantes e devem ser criados como Interfaces Java herdando a Interface CrudRepository veja o código abaixo. package com.blog.spring.data.springdata.repository; import com.blog.spring.data.springdata.model.Employee; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import java.util.List; import java.util.Optional; @Repository public interface EmployeeRepository extends CrudRepository { Optional findById(Long id); List findByFirstName(String firstName); List findByLastName(String lastName); List findByPosition(String position); List findByFirstNameAndPosition(String firstName, String position); } Entendendo em partes: @Repository Identifica esta interface como um repositório no contexto do Spring CrudRepository Herdar (extends) a interface CrudRepository possibilita utilizar os recursos oferecidos pelo Spring Data. Neste caso é necessário passar como parâmetro a entidade (Employee) e o tipo do identificador único da classe Employee, que seria o campo id (Long) Métodos Esta é a parte mais importante deste Post. Perceba que para cada método se inicia com findBy e em seguida o nome do campo da classe Employee combinada ou não com um operador (AND) e um parâmetro. Todos estes métodos equivale a uma consulta SQL, perceba que não foi necessário criar scripts SQL de forma explicita. Isso é um dos recursos mais poderosos quando se utiliza Spring Data. Optional findById(Long id) Retorno: Um objeto do tipo Employee SQL: select * from Employee where id = ? List findByFirstName(String firstName) Retorno: Uma lista de objetos do tipo Employee SQL: select * from Employee where firstName = "?" List findByLastName(String lastName) Retorno: Uma lista de objetos do tipo Employee SQL: select * from Employee where lastName = "?" List findByPosition(String position) Retorno: Uma lista de objetos do tipo Employee SQL: select * from Employee where position = "?" List findByFirstNameAndPosition(String firstName, String position) Retorno: Uma lista de objetos do tipo Employee SQL: select * from Employee where firstName = "?" and position = "?" Creio que agora está mais claro o funcionamento do Spring Data, agora vamos explorar na prática. Criando a classe EmployeeService Nesta classe criaremos a parte negocial onde invocaremos o repositório criado anteriormente. package com.blog.spring.data.springdata.service; import com.blog.spring.data.springdata.model.Employee; import com.blog.spring.data.springdata.repository.EmployeeRepository; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class EmployeeService { static EmployeeRepository employeeRepository; public EmployeeService(EmployeeRepository employeeRepository){ this.employeeRepository = employeeRepository; } public static void populateEmployeeToDataBase(){ List employees = new ArrayList<>(); employees.add(new Employee(1L, "Monica", "Silva", "Director")); employees.add(new Employee(2L, "Jessica", "Reies", "Coordinator")); employees.add(new Employee(3L, "Milena", "Torres", "Dev")); employees.add(new Employee(4L, "Maura", "Sanches", "Dev")); employeeRepository.saveAll(employees); } public void runQueries(){ populateEmployeeToDataBase(); employeeRepository.findById(3L) .ifPresent(System.out::println); employeeRepository.findByFirstName("Monica") .ifPresent(System.out::println); employeeRepository .findByFirstNameAndPosition( "Maura", "Dev") .ifPresent(System.out::println); employeeRepository.findByPosition("Dev") .ifPresent(System.out::println); } } A classe EmployeeService será responsável por popular na base uma lista de objetos do tipo Employee e executar os métodos para acesso a base na classe EmployeeRepository. Executando o código Para a execução do código, vamos criar a classe principal do projeto chamada SpringDataApplication.java, seguindo os padrões de um projeto Spring Boot. package com.blog.spring.data.springdata; import com.blog.spring.data.springdata.service.EmployeeService; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringDataApplication { private static EmployeeService employeeService; public SpringDataApplication(EmployeeService employeeService){ this.employeeService = employeeService; } public static void main(String[] args) { SpringApplication.run(SpringDataApplication.class, args); employeeService.runQueries(); } } Esta classe possui 2 particularidades: A anotação @SpringBootApplication Esta anotação permite identificar esta classe como a principal para o contexto do Spring Boot e será responsável por iniciar a aplicação O método main Método responsável pela inicialização da aplicação e para este tutorial a invocação do método runQueries que criamos anteriormente. Resultado da execução Employee [ID: 3,FistName: Milena,LastName: Torres,Position: Dev] [Employee [ID: 1,FistName: Monica,LastName: Silva,Position: Director]] [Employee [ID: 4,FistName: Maura,LastName: Sanches,Position: Dev]] [Employee [ID: 3,FistName: Milena,LastName: Torres,Position: Dev], Employee [ID: 4,FistName: Maura,LastName: Sanches,Position: Dev]] Todas estas linhas acima são resultados de consultas SQL de forma implícita executadas pelos métodos em que criamos na classe EmployeeRepository. Percebeu o poder do Spring Data sem a necessidade de criar consultas SQL no código? Material de estudo Se quer aprender mais sobre o assunto e alcançar um alto nível de conhecimento, recomendo fortemente a leitura do(s) seguinte(s) livro(s): Spring Microservices in Action (Versão Inglês) cobre os princípios de microserviços utilizando Spring, criação de aplicações Spring Boot utilizando Spring Cloud, resiliência, como fazer o deploy e exemplos da vida real com boas práticas de desenvolvimento. Spring MVC Beginner's Guide: Beginner's Guide (Versão Inglês) cobre os conceitos fundamentais do Spring como a arquitetura, fluxos de requisições, Bean validation, como controlar os fluxos de exceções, utilização de REST e Ajax, Testes e muito mais. Este livro é uma excelente escolha para você que queira entender mais a fundo sobre os fundamentos do Spring. Spring é um Framework Java contendo diferentes projetos, sendo que Spring MVC é um deles. Adquirindo um bom fundamento de Spring MVC, você estará apto para atuar em desafios utilizando qualquer projeto do Framework Spring. Learn Microservices with Spring Boot: A Practical Approach to RESTful Services using RabbitMQ, Eureka, Ribbon, Zuul and Cucumber (Versão Inglês) cobre as principais características do ecosistema Spring utilizando Spring Boot, como a criação de microserviços, arquitetura baseada em eventos, utilização de RabbitMQ como recurso de mensageria, criação de serviços RESTful e muito mais. Este livro é uma excelente escolha para você que deseja conhecer mais sobre o funcionamento e os recursos oferecidos pelo Spring Boot. Use a cabeça: Java é um clássico onde qualquer iniciante no assunto deve ler e estudar. Neste livro você aprenderá sobre os principais pilares de Java como, orientação a objetos, criação de classes, interfaces e seu funcionamento, controlar exceções, objetos genéricos e muito mais. Se quer entrar de cabeça neste mundo, é uma excelente opção! Java Efetivo escrito por Joshua Bloch e traduzido para o português, é um livro excelente para aqueles que possuem um pouco mais de experiência com Java mas que precisam aprofundar em detalhes mais específicos da linguagem como, quando e como usar específicos Padrões de Projeto ou Design Patterns, utilização de Lambdas e Streams, concorrências e serializações. Para aqueles que possuem mais de experiência, é um excelente material de consulta. Curtiu? Até mais!

  • Java: Default Methods

    Default methods: O que é? E quando usar? Default Methods foi uma das novidades adicionadas na versão 8 do Java e possibilita flexibilizar bastante o uso de Interfaces. Para entender melhor a utilidade, é necessário que nós lembremos algumas "dores" em utilizar Interfaces. A Interface é um ótimo recurso quando precisamos definir um contrato e que este seja cumprido por quem o implementa. Até então é bem útil e faz com que a aplicação siga boas práticas de implementação. Mas caso precisamos criar um novo método em uma interface que já é implementada por diversas outras classes? Antes do Java 8, teríamos que implementar este novo método em todas estas classes para que o código não quebre e mesmo assim nem todas estas classes utilizaria este novo método. Lembrando que métodos de uma Interface são assinaturas de um contrato. Para entender melhor, vamos criar alguns exemplos simulando uma destas situações. Veja a Interface chamada CrudInterface public interface CrudInterface { void create(); List read(); void update(); void delete(); } O objetivo desta Interface é criar um contrato para classes que necessitem executar operações de CRUD (Create, Read, Update e Delete) sigam o mesmo padrão de implementação. A seguir vamos criar duas classes que farão a implementação desta Interface. A classe UserDAO public class UserDAO implements CrudInterface { @Override public void create() { System.out.println("Create an User"); } @Override public List read() { return Arrays.asList("user 1","user 2"); } @Override public void update() { System.out.println("Update an User"); } @Override public void delete() { System.out.println("Delete an User"); } } A classe ProductDAO public class ProductDAO implements CrudInterface { @Override public void create() { System.out.println("Create a Product"); } @Override public List read() { return Arrays.asList("Product 1", "Product 2"); } @Override public void update() { System.out.println("Update a Product"); } @Override public void delete() { System.out.println("Delete a Product"); } } As classes ProductDAO e UserDAO são obrigadas a implementar todos os métodos ou assinaturas declaradas na Interface CrudInterface. Apesar desta obrigatoriedade ter um lado positivo, que é manter um padrão de implementação, também possui um lado negativo, que é a não flexibilidade e uma possível quebra de código caso a Interface CrudInterface sofra alguma mudança futuramente. Imagine que além das classes ProductDAO e UserDAO, outras 20 classes também implementam a Interface CrudInterface. Cada vez mais o código está criando uma dependência. Agora, imagine que você precise adicionar um novo método na Interface CrudInterface que nem todas as classes que já implementam esta Interface vão utilizar. No cenário atual, teríamos que implementar este novo método em cada uma delas. Mas é aí que o recurso Default Methods entra pra nos ajudar. Para entender melhor, vamos supor que a classe UserDAO precise de um método que vai remover todos os usuário da base e que o time preferiu criar este novo método na interface CrudInterface pois poderia ser útil para as demais classes. Mas o time precisa garantir que esta alteração não quebre o código, ou seja, que o compilador não obrigue as classes implementarem este novo método. Vamos alterar a Interface CrudInterface utilizando Default Methods public interface CrudInterface { void create(); List read(); void update(); void delete(); default void deleteAll(){ System.out.println("Delete all objects"); } } Adicionamos o método deleteAll( ) que supostamente será responsável por deletar todos os registros de uma entidade em específico. Perceba que é um método concreto dentro de uma Interface, que antes era utilizada apenas para declarar assinaturas abstratas. O ponto principal é a palavra chave default que assegura a utilização de Default Methods em uma Interface. Neste caso, não somos mais obrigados a implementar o método deleteAll( ) para todas as classes que implementam a Interface CrudInterface, ou seja, sem riscos de quebra o código. E por fim, possibilita a implementação de uma assinatura dentro da própria Interface. É claro que este é só uma forma de exemplificar a utilização de Default Methods, mas sugiro uma análise caso você precise criar um método que atenderá poucos cenários. Nem sempre a utilização de Interfaces seja a mais vantajosa. E aí, Curtiu? Até mais!

  • Criando recursos utilizando CloudFormation

    Existem diferentes formas de criar recursos na AWS, você pode criar um Bucket S3, SQS, RDS e dentre vários outros recursos de forma manual. Mas da forma que a sua infra vai crescendo, criar recursos manualmente se torna insustentável, pois chegará um ponto que se torna impossível gerenciar estes recursos. Uma outra forma é utilizando ferramentas IaC - Infrastructure as code que permite criar, gerenciar e provisionar recursos na nuvem com pouco esforço e mais clareza. Na AWS podemos utilizar o CloudFormation para nos ajudar na criação dos recursos em que deseja utilizar. Como funciona A partir de um template no formato JSON ou YAML e em seguida efetuando o upload deste arquivo para o CloudFormation na AWS. Bem simples. Para entender melhor este processo, vamos criar um Bucket S3 e uma fila SQS através do CloudFormation, seguindo o que foi descrito anteriormente, utilizando um template. Existe duas formas de criar um template, você pode utilizar um arquivo JSON ou YAML. Nesse exemplo vamos utilizar um template no formato YAML. Criando o template para um Bucket S3 Resources: S3Bucket: Type: 'AWS::S3::Bucket' DeletionPolicy: Retain Properties: BucketName: blog.data AccessControl: Private BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: "AES256" Para o template acima, utilizamos alguns parâmetros essenciais para a criação do Bucket, a lista completa pode ser consultada na própria documentação da AWS. A seguir, vamos entender de forma resumida o que significa cada parâmetro: S3Bucket: é um identificador dado ao recurso, crie sempre um identificador que faça sentido ao seu contexto Type: Tipo do recurso DeletionPolicy: Existem três opções, explicando de forma resumida: Delete: Caso a stack do CloudFormation seja deletada, todos os recursos relacionados serão deletados. Tome bastante cuidado e entenda dos riscos antes de utilizar esta opção Retain: Utilizando esta opção, você garante que ao deletar uma stack, os recursos relacionados serão mantidos. Snapshot: Opção utilizada para recursos que suportam snapshots, por exemplo: AWS::EC2::Volume AWS::ElastiCache::CacheCluster AWS::ElastiCache::ReplicationGroup AWS::Neptune::DBCluster AWS::RDS::DBCluster AWS::RDS::DBInstance AWS::Redshift::Cluster Nas propriedades, definimos as características do Bucket: BucketName: Nome do bucket. Lembrando que o nome do bucket deve ser único e deve seguir alguns padrões de nome conforme a documentação AccessControl: É o controle de acesso ao Bucket, existe diferentes opções de acesso, segue: Private PublicRead PublicReadWrite AuthenticatedRead LogDeliveryWrite BucketOwnerRead BucketOwnerFullControl AwsExecRead BucketEncryption: São as configurações de criptografia dos objetos do Bucket, neste caso utilizamos o algoritmo AES256 Fazendo o upload e criando o recurso 1. No console da AWS, acesse o CloudFormation 2. Clique no botão Create Stack 3. Selecione como pré-requisito Template is ready 4. Na seção Specify template, selecione Upload a template file, selecione o arquivo criado clicando em Choose file e por último clique no botão Next. Uma nova página será aberta para o preenchimento do nome da stack. 5. Clique em Next e faça o mesmo para as próximas paginas. 6. Por fim, o recurso será criado. Isso pode demorar alguns minutos dependendo do recurso. Perceba que foram criados dois buckets: blog.data: Criado via CloudFormation cf-templates-1nwl4b3ve439n-us-east-1: Bucket criado de forma automática no ato do upload do arquivo no início do processo. Criando o template para uma fila SQS Resources: SQS: Type: 'AWS::SQS::Queue' Properties: QueueName: sqs-blog.fifo ContentBasedDeduplication: true DelaySeconds: 120 FifoQueue: true MessageRetentionPeriod: 3600 Entendendo o template: SQS: Identificador do recurso Type: Tipo do recurso QueueName: Nome da fila SQS. Um detalhe importante é o sufixo .fifo, necessário caso a fila seja do tipo Fifo ContentBasedDeduplication: Garante a não duplicidade das mensagens, funciona apenas para filas do tipo Fifo DelaySeconds: Tempo de delay para cada mensagem (em segundos) FifoQueue: Forma de como a fila gerencia a chegada e saída das mensagens (First-in - First-out) MessageRetentionPeriod: Período em que as mensagens será retidas na fila (em segundos) Fila SQS criada Conclusão O CloudFormation é uma ferramenta exclusiva da AWS para a criação de recursos, ou seja, caso a sua arquitetura seja criado ou mantida com base na nuvem da AWS, o CloudFormation é uma ótima escolha. Caso você precise manter uma flexibilidade entre nuvens, como por exemplo a possibilidade em utilizar a Google Cloud, o Terraform pode ser uma melhor opção como ferramenta IaC. É isso, curtiu? Até mais!

  • Tutorial DynamoDB utilizando AWS SDK e Java

    O DynamoDB é um banco de dados chave-valor e documentos. Indicado para aplicações que necessitam de um super desempenho em leituras e escritas com baixa latência. O DynamoDB pode processar mais de 20 trilhões de solicitações por dia e mais de 20 milhões de solicitações por segundo. A estrutura do DynamoDB é um pouco diferente dos bancos relacionais, não se cria um banco e suas tabelas. Todas as tabelas são criadas de forma descentralizadas. Para conectar nestas tabelas, basta utilizar a SDK da AWS. Não existe um driver JDBC que nem usamos em bancos relacionais para conecta-los. Para este tutorial vamos criar alguns exemplo de acesso ao DynamoDB criando e lendo itens utilizando a AWS SDK, Java como linguagem de programação e Maven como Build Tool. Maven * Adicionamos a dependência do Lombok para auxiliar na criação do modelo a seguir. Criando o modelo Vamos criar o modelo que utilizaremos como uma instância dos itens que serão adicionados no DynamoBD. Entendendo as anotações da classe Book: @Data Anotação do Lombok que substituirá os getters e setters para cada campo. Estamos utilizando o Lombok apenas para deixar o código menos verboso. Não possui relação direta com o DynamoDB. @DynamoDBTable Anotação da SDK da AWS DynamoDB que define a classe como uma tabela destino do DynamoBD. Veja que é possível passar como parâmetro o nome da tabela que será Books. @DynamoDBHashKey Esta anotação define uma chave de partição, de forma resumida, seria uma espécie de chave primária. Para o nosso exemplo, o campo isbn será definida como campo chave na tabela Books no DynamoDB. @DynamoDBAttribute Na prática, cada campo mapeado no modelo será um campo da tabela de destino Books no DynamoDB. A anotação @DynamoDBAttribute pode ser utilizada para caso você precise definir um nome diferente do que foi declarado no modelo. Por ex: O campo publishingCompany será definido na tabela como publishing_company e os demais serão criadas conforme foram declarados no modelo. Nesse caso, mapeamos todos eles para manter um padrão. Existem várias outras anotações que podem ser utilizadas, basta acessar a documentação para entender um pouco mais. Criando a configuração de acesso ao Dynamo O próximo código será utilizado para conectar ao serviço do DynamoDB na AWS e criar uma instância responsável por manipular os itens. Entendendo a classe DynamoConfig: O objeto amazonDynamoDB é uma instância do tipo AmazonDynamoDB que será responsável em recuperar os recursos necessários para o acesso ao DynamoDB. O método getAmazonDynamoDB( ) conectará na AWS através de suas credencias de sua conta na região US-EAST-1. Caso utilize outra região é só alterar o enum Regions. O método getDynamoDBMapper( ) criará um um objeto do tipo DynamoDBMapper. Perceba que ele cria uma instância a partir do objeto amazonDynamoDB, responsável por manter a conexão com o serviço do AWS DynamoDB. Um objeto do tipo DynamoDBMapper permite invocar operações do tipo CRUD, segue alguns dos métodos disponíveis: save load delete query scan scanPage As operações estão disponíveis nesta doc. Criando o DAO A classe BookDAO é a classe que contém os métodos responsáveis em acessar o DynamoBD e manipular os itens. Entendendo a classe BookDAO: Conforme falamos anteriormente, o objeto dynamoMapper fornece as operações necessárias para manipular um item Método save(Book book) persistirá o item na tabela Books Método delete(Book book) é responsável por remover o item da tabela Books Método findByIsbn(String isbn) executa uma consulta nos itens filtrando pela chave isbn. Dentro deste método invocamos o método load(Class clazz, Object hashKey) do DynamoMapper que nos obriga a passar o tipo do objeto e a chave de busca. Executando o código Antes de executar o código, vamos criar a tabela Books utilizando o console do DynamoDB. Acesse o DynamoDB conforme imagem Clique em Criar Tabela e uma nova página será aberta No campo Nome da Tabela, digite Books No campo Chave primária, digite isbn. Para finalizar, clique em Criar. Após executar as etapas anteriores, vamos criar um código que executará um fluxo de criação de um Item, consulta e por fim, vamos deletá-lo. Após executar o código, verifique no console o resultado do fluxo. Paginando uma consulta utilizando scanPage O método scanPage faz parte do pacote de operações disponíveis na classe DynamoDBMapper. Utilizando este método é possível criar consultas mais completas utilizando paginação. No próximo exemplo vamos utilizar este método junto com expressões que pode deixar bem flexível a consulta utilizando diversos filtros. O método acima foi criada para exemplificar uma forma mais dinâmica e completa para fazer consultas no DynamoDB de forma mais segura. Utilize o scanPage ao invés do scan. Pois este faz uma leitura de toda a tabela que pode sair um pouco caro, lembrando que uma das formas de cobrança do DynamoDB são as quantidades de consultas. Imagine uma tabela com milhões de registro, a cobrança pode ser bem alta. É isso, curtiu? Até mais!

  • Java: Streams API - Map

    Desde o Java 8 lançado em 2014 foram adicionados dezenas de novas funcionalidades dentre melhorias na JVM e funções para facilitar a vida do desenvolvedor, pois ele merece. Dentre estas features, estão as Expression Lambda (EL) que foi o ponta pé inicial para a entrada do Java no mundo da programação funcional, melhoria na API de Data e a não necessidade de criar implementações de Interfaces já existentes com a utilização de Default methods. E a outra novidade é a API de Streams, o foco desse post. A Stream API é uma nova abordagem para se trabalhar com Collections deixando o código menos verboso e mais inteligente. A Stream API trabalha com o processamento de dados sob demanda e fornece dezenas de funcionalidades para manipular Collections diminuindo o código e simplificando o desenvolvimento em uma espécie de pipeline que será explicado mais a frente. Vamos criar um Classe representando a entidade Cidade no qual terá como atributos: nome, estado e população. E por fim um método chamado listaCidades que carrega uma lista de objetos do tipo Cidade. public class Cidade { String nome; String estado; long populacao; public Cidade(){} public Cidade(String nome, String estado, long populacao){ this.nome = nome; this.estado = estado; this.populacao = populacao; } public List listaCidades(){ List cidades = new ArrayList(); cidades.add(new Cidade("Hollywood", "CA", 30L)); cidades.add(new Cidade("Venice", "CA", 10L)); cidades.add(new Cidade("Houston", "TX", 14L)); cidades.add(new Cidade("New York", "NY", 21L)); cidades.add(new Cidade("Albany", "NY", 11L)); cidades.add(new Cidade("Rio de Janeiro", "RJ", 14L)); cidades.add(new Cidade("São Paulo", "SP", 90L)); return cidades; } @Override public String toString() { return "Cidade: " + nome + " /Estado: " + estado + " /População: " + populacao; } } Map O Map possibilita converter o objeto no tipo do campo que for passado como argumento List nomes = cidade.listaCidades() .stream() .map(c -> c.nome) .collect(Collectors.toList()); nomes.forEach( c -> System.out.println(c) ); No exemplo acima, no método map foi passado o campo nome do objeto Cidade, neste caso, ao invés de retornar a lista baseado nos objetos do tipo Cidade, será retornado uma lista do tipo String. Saída: Hollywood Venice Houston New York Albany Rio de Janeiro São Paulo Outro exemplo com o campo Cidade.populacao List listaPopulacao = cidade.listaCidades() .stream() .map(c -> c.populacao) .collect(Collectors.toList()); listaPopulacao.forEach( c -> System.out.println(c) ); Saída: 30 10 14 21 11 14 90 Resumindo, o map possibilita um cast para o campo do objeto em que você deseja que seja retornado. Como funciona a pipeline? Seguindo o exemplo anterior, a pipeline é um processo sequencial que se diferencia entre operações intermediárias e finais. No exemplo, a Stream é invocada a partir de uma fonte de dados (lista de objetos do tipo Cidade) que trabalha sob demanda, o método map é uma operação intermediária, ou seja, ela processa os dados até que o método collect é invocado, originando uma operação final. E aí, curtiu? Até mais!

  • Java: Streams API - Sorted

    Desde o Java 8 lançado em 2014 foram adicionados dezenas de novas funcionalidades dentre melhorias na JVM e funções para facilitar a vida do desenvolvedor, pois ele merece. Dentre estas features, estão as Expression Lambda (EL) que foi o ponta pé inicial para a entrada do Java no mundo da programação funcional, melhoria na API de Data e a não necessidade de criar implementações de Interfaces já existentes com a utilização de Default methods. E a outra novidade é a API de Streams, o foco desse post. A Stream API é uma nova abordagem para se trabalhar com Collections deixando o código menos verboso e mais inteligente. A Stream API trabalha com o processamento de dados sob demanda e fornece dezenas de funcionalidades para manipular Collections diminuindo o código e simplificando o desenvolvimento em uma espécie de pipeline que será explicado mais a frente. Vamos criar um Classe representando a entidade Cidade no qual terá como atributos: nome, estado e população. E por fim um método chamado listaCidades que carrega uma lista de objetos do tipo Cidade. public class Cidade { String nome; String estado; long populacao; public Cidade(){} public Cidade(String nome, String estado, long populacao){ this.nome = nome; this.estado = estado; this.populacao = populacao; } public List listaCidades(){ List cidades = new ArrayList(); cidades.add(new Cidade("Hollywood", "CA", 30L)); cidades.add(new Cidade("Venice", "CA", 10L)); cidades.add(new Cidade("Houston", "TX", 14L)); cidades.add(new Cidade("New York", "NY", 21L)); cidades.add(new Cidade("Albany", "NY", 11L)); cidades.add(new Cidade("Rio de Janeiro", "RJ", 14L)); cidades.add(new Cidade("São Paulo", "SP", 90L)); return cidades; } @Override public String toString() { return "Cidade: " + nome + " /Estado: " + estado + " /População: " + populacao; } } Sorted O método Sorted() possibilita a ordenação de uma Collection através da passagem do campo em que deseja ser ordenado. cidadesComPopulacaoMaiorQue20 = cidade.listaCidades() .stream() .sorted(Comparator.comparing(c -> c.nome)) .collect(Collectors.toList()); cidadesComPopulacaoMaiorQue20.forEach( c -> System.out.println(c) ); No método sorted() é utilizado o método comparing da interface Comparator que aceita uma função como parâmetro e em seguida extrai retornando uma chave de ordenação. No exemplo anterior a ordenação é feita pelo campo Cidade.nome em ordem alfabética. Saída: Cidade: Albany /Estado: NY /População: 11 Cidade: Hollywood /Estado: CA /População: 30 Cidade: Houston /Estado: TX /População: 14 Cidade: New York /Estado: NY /População: 21 Cidade: Rio de Janeiro /Estado: RJ /População: 14 Cidade: São Paulo /Estado: SP /População: 90 Cidade: Venice /Estado: CA /População: 10 Como funciona a pipeline? Seguindo o exemplo anterior, a pipeline é um processo sequencial que se diferencia entre operações intermediárias e finais. No exemplo, a Stream é invocada a partir de uma fonte de dados (lista de objetos do tipo Cidade) que trabalha sob demanda, o método sorted é uma operação intermediária, ou seja, ela processa os dados até que o método collect é invocado, originando uma operação final. Material de estudo Se quer aprender mais sobre o assunto e alcançar um alto nível de conhecimento, recomendo fortemente a leitura do(s) seguinte(s) livro(s): Use a cabeça: Java é um clássico onde qualquer iniciante no assunto deve ler e estudar. Neste livro você aprenderá sobre os principais pilares de Java como, orientação a objetos, criação de classes, interfaces e seu funcionamento, controlar exceções, objetos genéricos e muito mais. Se quer entrar de cabeça neste mundo, é uma excelente opção! Padrões de Projeto é um livro no qual recomendamos após alcançar uma certa maturidade em programação em geral. Escrito por grandes nomes do assunto e traduzido para o português, Padrões de Projeto ou Design patterns como também são conhecidos, são padrões e estratégias que te ajudará a resolver diferentes desafios arquiteturais, aplicando soluções de forma elegante. São estratégias em que desenvolvedores de todo o mundo tem adotado em seus projetos. Imagina se você precisa atuar em um projeto internacional, caso este projetos tem adotado estratégias de Padrões de projeto, você não terá nenhuma grande dificuldade em entender a dinâmica e a arquitetura do código ou caso contrário, você mesmo poderá aplicar os conhecimentos adquiridos através deste livro. O mais interessante que Padrões de Projeto independem de linguagem de programação, é possível aplicar em qualquer outra linguagem, ou seja, é um conteúdo 100% reutilizável. Java Efetivo escrito por Joshua Bloch e traduzido para o português, é um livro excelente para aqueles que possuem um pouco mais de experiência com Java mas que precisam aprofundar em detalhes mais específicos da linguagem como, quando e como usar específicos Padrões de Projeto ou Design Patterns, utilização de Lambdas e Streams, concorrências e serializações. Para aqueles que possuem mais de experiência, é um excelente material de consulta. E aí, curtiu? Até mais!

bottom of page