top of page

Search

86 itens encontrados para ""

  • MySQL: Criando um schema e migrando dados

    Não é raro que em meio a um projeto apareça a necessidade de fazer uma migração de dados. E quando pensamos na arquitetura de micro-serviços, começar separando os dados por schemas pode ser um primeiro passo para deixar o monolito para trás. Vamos lá!! O primeiro passo é criar um novo schema no MySQL, que pode ser feito com o seguinte comando: CREATE SCHEMA new_schema; Pronto! Agora já podemos iniciar a migração para o novo schema. A primeira coisa é copiar a estrutura de todas as tabelas que você deseja migrar. Da seguinte forma: CREATE TABLE new_schema.table_1 LIKE old_schema.table_1; CREATE TABLE new_schema.table_2 LIKE old_schema.table_2; CREATE TABLE new_schema.table_3 LIKE old_schema.table_3; Se fizermos um select agora em uma das tabelas do novo schema, vamos perceber que a tabela está criada exatamente com a mesma estrutura da antiga, porém estão todas vazias. Então, por fim, vamos fazer a migração dos dados. INSERT new_schema.table_1 SELECT * FROM old_schema.table_1; INSERT new_schema.table_2 SELECT * FROM old_schema.table_2; INSERT new_schema.table_3 SELECT * FROM old_schema.table_3; Um lembrete. Antes de fazer a cópia dos dados, é importante “desligar” qualquer aplicação que faça operações na base antiga para evitar perda de dados. Então é isso, seus dados já estão migrados e você já pode começar a usá-los. Até a próxima!!!

  • Java para iniciantes: Lendo e escrevendo arquivos de forma simples

    Existem muitas formas diferentes de manipular arquivos em Java. Algumas soluções podem chegar facilmente a 10 linhas de código (dependendo também da formatação do código) só para ler um arquivo e fazer um print no console. Felizmente, com as evoluções na linguagem através dos anos, essa tarefa se tornou bastante simples. Então, aqui vai um exemplo bem simples para vocês. Vamos lá!!! Para esse exemplo, vamos precisar importar as seguintes classes: import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; Para ler um arquivo de texto por exemplo, podemos usar a seguinte solução: private void read() throws IOException { String filePath = "/tmp/filetoread.txt"; Path path = Paths.get(filePath); Files.lines(path).forEach(line -> System.out.println(line)); } Essa solução vai funcionar em um ambiente linux, por causa do formato da string filePath. Para que funcione em um ambiente windows, podemos simplesmente alterar para: String filePath = "C:\\coffeeandtips\\tests\\filetoread.txt"; Bom, então após ler um arquivo com três linhas de código (que ainda pode ser reduzido para duas), vamos escrever um arquivo de forma bem resumida. private void write() throws IOException { String filePath = "/tmp/filewrited.txt"; Files.write(Path.of(filePath), "Write a phrase.".getBytes()); } Da mesma forma que o exemplo anterior, esse formato de path só irá funcionar no ambiente linux, para executar em ambiente windows é só alterar para: String filePath = "C:\\coffeeandtips\\tests\\filewrited.txt"; Então é isso pessoal. Espero ter contribuído. Até a próxima.

  • 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!

  • Upload de arquivo no S3 com AWS SDK e Java

    A Amazon Web Services AWS é um dos serviços de Cloud Computing mais tradicionais e com mais recursos no mercado. Neste post vamos ensinar como fazer um upload de arquivo para um bucket S3. Neste post vamos criar um código capaz de fazer um upload de arquivo no S3 utilizando Java e a SDK da AWS. Dependências Maven com.amazonaws aws-java-sdk-s3 com.amazonaws aws-java-sdk-bom 1.11.327 pom import Código de Upload import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; import java.io.File; public class UploadService { public void upload() throws Exception { try { String BUCKET = "SEU-BUCKET"; AmazonS3 amazonS3Client = AmazonS3ClientBuilder .standard().withRegion(Regions.US_EAST_1) .build(); File file = new File("imagem.jpg"); PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET, "nome-do-arquivo", file); ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentType("jpg"); putObjectRequest.setMetadata(objectMetadata); amazonS3Client.putObject(putObjectRequest); } catch (Exception e) { throw new Exception(e); } } } No código acima, temos um exemplo simples de upload de arquivos para o S3. Nele estamos enviando um arquivo do tipo .jpg. Passo a passo A variável BUCKET deve ser substituído pelo seu Bucket já criado O objeto amazonS3Client recebe as configurações necessárias para efetuar o upload. Nesta configuração é necessário configurar a região, neste caso estamos utilizando a região US-EAST-1 Em seguida carregamos o arquivo chamado imagem.jpg para o objeto do tipo File para efetuar o upload O objeto putObjectRequest é criado com base no Bucket, nome do arquivo e o objeto do tipo File criado anteriormente. Dessa forma o objeto putObjectRequest possui todas as informações para efetuar o upload. O objeto objectMetadata possibilita configurar detalhes do arquivo, como tipo do conteúdo, header, criptografia e etc. E em seguida ele é setado no objeto putObjectRequest. E por fim, a requisição final utilizando o método putObject que será responsável por fazer o upload do arquivo contendo todas as informações e configurações setadas nos passos anteriores. Agora, acesso o S3 e verifique se o arquivo esteja presente e pronto. É isso, curtiu? Até mais!

  • 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!

  • Schema Merging com Spark e Java

    Schema Merging é uma forma de evoluir schemas adicionando novas colunas através da junção de DataFrames. Imagine que você possui diferentes arquivos parquets com diferentes schemas, e que exista a necessidade em criar um novo schema a partir de todas as colunas destes variados parquets. Podemos resolver este problema em uma simples linha de código, conforme será mostrado a seguir. A seguir vamos criar arquivos parquets com diferentes schemas através de arquivos JSON e em seguida, faremos o merge destes arquivos transformando em um único schema consolidado. Arquivos JSON que usaremos como DataSource: Arquivo user.json {"id":1, "login": "Jonh", "age": 24} {"id":2, "login": "Lucas", "age": 24} {"id":3, "login": "Miesha", "age": 25} {"id":4, "login": "Suzie", "age": 22} Arquivo address.json {"id":1, "city": "Los Angeles", "country": "USA"} {"id":2, "city": "New York", "country": "USA"} {"id":3, "city": "San Louis Obispo", "country": "USA"} Criando o SparkSession SparkConf sparkConf = new SparkConf(); sparkConf.setMaster("local[1]"); sparkConf.setAppName("app"); SparkSession sparkSession = new SparkSession.Builder() .config(sparkConf) .getOrCreate(); Criando os DataFrames Dataset dfUser = sparkSession.read().json("user.json"); Dataset dfAddress = sparkSession.read().json("address.json"); Gerando os Parquets para cada DataFrame No código a seguir, estamos criando os parquets no diretório data/table/ para a partição chamada partition de valor 1 dfUser.write().parquet("data/table/partition=1"); No código a seguir, estamos criando os parquets no diretório data/table/ para a partição chamada partition de valor 2 dfAddress.write().parquet("data/table/partition=2"); Agora, criaremos um novo DataFrame com base nos parquets criados anteriormente executando o Schema Merging Dataset dfMerge = sparkSession .read().option("mergeSchema", true) .parquet("data/table"); O Schema Merging é executado no seguinte trecho option("mergeSchema", true) Por fim, podemos ver o novo schema mergeado através do mergeSchema dfMerge.printSchema(); Resultado root |-- age: long (nullable = true) |-- id: long (nullable = true) |-- login: string (nullable = true) |-- city: string (nullable = true) |-- country: string (nullable = true) |-- partition: integer (nullable = true) Veja que a partir dos DataFrames dfUser e dfAddress foi criado um novo schema consolidando as colunas entre eles. Código completo SparkConf sparkConf = new SparkConf(); sparkConf.setMaster("local[1]"); sparkConf.setAppName("app"); SparkSession sparkSession = new SparkSession.Builder() .config(sparkConf) .getOrCreate(); Dataset dfUser = sparkSession.read().json("user.json"); dfUser.write().parquet("data/table/partition=1"); Dataset dfAddress = sparkSession.read().json("address.json"); dfAddress.write().parquet("data/table/partition=2"); Dataset dfMerge = sparkSession .read().option("mergeSchema", true) .parquet("data/table"); dfMerge.printSchema(); Curtiu? Espero que sim, até mais!

  • Tipos primitivos em Java

    Falar de tipos primitivos pode causar algumas confusões, mas é bem simples de entender. Os tipos primitivos é a forma mais simples de um dado ou variável e não é considerado uma instância de uma classe. 1. Para tipos numéricos long int byte short float double 2. Para tipo texto char 3. Para tipo booleano boolean

  • Converter Date para LocalDateTime

    Conversão simples e rápida de um objeto do tipo Date para um objeto do tipo LocalDateTime LocalDateTime localDateTime = new Date() .toInstant() .atZone(ZoneOffset.UTC) .toLocalDateTime();

  • Consultas com Spark SQL

    Spark SQL faz parte do core do Apache Spark e permite consultas estruturadas dentro do contexto Spark utilizando SQL. Com Spark SQL é possível conectar em diversos datasources como Avro, ORC, JSON, Parquet e dentre outros. Neste tutorial vamos utilizar como datasource um arquivo JSON para mostrar os poderosos recursos do Spark SQL. Maven org.apache.spark spark-core_2.12 3.1.0 org.apache.spark spark-sql_2.12 3.1.0 Configurar o contexto Spark No passo seguinte, criaremos o SparkSession. Pense que neste ponto você pode reutiliza-lo em sua aplicação, então pense em um classe Singleton para alocar este objeto na inicialização. No exemplo a seguir não farei isso para que fique mais prático e simples. public class SparkSql { public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); } } O objeto SparkConf é responsável pela configuração da Session, perceba que é um objeto simples com os atributos appName e master. O atributo master é uma configuração especifica caso a aplicação execute em um cluster, como nesse exemplo é local, então o valor [1] é o suficiente para a execução e por fim o appName é o nome da aplicação. Listagem 1 - Select simples Conteúdo do arquivo produto.json {"id":1, "nome":"arroz", "preco":12.0, "qtde": 50} {"id":2, "nome":"feijao", "preco":7.50, "qtde": 30} {"id":3, "nome":"coca-cola", "preco":5.50, "qtde": 150} {"id":4, "nome":"suco", "preco":3.80, "qtde": 250} {"id":5, "nome":"milho", "preco":1.50, "qtde": 33} {"id":6, "nome":"yogurte", "preco":6.0, "qtde": 15} {"id":7, "nome":"leite", "preco":3.70, "qtde": 250} {"id":8, "nome":"oleo", "preco":5.60, "qtde": 100} public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select * from produto"); sqlFrame.show(); } Neste exemplo acima é uma forma simples de listar todo o conteúdo do arquivo através de um DataFrame. Um DataFrame é basicamente uma coleção de dados distribuídos que se assemelha bastante com uma tabela relacional. Neste trecho é criado uma view temporária com base no Dataframe que foi carregado pela sessão. dataFrame.createOrReplaceTempView("produto"); No próximo trecho é executado uma consulta SQL simples com base na view criada anteriormente. Dataset sqlFrame = session.sql("select * from produto"); Por fim é executado o método .show() que é uma ação do Spark. Este método lista todos os registros da coleção. É possível passar como argumento neste método a quantidade de registros para listagem, o valor padrão é de 20 registros. sqlFrame.show(); Resultado da execução Listagem 2 - Cláusula Where public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select nome, preco " + "from produto " + "where preco >= 5.0"); sqlFrame.show(); } Resultado da execução Listagem 3 - Between public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select " + "nome, preco, qtde " + "from produto " + "where qtde between 10 and 50 "); sqlFrame.show(); } Resultado da execução Listagem 4 - Sum public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select " + "sum(preco * qtde) as total " + "from produto " + "where qtde > 100 "); sqlFrame.show(); } Resultado da execução Conteúdo do arquivo produto.json alterado {"id":1,"nome":"arroz","preco":12.0,"qtde":50,"tipo":"sólido"} {"id":2,"nome":"feijao","preco":7.50,"qtde":30,"tipo":"sólido"} {"id":3,"nome":"coca","preco":5.50,"qtde":150,"tipo":"líquido"} {"id":4,"nome":"suco","preco":3.80,"qtde":250,"tipo":"líquido"} {"id":5,"nome":"milho","preco":1.50,"qtde":33,"tipo":"sólido"} {"id":6,"nome":"yogurte","preco":6.0,"qtde":15,"tipo":"líquido"} {"id":7,"nome":"leite","preco":3.70,"qtde":250,"tipo":"líquido"} {"id":8,"nome":"oleo","preco":5.60,"qtde":100,"tipo":"líquido"} Listagem 5 - Count + group by + having public static void main(String[] args) { SparkConf sparkConf = new SparkConf(); sparkConf.setAppName("spark-sql-app"); sparkConf.setMaster("local[1]"); SparkSession session = SparkSession .builder() .config(sparkConf) .getOrCreate(); Dataset dataFrame = session.read().json("produto.json"); dataFrame.createOrReplaceTempView("produto"); Dataset sqlFrame = session.sql("select " + "tipo, count(tipo) as qtde" + "from produto " + "group by tipo " + "having (tipo = 'sólido') "); sqlFrame.show(); } Resultado da execução Tentei mostrar alguns exemplos simples de como utilizar o Spark SQL e o que ele é capaz, espero ter ajudado no entendimento e até mais. Documentação: https://spark.apache.org/sql/ Github: https://github.com/apache/spark

  • Configurar TTL no DynamoDB

    O TTL (Time to Live) é um mecanismo do DynamoDB que permite controlar a expiração dos items. Dessa forma você não precisa manter grande volume de dados que não serão usados e nem se dar o trabalho de exclui-los manualmente. Como configurar É bem simples a configuração deste mecanismo, segue o passo a passo: 1. O atributo do item deve ser do tipo Number, conforme a documentação da AWS. Observe o campo expiredAt Observe o campo expiredAt, o formato do valor dever ser do tipo Epoch. O formato Epoch pode ser extraído de uma data, neste caso você pode preencher este campo utilizando LocalDateTime, segue o exemplo: LocalDateTime localDateTime = LocalDateTime.of(2020, 02, 02, 12,02,00); long expiredAt = localDateTime.plus(2, ChronoUnit.HOURS).toEpochSecond(ZoneOffset.UTC); System.out.println("Valor final(Epoch): " + expiredAt) No exemplo acima é uma soma de 2 horas á data de 02-02-2020 ás 12:02:00 que resultará em 02-02-2020 ás 14:02:00. Logo em formato Epoch o valor final será: Valor final(Epoch): 1580652120 2. Levando em conta que a sua aplicação já está salvando os items no formato esperado, basta efetuar a última configuração. Na aba Visão geral, na seção Detalhes da tabela selecione a opção Atributo tempo de vida - Gerenciar TTL Ao clicar na opção uma pop-up será aberta e no campo Atributo TTL preencha o nome do atributo que fará o controle. Pronto, basta clicar em continuar que a AWS vai provisionar a alteração para que o mecanismo comece a funcionar em breve. Neste cenário do exemplo, o item mostrado será apagado na data de 02-02-2020 ás 14:02:00. Ou seja, caso precise utilizar algo assim, pense em utilizar a data atual + o tempo de vida do item que você deseja de acordo com o seu negócio.

  • Criando um projeto Spring Boot em 2 minutos

    Uma das características do Spring Boot é diminuir o tempo de configuração para o projeto. Comparado com os projetos Springs tradicionais que necessita criar diversos arquivos de configuração tornando o processo tedioso e demorado. Hoje com Spring Boot é mais fácil. Neste poste mostrarei como gerar um projeto Spring Boot em 2 minutos. Siga os passos: 1. O primeiro passo é acessar o link https://start.spring.io/ A ideia principal deste portal é a possibilidade de gerar um arquivo POM para projetos Maven ou um arquivo Gradle para projetos Gradle com todas as dependências necessárias. Perceba que é possível selecionar a versão do Spring e do Java. 2. Escolha o tipo de projeto, versão e linguagem e preencha os campos na seção Project Metadata 3. Clique no botão ADD DEPENDENCIES para selecionar as dependências para o seu projeto 4. Após selecionar as dependências necessárias, basta clicar no botão GENERATE na tela principal 5. Será gerado um arquivo zipado para download. Agora basta descompactar o arquivo e importar o projeto!

  • SQS: Duplicidade de mensagens na fila

    Evitar mensagens duplicadas na fila pode ser um requisito básico para o modelo do seu negócio. A SQS possui um recurso de "desduplicação" de mensagens bem interessante. Este recurso está presente apenas em filas do tipo FIFO Existe duas formas de configurar: 1. Na configuração da fila, habilite a opção Desduplicação baseada em conteúdo 2. Uma outra forma de configurar seria no ato do envio da mensagem. Neste caso a fila não precisa ser configurada conforme o item 1. Segue um trecho de código Java utilizando a SDK da AWS que exemplifica este cenário: AmazonSQS amazonSQS = AmazonSQSClientBuilder.standard() .withRegion(Regions.US_EAST_1) .build(); SendMessageRequest messageRequest = new SendMessageRequest(); messageRequest.setMessageBody("mensagem"); messageRequest.setMessageGroupId("id-grupo"); messageRequest.setMessageDeduplicationId("id-desduplicação"); messageRequest.setQueueUrl("url-da-fila"); amazonSQS.sendMessage(messageRequest); Neste caso é necessário atribuir um ID que identifica a mensagem como duplicada, conforme o trecho: messageRequest.setMessageDeduplicationId("id-desduplicação"); O código descrito no item 2 pode ser utilizado para o item 1, mas nesse caso, o trecho anterior é opcional. Ponto de atenção Este recurso só funciona quando mensagens de mesmo conteúdo são enviadas em um intervalo de até 5 minutos. Caso o intervalo entre elas seja maior que 5 minutos, a fila receberá a mensagem duplicada.

bottom of page