O que fazer ao receber o Caso de Uso NOMEDAENTIDADE CRUD ? Um caso de uso envolve uma regra de negocio com as telas e entidades associadas na operação. No documento é descrito todas as validações de tela e o fluxo do processo. No caso de uso CRUD, as funcionalidades são as Create, Retrieve, Update e Delete, ou seja, a criação de uma tela de pesquisa e uma tela para alterar/inserir os registros. Normalmente estas atividades CRUD são desenvolvidas com ferramentas de geração de código, no entanto para o nosso caso (didático) faremos nós mesmos as estruturas para entender como funciona o fluxo das chamadas. 1 - Começe criando as entidades envolvidas. Cliente, Produto, Fornecedor, Usuario, etc. Se as classes destas entidades ainda não existem começe criando-as. As classes precisam seguir alguns padrões pois utilizam recursos de Annotations, para facilitar a integração com o framework de persistência Hibernate. Estas classes recebem o nome de classes de domínio pois estão diretamente relacionados com regras de negocio. No “caso de uso” você ira receber a estrutura da classe em formato UML (diagrama de class). Se esta é uma tarefa de Laboratorio, você deve utilizar a classe que voce modelou. Diagrama de Classe Modelo de Entidade-Relacionamento (MER) Codigo Java package br.com.twosolutions.simplemodularerp.domain; import import import import java.io.Serializable; java.util.Date; java.util.HashSet; java.util.Set; import import import import import import import import javax.persistence.CascadeType; javax.persistence.Column; javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.Id; javax.persistence.JoinColumn; javax.persistence.ManyToMany; javax.persistence.ManyToOne; import import import import javax.persistence.OneToMany; javax.persistence.Table; javax.persistence.Temporal; javax.persistence.TemporalType; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="GELADEIRAS") public class Geladeira implements VO { private private private private private private private Integer id; String marca; String modelo; String cor; Double volume; Integer voltagem; Date dataRevisao; // N - 1 private GeladeiraCategoria geladeiraCategoria; // 1 - N private Set<Alimento> alimentos; @Id @GeneratedValue(generator="generator") @GenericGenerator(name="generator", strategy = "increment") @Column(name="ID_GELADEIRA") public Integer getId() { return id; } public void setId(Serializable id) { this.id = (Integer)id; } @Column(name="COR") public String getCor() { return cor; } public void setCor(String cor) { this.cor = cor; } @Column(name="MARCA") public String getMarca() { return marca; } public void setMarca(String marca) { this.marca = marca; } @Column(name="MODELO") public String getModelo() { return modelo; } public void setModelo(String modelo) { this.modelo = modelo; } @Column(name="DATA_REVISAO") @Temporal(TemporalType.DATE) public Date getDataRevisao() { return dataRevisao; } public void setDataRevisao(Date dataRevisao) { this.dataRevisao = dataRevisao; } @Column(name="VOLTAGEM") public Integer getVoltagem() { return voltagem; } public void setVoltagem(Integer voltagem) { this.voltagem = voltagem; } @Column(name="VOLUME") public Double getVolume() { return volume; } public void setVolume(Double volume) { this.volume = volume; } @ManyToOne @JoinColumn(name="ID_GELADEIRA_CATEGORIA") public GeladeiraCategoria getGeladeiraCategoria() { return geladeiraCategoria; } public void setGeladeiraCategoria(GeladeiraCategoria geladeiraCategoria) { this.geladeiraCategoria = geladeiraCategoria; } } @ManyToMany(cascade={CascadeType.ALL}) @JoinColumn(name="ID_ALIMENTO") public Set<Alimento> getAlimentos() { return alimentos; } public void setAlimentos(Set<Alimento> alimentos) { this.alimentos = alimentos; } public void addAlimento(Alimento alimento){ if(alimentos == null){ alimentos = new HashSet<Alimento>(); } alimentos.add(alimento); } Como começar: Utilizando o Eclipse, crie a classe dentro do pacote br.com.twosolutions.nomeprojeto.domain Comece fazendo os atributos Gere os Getters/Setter Inclua as Annotations Observações: A classe de dominio criada deve implementar a interface VO. Esta interface é uma interface de infraestrutra, ou seja, serve de padrão para a aplicação de polimorfismo em algumas situações. Todas os atributos da classes são declarados com private e utilizam as classes Wrappers como seus tipos (Integer,Double,Character,etc) Gerar os Getters/Setters, com detalhe para os atributos do tipo Collection que precisam do add Colocar a Annotation @Entity e @Table indicando qual o nome da tabela que a classe esta associada. Annotations é um recurso da versão 1.5, em que é possivel incluir informaçoes adicionais em algumas estruturas. Colocar as Annotations @Column indicando qual a coluna de cada atributo. Para entender melhor para que serve as Annotations e Hibernate visite o site do projeto http://www.hibernate.org Atenção aos imports, pois nestes ficam descritos os respectivos pacotes das Annotations. 2 – Criar a Classe Form (específica do Struts) O Framework Struts 1.2 exige que você crie uma classe que irá armazenar todos os valores que serão utilizados nas telas. Essas classes recebem o nome de ActionForm. São classes simples que possuem os mesmos atributos que a sua classe de domínio. package br.com.twosolutions.simplemodularerp.form; import java.io.Serializable; import java.util.Date; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import br.com.twosolutions.simplemodularerp.util.DateUtils; import br.com.twosolutions.simplemodularerp.util.MonetaryUtils; public class GeladeiraForm extends GenericForm { private private private private private private private private private private Integer id; String marca; String modelo; String cor; Double volume; String volumeStr; Integer voltagem; String voltagemStr; Date dataRevisao; String dataRevisaoStr; // N - 1 private Integer geladeiraCategoriaId; public void setId(Serializable id) { if(id != null && !id.toString().equals("")){ if(id instanceof Integer){ this.id = (Integer)id; }else{ this.id = new Integer(id.toString()); } } } public Serializable getId() { return id; } public String getCor() { return cor; } public void setCor(String cor) { this.cor = cor; } public String getMarca() { return marca; } public void setMarca(String marca) { this.marca = marca; } public String getModelo() { return modelo; } public void setModelo(String modelo) { this.modelo = modelo; } public Integer getVoltagem() { return voltagem; } public void setVoltagem(Integer voltagem) { this.voltagem = voltagem; } public Integer getGeladeiraCategoriaId() { return geladeiraCategoriaId; } public void setGeladeiraCategoriaId(Integer geladeiraCategoriaId) { this.geladeiraCategoriaId = geladeiraCategoriaId; } public Double getVolume() { return volume; } public void setVolume(Double volume) { this.volume = volume; } public String getVolumeStr() { if(volume != null){ return MonetaryUtils.format(volume); } return ""; } public void setVolumeStr(String volumeStr) { this.volumeStr = volumeStr; } public String getVoltagemStr() { if(voltagem != null){ return '' + voltagem; } return ""; } public void setVoltagemStr(String voltagemStr) { this.voltagemStr = voltagemStr; } public Date getDataRevisao() { return dataRevisao; } public void setDataRevisao(Date dataRevisao) { this.dataRevisao = dataRevisao; } public String getDataRevisaoStr() { if(dataRevisao != null){ return DateUtils.format(dataRevisao); } return ""; } public void setDataRevisaoStr(String dataRevisaoStr) { this.dataRevisaoStr = dataRevisaoStr; } } public ActionErrors validateSalvar(ActionMapping mapping, HttpServletRequest request){ ActionErrors errors = new ActionErrors(); try { volume = MonetaryUtils.parse(volumeStr); } catch (Exception e) { ActionMessage message = new ActionMessage("geladeira.volume.invalido"); errors.add("volumeStr", message); } try { dataRevisao = DateUtils.parse(dataRevisaoStr); } catch (Exception e) { ActionMessage message = new ActionMessage("geladeira.dataRevisao.invalido"); errors.add("dataRevisaoStr", message); } return errors; } Observações : 1 – A classe Form precisa extender de GenericForm. A classe GenericForm, assim como a interface VO, é uma classe de infra-estrutura, específica do projeto. 2 – Adicionar todos os atributos da classe de domínio específica com os mesmos tipos 3 – Para os atributos que não são do tipo String criar um respectivo com o sufixo Str (Ex. para o atributo volume é preciso ter o volumeStr também) 4 – Criar o metodo validateOperacao (Ex. ValidatePesquisar, validateSalvar, validateRemover) que deve conter todas as regras de validação dos dados da tela. 5 – Para entender melhor as classes do Struts visite o site do projeto em http://jakarta.apache.org (Struts 1.2.9) 6 – No metodo de validação é preciso incluir todas as suas regras de validação que seriam realizadas na tela, além de servir de binding para os Str. Toda variavel Str precisa ser convertida para o seu tipo. try { dataRevisao = DateUtils.parse(dataRevisaoStr); } catch (Exception e) { ActionMessage message = new ActionMessage("geladeira.dataRevisao.invalido"); errors.add("dataRevisaoStr", message); } Podem ocorrer situaçoes de validação de regra de negocio além da validação de parse. if (volume < 0) { ActionMessage message = new ActionMessage("geladeira.volume.negativo"); errors.add("volumeStr", message); } Se ocorrer uma exceção é preciso adicionar a descrição da exceção utilizando a classe ActionMessages do Struts. Na sua definição a mensagem de erro na verdade é uma “chave de mensagem”. A verdadeira mensagem de erro esta localizada no arquivo MessageResources.properties. 3 – Registrar suas mensagens no arquivo MessageResources.properties Toda mensagem (ou texto) que utilizarmos no Struts deve ficar registrada neste arquivo de propriedades. O benefício disto é que além de centralizarmos as mensagens, podemos utilizar o recurso de internacionalização do Struts para suportar varias linguas na aplicação. Cada nova propriedade segue o padrão entidade.atributo.operacao=Mensagem geladeira.volume.negativo=O volume nao pode ser negativo 4 – Registrar a classe de dominio no arquivo hibernate.cfg.xml Para que o Hibernate consiga trabalhar com a classe de dominio que foi criada é preciso registra-la no arquivo de configuração do Hibernate (/resources/hibernate.cfg.xml). <mapping class="br.com.twosolutions.simplemodularerp.domain.Entidade" /> 5 – Registrar a classe Form no arquivo struts-config.xml Para poder utilizar a classe de Formulario e assim poder trabalhar com as validações e funcionalidades do Struts é preciso registrar as classes Form no arquivo de configuração do Struts (/WebContent/WEBINF/struts-config.xml). <form-bean name="EntidadeForm" type="br.com.twosolutions.simplemodularerp.form.EntidadeForm"/> 6 – Fazer o arquivo da tela com o nome Entidade.jsp As telas são criadas utilizando tecnologia JSP. Esta é uma mistura de HTML, JAVA, TagLibs Struts e JSTL. Muitos dos recursos utilizados na tela são padronizados para manter uma coerencia de funcionalidades entre as diversas telas do sistema. <%@ include file="/WEB-INF/common/Header.jsp"%> <table> <tr> <td valign="top"> <html:errors/> <html:form action="/Geladeira.do?metodo=salvar"> <table> <tr> <th colspan="2">Geladeira</th> </tr> <tr> <td>Marca</td> <td><html:text property="marca"/></td> </tr> <tr> <td>Modelo</td> <td><html:text property="modelo"/></td> </tr> <tr> <td>Cor</td> <td><html:text property="cor"/></td> </tr> <tr> <td>Data revisao</td> <td> <html:text property="dataRevisaoStr" size="15"/> <input type="image" name="anchorDataRevisaoStr" id="anchorDataRevisaoStr" src="<%=request.getContextPath()%>/img/calendar.gif" onclick="new CalendarPopup().select(this.form.dataRevisaoStr,'anchorDataRevisaoStr','dd/MM/yyyy'); return false;"/> </td> </tr> <tr> <td>Categoria</td> <td><smerp:select property="geladeiraCategoriaId" entity="GeladeiraCategoria" valueProperty="id" labelProperty="descricao"/></td> </tr> </table> <hr/> <table border="0"> <tr> <td colspan="2"> <jsp:include page="/associar/AssociarInclude.jsp"> <jsp:param name="label" value="Alimentos"/> <jsp:param name="property" value="alimentos"/> <jsp:param name="voAssociarType" value="Alimento"/> <jsp:param name="voAssociarMethod" value="addAlimento"/> <jsp:param name="voAssociarDescricao" value="descricao"/> </jsp:include> </td> </tr> </table> <table border="0"> <tr> <td> <html:submit property="btnSalvar" value="Salvar"/> <html:reset property="btnLimpar" value="Limpar"/> <html:button property="btnPesquisar" value="Pesquisar" onclick="pesquisar('Geladeira');"/> </td> </tr> </table> </html:form> </td> </tr> </table> <%@ include file="/WEB-INF/common/Footer.jsp"%> Observações : 1 – Utilizar as Tags do Struts no lugar das de HTML na parte de formularios 2 – Toda propriedade que for multivalorada (Collection, Set, List, etc) utilizar o componente preenchendo com as respectivas propriedades <jsp:include page="/associar/AssociarInclude.jsp"> <jsp:param name="label" value="Alimentos"/> <jsp:param name="property" value="alimentos"/> <jsp:param name="voAssociarType" value="Alimento"/> <jsp:param name="voAssociarMethod" value="addAlimento"/> <jsp:param name="voAssociarDescricao" value="descricao"/> </jsp:include> 3 – Para as propriedades que são datas deve adicionar a parte de montagem do calendario <input type="image" name="anchorDataRevisaoStr" id="anchorDataRevisaoStr" src="img/calendar.gif" onclick="new CalendarPopup().select(this.form.dataRevisaoStr,'anchorDataRevisaoStr','dd/MM/yyyy'); return false;"/> 4 – Para as propriedades que são associações de outras classes utilizar um componente de montagem de combo com dados específicos. <smerp:select property="geladeiraCategoriaId" entity="GeladeiraCategoria" valueProperty="id" labelProperty="descricao"/> 7 – Fazer o arquivo PesquisarEntidade.jsp <%@ include file="/WEB-INF/common/Header.jsp" %> <br/> <html:form action="/Geladeira.do?metodo=pesquisar"> <table border="0"> <tr> <td> <fieldset> <legend><font color='red'>Filtros de Pesquisa</font></legend> <table> <tr> <td>Codigo:<br/> <html:text property="id" size="15"/> </td> </tr> <tr> <td colspan="2" align="left"><html:submit value="Buscar"/></td> </tr> </table> </fieldset> </td> </tr> </table> </html:form> <form method="POST" id="mainForm"> <input type="hidden" name="id"> <table border="0" cellpadding="0" cellspacing="0"> <tr> <th width="1"><hwp:pagedtitle label="#" property="id"/></th> <th width="200"><hwp:pagedtitle label="Marca" property="marca"/></th> <th width="200"><hwp:pagedtitle label="Modelo" property="modelo"/></th> <th width="1">Acao</th> </tr> <c:forEach var="entity" items="${resultadoPesquisaGeladeira}" varStatus="status"> <tr bgcolor="#FFFFFF" onmouseover="this.style.backgroundColor='#FFFF99'" onmouseout="this.style.backgroundColor='#FFFFFF'"> <td align="left" nowrap="nowrap">${entity.id}</td> <td align="left" nowrap="nowrap">${entity.modelo}</td> <td align="left" nowrap="nowrap">${entity.marca}</td> <td> <c:if test="${param.popup != '1'}"> <input type="image" src="img/visualizar.png" onclick="visualizar(this,'Geladeira','${entity.id}');"> <input type="image" src="img/editar.png" onclick="editar(this,'Geladeira','${entity.id}');"> <input type="image" src="img/remover.png" onclick="remover(this,'Geladeira','${entity.id}');"> </c:if> </td> </tr> </c:forEach> <tr> <th colspan="3" nowrap="nowrap"><hwp:pagedlink name="resultadoPesquisaGeladeira" label="Ant" number="-10"/> <hwp:pagedlink name="resultadoPesquisaGeladeira" label="Anterior" number="-1"/> <hwp:spn name="resultadoPesquisaGeladeira"/> Itens <b>${resultadoPesquisaGeladeira_total}</b> <hwp:pagedlink name="resultadoPesquisaGeladeira" label="Proximo" number="1"/> <hwp:pagedlink name="resultadoPesquisaGeladeira" label="Prox" number="10"/></th> </tr> </table> <table border="0" cellpadding="5" cellspacing="5"> <tr> <td> <c:if test="${param.popup != '1'}"> <input class="Btn2" type="button" value="Adicionar" onclick="adicionar(this,'Geladeira');" /> </c:if> <c:if test="${param.popup == '1'}"> <input class="Btn2" type="button" value="Seleciona" onclick="selecionar(this);" /> </c:if> </td> </tr> </table> </form> <%@include file="/WEB-INF/common/Footer.jsp" %> Observaçoes : 1 – Para percorrer as pesquisas é so utilizar o nome “resultadoPesquisaEntidade”. Isso acontece pois quando foi feito a pesquisa é colocado na sessao uma colecao com este nome. 2 – A parte do codigo que aparece <c:if test="${param.popup != '1'}"> serve para quando esta tela for utilizada por um componente de associacao AJAX em que a mesma tela é apresentada em um popup. 8 – Incluir suas telas no menu Para que seja possivel acessar as telas que foram desenvolvidas é preciso adicinar algumas linhas de programação Javascript no arquivo /WebContent/js/menu/menu_data.jsp. with(milonic=new menuname("Exemplo")){ style=windows98style; aI("showmenu=Cargo;text=Cargo;"); aI("showmenu=TipoContato;text=Tipo de Contato;"); aI("showmenu=TipoContatoCategoria;text=Tipo de Contato Categoria;"); aI("showmenu=NOMEDAENTIDADE;text=NOMEDAENTIDADE;"); } with(milonic=new menuname("NOMEDAENTIDADE")){ style=windows98style; aI("text=Adicionar NOMEDAENTIDADE;url=NOMEDAENTIDADE .do?metodo=adicionar;"); aI("text=Pesquisar NOMEDAENTIDADEs;url=NOMEDAENTIDADE .do?metodo=pesquisar;"); } Observaçoes : Na fase de laboratorio suas entidades ficam descritas no menu “Exemplo”.