quinta-feira, 13 de novembro de 2014

C#, .NET Open Source, Visual Studio, Window Phone... quanta coisa!

Olá, pessoal!

Sei que faz tempo que não posto nada no blog e não vim aqui para dar desculpas, então... vamos ao que interessa! :D

Neste período que fiquei afastado do blog algumas coisas interessantes aconteceram. E a última delas ocorreu ontem. Se você trabalha com desenvolvimento e mora neste planeta, deve ter visto a notícia de que o .NET é Open Source agora e, inclusive, já está disponível no Github.

Outra excelente notícia da Microsoft, que veio de carona com a do .NET Open Source, foi o lançamento do Visual Studio 2013 Community, que tem todas as características do Visual Studio 2013 "full" e ainda por cima é de uso irrestrito para equipes pequenas (de até 5 pessoas). Para mais detalhes, dê uma olhada aqui.

E por qual motivo eu apareci no blog falando de Microsoft e .NET?

Mais ou menos na época do meu último post no blog, eu já estava estudando um pouco da plataforma do Windows Phone. Havia 2 motivos para isto:

  • Eu estava prestes a trabalhar em um projeto com C# e precisava ficar mais familiar com a linguagem
  • Eu tenho um Lumia 520 com Windows Phone e eu me identifiquei muito com o conceito de simplicidade do sistema operacional (pretendo criar um post sobre isto no futuro!);

Foi então que resolvi "aprimorar" meu C# criando um aplicativo para Windows Phone. O aplicativo é para apontamento de horas trabalhadas, integrado ao sistema de gerenciamento de apontamentos da empresa na qual trabalho, a Dextra. Sei que não é útil para a maioria das pessoas, mas foi uma forma de criar um aplicativo no qual tivesse uso para mim. O código-fonte do aplicativo está disponível no meu Github, sendo que ele ainda está em desenvolvimento!

Bem, este post também é para mantê-los informados da pequena mudança de direção do blog. Nada me impede de voltar a falar dos assuntos que já abordei, como Git e Hibernate, mas creio que mais posts sobre C#, .NET e Windows Phone aparecerão a partir de agora. 

Aliás, falando em C#, recomendo darem uma olhada na linguagem para quem ainda não a conhece. A linguagem por si só já oferecia diversas vantagens sobre o Java e agora, com o .NET tornando-se Open Source, a principal desvantagem de não ser multiplataforma tende a sumir, ainda mais com a ajuda certa do projeto Mono para que isto ocorra o mais rápido possível.

quinta-feira, 22 de maio de 2014

Refatoração e prazos: como conciliar?

Olá, pessoal!

Escrevi um novo texto para o blog da Dextra!

Hoje vou falar um pouco da importância da refatoração (refactoring) de código. Para alguns, um assunto tão natural que não mereceria nem uma discussão longa do seu valor. Mas, infelizmente, ainda temos pessoas que não compreendem totalmente esta técnica de melhoria de código. Porém, hoje não falaremos deste assunto na linha técnica, mas na comportamental.

Leia mais aqui: http://www.dextra.com.br/refatoracao-e-prazos-como-conciliar/

quarta-feira, 30 de abril de 2014

Cuidado ao realizar consultas com JOIN FETCH e WHERE no Hibernate

Olá, pessoal!

Gostaria hoje de falar um pouco sobre os cuidados que precisamos ter ao usar FETCH JOIN no JPA/Hibernate. Apesar de ser um recurso muito útil, a falta do seu completo entendimento pode trazer confusões e surpresas nada agradáveis. Mas que, para nossa sorte, podem ser facilmente contornadas.

Mas antes, uma introdução rápida sobre o que faz um JOIN FETCH em uma consulta. O JOIN FETCH é um recurso do JPA no qual permite que uma única consulta (em JPQL) à determinada entidade também traga outras entidades associadas. Podemos dizer que o JOIN FETCH é uma alternativa ao mapeamento entre entidades com o fetch em EAGER, tendo a grande vantagem de podermos escolher quando vamos aplicar este "EAGER" na entidade.

Vamos a um exemplo. Imagine que temos as entidades Pessoa e Endereco:

@Entity
public class Pessoa {
   @Id
   private Integer id;
   @Column
   private String nome;
   @OneToMany(mappedBy= "pessoa")
   private Set<Endereco> enderecos;
}

@Entity
public class Endereco {
   @Id
   private Integer id;
   @Column
   private String rua;
   @Column
   private Integer numero;
   @JoinColumn("pessoa_id")
   private Pessoa pessoa;
}

Explicação rápida: as entidades Pessoa e Endereço contém um atributo "id", identificador único de cada entidade1. A Pessoa pode ter mais de um Endereco cadastrado, e cada Endereco contém a chave estrangeira para a Pessoa que reside naquele Endereco.

Assim, com o FETCH JOIN, podemos fazer uma consulta na Pessoa que nos traga todos os Endereços dela de uma só vez! É para isto que ele é utilizado. Podemos fazer este JPQL desta maneira:

SELECT pessoa FROM Pessoa pessoa JOIN FETCH enderecos endereco

Agora, temos a vantagem de consultar as pessoas com os seus endereços, não precisando ir novamente no banco de dados para recuperar apenas os endereços!

Mas, se estendermos este exemplo para algo mais elaborado, temos que prestar atenção. Ao escrevermos o JPQL, temos que levar em conta um aspecto especial com o JOIN FETCH.

Digamos que agora você deseja trazer todas as pessoas com endereço no qual a rua contenha a palavra "Alameda". Em uma consulta SQL, escreveríamos:

SELECT * from Pessoa pessoa
JOIN Endereco endereco ON endereco.pessoa_id = pessoa.id
WHERE endereco.rua LIKE "%Alameda%"

Esta consulta traria todas as pessoas que tem endereço no qual a rua contém a palavra "Alameda".

Agora, com a facilidade do JPQL, você quer fazer o mesmo: trazer as Pessoas com Endereco onde a rua contém a palavra "Alameda", mas com todos os seus endereços carregados em uma única consulta, conforme o primeiro exemplo em JPQL. No final das contas, você só quer adicionar um inocente WHERE.

Deste modo, é natural pensar que o JPQL ficaria da maneira abaixo (estou abstraindo o setParameter(), para simplificar):

SELECT pessoa FROM Pessoa pessoa
JOIN FETCH enderecos endereco
WHERE endereco.rua LIKE "%Alameda%"

Esta consulta vai executar? Vai. Trará resultados? Trará. O resultado vai ser o que você queria? Provavelmente não!

Esta consulta irá trazer todas as Pessoas que tem Endereco que contenham a palavra "Alameda" na rua, contudo não irá trazer os demais endereços que não contenham "Alameda" para esta mesma pessoa! Por exemplo, se a Pessoa tiver um Endereco contendo "Alameda" e outro "Avenida", ele vai trazer a Pessoa apenas com o Endereco com "Alameda" no nome da rua.

O que fazer neste caso? Agora vem o "jump of the cat": o correto é fazer outro JOIN sem FETCH. Desta maneira:

SELECT pessoa FROM Pessoa pessoa
JOIN FETCH enderecos
JOIN enderecos endereco
WHERE endereco.rua LIKE "%Alameda%"

Agora a consulta é realizada com a condição no JOIN no qual não tem o FETCH associado. O JOIN com FETCH ficará encarregado de trazer as Pessoas com todos os seus Enderecos. Percebam que também removi o alias do "JOIN FETCH enderecos", que existia no JPQL anterior, pois não é necessário2.

1. Evite o uso de chaves compostas quando puder, pois elas complicam as consultas com o JPA. Se você não for o responsável pelo banco de dados, negocie com o DBA a criação de chaves simples nas tabelas, mesma aquelas que a chave composta pareça a solução correta para o banco, pois a criação de uma constraint ou unique index resolverá na maioria dos casos para o DBA.

2. Alguns frameworks JPA não permitem o alias para JOIN FETCH, exatamente para evitar este problema que ilustramos. O Hibernate permite, mas recomendo não utilizar alias com JOIN FETCH.

sexta-feira, 21 de março de 2014

Dica rápida: como saber se há itens duplicados em um List no Java?

Olá, pessoal!

A pergunta pode parecer muito simples, mas acredite, ela pode ter soluções e desdobramentos interessantes!

Estava eu desenvolvendo normalmente, e apareceu um pequeno problema em que eu precisaria evitar que elementos duplicados estivessem em um List (ArrayList). Quando encontro problemas simples que podem ter diferentes soluções, eu costumo fazer uma pesquisa no Google, no qual costumo sempre parar no StackOverFlow, para saber o quão criativo e eficiente podem ser as soluções de outros desenvolvedores. Sempre aprendo algo novo e, o melhor, normalmente não é com a solução mais votada!

Inclusive, se um dia me acompanharem programando, vão se deparar comigo digitando no Google por soluções para questões tão básicas como esta... =P

Surpreendido após o resultado da minha pesquisa, fiz a pergunta a alguns de meus colegas e pedi para eles dizerem a primeira solução eficiente que vierem à cabeça de cada um. Apareceram várias soluções:

  1. Ordenar a lista e percorrê-la, identificando se o próximo elemento da lista era igual ao atual.
  2. Percorrer a List, adicionando cada elemento em uma nova lista, verificando se o elemento que está sendo adicionado já não existe nesta nova lista.
  3. Transformar a List em um Set, para depois voltar para List .

Acredito que tenham ocorrido mais algumas ainda!

Posteriormente, fiz outra pergunta:

Se eu quiser não removê-los, mas apenas saber se existem elementos duplicados?


Outras soluções apareceram e um deles já indicou uma das formas mais elegantes de o fazê-lo, que é copiar uma List para Set e verificar se o tamanho da List original difere do tamanho do Set.


List<Integer> list = ...;
Set<Integer> set = new HashSet<Integer>(list);

if(set.size() < list.size()){
    /* There are duplicates */
}

Assim, fiz a última pergunta:

E se eu quiser saber qual elemento é o duplicado?


A solução 2 parecia ser a mais correta agora. Mas, o interessante é a forma como podemos implementá-la... Depois que mostrei esta solução para os meus colegas, foi muito interessante ver a reação deles. Por mais que você entenda de coleções em Java, poucos pensariam nisto:


public static <T> boolean hasDuplicate(Iterable<T> all) {
    Set<T> set = new HashSet<T>();
    // Set#add returns false if the set does not change, which
    // indicates that a duplicate element has been added.
    for (T each: all) if (!set.add(each)) return true;
    return false;
}

Eu sei que é simples, mas não é legal? =)

Querem uma outra dica? Façam uma pesquisa sobre remover elementos duplicados de um List preservando a ordem dos elementos (lembrando que qualquer transformação de List para HashSet altera a ordenação).

segunda-feira, 6 de janeiro de 2014

Git rebase vs merge: diferenças e qual escolher em cada situação

Olá, pessoal!

Muito se discute sobre qual é a melhor maneira de realizar o merge das alterações de uma branch para outra com o Git. Como opções, temos o merge e rebase. De fato, ambas tem sua utilidade, mas quando devemos usar uma solução em detrimento da outra?

Primeiro, vamos explicar o que cada uma delas faz.

Comecemos com um exemplo simples. Imagine que você está fazendo commits na branch master com outras pessoas da sua equipe. Para simplificar o cenário1, vocês estão usando o Github para sincronizar todos os commits da branch master, usando-o como repositório principal.

Sendo assim, digamos que existem os seguintes commits neste momento no master do Github.


M1<---M2<---M3


(Podem notar que representei os commits como um grafo não-cíclico, pois é desta maneira que o Git organiza os commits internamente.)

Após fazer o clone do repositório, você faz mais 2 commits locais (C1 e C2). Na sua máquina, o repositório ficará parecido com o seguinte:


                  .---C1<---C2
                 /
M1<---M2<---M3<-´



Contudo, no Gibhub, outros desenvolvedores já fizeram o push de mais 3 commits no master enquanto você tem ainda seus 2 commits que estão apenas no seu repositório local. No master do Github, teremos então a seguinte situação:


M1<---M2<---M3<---M4<---M5<---M6


Agora chegamos ao ponto crucial: como suas alterações farão merge com as alterações dos outros desenvolvedores?

Com o git merge, o Git vai gerar um novo commit que concentra as alterações que ocorreram frutos deste merge dos commits C1 e C2 com os commits M4, M5 e M6.

Se o usuário fizer um git fetch2, irá receber as alterações e terá no seu repositório a seguinte situação:


                  .---C1<---C2
                 /
M1<---M2<---M3<-´---M4<---M5<---M6


Ao fazer o merge, obterá o commit M7, resultado deste merge:


                  .----C1<---C2<----.
                 /                   \
M1<---M2<---M3<-´---M4<---M5<---M6<---`M7



Como podem notar, o conceito é simples. Agora, o que ocorre quando fazemos um rebase?

O git rebase é uma espécie de merge também, mas usa uma lógica diferente. Ao invés de gerar um novo commit, ele reaplica cada um dos commits da branch local "em cima" do último commit da branch remota.

Ou seja, se temos 2 commits (C1 e C2), eles serão aplicados a partir do commit M7.

Sendo assim, ao realizar o rebase, temos a situação:


                                            .----C1’<---C2’
                                           /
M1<---M2<---M3<-´---M4<---M5<---M6<---M7<-´


Notem que chamei o C1 e C2 agora de C1’ e C2’. Motivo? Após o rebase, estes commits não são os mesmos commits originais. Serão commits totalmente novos, até mesmo com o SHA (identificador único e hash) diferentes.

No entanto, o conteúdo destes commits serão muitos similares aos dos originais. Mas, por qual motivo são similares e não exatamente iguais?

Ao realizar o merge de cada commit local (C1 e C2), ocorrerão merges e até conflitos com os commits remotos (M4, M5 e M6), que podem modificar o conteúdo original dos arquivos envolvidos nos commits C1 e C2. O resultado deste merge e resolução de conflitos, portanto, serão o C1’ e C2’.

Entendendo as diferenças entre as 2 opções, quando usar rebase ou merge?


O rebase é muito útil para não “sujar” o histórico do repositório. Se não fizer o rebase, o git quase sempre3 vai criar um novo commit para o merge dos commits locais com os commits remotos. Este commit extra pode parecer inofensivo, mas torna-se um pequeno estorvo ao visualizar e entender a árvore de commits do git entre as várias branches.

No mais, tirando ocasiões especiais, não recomendo o uso do rebase. Como rebase faz um merge para cada um dos commits da branch, o número de merges necessários é mais alto assim como o de conflitos (se ocorrer), e este trabalho aumenta a cada novo commit na branch. Se ainda não estiver convencido que esta opção é custosa, recomendo a leitura deste comentário no StackOverFlow.

Outro contra do rebase, para algumas pessoas, é que quando vocês faz um rebase você está "mentindo" sobre o que está acontecendo no histórico do seu repositório.

Mas o problema mais grave com o rebase não são estes. O rebase, muitas vezes por falta de conhecimento, é usado em branches remotas nos quais há desenvolvedores trabalhando ativamente nelas. Esta é a receita para o desastre. Porém, se ninguém está trabalhando na branch com commits pendentes, o rebase pode ser feito na branch remota de maneira segura.

Depois destas considerações, na maioria dos casos é recomendado usar o merge: único momento de merge de alterações, único momento para resolver conflitos, não há risco de afetar o trabalho de outras pessoas, preserva a ordem real dos eventos no histórico mas com o efeito colateral de poluí-lo um pouco.

O rebase é interessante apenas para ser aplicado no seu repositório local, onde temos normalmente poucos commits e apenas o próprio usuário realiza commits nela. No restante das situações, avalie com muito cuidado e tenha completo entendimento do que irá fazer.

Esta última dica pode ser estendida, inclusive, para qualquer coisa que pretenda fazer com o Git: o entendimento da árvore de commits dele, ao meu ver, é essencial para entendimento e resolução de problemas. Deixe a decoreba de comandos do git para depois.


1. No git não há o conceito de repositório central. O que acontece é que a equipe acaba elegendo um dos repositórios para concentrar as alterações que podem gerar versões do sistema. Quando utiliza-se o Github, o intuito acaba sendo dele virar este repositório.
2. Ao fazer git pull, o git por trás dos bastidores faz exatamente um git fetch (recebe os novos commits do repositório remoto) e em seguida faz o git merge (merge dos commits remotos com os commits locais).
3. Quando é possível fazer um fast-forward.