Relacionamento bidirecional entre classes

Por Paulo Silveira em 28/03/07

Hoje estava pareando com o Lucas Cavalcanti, em um sistema de alunos para a Universidade de São Paulo. Lucas é o novo estagiário da Caelum, e o primeiro da parceria USP-Caelum, onde colocamos um aluno para trabalhar nos projetos da universidade, e ele é orientado, treinado e coordenado pela empresa.

Começamos um projeto novo, e nas primeiras classes de modelo do Hibernate tinhamos uma relação OneToOne. Muitas pessoas ficam em dúvida se devem fazer o relacionamento bidrecional, e isso é um ponto importante a ser considerado não apenas no uso do Hibernate, e sim em qualquer situação.

Para exemplificar, imagine a classe Carro e a classe Motor, sendo que um Carro tem um e apenas um Motor, e o Motor pertence a um único Carro.

Como modelar essas classes? Devemos colocar os atributos correspondentes nas duas classes (relacionamento bidirecional)? Apenas Carro tem de saber qual é o seu Motor? Ou é o Motor quem deve saber sobre o seu Carro? Vamos ver o código no caso de optarmos pelo relacionamento bidirecional:

Motor m = new Motor();
Carro c = new Carro();
c.setMotor(m);
m.setCarro(c);  // hummm….

Repare que se não tivessemos m.setCarro(c) cairíamos em um estado inconsistente para o nosso modelo. Poderia ser ainda pior: imagine alterar o Motor m do nosso Carro c criados anteriormente:

Motor outroMotor = new Motor();
c.setMotor(outroMotor);
// e o Motor m? continua apontando para o Carro c

Agora o antigo Motor m ficou referenciando a um Carro que não o contem, e o outroMotor aponta para Carro algum (null)! Claro que temos como tentar nos precaver dessa situação, mas sempre teremos um código mais complicado e muito sucetível a deixar o estado de seus objetos incosistente.

Imagine agora se essas classes são entidades do Hibernate. Se elas não estiverem concordando a respeito do relacionamento, que lado vai ser considerado na hora de persistir os dados? Quem manda, o Carro ou o Motor? Ou deve ser lançada uma exceção? É para isso que temos os relacionamentos marcados como inverse (os que são mappedBy nas anotações): esse lado do relacionamento não é considerado para a atualização.

Isso tudo pode ser muito mais complicado em relacionamentos 1:N e N:M. O conselho é tentar evitar o relacionamento bidirecional, e nunca cria-lo sem uma real necessidade, assim como já comentamos sobre evitar herança e evitar getters e setters.

Algumas pessoas sugerem que as novas linguagens OO deviam suportar esse tipo de auto ajuste da outra ponta do relacionamento, usando sintaxes novas. O quão dependente suas classes são uma das outras, e quanto mais ciclos elas formam, geram um número que é usado como métrica para avaliar a qualidade de um projeto orientado a objetos. O plugin do Eclipse ByeCycle mostra o grafo de relacionamento entre suas classes. Se houver um ciclo, é um sinal de perigo.

No fim da noite terminamos algumas entidades do hibernate e algumas queries do DAO, e os respectivos testes unitários através de um banco H2 criado e derrubado em memória para cada teste. Foi extremamente produtivo e interessante.

WebServices com o XFire

Por Paulo Silveira em 25/03/07

Nessa segunda feira o Fábio Kung estava me contando de algumas vantagens que enxergava no XFire para criar webservices, e eu logo comentei que preferia o JBossWS porque bastava eu colocar um jar no diretório de deploy, conforme já blogamos anteriormente.

Uma das vantagens que ele citou é do Jetty embutido que existe no XFire, que facilita em muito o desenvolvimento, sem a necessidade de fazer deploys ou reiniciar o JBoss. Considere o bean, o mesmo do post sobre o JBossWS, que quero expor como um webservice:

@WebService
public class AgenteDeReservaBean {
  @WebMethod
  public boolean reserva(@WebParam(name = "nome"String nome, 
              @WebParam(name = "voo"String voo) {

    // logica de negocios, ou delegacao para o BO
    return false
  }
}

Agora, para verificar o WSDL gerado e fazer o deploy do serviço e até mesmo invocá-lo, basta seguir o seguinte código:

public class LigaJettyDoXFire {
   public static void main(String[] argsthrows Exception {
       XFire xfire = XFireFactory.newInstance().getXFire();

       AnnotationServiceFactory factory = 
              new AnnotationServiceFactory(xfire.getTransportManager());
       Service service = factory.create(AgenteDeReserva.class);
       xfire.getServiceRegistry().register(service);

       XFireHttpServer server = new XFireHttpServer(xfire);
       server.start();
   }
}

Através do XFireHttpServer o XFire inicia um jetty embutido rodando na porta 8081. Acessando localhost nessa porta você terá uma lista dos serviços registrados que foram expostos pelo XFire. A velocidade de inicialização do servidor é simplesmente impressionante. Essa dica do Fábio já aplicamos em um projeto nessa mesma semana, e diminuiu bastante o tempo gasto para desenvolvimento e testes.

O Fábio também me mostrou o recurso do XFire fazer uma invocação dinâmica, sem necessidade de stubs. Ainda não fiz nenhum teste, até porque não sei como ele vai serializar em XML uma array de objetos para que eles estejam conforme os complex types utilizados no WSDL. Agora é só torcer para que o gsoap não tenha problemas para gerar os stubs, pois quem vai consumir esse serviços são pequenos dispositivos que rodam código escrito em C.

É impressionante como diversas ferramentas e frameworks clássicos da Apache, como nesse caso o Axis, perdem cada vez mais espaço para os projetos concorrentes pertencentes aos outros grupos, tais como o opensymphony, codehaus e JBoss. Apache que se cuide…

Guia rápido de migração EJB2 para EJB3

Por Paulo Silveira em 19/03/07

Migrar um sistema de EJB2 para EJB3 poderá ser um trabalho muito requisitado no futuro. A especificação do EJB3 facilita muito essa tarefa.

Considere um session bean 2.x chamado ServicoBean, com sua interface remota Servico e a interface home remota ServicoHome:

public interface ServicoHome extends EJBHome {
  Servico create() throws RemoteException, CreateException;
}

public interface Servico extends EJBObject {
  void executa() throws RemoteException;
}

public class ServicoBean implements SessionBean {

  public void executa() {
    // executa a logica de negocios
  }

  public void ejbActivate() {
  }

  public void ejbPassivate() {
  }

  public void ejbRemove() {
  }

  public void setSessionContext(SessionContext sc) {
  }
}

Repare que só poderemos alterar a nossa classe ServicoBean! A alteração das duas interfaces é inadmissível, para que não tenhamos de mudar os possíveis vários clientes desse EJB. Vamos mudar então o ServicoBean, começando por anota-lo como um @Stateless session bean e informando quais são suas interfaces (em vez de anotar as interfaces, o que é a opção mais interessante no EJB3). Além disso não implementamos mais a interface SessionBean, já que qualquer método de callback pode ser definido através de anotações:

@Stateless
@Remote(Servico.class)
@RemoteHome(ServicoHome.class)
public class ServicoBean {
}

Infelizmente aqui não podemos implementar a interface Servico, como ocorreria em um EJB3 comum, por causa de um dos mesmos motivos que um session bean 2.x não deve implementar sua interface remota: teríamos de escrever os métodos encontrados em EJBObject (além de não ter sentido semântico, mas isso foi quebrado no EJB3).

Precisamos então adicionar nossos métodos de negócio com cuidado, para não ocorrer “spec violations” durante o deploy:

@Stateless
@Remote(Servico.class)
@RemoteHome(ServicoHome.class)
public class ServicoBean  {}
  public void executa() {
    // executa a logica de negocios
  }
}

No caso de um Session Bean Stateful, teríamos ainda o ejbCreate. Para isso basta anotarmos um ou mais métodos com @Init. Esse método pode ter qualquer nome, é pela assinatura do create na Home e pela anotação que o container descobrirá qual invocar.
Pronto! Você pode usar seus clientes antigos normalmente, e agora você não precisa mais do ejb-jar.xml no jar desse seu ejb e pode contar com todos os recursos da nova especificação.

Os entity beans também podem ser facilmente migrados através dos VOs, desde que você tenha seguido as boas práticas e não os esteja acessando via interface remota.

JSR-310: Date and Time API

Por Fabio Kung em 15/03/07

Já faz algumas semanas, mas é com muito prazer que comunico oficialmente: faço parte do Expert Group da JSR-310: Date and Time API. JSR vem de Java Specification Request e os Experts são o grupo que define a especificação e cria a implementação de referência.

Um dos líderes da especificação é o também brasileiro Michael Nascimento (aka mister_m). O outro líder é o criador da Joda Time: Stephen Colebourne. O que leva a concluir que a nova especificação de datas do Java vai ser fortemente baseada na Joda Time.

Depois de tanto tempo trabalhando com Java, é uma satisfação muito grande poder contribuir com a evolução da plataforma. A satisfação aumenta ainda mais sabendo da importância desta especificação para todos nós que já sofremos com a java.util.Date e com a java.util.Calendar. Assim que soube da submissão da proposta, corri para ver se poderia ajudar!

A especificação vai correr da maneira mais aberta/transparente possível. A página de desenvolvimento está hospedada no java.net. Lá você pode assinar as listas de discussão e dar uma olhada no wiki, onde estarão os principais detalhes de andamento do desenvolvimento.

Não posso deixar de chamar a atenção para o fato de que qualquer pessoa ou empresa pode fazer parte do Java Community Process (JCP) e ajudar a definir os rumos do Java. Mais informações podem ser encontradas no próprio site do JCP. Nem preciso dizer que inglês é imprescindível, não é?

Além disso deixo aqui neste post também um apelo. Essa JSR afeta grande parte das aplicações, já que grande parte lida de alguma forma com datas e horários. Por isso, muito provavelmente você também será beneficiado. Se tiver alguma idéia, sugestão, crítica ou vontade, eu ficaria muito feliz em ouvir: fabio.kung@caelum.com.br. Participe!

JPA com Hibernate: Herança e Mapeamentos

Por Paulo Silveira em 04/03/07

Essas semanas postarei algumas dicas rápidas no uso da JPA com Hibernate. São alguns pontos que sempre aparecem no desenvolvimento, referentes a performance, elegância e facilidades. Alguns tópicos já são de amplo conhecimento dos usuários do Hibernate, porém na JPA alguns deles são utilizados diferentemente.

Cuidado com herança por InheritanceType.JOINED

Aqui na Caelum, conforme discutido anteriormente, usamos herança com muito critério. Herança na JPA é mapeada com @Inheritance(strategy=InheritanceType.SINGLE_TABLE) por padrão, isto é, ele vai utilizar uma única tabela para guardar todos os dados de todas as classes filhas: não há normalização e uma coluna (o discriminator, por default DTYPE no Hibernate) será utilizada para distinguir entre as possíveis subclasses.

Muitos administradores de banco de dados reclamam dessa estratégia, sendo que a mais elegante é a InheritanceType.JOINED, onde cada classe terá uma tabela, mas sem repetir colunas. As tabelas que representam as classes filhas possuem uma chave estrangeira para a tabela que representa a mãe, normalizando o banco nesse aspecto.

O grande problema dessa estratégia são as queries polimórficas: no caso de você possuir uma classe mãe Pessoa e duas filhas PessoaFisica e PessoaJuridica, ao procurar por uma Pessoa pela sua chave primária o hibernate vai gerar um join entre todas essas tabelas (pode variar um pouco de acordo com o dialeto usado), já que não possuímos o discriminator nesse caso.

Mapear resultados em vez de trabalhar com List<Object[]>

Se executamos uma Query em que nosso select não escolhe apenas um tipo de valor a ser retornado, recebemos uma List<Object[]> como resultado. Por exemplo, um select e1.atributoInteiro, e2.atributoString from Entidade1 as e1, Entidade2 as e2 where... retornaria uma List<Object[]> em que, para cada item da lista, a primeira posição da array é um Integer referente ao atributoInteiro da Entidade1 e a segunda posição é uma String referente ao atributoString da Entidade2.

Tanto a JPA quanto o Hibernate permitem retornar qualquer tipo de objeto através da clausula de select, utilizando um construtor. Podemos mudar a query anterior para select new br.com.caelum.Bean(e1.atributoInteiro, e2.atributoString) from Entidade1 as e1, Entidade2 as e2 where... para receber um List<AlgumBean> como resultado, desde que a classe br.com.caelum.AlgumBean possua um construtor que receba um Integer e uma String. Muito mais elegante que precisar percorrer uma List<Object[]> e encher nosso código de castings. Excelente parar gerar relatórios e já devolver os dados organizados em beans específicos.

Para mapeamentos mais avançados o Hibernate possui a interface ResultTransformer e sua factory Transformers. A JPA define a SQLResultSetMapping e outras anotações, mas para o uso de native queries.