Revista Eletrônica da Faculdade Metodista Granbery http://re.granbery.edu.br - ISSN 1981 0377 Curso de Sistemas de Informação - N. 10, JAN/JUN 2011 RELACIONAMENTOS ENTRE OS OBJETOS NO FRAMEWORK HIBERNATE Elio Lovisi Filho1 Ronney Moreira de Castro2 RESUMO Este artigo apresenta a implementação de relacionamentos no framework Hibernate, em duas diferentes situações. São apresentadas técnicas e as ferramentas empregadas neste desenvolvimento, em especial o framework Hibernate, a IDE NetBeans, bem como os resultados obtidos durante a realização do processo. PALAVRAS-CHAVE: HIBERNATE, RELACIONAMENTOS, NETBEANS, JAVA ABSTRACT This article presents the implementation of relationships in Hibernate Framework in two different situations. Techniques and the tools used are presented in this development, especially the Hibernate framework, the NetBeans IDE, and the results obtained during the development process. KEY-WORDS: HIBERNATE, RELATIONSHIPS, NETBEANS, JAVA 1 - Mestre em Informática pelo Instituto Tecnológico de Aeronáutica, Professor do curso de Sistemas de Informação da Faculdade Metodista Granbery e do curso de Redes de Computadores da Faculdade Estácio de Sá, e ainda, Analista de Sistemas da Prefeitura de Juiz de Fora. Email: [email protected] 2 - Mestrando em Ciência da Computação pala Universidade Federal de Viçosa, Professor do curso de Sistemas de Informação da Faculdade Metodista Granbery e Analista de Sistemas da Prefeitura de Juiz de Fora. Email: [email protected] 1. INTRODUÇÃO O framework Hibernate fornece ferramentas que permitem o mapeamento objetorelacional (Object-relational mapping – ORM) com o objetivo principal de possibilitar que os objetos de uma determinada aplicação sejam mapeados em entidades de uma base de dados relacional. Este framework pode ser utilizado em programas implementados na linguagem Java, possuindo também uma versão para .Net chamada NHibernate [Bauer e King 2007] . Como o Hibernate é um framework para mapeamento objeto-relacional, ele também deve oferecer suporte para as associações e heranças entre as classes, permitindo também a realização da correspondência das mesmas com relacionamentos entre as tabelas. Sendo assim, o Hibernate permite a realização de diferentes tipos de associações entre as classes, como: um-para-um; um-para-muitos; e muitos-para-muitos. Além disso, pode-se também utilizar a herança no modelo de classes do sistema. Neste artigo, apresenta-se um tutorial sobre a implementação de relacionamentos no framework Hibernate. Para permitir o melhor entendimento, será utilizado como exemplo o software apresentado no artigo de Lovisi e Castro (2009), acrescentando-se ao mesmo uma associação um-para-muitos e uma herança. A aplicação original permite apenas a manutenção de departamentos. Para exemplificar os relacionamentos serão acrescentadas duas novas funcionalidades: a manutenção de funcionários e de departamentos especiais. As ferramentas empregadas no desenvolvimento deste tutorial foram: • a IDE NetBeans 6.9, disponível em http://www.netbeans.org/; e • o framework Hibernate versão 3.6.1, que pode ser obtido em http://www.hibernate.org/. Nos próximos itens serão apresentadas as atividades realizadas para a implementação das funcionalidades citadas. Apresenta-se o código das classes das camadas model, view, control e DAO necessárias para o desenvolvimento do aplicativo. 2. CRIAÇÃO DE NOVAS TABELAS Até então, o sistema utilizado no exemplo possui somente a tabela Departamento. Para apresentar a utilização de relacionamentos, serão criadas as tabelas Funcionário e 2 Departamento Especial, onde um funcionário pertence a um departamento, e um departamento pode ter vários funcionários. Para tanto, deve-se criar as tabelas utilizando o seguinte script SQL CREATE TABLE `funcionario` ( `id` int(11) NOT NULL AUTO_INCREMENT, `cargo` varchar(60) DEFAULT NULL, `dataNascimento` date DEFAULT NULL, `nome` varchar(60) DEFAULT NULL, `departamento` int(11) DEFAULT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`departamento`) REFERENCES `departamento` (`id`) ) ENGINE=InnoDB ; CREATE TABLE `departamentoespecial` ( `id` int(11) NOT NULL, `caracteristica` varchar(60) default NULL, PRIMARY KEY (`id`), CONSTRAINT `departamentoespecial_ibfk_1` FOREIGN KEY (`id`) REFERENCES `departamento` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB; 3. CRIAÇÃO DA CLASSE FUNCIONÁRIO Na aplicação Java, deve-se criar uma nova classe chamada Funcionário no pacote model. Esta classe será empregada para exemplificar uma associação um-para-muitos com a classe Departamento, devendo ter os seguintes atributos: • id do tipo Integer; • cargo do tipo String; • dataNascimento do tipo Date (java.util.Date); • nome do tipo String; e • departamento da classe Departamento. Encapsule os atributos e crie um construtor para a classe. Finalmente, acrescente as anotações nos atributos conforme o fragmento de código abaixo e importe os pacotes necessários. 1. 2. 3. 4. @Entity public class Funcionario implements Serializable { @Id @Column(unique=true) 3 5. 6. 7. 8. 9. 10. 11. 12. 13. @GeneratedValue (strategy=GenerationType.IDENTITY) private Integer id; private String nome; private String cargo; @Temporal(javax.persistence.TemporalType.DATE) private Date dataNascimento; @ManyToOne @JoinColumn(name="departamento") private Departamento departamento; Na linha 1 do código acima, deve-se observar que a anotação @Entity define que a classe Funcionário corresponde a uma tabela de mesmo nome. Já na linha 3, a anotação @Id define que o campo é chave e a anotação @ManyToOne (linha 11) define o relacionamento entre as tabelas, por meio do atributo definido em @JoinColumn (linha 11). Além disso, sobrescreva o método toString() da classe funcionário da seguinte forma: 1. public String toString() { 2. return this.getNome(); 3. } 4. CRIAÇÃO DAS CLASSES DAO E CONTROLADORA DO FUNCIONARIO No pacote DAO, criar uma nova classe chamada DAOFuncionario, a qual herda de DAOGenerico, com um construtor próprio. Lembre-se que as classes DAO devem permitir o acesso da aplicação ao banco de dados, e que os métodos da classe DAOGenerico já implementam genericamente as operações básicas de acesso aos dados [Richardson 2007]. Agora, no pacote control, crie uma nova classe chamada CtrManterFuncionario, com um contrutor próprio. Acrescente o código abaixo à nova classe. 1. DAOFuncionario acessohibernatefunc = new DAOFuncionario(); 2. 3. public int gravarFuncionario(Funcionario funcionario) { 4. try { 5. acessohibernatefunc.gravar(funcionario); 6. return 1; 7. } catch (HibernateException e) { 8. return 2; 9. } 10. } 11. 12. public List carregarFuncionarios() { 4 13. 14. try { return acessohibernatefunc.carregarTudoOrdenado (Funcionario.class, "nome"); 15. } catch (HibernateException e) { 16. return null; 17. } 18. } 19. public boolean excluirFuncionario(Funcionario funcionario) { 20. try { 21. acessohibernatefunc.excluir(funcionario); 22. return true; 23. } catch (HibernateException e) { 24. return false; 25. } 26. } 27. 28. public boolean alterarFuncionario(Funcionario funcionario) { 29. try { 30. acessohibernatefunc.alterar(funcionario); 31. return true; 32. } catch (HibernateException e) { 33. return false; 34. } 35. } Os métodos acima permitem a realização das operações básicas (gravar, excluir, alterar e carregar) para os objetos da classe Funcionário, chamando os métodos que estão implementados na classe DAO. Agora, no método criaSessionFactory() da classe HibernateConfiguracao altere o código conforme apresentado a seguir. 1. 2. 3. 4. 5. 6. if (sessionFactory == null) { AnnotationConfiguration cfg = new AnnotationConfiguration(); cfg.addAnnotatedClass(Departamento.class); cfg.addAnnotatedClass(Funcionario.class); sessionFactory = cfg.buildSessionFactory(); } Observe que a linha 4 foi incluída no código, permitindo que o framework carregue as configurações da classe Funcionario. 5. CRIAÇÃO DO FORMULÁRIO DE FUNCIONÁRIOS Agora, no pacote view, crie um novo formulário Jframe (FrmManterFuncionario) de acordo com a próxima figura. 5 Figura 1: Tela de Manutenção de Funcionários Observe que o nome dos componentes do formulário devem ser os seguintes: • Lista de funcionários: jLstFuncionarios; • Texto para o nome do funcionário: jTxtNome; • Texto para o cargo do funcionário: jTxtCargo; • Texto com máscara (JFormattedTextField) para a data de nascimento do funcionário: jTxtDataNascimento; • Combo para listagem de departamentos: jCbxDepartamento; • Botão para inclusão: jBtnIncluir; • Botão para exclusão: jBtnExcluir; • Botão para alteração: jBtnAlterar. Para a lista de funcionários e a combo de departamentos, a propriedade model deve ser vazia. O campo data de nascimento é do tipo Date. Logo, deve-se apresentar uma máscara no mesmo para que seja inserido o dado. Para tanto, selecione o campo texto e altere sua propriedade Pre-Init Code conforme a próxima figura. 6 Figura 2: Pre-Init Code do campo Data de Nascimento Nesta propriedade inclua o código a seguir: 1. 2. 3. 4. 5. 6. 7. 8. 9. MaskFormatter mascara; try { mascara = new MaskFormatter("##/##/####"); mascara.setValidCharacters("0123456789"); mascara.install(jTxtDataNascimento); } catch(Exception ex) { ex.printStackTrace(); } jTxtDataNascimento.updateUI(); O código anterior acrescenta uma máscara ao campo data de nascimento, no formato dia/mês/ano, aceitando somente números, conforme pode ser visto nas linhas 3 e 4. 7 Agora acesse o código do formulário e inclua os seguintes atributos fazendo as importações necessárias: 1. 2. 3. 4. 5. CtrManterFuncionario ctrManterFuncionario; CtrManterDepartamento ctrManterDepartamento; Departamento depart; Funcionario func; SimpleDateFormat df; No evento windowActivated do formulário, inclua o seguinte código, para carregar a lista de funcionários e a combo box de departamentos. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. //inicia os atributos ctrManterFuncionario = new CtrManterFuncionario(); ctrManterDepartamento = new CtrManterDepartamento(); depart = new Departamento(); func = new Funcionario(); DefaultListModel listModel = new DefaultListModel(); List listFunc = new ArrayList(); listFunc = ctrManterFuncionario.carregarFuncionarios(); if (listFunc != null) { Iterator i = listFunc.iterator(); while (i.hasNext()) { Funcionario funcList = (Funcionario) i.next(); listModel.addElement(funcList); } jLstFuncionarios.setModel(listModel); } //carrega combo de departamentos DefaultComboBoxModel modelCombo = new DefaultComboBoxModel(); List listDept = ctrManterDepartamento.carregarDepartamentos(); if (listDept != null) { Iterator j = listDept.iterator(); while (j.hasNext()) { Departamento deptList = (Departamento) j.next(); modelCombo.addElement(deptList); } jCbxDepartamento.setModel(modelCombo); } //inicializa o formato das datas df = new SimpleDateFormat("dd/MM/yyyy"); df.setLenient(false); Para exibir os dados dos funcionários, implemente o código a seguir no método mouseClicked da lista. 8 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. //recupera o funcionario selecionado func = (Funcionario) jLstFuncionarios.getSelectedValue(); //apresenta os dados do funcionario if (func != null) { jTxtNome.setText(func.getNome()); jTxtCargo.setText(func.getCargo()); jTxtDataNascimento.setText(df.format(func.getDataNascimento())); int cont; //apresenta o departamento do funcionario for (cont = 0; cont < jCbxDepartamento.getModel().getSize(); cont+ +) { if (((Departamento) jCbxDepartamento.getModel(). getElementAt(cont)) .getId(). equals (func.getDepartamento().getId())) 12. 13. 14. 15. jCbxDepartamento.setSelectedIndex(cont); break; } else { JOptionPane.showMessageDialog(null, "Objeto não Encontrado!"); } { 16. Ao selecionar-se um funcionário, deve-se exibir o departamento que ele está associado. Isto é feito no código, entre as linhas 10 e 15, onde percorre-se a lista de departamentos e, se o departamento da lista possuir o mesmo atributo id do departamento associado ao funcionário (condição da linha 11), o índice da lista será posicionado neste valor. Finalmente, na página principal crie um botão para carregar o formulário e teste a aplicação. A tela deverá exibir os funcionários cadastrados no banco. Ao selecionar-se um funcionário, serão exibidos os seus dados nos campos texto e no combo. 6. MANTENDO OS DADOS DOS FUNCIONÁRIOS Agora, as funcionalidades de manutenção de dados do usuário (Incluir, Alterar e Excluir) serão adicionadas à aplicação. Para tanto implemente o código a seguir no evento mouseClicked do botão de inclusão. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. //recupera o departamento selecionado depart = (Departamento) jCbxDepartamento.getSelectedItem(); func = new Funcionario(); //atribui os valores func.setNome(jTxtNome.getText()); func.setCargo(jTxtCargo.getText()); func.setDepartamento(depart); //valida data try { func.setDataNascimento(df.parse(jTxtDataNascimento.getText())); 9 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. } catch (ParseException ex) { JOptionPane.showMessageDialog(this, "Data Incorreta!"); return; } //inclui objeto if (ctrManterFuncionario.gravarFuncionario(func) == 1) { JOptionPane.showMessageDialog(this, "Objeto persistido"); } else { JOptionPane.showMessageDialog(this,"Objeto não persistido"); } No código acima, é mostrada a inserção de objetos da classe Funcionário. Na linha 7, é atribuído o Departamento selecionado ao Funcionário, criando a associação. O tratamento adequado desta associação no banco de dados é feito pelo hibernate. Agora, o código a seguir no evento mouseClicked do botão de alteração: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. //recupera o funcionario e o departamento selecionado func = (Funcionario) jLstFuncionarios.getSelectedValue(); depart = (Departamento) jCbxDepartamento.getSelectedItem(); if (func != null) { //atribui os valores func.setNome(jTxtNome.getText()); func.setCargo(jTxtCargo.getText()); func.setDepartamento(depart); //valida data try { func.setDataNascimento(df.parse(jTxtDataNascimento.getText())); } catch (ParseException ex) { JOptionPane.showMessageDialog(this, "Data Incorreta!"); return; } //altera objeto if (ctrManterFuncionario.alterarFuncionario(func)) { JOptionPane.showMessageDialog(this, "Objeto persistido"); } else { JOptionPane.showMessageDialog(this, "Objeto não persistido"); } } else { JOptionPane.showMessageDialog(this, "Objeto não localizado"); } E finalmente, o código a seguir no evento mouseClicked do botão de exclusão: 1. //recupera o funcionario selecionado 2. func = (Funcionario) jLstFuncionarios.getSelectedValue(); 3. //exclui o funcionario 4. if (ctrManterFuncionario.excluirFuncionario(func)) { 5. JOptionPane.showMessageDialog(this, "Objeto excluido"); 6. } else { 7. JOptionPane.showMessageDialog(this, "Objeto não excluido"); 10 8. } Execute o programa e observe os resultados. Se desejar, limpe os campos da tela após a inclusão, a alteração e a exclusão do objeto. 7. CRIANDO A CLASSE DEPARTAMENTO ESPECIAL Agora, será mostrada a implementação de herança empregando o framework Hibernate. Para tanto, no pacote model, crie uma nova classe chamada Departamento especial. Esta classe será empregada para exemplificar uma herança com a classe Departamento, devendo ter somente o atributo característica do tipo String e um construtor vazio. O código da classe deverá ser implementado conforme apresentado abaixo. 1. 2. 3. 4. 5. 6. @Entity public class DepartamentoEspecial extends Departamento{ private String caracteristica; public DepartamentoEspecial() { } 7. 8. 9. 10. 11. 12. 13. public String getCaracteristica() { return caracteristica; } public void setCaracteristica(String caracteristica) { this.caracteristica = caracteristica; } 14. 15. } Além disso, acrescente a anotação que define a herança na classe Departamento conforme apresentado abaixo e faça as importações necessárias. 1. 2. 3. 4. @Entity @Inheritance(strategy = InheritanceType.JOINED) public class Departamento implements Serializable{ ...} Neste exemplo cada classe corresponde a uma tabela. Outros tipos de herança podem ser empregadas, de acordo com o que for definido na anotação @Inheritance [Bauer e King 2007] . 11 8. CRIAÇÃO DAS CLASSES DAO E CONTROLADORA DO DEPARTAMENTO ESPECIAL Conforme feito no item 4, no pacote DAO, crie a classe DAODepartamentoEspecial herdando de DAOGenerico, com um construtor próprio. Agora, no pacote control, crie uma nova classe chamada CtrManterDepartamentoEspecial, com um construtor próprio. Acrescente o código abaixo à nova classe. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. DAODepartamentoEspecial acessohibernatedepto; public CtrManterDepartamentoEspecial( ){ acessohibernatedepto = new DAODepartamentoEspecial(); } public int gravarDepartamento( DepartamentoEspecial departamento{ try { acessohibernatedepto.gravar(departamento); return 1; } catch (HibernateException e) { e.printStackTrace(); return 2; } } public List carregarDepartamentos() { try { return acessohibernatedepto.carregarTudoOrdenado ( DepartamentoEspecial.class, "nome"); } catch (HibernateException e) { return null; } } public boolean excluirDepartamento( DepartamentoEspecial departamento) { try { acessohibernatedepto.excluir(departamento); return true; } catch (HibernateException e) { e.printStackTrace(); return false; } } public boolean alterarDepartamento( DepartamentoEspecial departamento) { try { acessohibernatedepto.alterar(departamento); return true; 12 37. 38. 39. 40. 41. } catch (HibernateException e) { e.printStackTrace(); return false; } } Os métodos acima permitem a realização das operações básicas (gravar, excluir, alterar e carregar) para os objetos da classe Departamento Especial. Observe que os métodos são semelhantes àqueles desenvolvidos para os controladores de Departamento e Funcionário. 9. CRIAÇÃO DO FORMULÁRIO DE DEPARTAMENTOS ESPECIAIS Novamente no pacote view, crie um novo formulário Jframe (FrmManterDepartamentoEspecial). Observe que o nome dos componentes do formulário devem ser os seguintes: • Lista de departamentos: jLstDepartamentos; • Texto para o nome do departamento: jTxtNome; • Texto para a área do departamento: jTxtArea; • Texto para a característica do departamento: jTxtCaracteristica; • Botão para inclusão: jBtnIncluir; • Botão para exclusão: jBtnExcluir; e • Botão para alteração: jBtnAlterar. Figura 3: Tela de Manutenção de Departamentos Especiais 13 Agora, inclua no formulário os seguintes atributos e faça as importações necessárias. CtrManterDepartamentoEspecial ctrManterDepartamento; DepartamentoEspecial depart; No evento windowActivated do formulário, inclua o seguinte código. 1. DefaultListModel listModel = new DefaultListModel(); 2. List listDepartamento = new ArrayList(); 3. ctrManterDepartamento = new CtrManterDepartamentoEspecial(); 4. depart = new DepartamentoEspecial(); 5. listDepartamento = ctrManterDepartamento.carregarDepartamentos(); 6. if (listDepartamento != null) { 7. Iterator i = listDepartamento.iterator(); 8. while (i.hasNext()) { 9. Departamento deptList = (Departamento) i.next(); 10. listModel.addElement(deptList); 11. } 12. jLstDepartamentos.setModel(listModel); 13. } Para exibir os dados de cada departamento especial selecionado, implemente o código a seguir no método mouseClicked da lista. 1. depart = (DepartamentoEspecial) jLstDepartamentos.getSelectedValue(); 2. if (depart != null) { 3. jTxtNome.setText(depart.getNome()); 4. jTxtArea.setText(depart.getArea()); 5. jTxtCaracteristica.setText(depart.getCaracteristica()); 6. } else { 7. JOptionPane.showMessageDialog(null, "Objeto não Encontrado!"); 8. } Na página principal crie um botão para carregar o formulário e teste a aplicação. Além disso, incluir o mapeamento da classe Departamento Especial no método criaSessionFactory(), conforme a seção 4. 10. MANTENDO OS DADOS DOS DEPARTAMENTOS ESPECIAIS Agora, conforme foi feito para a classe funcionário, serão adicionadas as funcionalidades de manutenção de dados do departamento: Incluir, Alterar e Excluir. Para tanto, implemente o código a seguir no evento mouseClicked do botão de inclusão. 1. depart = new DepartamentoEspecial(); 2. depart.setNome(jTxtNome.getText()); 3. depart.setArea(jTxtArea.getText()); 14 4. 5. 6. 7. 8. 9. depart.setCaracteristica(jTxtCaracteristica.getText()); if (ctrManterDepartamento.gravarDepartamento(depart) == 1) { JOptionPane.showMessageDialog(this, "Objeto persistido"); } else { JOptionPane.showMessageDialog(this, "Objeto não persistido"); } A seguir, o código a seguir no evento mouseClicked do botão de alteração. 1. depart = (DepartamentoEspecial) jLstDepartamentos.getSelectedValue(); 2. if (depart != null) { 3. depart.setNome(jTxtNome .getText()); 4. depart.setArea(jTxtArea .getText()); 5. depart.setCaracteristica(jTxtCaracteristica.getText()); 6. if (ctrManterDepartamento.alterarDepartamento(depart)) { 7. JOptionPane.showMessageDialog(this, "Objeto persistido"); 8. } else { 9. JOptionPane.showMessageDialog(this, "Objeto não persistido"); 10. } 11. } else { 12. JOptionPane.showMessageDialog(this, "Objeto não localizado"); 13. } E finalmente, o código a seguir no evento mouseClicked do botão de exclusão: 1. depart = (DepartamentoEspecial) jLstDepartamentos.getSelectedValue(); 2. if (depart != null) { 3. if (ctrManterDepartamento.excluirDepartamento(depart)) { 4. JOptionPane.showMessageDialog(null, "Objeto Excluído"); 5. } else { 6. JOptionPane.showMessageDialog(null, "Objeto não excluído"); 7. } 8. } else { 9. JOptionPane.showMessageDialog(null, "Selecione o Objeto"); 10. } Novamente, se desejar limpe os campos da tela após a inclusão, a alteração e a exclusão do objeto. 11. CONSIDERAÇÕES FINAIS Neste artigo foi apresentado um tutorial sobre a implementação de relacionamentos no framework Hibernate, acrescentando-se a uma aplicação existente uma associação um-para-muitos e uma herança. A utilização do framework Hibernate permite o mapeamento objeto-relacional, possibilitando uma implementação mais simples de relacionamentos entre as classes. Propõe-se a aplicação, em trabalhos futuros, da ferramenta apresentada para o desenvolvimento de aplicações em outras plataformas, em especial, para softwares Web. 15 Além disso, pode-se aplicar a refatoração nas classes da aplicação melhorando a qualidade do código desenvolvido. 12. REFERÊNCIAS BIBLIOGRÁFICAS BAUER , Christian; KING Gavin.Java Persistence com Hibernate. 1ª ed. Rio de Janeiro: Ciência Moderna. 2007. 872 p. LOVISI FILHO, Elio; CASTRO, Gustavo Mendes. Tutorial sobre a Construção de Aplicações empregando Java, Hibernate e MySql. Revista Eletrônica da Faculdade Metodista Granbery - N. 6, JAN/JUN 2009. Disponível em: < http://re.granbery.edu.br> Acesso em: 21 jan. 2011. RICHARDSON, Chris. POJOS em Ação - Como Desenvolver aplicações Corporativas com Frameworks Leves. 1ª ed. Rio de Janeiro: Ciência Moderna. 2007. 584 p. 16