Upload
others
View
7
Download
0
Embed Size (px)
Citation preview
EstaapostiladaCaelumvisaensinardeumamaneiraelegante,mostrandoapenasoqueénecessárioequandoénecessário,nomomentocerto,poupandoo leitordeassuntosquenãocostumamserde seuinteresseemdeterminadasfasesdoaprendizado.
ACaelumesperaquevocêaproveiteessematerial.Todososcomentários,críticasesugestõesserãomuitobem-vindos.
EssaapostilaéconstantementeatualizadaedisponibilizadanositedaCaelum.Sempreconsulteositeparanovasversõese,aoinvésdeanexaroPDFparaenviaraumamigo,indiqueositeparaqueelepossasemprebaixarasúltimasversões.Vocêpodeconferirocódigodeversãodaapostilalogono naldoíndice.
Baixesempreaversãomaisnovaem:www.caelum.com.br/apostilas
Esse material é parte integrante do treinamento Java para Desenvolvimento Web e distribuídogratuitamente exclusivamente pelo site da Caelum. Todos os direitos são reservados à Caelum. Adistribuição, cópia, revenda e utilização paraministrar treinamentos são absolutamente vedadas. Parausocomercialdestematerial,porfavor,consulteaCaelumpreviamente.
SOBREESTAAPOSTILA
1
3
32
Sumário
1EnfrentandooJavanaWeb1.1OgrandemercadodoJavanaWeb 1
1.2Livrosesitesinteressantes 2
2BancosdedadoseJDBC2.1Porqueusarumbancodedados? 3
2.2PersistindoatravésdeSockets? 4
2.3AconexãoemJava 4
2.4FábricadeConexões 7
2.5DesignPatterns 9
2.6Exercícios:ConnectionFactory 10
2.7AtabelaContato 14
2.8Javabeans 15
2.9Inserindodadosnobanco 16
2.10DAO-DataAccessObject 20
2.11Exercícios:JavabeanseContatoDao 22
2.12Fazendopesquisasnobancodedados 25
2.13Exercícios:Listagem 27
2.14Umpoucomais... 28
2.15Exercíciosopcionais 29
2.16OutrosmétodosparaoseuDAO 29
2.17Exercíciosopcionais-Alterareremover 30
3OqueéJavaEE?3.1ComooJavaEEpodeteajudaraenfrentarproblemas 32
3.2AlgumasespecificaçõesdoJavaEE 34
3.3ServidordeAplicação 34
3.4ServletContainer 35
3.5Exercícios:PreparandooTomcat 36
3.6PreparandooTomcatemcasa 37
SumárioCaelum
44
56
82
3.7Outraopção:Jetty 38
3.8IntegrandooTomcatnoEclipse 39
3.9OpluginWTP 39
3.10Exercícios:ConfigurandooTomcatnoEclipse 40
4NovoprojetoWebusandoEclipse4.1Novoprojeto 44
4.2Exercícios:Novoprojetoweb 44
4.3Análisedoresultadofinal 48
4.4CriandonossaspáginaseHTMLBásico 52
4.5Exercícios:primeirapágina 53
4.6Parasabermais:configurandooTomcatsemoplugin 53
4.7AlgumastagsHTML 54
5Servlets5.1Páginasdinâmicas 56
5.2Servlets 57
5.3Mapeandoumaservletnoweb.xml 59
5.4Aestruturadediretórios 60
5.5Exercícios:PrimeiraServlet 61
5.6Erroscomuns 64
5.7FacilidadesdasServlets3.0 65
5.8Parasabermais:WebServleteInitParamAnnotation 67
5.9Enviandoparâmetrosnarequisição 68
5.10Pegandoosparâmetrosdarequisição 69
5.11Exercícios:Criandofuncionalidadeparagravarcontatos 71
5.12GET,POSTemétodosHTTP 75
5.13TratandoexceçõesdentrodaServlet 75
5.14Exercício:TratandoexceçõesecódigosHTTP 77
5.15IniteDestroy 78
5.16UmaúnicainstânciadecadaServlet 79
5.17Exercíciosopcionais 81
5.18Discussão:Criandopáginasdentrodeumaservlet 81
6JavaServerPages6.1ColocandooHTMLnoseudevidolugar 82
6.2Exercícios:PrimeiroJSP 84
6.3ListandooscontatoscomScriptlet 85
6.4Exercíciosopcionais:Listadecontatoscomscriptlet 86
CaelumSumário
90
107
116
6.5MisturandocódigoJavacomHTML 87
6.6EL:Expressionlanguage 88
6.7Exercícios:parâmetroscomaExpressionLanguage 88
6.8Parasabermais:CompilandoosarquivosJSP 89
7UsandoTaglibs7.1Taglibs 90
7.2InstanciandoPOJOs 91
7.3JSTL 92
7.4Instalação 92
7.5CabeçalhoparaaJSTLcore 93
7.6ForEach 93
7.7Exercícios:forEach 95
7.8Exercíciosopcionais 96
7.9Evoluindonossalistagem 97
7.10FazendoifscomaJSTL 97
7.11Exercícios:listadecontatoscomcondicionais 98
7.12Importandopáginas 99
7.13Exercícios:cabeçalhoserodapés 100
7.14Formataçãodedatas 102
7.15Exercícios:Formatandoadatadenascimentodoscontatos 103
7.16Parasabermais:linkscom<c:url> 104
7.17Exercíciosopcionais:Caminhoabsoluto 105
7.18Parasabermais:Outrastags 105
8TagscustomizadascomTagfiles8.1PorqueeuprecisariadeoutrastagsalémdaJSTL? 107
8.2CalendárioscomjQuery 108
8.3CriandominhasprópriastagscomTagfiles 109
8.4Exercícios:criandonossaprópriatagparacalendário 111
8.5Parasabermais:Outrastaglibsnomercado 114
8.6Desafio:Colocandodisplaytagnoprojeto 115
9MVC-ModelViewController9.1ServletouJSP? 116
9.2RequestDispatcher 118
9.3Exercícios:RequestDispatcher 119
9.4Melhorandooprocesso 120
9.5RetomandoodesignpatternFactory 124
SumárioCaelum
133
146
9.6Exercícios:Criandonossaslógicaseaservletdecontrole 125
9.7Exercícios:Criandoumalógicapararemovercontatos 126
9.8Fazendoalógicaparalistaroscontatos 127
9.9Exercícios:Lógicaparalistarcontatos 128
9.10Escondendonossaspáginas 129
9.11Exercíciosopcionais 130
9.12ModelViewController 131
9.13Listadetecnologias:camadadecontrole 131
9.14Listadetecnologias:camadadevisualização 132
9.15Discussãoemaula:ospadrõesCommandeFrontController 132
10Recursosimportantes:Filtros10.1ReduzindooacoplamentocomFiltros 141
10.2Exercíciosopcionais:Filtroparamedirotempodeexecução 138
10.3Problemasnacriaçãodasconexões 139
10.4Tentandooutrasestratégias 140
10.5ReduzindooacoplamentocomFiltros 141
10.6Exercícios:Filtros 144
11SpringMVC11.1PorqueprecisamosdeframeworksMVC? 146
11.2Umpoucodehistória 147
11.3ConfigurandooSpringMVC 148
11.4Criandoaslógicas 150
11.5AlógicaOláMundo! 150
11.6Parasabermais:ConfigurandooSpringMVCemcasa 152
11.7Exercícios:ConfigurandooSpringMVCetestandoaconfiguração 152
11.8Adicionandotarefasepassandoparâmetros 154
11.9Exercícios:Criandotarefas 157
11.10Incluindovalidaçãonocadastrodetarefas 160
11.11ValidaçãocomBeanValidation 161
11.12Exercícios:Validandotarefas 163
11.13Listandoastarefasedisponibilizandoobjetosparaaview 165
11.14Exercícios:Listandotarefas 166
11.15Redirecionandoarequisiçãoparaoutraação 168
11.16Exercícios:Removendoealterandotarefas 169
11.17Desafio-Calendário 171
11.18Melhorandoausabilidadedanossaaplicação 171
11.19UtilizandoAJAXparamarcartarefascomofinalizadas 173
CaelumSumário
182
191
207
11.20ConfiguraroSpringMVCparaacessararquivoscomuns 174
11.21Exercícios:Ajax 175
11.22Parasabermais:AlterandovalordadatacomAJAX 177
11.23ExercíciosOpcionais:MelhorandonossoAJAX 180
12SpringMVC:Autenticaçãoeautorização12.1Autenticandousuários:comofunciona? 182
12.2Cookies 182
12.3Sessão 183
12.4Configurandootempolimite 183
12.5Registrandoousuáriologadonasessão 184
12.6Exercício:Fazendoologinnaaplicação 185
12.7BloqueandoacessosdeusuáriosnãologadoscomInterceptadores 186
12.8Exercícios:Interceptandoasrequisições 188
12.9Exercíciosopcionais:Logout 189
13SpringIoCedeploydaaplicação13.1Menosacoplamentocominversãodecontroleeinjeçãodedependências 191
13.2ContainerdeInjeçãodedependências 194
13.3ContainerSpringIoC 195
13.4Outrasformasdeinjeção 197
13.5Exercícios:InversãodecontrolecomoSpringContainer 198
13.6AprimorandoovisualatravésdeCSS 200
13.7Exercíciosopcionais:AplicandoCSSnaspáginas 202
13.8Deploydoprojetoemoutrosambientes 203
13.9Exercícios:Deploycomwar 204
13.10Discussãoemaula:lidandocomdiferentesnomesdecontexto 206
14UmaintroduçãopráticaaoJPAcomHibernate14.1MapeamentoObjetoRelacional 207
14.2JavaPersistenceAPIeFrameworksORM 208
14.3BibliotecasdoHibernateeJPA 208
14.4MapeandoumaclasseTarefaparanossoBancodeDados 209
14.5ConfigurandooJPAcomaspropriedadesdobanco 211
14.6UsandooJPA 212
14.7Parasabermais:ConfigurandooJPAcomHibernateemcasa 212
14.8Exercícios:ConfigurandooJPAegerandooschemadobanco 213
14.9Trabalhandocomosobjetos:oEntityManager 216
14.10Exercícios:GravandoeCarregandoobjetos 217
SumárioCaelum
222
225
239
14.11Removendoeatualizandoobjeto 219
14.12Buscandocomumacláusulawhere 219
14.13Exercícios:BuscandocomJPQL 220
15Eagora?15.1Osapêndicesdessaapostila 222
15.2FrameworksWeb 222
15.3Frameworksdepersistência 223
15.4Ondeseguirseusestudos 224
16Apêndice-IntegraçãodoSpringcomJPA16.1GerenciandooEntityManager 225
16.2ConfigurandooJPAnoSpring 226
16.3InjetandooEntityManager 227
16.4Baixoacoplamentopelousodeinterface 228
16.5Gerenciandoatransação 230
16.6Exercícios:IntegrandoJPAcomSpring 232
16.7ExercíciosOpcionais:IntegrandoaentidadeUsercomoJPA 235
17Apêndice-TópicosdaServletAPI17.1Init-paramsecontext-params 239
17.2welcome-file-list 240
17.3PropriedadesdepáginasJSP 240
17.4Inclusãoestáticadearquivos 241
17.5TratamentodeerroemJSP 242
17.6Descobrindotodososparâmetrosdorequest 243
17.7Trabalhandocomlinkscomac:url 243
17.8Contextlistener 244
17.9OServletContexteoescopodeaplicação 244
17.10Outroslisteners 246
Versão:25.1.6
CaelumSumário
CAPÍTULO1
"Todohomemtemalgumaslembrançasqueelenãocontaatodomundo,masapenasaseusamigos.Eletem outras lembranças que ele não revelaria nem mesmo para seus amigos, mas apenas para elemesmo,efazissoemsegredo.Masaindaháoutraslembrançasqueohomemtemmedodecontaratéaelemesmo,etodohomemdecentetemumconsiderávelnúmerodessascoisasguardadasbemnofundo.Alguématépoderiadizerque,quantomaisdecenteéohomem,maioronúmerodessascoisasemsuamente."--FiodórDostoiévski,emMemóriasdoSubsolo
ComofazeraplataformaJavaeaWebtrabalharemjuntas?
CertamenteomercadocommaisoportunidadesemqueoJavaestápresenteéodeWeb.Nãoéporacasosuapopularidade:criarumprojetocomJavadámuitaliberdade,evitandocairnovendorlock-in.Em outras palavras, sua empresa fica independente do fabricante de vários softwares: do servletcontainer,dobancodedadoseatédaprópriafabricantedasuaVirtualMachine!Alémdisso,podemosfazertodoodesenvolvimentoemumsistemaoperacionalefazerodeploy(implantação)emoutro.
Apesar de tanta popularidade no ambiente Web, o desenvolvimento com Java não é trivial: énecessárioconhecercomcertaprofundidadeasAPIsdeservletsedeJSP,mesmoquesuaequipevenhautilizar frameworkscomoStruts,VRaptorouJSF.ConceitosdeHTTP,sessionecookies tambémsãomandatóriosparapoderenxergargargaloseproblemasquesuaaplicaçãoenfrentará.
Essecursoabordadesdeobancodedados,atéousode frameworksMVCparadesacoplaro seucódigo. Ao final do curso, ainda veremos o SpringMVC e o Hibernate, duas das ferramentasmaispopularesentreosrequisitosnasmuitasvagasdeempregoparadesenvolvedorWeb.
Por fim, faltamencionar sobre aprática, quedeve ser tratada seriamente: todosos exercícios sãomuito importantes e os desafios podem ser feitos quando o curso acabar. De qualquer maneira,recomendamosaosalunosestudaremcasa,principalmenteàquelesquefazemoscursosintensivos.
Como estudaremos várias tecnologias e ambientes, é comum esbarrarmos em algum erro que setorneumimpeditivoparaprosseguirnosexercícios,portantonãodesanime.
Lembre-se de usar o fórum do GUJ (http://www.guj.com.br/), onde sua dúvida será respondidaprontamente.Vocêtambémpodeentraremcontatocomseuinstrutorparatirardúvidasdurantetodoo
ENFRENTANDOOJAVANAWEB
1.1OGRANDEMERCADODOJAVANAWEB
1ENFRENTANDOOJAVANAWEB 1
seucurso.
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
É possível aprender muitos dos detalhes e pontos não cobertos no treinamento em tutoriais naInternetemportaiscomooGUJ,emblogseemmuitossitesespecializados.
http://www.guj.com.br/
AeditoraCasadoCódigopossui livrosdeJava,comodeAndroid,JSFeJPA,evendee-booksapreçosbastantejustos:
http://www.CasaDoCodigo.com.br/
Não deixe de conhecer os outros cursos daCaelum, inclusive os novos cursos online, onde vocêpodecomeçaraestudaragoramesmo.
http://www.alura.com.br/
Seuslivrosdetecnologiaparecemdoséculopassado?
1.2LIVROSESITESINTERESSANTES
2 1.2LIVROSESITESINTERESSANTES
CAPÍTULO2
"Omedoéopaidamoralidade"--FriedrichWilhelmNietzsche
Aotérminodessecapítulo,vocêserácapazde:
conectar-seaumbancodedadosqualqueratravésdaAPIJDBC;criarumafábricadeconexõesusandoodesignpatternFactory;pesquisardadosatravésdequeries;encapsularsuasoperaçõescombancosdedadosatravésdeDAO-DataAccessObject.
Muitos sistemas precisam manter as informações com as quais eles trabalham para permitirconsultas futuras,geraçãoderelatóriosoupossíveisalteraçõesnas informações.Paraqueessesdadossejammantidospara sempre, esses sistemasgeralmenteguardamessas informações emumbancodedados,queasmantémdeformaorganizadaeprontasparaconsultas.
A maioria dos bancos de dados comerciais são os chamados relacionais, que é uma forma detrabalharepensardiferenteaoparadigmaorientadoaobjetos.
OMySQLéobancodedadosqueusaremosduranteocurso.Éumdosmaisimportantesbancosdedados relacionais, e é gratuito, além de ter uma instalação fácil para todos os sistemas operacionais.Depoisdeinstalado,paraacessá-loviaterminal,fazemosdaseguinteforma:
mysql-uroot-p
Digitandoasenhaemseguida,casohaja.Seousuáriorootnãotiversenha,ocomandoacimanão
precisado-p.
BANCODEDADOS
Para aqueles que não conhecem um banco de dados, é recomendado ler um pouco sobre oassuntoetambémterumabasedeSQLparacomeçarausaraAPIJDBC.
BANCOSDEDADOSEJDBC
2.1PORQUEUSARUMBANCODEDADOS?
2BANCOSDEDADOSEJDBC 3
O processo de armazenamento de dados é também chamado de persistência. A biblioteca depersistênciaembancodedadosrelacionaisdoJavaéchamadaJDBC(JavaDataBaseConnectivity),etambémexistemdiversasferramentasdotipoORM(ObjectRelationalMapping)quefacilitambastanteousodoJDBC.Nestemomento, focaremosnosconceitosenousodoJDBC.Veremosumpoucodaferramenta de ORMHibernate ao final destemesmo curso e, no curso FJ-25, commuitos detalhes,recursosetópicosavançados.
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
Paraconectar-seaumbancodedados,poderíamosabrirsocketsdiretamentecomoservidorqueohospeda,porexemplo,umOracleouMySQL,enoscomunicarmoscomeleatravésdeseuprotocoloproprietário.
Mas você conhece o protocolo proprietário de algum banco de dados? Conhecer um protocolocomplexoemprofundidadeédifícil,trabalharcomeleémuitotrabalhoso.
Uma segunda ideia seria utilizar umaAPI específica para cada banco de dados.Antigamente, noPHP,porexemplo,aúnicamaneiradeacessaroOracleeraatravésdefunçõescomooracle_connect,
oracle_result,eassimpordiante.OMySQLtinhasuasfunçõesanálogas,comomysql_connect.
Essaabordagemfacilitamuitonossotrabalhopornãoprecisarmosentenderoprotocolodecadabanco,mas fazcomque tenhamosdeconhecerumaAPIumpoucodiferenteparacada tipodebanco.Alémdisso,casoprecisemostrocardebancodedadosumdia,precisaremostrocartodoonossocódigopararefletirafunçãocorretadeacordocomonovobancodedadosqueestamosutilizando.
AconexãoaumbancodedadoséfeitademaneiraelegantecomJava.Paraevitarquecadabanco
EditoraCasadoCódigocomlivrosdeumaformadiferente
2.2PERSISTINDOATRAVÉSDESOCKETS?
2.3ACONEXÃOEMJAVA
4 2.2PERSISTINDOATRAVÉSDESOCKETS?
tenhaa suaprópriaAPI eumconjuntode classes emétodos, temosumúnicoconjuntode interfacesmuitobemdefinidasquedevemser implementadas.Esseconjuntode interfacesficadentrodopacotejava.sqlenosreferiremosaelecomoJDBC.
Entreasdiversasinterfacesdestepacote,existeainterfaceConnection,quedefinemétodospara
executarumaquery(comouminserteselect),comitartransação,fecharaconexão,entreoutros.
Caso queiramos trabalhar com o MySQL, precisamos de classes concretas que implementem essasinterfacesdopacotejava.sql.
EsseconjuntodeclassesconcretaséquemfaráaponteentreocódigoclientequeusaaAPIJDBCeobancodedados.Sãoessasclassesquesabemsecomunicaratravésdoprotocoloproprietáriodobancodedados.Esseconjuntodeclassesrecebeonomededriver.TodososprincipaisbancosdedadosdomercadopossuemdriversJDBCparaquevocêpossautilizá-loscomJava.Onomedriveréanálogoaoqueusamosparaimpressoras:comoéimpossívelqueumsistemaoperacionalsaibaconversarcomtodotipodeimpressoraexistente,precisamosdeumdriverquefaçaopapelde"tradutor"dessaconversa.
Para abrir uma conexão comumbancode dados, precisamos utilizar sempre umdriver.A classeDriverManageréaresponsávelporsecomunicarcomtodososdriversquevocêdeixoudisponível.
Paraisso,invocamosométodoestáticogetConnectioncomumaStringque indicaaqualbanco
desejamosnosconectar.
EssaString -chamadadeStringdeconexãoJDBC -queutilizaremosparaacessaroMySQL
temsempreaseguinteforma:
jdbc:mysql://ip/nome_do_banco
2.3ACONEXÃOEMJAVA 5
DevemossubstituirippeloIPdamáquinadoservidorenome_do_bancopelonomedobancode
dadosaserutilizado.
Seguindooexemploda linhaacimae tudoque foiditoatéagora, seriapossível rodaroexemploabaixoereceberumaconexãoparaumbancoMySQL,casoeleestejarodandonamesmamáquina:
publicclassJDBCExemplo{publicstaticvoidmain(String[]args)throwsSQLException{Connectionconexao=DriverManager.getConnection("jdbc:mysql://localhost/fj21");System.out.println("Conectado!");conexao.close();}}
ReparequeestamosdeixandopassaraSQLException,queéumaexceptionchecked, lançadapor
muitosdosmétodosdaAPIdeJDBC.Numaaplicaçãorealdevemosutilizartry/catchnos lugares
quejulgamoshaverpossibilidadederecuperardeumafalhacomobancodedados.Tambémprecisamostomarsemprecuidadoparafechartodasasconexõesqueforamabertas.
Aotestarocódigoacima,recebemosumaexception.Aconexãonãopôdeseraberta.Recebemosamensagem:
java.sql.SQLException:Nosuitabledriverfoundforjdbc:mysql://localhost/fj21
Porquê?
Osistemaaindanãoachouuma implementaçãodedriverJDBC quepode ser usadapara abrir aconexãoindicadapelaURLjdbc:mysql://localhost/fj21.
O que precisamos fazer é adicionar o driver do MySQL ao classpath, ou seja, o arquivo .jarcontendo a implementação JDBC do MySQL (mysql connector) precisa ser colocado em um lugarvisívelpeloseuprojetoouadicionadoàvariáveldeambienteCLASSPATH.ComousaremosoEclipse,
depoisdeadicionarojardodriverJDBCdoMySQLnapastadoprojeto,faremosissoatravésdeumcliquedadireitaemnossoprojeto,BuildPatheemAddtoBuildPath.Veremosistopassoapassonosexercícios.
6 2.3ACONEXÃOEMJAVA
EOCLASS.FORNAME?
Atéaversão3doJDBC,antesdechamaroDriverManager.getConnection()eranecessário
registrar o driver JDBC que iria ser utilizado através do métodoClass.forName("com.mysql.jdbc.Driver"),nocasodoMySQL,quecarregavaessaclasse,e
essasecomunicavacomoDriverManager.
ApartirdoJDBC4,queestápresentenoJava6,essepassonãoémaisnecessário.Maslembre-se:casovocêutilizeJDBCemumprojetocomJava5ouanterior,seráprecisofazeroregistrodoDriverJDBC,carregandoasuaclasse,quevaiseregistrarnoDriverManager.
Issotambémpodesernecessárioemalgunsservidoresdeaplicaçãoeweb,comonoTomcat7ouposterior,porproteçãoparapossíveisvazamentosdememória:
http://bit.ly/18BpDfG
Teoricamente,bastaalterarasduasStringsqueescrevemosparamudardeumbancoparaoutro.
Porém,nãoétudotãosimplesassim!OcódigoSQLqueveremosaseguirpodefuncionaremumbanco
enãoemoutros.DependedequãoaderenteaopadrãoANSISQLéseubancodedados.
Isso só causa dor de cabeça e existem projetos que resolvem isso, como é o caso do Hibernate(www.hibernate.org)edaespecificaçãoJPA(JavaPersistenceAPI).VeremosumpoucodoHibernateaofinaldessecursoebastantesobreelenoFJ-25.
Osdriverspodemserbaixadosnormalmentenositedofabricantedobancodedados.
Algunscasos,comonoMicrosoftSQLServer,existemoutrosgruposquedesenvolvemodriveremhttp://jtds.sourceforge.net . Enquanto isso, você pode achar o driver doMYSQL (chamado demysqlconnector)nositehttp://www.mysql.org.
Emdeterminadomomentodenossaaplicação,gostaríamosdeterocontrolesobreaconstruçãodosobjetosdanossaclasse.Muitopodeserfeitoatravésdoconstrutor,comosaberquantosobjetosforaminstanciadosoufazerologsobreessasinstanciações.
Àsvezes, tambémqueremoscontrolarumprocessomuitorepetitivoe trabalhoso,comoabriruma
Alterandoobancodedados
Driversdeoutrosbancosdedados
2.4FÁBRICADECONEXÕES
2.4FÁBRICADECONEXÕES 7
conexãocomobancodedados.Tomemoscomoexemploaclasseaseguir,queseria responsávelporabrirumaconexãocomobanco:
publicclassConnectionFactory{publicConnectiongetConnection(){try{returnDriverManager.getConnection("jdbc:mysql://localhost/fj21","root","<SENHADOBANCOAQUI>");}catch(SQLExceptione){thrownewRuntimeException(e);}}}
Assim, sempre que quisermos obter uma conexão com o banco no nosso código, usaremos ocomandoaseguir:
Connectioncon=newConnectionFactory().getConnection();
NotequeométodogetConnection()éumafábricadeconexões,istoé,elecrianovasconexões
paranós.Bastainvocarométodoerecebemosumaconexãoprontaparauso,nãoimportandodeondeelaveioeeventuaisdetalhesdecriação.Portanto,vamoschamaraclassedeConnectionFactoryeo
métododegetConnection.
Encapsulandodessaforma,podemosmaistardemudaraobtençãodeconexões,para,porexemplo,usarummecanismodepooling,queéfortementerecomendávelemumaaplicaçãoreal.
TRATAMENTODEEXCEÇÕES
Reparequeestamosfazendoumtry/catchemSQLException e relançando-a comouma
RuntimeException.Fazemosissoparaqueoseucódigoquechamaráafábricadeconexõesnão
fiqueacopladocomaAPIdeJDBC.TodavezquetivermosquelidarcomumaSQLException,
vamosrelançá-lascomoRuntimeException.
PoderíamosaindacriarnossaprópriaexceçãoparaindicarqueocorreuumerrodentrodanossaFactory,algocomoumaConnectionFactoryException.
8 2.4FÁBRICADECONEXÕES
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
Orientaçãoaobjetosresolveasgrandesdoresdecabeçaquetínhamosnaprogramaçãoprocedural,restringindoecentralizandoresponsabilidades.
Masalgunsproblemasnãopodemosresolversimplesmentecomorientaçãoaobjetos,poisnãoexistepalavrachaveparaumafuncionalidadetãoespecífica.
Algunsdessespequenosproblemasaparecemcomtantafrequênciaqueaspessoasdesenvolvemumasolução"padrão"paraele.Comisso,aonosdefrontarmoscomumdessesproblemasclássicos,podemosrapidamente implementaressasoluçãogenéricacomumaououtramodificação,deacordocomnossanecessidade.EssassoluçõespadrõestemonomedeDesignPatterns(PadrõesdeProjeto).
AmelhormaneiraparaaprenderoqueéumDesignPatternévendocomosurgiusuanecessidade.
AnossaConnectionFactoryimplementaodesignpatternFactory,quepregaoencapsulamento
daconstrução(fabricação)deobjetoscomplicados.
ABÍBLIADOSDESIGNPATTERNS
OlivromaisconhecidodeDesignPatterns foi escritoem1995e tem trechosdecódigoemC++eSmalltalk.Masoquerealmenteimportasãoosconceitoseosdiagramasquefazemdesselivroindependentedequalquerlinguagem.Alémdetudo,olivroédeleituraagradável.
DesignPatterns,ErichGammaetal.
JáconheceoscursosonlineAlura?
2.5DESIGNPATTERNS
2.5DESIGNPATTERNS 9
1. NoscomputadoresdaCaelum,cliquenoíconedoEclipsenoDesktop;
BAIXANDOOECLIPSEEMCASA
Estamos usando o Eclipse for Java EEDevelopers. Você pode obtê-lo direto no site doEclipseemwww.eclipse.org
FecheateladeWelcomecasoelaapareça
VamoscriarumprojetonoEclipsechamadofj21-jdbc.
VáemFile->New->Project:
SelecioneJavaProjectecliqueemNext:
2.6EXERCÍCIOS:CONNECTIONFACTORY
10 2.6EXERCÍCIOS:CONNECTIONFACTORY
Coloqueonomedoprojetocomofj21-jdbcecliqueemFinish:
Umanova janela surgiráperguntandosevocêquercriarumnovomodule-info.java,cliqueemDon'tCreate
Aceiteamudançadeperspectiva:
2.6EXERCÍCIOS:CONNECTIONFACTORY 11
2. CopieodriverdoMySQLparaoseuprojeto.
noseuDesktop,cliquenoatalhoAtalhoparaarquivosdocursos;copieapasta21paraoseuDesktop;entrenapasta21/projeto-jdbc/mysqldoDesktop,cliquecomobotãodireitodomousenodriverdoMySQLecopie;váparasuapastaprincipal(home);
entrenodiretórioworkspace,fj21-jdbc;
cliquecomobotãodireitodomouseecoleodriveraqui:vocêacabadecolocaroarquivo".jar"noseuprojeto.
3. Vamoscriaraclassequefabricaconexões:
CliqueemFile->New->Class.
Crie-anopacotebr.com.caelum.jdbcenomeie-acomoConnectionFactory.
CliqueemFinish
Nocódigo,crieométodogetConnection,queretornaumanovaconexão.Quandoperguntado,
importeasclassesdopacotejava.sql(cuidadoparanãoimportarNADAdecom.mysql).
PresteatençãoaosparâmetrosdométodogetConnection, o último é a senhadousuárioque
estamosusando,nocasooroot.Esseusuárioestáconfiguradocomasenha""(aspasvazias).
publicConnectiongetConnection(){try{returnDriverManager.getConnection("jdbc:mysql://localhost/fj21","root","");}catch(SQLExceptione){thrownewRuntimeException(e);}}
12 2.6EXERCÍCIOS:CONNECTIONFACTORY
1. CrieumaclassechamadaTestaConexaonopacotebr.com.caelum.jdbc.teste.Todasasnossas
classesdetestedeverãoficarnessepacote.
Crieummétodomaindentrodaclasse.UseoatalhodoEclipseparaajudar.
Dentrodomain, fabriqueuma conexãousando aConnectionFactory que criamos.Vamos
apenastestaraaberturadaconexãoedepoisfechá-lacomométodoclose:
Connectionconnection=newConnectionFactory().getConnection();System.out.println("Conexãoaberta!");connection.close();
Trateoserroscomthrows.(Use:Ctrl+1eescolha"addthrowsdeclaration").
1. AntesdetestaraconexãocomobancodedadosatravésdaclasseTestaConexao,verifiqueseo
bancodedadosfj21existe.
Abraoterminaledigite:
mysql-uroot
Lembrandoque,sehouversenhaparalogarcomobanco,éprecisoescrevermysql-uroot-pe,
emseguida,digitarasenhadobancodedados.
DentrodoMySQL,procurepelobancodedadosfj21comocomando:
showdatabases;
Seelenãoexistir,criecomocomando:
createdatabasefj21;
1. RodeasuaclasseTestaConexaopeloEclipse.
CliquedadireitanasuaclasseTestaConexao
EscolhaRun as, Java Application (caso prefira, aprenda a tecla de atalho para agilizar naspróximasexecuções)
2. A aplicação não funciona pois o driver não foi encontrado? Esquecemos de colocar o JAR no
2.6EXERCÍCIOS:CONNECTIONFACTORY 13
classpath!(BuildPathnoEclipse)
CliquenoseuprojetocomobotãodadireitaeescolhaRefresh(oupressioneF5).SelecioneoseudriverdoMySQL,cliquedadireitaeescolhaBuildPath,AddtoBuildPath:
RodenovamentesuaaplicaçãoTestaConexaoagoraquecolocamosodrivernoclasspath.
Paracriarumatabelanova,primeirodevemosacessaroterminalefazerocomandoparalogarmosnomysql:
mysql-uroot
senãohouversenha,ou
mysql-uroot-p
sehouver,digitandoemseguidaasenha,queoinstrutordiráqualé.
Nospreparamosparausarobancodedadosfj21:
usefj21;
Aseguintetabelaseráusadanosexemplosdessecapítulo:
createtablecontatos(idBIGINTNOTNULLAUTO_INCREMENT,nomeVARCHAR(255),emailVARCHAR(255),enderecoVARCHAR(255),dataNascimentoDATE,primarykey(id));
No banco de dados relacional, é comum representar um contato (entidade) em uma tabela decontatos.
2.7ATABELACONTATO
14 2.7ATABELACONTATO
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
OquesãoJavabeans?AperguntaquenãoquersecalarpodeserrespondidamuitofacilmenteumavezqueumadasmaioresconfusõesfeitasaíforaéentreJavabeanseEnterpriseJavaBeans(EJB).
Javabeanssãoclassesquepossuemoconstrutorsemargumentosemétodosdeacessodotipogete
set! Mais nada! Simples, não? Já os EJBs costumam ser javabeans com características mais
avançadas.
Podemos usar beans em diversas situações, como em classes que representam nosso modelo dedados.
Aseguir,vocêvêumexemplodeumaclasseJavaBeanqueseriaequivalenteaonossomodelodeentidadedobancodedados:
packagebr.com.caelum.jdbc.modelo;
publicclassContato{
privateLongid;privateStringnome;privateStringemail;privateStringendereco;privateCalendardataNascimento;
//métodosgetesetparaid,nome,email,endereçoedataNascimento
publicStringgetNome(){returnthis.nome;}publicvoidsetNome(Stringnovo){this.nome=novo;}
publicStringgetEmail(){returnthis.email;}
VocêpodetambémfazerocursodatadessaapostilanaCaelum
2.8JAVABEANS
2.8JAVABEANS 15
publicvoidsetEmail(Stringnovo){this.email=novo;}
publicStringgetEndereco(){returnthis.endereco;}publicvoidsetEndereco(Stringnovo){this.endereco=novo;}
publicLonggetId(){returnthis.id;}publicvoidsetId(Longnovo){this.id=novo;}
publicCalendargetDataNascimento(){returnthis.dataNascimento;}publicvoidsetDataNascimento(CalendardataNascimento){this.dataNascimento=dataNascimento;}}
AespecificaçãoJavaBeansémuitograndeemaisinformaçõessobreessavastaáreaqueéabasedoscomponentesescritosemJavapodeserencontradaem:
http://docs.oracle.com/javase/tutorial/javabeans/
MÉTODOSGETTERSESETTERS
UmerromuitocomumcometidopelosdesenvolvedoresJavaéacriaçãodosmétodosgettersesettersindiscriminadamente,semterarealnecessidadedaexistênciadetaismétodos.
Existe um artigo no blog da Caelum que trata desse assunto:http://blog.caelum.com.br/2006/09/14/nao-aprender-oo-getters-e-setters/
Os cursosFJ-11 eFJ-22 tambémmostram isso claramente, quando criam algumas entidadesquenãopossuemapenasgettersesetters.
Para inserirdadosemumatabeladeumbancodedadosentidade-relacional,bastausaracláusulaINSERT.Precisamosespecificarquaisoscamposquedesejamosatualizareosvalores.
PrimeiroocódigoSQL:
Stringsql="insertintocontatos"+"(nome,email,endereco,dataNascimento)"+
2.9INSERINDODADOSNOBANCO
16 2.9INSERINDODADOSNOBANCO
"values('"+nome+"','"+email+"','"+endereco+"','"+dataNascimento+"')";
O exemplo acima possui três pontos negativos que são importantíssimos. O primeiro é que oprogramadorquenãoescreveuocódigooriginalnãoconseguebateroolhoeentenderoqueestáescrito.O que o código acima faz? Lendo rapidamente fica difícil.Mais difícil ainda é saber se faltou umavírgula,umfechaparêntesestalvez?Eaindaassim,esseéumcasosimples.Existemtabelascom10,20,30 e até mais campos, tornando inviável entender o que está escrito no SQL misturado com asconcatenações.
Outro problema é o clássico "preconceito contra Joana D'arc", formalmente chamado de SQLInjection.Oque acontecequandoo contato a ser adicionadopossuinonomeumaaspas simples?OcódigoSQLsequebra todoeparade funcionarou,pior ainda,ousuário final é capazde alterar seucódigoSQLparaexecutaraquiloqueeledeseja(SQLinjection)...tudoissoporqueescolhemosaquelalinhadecódigoenãofizemosoescapedecaracteresespeciais.
Maisumproblemaqueenxergamosaíénadata.ElaprecisaserpassadacomoumaStringeno
formatoqueobancodedadosentenda,portanto,sevocêpossuiumobjetojava.util.Calendar,que
éonossocaso,vocêprecisaráfazeraconversãodesseobjetoparaaString.
Por esses trêsmotivosnãousaremos códigoSQLcomomostrado anteriormente.Vamos imaginaralgomaisgenéricoeumpoucomaisinteressante:
Stringsql="insertintocontatos"+"(nome,email,endereco,dataNascimento)"+"values(?,?,?,?)";
ExisteumamaneiraemJavadeescreverocódigoSQLcomonoprimeiroexemplodessaseção(comconcatenaçõesdeString).Essamaneiranãoseráensinadaduranteocursopoiséumapéssimaprática
quedificultaamanutençãodoseuprojeto.
Percebaquenãocolocamosospontosdeinterrogaçãodebrincadeira,massimporquerealmentenãosabemoso que desejamos inserir.Estamos interessados em executar aquele códigomas não sabemosainda quais são osparâmetros que utilizaremos nesse código SQL que será executado, chamado destatement.
AscláusulassãoexecutadasemumbancodedadosatravésdainterfacePreparedStatement.Para
receber umPreparedStatement relativo à conexão, basta chamar ométodo prepareStatement,
passandocomoargumentoocomandoSQLcomosvaloresvindosdevariáveispreenchidoscomumainterrogação.
Stringsql="insertintocontatos"+"(nome,email,endereco,dataNascimento)"+"values(?,?,?,?)";PreparedStatementstmt=connection.prepareStatement(sql);
2.9INSERINDODADOSNOBANCO 17
Logoemseguida, chamamosométodosetString doPreparedStatement para preencher os
valoresquesãodotipoString,passandoaposição (começandoem1)da interrogaçãonoSQLeo
valorquedevesercolocado:
//preencheosvaloresstmt.setString(1,"Caelum");stmt.setString(2,"[email protected]");stmt.setString(3,"R.Vergueiro3185cj57");
Precisamosdefinir tambémadatadenascimentodonossocontato,para isso,precisaremosdeumobjetodotipojava.sql.DateparapassarmosparaonossoPreparedStatement.Nesse exemplo,
vamospassaradataatual.Paraisso,vamospassarumlongquerepresentaosmilissegundosdadata
atual para dentrodeumjava.sql.Date, que é o tipo suportado pelaAPI JDBC.Vamos utilizar a
classeCalendarparaconseguirmosessesmilissegundos:
java.sql.DatedataParaGravar=newjava.sql.Date(Calendar.getInstance().getTimeInMillis());stmt.setDate(4,dataParaGravar);
Porfim,umachamadaaexecute()executaocomandoSQL:
stmt.execute();
Imagine todo esse processo sendo escrito toda vez que desejar inserir algo no banco?Ainda nãoconseguevisualizaroquãodestrutivoissopodeser?
Vejaoexemploabaixo,queabreumaconexãoeinsereumcontatonobanco:
publicclassJDBCInsere{
publicstaticvoidmain(String[]args)throwsSQLException{
//conectandoConnectioncon=newConnectionFactory().getConnection();
//criaumpreparedStatementStringsql="insertintocontatos"+"(nome,email,endereco,dataNascimento)"+"values(?,?,?,?)";PreparedStatementstmt=con.prepareStatement(sql);
//preencheosvaloresstmt.setString(1,"Caelum");stmt.setString(2,"[email protected]");stmt.setString(3,"R.Vergueiro3185cj57");stmt.setDate(4,newjava.sql.Date(Calendar.getInstance().getTimeInMillis()));
//executastmt.execute();stmt.close();
System.out.println("Gravado!");
con.close();}
18 2.9INSERINDODADOSNOBANCO
}
NãoécomumutilizarJDBCdiretamentehojeemdia.OmaispraticadoéousodealgumaAPIdeORMcomoaJPAouoHibernate.TantonaJDBCquantoembibliotecasORMdeve-seprestaratençãonomomentodefecharaconexão.
Oexemplodadoacimanãoafechacasoalgumerroocorranomomentodeinserirumdadonobancodedados.Ocomuméfecharaconexãoemumblocofinally:
publicclassJDBCInsere{publicstaticvoidmain(String[]args)throwsSQLException{Connectioncon=null;try{con=newConnectionFactory().getConnection();
//fazummontedeoperações.//quepodemlançarexceptionsruntimeeSQLException}catch(SQLExceptione){System.out.println(e);}finally{con.close();}}}
Dessaforma,mesmoqueocódigodentrodotrylanceexception,ocon.close()seráexecutado.
Garantimosquenãodeixaremosumaconexãopenduradasemuso.Essecódigopodeficarmuitomaiorse quisermos ir além. Pois o que acontece no caso de con.close lançar uma exception? Quem a
tratará?
Trabalharcomrecursoscaros,comoconexões,sessões, threadsearquivos,sempredevesermuitobem pensado. Deve-se tomar cuidado para não deixar nenhum desses recursos abertos, pois poderávazaralgopreciosodanossaaplicação.Comoveremosduranteocurso, é importantecentralizaressetipodecódigoemalgumlugar,paranãorepetirumatarefacomplicadacomoessa.
Além disso, há a estrutura do Java 7 conhecida como try-with-resources. Ela permite declarar einicializar,dentrodotry,objetosque implementamAutoCloseable.Dessa forma, ao términodo
try, o próprio compilador inserirá instruções para invocar o close desses recursos, além de se
precaveremrelaçãoaexceçõesquepodemsurgirporcausadessainvocação.Nossocódigoficariamaisreduzidoeorganizado,alémdoescopodeconsóvalerdentrodotry:
try(Connectioncon=newConnectionFactory().getConnection()){//fazummontedeoperações.//quepodemlançarexceptionsruntimeeSQLException}catch(SQLExceptione){System.out.println(e);}
Parasabermais:Fechandoaconexãopropriamente
2.9INSERINDODADOSNOBANCO 19
Em vez de usar o PreparedStatement, você pode usar uma interface mais simples chamada
Statement,quesimplesmenteexecutaumacláusulaSQLnométodoexecute:
Statementstmt=con.createStatement();stmt.execute("INSERTINTO...");stmt.close();
MasprefiraaclassePreparedStatementqueémaisrápidaqueStatementedeixaseucódigo
muitomaislimpo.
Geralmente, seus comandos SQL conterão valores vindos de variáveis do programa Java; usandoStatements,vocêteráquefazermuitasconcatenações,masusandoPreparedStatements,issofica
maislimpoefácil.
AAPIdedatasdoJava,mesmoconsiderandoalgumasmelhoriasdaCalendaremrelaçãoaDate,
aindaémuitopobre.Naversão8doJavatemosumaAPInova,ajava.time,quefacilitabastanteo
trabalho.ElaébaseadanaexcelentebibliotecadedataschamadaJodaTime.
JáfoipossívelsentirquecolocarcódigoSQLdentrodesuasclassesdelógicaéalgonemumpoucoeleganteemuitomenosviávelquandovocêprecisamanteroseucódigo.
Quantasvezesvocênãoficoubravocomoprogramadorresponsávelporaquelecódigoilegível?
Aideiaaseguiréremoverocódigodeacessoaobancodedadosdesuasclassesdelógicaecolocá-loemumaclasseresponsávelpeloacessoaosdados.Assimocódigodeacessoaobancodedadosficaemumlugarsó,tornandomaisfácilamanutenção.
QuetalsepudéssemoschamarummétodoadicionaqueadicionaumContatoaobanco?
Emoutraspalavrasqueroqueocódigoaseguirfuncione:
//adicionaosdadosnobancoMisteriobd=newMisterio();bd.adiciona("meunome","meuemail","meuendereço",meuCalendar);
Tem algo estranho nesse código. Repare que todos os parâmetros que estamos passando são asinformaçõesdocontato.Secontatotivesse20atributos,passaríamos20parâmetros?JavaéorientadoaStrings?Vamostentarnovamente:emoutraspalavrasqueroqueocódigoaseguirfuncione:
//adicionaumcontatonobancoMisteriobd=newMisterio();
Parasabermais:AmápráticaStatement
Parasabermais:JodaTime
2.10DAO-DATAACCESSOBJECT
20 2.10DAO-DATAACCESSOBJECT
//métodomuitomaiselegantebd.adiciona(contato);
Tentaremoschegaraocódigoanterior:seriamuitomelhoremaiselegantepoderchamarumúnicométodoresponsávelpelainclusão,certo?
publicclassTestaInsere{
publicstaticvoidmain(String[]args){
//prontoparagravarContatocontato=newContato();contato.setNome("Caelum");contato.setEmail("[email protected]");contato.setEndereco("R.Vergueiro3185cj87");contato.setDataNascimento(Calendar.getInstance());
//gravenessaconexão!!!Misteriobd=newMisterio();
//métodoelegantebd.adiciona(contato);
System.out.println("Gravado!");}}
Ocódigoanteriorjámostraopoderquealcançaremos:atravésdeumaúnicaclasseseremoscapazesdeacessarobancodedadose,maisainda,somenteatravésdessaclasseserápossívelacessarosdados.
Estaideia,inocenteàprimeiravista,écapazdeisolartodooacessoabancoemclassesbemsimples,cujainstânciaéumobjetoresponsávelporacessarosdados.DaresponsabilidadedesteobjetosurgiuonomedeDataAccessObjectousimplesmenteDAO,umdosmaisfamosospadrõesdeprojeto(designpattern).
Oque faltaparaocódigoacima funcionaréumaclassechamadaContatoDao comummétodo
chamadoadiciona.Vamoscriarumaqueseconectaaobancoaoconstruirmosumainstânciadela:
publicclassContatoDao{
//aconexãocomobancodedadosprivateConnectionconnection;
publicContatoDao(){this.connection=newConnectionFactory().getConnection();}}
Agora que todo ContatoDao possui uma conexão com o banco, podemos focar no método
adiciona, que recebe um Contato como argumento e é responsável por adicioná-lo através de
códigoSQL:
publicvoidadiciona(Contatocontato){Stringsql="insertintocontatos"+"(nome,email,endereco,dataNascimento)"+"values(?,?,?,?)";
2.10DAO-DATAACCESSOBJECT 21
try{//preparedstatementparainserçãoPreparedStatementstmt=con.prepareStatement(sql);
//setaosvalores
stmt.setString(1,contato.getNome());stmt.setString(2,contato.getEmail());stmt.setString(3,contato.getEndereco());stmt.setDate(4,newDate(contato.getDataNascimento().getTimeInMillis()));
//executastmt.execute();stmt.close();}catch(SQLExceptione){thrownewRuntimeException(e);}}
Encapsulamos a SQLException em uma RuntimeException mantendo a ideia anterior da
ConnectionFactorydedesacoplarocódigodeAPIdeJDBC.
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
1. CrieaclassedeContatonopacotebr.com.caelum.jdbc.modelo.ElaseránossoJavaBeanpara
representaraentidadedobanco.Deveráterid,nome,email,endereçoeumadatadenascimento.
Coloqueosatributosnaclasseegereosgettersesettersparacadaatributo:
publicclassContato{privateLongid;privateStringnome;privateStringemail;privateStringendereco;privateCalendardataNascimento;
Seuslivrosdetecnologiaparecemdoséculopassado?
2.11EXERCÍCIOS:JAVABEANSECONTATODAO
22 2.11EXERCÍCIOS:JAVABEANSECONTATODAO
}
Dica:useoatalhodoEclipseparagerarosgettersesetters.AperteCtrl+3,digiteggasqueéaabreviaçãodeGenerategettersandsetterseselecionetodososgettersesetters.
1. Vamos desenvolver nossa classe de DAO. Crie a classe ContatoDao no pacote
br.com.caelum.jdbc.dao. Seu papel será gerenciar a conexão e inserir Contatos no banco de
dados.
Paraaconexão,vamoscriá-lanoconstrutoresalvaremumatributo:
publicclassContatoDao{
//aconexãocomobancodedadosprivateConnectionconnection;
2.11EXERCÍCIOS:JAVABEANSECONTATODAO 23
publicContatoDao(){this.connection=newConnectionFactory().getConnection();}
}
UseoEclipseparaajudarcomosimports!(atalhoCtrl+Shift+O)
O próximo passo é criar o método de adição de contatos. Ele deve receber um objeto do tipoContatocomoargumentoeencapsulartotalmenteotrabalhocomobancodedados.Internamente,use
umPreparedStatementcomovimosnaaulaparaexecutaroSQL:
publicvoidadiciona(Contatocontato){Stringsql="insertintocontatos"+"(nome,email,endereco,dataNascimento)"+"values(?,?,?,?)";
try{//preparedstatementparainserçãoPreparedStatementstmt=connection.prepareStatement(sql);
//setaosvaloresstmt.setString(1,contato.getNome());stmt.setString(2,contato.getEmail());stmt.setString(3,contato.getEndereco());stmt.setDate(4,newDate(contato.getDataNascimento().getTimeInMillis()));
//executastmt.execute();stmt.close();}catch(SQLExceptione){thrownewRuntimeException(e);}}
ATENÇÃO:Lembre-sedeimportarasclassesdeSQLdopacotejava.sql,inclusiveaclasseDate!
1. Antesdetestarainserçãodeumcontatonobancodedados,verifiqueseatabelacontatosexiste.
Abraoterminaledigite:
mysql-uroot
Lembrandoque,sehouversenhaparalogarcomobanco,éprecisoescrevermysql-uroot-pe,
emseguida,digitarasenhadobancodedados.
DentrodoMySQL,listetodasastabelasjácriadascomocomando:
showtables;
Procurepelatabelacontatos.Seelanãoexistir,criecomocomando:
createtablecontatos(idBIGINTNOTNULLAUTO_INCREMENT,
24 2.11EXERCÍCIOS:JAVABEANSECONTATODAO
nomeVARCHAR(255),emailVARCHAR(255),enderecoVARCHAR(255),dataNascimentoDATE,primarykey(id));
(Dica:essecódigoencontra-senoarquivocontatos.sqlnapasta21/projeto-jdbc)
1. ParatestarnossoDAO,desenvolvaumaclassedetestescomométodomain.Porexemplo,uma
chamadaTestaInserenopacotebr.com.caelum.jdbc.teste.GereomainpeloEclipse.
Onossoprogramadetestesdeve,dentrodomain,criarumnovoobjetoContatocomdadosde
testeechamaranovaclasseContatoDaoparaadicioná-loaobancodedados:
//prontoparagravarContatocontato=newContato();contato.setNome("Caelum");contato.setEmail("[email protected]");contato.setEndereco("R.Vergueiro3185cj57");contato.setDataNascimento(Calendar.getInstance());
//gravenessaconexão!!!ContatoDaodao=newContatoDao();
//métodoelegantedao.adiciona(contato);
System.out.println("Gravado!");
Executeseuprogramaevejasetudocorreubem.
1. Verifiqueseocontatofoiadicionado.Abraoterminaledigite:
mysql-urootusefj21;select*fromcontatos;
Lembrandoquesehouversenhaparaousuárioroot,oprimeirocomandodeveser:mysql-u
root-p.DigiteexitparasairdoconsoledoMySQL.
Para pesquisar tambémutilizamos a interfacePreparedStatement paramontar nosso comando
SQL. Mas como uma pesquisa possui um retorno (diferente de uma simples inserção), usaremos ométodoexecuteQueryqueretornatodososregistrosdeumadeterminadaquery.
OobjetoretornadoédotipoResultSetdoJDBC,oquenospermitenavegarporseusregistros
atravésdométodonext.Essemétodoretornaráfalsequandochegaraofimdapesquisa,portanto
eleénormalmenteutilizadoparafazerumlaçonosregistros:
//pegaaconexãoeoStatement
2.12FAZENDOPESQUISASNOBANCODEDADOS
2.12FAZENDOPESQUISASNOBANCODEDADOS 25
Connectioncon=newConnectionFactory().getConnection();PreparedStatementstmt=con.prepareStatement("select*fromcontatos");
//executaumselectResultSetrs=stmt.executeQuery();
//iteranoResultSetwhile(rs.next()){}
rs.close();stmt.close();con.close();
Para retornarovalordeumacolunanobancodedados,basta chamarumdosmétodosget do
ResultSet,dentreosquais,omaiscomum:getString.
//pegaaconexãoeoStatementConnectioncon=newConnectionFactory().getConnection();PreparedStatementstmt=con.prepareStatement("select*fromcontatos");
//executaumselectResultSetrs=stmt.executeQuery();
//iteranoResultSetwhile(rs.next()){Stringnome=rs.getString("nome");Stringemail=rs.getString("email")
System.out.println(nome+"_"+email);}
stmt.close();con.close();
RECURSOAVANÇADO:OCURSOR
Assimcomoocursordobancodedados, só épossívelmoverparaopróximo registro.ParapermitirumprocessodeleituraparatrásénecessárioespecificarnaaberturadoResultSetque
talcursordeveserutilizado.
Mas, novamente, podemos aplicar as ideias deDAO e criar ummétodogetLista() no nossoContatoDao . Mas o que esse método retornaria? Um ResultSet ? E teríamos o código de
manipulaçãodeResultSetespalhadoportodoocódigo?VamosfazernossogetLista() devolver
algomaisinteressante,umalistadeContato:
PreparedStatementstmt=this.connection.prepareStatement("select*fromcontatos");ResultSetrs=stmt.executeQuery();
List<Contato>contatos=newArrayList<Contato>();
26 2.12FAZENDOPESQUISASNOBANCODEDADOS
while(rs.next()){
//criandooobjetoContatoContatocontato=newContato();contato.setNome(rs.getString("nome"));contato.setEmail(rs.getString("email"));contato.setEndereco(rs.getString("endereco"));
//montandoadataatravésdoCalendarCalendardata=Calendar.getInstance();data.setTime(rs.getDate("dataNascimento"));contato.setDataNascimento(data);
//adicionandooobjetoàlistacontatos.add(contato);}
rs.close();stmt.close();
returncontatos;
1. CrieométodogetListanaclasseContatoDao.ImporteListdejava.util:
publicList<Contato>getLista(){try{List<Contato>contatos=newArrayList<Contato>();PreparedStatementstmt=this.connection.prepareStatement("select*fromcontatos");ResultSetrs=stmt.executeQuery();
while(rs.next()){//criandooobjetoContatoContatocontato=newContato();contato.setId(rs.getLong("id"));contato.setNome(rs.getString("nome"));contato.setEmail(rs.getString("email"));contato.setEndereco(rs.getString("endereco"));
//montandoadataatravésdoCalendarCalendardata=Calendar.getInstance();data.setTime(rs.getDate("dataNascimento"));contato.setDataNascimento(data);
//adicionandooobjetoàlistacontatos.add(contato);}rs.close();stmt.close();returncontatos;}catch(SQLExceptione){thrownewRuntimeException(e);}}
1. VamosusarométodogetListaparalistartodososcontatosdonossobancodedados.
2.13EXERCÍCIOS:LISTAGEM
2.13EXERCÍCIOS:LISTAGEM 27
CrieumaclassechamadaTestaListacomummétodomain:
CrieumContatoDao:
ContatoDaodao=newContatoDao();
ListeoscontatoscomoDAO:
List<Contato>contatos=dao.getLista();
Iterenessalistaeimprimaasinformaçõesdoscontatos:
for(Contatocontato:contatos){System.out.println("Nome:"+contato.getNome());System.out.println("Email:"+contato.getEmail());System.out.println("Endereço:"+contato.getEndereco());System.out.println("DatadeNascimento:"+contato.getDataNascimento().getTime()+"\n");}
1. RodeoprogramaacimaclicandoemRun>Runas>JavaApplication(aproveiteparaaprenderatecladeatalhoparaexecutaraaplicação).
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
AssimcomooMySQLexistemoutrosbancosdedadosgratuitoseopensourcenainternet.OH2éumbancodesenvolvidoemJavaquepodeseracopladoaqualqueraplicaçãoeliberaoclientedanecessidadedebaixarqualquerbancodedadosantesdainstalaçãodeumprodutoJava!
OHibernate tomoucontadomercadoevirou febremundial poisnão se faznecessário escreverumalinhadecódigoSQL!
SeumprojetonãousanenhumadastecnologiasdeORM(ObjectRelationalMapping)disponíveis,omínimoaserfeitoéseguiroDAO.
Agoraéamelhorhoradeaprenderalgonovo
2.14UMPOUCOMAIS...
28 2.14UMPOUCOMAIS...
1. A impressão da data de nascimento ficou um pouco estranha. Para formatá-la, pesquise sobre aclasseSimpleDateFormat.
2. Crie uma classe chamadaDAOException que estenda deRuntimeException e utilize-a no seu
ContatoDao.
3. Usecláusulaswherepararefinarsuapesquisanobancodedados.Porexemplo:wherenomelike
'C%'
4. Crieométodopesquisarquerecebeumid(int)eretornaumobjetodotipoContato.
1. Façaconexõesparaoutrostiposdebancodedadosdisponíveis.
AgoraquevocêjásabeusaroPreparedStatementparaexecutarqualquertipodecódigoSQLe
ResultSetparareceberosdadosretornadosdasuapesquisaficasimples,porémmaçante,escrevero
códigodediferentesmétodosdeumaclassetípicadeDAO.
Vejaprimeiroométodoaltera,querecebeumcontatocujosvaloresdevemseralterados:
publicvoidaltera(Contatocontato){Stringsql="updatecontatossetnome=?,email=?,endereco=?,"+"dataNascimento=?whereid=?";try{PreparedStatementstmt=connection.prepareStatement(sql);stmt.setString(1,contato.getNome());stmt.setString(2,contato.getEmail());stmt.setString(3,contato.getEndereco());stmt.setDate(4,newDate(contato.getDataNascimento().getTimeInMillis()));stmt.setLong(5,contato.getId());stmt.execute();stmt.close();}catch(SQLExceptione){thrownewRuntimeException(e);}}
Nãoexistenadadenovonaslinhasacima.Umaexecuçãodequery!Simples,não?
Ocódigopararemoção:começacomumaquerybaseadaemumcontato,masusasomenteoiddeleparaexecutaraquerydotipodelete:
publicvoidremove(Contatocontato){try{PreparedStatementstmt=connection.prepareStatement("delete"+"fromcontatoswhereid=?");stmt.setLong(1,contato.getId());
2.15EXERCÍCIOSOPCIONAIS
Desafios
2.16OUTROSMÉTODOSPARAOSEUDAO
2.15EXERCÍCIOSOPCIONAIS 29
stmt.execute();stmt.close();}catch(SQLExceptione){thrownewRuntimeException(e);}}
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
1. AdicioneométodoparaalterarcontatonoseuContatoDao.
publicvoidaltera(Contatocontato){Stringsql="updatecontatossetnome=?,email=?,"+"endereco=?,dataNascimento=?whereid=?";
try{PreparedStatementstmt=connection.prepareStatement(sql);stmt.setString(1,contato.getNome());stmt.setString(2,contato.getEmail());stmt.setString(3,contato.getEndereco());stmt.setDate(4,newDate(contato.getDataNascimento().getTimeInMillis()));stmt.setLong(5,contato.getId());stmt.execute();stmt.close();}catch(SQLExceptione){thrownewRuntimeException(e);}}
1. AdicioneométodopararemovercontatonoseuContatoDao
publicvoidremove(Contatocontato){try{PreparedStatementstmt=connection.prepareStatement("deletefromcontatoswhereid=?");stmt.setLong(1,contato.getId());stmt.execute();
EditoraCasadoCódigocomlivrosdeumaformadiferente
2.17EXERCÍCIOSOPCIONAIS-ALTERAREREMOVER
30 2.17EXERCÍCIOSOPCIONAIS-ALTERAREREMOVER
stmt.close();}catch(SQLExceptione){thrownewRuntimeException(e);}}
1. Useosmétodoscriadosanteriormenteparafazertestescomoseubancodedados:atualizeeremovaumcontato.
2. CrieumaclassechamadaFuncionariocomoscamposid(Long),nome,usuarioesenha(String).3. Crieumatabelanobancodedadoschamadafuncionarios.4. CrieumaclasseDAOparaFuncionario.5. Use-aparainstanciarnovosfuncionáriosecolocá-losnoseubanco.
2.17EXERCÍCIOSOPCIONAIS-ALTERAREREMOVER 31
CAPÍTULO3
"Ensinaréaprenderduasvezes."--JosephJoubert
Aotérminodessecapítulo,vocêserácapazde:
EntenderoqueéoJavaEnterpriseEdition;DiferenciarumServidordeAplicaçãodeumServletContainer;InstalarumServletContainercomooApacheTomcat;ConfigurarumServletContainerdentrodoEclipse.
As aplicaçõesWebdehoje emdia jápossuem regrasdenegóciobastante complicadas.Codificaressasmuitasregrasjárepresentamumgrandetrabalho.Alémdessasregras,conhecidascomorequisitosfuncionais de uma aplicação, existem outros requisitos que precisam ser atingidos através da nossainfraestrutura:persistênciaembancodedados,transação,acessoremoto,webservices,gerenciamentode threads, gerenciamento de conexões HTTP, cache de objetos, gerenciamento da sessão web,balanceamentodecarga,entreoutros.Sãochamadosderequisitosnão-funcionais.
Seformostambémosresponsáveisporescrevercódigoquetratedessesoutrosrequisitos,teríamosmuitomaistrabalhoafazer.Tendoissoemvista,aSuncriouumasériedeespecificaçõesque,quandoimplementadas, podem ser usadas por desenvolvedores para tirar proveito e reutilizar toda essainfraestruturajápronta.
OJavaEE(JavaEnterpriseEdition)consistedeumasériedeespecificaçõesbemdetalhadas,dandouma receita de como deve ser implementado um software que faz cada um desses serviços deinfraestrutura.
Veremosnodecorrerdessecursováriosdessesserviçosecomoutilizá-los,focandonoambientededesenvolvimentowebatravésdoJavaEE.Veremos tambémconceitosmuito importantes,paradepoisconceituartermosfundamentaiscomoservidordeaplicaçãoecontainers.
PorqueaSunfazisso?Aideiaéquevocêpossacriarumaaplicaçãoqueutilizeessesserviços.Comoesses serviços são bem complicados, você não perderá tempo implementando essa parte do sistema.Existemimplementaçõestantoopensourcequantopagas,ambasdeboaqualidade.
OQUEÉJAVAEE?
3.1COMOOJAVAEEPODETEAJUDARAENFRENTARPROBLEMAS
32 3OQUEÉJAVAEE?
Algum dia, você pode querer trocar essa implementação atual por uma que é mais rápida emdeterminadospontos,queusemenosmemória,etc.Fazendoessamudançadeimplementaçãovocênãoprecisará alterar seu software, já que o JavaEE é uma especificaçãomuito bemdeterminada.O quemuda é a implementação da especificação: você tem essa liberdade, não está preso a um código e aespecificaçãogarantequesuaaplicaçãofuncionarácomaimplementaçãodeoutrofabricante.Esseéumatrativomuitograndeparagrandesempresasegovernos,quequeremsempreevitarovendor lock-in:expressãousadaquandovocêestápresosemprenasmãosdeumúnicofabricante.
ONDEENCONTRARASESPECIFICAÇÕES
O grupo responsável por gerir as especificações usa o site do Java Community Process:http://www.jcp.org/
Lá você pode encontrar tudo sobre as Java SpecificationRequests (JSR), isto é, os novospedidosdebibliotecaseespecificaçõesparaoJava,tantoparaJavaSE,quantoEEeoutros.
SobreoJavaEE,vocêpodeencontrarem:http://java.sun.com/javaee/
J2EE
OnomeJ2EEerausadonasversõesmaisantigas,atéa1.4.Hoje,onomecorretoéJavaEE,porumaquestãodemarketing,masvocêaindavaiencontrarmuitas referênciasaoantigo termoJ2EE.
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
Agoraéamelhorhoradeaprenderalgonovo
3.1COMOOJAVAEEPODETEAJUDARAENFRENTARPROBLEMAS 33
AsAPIsaseguirsãoasprincipaisdentreasdisponibilizadaspeloJavaEnterprise:
JavaServer Pages (JSP), Java Servlets, Java Server Faces (JSF) (trabalhar para a Web, onde éfocadoestecurso)
Enterprise Javabeans Components (EJB) e Java Persistence API (JPA). (objetos distribuídos,clusters,acessoremotoaobjetosetc)
JavaAPIforXMLWebServices(JAX-WS),JavaAPIforXMLBinding(JAX-B)(trabalharcomarquivosxmlewebservices)
JavaAutenthicationandAuthorizationService(JAAS)(APIpadrãodoJavaparasegurança)
JavaTransactionAPI(JTA)(controledetransaçãonocontêiner)
JavaMessageService(JMS)(trocademensagensassíncronas)
JavaNamingandDirectoryInterface(JNDI)(espaçodenomeseobjetos)
JavaManagementExtensions(JMX)(administraçãodasuaaplicaçãoeestatísticassobreamesma)
AúltimaversãodisponíveldaespecificaçãodoJavaEEéaversão7, lançadaem12de junhode2013.Éumaversão aindamuito recente, compoucas ferramentas e servidores disponíveis.Aversãomaisusadanomercadoéaversão6,de2009.Estecursoéfocadonaversão6quevocêencontraránomercadoe jáapresentandoasnovidadesdonovoJavaEE7quedeveganharespaçonomercadonospróximosanos.
Neste curso FJ-21, atacamos especialmente JSP e Servlets. No curso FJ-26, estuda-se comprofundidadeJSFcomCDIeJBossSeam.NoFJ-25éapresentadoemdetalheJPAcomHibernateeoEJB. No FJ-36, estuda-se as especificações mais relacionadas a sistemas de alto desempenho: EJB,JNDI,JMS,JAX-BalémdeWebServices(JAX-WSeJAX-RS).
JSPeServletssãosemdúvidaasespecificaçõesessenciaisquetododesenvolvedorJavavaiprecisarparadesenvolvercomaWeb.MesmousandoframeworksebibliotecasquefacilitamotrabalhoparaaWeb, conhecer bem essas especificações é certamente um diferencial, e fará com que você entendamotivaçõesedificuldades,auxiliandonatomadadedecisõesarquiteturaisededesign.
Como vimos, o Java EE é um grande conjunto de especificações. Essas especificações, quandoimplementadas,vãoauxiliarbastanteodesenvolvimentoda suaaplicação,poisvocênãoprecisará sepreocuparcomgrandepartedecódigodeinfraestrutura,quedemandariamuitotrabalho.
3.2ALGUMASESPECIFICAÇÕESDOJAVAEE
3.3SERVIDORDEAPLICAÇÃO
34 3.2ALGUMASESPECIFICAÇÕESDOJAVAEE
Comofazero"downloaddoJavaEE"?O JavaEEé apenasumgrandePDF, uma especificação,detalhando quais especificações fazem parte deste. Para usarmos o software, é necessário fazer odownloaddeumaimplementaçãodessasespecificações.
Existemdiversas dessas implementações. Já que esse software tempapel de servir sua aplicaçãoparaauxiliá-lacomserviçosdeinfraestrutura,essesoftwareganhaonomedeservidordeaplicação.AprópriaSun/Oracledesenvolveumadessasimplementações,oGlassfishqueéopensourceegratuito,porémnãoéolíderdemercadoapesardeganharforçanosúltimostempos.
Existem diversos servidores de aplicação famosos compatíveis com a especificação do J2EE 1.4,JavaEE5ealguns jádoJavaEE6.OJBosséumdos líderesdomercadoe temavantagemdesergratuitoeopensource.AlgunssoftwaresimplementamapenasumapartedessasespecificaçõesdoJavaEE, comooApacheTomcat, que só implementa JSP eServlets (comodissemos, duas das principaisespecificações),portantonãoé totalmentecorretochamá-lodeservidordeaplicação.ApartirdoJavaEE6, existeo termo"applicationserverwebprofile",parapoder se referenciar a servidoresquenãooferecemtudo,masumgrupomenordeespecificações,consideradasessenciaisparaodesenvolvimentoweb.
Você pode ver uma lista de servidores Java EE 5 aqui:http://java.sun.com/javaee/overview/compatibility-javaee5.jsp
E Java EE 6 aqui, onde a lista ainda está crescendo:http://java.sun.com/javaee/overview/compatibility.jsp
Algunsdosservidoresdeaplicaçãomaisconhecidosdomercado:
Oracle/Sun,GlassFishServerOpenSourceEdition4.0,gratuito,JavaEE7;RedHat,JBossApplicationServer7.x,gratuito,JavaEE6;Apache,ApacheGeronimo,gratuito,JavaEE6(nãocertificado);Oracle/BEA,OracleWebLogicServer8.x,JavaEE6;IBM,IBMWebSphereApplicationServer,JavaEE6;SAP,SAPNetWeaverApplicationServerouSAPWebApplicationServer,JavaEE6WebProfile;
Nos cursos da Caelum utilizamos o Apache Tomcat e o RedHat JBoss, mas todo conhecimentoadquiridoaquipodeseraplicadocomfacilidadeparaosoutrosservidorescompatíveis,mudandoapenasaformadeconfigurá-los.
NocursoFJ-36,estuda-seprofundamentealgumasdasoutrastecnologiasenvolvidasnoJavaEE:oEJB,oJMS,oJAX-WSparaWebServiceseoJNDI.
O Java EE possui várias especificações, entre elas, algumas específicas para lidar com o
3.4SERVLETCONTAINER
3.4SERVLETCONTAINER 35
desenvolvimentodeumaaplicaçãoWeb:
ServletJSPJSTLJSF
UmServletContaineréumservidorquesuportaessasfuncionalidades,masnãonecessariamenteoJavaEEWebProfilenemoJavaEEcompleto.ÉindicadoaquemnãoprecisadetudodoJavaEEeestáinteressadoapenasnaparteweb(boapartedasaplicaçõesdemédioporteencaixam-senessacategoria).
Háalgunsservletcontainersfamososnomercado.OmaisfamosoéoApacheTomcat,masháoutroscomooJetty,quenósdaCaelumusamosmuitoemprojetoseoGoogleusaemseucloudGoogleAppEngine.
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
ParaprepararoTomcatnaCaelum,sigaosseguintespassos:
1. Entrenapasta21/projeto-agenda/tomcat-9doDesktopeselecioneoarquivodoapache-tomcat-9.x;
2. DêdoiscliquesparaabriroArchiveManagerdoLinux;
EditoraCasadoCódigocomlivrosdeumaformadiferente
3.5EXERCÍCIOS:PREPARANDOOTOMCAT
36 3.5EXERCÍCIOS:PREPARANDOOTOMCAT
3. CliqueemExtract;4. EscolhaoseuDesktopecliqueemextract;
5. Oresultadoéumapastachamadaapache-tomcat-9.x.Pronto,otomcatjáestáinstalado.
Baixe o Tomcat 9 em http://tomcat.apache.org/ na página de downloads da versão que
escolher,vocêprecisarádeuma"BinaryDistribution".Mesmonowindows,dêpreferênciaaversão.zip,paravocê entendermelhoroprocessode inicializaçãodo servidor.Aversão executável é apenasumwrapperparaexecutaraJVM,jáqueoTomcaté100%Java.
OTomcatfoipormuitotempoconsideradoaimplementaçãopadrãoereferênciadasnovasversões
3.6PREPARANDOOTOMCATEMCASA
3.6PREPARANDOOTOMCATEMCASA 37
da API de servlets. Ele também é o servlet container padrão utilizado pelo JBoss. Ele continua emprimeiraposiçãonomercado,mashojetemesselugardisputadopeloJettyepeloGrizzly(esseúltimoéoservletcontainerquefazpartedoservidordeaplicaçãodaOracle/Sun,oGlassfish).
Entrenodiretóriodeinstalação,depermissãodeexecuçãoatodososscriptsdessapastaeporfimexecuteostartup.shparaoservidorinicializar:
cdapache-tomcat<TAB>/binsudochmod+x*startup*.sh./startup.sh
UsingCATALINA_BASE:/home/fsouto/ÁreadeTrabalho/apache-tomcat-9.0.34UsingCATALINA_HOME:/home/fsouto/ÁreadeTrabalho/apache-tomcat-9.0.34...Tomcatstarted.
Parapararoservidorutilizeoscriptshutdown.sh:
cdapache-tomcat<TAB>/bin./shutdown.sh
AprenderemosfuturamentecomoiniciarocontainerdedentrodopróprioEclipse,porcomodidadeeparafacilitarousododebug.
ParainstalaroTomcatnoWindowsbastaexecutaroarquivo.exequepodeserbaixadonositedo
Tomcat(comofalamos,dêpreferênciaaozip).Depoisdisso,vocêpodeusarosscriptsstartup.bate
shutdown.bat,analogamenteaosscriptsdoLinux.
TudooquevamosdesenvolvernestecursofuncionaemqualquerambientecompatívelcomoJavaEnterpriseEdition,sejaoLinux,WindowsouMacOS.
OJettyéumaoutraimplementaçãocriadapelaMortBay(https://www.eclipse.org/jetty/)deServletContainereHTTPServer.Em2009passouaserhospedadonafundaçãoEclipse.
Pequeno e eficiente, ele é uma opção ao Tomcat bastante utilizada devido a algumas de suascaracterísticas.Especialmente:
facilmenteembarcável;escalável;"plugabilidade":éfáciltrocarasimplementaçõesdosprincipaiscomponentesdaAPI.
OJettytambémcostumaimplementar,antesdoTomcat,ideiasdiferentesqueaindanãoestãonaAPIdeservletsdoJavaEE.UmadessasimplementaçõespioneirasfoidousodoschamadosconectoresNIO,
TomcatnoWindows
3.7OUTRAOPÇÃO:JETTY
38 3.7OUTRAOPÇÃO:JETTY
porexemplo,quepermitiramumaperformancemelhorparaousodeAJAX.
O GUJ.com.br roda com o Jetty, em uma instalação customizada que pode ser lida aqui:https://blog.caelum.com.br/melhorando-o-guj-jetty-nio-e-load-balancing/amp/
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
Sempre que estamos trabalhando como desenvolvimento de uma aplicação queremos ser omaisprodutivos possível, e não é diferente com uma aplicação web. Uma das formas de aumentar aprodutividade do desenvolvedor é utilizar uma ferramenta que auxilie no desenvolvimento e o tornemaiságil,nonossocaso,umaIDE.
O WTP, Web Tools Platform, é um conjunto de plugins para o Eclipse que auxilia odesenvolvimentodeaplicaçõesJavaEE,emparticular,deaplicaçõesWeb.ContémdesdeeditoresparaJSP,CSS,JSeHTMLatéperspectivasejeitosderodarservidoresdedentrodoEclipse.
Estepluginvainosajudarbastantecomcontent-assistseatalhosparatornarodesenvolvimentoWebmaiseficiente.
AversãodedownloadEclipseIDEforJavaEEDevelopersjávemporpadrãocomoplugin.Sendoassim,bastairnositedoEclipsee:
Abraapáginawww.eclipse.org/downloads;
BaixeoEclipseIDEforJavaEEDevelopers;Descompacteoarquivoepronto.
JáconheceoscursosonlineAlura?
3.8INTEGRANDOOTOMCATNOECLIPSE
3.9OPLUGINWTP
3.8INTEGRANDOOTOMCATNOECLIPSE 39
VamosprimeiroconfigurarnoWTPoservidorTomcatqueacabamosdedescompactar.
1. Mude a perspectiva doEclipse paraJava (e não Java EE, por enquanto). Para isso, vá no cantodireitosuperiorecliquenobotão:
DepoisselecioneJava:
2. AbraaViewdeServersnaperspectivaatual.AperteCtrl+3edigiteServers:
3. CliquecomobotãodireitodentrodaabaServerseváemNew>Server:
3.10EXERCÍCIOS:CONFIGURANDOOTOMCATNOECLIPSE
40 3.10EXERCÍCIOS:CONFIGURANDOOTOMCATNOECLIPSE
4. SelecioneoApacheTomcat9.0ecliqueemNext:
5. Napróximatela,selecioneodiretórioondevocêdescompactouoTomcatecliqueemFinish:
3.10EXERCÍCIOS:CONFIGURANDOOTOMCATNOECLIPSE 41
6. Porpadrão,oWTPgerenciatodooTomcatparanósenãopermitequeconfiguraçõessejamfeitasporforadoEclipse.Parasimplificar,vamosdesabilitarissoedeixaroTomcatnomodopadrãodopróprioTomcat.NaabaServers,dêdoiscliquesnoservidorTomcatqueumateladeconfiguraçãoseabrirá. Localize a seção Server Locations. Repare que a opção use workspace metadata estámarcada.MarqueaopçãoUseTomcatinstallation:Percebaqueapareceuumasterisconaaba,salveefecheessatela.
42 3.10EXERCÍCIOS:CONFIGURANDOOTOMCATNOECLIPSE
7. Selecione o servidor que acabamos de adicionar e clique em Start (ícone play verde na viewservers):
8. Abra o navegador e acesse a URL http://localhost:8080/ Deve aparecer uma tela de
mensagemdoTomcat.Pronto!OWTPestáconfiguradopararodarcomoTomcat!
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
VocêpodetambémfazerocursodatadessaapostilanaCaelum
3.10EXERCÍCIOS:CONFIGURANDOOTOMCATNOECLIPSE 43
CAPÍTULO4
"Sãomuitososqueusamarégua,maspoucososinspirados."--Platão
Aotérminodessecapítulo,vocêserácapazde:
criarumnovoprojetoWebnoEclipse;compreenderquaissãoosdiretóriosimportantesdeumaaplicaçãoWeb;compreenderquaissãoosarquivosimportantesdeumaaplicaçãoWeb;entenderondecolocarsuaspáginasearquivosestáticos.
Nesse capítulo veremos como, passo a passo, criar um novo projetoWeb no Eclipse usando osrecursosdoEclipseJavaEE.Dessaformanãoprecisarmosiniciaroservletcontainernamão,alémdepermitiraconfiguraçãodeumprojeto,desuasbibliotecas,edeseudebug,deumamaneirabemmaissimplesdoquesemele
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
1. VáemNew>ProjecteselecioneDynamicWebProjectecliqueNext:
NOVOPROJETOWEBUSANDOECLIPSE
4.1NOVOPROJETO
Seuslivrosdetecnologiaparecemdoséculopassado?
4.2EXERCÍCIOS:NOVOPROJETOWEB
44 4NOVOPROJETOWEBUSANDOECLIPSE
2. Coloqueonomedoprojetocomofj21-agenda,verifiqueseoTargetruntimeestáapontandoparaaversãodoTomcatqueacabamosdeconfigurareseoDynamicwebmoduleversionestáconfiguradopara4.0.DepoiscliqueNext.ATENÇÃO:NÃOCLIQUEEMFinishAINDA!!!
4.2EXERCÍCIOS:NOVOPROJETOWEB 45
3. CliqueemNextnaconfiguraçãodaspastas:
46 4.2EXERCÍCIOS:NOVOPROJETOWEB
4. NaconfiguraçãoWebmodulemarqueoboxGenerateweb.xmldeploymentdescriptorecliqueemFinish:
5. SeforperguntadosobreamudançaparaaperspectivaJavaEE,selecioneNão.
4.2EXERCÍCIOS:NOVOPROJETOWEB 47
6. O último passo é configurar o projeto para rodar noTomcat que configuramos.Na abaServers,cliquecomobotãodireitonoTomcateváemAddandRemove...:
7. Bastaselecionaronossoprojetofj21-agendaeclicaremAdd:
8. Dêumaolhadanaspastasqueforamcriadasenaestruturadonossoprojetonesseinstante.Vamosanalisá-laemdetalhes.
4.3ANÁLISEDORESULTADOFINAL
48 4.3ANÁLISEDORESULTADOFINAL
Olhebemaestruturadepastaseveráalgoparecidocomoquesegue:
Odiretóriosrc já é velho conhecido. É onde vamos colocar nosso código fonte Java. Em um
projetonormaldoEclipse,essasclassesseriamcompiladasparaapastabin,masnoEclipseécostume
seusarapastabuildquevemosemnossoprojeto.
Nosso projeto será composto demuitas páginasWeb e vamos querer acessá-lo no navegador. Jásabemosqueoservidoréacessadopelohttp://localhost:8080,mascomoseráquedizemosque
queremosacessaronossoprojetoenãooutrosprojetos?
No Java EE, trabalhamos com o conceito de contextosWeb para diferenciar sites ou projetosdistintosemummesmoservidor.Naprática,écomoumapastavirtualque,quandoacessada,remeteaalgumprojetoemquestão.
Porpadrão,oEclipsegeraocontextname comomesmonomedoprojeto;nonossocaso, fj21-agenda.PodemosmudarissonahoradecriaroprojetoouposteriormenteindoemProject>Properties>WebProjectSettingsemudandoaopçãoContextRoot.Reparequenãoénecessárioqueonomedocontextosejaomesmonomedoprojeto.
Assim,paraacessaroprojeto,usaremosaURL:http://localhost:8080/fj21-agenda/
MasoqueseráquevaiaparecerquandoabrirmosessaURL?Seráqueveremostodooconteúdodoprojeto?Porexemplo,serápossívelacessarapastasrcqueestádentrodonossoprojeto?Tomaraquenão,afinaltodonossocódigofonteestálá.
Para solucionar isso, uma outra configuração é importante no Eclipse: oContentDirectory. Aoinvésdeabriracessoatudo,criamosumapastadentrodoprojetoedizemosqueelaéaraiz(root)doconteúdo a ser exibido no navegador. No Eclipse, por padrão, é criada a pastaWebContent, mas
4.3ANÁLISEDORESULTADOFINAL 49
poderia ser qualquer outra pasta configurada na criação do projeto (outro nome comumde se usar éweb).
Tudoque colocarmosnapastaWebContent será acessível naURLdo projeto. Por exemplo, se
queremosumapáginadeboasvindas:
http://localhost:8080/fj21-agenda/bemvindo.html
entãocriamosoarquivo:
fj21-agenda/WebContent/bemvindo.html
Repare também que dentro da WebContent há uma pasta chamada WEB-INF . Essa pasta é
extremamente importante para qualquer projeto web Java EE. Ela contém configurações e recursosnecessáriosparanossoprojetorodarnoservidor.
Oweb.xmléoarquivoondeficaráarmazenadaasconfiguraçõesrelativasasuaaplicação,usaremosessearquivoembreve.
Porenquanto,abra-oevejasuaestrutura,atéentãobemsimples:
<?xmlversion="1.0"encoding="UTF-8"?><web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"id="WebApp_ID"version="2.5">
<display-name>fj21-agenda</display-name>
<welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list></web-app>
ÉobásicogeradopelopróprioEclipse.Tudooqueelefazédefinironomedaaplicaçãoealistadearquivosacessadosquevãoserprocuradosporpadrão.Todasessasconfiguraçõessãoopcionais.
Repare ainda que há uma pasta chamada lib dentro daWEB-INF. Quase todos os projetosWebexistentes precisamusar bibliotecas externas, comopor exemplo o driver doMySQLno nosso caso.Copiaremostodaselasparaessapastalib.Comoessediretóriosóaceitabibliotecas,apenascolocamosnele arquivos .jar ou arquivos zip com classes dentro. Caso um arquivo com outra extensão sejacolocadonolib,eleseráignorado.
WEB-INF
50 4.3ANÁLISEDORESULTADOFINAL
WEB-INF/LIB
O diretório lib dentro do WEB-INF pode conter todas as bibliotecas necessárias para aaplicação Web, evitando assim que o classpath da máquina que roda a aplicação precise seralterado.
Além do mais, cada aplicaçãoWeb poderá usar suas próprias bibliotecas com suas versõesespecíficas!Vocêvaiencontrarprojetosopensourcequesomentefornecemsuporteerespondemperguntas aqueles usuários que utilizam tal diretório para suas bibliotecas, portanto evite aomáximoousodoclasspathglobal.
Parasabermaissobreousodeclassloaderseclasspathglobal,vocêpodeprocurarnocapítulo2dolivroIntroduçãoàArquiteturaeDesigndeSoftware,publicadopelaEditoraCasadoCódigoepelaElsevier.
Há ainda umúltimo diretório, oculto noEclipse, o importanteWEB-INF/classes. Para rodarmosnossa aplicação no servidor, precisamos ter acesso as classes compiladas (não necessariamente aocódigo fonte).Por isso, nossos.class são colocados nessa pasta dentro do projeto.Esse padrão é
definido pela especificação de Servlets do JavaEE.Repare que oEclipse compila nossas classes napastabuildedepoisautomaticamenteascopiaparaoWEB-INF/classes.
NotequeapastaWEB-INFémuitoimportanteecontémrecursosvitaisparaofuncionamentodoprojeto. Imagine se o usuário tiver acesso a essa pasta! Códigos compilados (facilmentedescompiláveis), bibliotecas potencialmente sigilosas, arquivos de configuração internos contendosenhas,etc.
Paraque issonãoaconteça,apastaWEB-INF comessenomeespecial éumapasta invisível aousuário final. Isso quer dizer que se alguém acessar a URL http://localhost:8080/fj21-agenda/WEB-INFveráapenasumapáginadeerro(404).
src-códigofonteJava(.java)build-ondeoEclipsecompilaasclasses(.class)WebContent-contentdirectory(páginas,imagens,cssetcvãoaqui)WebContent/WEB-INF/-pastaocultacomconfiguraçõeserecursosdoprojetoWebContent/WEB-INF/lib/-bibliotecas.jarWebContent/WEB-INF/classes/-arquivoscompiladossãocopiadosparacá
Resumofinaldaspastas
4.3ANÁLISEDORESULTADOFINAL 51
META-INF
ApastaMETA-INFéopcionalmaségeradapeloEclipse.Éondeficaoarquivodemanifesto
comousadoemarquivos.jar.
Para criarmos as nossas páginas, precisamos utilizar uma linguagem que consiga ser interpretadapelosnavegadores.EssalinguageméaHTML(HypertextMarkupLanguage).
Comoopróprionomediz,elaéumalinguagemdemarcação,oquesignificaqueelaécompostaportagsquedefinemocomportamentodapáginaqueseestácriando.Essastagsdizemcomoapáginaserávisualizada, através da definição dos componentes visuais que aparecerão na tela. É possível exibirtabelas,parágrafos,blocosdetexto,imagenseassimpordiante.
TodoarquivoHTMLdeveconteraextensão.html,eseuconteúdodeveestardentrodatag<html>.
EmumHTMLbemformado,todasastagsquesãoabertastambémsãofechadas,dessaforma,todooseucódigonumarquivo .html ficarádentrode<html> e</html>.Alémdisso, podemos também
definiralgunsdadosdecabeçalhosparanossapágina,comoporexemplo,otítuloqueserácolocadonajaneladonavegador,atravésdastags<head>e<title>:
<html><head><title>Títuloquevaiaparecernonavegador</title></head></html>
Para escrevermos algo que seja exibido dentro do navegador, no corpo da nossa página, bastacolocarmosatag<body>dentrode<html>,comoaseguir:
<html><body>Textoquevaiaparecernocorpodapágina</body></html>
4.4CRIANDONOSSASPÁGINASEHTMLBÁSICO
52 4.4CRIANDONOSSASPÁGINASEHTMLBÁSICO
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
VamostestarnossasconfiguraçõescriandoumarquivoHTMLdeteste.
1. CrieoarquivoWebContent/index.htmlcomoseguinteconteúdo:
<html><head><title>Projetofj21-agenda</title></head><body><h1>Primeirapáginadoprojetofj21-agenda</h1></body></html>
2. Inicie(oureinicie)oTomcatclicandonobotãodeplaynaabaServers.
3. Acesse pelo navegador (nas máquinas da caelum existe um Firefox instalado):http://localhost:8080/fj21-agenda/index.html
Testetambémaconfiguraçãodowelcome-filenoweb.xml:http://localhost:8080/fj21-agenda/
SefosseocasodecriarumaaplicaçãowebsemutilizaroplugindoTomcat,precisaríamoscolocarnossaaplicaçãodentrodoTomcatmanualmente.
Dentro do diretório do Tomcat, há um diretório chamado webapps. Podemos colocar nossasaplicaçõesdentrodessediretório,cadaumadentrodeumdiretórioespecífico.Onomedodiretórioseráonomedocontextodaaplicação.
Por exemplo, para colocar nossa aplicação fj21-agenda no Tomcat manualmente, basta copiar oconteúdododiretórioWebContentparaumdiretóriochamado,porexemplo,agenda dentrodesse
Agoraéamelhorhoradeaprenderalgonovo
4.5EXERCÍCIOS:PRIMEIRAPÁGINA
4.6PARASABERMAIS:CONFIGURANDOOTOMCATSEMOPLUGIN
4.5EXERCÍCIOS:PRIMEIRAPÁGINA 53
diretóriowebappsdoTomcat.Pronto,oTomcatserviráanossaaplicaçãocomocontextoagenda.ParaqueoTomcatsirvanossaaplicaçãonocontexto/,bastacolocarnossaaplicaçãodentrodeumdiretório
chamadoROOT.
DevidoaofocodocursonãosernoHTML,nãomostraremosafundoastagsdoHTML.Noentanto,abaixoestáumalistacomalgumasdastagsmaiscomunsdoHTML:
h1 -h6:definecabeçalhos,doh1 aoh6, ondemenoronúmero,maior a importância do
cabeçalho;a:criaumlinkparaoutrapágina,ouparaalgumpontodamesmapágina;
p:definequeotextodentrodelaestaráemumparágrafo;
ul:criaumalistadeelementos;
li:criaumnovoitemnumalistadeelementos;
input:colocaumcontroledeformulárionatela(caixadetexto,botão,radiobuttonetc.)
table:defineumatabela;
tr:colocadadentrodeumtableparadefinirumalinhadatabela;
td:colocadadentrodeumtrparadefinirumacélula;
TUTORIALDEHTML
Para um tutorial completo sobre HTML, recomendamos uma visita ao site da MozillaDeveloper Network, que possui referências das tags e exemplos de uso:https://developer.mozilla.org/pt-BR/docs/Web/HTML
Você também pode visitar o tutorial de HTML da W3schools.com:http://www.w3schools.com/html/
4.7ALGUMASTAGSHTML
54 4.7ALGUMASTAGSHTML
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
EditoraCasadoCódigocomlivrosdeumaformadiferente
4.7ALGUMASTAGSHTML 55
CAPÍTULO5
"Vivemostodossobomesmocéu,masnemtodostemosomesmohorizonte."--KonradAdenauer
Aotérminodessecapítulo,vocêserácapazde:
fazercomqueumaclassesejaacessívelvianavegador;criarpáginascontendoformulários;recebereconverterparâmetrosenviadosporumapágina;distinguirosmétodosHTTP;executarsuaslógicaseregrasdenegócio.
Quando aWeb surgiu, seu objetivo era a troca de conteúdos através, principalmente, de páginasHTMLestáticas.EramarquivosescritosnoformatoHTMLedisponibilizadosemservidoresparaseremacessadosnosnavegadores.Imagens,animaçõeseoutrosconteúdostambémeramdisponibilizados.
MaslogoseviuqueaWebtinhaumenormepotencialdecomunicaçãoeinteraçãoalémdaexibiçãodesimplesconteúdos.Paraatingiressenovoobjetivo,porém,páginasestáticasnãoseriamsuficientes.EraprecisoservirpáginasHTMLgeradasdinamicamentebaseadasnasrequisiçõesdosusuários.
Hoje,boapartedoqueseacessanaWeb(portais,blogs,homebankingsetc)ébaseadoemconteúdodinâmico.Ousuáriorequisitaalgoaoservidorque,porsuavez,processaessarequisiçãoedevolveumarespostanovaparaousuário.
Umadasprimeirasideiasparaesses"geradoresdinâmicos"depáginasHTMLfoifazeroservidorWeb invocar um outro programa externo em cada requisição para gerar oHTML de resposta. Era ofamosoCGIquepermitiaescreverpequenosprogramasparaapresentarpáginasdinâmicasusando,porexemplo,Perl,PHP,ASPeatéCouC++.
Na plataforma Java, a primeira e principal tecnologia capaz de gerar páginas dinâmicas são asServlets, que surgiram no ano de 1997. Hoje, a versão mais encontrada no mercado é baseada nasversões2.x,maisespecificamentea2.4(partedoJ2EE1.4)ea2.5(partedoJavaEE5).Aúltimaversãodisponível é a versão 4.0.3 lançada emAgosto de 2019. Já o JavaEE está na versão 8, lançada emSetembrode2017.
SERVLETS
5.1PÁGINASDINÂMICAS
56 5SERVLETS
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
AsServletssãoa primeira formaqueveremosde criar páginas dinâmicas com Java.Usaremos apróprialinguagemJavaparaisso,criandoumaclassequeterácapacidadedegerarconteúdoHTML.Onome"servlet"vemdaideiadeumpequenoservidor(servidorzinho,eminglês)cujoobjetivoéreceberchamadasHTTP,processá-lasedevolverumarespostaaocliente.
Umaprimeiraideiadaservletseriaquecadaumadelaséresponsávelporumapágina,sendoqueelalêdadosdarequisiçãodoclienteerespondecomoutrosdados(umapáginaHTML,umaimagemGIFetc).ComonoJavatentamossemprequepossíveltrabalharorientadoaobjetos,nadamaisnaturalqueumaservletsejarepresentadacomoumobjetoapartirdeumaclasseJava.
Cada servlet é, portanto, um objeto Java que recebe tais requisições (request) e produz algo(response),comoumapáginaHTMLdinamicamentegerada.
OdiagramaabaixomostratrêsclientesacessandoomesmoservidoratravésdoprotocoloHTTP:
JáconheceoscursosonlineAlura?
5.2SERVLETS
5.2SERVLETS 57
OcomportamentodasservletsquevamosvernestecapítulofoidefinidonaclasseHttpServletdo
pacotejavax.servlet.
AinterfaceServletéaquedefineexatamentecomoumaservletfunciona,masnãoéoquevamos
utilizar,umavezqueelapossibilitaousodequalquerprotocolobaseadoemrequisiçõeserespostas,enãoespecificamenteoHTTP.
Paraescrevermosumaservlet,criamosumaclasseJavaqueestendaHttpServlet e sobrescreva
ummétodo chamado service. Essemétodo será o responsável por atender requisições e gerar as
respostasadequadas.Suaassinatura:
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{...}
Reparequeométodorecebedoisobjetosquerepresentam,respectivamente,arequisiçãofeitapelousuário e a resposta que será exibida no final. Veremos que podemos usar esses objetos para obterinformaçõessobrearequisiçãoeparaconstruirarespostafinalparaousuário.
Nosso primeiro exemplo de implementação dométodo service não executa nada de lógica e
apenasmostraumamensagemestáticadebemvindoparaousuário.Paraisso,precisamosconstruirarespostaqueaservletenviaráparaocliente.
É possível obter um objeto que represente a saída a ser enviada ao usuário através do métodogetWriterdavariávelresponse.E,apartirdisso,utilizarumPrintWriterparaimprimiralgona
respostadocliente:
publicclassOiMundoextendsHttpServlet{protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
PrintWriterout=response.getWriter();
//escreveotextoout.println("<html>");out.println("<body>");out.println("Primeiraservlet");out.println("</body>");out.println("</html>");}}
OúnicoobjetivodaservletacimaéexibirumamensagemHTMLsimplesparaosusuáriosquearequisitarem.MasnotecomoseriamuitofácilescreveroutroscódigosJavamaispoderososparagerarasStringsdoHTMLbaseadaseminformaçõesdinâmicasvindas,porexemplo,deumbancodedados.
58 5.2SERVLETS
SERVLETXCGI
Diversasrequisiçõespodemserfeitasàmesmaservletaomesmotempoemumúnicoservidor.Porisso,elaémaisrápidaqueumprogramaCGIcomumquenãopermitiaisso.AespecificaçãodeservletscitaalgumasvantagensemrelaçãoaoCGI.
Ficanamemóriaentrerequisições,nãoprecisaserreinstanciada;OníveldesegurançaepermissãodeacessopodesercontroladoemJava;emCGI,cadaclienteérepresentadoporumprocesso,enquantoquecomServlets,cadaclienteérepresentadoporumalinhadeexecução.
Esse capítulo está focado naHttpServlet, um tipo que gera aplicaçõesWeb baseadas no
protocoloHTTP,masvalelembrarqueaAPInãofoicriadasomenteparaesteprotocolo,podendoserestendidaparaoutrosprotocolostambémbaseadosemrequisiçõeserespostas.
Acabamos de definir uma Servlet, mas como vamos acessá-la pelo navegador? Qual o endereçopodemos acessar para fazermos com que ela execute? O container não tem como saber essasinformações,anãoserquedigamosissoparaele.Paraisso,vamosfazerummapeamentodeumaURLespecíficaparaumaservletatravésdoarquivoweb.xml,queficadentrodoWEB-INF.
Uma vez que chamar a servlet pelo pacote e nome da classe acabaria criandoURLs estranhas ecomplexas,écomummapear,porexemplo,umaservletcomonoexemplo,chamadaOiMundoparao
nomeprimeiraServlet.
Começamoscomadefiniçãodaservletemsi,dentrodatag<servlet>:
<servlet><servlet-name>primeiraServlet</servlet-name><servlet-class>br.com.caelum.servlet.OiMundo</servlet-class></servlet>
Em seguida, mapeie nossa servlet para a URL /oi. Perceba que isso acontece dentro da tag
<servlet-mapping> (mapeamentodeservlets)equevocê temque indicarqueestáfalandodaquela
servletquedefinimoslogoacima:passamosomesmoservlet-nameparaomapeamento.
<servlet-mapping><servlet-name>primeiraServlet</servlet-name><url-pattern>/oi</url-pattern></servlet-mapping>
Portanto,sãonecessáriosdoispassosparamapearumaservletparaumaURL:
5.3MAPEANDOUMASERVLETNOWEB.XML
5.3MAPEANDOUMASERVLETNOWEB.XML 59
Definironomeeclassedaservlet;Usandoonomedaservlet,definiraURL.
AservletpodeseracessadaatravésdaseguinteURL:
http://localhost:8080/fj21-agenda/oi
Assimqueo arquivoweb.xml e a classe da servlet de exemplo forem colocados nos diretórios
corretos,bastaconfiguraroTomcatparautilizarodiretóriodebasecomopadrãoparaumaaplicaçãoWeb.
Atag<url-pattern>tambémtedáaflexibilidadededisponibilizarumaservletatravésdevárias
URLsdeumcaminho,porexemploocódigoabaixofarácomquequalquerendereçoacessadodentrode/oisejainterpretadopelasuaservlet:
<servlet-mapping><servlet-name>primeiraServlet</servlet-name><url-pattern>/oi/*</url-pattern></servlet-mapping>
Vocêaindapodeconfigurar"extensões"paraassuasservlets,porexemplo,omapeamentoabaixofarácomquesuaservletsejachamadaporqualquerrequisiçãoqueterminecom.php:
<servlet-mapping><servlet-name>primeiraServlet</servlet-name><url-pattern>*.php</url-pattern></servlet-mapping>
Reparequenãocriamosdiretórionenhumnanossaaplicação(excetoopacoteparaanossaclasseServlet). Ou seja, o mapeamento da servlet não tem relação alguma com um diretório físico naaplicação. Essemapeamento é apenas um nome atribuído, virtual, que é utilizado para acessarmos aaplicação.
Maissobreourl-pattern
5.4AESTRUTURADEDIRETÓRIOS
60 5.4AESTRUTURADEDIRETÓRIOS
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
1. ParacriarnossasServletsprecisamosusarasclasseseinterfacesquepertencemaAPIdeServlet,ouseja, precisamos adicionar o jar dessa especificação ao Classpath. Se o projeto foi criadocorretamente o Tomcat 9 faz parte do nosso Classpath e como consequência todas as suasdependênciastambém.Dessaformapodemosusarojarquevemjuntodele:
CasooTomcat9nãofaçapartedonossoClasspathpodemosadicionarmanualmenteadependênciaao nosso projeto. Acesse a pasta 21/projeto-agenda/servlet e copie o arquivo jakarta.servlet-api-4.X.X.jarecoledentrodonossoprojeto,nodiretórioWebContent/WEB-INF/lib:
VocêpodetambémfazerocursodatadessaapostilanaCaelum
5.5EXERCÍCIOS:PRIMEIRASERVLET
5.5EXERCÍCIOS:PRIMEIRASERVLET 61
2.CrieaservletOiMundonopacotebr.com.caelum.servlet.EscolhaomenuFile,New,Class(maisumavez,aproveiteparaaprenderteclasdeatalho).
EstendaHttpServlet:
publicclassOiMundoextendsHttpServlet{}
UtilizeoCTRL+SHIFT+OparaimportarHttpServlet.
Para escrever a estrutura do método service, dentro da classe, escreva apenas service e dêCtrl+espaço:oEclipsegerapravocêométodo.
62 5.5EXERCÍCIOS:PRIMEIRASERVLET
ATENÇÃO: Cuidado para escolher corretamente a versão de service que recebeHttpServletRequest/Response.
Aanotação@Overrideserveparanotificarocompiladorqueestamossobrescrevendoométodo
servicedaclassemãe.Se,poracaso,errarmosonomedométodoou trocarmosaordemdos
parâmetros,ocompiladorvaireclamarevocêvaiperceberoerroaindaemtempodecompilação.
Ométodogeradodeveseresse.Troqueosnomesdosparâmetrosarg0earg1comoabaixo:
@Overrideprotectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{}
Escreva dentro do método service sua implementação. Por enquanto, queremos apenas que
nossaServletmonteumapáginaHTMLsimplesparatestarmos.
Cuidadoemtirarachamadaaosuper.serviceantesereparequeadeclaraçãodométodojáfoi
feitanopassoanterior.
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
PrintWriterout=response.getWriter();
out.println("<html>");out.println("<head>");out.println("<title>PrimeiraServlet</title>");out.println("</head>");out.println("<body>");out.println("<h1>OimundoServlet!</h1>");out.println("</body>");out.println("</html>");}
3.Abraoarquivoweb.xmlecliquenaabaSourcenaparteinferiordoeditordecódigo.Dentrodatag<web-app>,mapeieaURL/oiparaaservletOiMundo.AproveiteoautocompletardoEclipseecuidadoaoescreveronomedaclasseedopacote.
5.5EXERCÍCIOS:PRIMEIRASERVLET 63
<servlet><servlet-name>servletOiMundo</servlet-name><servlet-class>br.com.caelum.servlet.OiMundo</servlet-class></servlet>
<servlet-mapping><servlet-name>servletOiMundo</servlet-name><url-pattern>/oi</url-pattern></servlet-mapping>
4. Reinicie o Tomcat clicando no botão verde na aba Servers. 5. Teste a urlhttp://localhost:8080/fj21-agenda/oi
Existemdiversoserroscomunsnosexercíciosanteriores.Aquivãoalgunsdeles:
EsquecerdabarrainicialnoURLpattern:
<url-pattern>oi</url-pattern>
5.6ERROSCOMUNS
64 5.6ERROSCOMUNS
Nessecaso,umaexceçãoaconteceránomomentoemqueotomcatforinicializado:
Digitarerradoonomedopacotedasuaservlet:
<servlet-class>br.caelum.servlet.OiMundo</servlet-class>
Esquecerdecolocaronomedaclassenomapeamentodaservlet:
<servlet-class>br.com.caelum.servlet</servlet-class>
Como foi visto no exercício anterior, criar Servlets usando o Java EE 5 é um processo muitotrabalhoso. Um dos grandes problemas é que temos que configurar cada um de nossas Servlets noweb.xml e se quisermos acessar essa servlet de maneiras diferentes, temos que criar váriosmapeamentosparaamesmaservlet,oquepodecomo tempo tornar-seumproblemadevidoadifícilmanutenção.
ApartirdaespecificaçãoServlets3.0,que fazpartedoJavaEE6,podemosconfiguraramaneiracomovamosacessaranossaServletdemaneiraprogramática,utilizandoanotaçõessimples.
5.7FACILIDADESDASSERVLETS3.0
5.7FACILIDADESDASSERVLETS3.0 65
Demodogeral,nãoémaisprecisoconfigurarasnossasServletsnoweb.xml,sendosuficienteusaraanotação@WebServletapenas:
@WebServlet("/oi")publicclassOiServlet3extendsHttpServlet{...}
IssoéequivalenteaconfiguraraServletacimacomaurl-patternconfiguradacomo/oi.
Naanotação@WebServlet,podemoscolocaraindaumparâmetroopcionalchamadoname que
defineumnomeparaaServlet(equivalenteaoservlet-name).Senãodefinirmosesseatributo,por
padrão,onomedaServletéonomecompletodaclassedasuaServlet,tambémconhecidocomoFullyQualifiedName.
SequisermosquenossaServletsejaacessadoatravésdeapenasumaURL,recomenda-sedefiniraURLdiretamentenoatributovaluecomonoexemploacima.Masseprecisarmosdefinirmaisdeuma
URLparaacessaraServlet,podemosutilizaroatributourlPatternsepassarumvetordeURLs:
@WebServlet(name="MinhaServlet3",urlPatterns={"/oi","/ola"})publicclassOiServlet3extendsHttpServlet{...}
É bom reforçar que, mesmo a Servlet estando anotado com @WebServlet() , ele deve
obrigatoriamenterealizarumextendsaclassejavax.servlet.http.HttpServlet.
ARQUIVOWEB.XML
Dentro da tag <web-app> no web.xml, existe um novo atributo chamado metadata-
complete. Nesse atributo podemos configurar se nossas classes anotadas com @WebServlet
serãoprocuradaspeloservidordeaplicação.Sedefinirmoscomotrueasclassesanotadasserão
ignoradas.
SenãodefinirmosoudefinirmoscomofalseasclassesqueestiveremnoWEB-INF/classes
ouemalgum.jardentroWEB-INF/libserãoexaminadaspeloservidordeaplicação.
66 5.7FACILIDADESDASSERVLETS3.0
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
Mesmo sendo uma prática questionada por alguns desenvolvedores, podemos passar parâmetrosprogramaticamenteparaasServletsnasuainicializaçãoesemprecisardoarquivoweb.xml.Bastausaraanotação@WebInitParam(), para declarar cadaparâmetronopadrão chave/valor e depois passá-los
dentrodeumvetorparaapropriedadeinitParamsdaanotação@WebServlet():
@WebServlet(name="OiServlet3",urlPatterns={"/oi"},initParams={@WebInitParam(name="param1",value="value1"),@WebInitParam(name="param2",value="value2")})publicclassOiServlet3{...}
Pararecuperá-losdentrodaServlettemostrêsestratégias:
Usandoasobrecargadométodoinit()dasServlets:
//códigoomitidoprivateStringparametro1;privateStringparametro2;
@Overridepublicvoidinit(ServletConfigconfig)throwsServletException{super.init(config);this.parametro1=config.getInitParameter("param1");this.parametro2=config.getInitParameter("param2");}
EmqualqueroutrométododaServletpormeiodeumobjetodaclasseServletConfig:
publicvoidservice(HttpServletRequestrequest,
Seuslivrosdetecnologiaparecemdoséculopassado?
5.8PARASABERMAIS:WEBSERVLETEINITPARAMANNOTATION
5.8PARASABERMAIS:WEBSERVLETEINITPARAMANNOTATION 67
HttpServletResponseresponse)throwsServletException,IOException{
response.setContentType("text/html");PrintWriterout=response.getWriter();
out.println("<h2>ExemplocomInitParamServlet</h2>");
ServletConfigconfig=getServletConfig();
Stringparametro1=config.getInitParameter("param1");out.println("Valordoparâmetro1:"+parametro1);
Stringparametro2=config.getInitParameter("param2");out.println("<br>Valordoparâmetro1:"+parametro2);
out.close();}
OuusandoométodogetServletConfig()comgetInitParameter()diretonaopçãodesaída:
out.println("Valordoparâmetro1:"+getServletConfig().getInitParameter("param1"));
AodesenvolverumaaplicaçãoWeb,sempreprecisamosrealizaroperaçõesnoladodoservidor,comdadosinformadospelousuário,sejaatravésdeformuláriosousejaatravésdaURL.
Porexemplo,paragravarmosumcontatonobancodedados,precisamosdonome,e-mail,endereçoeadatadenascimentodele.Temosumapáginacomumformulárioqueousuáriopossapreenchereaoclicaremumbotãoessesdadosdevem,dealgumaforma,serpassadosparaumServlet.Jásabemosquea Servlet responde por uma determinada URL (através do url-pattern/v2.5 ou do urlPatterns/v3.0),portanto,sóprecisamosindicarqueaoclicarnobotãodevemosenviarumarequisiçãoparaessaServlet.
Para isso, vamos criar uma página HTML, chamada adiciona-contato.html , contendo um
formulárioparapreenchermososdadosdoscontatos:
<html><body><formaction="adicionaContato">Nome:<inputtype="text"name="nome"/><br/>E-mail:<inputtype="text"name="email"/><br/>Endereço:<inputtype="text"name="endereco"/><br/>DataNascimento:<inputtype="text"name="dataNascimento"/><br/>
<inputtype="submit"value="Gravar"/></form></body></html>
Essecódigopossuiumformulário,determinadopelatag<form>.Oatributoactionindicaqual
endereçodeveserchamadoaosubmeteroformulário,aoclicarnobotãoGravar.Nessecaso,estamosapontandooactionparaumendereçoqueseráumaServletquejávamoscriar.
5.9ENVIANDOPARÂMETROSNAREQUISIÇÃO
68 5.9ENVIANDOPARÂMETROSNAREQUISIÇÃO
Aoacessarapáginaadiciona-contato.html,oresultadodeverásersimilaràfiguraabaixo:
Pararecebermososvaloresqueforampreenchidosnatelaesubmetidos,criaremosumaServlet,
cujafunçãoseráreceberdealgumamaneiraessesdadoseconvertê-los,senecessário.
DentrodométodoservicedanossaServletparaadiçãodecontatos,vamosbuscarosdadosque
foramenviadosnarequisição.Parabuscarmosessesdados,precisamosutilizaroparâmetrorequestdométodoservice chamando ométodo getParameter("nomeDoParametro"), onde o nome do
parâmetroéomesmonomedoinputquevocêquerbuscarovalor.IssovairetornarumaString
comovalordoparâmetro.Casonãoexistaoparâmetro,seráretornadonull:
StringvalorDoParametro=request.getParameter("nomeDoParametro");
OfatodeserdevolvidaumaStringnostrazumproblema,pois,adatadenascimentodocontato
está criada como um objeto do tipo Calendar. Então, o que precisamos fazer é converter essaStringemumobjetoCalendar.MasaAPIdoJavaparatrabalharcomdatasnãonospermitefazer
issodiretamente.TeremosqueconverterantesaStringemumobjetodotipojava.util.Datecom
auxíliodaclasseSimpleDateFormat,utilizandoométodoparse,daseguinteforma:
StringdataEmTexto=request.getParameter("dataNascimento");Datedate=newSimpleDateFormat("dd/MM/yyyy").parse(dataEmTexto);
Repare que indicamos também o pattern (formato) com que essa data deveria chegar para nós,atravésdoparâmetropassadonoconstrutordeSimpleDateFormatcomovalordd/MM/yyyy.TemosquetomarcuidadopoisométodoparselançaumaexceçãodotipoParseException.Essaexceção
indicaqueoquefoipassadonadatanãopôdeserconvertidoaopatternespecificado.Comoobjetodotipojava.util.Date que foi devolvido, queremos criar um Calendar. Para isso vamos usar o
métodosetTimedaclasseCalendar,querecebeumDate.
dataNascimento=Calendar.getInstance();dataNascimento.setTime(date);
VamosutilizartambémonossoDAOparagravaroscontatosnobancodedados.Nofinal,anossa
5.10PEGANDOOSPARÂMETROSDAREQUISIÇÃO
5.10PEGANDOOSPARÂMETROSDAREQUISIÇÃO 69
Servletficarádaseguinteforma:
@WebServlet("/adicionaContato")publicclassAdicionaContatoServletextendsHttpServlet{protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException,ServletException{
PrintWriterout=response.getWriter();
//pegandoosparâmetrosdorequestStringnome=request.getParameter("nome");Stringendereco=request.getParameter("endereco");Stringemail=request.getParameter("email");StringdataEmTexto=request.getParameter("dataNascimento");CalendardataNascimento=null;
//fazendoaconversãodadatatry{Datedate=newSimpleDateFormat("dd/MM/yyyy").parse(dataEmTexto);dataNascimento=Calendar.getInstance();dataNascimento.setTime(date);}catch(ParseExceptione){out.println("Errodeconversãodadata");return;//paraaexecuçãodométodo}
//montaumobjetocontatoContatocontato=newContato();contato.setNome(nome);contato.setEndereco(endereco);contato.setEmail(email);contato.setDataNascimento(dataNascimento);
//salvaocontatoContatoDaodao=newContatoDao();dao.adiciona(contato);
//imprimeonomedocontatoquefoiadicionadoout.println("<html>");out.println("<body>");out.println("Contato"+contato.getNome()+"adicionadocomsucesso");out.println("</body>");out.println("</html>");}}
70 5.10PEGANDOOSPARÂMETROSDAREQUISIÇÃO
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
1. Comovamosprecisargravarcontatos,precisaremosdasclassesparatrabalharcombancodedadosque criamos no capítulo de JDBC. Para isso, deixamos disponível um arquivo zip contendo asclassesnecessáriasquecriamosanteriormente.
NoEclipse,selecioneoprojetofj21-agendaevánomenuFile->Import
DentrodajaneladeImport,escolhaGeneral->ArchiveFileecliqueemNext:
No campoFromarchive file clique emBrowse, selecioneo arquivo /21/projeto-agenda/dao-modelo.zipecliqueemFinish
vejaque,napastaWEB-INF/lib encontram-se2driversMySQL.Verifiquequal a versãoque
vocêestáutilizando,eremovaodriverquevocênãoprecisar.
EMCASA
Casovocêesteja fazendoemcasa,vocêpodeusarexatamenteasmesmasclassescriadasdurante os exercícios do capítulo de JDBC, mas terá que fazer uma mudança na classeConnectionFactory(volteaocapítulo2,noboxsobreoClass.forName.NãoesqueçadecopiartambémoDriverdoMySQL.
2. Temosquecriarapáginaquepermitiráaosusuárioscadastraroscontatos
VánomenuFile->New->Other.
Agoraéamelhorhoradeaprenderalgonovo
5.11 EXERCÍCIOS: CRIANDO FUNCIONALIDADE PARA GRAVARCONTATOS
5.11EXERCÍCIOS:CRIANDOFUNCIONALIDADEPARAGRAVARCONTATOS 71
EscolhaWeb->HTMLPageouHTMLFileecliqueNext:
Chame o arquivo de adiciona-contato.html e clique emFinish (garanta que o arquivo estejadentrododiretórioWebContent):
EssearquivoHTMLdeveráteroseguinteconteúdo(cuidadocomonomedosinputs):
<html><body><h1>AdicionaContatos</h1><hr/><formaction="adicionaContato">Nome:<inputtype="text"name="nome"/><br/>E-mail:<inputtype="text"name="email"/><br/>Endereço:<inputtype="text"name="endereco"/><br/>DataNascimento:<inputtype="text"name="dataNascimento"/><br/>
<inputtype="submit"value="Gravar"/></form>
72 5.11EXERCÍCIOS:CRIANDOFUNCIONALIDADEPARAGRAVARCONTATOS
</body></html>
Acessenonavegadoroendereço:
http://localhost:8080/fj21-agenda/adiciona-contato.html
3. PrecisamoscriaraServletquegravaráocontatonobancodedados:
Crie uma nova Servlet no pacote br.com.caelum.agenda.servlet chamado
AdicionaContatoServletcomoseguintecódigo.
Cuidadoaoimplementaressaclasse,queégrandeecomplicada.
UseoCtrl+Shift+Oparaajudarnosimports.AclasseDatedeveserdejava.utileaclasse
ParseException,dejava.text.
@WebServlet("/adicionaContato")publicclassAdicionaContatoServletextendsHttpServlet{protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException,ServletException{//buscaowriterPrintWriterout=response.getWriter();
//buscandoosparâmetrosnorequestStringnome=request.getParameter("nome");Stringendereco=request.getParameter("endereco");Stringemail=request.getParameter("email");StringdataEmTexto=request.getParameter("dataNascimento");CalendardataNascimento=null;
//fazendoaconversãodadatatry{Datedate=newSimpleDateFormat("dd/MM/yyyy").parse(dataEmTexto);dataNascimento=Calendar.getInstance();dataNascimento.setTime(date);}catch(ParseExceptione){out.println("Errodeconversãodadata");return;//paraaexecuçãodométodo}
5.11EXERCÍCIOS:CRIANDOFUNCIONALIDADEPARAGRAVARCONTATOS 73
//montaumobjetocontatoContatocontato=newContato();contato.setNome(nome);contato.setEndereco(endereco);contato.setEmail(email);contato.setDataNascimento(dataNascimento);
//salvaocontatoContatoDaodao=newContatoDao();dao.adiciona(contato);
//imprimeonomedocontatoquefoiadicionadoout.println("<html>");out.println("<body>");out.println("Contato"+contato.getNome()+"adicionadocomsucesso");out.println("</body>");out.println("</html>");}}
UTILIZANDOASERVLETV2.5
Se ainda estivéssemos utilizando a versão 2.5 da Servlet, precisaríamos fazer o seguintemapeamentonoweb.xml:
<servlet><servlet-name>AdicionaContato</servlet-name><servlet-class>br.com.caelum.agenda.servlet.AdicionaContatoServlet</servlet-class></servlet>
<servlet-mapping><servlet-name>AdicionaContato</servlet-name><url-pattern>/adicionaContato</url-pattern></servlet-mapping>
Reinicieoservidor,paraqueanovaServletsejareconhecido
Acesse novamente no navegador a URL http://localhost:8080/fj21-agenda/adiciona-contato.html
PreenchaoformulárioecliqueemGravar.Oresultadodevesersemelhanteàimagemaseguir:
74 5.11EXERCÍCIOS:CRIANDOFUNCIONALIDADEPARAGRAVARCONTATOS
Verifiquenobancodedadosseodadorealmentefoiadicionadocomsucesso.
Repare que no exercício anterior, ao clicarmos no botão salvar, todos os dados que digitamos noformulário aparecem na URL da página de sucesso. Isso acontece porque não definimos no nossoformulárioaformacomqueosdadossãoenviadosparaoservidor,atravésdoatributomethodparao
<form>daseguinteforma:
<formaction="adicionaContato"method="POST">
Comonão tínhamosdefinido,porpadrãoentãoéusadoométodoGET,que indicaqueosvaloresdosparâmetrossãopassadosatravésdaURLjuntodosnomesdosmesmos,separadospor&,comoem:nome=Adriano&[email protected]
Podemos tambémdefinir ométodoparaPOST e, dessa forma, os dados sãopassadosdentrodo
corpodoprotocoloHTTP,semaparecernaURLqueémostradanonavegador.
Podemos, além de definir no formulário como os dados serão passados, também definir quaismétodosHTTPnossaservletaceitará.
OmétodoserviceaceitatodososmétodosHTTP,portanto,tantoométodoGETquantooPOST.
Para especificarmos como trataremos cada método, temos que escrever os métodos doGet e/ou
doPostnanossaservlet:
voiddoGet(HttpServletRequestreq,HttpServletResponseres);
voiddoPost(HttpServletRequestreq,HttpServletResponseres);
OUTROSMÉTODOSHTTP
AlémdoGETedoPOST,oprotocoloHTTPpossui aindamais7métodos:PUT,DELETE,HEAD,TRACE,CONNECT,OPTIONSePATCH.
MuitaspessoasconhecemapenasoGETePOST,pois,sãoosúnicosqueHTML4suporta.
OqueseráquevaiacontecersealgumSQLdonossoDAOcontivererrodesintaxeeocomandonãopuderserexecutado?Seráquevaiaparecerumamensagemagradávelparaousuário?
Naverdade,casoaconteçaumerrodentrodanossaServletastacktracedaexceçãoocorridaserá
mostradaemumatelapadrãodocontainer.Oproblemaéqueparaousuáriocomum,amensagemde
5.12GET,POSTEMÉTODOSHTTP
5.13TRATANDOEXCEÇÕESDENTRODASERVLET
5.12GET,POSTEMÉTODOSHTTP 75
errodoJavanãofaráomenorsentido.Oidealseriamostrarmosumapáginadeerrodizendo:"Umerroocorreu"ecominformaçõesdecomonotificaroadministrador.
Parafazermosisso,bastaconfigurarmosnossaaplicaçãodizendoque,casoaconteçaumaException,uma página de erro deverá ser exibida. Essa configuração é feita no web.xml, com a seguintedeclaração:
<error-page><exception-type>java.lang.Exception</exception-type><location>/erro.html</location></error-page>
Alémdetratarmosasexceçõesquepodemacontecernanossaaplicação,podemostambémtrataroscódigos de erro HTTP, como por exemplo, 404, que é o erro dado quando se acessa uma páginainexistente.Paraissobastafazermosadeclaraçãonoweb.xml:
<error-page><error-code>404</error-code><location>/404.html</location></error-page>
WRAPPINGEMSERVLETEXCEPTION
Caso aconteça uma exceção que seja do tipo checada (não filha de RuntimeException),
tambémteríamosque repassá-laparacontainer.Noentanto,ométodoservice só nospermite
lançarServletExceptioneIOException.
Para podermos lançar outra exceção checked, precisamos escondê-la em umaServletException,comoaseguir:
try{//códigoquepodelançarSQLException}catch(SQLExceptione){thrownewServletException(e);}
Essa técnica é conhecida como wrapping de exceptions. O container, ao receber aServletException,vaidesembrulharaexceptioninternaetratá-la.
76 5.13TRATANDOEXCEÇÕESDENTRODASERVLET
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
1. Vamoscriarumapáginaparamostraramensagemgenéricadetratamento:
CrieumnovoHTMLchamadoerro.htmlcomoseguinteconteúdo:
<html><body>Umerroocorreu!</body></html>
Adicioneadeclaraçãodapáginadeerronoweb.xml:
<error-page><exception-type>java.lang.Exception</exception-type><location>/erro.html</location></error-page>
AltereousuáriodeacessoaobanconaclasseConnectionFactoryderootparaalgumoutrousuárioquenãoexista,porexemplo,toor.
Reinicieoservidor,paraqueasalteraçõestenhamefeito
AcessenonavegadoraURLhttp://localhost:8080/fj21-agenda/adiciona-contato.html
PreenchaoformulárioecliqueemGravar,oresultadodevesersemelhanteàimagemaseguir:
EditoraCasadoCódigocomlivrosdeumaformadiferente
5.14EXERCÍCIO:TRATANDOEXCEÇÕESECÓDIGOSHTTP
5.14EXERCÍCIO:TRATANDOEXCEÇÕESECÓDIGOSHTTP 77
AlterenovamenteousuáriodeacessoaobanconaclasseConnectionFactorypararoot.
1. Vamoscriarumapáginaparaserexibidaquandoousuárioacessaralgoinexistente:
CrieumnovoHTMLchamado404.htmlcomoseguinteconteúdo:
<html><body>Apáginaacessadanãoexiste.</body></html>
Adicioneadeclaraçãodapáginanoweb.xml:
<error-page><error-code>404</error-code><location>/404.html</location></error-page>
Reinicienovamenteoservidor;
Acesse no navegador uma URL inexistente no projeto, por exemplo, http://localhost:8080/fj21-agenda/naoexiste.html:
TodaServletdevepossuirumconstrutorsemargumentosparaqueocontainerpossacriá-lo.Apósacriação,oservletcontainerinicializaaServletcomométodoinit(ServletConfigconfig)eousa
durante todoo seuperíodoativo, atéquevaidesativá-lo atravésdométododestroy(), para então
liberaroobjeto.
ÉimportanteperceberqueasuaServletseráinstanciadoumaúnicavezpelocontainereesseúnicoobjetoseráusadoparaatenderatodasasrequisiçõesdetodososclientesemthreadsseparadas.Aliás,éjustoissoquetrazumamelhoriaemrelaçãoaosCGIcomunsquedisparavamdiversosprocessos.
NainicializaçãodeumaServlet,quandoparâmetrospodemserlidosevariáveiscomunsatodasasrequisiçõesdevemserinicializadas,éumbommomento,porexemplo,paracarregararquivosdiversosdeconfiguraçõesdaaplicação:
voidinit(ServletConfigconfig);
5.15INITEDESTROY
78 5.15INITEDESTROY
Nafinalização,devemosliberarpossíveisrecursosqueestejamossegurando:
voiddestroy();
Osmétodosinit edestroy, quando reescritos, sãoobrigados a chamarosuper.init() e
super.destroy() respectivamente. Isso acontece pois um método é diferente de um construtor.
Quando estendemos uma classe e criamos o nosso próprio construtor da classe filha, ela chama oconstrutor da classe pai sem argumentos, preservando a garantia da chamada de um construtor. Omesmonãoacontececomosmétodos.
Supondoqueométodoinit(oudestroy)executaalgumatarefafundamentalemsuaclassepai,
sevocêesquecerdechamarosuper,teráproblemas.
OexemploaseguirmostraumaServlet implementandoosmétodosde inicializaçãoefinalização.Osmétodosinitedestroypodemserbemsimples(lembre-sequesãoopcionais):
@WebServlet("/minhaServlet")publicclassMinhaServletextendsHttpServlet{
publicvoidinit(ServletConfigconfig)throwsServletException{super.init(config);log("Iniciandoaservlet");}
publicvoiddestroy(){super.destroy();log("Destruindoaservlet");}
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException,ServletException{//códigodoseumétodoservice}}
DeacordocomaespecificaçãodeServlets,porpadrão,existeumaúnicainstânciadecadaServletdeclarada.AochegarumarequisiçãoparaaServlet,umanovaThreadéabertasobreaquelainstância
quejáexiste.
Isso significa que, se colocássemos em nossa Servlet uma variável de instância, ela seriacompartilhadaentretodasasthreadsqueacessamessaServlet!Emoutraspalavras,seriacompartilhadoentre todasasrequisiçõese todososclientesenxergariamomesmovalor.Provavelmentenãoéoquequeremosfazer.
UmexemplosimplesparanosauxiliarenxergarissoéumaServletcomumavariávelparacontaraquantidadederequisições:
5.16UMAÚNICAINSTÂNCIADECADASERVLET
5.16UMAÚNICAINSTÂNCIADECADASERVLET 79
@WebServlet("/contador")publicclassContadorextendsHttpServlet{privateintcontador=0;//variaveldeinstância
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{contador++;//acadarequisiçãoamesmavariáveléincrementada
//recebeowriterPrintWriterout=response.getWriter();
//escreveotextoout.println("<html>");out.println("<body>");out.println("Contadoragoraé:"+contador);out.println("</body>");out.println("</html>");}}
QuandoaServlet for inicializada,ovalordocontadorédefinidopara0 (zero).Após isso,acadarequisição que é feita para essa Servlet, devido ao fato da instância ser sempre amesma, a variávelutilizadaparaincrementarserásempreamesma,eporconsequênciaimprimiráonúmeroatualparaocontador.
Sabemosquecompartilharvariáveis entremúltiplasThreadspodenos trazerproblemasgravesdeconcorrência. Se duas threads (no caso, duas requisições)modificarem amesmavariável ao "mesmotempo",podemosterperdadeinformaçõesmesmoemcasossimplescomoodocontadoracima.
Háduassoluçõesparaesseproblema.Aprimeiraseriaimpedirqueduasthreadsacessemaomesmotempoomesmoobjetocrítico;paraisso,podemossincronizarométodoservice.Masissotrariamuitosproblemas de escalabilidade (apenas uma pessoa por vez poderia requisitar minha página). A outrasolução,maissimples,éapenasnãocompartilharobjetosentrethreads.
QuandosefaladeServlets,aboapráticadizparaevitarusaratributoscompartilhados.
80 5.16UMAÚNICAINSTÂNCIADECADASERVLET
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
1. ImplementeoscódigosdasseçõesanterioressobreciclodevidaeconcorrênciaemServlets.FaçaaclasseContador e use tambémosmétodosinit edestroy.Oobjetivo é ver naprática os
conceitosdiscutidos.
Imaginesequiséssemoslistarosnossoscontatos,comopoderíamosfazer?Comoatéomomentosóconhecemos Servlet, provavelmente nossa sugestão seria criarmos uma Servlet que faça toda a
listagematravésdeout.println().Mas,seráqueamanutençãodissoseriaagradável?Eseumdia
precisarmos adicionar uma coluna nova na tabela? Teríamos que recompilar classes, e colocarmos aatualizaçãonoar.
Com o decorrer do curso aprenderemos que essa não é a melhor forma de fazermos essafuncionalidade.
JáconheceoscursosonlineAlura?
5.17EXERCÍCIOSOPCIONAIS
5.18DISCUSSÃO:CRIANDOPÁGINASDENTRODEUMASERVLET
5.17EXERCÍCIOSOPCIONAIS 81
CAPÍTULO6
"Omaiorprazeréesperarpeloprazer."--GottholdLessing
Nessecapítulo,vocêaprenderá:
OqueéJSP;Suasvantagensedesvantagens;EscreverarquivosJSPcomscriptlets;UsarExpressionLanguage.
Atéagora,vimosquepodemosescreverconteúdodinâmicoatravésdeServlets.Noentanto, se
todahoracriarmosServletsparafazermosessetrabalho,teremosmuitosproblemasnamanutenção
das nossas páginas e também na legibilidade do nosso código, pois sempre aparece código JavamisturadocomcódigoHTML.ImaginetodoumsistemacriadocomServletsfazendoageraçãodo
HTML.
Para não termos que criar todos os nossos conteúdos dinâmicos dentro de classes, misturandofortementeHTMLcomcódigo Java, precisamosusar uma tecnologia que podemos usar oHTMLdeformadireta,equetambémvápossibilitarautilizaçãodoJava.AlgosimilaraoASPePHP.
Essa tecnologiaéoJavaServerPages (JSP).OprimeiroarquivoJSPquevamoscriaréchamadobemvindo.jsp.EssearquivopoderiacontersimplesmentecódigoHTML,comoocódigoaseguir:
<html><body>Bemvindo</body></html>
Assim, fica claro que uma página JSP nadamais é que um arquivo baseado emHTML, com aextensão.jsp.
Dentro de um arquivo JSP podemos escrever também código Java, para que possamos adicionarcomportamentodinâmicoemnossaspáginas,comodeclaraçãodevariáveis,condicionais(if), loops
(for,while)entreoutros.
JAVASERVERPAGES
6.1COLOCANDOOHTMLNOSEUDEVIDOLUGAR
82 6JAVASERVERPAGES
Portanto,vamosescreverumpoucodecódigoJavananossaprimeirapágina.VamosdeclararumavariáveldotipoStringeinicializá-lacomalgumvalor.
<%Stringmensagem="Bemvindo!";%>
Para escrever código Javana suapágina,basta escrevê-lo entre as tags<% e%>.Esse tipode
códigoéchamadodescriptlet.
SCRIPTLET
Scriptletéocódigoescritoentre<%e%>.Essenomeécompostodapalavrascript(pedaço
decódigoemlinguagemdescript)comosufixolet,queindicaalgopequeno.
Comovocêjápercebeu,aSunpossuiessamaniadecolocarosufixoletemseusprodutoscomoosscriptlets,servlets,portlets,midlets,appletsetc...
PodemosavançarmaisumpoucoeutilizarumadasvariáveisjáimplícitasnoJSP:todoarquivoJSPjápossuiumavariávelchamadaout(dotipoJspWriter)quepermite imprimirparaoresponse
atravésdométodoprintln:
<%out.println(nome);%>
A variável out é um objeto implícito na nossa página JSP e existem outras de acordo com a
especificação. Repare também que sua funcionalidade é semelhante ao out que utilizávamos nas
Servletsmassemprecisarmosdeclará-loantes.
Existemaindaoutraspossibilidadespara imprimiroconteúdodanossavariável:podemosutilizarumatalho(muitoparecido,ouigual,aoutraslinguagensdescriptparaaWeb):
<%=nome%><br/>
IssojáéosuficienteparaquepossamosescreveronossoprimeiroJSP.
COMENTÁRIOS
OscomentáriosemumapáginaJSPdevemserfeitoscomooexemploaseguir:
<%--comentárioemjsp--%>
6.1COLOCANDOOHTMLNOSEUDEVIDOLUGAR 83
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
1. CrieoarquivoWebContent/bemvindo.jspcomoseguinteconteúdo:
<html><body><%--comentárioemJSPaqui:nossaprimeirapáginaJSP--%>
<%Stringmensagem="BemvindoaosistemadeagendadoFJ-21!";%><%out.println(mensagem);%>
<br/>
<%Stringdesenvolvido="Desenvolvidopor(SEUNOMEAQUI)";%><%=desenvolvido%>
<br/>
<%System.out.println("Tudofoiexecutado!");%></body></html>
1. AcesseaURLhttp://localhost:8080/fj21-agenda/bemvindo.jspnonavegador
VocêpodetambémfazerocursodatadessaapostilanaCaelum
6.2EXERCÍCIOS:PRIMEIROJSP
84 6.2EXERCÍCIOS:PRIMEIROJSP
2. Ondeapareceuamensagem"Tudofoiexecutado!"?
É muito importante você se lembrar que o código Java é interpretado no servidor, portantoapareceunoconsoledoseuTomcat.
VerifiqueoconsoledoseuTomcat.
Uma vez que podemos escrever qualquer código Java como scriptlet, não fica difícil criar umalistagemdetodososcontatosdobancodedados.
Temos todas as ferramentas necessárias para fazer essa listagem uma vez que já fizemos isso nocapítulodeJDBC.
Basicamente,ocódigoutilizaráoContatoDaoquecriamosanteriormenteparaimprimiralistade
Contato:
<%ContatoDaodao=newContatoDao();List<Contato>contatos=dao.getLista();
for(Contatocontato:contatos){%><li><%=contato.getNome()%>,<%=contato.getEmail()%>:<%=contato.getEndereco()%></li>
<%}%>
Nessecódigoaindafalta,assimcomonoJavapuro,importarasclassesdospacotescorretos.
Parafazermosoimportdasclasses,precisamosdeclararqueaquelapáginaprecisadeacessoàoutrasclassesJava.Paraisso,utilizamosdiretivas,quepossuemaseguintesintaxe:<%@...%>.
Reparequeelaseparecemuitocomscriptlet,comadiferenç[email protected]éumadiretivadepágina,ouseja,umaconfiguraçãoespecíficadeumapágina.
Para isso, utilizamos <%@ page %>. Basta dizermos qual configuração queremos fazer nessa
página,quenonossocasoéimportarumaclasseparautilizá-la:
6.3LISTANDOOSCONTATOSCOMSCRIPTLET
6.3LISTANDOOSCONTATOSCOMSCRIPTLET 85
<%@pageimport="br.com.caelum.agenda.dao.ContatoDao"%>
Oatributoimport permite que seja especificado qual o pacote a ser importado. Para importar
diversospacotes,podemossepará-losporvírgulas(videoexercício).
1. CrieoarquivoWebContent/lista-contatos-scriptlet.jspesiga:
Importeospacotesnecessários.UseoCtrl+EspaçodoEclipseparaajudaraescreverospacotes.
<%@pageimport="java.util.*,br.com.caelum.agenda.dao.*,br.com.caelum.agenda.modelo.*"%>
Coloqueocódigoparafazeralistagem.UsebastanteoCtrl+EspaçodoEclipse.
<html><body><table><%ContatoDaodao=newContatoDao();List<Contato>contatos=dao.getLista();
for(Contatocontato:contatos){%><tr><td><%=contato.getNome()%></td><td><%=contato.getEmail()%></td><td><%=contato.getEndereco()%></td><td><%=contato.getDataNascimento().getTime()%></td></tr><%}%></table></body></html>
Testeaurlhttp://localhost:8080/fj21-agenda/lista-contatos-scriptlet.jsp
1. Repare que a data apareceu de uma forma complicada de ler. Tente mostrá-la formatadautilizandoaclasseSimpleDateFormat.
2. Coloquecabeçalhosparaascolunasdatabela,descrevendooquecadacolunasignifica.3. Tenteutilizaroquadroaseguirparadefinirapáginapadrãodeseusite.
6.4EXERCÍCIOSOPCIONAIS:LISTADECONTATOSCOMSCRIPTLET
86 6.4EXERCÍCIOSOPCIONAIS:LISTADECONTATOSCOMSCRIPTLET
WELCOME-FILE-LIST
Oarquivoweb.xmlabaixodizqueosarquivoschamados"bemvindo.jsp"devemserchamadosquandoumclientetentaacessarumdiretóriowebqualquer.
Ovalordessecampocostumaser"index.html"emoutraslinguagensdeprogramação.
ComovocêpodeverpeloarquivogeradoautomaticamentepeloWTP,épossívelindicarmaisdeumarquivoparaseroseuwelcome-file!Mude-opara:
<welcome-file-list><welcome-file>bemvindo.jsp</welcome-file></welcome-file-list>
ReinicieotomcateacesseaURL:http://localhost:8080/fj21-agenda/
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
AbrimosocapítulodizendoquenãoqueríamosmisturarcódigoJavacomcódigoHTMLnasnossasServlets,pois,prejudicavaalegibilidadedonossocódigoeafetavaamanutenção.
Maséjustamenteissoqueestamosfazendoagora,sóquenoJSP,atravésdeScriptlets.
ÉcomplicadoficarescrevendoJavaemseuarquivoJSP,nãoé?
Primeiro,ficatudomalescritoedifícildeler.OJavapassaaatrapalharocódigoHTMLemvezdeajudar. Depois, quando o responsável pelo design gráfico da página quiser alterar algo, terá queconhecerJavaparaentenderoqueestáescritoládentro.Hmm...nãopareceumaboasolução.
Seuslivrosdetecnologiaparecemdoséculopassado?
6.5MISTURANDOCÓDIGOJAVACOMHTML
6.5MISTURANDOCÓDIGOJAVACOMHTML 87
E existe hoje em dia no mercado muitas aplicações feitas inteiramente utilizando scriptlets eescrevendocódigoJavanomeiodosHTMLs.
Comodecorrerdocurso,vamosevoluirnossocódigoatéumpontoemquenãofaremosmaisessamistura.
PararemoverumpoucodocódigoJavaqueficanapáginaJSP,aSundesenvolveuumalinguagemchamadaExpressionLanguagequeéinterpretadapeloservletcontainer.
Nosso primeiro exemplo com essa linguagem é utilizá-la para mostrar parâmetros que o clienteenviaatravésdesuarequisição.
Porexemplo,seoclientechamaapáginatestaparam.jsp?idade=24,oprogramadevemostrara
mensagemqueoclientetem24anos.
Como fazer isso? Simples, existe uma variável chamadaparam que, na expression language, é
responsável pelos parâmetros enviados pelo cliente. Para ler o parâmetro chamado idade basta usar${param.idade}.Paraleroparâmetrochamadodiadevemosusar${param.dia}.
A expression language ainda vai ser utilizada para muitos outros casos, não apenas para pegarparâmetrosquevieramdorequest.ElaéaformamaiselegantehojeemdiaparatrabalharnoJSPeseráexploradanovamenteduranteoscapítulosposteriores.
1. CrieumapáginachamadaWebContent/digita-idade.jspcomoconteúdo:
<html><body>Digitesuaidadeepressioneobotão:<br/>
<formaction="mostra-idade.jsp">Idade:<inputtype="text"name="idade"/><inputtype="submit"/></form></body></html>
1. Crie um arquivo chamado WebContent/mostra-idade.jsp e coloque o código de expressionlanguagequemostraaidadequefoienviadacomoparâmetroparaessapágina:
<html><body>Testandoseusparâmetros:<br/>Aidadeé${param.idade}.</body></html>
6.6EL:EXPRESSIONLANGUAGE
6.7EXERCÍCIOS:PARÂMETROSCOMAEXPRESSIONLANGUAGE
88 6.6EL:EXPRESSIONLANGUAGE
1. Testeosistemaacessandoapáginahttp://localhost:8080/fj21-agenda/digita-idade.jsp.
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
Os arquivos JSPs não são compilados dentro do Eclipse, por esse motivo na hora que estamosescrevendooJSPnoEclipsenãoprecisamosdasclassesdodriver.
Os JSPs são transformados emumaServlet, quevimos anteriormente, porumcompilador JSP (oTomcatcontémumcompiladorembutido).EssecompiladorJSPpodegerarumcódigoJavaqueéentãocompiladoparagerarbytecodediretamenteparaaservlet.
Então,somenteduranteaexecuçãodeumapáginaJSP,quandoeleétransformadoemumaservlet,queseucódigoJavaécompiladoenecessitamosdasclassesdodriverquesãoprocuradasnodiretóriolib.
Agoraéamelhorhoradeaprenderalgonovo
6.8PARASABERMAIS:COMPILANDOOSARQUIVOSJSP
6.8PARASABERMAIS:COMPILANDOOSARQUIVOSJSP 89
CAPÍTULO7
"Saberécompreendermosascoisasquemaisnosconvém."--FriedrichNietzsche
Nesse capítulo, você aprenderá o que são Taglibs e JSTL. E também terá a chance de utilizaralgumasdasprincipaistagsdogrupocoreefmt.
Nocapítuloanterior,começamosamelhorarnossosproblemascomrelaçãoàmisturadecódigoJavacomHTMLatravésdaExpressionLanguage.No entanto, ela sozinhanãopodenos ajudarmuito,
poiselanãonospermite,porexemplo,instanciarobjetos,fazerverificaçõescondicionais(ifelse),
iteraçõescomoemumforeassimpordiante.
Paraquepossamosteressecomportamentosemimpactarnalegibilidadedonossocódigo,teremosque escrever esse código nos nossos JSPs numa forma parecida com o que já escrevemos lá, que éHTML,logo,teremosqueescrevercódigobaseadoemTags.
Issomesmo,umatag!ASunpercebeuqueosprogramadoresestavamabusandodocódigoJavanoJSP e tentou criar algo mais "natural" (um ponto um tanto quanto questionável da maneira que foiapresentadanoinício),sugerindoousodetagsparasubstituirtrechosdecódigo.
Oresultadofinaléumconjuntodetags(umataglibrary,outaglib)padrão,quepossui,entreoutrastags,afuncionalidadedeinstanciarobjetosatravésdoconstrutorsemargumentos.
USANDOTAGLIBS
7.1TAGLIBS
90 7USANDOTAGLIBS
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
Como já foi comentado anteriormente, os Javabeans devem possuir o construtor público semargumentos(umtípicoPlainOldJavaObject:POJO),gettersesetters.
Instanciá-los na nossa página JSP não é complicado.Basta utilizarmos a tag correspondente paraessafunção,quenonossocasoéa<jsp:useBean>.
Parautilizá-la,bastaindicarmosqualaclassequeremosinstanciarecomosechamaráavariávelqueseráatribuídaessanovainstância.
<jsp:useBeanid="contato"class="br.com.caelum.agenda.modelo.Contato"/>
Podemosimprimironomedocontato(queestáembranco,claro...):
${contato.nome}
Mas,ondeestáogetNome()?Aexpressionlanguageécapazdepercebersozinhaanecessidadede
chamarummétododotipogetter,porissoopadrãogetter/setterdoPOJOétãoimportantehojeemdia.
Desta maneira, classes como Contato são ferramentas poderosas por seguir esse padrão pois
diversasbibliotecasimportantesestãobaseadasnele:Hibernate,Struts,VRaptor,JSF,EJBetc.
ATENÇÃO
NaExpressionLanguage${contato.nome} chamaráométodogetNome por padrão.Para
que isso sempre funcione, devemos colocar o parâmetro em letra minúscula. Ou seja,${contato.Nome}nãofunciona.
EditoraCasadoCódigocomlivrosdeumaformadiferente
7.2INSTANCIANDOPOJOS
7.2INSTANCIANDOPOJOS 91
Seguindo a ideia demelhorar o código Java que precisa de umamaneira ou outra ser escrito napáginaJSP,aSunsugeriuousodaJavaServerPagesStandardTagLibrary,aJSTL.
OBSERVAÇÃO
Antesde2005,JSTLsignificavaJavaServerPagesStandardTemplateLibrary.
AJSTLéaAPIqueencapsulouemtagssimplestodaafuncionalidadequediversaspáginasWebprecisam,comocontroledelaços(fors),controledefluxodotipoifelse,manipulaçãodedados
XMLeainternacionalizaçãodesuaaplicação.
Antigamente,diversasbibliotecasforamcriadasporváriosgruposcomfuncionalidadessimilaresaoJSTL (principalmente aoCore), culminando com a aparição damesma, em uma tentativa da Sun depadronizaralgoqueomercadovêcomoútil.
Existem ainda outras partes da JSTL, por exemplo aquela que acessa banco de dados e permiteescrevercódigosSQLnanossapágina,masseodesignernãocompreendeJavaoquediremosdeSQL?OusodetalpartedaJSTLédesencorajado.
AJSTLfoiaformaencontradadepadronizarotrabalhodemilharesdeprogramadoresdepáginasJSP.
Antesdisso,muitagenteprogramavacomonosexemplosquevimosanteriormente, somentecomJSPseJavabeans,ochamadoModelo1,quenaépocafaziapartedosBlueprintsdeJ2EEdaSun(boaspráticas)enósvamosdiscutirmaisparafrentenocurso.
MuitaspáginasJSPnoBrasilaindapossuemgrandespedaçosdescriptletsespalhadosdentrodelas.
RecomendamosatodososnossosalunosqueoptarempeloJSPcomocamadadevisualização,queutilizemaJSTLeoutrasbibliotecasde tagparaevitarocódigo incompreensívelquepodesergeradocomscriptlets.
Ocódigodasscriptletsmaisconfundedoqueajuda,tornandoamanutençãodapáginaJSPcadavezmaiscustosaparaoprogramadoreparaaempresa.
Para instalar a implementação mais famosa da JSTL basta baixar a mesma no site
7.3JSTL
Asempresashojeemdia
7.4INSTALAÇÃO
92 7.3JSTL
http://jstl.java.net/.
AousaroJSTLemalgumapáginaprecisamosprimeirodefinirocabeçalho.ExistemquatroAPIsbásicasevamosaprenderprimeiroautilizarabibliotecachamadadecore.
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
SemprequevamosutilizarumataglibdevemosprimeiroescreverumcabeçalhoatravésdeumatagJSPquedefinequaltaglibvamosutilizareumnome,chamadoprefixo.
EsseprefixopodeterqualquervalormasnocasodataglibcoredaJSTLopadrãodaSunéaletrac.Já a URI (que não deve ser decorada) é mostrada a seguir e não implica em uma requisição peloprotocolohttpesimumabuscaentreosarquivos.jarnodiretóriolib.
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%>
UsandoaJSTLcore,vamosreescreveroarquivoquelistatodoscontatos.
Ocabeçalhojáéconhecidodaseçãoanterior:
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%>
Depois, precisamos instanciar e declarar nosso DAO. Ao revisar o exemplo da lista através descriptlets,queremosexecutaroseguinte:
classe:br.com.caelum.jdbc.dao.ContatoDao;
construtor:semargumentos;variável:DAO.
JáconheceoscursosonlineAlura?
7.5CABEÇALHOPARAAJSTLCORE
7.6FOREACH
7.5CABEÇALHOPARAAJSTLCORE 93
Já vimos a tag jsp:useBean, capaz de instanciar determinada classe através do construtor semargumentosedarumnome(id)paraessavariável.
PortantovamosutilizarataguseBeanparainstanciarnossoContatoDao:
<jsp:useBeanid="dao"class="br.com.caelum.agenda.dao.ContatoDao"/>
Comotemosavariáveldao,desejamoschamarométodogetListaepodemosfazerissoatravés
daEL:
${dao.lista}
Desejamosexecutarumloopparacadacontatodentrodacoleçãoretornadaporessemétodo:
arrayoucoleção:dao.lista;variáveltemporária:contato.
Nonossoexemplocomscriptlets,oquefaltaéachamadadométodogetListaeaiteração:
<%//...List<Contato>contatos=dao.getLista();
for(Contatocontato:contatos){%><%=contato.getNome()%>,<%=contato.getEmail()%>,<%=contato.getEndereco()%>,<%=contato.getDataNascimento()%><%}%>
A JSTL core disponibiliza uma tag chamada c:forEach capaz de iterar por uma coleção,
exatamente o que precisamos. No c:forEach, precisamos indicar a coleção na qual vamos iterar,
atravésdoatributoitemsetambémcomochamaráoobjetoqueseráatribuídoparacadaiteraçãono
atributovar.Oexemploaseguirmostraousodeexpressionlanguagedeumamaneiramuitomais
elegante:
<c:forEachvar="contato"items="${dao.lista}">${contato.nome},${contato.email},${contato.endereco},${contato.dataNascimento}</c:forEach>
Maiselegantequeocódigoquefoiapresentadousandoscriptlets,não?
94 7.6FOREACH
FOREACHEVARSTATUS
ÉpossívelcriarumcontadordotipointdentrodoseulaçoforEach.Paraisso,bastadefinir
oatributochamadovarStatus para avariáveldesejadaeutilizar apropriedadecount dessa
variável.
<tableborder="1"><c:forEachvar="contato"items="${dao.lista}"varStatus="id"><trbgcolor="#${id.count%2==0?'aaee88':'ffffff'}"><td>${id.count}</td><td>${contato.nome}</td></tr></c:forEach></table>
1. PrecisamosprimeirocolocarosJARsdaJSTLemnossaaplicação.
Primeiro,váaoDesktop,eentrenodiretório21/projeto-agenda/jstlHaverádoisjars,jakarta.servlet.jsp.jstl-1.2.x.jarejakarta.servlet.jsp.jstl-api-1.2.x.jarCopie-os (CTRL+C) e cole-os (CTRL+V) dentro de workspace/fj21-agenda/WebContent/WEB-INF/libNoEclipse,dêumF5noseuprojetooucliquecomobotãodireitodomousesobreonomedoprojetoeescolhaaopçãoRefresh.
EMCASA
Casovocêesteja emcasa,pode fazerodownloadda JSTLAPIeda implementaçãoem:https://bit.ly/fj21_jstl_apihttps://bit.ly/fj21_jstl_impl
2. ListeoscontatosdeContatoDaousandojsp:useBeaneJSTL.
Crieoarquivolista-contatos.jsp,dentrodapastaWebContent/ usandoo atalhodenovoJSPnoEclipse;
Antesdeescrevermosnossocódigo,precisamosimportarataglibJSTLCore.Issoéfeitocomadiretiva<%@taglib%> no topo do arquivo.Usando o recurso de autocompletar doEclipse
(inclusivenaURL),declareataglibnotopodoarquivo:
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%>
Localizeatag<body>noarquivoe implementeoconteúdodanossapáginadentrodobody.
7.7EXERCÍCIOS:FOREACH
7.7EXERCÍCIOS:FOREACH 95
VamosusarumatabelaHTMLeastags<jsp:useBean/>e<c:forEach/>quevimosantes:
<!--criaoDAO--><jsp:useBeanid="dao"class="br.com.caelum.agenda.dao.ContatoDao"/>
<table><!--percorrecontatosmontandoaslinhasdatabela--><c:forEachvar="contato"items="${dao.lista}"><tr><td>${contato.nome}</td><td>${contato.email}</td><td>${contato.endereco}</td><td>${contato.dataNascimento.time}</td></tr></c:forEach></table>
Acessehttp://localhost:8080/fj21-agenda/lista-contatos.jsp
ReparequeapóscriarumanovapáginaJSPnãoprecisamosreiniciaronossocontainer!
3. ScriptletsouJSTL?Qualdosdoisémaisfácilentender?
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
1. Coloqueumcabeçalhonascolunasdatabelacomumtítulodizendoàqueserefereacoluna.2. Utilizeumavariáveldestatusnoseuc:forEachparacolocarduascoresdiferentesemlinhaspares
eímpares.(UtilizecomoauxíliooboximediatamenteantesdoexercíciosobreforEach)
VocêpodetambémfazerocursodatadessaapostilanaCaelum
7.8EXERCÍCIOSOPCIONAIS
96 7.8EXERCÍCIOSOPCIONAIS
Alistagemdosnossoscontatosfuncionaperfeitamente,masonossoclienteaindanãoestásatisfeito.Elequerumpoucomaisdefacilidadenessatela,esugerequecasoousuáriotenhae-mailcadastrado,coloquemosumlinknoe-mailquequandoclicadoabraosoftwaredee-maildocomputadordousuárioparaenviarumnovoe-mailparaesseusuário.Comopodemosfazeressafuncionalidade?
Vamosanalisaroproblemacomcalma.Primeiro,percebemosquevamosprecisarcriarumlinkparaenvio de e-mail. Isso é facilmente conseguido através da tag do HTML <a> com o parâmetro
href="mailto:[email protected]".Primeiroproblemaresolvidofacilmente,masagoratemosoutro.
Comofaremosaverificaçãoseoe-mailestáounãopreenchido?
Paraquepossamosfazeressaverificaçãoprecisaremosfazerumifparasabermosseoe-mailestá
preenchido ou não. Mas, novamente, não queremos colocar código Java na nossa página e jáaprendemosqueestamosmudandoissoparautilizar tags.Paraessafinalidade,existeatagc:if, na
qualpodemosindicarqualotestelógicodeveserfeitoatravésdoatributotest.Essetesteéinformado
atravésdeExpressionLanguage.
Paraverificarmosseoe-mailestápreenchidoounão,podemosfazeroseguinte:
<c:iftest="${notemptycontato.email}"><ahref="mailto:${contato.email}">${contato.email}</a></c:if>
Podemos também, caso o e-mail não tenha sido preenchido, colocar a mensagem "e-mail nãoinformado",aoinvésdenossatabelaficarcomumespaçoembranco.Reparequeesseéjustamenteocasocontrárioquefizemosnonossoif,logo,éequivalenteaoelse.
OproblemaéquenãotemosatagelsenaJSTL,porquestõesestruturaisdeXML.Umaprimeira
alternativa seria fazermosoutro<c:if> coma lógica invertida.Mas issonão é uma soluçãomuito
elegante. No Java, temos outra estrutura condicional que consegue simular um if/else, que é o
switch/case.
Parasimularmosswitch/casecomJSTL,utilizamosatagc:chooseeparacadacasodoswitch
fazemosc:when.Odefaultdoswitchpodeserrepresentadoatravésdatagc:otherwise,comono
exemploaseguir:
<c:choose><c:whentest="${notemptycontato.email}"><ahref="mailto:${contato.email}">${contato.email}</a></c:when><c:otherwise>E-mailnãoinformado</c:otherwise>
7.9EVOLUINDONOSSALISTAGEM
7.10FAZENDOIFSCOMAJSTL
7.9EVOLUINDONOSSALISTAGEM 97
</c:choose>
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
1. Vamoscolocaremnossalistagemumlinkparaenviodee-mailcasoomesmotenhasidoinformado.
Abraoarquivolista-contatos.jspnoEclipse;
Nomomentode imprimiroe-maildocontato, adicioneumaverificaçãopara saber seoe-mailestápreenchidoe,casoesteja,adicioneumlinkparaenviodee-mail:
<c:forEachvar="contato"items="${dao.lista}"><tr><td>${contato.nome}</td><td><c:iftest="${notemptycontato.email}"><ahref="mailto:${contato.email}">${contato.email}</a></c:if></td><td>${contato.endereco}</td><td>${contato.dataNascimento.time}</td></tr></c:forEach>
Acesseapáginanonavegadorpeloendereçohttp://localhost:8080/fj21-agenda/lista-contatos.jsp
1. Vamoscolocaramensagem"E-mailnãoinformado"casooe-mailnãotenhasidoinformado
Seuslivrosdetecnologiaparecemdoséculopassado?
7.11EXERCÍCIOS:LISTADECONTATOSCOMCONDICIONAIS
98 7.11EXERCÍCIOS:LISTADECONTATOSCOMCONDICIONAIS
Abaixodonovoifquefizemosnoitemanterior,vamoscolocarmaisumif,dessavezcoma
verificaçãocontrária,ouseja,queremossaberseestávazio:
<c:forEachvar="contato"items="${dao.lista}"><tr><td>${contato.nome}</td><td><c:iftest="${notemptycontato.email}"><ahref="mailto:${contato.email}">${contato.email}</a></c:if>
<c:iftest="${emptycontato.email}">E-mailnãoinformado</c:if></td><td>${contato.endereco}</td><td>${contato.dataNascimento.time}</td></tr></c:forEach>
Casovocênãopossuanenhumcontatoseme-mail,cadastrealgum.
Acessealista-contatos.jsppelonavegadorevejaoresultadofinal.
1. (Opcional)Aoinvésdeutilizardoisifs,useatagc:choose
UmrequisitocomumquetemosnasaplicaçõesWebhojeemdiaécolocarcabeçalhoserodapénaspáginasdonossosistema.Essescabeçalhoserodapéspodemterinformaçõesdaempresa,dosistemaeassimpordiante.Oproblemaéque,nagrandemaioriadasvezes,todasaspáginasdanossaaplicaçãoprecisamteressemesmocabeçalhoerodapé.Comopoderíamosresolverisso?
Uma primeira alternativa e talvez a mais inocente é colocarmos essas informações em todas aspáginasdanossaaplicação,copiandoecolandotodoocabeçalhováriasvezes.
Masoqueaconteceriaseprecisássemosmudarologotipodaempresa?Teríamosquemudartodasaspáginas,oquenãoéumtrabalhoagradável.
Umaalternativamelhorseriaisolarmosessecódigoqueserepeteemtodasaspáginasemumaoutra
7.12IMPORTANDOPÁGINAS
7.12IMPORTANDOPÁGINAS 99
página, por exemplo, cabecalho.jsp e todas as páginas da nossa aplicação, apenas dizem que
precisamdessaoutrapáginanela,atravésdeumatagnova,ac:import.
Para utilizá-la, podemos criar uma pagina com o cabeçalho do sistema, por exemplo, acabecalho.jsp:
<imgsrc="imagens/logomarca.jpg"/>Nomedaempresa
Eumapáginaparaorodapé,porexemplo,rodape.jsp:
Copyright2010-Todososdireitosreservados
Bastariaque, em todasasnossaspáginas,porexemplo,nalista-contatos.jsp, colocássemos
ambasaspáginas,atravésdac:importcomoabaixo:
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%><html><body>
<c:importurl="cabecalho.jsp"/>
<jsp:useBeanid="dao"class="br.com.caelum.agenda.dao.ContatoDao"/><table><!--for--><c:forEachvar="contato"items="${dao.lista}"><tr><td>${contato.nome}</td><td>${contato.email}</td><td>${contato.endereco}</td><td>${contato.dataNascimento.time}</td></tr></c:forEach></table>
<c:importurl="rodape.jsp"/>
</body></html>
1. Vamosprimeirocriaronossocabeçalho,utilizandoologotipodaCaelum.
Vá noDesktop, entre na pasta21/projeto-agenda e copie o diretório imagens para dentro dodiretórioWebContentdoseuprojeto.EssediretóriopossuiologotipodaCaelum.
CriedentrodeWebContentumarquivochamadocabecalho.jspcomologotipodosistema:
<imgsrc="imagens/caelum.png"style="width:40%;"/><h2>AgendadeContatosdo(a)(Seunomeaqui)</h2><hr/>
Vocêpodeajustar a largurada imagemalterandoaporcentagemnoestilo cssstyle="width:
7.13EXERCÍCIOS:CABEÇALHOSERODAPÉS
100 7.13EXERCÍCIOS:CABEÇALHOSERODAPÉS
40%;"
Crietambémapáginarodape.jsp:
<hr/>Copyright2020-Todososdireitosreservados
Podemosimportarasduaspáginas(cabecalho.jsperodape.jsp), dentrodanossalista-
contatos.jspusandoatagc:importquevimos:
<html><body><%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%>
<c:importurl="cabecalho.jsp"/>
<!--criaalista--><jsp:useBeanid="dao"class="br.com.caelum.agenda.dao.ContatoDao"/><table><!--for--><c:forEachvar="contato"items="${dao.lista}"><tr><td>${contato.nome}</td>
<td><c:iftest="${notemptycontato.email}"><ahref="mailto:${contato.email}">${contato.email}</a></c:if><c:iftest="${emptycontato.email}">E-mailnãoinformado</c:if></td>
<td>${contato.endereco}</td><td>${contato.dataNascimento.time}</td></tr></c:forEach></table>
<c:importurl="rodape.jsp"/></body></html>
Visualize o resultado final acessando no navegador http://localhost:8080/fj21-
agenda/lista-contatos.jsp
7.13EXERCÍCIOS:CABEÇALHOSERODAPÉS 101
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
Apesar da nossa listagem de contatos estar bonita e funcional, ela ainda possui problemas, porexemplo, a visualização da data de nascimento.Muitas informações aparecem na data, como horas,minutosesegundos,coisasquenãoprecisamos.
Jáaprendemosanteriormentequeumadas formasquepodemos fazer essa formataçãoemcódigoJava é através da classeSimpleDateFormat,mas não queremos utilizá-la aqui, pois não queremos
códigoJavanonossoJSP.
Paraisso,vamosutilizaroutraTaglibdaJSTLqueéataglibdeformatação,afmt.
Para utilizarmos a taglib fmt, precisamos importá-la, damesma forma que fizemos com a tag
core,atravésdadiretivadetaglib,comoabaixo:
<%@tagliburi="http://java.sun.com/jsp/jstl/fmt"prefix="fmt"%>
Dentro da taglib fmt, uma das tags que ela possui é a formatDate, que faz com que um
determinadoobjetodotipojava.util.Datesejaformatadoparaumdadopattern.Então,podemos
utilizaratagfmt:formatDatedaseguinteforma:
<fmt:formatDatevalue="${contato.dataNascimento.time}"pattern="dd/MM/yyyy"/>
Repare que, naExpressionLanguage que colocamos no value, há no final um .time. Isso
porqueoatributovaluesóaceitaobjetosdotipojava.util.Date,enossadatadenascimentoéum
java.util.Calendar que possui um método getTime() para chegarmos ao seu respectivo
java.util.Date.
Agoraéamelhorhoradeaprenderalgonovo
7.14FORMATAÇÃODEDATAS
102 7.14FORMATAÇÃODEDATAS
COMOFAZERPATTERNSMAISCOMPLICADOS?
Podemos no atributo pattern colocar outras informações com relação ao objeto
java.util.Datequequeremosmostrar,porexemplo:
m-Minutos
s-Segundos
H-Horas(0-23)
D-Dianoano,porexemplo,230
Sugerimosquequandoprecisardeformataçõesmaiscomplexas,leiaadocumentaçãodaclasseSimpleDateFormat,aondeseencontraadescriçãodealgunscaracteresdeformatação.
1. Vamosfazeraformataçãodadatadenascimentodosnossoscontatos.
Nalista-contatos.jsp, importea taglibfmtno topodoarquivo.Useoautocompletardo
Eclipseparateajudaraescrever:
<%@tagliburi="http://java.sun.com/jsp/jstl/fmt"prefix="fmt"%>
TroqueaExpressionLanguagequemostraadatadenascimentoparapassarpelatagformatDate
abaixo:
<fmt:formatDatevalue="${contato.dataNascimento.time}"pattern="dd/MM/yyyy"/>
Acesseapáginapelonavegadorevejaoresultadofinal:
7.15 EXERCÍCIOS: FORMATANDO A DATA DE NASCIMENTO DOSCONTATOS
7.15EXERCÍCIOS:FORMATANDOADATADENASCIMENTODOSCONTATOS 103
Muitas vezes trabalhar com links em nossa aplicação é complicado. Por exemplo, no arquivocabecalho.jspincluímosumaimagemchamadacaelum.pngqueestánapastaimagens.
IncluímosestaimagemcomatagHTML<img>utilizandoocaminhoimagens/caelum.pngqueéumcaminhorelativo,ouseja,seocabecalho.jspestivernaraizdoprojeto,oarquivocaelum.png
deveráestaremumapastachamadaimagenstambémnaraizdoprojeto.
Utilizar caminhos relativos muitas vezes é perigoso. Por exemplo, se colocarmos o arquivocabecalho.jsp emumdiretóriochamadoarquivos_comuns, a imagemque tínhamos adicionado
seráprocuradaemumdiretórioimagensdentrododiretórioarquivos_comuns.Resultado,aimagem
nãoseráexibida.
Poderíamos resolver isso utilizando um caminho absoluto. Ao invés de adicionarmos a imagemutilizando o caminho imagens/caelum.png , poderíamos usar /imagens/caelum.png . Só que
utilizandoa/elenãovaiparaaraizdaminhaaplicaçãoesimparaaraizdotomcat,ouseja,eleiria
procuraraimagemcaelum.pngemumdiretórioimagensna raizdo tomcat,quandonaverdadeo
diretórionemmesmoexiste.
Poderíamos resolver isso utilizando o caminho /fj21-tarefas/imagens/caelum.png . Nosso
problemaseriaresolvido,entretanto,sealgumdiamudássemosocontextodaaplicaçãoparatarefasa
imagemnãoseriaencontrada,poisdeixamosfixononossojspqualeraocontextodaaplicação.
Pararesolveresteproblema,podemosutilizaratag<c:url>daJSTL.Ousodelaéextremamente
simples.Paraadicionarmosologotipodacaelumnocabecalho.jsp,faríamosdaseguintemaneira:
<c:urlvalue="/imagens/caelum.png"var="imagem"/><imgsrc="${imagem}"/>
Oudeumaformaaindamaissimples:
<imgsrc="<c:urlvalue="/imagens/caelum.png"/>"/>
OHTMLgeradopeloexemploseria:<imgsrc="fj21-tarefas/imagens/caelum.png"/>.
7.16PARASABERMAIS:LINKSCOM<C:URL>
104 7.16PARASABERMAIS:LINKSCOM
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
1. Vamossubstituirocaminhorelativopeloabsoluto.Abraoarquivocabecalho.jspealtere-oadicionandoatag<c:url>:
<imgsrc="<c:urlvalue="/imagens/caelum.png"/>"style="width:40%;"/>
Como vamos usar a JSTL também nesse novo arquivo de cabeçalho, não deixe de incluir adeclaraçãodataglibnoiníciodoarquivo.(ocomando<%@taglib...%>semelhanteaousadona
listagem)
Visualize o resultado acessando no navegador http://localhost:8080/fj21-agenda/lista-contatos.jsp
Aimagemcaelum.pngcontinuaaparecendonormalmente
Se,algumdia,alterarmosocontextodanossaaplicação,ocabeçalhocontinuariaexibindoaimagemdamaneiracorreta.Usandoatag<c:url>ficamoslivresparautilizarcaminhosabsolutos.
AJSTLpossuialémdastagsquevimosaqui,muitasoutras,eparadiversasfinalidades.AbaixoestáumresumocomalgumasdasoutrastagsdaJSTL:
c:catch-blocodotipotry/catchc:forTokens-foremtokens(ex:"a,b,c"separadosporvírgula)c:out-saídac:param-parâmetroc:redirect-redirecionamento
EditoraCasadoCódigocomlivrosdeumaformadiferente
7.17EXERCÍCIOSOPCIONAIS:CAMINHOABSOLUTO
7.18PARASABERMAIS:OUTRASTAGS
7.17EXERCÍCIOSOPCIONAIS:CAMINHOABSOLUTO 105
c:remove-remoçãodevariávelc:set-criaçãodevariável
Leia detalhes sobre as taglibs da JSTL (JavaDoc das tags e classes) em:https://jakarta.ee/specifications/tags/1.2/tagdocs/
106 7.18PARASABERMAIS:OUTRASTAGS
CAPÍTULO8
"Euapenasinventoeesperoqueoutrosapareçamprecisandodoqueinventei"--R.BuckminsterFuller
Aotérminodessecapítulo,vocêserácapazde:
criarsuaprópriataglibatravésdetagfiles;encapsularcódigosrepetitivosemtags;conheceroutrastaglibsexistentesnomercado.
Émuitocomum,nomomentoemqueestamosdesenvolvendonossosJSPs,cairmosemsituaçõesemquefazemosmuitarepetiçãodecódigo.Porexemplo,semprequecriamosumacaixadetexto,podemosassociá-lacomumlabel,atravésdoseguintecódigo:
<labelfor="nomeContato">Nome</label><inputtype="text"id="nomeContato"name="nome"/>
Essecódigofazcomque,sevocêclicarnapalavraNome,passeofocoparaocampodetexto.Masreparequetemosalgoqueserepetenessetrecho,queéoiddoinputeoatributofordolabel.
Semudarmosoiddoinputteremosquerefletiressaalteraçãonofordolabel.Alémdisso,temos
quesempreescrevertodoessecódigo.
Não seria mais simples escrevermos <campoTexto id="nomeContato" name="nome"
label="Nome:"/>etodoaquelecódigosergeradoparanós?
UmoutroexemplopoderiaserquandoutilizamoscomponentesquedependemdeJavascript,comoumcampoque,aoganharofoco,mostraumcalendário.Todavezquetemosqueusaressecomponente,precisamosescreverumpedaçodecódigoJavaScript.Porquenãoencapsulartudoissoemnovastags
TAGSCUSTOMIZADASCOMTAGFILES
8.1PORQUEEUPRECISARIADEOUTRASTAGSALÉMDAJSTL?
8TAGSCUSTOMIZADASCOMTAGFILES 107
customizadas?
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
VamospegarumexemplopráticodepossívelusodeJavaScriptnonossosistema.Temosnocadastrodecontatos,umcampoparadatadenascimento.Masatéomomento,ousuárioprecisadigitaradatanamãoseguindooformatoqueespecificamos.
EsemostrássemosumcalendárioemJavaScriptparaousuárioescolheradataapenasclicandoemumcomponentejápronto?
Paraconseguirmosessecomportamento,podemosutilizarporexemplooscomponentesvisuaisdojQuery,queéumabibliotecacomalgunscomponentesJavaScript.
EntreosdiversoscomponentesqueojQuerypossui,umdeleséocampocomcalendário,tambémconhecidopordatepicker. Para utilizarmosodatepicker do jQuery, precisamos criar uminput de
textosimpleseassociá-locomumafunçãoJavaScript,comonocódigoseguinte:
<inputid="dataNascimento"type="text">
<script>$("#dataNascimento").datepicker();</script>
AfunçãoJavaScriptfazcomqueoinputcujoidédataNascimentosejaumcalendário.
Imagine se toda hora que fossemos criar um campode data tivéssemos que escrever esse códigoJavaScript?Aschancesdeerrarmosessecódigoérazoável,masaindaassim,opiorpontoaindaseriaperdertempoescrevendoumcódigorepetitivo,sendoquepoderíamosobteromesmoresultadoapenasescrevendo:
<campoDataid="dataNascimento"/>
JáconheceoscursosonlineAlura?
8.2CALENDÁRIOSCOMJQUERY
108 8.2CALENDÁRIOSCOMJQUERY
Qualabordagemémaissimples?
Paraquepossamosteratag<campoData>precisaremoscriá-la.Umadasformasquetemosdecriar
nossasprópriastaglibsécriandoumarquivocontendoocódigoquenossaTaglibgerará.Essesarquivoscontendoocódigodastagssãoconhecidoscomotagfiles.
Tagfiles nadamais são do que pedaços de JSP, com a extensão .tag, contendo o código que
queremosqueanossataggere.
VamoscriarumarquivochamadocampoData.tagcomocódigoquequeremosgerar.
<inputid="dataNascimento"name="dataNascimento">
<script>$("#dataNascimento").datepicker();</script>
Mas essa nossa tag está totalmente inflexível, pois o id e o nome do campo estão escritos
diretamente dentro da tag. O que aconteceria se tivéssemos dois calendários dentro do mesmoformulário?Ambosteriamomesmoname.
Precisamosfazercomqueanossatagrecebaparâmetros.Podemosfazerissoadicionandoadiretiva<%@attribute%> na nossa tag, comosparâmetrosname representandoonomedo atributo e o
parâmetrorequiredcomosvalorestrueoufalse,indicandoseoparâmetroéobrigatórioounão.
<%@attributename="id"required="true"%>
Reparecomoésimples.Bastadeclararessadiretivadeatributonocomeçodonossotagfileetemosa capacidade de receber um valor quando formos usar a tag. Imagine usar essa tag nova no nossoformulário:
<campoDataid="dataNascimento"/>
Já que nossa tag sabe receber parâmetros, basta usarmos esse parâmetro nos lugares adequadosatravésdeexpressionlanguage,comonocódigoabaixo:
<%@attributename="id"required="true"%>
<inputid="${id}"name="${id}"type="text"><script>$("#${id}").datepicker();</script>
Parausarnossatag,assimcomonasoutrastaglibs,precisamosimportarnossostagfiles.Comonãoestamos falandode taglibscomplexascomoaJSTL,massimdepequenosarquivosde tagsdonossopróprioprojeto,vamosreferenciardiretamenteapastaondenossosarquivos.tagestãosalvos.
8.3CRIANDOMINHASPRÓPRIASTAGSCOMTAGFILES
8.3CRIANDOMINHASPRÓPRIASTAGSCOMTAGFILES 109
Nossos tagfilesdevemficarnapastaWEB-INF/tags/dentrodoprojetoe,nomomentodeusarastags,vamosimportarcomamesmadiretiva<%@taglib%>queusamosantes.Adiferençaéque,além
dereceberoprefixodanovataglib,indicamosapastaWEB-INF/tags/comolocalizaçãodastags:
<%@taglibtagdir="/WEB-INF/tags"prefix="caelum"%>
Aqui decidimos importar a nova taglib sob o prefixo caelum, mas poderia ser qualquer nome.Assim,podemosusaratagcomo:
<caelum:campoDataid="dataNascimento"/>
Repare que o nome da nossa nova Tag, é o mesmo nome do arquivo que criamos, ou seja,campoData.tagseráutilizandocomo<caelum:campoData>.
UTILIZANDOBIBLIOTECASJAVASCRIPT
Para podermos utilizar bibliotecas Javascript em nossa aplicação precisamos importar oarquivo.jsquecontémabiblioteca.
Para fazermos essa importação, basta que no cabeçalho da página que queremos utilizar oJavascript,ouseja,naTaghead,declaremososeguinte:
<head><scriptsrc="js/arquivo.js"></script></head>
ParasabermaissobreJavaScript,temososcursosdeWebdaCaelum:
http://www.caelum.com.br/cursos-web-front-end/
110 8.3CRIANDOMINHASPRÓPRIASTAGSCOMTAGFILES
UTILIZANDOESTILOSEMCSS
Para que possamos construir uma interface agradável para o usuário, na maioria das vezessomenteHTMLnãoésuficiente.
Podemos também utilizar CSS (Cascading Stylesheet), que nada mais é que um arquivocontendo as definições visuais para sua página. Esses arquivos são distribuídos com a extensão.csseparaquepossamosusá-los,precisamostambémimportá-losdentrodatagheaddanossa
páginaquevaiutilizaressesestilos.Comoabaixo:
<head><linkhref="css/meuArquivo.css"rel="stylesheet"></head>
A Caelum oferece os treinamentosWD-43 eWD-47 para quem quer dominar as melhorestécnicas de desenvolvimentoWeb com semântica perfeita, estilos CSS poderosos e JavaScriptscorretosefuncionais.
http://www.caelum.com.br/cursos-web-front-end/
1. Vamos criar nossa tag para o campo de calendário com datepicker. Para isso vamos utilizar abibliotecajavascriptjQuery.
VáaoDesktop,eentrenapasta21/projeto-agenda/;
Copieosdiretóriosjsecssecole-osdentrodeWebContentnoseuprojeto;
EMCASA
Sevocêestiverfazendodecasa,podeacharessesdoisarquivosnopacotedoJQueryUIem:http://jqueryui.com/download
2. QueremosusarJSTLnapáginadeadicionarcontatos,aadiciona-contato.html.Mastemosum
problema. Não podemos utilizar as tags ou expression language em uma página HTML . Para
resolvermosisso,vamostrocarsuaextensãopara.jsp
Clique como botão direito em cima do arquivoadiciona-contato.html e escolha a opção
8.4 EXERCÍCIOS: CRIANDO NOSSA PRÓPRIA TAG PARACALENDÁRIO
8.4EXERCÍCIOS:CRIANDONOSSAPRÓPRIATAGPARACALENDÁRIO 111
Renameetroqueaextensãodehtmlparajsp,dessaformaoarquivosechamaráadiciona-
contato.jsp.
3. Tambémvamosimportarocabeçalhoeorodapénessapágina,portanto,vamosusarotaglibJSTLcore.
Adicionenotopodapágina:
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%>
Vamosimportarocabeçalho,depoisdatag<body>adicione:
<c:importurl="cabecalho.jsp"/>
Faltaorodapé.Antesdefecharotagbody(</body>)adicione:
<c:importurl="rodape.jsp"/>
Cuidadoparanãoapagaroformulário,precisaremosdelemaisparafrente.
Acesse http://localhost:8080/fj21-agenda/adiciona-contato.jsp e verifique o resultado com ocabeçalhoerodapé.
4. PrecisamosimportarojQuerynapáginaadiciona-contato.jsp.Paraissoadicionedepoisdatag
<html>eantesdatag<body>,osestilosCSSeosJavascripts
<html><head><linkhref="css/jquery.css"rel="stylesheet"><scriptsrc="js/jquery.js"></script><scriptsrc="js/jquery-ui.js"></script></head>
112 8.4EXERCÍCIOS:CRIANDONOSSAPRÓPRIATAGPARACALENDÁRIO
<body>
<!--Restantedapáginaaqui-->
1. Vamoscriaranossatagparaocalendário.
DentrodeWEB-INFcrieumdiretóriochamadotags.
CrieumarquivochamadocampoData.tagdentrodeWEB-INF/tags/:
<%@attributename="id"required="true"%>
<inputtype="text"id="${id}"name="${id}"/><script>$("#${id}").datepicker({dateFormat:'dd/mm/yy'});</script>
Parautilizá-la,vamosvoltarparaoadiciona-contato.jspejuntoàdeclaraçãodaTaglibcore,
vamosimportarnossanovatag:
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%><%@taglibtagdir="/WEB-INF/tags"prefix="caelum"%>
Bastatrocarmosoinputdadatadenascimentopelanossanovatag:
<formaction="adicionaContato">Nome:<inputtype="text"name="nome"/><br/>E-mail:<inputtype="text"name="email"/><br/>Endereço:<inputtype="text"name="endereco"/><br/>DataNascimento:<caelum:campoDataid="dataNascimento"/><br/>
<inputtype="submit"value="Gravar"/></form>
Recarregue a página adiciona-contato.jsp e clique no campo da data de nascimento. O
calendáriodeveseraberto,escolhaumadataefaçaagravaçãodocontato.
8.4EXERCÍCIOS:CRIANDONOSSAPRÓPRIATAGPARACALENDÁRIO 113
Verifiqueocódigofontegerado,ereparequeoJavascriptdocalendárioeoinputforamgeradosparanósatravésdenossaTaglib.
1. (opcional)ConsulteadocumentaçãodocomponentedatepickerdojQueryUIparavermaisopções.Vocêpodeacessá-laem:http://jqueryui.com/demos/datepicker/
Consulte,porexemplo,asopçõeschangeYearechangeMonthparamostrarmenusmaisfáceis
paraescolheroanoeomês.Hámuitasoutrasopçõestambém.
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
VocêpodetambémfazerocursodatadessaapostilanaCaelum
8.5PARASABERMAIS:OUTRASTAGLIBSNOMERCADO
114 8.5PARASABERMAIS:OUTRASTAGLIBSNOMERCADO
Muitosdosproblemasdodiaadiaquenóspassamos,alguémjápassouantes,portanto,muitasdasvezes, pode acontecer de já existir uma Taglib que resolva omesmo problema que você pode estarpassando.Portanto, recomendamosque antes de criar a suaTaglib, sempreverifique se já não existealgumaqueresolvaseuproblema.
OmercadoatualmentepossuiváriasTaglibsdisponíveis,eparadiversasfunções,algumasdasmaisconhecidassão:
Displaytag: É uma Taglib para geração fácil de tabelas, e pode ser encontrada emhttp://displaytag.sf.net
Cewolf:ÉumaTaglibparageraçãodegráficosnassuaspáginasetambémpodeserencontradaem:http://cewolf.sourceforge.net/new/
Waffle Taglib: Tags para auxiliar na criação de formulários HTML que é encontrada em:http://waffle.codehaus.org/taglib.html
1. Adicioneadisplaytagnoseuprojetoparafazeralistagemdoscontatos.
8.6DESAFIO:COLOCANDODISPLAYTAGNOPROJETO
8.6DESAFIO:COLOCANDODISPLAYTAGNOPROJETO 115
CAPÍTULO9
"Ensinaréaprenderduasvezes."--JosephJoubert
Nessecapítulo,vocêaprenderá:
OpadrãoarquiteturalMVC;AconstruirumframeworkMVCsimples.
ColocartodoHTMLdentrodeumaServletrealmentenãonospareceamelhorideia.Oqueacontecequando precisamosmudar o design da página?O designer não vai saber Java para editar a Servlet,recompilá-laecolocá-lanoservidor.
ImagineusarapenasJSP.Ficaríamoscommuitoscriptlet,queémuitodifícildedarmanutenção.
Umaideiamaisinteressanteéusaroqueébomdecadaumdosdois.
OJSPfoifeitoapenasparaapresentaroresultado,eelenãodeveriafazeracessosabancodedadosenemfazerainstanciaçãodeobjetos.IssodeveriaestaremcódigoJava,naServlet.
O ideal então é que a Servlet faça o trabalho árduo, a tal da lógicadenegócio. E o JSP apenasapresente visualmente os resultados gerados pela Servlet. A Servlet ficaria então com a lógica denegócios(ouregrasdenegócio)eoJSPtemalógicadeapresentação.
ImagineocódigodométododaservletAdicionaContatoServletquefizemosantes:
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse){
//logSystem.out.println("Tentandocriarumnovocontato...");
//acessaobeanContatocontato=newContato();//chamaossetters...
//adicionaaobancodedadosContatoDaodao=newContatoDao();dao.adiciona(contato);
MVC-MODELVIEWCONTROLLER
9.1SERVLETOUJSP?
116 9MVC-MODELVIEWCONTROLLER
//ok....visualizaçãoout.println("<html>");out.println("<body>");out.println("Contato"+contato.getNome()+"adicionadocomsucesso");out.println("</body>");out.println("</html>");
}
Repare que, no final do nossométodo,misturamos o códigoHTML com Java. O que queremosextrairdocódigoacimaéjustamenteessasúltimaslinhas.
Seriamuitomais interessanteparaoprogramador eparaodesigner terumarquivo JSPchamadocontato-adicionado.jspapenascomoHTML:
<html><body>Contato${param.nome}adicionadocomsucesso</body></html>
Buscamosumaformaderedirecionarasrequisições,capazdeencaminharessarequisiçãoparaumoutrorecursodoservidor:porexemploindodeumaservletparaumJSP.
Paraisso,fazemosodispatchdasrequisições,paraqueoJSPsósejarenderizadodepoisquesuasregrasdenegócio,dentrodeumaservletporexemplo,foramexecutadas.
9.1SERVLETOUJSP? 117
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
PoderíamosmelhoraranossaaplicaçãosetrabalhássemoscomocódigoJavanaservleteoHTMLapenasnoJSP.
A API de Servlets nos permite fazer tal redirecionamento. Basta conhecermos a URL que
queremosacessarepodemosusarumobjetoRequestDispatcherparaacessaroutrorecursoWeb,seja
esserecursoumapáginaJSPouumaservlet:
RequestDispatcherrd=request.getRequestDispatcher("/contato-adicionado.jsp");rd.forward(request,response);
PodemosfacilmenteexecutaralógicadenossaaplicaçãoWebemumaservleteentãoredirecionarparaumapáginaJSP,ondevocêpossuiseucódigoHTMLetagsqueirãomanipularosdadostrazidospelaservlet.
FORWARDEINCLUDE
Ométodo forward só pode ser chamado quando nada foi ainda escrito para a saída. No
momentoquealgoforescrito,ficaimpossívelredirecionarousuário,poisoprotocoloHTTPnãopossuimeiosdevoltaratrásnaquiloquejáfoienviadoaocliente.
ExisteoutrométododaclasseRequestDispatcherquerepresentaainclusãodepáginaenão
oredirecionamento.Essemétodosechamaincludeepodeserchamadoaqualquerinstantepara
acrescentaraoresultadodeumapáginaosdadosdeoutra.
Seuslivrosdetecnologiaparecemdoséculopassado?
9.2REQUESTDISPATCHER
118 9.2REQUESTDISPATCHER
Vamos evoluir nossa adição de contatos antes puramente usando Servlets para usar oRequestDispatcher.
1. Seguindo a separação aprendida nesse capítulo, queremos deixar em um JSP separado aresponsabilidadedemontaroHTMLaserdevolvidoparaousuário.
Crieentãoumnovoarquivocontato-adicionado.jspnapastaWebContent:
<html><body>Contato${param.nome}adicionadocomsucesso</body></html>
2. AlteresuaservletAdicionaContatoServletparaque,apósaexecuçãodalógicadenegócios,ofluxodarequisiçãosejaredirecionadoparanossonovoJSP.
Remova no fim da classe o código quemonta a saídaHTML (as chamadas deout.println).
VamossubstituirporumachamadaaoRequestDispatchereexibiromesmoresultadousandoo
JSPquecriamos.Achamadaficanofinaldenossaservlet:
RequestDispatcherrd=request.getRequestDispatcher("/contato-adicionado.jsp");rd.forward(request,response);
1. TesteaURL:http://localhost:8080/fj21-agenda/adiciona-contato.jsp
9.3EXERCÍCIOS:REQUESTDISPATCHER
Resultado
9.3EXERCÍCIOS:REQUESTDISPATCHER 119
Percebaquejáatingimosumresultadoquenãoerapossívelanteriormente.
MuitosprojetosantigosqueforamescritosemJavautilizavamsomenteJSPouservletseoresultadoera assustador: diferentes linguagens misturadas num único arquivo, tornando difícil a tarefa demanutenção do código.Como conteúdomostrado até essemomento, é possível escrever um códigocommuitomaisqualidade:cadatecnologiacomasuaresponsabilidade.
Aquitemosváriasservletsacessandoobancodedados,trabalhandocomosDAOsepedindoparaque o JSP apresente esses dados, o diagrama a seguir mostra a representação doAdicionaContatoServletapósamodificaçãodoexercícioanterior.
Temos o problema de ter muitas servlets. Para cada lógica de negócios, teríamos uma servletdiferente,quesignificaváriasportasdeentradas,algoabominávelemumprojetodeverdade.Imaginedezclassesdemodelo,cincológicasdiferentes,issototalizacinquentaformasdiferentesdeacesso.
EsequiséssemosfazerumLogdaschamadasdessaslógicas?TeríamosqueespalharocódigoparaesseLogportodanossaaplicação.
Sabemosdaexistênciadeferramentasparagerartalcódigoautomaticamente,masissonãoresolveoproblemadacomplexidadedeadministrartantasservlets.
Utilizaremosumaideiaquediminuirábastanteonúmerodeportasdeentradasemnossaaplicação:colocar tudoemumaServlet só e,deacordocomosparâmetrosqueoclienteusar,decidimosoqueexecutar.TeríamosaíumaServletparacontrolaressaparte,comooesboçoabaixo:
//TodasaslógicasdentrodeumaServlet@WebServlet("/sistema")publicclassSistemaTodoServletextendsHttpServlet{
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse){
Stringacao=request.getParameter("logica");
9.4MELHORANDOOPROCESSO
120 9.4MELHORANDOOPROCESSO
ContatoDaodao=newContatoDao();
if(acao.equals("AdicionaContato")){Contatocontato=newContato();contato.setNome(request.getParameter("nome"));contato.setEndereco(request.getParameter("endereco"));contato.setEmail(request.getParameter("email"));dao.adiciona(contato);
RequestDispatcherrd=request.getRequestDispatcher("/contato-adicionado.jsp");rd.forward(request,response);}elseif(acao.equals("ListaContatos")){//buscaalistanoDAO//despachaparaumjsp}elseif(acao.equals("RemoveContato")){//fazaremoçãoeredirecionaparaalista}}}
Poderíamos acessar no navegador algo como http://localhost:8080/<contexto>/sistema?
logica=AdicionaContato.
Masparacadaaçãoteríamosumif/elseif,tornandoaServletmuitogrande,comtodaregra
denegóciodosistemainteiro.
Podemosmelhorar fazendo refactoring de extrair métodos.Mas continuaríamos com uma classemuitogrande.
Seriamelhorcolocarcadaregradenegócio(comoinserircontato,removercontato,fazerrelatórioetc)emumaclasseseparada.Cadaação(regradenegócio)emnossaaplicaçãoestariaemumaclasse.
Entãovamosextrairanossa lógicaparadiferentesclasses,paraquenossaServletpudesse terumcódigomaisenxutocomoesse:
if(acao.equals("AdicionaContato")){newAdicionaContato().executa(request,response);}elseif(acao.equals("ListaContato")){newListaContatos().executa(request,response);}
EteríamosclassesAdicionaContato,ListaContatos,etccomummétodo(digamos,executa)
quefazalógicadenegóciosapropriada.
Porém, a cada lógica nova, lógica removida, alteração etc, temos que alterar essa servlet. Isso étrabalhosoemuitopropensoaerros.
Reparedoispontosnocódigoacima.Primeiroqueelepossuiomesmocomportamentodeswitch!
EswitchemJavaquasesemprepodesersubstituídocomvantagemporpolimorfismo,comoveremos
aseguir.Outraquestãoéquerecebemoscomoparâmetrojustamenteonomedaclassequechamamosemseguida.
9.4MELHORANDOOPROCESSO 121
Vamostentargeneralizarentão,queremosexecutaroseguintecódigo:
StringnomeDaClasse=request.getParameter("logica");newnomeDaClasse().executa(request,response);
Queremospegaronomedaclasseapartirdoparâmetroeinstanciá-la.Entretantonãopodemos,poisnomeDaClasseéonomedeumavariáveleocódigoacimanãoéválido.Onossoproblemaéquesó
sabemosoquevamosinstanciaremtempodeexecução(quandooparâmetrochegar)enãoemtempodecompilação.
Masapartirdonomedaclassenóspodemosrecuperarumobjetoquerepresentaráasinformaçõescontidas dentro daquela classe, como por exemplo atributos, métodos e construtores. Para queconsigamosesseobjeto,bastautilizarmosaclasseClassinvocandoométodoforNameindicandode
qualclassequeremosumarepresentação.IssonosretornaráumobjetodotipoClassrepresentandoa
classe.Comoabaixo:
StringnomeDaClasse="br.com.caelum.mvc.logica."+request.getParameter("logica");Classclasse=Class.forName(nomeDaClasse);
Ótimo,podemosterumarepresentaçãodeAdicionaContatooudeListaContato eassimpor
diante.Masprecisamosdealgumaformainstanciaressasclasses.
Já que umadas informações guardadas pelo objeto do tipoClass é o construtor, nós podemos
invocá-loparainstanciaraclasseatravésdométodonewInstance.
Objectobjeto=classe.newInstance();
Ecomochamarométodoexecuta?ReparequeotipodeclaradodonossoobjetoéObject.Dessa
forma,nãopodemoschamarométodoexecuta.Umaprimeiraalternativaseríamosfazernovamente
if/elseparasabermosqualéalógicaqueestásendoinvocada,comoabaixo:
StringnomeDaClasse="br.com.caelum.mvc."+request.getParameter("logica");Classclasse=Class.forName(nomeDaClasse);
Objectobjeto=classe.newInstance();
if(nomeDaClasse.equals("br.com.caelum.mvc.AdicionaContato")){((AdicionaContato)objeto).executa(request,response);}elseif(nomeDaClasse.equals("br.com.caelum.mvc.ListaContatos")){((ListaContatos)objeto).executa(request,response);}//eassimpordiante
Masestamosvoltandoparaoif/elsequeestávamosfugindonocomeço.Issonãoébom.Todo
esseif/elseéocasionadoporcontadotipoderetornodométodonewInstanceserObjectenós
tratarmoscadaumadenossaslógicasatravésdeumtipodiferente.
Repareque,tantoAdicionaContatoquantoListaContatos,sãoconsideradasLogicasdentro
122 9.4MELHORANDOOPROCESSO
donossocontexto.OquepodemosfazerentãoétratarambascomoalgoquesigaocontratodeLogica
implementandoumainterfacedemesmonomequedeclareométodoexecuta:
publicinterfaceLogica{voidexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException;}
PodemossimplificarnossaServletparaexecutaralógicadeformapolimórficae,tudoaquiloque
fazíamosemaproximadamente8linhasdecódigo,podemosfazeremapenas2:
Logicalogica=(Logica)classe.newInstance();logica.executa(request,response);
Dessaforma,umalógicasimplesparalogaralgonoconsolepoderiaserequivalentea:
publicclassPrimeiraLogicaimplementsLogica{publicvoidexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{System.out.println("Executandoalogicaeredirecionando...");RequestDispatcherrd=req.getRequestDispatcher("primeira-logica.jsp");rd.forward(req,res);}}
Alguém precisa controlar então que ação será executada para cada requisição, e que JSP seráutilizado.Podemosusarumaservletpara isso,eentãoelapassaa sera servletcontroladoradanossaaplicação,chamandoaaçãocorretaefazendoodispatchparaoJSPdesejado.
Melhorandoaindamaisnossaservletcontroladora,poderíamosdeixarnelaaresponsabilidadedenosredirecionar para uma página JSP ou para qualquer outra lógica ao final da execução das lógicas,bastandoqueométodoexecutaretorneumsimplesString,eliminandotodaarepetiçãodecódigo
RequestDispatcher.
ComeçaríamosalterandoaassinaturadométodoexecutadainterfaceLogicaqueeravoid e
agoraretornaráString:
publicinterfaceLogica{Stringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException;
}
DepoisfaríamosaslógicasretornaremumStringcomonomedo.jspquedeveserchamadoaofinaldassuasexecuções.
publicclassPrimeiraLogicaimplementsLogica{publicStringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{System.out.println("Executandoalogicaeredirecionando...");
9.4MELHORANDOOPROCESSO 123
return"primeira-logica.jsp";}}
Por fim, a servlet controladora deve receber esse String e implementar o código de
RequestDispatcher:
@WebServlet("/sistema")publicclassControllerServletextendsHttpServlet{
protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
Stringparametro=request.getParameter("logica");StringnomeDaClasse="br.com.caelum.mvc.logica."+parametro;
try{Class<?>classe=Class.forName(nomeDaClasse);Logicalogica=(Logica)classe.newInstance();
//RecebeoStringapósaexecuçãodalógicaStringpagina=logica.executa(request,response);
//FazoforwardparaapáginaJSPrequest.getRequestDispatcher(pagina).forward(request,response);
}catch(Exceptione){thrownewServletException("Alógicadenegócioscausouumaexceção",e);}}}
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
NotequeométodoforNamedaclasseClassretornaumobjetodotipoClass,masesseobjetoé
novo?Foirecicladoatravésdeumcachedessesobjetos?
Agoraéamelhorhoradeaprenderalgonovo
9.5RETOMANDOODESIGNPATTERNFACTORY
124 9.5RETOMANDOODESIGNPATTERNFACTORY
ReparequenãosabemosoqueaconteceexatamentedentrodométodoforName,masaoinvocá-lo
eaexecuçãoocorrercomsucesso,sabemosqueaclassequefoipassadaemformadeStringfoilidae
inicializadadentrodavirtualmachine.
Na primeira chamada aClass.forName para determinada classe, ela é inicializada. Já em uma
chamadaposterior,Class.forNamedevolveaclassequejáfoilidaeestánamemória,tudoissosem
queafeteonossocódigo.
Esse exemplo do Class.forName é ótimo para mostrar que qualquer código que isola a
instanciaçãoatravésdealgumrecursodiferentedoconstrutoréumafactory.
1. Crieasuainterfacenopacotebr.com.caelum.mvc.logica:
publicinterfaceLogica{Stringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException;}
1. CrieumaimplementaçãodainterfaceLogica,nossaclassePrimeiraLogica,tambémnopacote
br.com.caelum.mvc.logica:
publicclassPrimeiraLogicaimplementsLogica{publicStringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{
System.out.println("Executandoalogica...");
System.out.println("RetornandoonomedapáginaJSP...");return"primeira-logica.jsp";
}}
1. FaçaumarquivoJSPchamadoprimeira-logica.jspdentrododiretórioWebContent:
<html><body><h1>Páginadanossaprimeiralógica</h1></body></html>
2. VamosescrevernossaServletquecoordenaráofluxodanossaaplicação.
CriesuaservletchamadaControllerServletnopacotebr.com.caelum.mvc.servlet:
@WebServlet("/mvc")publicclassControllerServletextendsHttpServlet{protectedvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
9.6 EXERCÍCIOS: CRIANDO NOSSAS LÓGICAS E A SERVLET DECONTROLE
9.6EXERCÍCIOS:CRIANDONOSSASLÓGICASEASERVLETDECONTROLE 125
throwsServletException,IOException{
Stringparametro=request.getParameter("logica");StringnomeDaClasse="br.com.caelum.mvc.logica."+parametro;
try{Classclasse=Class.forName(nomeDaClasse);
Logicalogica=(Logica)classe.newInstance();Stringpagina=logica.executa(request,response);
request.getRequestDispatcher(pagina).forward(request,response);
}catch(Exceptione){thrownewServletException("Alógicadenegócioscausouumaexceção",e);}}}
1. Testeaurlhttp://localhost:8080/fj21-agenda/mvc?logica=PrimeiraLogica
1. Crie uma nova classe chamada RemoveContatoLogica no mesmo pacote
br.com.caelum.mvc.logica.DevemosimplementarainterfaceLogicaedurantesuaexecução
receberemosumidpelorequesteremoveremosocontatonobancoapartirdesteid.
publicclassRemoveContatoLogicaimplementsLogica{
publicStringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{
longid=Long.parseLong(req.getParameter("id"));
Contatocontato=newContato();contato.setId(id);
ContatoDaodao=newContatoDao();dao.exclui(contato);
System.out.println("Excluindocontato...");
return"lista-contatos.jsp";}
9.7 EXERCÍCIOS: CRIANDO UMA LÓGICA PARA REMOVERCONTATOS
126 9.7EXERCÍCIOS:CRIANDOUMALÓGICAPARAREMOVERCONTATOS
}
1. Napáginalista-contatos.jsp, vamos acrescentar uma colunana tabela que lista os contatos
comumlinkchamandoalógicaderemoçãoepassandooiddocontato:
<!--códigoomitido-->
<c:forEachvar="contato"items="${dao.lista}"><tr>
<!--códigoomitido-->
<td><ahref="mvc?logica=RemoveContatoLogica&id=${contato.id}">Remover</a></td></tr></c:forEach>
<!--códigoomitido-->
1. Testea lógicade remoçãoacessandohttp://localhost:8080/fj21-agenda/lista-contatos.jspeclicandoemalgumlinkRemover.
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
Agora que todo nosso processamento está passando pela Servlet controladora e conseguimosorganizarnossocódigoemcamadasbemdefinidas,nosdeparamoscomumasituaçãoqueaindaestáumpoucodistantedoideal.
Se olharmos a página lista-contatos.jsp veremos que para fazer a listagem dos contatos
funcionar estamos criando uma instância da classe ContatoDao para utilizá-la depois no
<c:forEach>recuperandoumalistadecontatos.
EditoraCasadoCódigocomlivrosdeumaformadiferente
9.8FAZENDOALÓGICAPARALISTAROSCONTATOS
9.8FAZENDOALÓGICAPARALISTAROSCONTATOS 127
<jsp:useBeanid="dao"class="br.com.caelum.agenda.dao.ContatoDao"/>
<table><c:forEachvar="contato"items="${dao.lista}"><tr><td>${contato.nome}</td><!--códigoomitido--></tr></c:forEach></table>
Instanciar objetos da camada Model na camada View não é considerada uma boa prática naarquiteturaMVC(antipattern).
Podemos resolver facilmente isso tranferindo essa responsabilidade demontar a lista de contatosparaumalógicaListaContatosLogicaedepoispassá-laprontadiretoparaoJSPpelorequest.
Paraguardarmosalgonarequisição,precisamosinvocarométodo.setAttribute() no request.
Passamos para essemétodo uma identificação para o objeto que estamos guardando na requisição etambémpassamosopróprioobjetoparaserguardadonorequest.
publicclassListaContatosLogicaimplementsLogica{
publicStringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{
//MontaalistadecontatosList<Contato>contatos=newContatoDao().getLista();
//Guardaalistanorequestreq.setAttribute("contatos",contatos);
return"lista-contatos.jsp";}}
Agora é só ajustar a página lista-contatos.jsp para não instanciar mais o ContatoDao ,
removendoalinha<jsp:useBeanid="dao"class="br.com.caelum.agenda.dao.ContatoDao"/>,
edepoisfazercomqueo<c:forEach>usealistadecontatosquefoicolocadanorequest:
<c:forEachvar="contato"items="${contatos}">
1. Crie uma nova classe chamada ListaContatosLogica no mesmo pacote
br.com.caelum.mvc.logica. Devemos implementar nela a interface Logica e, durante sua
execução vamos criar uma lista de contatos através de uma instância da classe ContatoDao ,
guardá-lanorequesteretornarparaaservletcontroladora:
publicclassListaContatosLogicaimplementsLogica{
publicStringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{
9.9EXERCÍCIOS:LÓGICAPARALISTARCONTATOS
128 9.9EXERCÍCIOS:LÓGICAPARALISTARCONTATOS
List<Contato>contatos=newContatoDao().getLista();
req.setAttribute("contatos",contatos);
return"lista-contatos.jsp";}}
1. Agora,vamosmodificarapáginalista-contatos.jspparanãoinstanciarmaisContatoDaona
View, removendo a linha <jsp:useBean id="dao"
class="br.com.caelum.agenda.dao.ContatoDao"/>,ealteraro<c:forEach>parausaralista
decontatosquefoicolocadapelalógicanorequestaoinvésde${dao.lista}:
<c:forEachvar="contato"items="${contatos}">
1. Agora podemos testar chamando: http://localhost:8080/fj21-agenda/mvc?
logica=ListaContatosLogica
2. Depoisdessasalterações, seránecessárioalteraro retornodaclasseRemoveContatoLogica pois
agoraachamadadiretadolista-contatos.jsp nãoémaispossível.Devemosagora chamara
lógicaquelistaoscontatos:
publicclassRemoveContatoLogicaimplementsLogica{
publicStringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{
//códigoomitido
return"mvc?logica=ListaContatosLogica";}
}
Como alteramos nossa listagem para ser acessada pela lógica ListaContatosLogica , se
acessarmosa jsplista-contatos.jsp diretamentepelonavegador, a páginanãomostrará nenhum
contato.Precisamosentãosemprepassarpelalógica,queporsuavezdisponibilizaráalistagemparaapágina.
Portanto,nãodevemospermitirqueousuárioacessediretamentenossapágina.Paraimpossibilitaresteacessodireto,colocaremosnossaspáginasdentrododiretórioWEB-INF/jsp.
AgoraqueestamosusandoMVC,umaboapráticaénãodeixarmososusuáriosacessaremnossaspáginasdiretamente,esimpassandosempreporumalógica.
Nossalógicadelistagemficarádaseguinteforma:
publicclassListaContatosLogicaimplementsLogica{
9.10ESCONDENDONOSSASPÁGINAS
9.10ESCONDENDONOSSASPÁGINAS 129
publicStringexecuta(HttpServletRequestreq,HttpServletResponseres)throwsException{
List<Contato>contatos=newContatoDao().getLista();
req.setAttribute("contatos",contatos);
return"/WEB-INF/jsp/lista-contatos.jsp";}
Alémdisso,sequisermosaplicarestemesmoconceitoparaasdemaisjsps,precisaremosalterarasdemaislógicascorrespondentesacrescentandoodiretórioWEB-INF/jspantesdonomedapágina.
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
1. CrieumalógicachamadaAlteraContatoLogicaetesteamesmaatravésdeumlinknalistagem
dalista-contatos.jsp.Lembre-se,antesdechamaressalógicaéprecisocriarumaoutralógica
quemostreosdadosdocontatoemumanovapágina,permitindoassimaalteraçãodosdados,esódepois,nocliquedeumbotão,queaalteraçãoserádefatoefetivada.
2. Criealógicadeadicionarcontatos(AdicionaContatoLogica).Reparequeelaébemparecidacom
aAlteraContatoLogica.Crie um formulário de adiçãodenovo contato.Coloqueum linkpara
adicionarnovoscontatosdentrodolista-contatos.jsp.
3. Desafio:Aslógicasdeadiçãoedealteraçãoficarammuitoparecidas.Tentecriarumaversãodeumadessas lógicas que faça as duas. Dica: A única diferença entre as duas é a presença ou não doparâmetroId.
4. Desafio: Altere seu projeto para que nenhuma jsp seja acessível diretamente, colocando-as nodiretórioWEB-INF/jsp.Modifique também suas lógicas de acordo.OBS:Deverá ser criadaumanovalógicaparaavisualizaçãodoformuláriodeadiçãodecontatos.
JáconheceoscursosonlineAlura?
9.11EXERCÍCIOSOPCIONAIS
130 9.11EXERCÍCIOSOPCIONAIS
Generalizandoomodeloacima,podemosdarnomesacadaumadaspartesdessanossaarquitetura.QueméresponsávelporapresentarosresultadosnapáginawebéchamadodeApresentação(View).
Aservlet(eauxiliares)quefazosdispatchesparaquemdeveexecutardeterminadatarefaéchamadadeControladora(Controller).
Asclassesque representamsuasentidadeseasque teajudamaarmazenarebuscarosdados sãochamadasdeModelo(Model).
EssestrêsformamumpadrãoarquiteturalchamadodeMVC,ouModelViewController.Elepodesofrervariaçõesdediversasmaneiras.OqueoMVCgaranteéaseparaçãodetarefas,facilitandoassimareescritadealgumaparte,eamanutençãodocódigo.
OfamosoStrutsajudavocêaimplementaroMVC,poistemumacontroladorajápronta,comumasériedeferramentasparateauxiliar.OHibernatepodeserusadocomoModel,porexemplo.EcomoViewvocênãoprecisausarsóJSP,podeusaraferramentaVelocity,porexemplo.
Hádiversasopçõesparaacamadadecontrolenomercado.Vejaumpoucosobrealgumasdelas:
StrutsAction-ocontroladormaisfamosodomercadoJava,éutilizadoprincipalmenteporseromais divulgado e com tutoriais mais acessíveis. Possui vantagens características do MVC edesvantagensquenaépocaaindanãoerampercebidas.ÉocontroladorpedidonamaiorpartedasvagasemJavahojeemdia.ÉumprojetoquenãoterágrandesatualizaçõespoisaequipedelesejuntoucomoWebWorkparafazeroStruts2,novaversãodoStrutsincompatívelcomaprimeiraetotalmentebaseadanoWebWork.
VRaptor - desenvolvido inicialmente por profissionais daCaelume baseado emdiversas ideiasdoscontroladoresmencionadosacima,oVRaptorusaoconceitodefavorecerConvençõesemvezdeConfiguraçõesparaminimizarousodeXMLeanotaçõesemsuaaplicaçãoWeb.
JSF - JSF é uma especificação Java para frameworksMVC. Ele é baseado em componentes epossui várias facilidades para desenvolver a interface gráfica.Devido ao fato de ser um padrãooficial,eleébastanteadotado.OJSFéensinadoemdetalhesnonossocursoFJ-26.
SpringMVC-éumapartedoSpringFrameworkfocadoemimplementarumcontroladorMVC.ÉfácildeusaremsuasúltimasversõesetemavantagemdeseintegraratodaaestruturadoSpringcomváriastecnologiasdisponíveis.
9.12MODELVIEWCONTROLLER
9.13LISTADETECNOLOGIAS:CAMADADECONTROLE
9.12MODELVIEWCONTROLLER 131
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
Temostambémdiversasopçõesparaacamadadevisualização.Umpoucosobrealgumasdelas:
JSP - como já vimos, o JavaServer Pages, temos umaboa ideia do que ele é, suas vantagens edesvantagens.Ousodetaglibs(aJSTLporexemplo)eexpressionlanguageémuitoimportantesevocêescolherJSPparaoseuprojeto.Éaescolhadomercadohojeemdia.
Velocity-umprojetoantigo,noqualaELdoJSPsebaseou,capazdefazertudooquevocêprecisaparaasuapáginadeumamaneiraextremamentecompacta. IndicadopelaCaelumparaconhecerumpoucomaissobreoutrasopçõesparacamadadevisualização.
Freemarker-similaraoVelocityecomideiasdoJSP-comosuporteataglibs-ofreemarkervemsendocadavezmaisutilizado,elepossuidiversasferramentasnahoradeformatarseutextoquefacilitammuitootrabalhododesigner.
Sitemesh - não é uma alternativa para as ferramentas anterioresmas sim umamaneira de criartemplatesparaseusite,comumaideiamuitoparecidacomostrutstiles,porémgenérica:funcionainclusivecomoutraslinguagenscomoPHPetc.
Em pequenas equipes, é importante uma conversa para mostrar exemplos de cada uma dastecnologiasacimaparaodesigner,afinalquemvai trabalharcomaspáginaséele.Aqueelepreferir,vocêusa,afinaltodaselasfazemomesmodemaneirasdiferentes.Comoemumprojetoécomumterpoucos designers e muitos programadores, talvez seja proveitoso facilitar um pouco o trabalho paraaqueles.
VocêpodetambémfazerocursodatadessaapostilanaCaelum
9.14LISTADETECNOLOGIAS:CAMADADEVISUALIZAÇÃO
9.15 DISCUSSÃO EM AULA: OS PADRÕES COMMAND E FRONTCONTROLLER
132 9.14LISTADETECNOLOGIAS:CAMADADEVISUALIZAÇÃO
CAPÍTULO10
"Aartenuncaestáterminada,apenasabandonada."--LeonardoDaVinci
Aotérminodessecapítulo,vocêserácapazde:
criarclassesquefiltramarequisiçãoearesposta;guardarobjetosnarequisição;descreveroqueéinjeçãodedependências;descreveroqueéinversãodecontrole;
Em qualquer aplicação surgem requisitos que não são diretamente relacionados com a regra denegócio.Umexemploclássicodessesrequisitosnãofuncionaiséaauditoria(Logging).Queremoslogaraschamadasdeumalógicadanossaaplicação.Outrosexemplossãoautorização,tratamentodeerrooucriptografia.Existemváriosoutros,masoque todoseles tememcomuméquenão são relacionadoscomasregrasdenegócios.
A pergunta como implementar estas funcionalidades, nos vem a cabeça. A primeira ideia seriacolocar o código diretamente na classe que possui a lógica. A classe seguinte mostra através depseudocódigoaschamadasparafazerauditoriaeautorização:
publicclassRemoveContatoLogicimplementsLogica{
publicvoidexecuta(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{
//auditoriaLogger.info("acessandoremovecontatologic");
//autorizaçãoif(!usuario.ehCliente()){request.getRequestDispatcher("/acessoNegado.jsp").forward(request,response);}
//todalógicapararemoverocontatoaqui//...}}
RECURSOSIMPORTANTES:FILTROS
10.1REDUZINDOOACOPLAMENTOCOMFILTROS
10RECURSOSIMPORTANTES:FILTROS 133
Podemosverquealémdalógicaéprecisoimplementarosoutrosrequisitos,masnãosóapenasnalógica que altera o contato, também é necessário colocar omesmo código nas lógicas que adiciona,removeoufazalistagemdoscontatos.Issopodepiorarpensandoqueexistemmuitomaisrequisitosnãofuncionais, como resultado o código aumenta em cada lógica. Desse jeito criamos um acoplamentomuitoforteentrealogicaeaimplementaçãodosrequisitosnãofuncionais.Ograndeproblemaéqueosmesmosficamespalhadosemtodasaslógicas.Aimagemabaixoilustraesseacoplamento:
A API de Servlets nos provê um mecanismo para tirar esse acoplamento e isolar essecomportamento,quesãoosFiltros.Filtrossãoclassesquepermitemqueexecutemoscódigoantesdarequisiçãoetambémdepoisquearespostafoigerada.
Umaboaanalogiaépensarqueaslógicassãoquartosemumacasa.Paraacessarumquartoéprecisopassarporváriasportas.Asportassãoosfiltros,ondevocêpassanaidaenavolta.Cadafiltroencapsulaapenasuma responsabilidade,ou sejaum filtropara fazer auditoria,outropara fazer a segurançaetc.Entãoépossívelusarváriosfiltrosemconjunto.Umaportatambémpodeficarfechada,casoousuárionão possua acesso a lógica, ou seja, o filtro pode negar a execução de uma lógica. Veja a imagemseguintequemostraosfiltrosaplicadosnoexemploanterior:
134 10RECURSOSIMPORTANTES:FILTROS
A grande vantagem é que cada requisito fica em um lugar só e conseguimos desacoplar nossaslógicas.
ParacriarmosfiltrosutilizandoaAPIdeServletsdoJavaEE5,temosasmesmasdificuldadesquetemosquandovamosdefinirServlets.Paracadafiltroénecessáriocriarmosaclassequeimplementa
ainterfacejavax.servlet.Filteredepoisdeclararmosofiltronoweb.xml, alémde termosque
declararparaquaisURL'saquelefiltroseráaplicado.
Configuraçãodeumfiltronoweb.xml:
<filter><filter-name>meuFiltro</filter-name><filter-class>br.com.caelum.filtro.MeuFiltro</filter-class></filter>
<filter-mapping><filter-name>meuFiltro</filter-name><url-pattern>/*</url-pattern></filter-mapping>
MasparadefinirmosumfiltrousandoanovaAPIdeServletsdoJavaEE6,bastaapenascriarmosuma classe que implementa a interface javax.servlet.Filter e anotarmos a classe com
@WebFilter.Podemosaproveitarepassarcomoparâmetronaanotaçãoopadrãoderequisiçõesque
serãofiltradas:
@WebFilter("/oi")publicclassMeuFiltroimplementsFilter{publicvoiddoFilter(ServletRequestreq,ServletResponseres,FilterChainchain){//...}}
Destaformaindicamosquetodasasrequisiçõesvindasapartirde/oiserãofiltradase,portanto,o
filtroseráaplicadoemcadarequisição.
10RECURSOSIMPORTANTES:FILTROS 135
Épossível usar um filtromais especifico.Por exemplo, podemos filtrar todas as requisiçõesparapaginasJSPs:
@WebFilter("/*.jsp")publicclassMeuFiltroimplementsFilter{publicvoiddoFilter(ServletRequestreq,ServletResponseres,FilterChainchain){//...}}
Ouumfiltromaisamplo,filtrandoTODASasrequisiçõesdaaplicação:
@WebFilter("/*")publicclassMeuFiltroimplementsFilter{publicvoiddoFilter(ServletRequestreq,ServletResponseres,FilterChainchain){//...}}
Ao implementar a interface Filter, temos que implementar 3 métodos: init, destroy e
doFilter.
Os métodos init e destroy possuem a mesma função dos métodos de mesmo nome da
Servlet,ouseja,executaralgoquandooseufiltroécarregadopelocontainerequandoédescarregado
pelocontainer.
Ométodoque fará todooprocessamentoquequeremosexecutaréodoFilter, que recebe três
parâmetros:ServletRequest,ServletResponseeFilterChain.
@WebFilter("/*")publicclassFiltroTempoDeExecucaoimplementsFilter{
//implementaçãodoinitedestroy
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
//todooprocessamentovaiaqui}}
PercebaasemelhançadessemétodocomométodoservicedaclasseServlet.Aideiadofiltroé
também processar requests,mas ele poderá fazer isso demaneiramais genérica para vários tipos derequests.Masumfiltrovaialémdeumservlet,comumfiltropodemostambémfechar"aporta".EssepodervemdoargumentoFilterChain(acadeiadefiltros).Elenospermiteindicaraocontainerqueo
requestdeveprosseguirseuprocessamento. IssoéfeitocomumachamadadométododoFilter da
classeFilterChain:
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)
136 10RECURSOSIMPORTANTES:FILTROS
throwsIOException,ServletException{
//passapelaportachain.doFilter(request,response);}
Um filtro não serve para processar toda a requisição. A ideia é ele interceptar vários requestssemelhantes, executar algo, mas depois permitir que o processamento normal do request prossigaatravésdasServletseJSPsnormais.
Qualquer código colocado antes da chamada chain.doFilter(request,response) será
executadonaida,qualquercódigodepoisnavolta.Comissopodemosfazerumverificaçãodeacessoantesdalógica,ouabrirumrecurso(conexãooutransação)antesenavoltafecharomesmo.Umfiltroéidealparafazertratamentodeerroroumedirotempodeexecução.
A única coisa que precisamos fazer para que o nosso filtro funcione é registrá-lo, para que ocontainer saiba que ele precisa ser executado. Fazemos isso de forma simples, usando atributos daanotação@WebFilter. Através dos atributos, declaramos o filtro e quais URLs ou Servlets serão
filtradas.
O atributo filterName define um nome (ou alias) para o filtro. Se não definirmos o atributofilterName para nosso filtro, o nome dele será o nome completo da classe, damesma forma que
acontececomasServlets.ParadefinirmosquaisURLpassarãopelonossofiltro,podemosfazê-lode
maneira similaràanotaçã[email protected] aquele filtropara apenasumaURL,
passamos através do parâmetro na anotação @WebFilter como foi feito no exemplo acima
-@WebFilter("/oi").Mas,sequisermosdefinirquemaisdeumaURLseráfiltrada,podemosusaro
atributourlPatterns:
@WebFilter(filterName="MeuFiltro",ulrPatterns={"/oi","/ola"})publicclassMeuFiltroimplementsFilter{publicvoiddoFilter(ServletRequestreq,ServletResponseres,FilterChainchain){//...}}
PodemosaindaconfigurarquaisservletsserãofiltradosporaquelefiltrodeclarandoseusnomesnoatributoservletNames.Porexemplo:
@WebFilter(filterName="MeuFiltro",servletNames={"meuServlet","outroServlet"})publicclassMeuFiltroimplementsFilter{publicvoiddoFilter(ServletRequestreq,ServletResponseres,FilterChainchain){//...}}
10RECURSOSIMPORTANTES:FILTROS 137
OUTRASANOTAÇÕES
ExistemoutrasanotaçõespresentesnaAPIServlets3.0:
@WEBLISTENER -Utilizada para definirListeners de eventos que podemocorrer emváriospontosdasuaaplicação;equivaleaodehoje;
@WEBINITPARAM - Utilizada para especificar parâmetros de inicialização que podem serpassadosparaServletseFiltros.Podeserpassadacomoparâmetroparaasanotações
@WebServlete@WebFilter;equivaleaodehoje;
@MULTIPARTCONFIG -Utilizada para definir que um determinado Servlet receberá uma
requisiçãodotipomime/multipart.
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
1. Vamoscriaronossofiltroparamedirotempodeexecuçãodeumarequisição.
Crie uma nova classe chamada FiltroTempoDeExecucao no pacote
br.com.caelum.agenda.filtroefaçaelaimplementarainterfacejavax.servlet.Filter
Anote-acom@WebFilteredigaqueTODASasrequisiçõesparaanossaaplicaçãodevemser
filtradas(@WebFilter("/*")).
DeixeosmétodosinitedestroyvazioseimplementeodoFilter:
Seuslivrosdetecnologiaparecemdoséculopassado?
10.2 EXERCÍCIOS OPCIONAIS: FILTRO PARA MEDIR O TEMPO DEEXECUÇÃO
138 10.2EXERCÍCIOSOPCIONAIS:FILTROPARAMEDIROTEMPODEEXECUÇÃO
@WebFilter("/*")publicclassFiltroTempoDeExecucaoimplementsFilter{publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
longtempoInicial=System.currentTimeMillis();
chain.doFilter(request,response);
longtempoFinal=System.currentTimeMillis();Stringuri=((HttpServletRequest)request).getRequestURI();Stringparametros=((HttpServletRequest)request).getParameter("logica");System.out.println("Tempodarequisicaode"+uri+"?logica="+parametros+"demorou(ms):"+(tempoFinal-tempoInicial));
}//métodosinitedestroyomitidos}
Reinicie o servidor e acesse http://localhost:8080/fj21-agenda/mvc?logica=ListaContatosLogica
Procureasaídanoconsole.
Nossaaplicaçãodeagendanecessita,emváriosmomentos,deumaconexãocomobancodedadose,paraisso,onossoDAOinvocaemseuconstrutoraConnectionFactorypedindoparaamesmauma
novaconexão.Masemquallugarficaráofechamentodaconexão?
publicclassContatoDao{privateConnectionconnection;
publicContatoDao(){this.connection=newConnectionFactory().getConnection();}
//métodosadiciona,remove,getListaetc//ondefechamosaconexão?}
Até omomento, não estamos nos preocupando como fechamento das conexões como banco dedados. Isso é uma péssima prática para uma aplicação que vai para produção. Estamos deixandoconexões abertas e sobrecarregando o servidor de banco de dados, que pode aguentar apenas umdeterminado número de conexões abertas, o que fará com que sua aplicação pare de funcionarsubitamente.
OutroproblemaquetemosaoseguiressaestratégiadeadquirirconexãonoconstrutordosDAOsé
quando queremos usar dois DAOs diferentes, por exemplo ContatoDao e FornecedorDao. Ao
10.3PROBLEMASNACRIAÇÃODASCONEXÕES
10.3PROBLEMASNACRIAÇÃODASCONEXÕES 139
instanciarmos ambos os DAOs, vamos abrir duas conexões, enquanto poderíamos abrir apenas uma
conexãoparafazerasduasoperações.Deoutraformaficaimpossívelrealizarasoperaçõesnumaúnicatransação.
JápercebemosquenãoéumaboaideiacolocarmosacriaçãodaconexãonoconstrutordosnossosDAOs.Masqualéolugaridealparacriarmosessaconexãocomobancodedados?
PoderíamoscriaraconexãodentrodecadamétododoDAO,masnessecaso,seprecisássemosusar
dois métodos diferentes do DAO em uma mesma operação, novamente abriríamos mais de uma
conexão. Dessa forma, abrir e fechar as conexões dentro dosmétodos dos DAOs também não nos
pareceumaboaalternativa.
Precisamos,dealgumaforma,criaraconexãoefazercomqueessamesmaconexãopossaserusadaportodososseusDAOsemumadeterminadarequisição.Assim,podemoscriarnossasconexõescomo
bancodedadosdentrodenossasServlets(oulógicas,nocasodonossoframeworkMVCvistono
capítuloanterior)eapenaspassá-lasparaoDAOquevamosutilizar.Paraisso,nossosDAOsdeverãoter
umconstrutorquerecebaConnection.
Vejamos:
publicclassContatoDao{privateConnectionconnection;
publicContatoDao(Connectionconnection){this.connection=connection;}
//métodosadiciona,remove,getListaetc}
publicclassAdicionaContatoLogicimplementsLogica{publicStringexecuta(HttpServletRequestrequest,HttpServletResponseresponse){Contatocontato=//contatomontadocomosdadosdorequest
Connectionconnection=newConnectionFactory().getConnection();
//passaconexãoproconstrutorContatoDaodao=newContatoDao(connection);dao.adiciona(contato);
connection.close();
//retornaparaoJSP}}
Isso já é uma grande evolução com relação ao que tínhamos no começo mas ainda não é uma
10.4TENTANDOOUTRASESTRATÉGIAS
140 10.4TENTANDOOUTRASESTRATÉGIAS
solução muito boa. Acoplamos com a ConnectionFactory todas as nossas lógicas que precisam
utilizar DAOs. E, em orientação a objetos, não é uma boa prática deixarmos nossas classes com
acoplamentoalto.
INJEÇÃODEDEPENDÊNCIASEINVERSÃODECONTROLE
Aonão fazermosmais a criaçãoda conexãodentrodoContatoDaomas sim recebermos a
Connectiondaqualdependemosatravésdoconstrutor,dizemosqueonossoDAOnãotemmais
o controle sobre a criação da Connection e, por isso, estamos Invertendo oControle dessacriação.AúnicacoisaqueonossoDAOdizéqueeledependedeumaConnectionatravésdo
construtore,porisso,onossoDAOprecisaqueesseobjetodotipoConnectionsejarecebido,ou
seja,eleesperaqueadependênciasejainjetada.
Injeção de Dependências e Inversão de Controle são conceitos muito importantes nasaplicaçõesatuaisenósasestudaremoscommaisdetalhesaindanocurso.
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
NãoqueremostambémqueanossalógicaconheçaaclasseConnectionFactorymas,aindaassim,
precisamosqueelapossuaaconexãoparaquepossamosrepassá-laparaoDAO.
Paradiminuirmosesseacoplamento,queremosque,semprequechegarumarequisiçãoparaanossaaplicação, uma conexão seja aberta e, depois que essa requisição for processada, ela seja fechada.Tambémpodemosadicionaro tratamentoa transaçãoaqui, se acharmosnecessário.Precisamosentãointerceptartodarequisiçãoparaexecutaressesprocedimentos.
Agoraéamelhorhoradeaprenderalgonovo
10.5REDUZINDOOACOPLAMENTOCOMFILTROS
10.1REDUZINDOOACOPLAMENTOCOMFILTROS 141
ComojávistoosFiltrospermitemqueexecutemoscódigoantesdarequisiçãoetambémdepoisquearespostafoigerada.Idealparaabrirumaconexãoantesefecharnavolta.
Vamosentãoimplementarumfiltrocomessecomportamento:
@WebFilter("/*")publicclassFiltroConexaoimplementsFilter{//implementaçãodoinitedestroy,senecessário
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
//abreumaconexãoConnectionconnection=newConnectionFactory().getConnection();
//indicaqueoprocessamentodorequestdeveprosseguirchain.doFilter(request,response);
//fechaconexãoconnection.close();}}
Comaconexãoaberta,precisamosentãofazercomquearequisiçãosaiadonossofiltroeváparaopróximo passo, seja ele um outro filtro, ou uma Servlet ou um JSP. Dessa forma, nossa lógica denegóciopodeexecutarnormalmente.Para isso,usamoso argumentoFilterChain quenospermite
indicaraocontainerqueorequestdeveprosseguirseuprocessamento.IssoéfeitocomumachamadaaodoFilterdoFilterChain:
Atéagora,conseguimosabrirumaconexãonocomeçodosrequests,prosseguiroprocessamentodorequestnormalmenteefecharaconexãoaposdaexecução.Masnossaslógicasvãoexecutar,digamos,manipulações de Contatos e vão precisar da conexão aberta no filtro. Mas como acessá-la? Como,dentrodeumaServlet,pegarumobjetocriadodentrodeumfiltro,umaoutraclasse?
Aideiaéassociar(pendurar)dealgumaformaaconexãocriadaaorequestatual.IssoporquetantoofiltroquantoaServletestãonomesmorequesteporque,comodefinimos,asconexõesvãoserabertasporrequests.
142 10.5REDUZINDOOACOPLAMENTOCOMFILTROS
Paraguardarmosalgonarequisição,precisamosinvocarométodosetAttributenorequest.
Passamos para essemétodo uma identificação para o objeto que estamos guardando na requisição etambémpassamosopróprioobjetoparaserguardadonorequest.
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain){
Connectionconnection=newConnectionFactory().getConnection();
//"penduraumobjetonoRequest"request.setAttribute("connection",connection);
chain.doFilter(request,response);
connection.close();}
AoinvocarmosodoFilter,arequisiçãoseguiráoseufluxonormallevandooobjetoconnection
junto.Oqueousuário requisitou será entãoexecutadoaté a resposta sergerada (porumaServletouJSP). Após isso, a execução volta para o ponto imediatamente abaixo da invocação dochain.doFilter. Ou seja, através de um filtro conseguimos executar algo antes do request serprocessadoedepoisdarespostasergerada.
Pronto,nossoFiltroéoúnicopontodanossaaplicaçãoquecriaráconexões.
Repare comousamosowildcard no parâmetro da anotação para indicar que todas as requisiçõesserãofiltradase,portanto,terãoumaconexãoabertajádisponível.
Só falta na nossa lógica pegarmos a conexão que guardamos no request . Para isso basta
invocarmosométodogetAttributenorequest.Nossalógicaficarádaseguintemaneira:
publicclassAdicionaContatoLogicimplementsLogica{
publicStringexecuta(HttpServletRequestrequest,HttpServletResponseresponse)
10.5REDUZINDOOACOPLAMENTOCOMFILTROS 143
throwsException{
Contatocontato=//contatomontadocomosdadosdorequest
//buscandoaconexãodorequestConnectionconnection=(Connection)request.getAttribute("connection");
ContatoDaodao=newContatoDao(connection);dao.adiciona(contato);
//fazoreturndoJSPcomodecostume}}
Umaoutragrandevantagemdessedesacoplamentoéqueeletornaonossocódigomaisfácildesetestarunitariamente,assuntoqueaprendemosepraticamosbastantenocursoFJ-22.
1. Vamoscriaronossofiltroparaabrirefecharaconexãocomobancodedados
CrieumanovaclassechamadaFiltroConexaonopacotebr.com.caelum.agenda.filtro e
façaelaimplementarainterfacejavax.servlet.Filter
Anote-acom@WebFilter("/*")para registraro filtronocontainer e fazer comque todasas
requisiçõespassemporele:
DeixeosmétodosinitedestroyvazioseimplementeodoFilter:
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
try{Connectionconnection=newConnectionFactory().getConnection();
//pendurandoaconnectionnarequisiçãorequest.setAttribute("conexao",connection);
chain.doFilter(request,response);
connection.close();}catch(SQLExceptione){thrownewServletException(e);}}
2. CrieumconstrutornoseuContatoDao(casonãoexista)querecebaConnectionearmazene-ano
atributo:
publicclassContatoDao{privateConnectionconnection;
publicContatoDao(Connectionconnection){
10.6EXERCÍCIOS:FILTROS
144 10.6EXERCÍCIOS:FILTROS
this.connection=connection;}
//outroconstrutoremétodosdoDAO}
3. Na suaRemoveContatoLogica, criada no capítulo anterior, busque a conexão no request, e
repasse-aparaoDAO.ProcurenalógicaacriaçãodoDAOefaçaasalterações:
publicclassRemoveContatoLogicaimplementsLogica{publicStringexecuta(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{
//...
//(procureoContatoDaonocódigoexistente)//buscaaconexãopenduradanarequisiçãoConnectionconnection=(Connection)request.getAttribute("conexao");
//passeaconexãonoconstrutorContatoDaodao=newContatoDao(connection);
//...}}
OuvocêpodefazeressamesmamodificaçãonanossaantigaAdicionaContatoServlet.
1. Removaumcontatonasuaaplicaçãoeverifiquequetudocontinuafuncionandonormalmente.
10.6EXERCÍCIOS:FILTROS 145
CAPÍTULO11
"Umhomempintacomseucérebroenãocomsuasmãos."--Michelangelo
Nessecapítulo,vocêaprenderá:
Porqueutilizarframeworks;ComofuncionaoframeworkSpringMVC;AsdiferentesformasdesetrabalharcomoSpringMVC.
Quandoestamosdesenvolvendoaplicações,emqualquer linguagem,queremosnospreocuparcominfraestrutura omínimo possível. Isso não é diferente quando trabalhamos com uma aplicaçãoWeb.Imagine termos que lidar diretamente com o protocolo HTTP a todo momento que tivermos quedesenvolverumafuncionalidadequalquer.Nesseponto,oscontainerseaAPIdeServletsencapsulamoprotocoloparaquenãoprecisemoslidardiretamentecomele,masmesmoassimexistemuitotrabalhorepetitivoqueprecisamosfazerparaquepossamosdesenvolvernossalógica.
Um exemplo desse trabalho repetitivo que fazíamos era a conversão da data. Como o protocoloHTTP sempre interpreta tudo como texto, é preciso transformar essa data em um objeto do tipo Calendar . Mas sempre que precisamos de uma data temos essa mesma conversão usando a
SimpleDateFormat.
Outroexemploé,queparagravarmosumobjetodo tipoContato, precisamospegar naAPIde
Servlets parâmetropor parâmetroparamontar umobjetodo tipoContato invocandoos setters
adequados.
NãoseriamuitomaisfácilquenossalógicarecebessedealgumaformaumobjetodotipoContato
já devidamente populado com os dados que vieram na requisição?Nosso trabalho seria apenas, porexemploinvocaroContatoDaopassandooContatoparaseradicionado.
OgrandeproblemaéqueestamosatreladosaAPIdeServlets que ainda exigemuito trabalho
braçal para desenvolvermos nossa lógica. E, justamente para resolver esse problema, começaram asurgir os frameworksMVC, comoobjetivodediminuir o impactodaAPIdeServlets emnosso
trabalhoefazercomquepassemosanospreocuparexclusivamentecomalógicadenegócios,queéo
SPRINGMVC
11.1PORQUEPRECISAMOSDEFRAMEWORKSMVC?
146 11SPRINGMVC
códigoquepossuivalorparaaaplicação.
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
LogosepercebeuqueotrabalhocomServletseJSPspurosnãoeratãoprodutivoeorganizado.AprópriaSuncomeçouafomentarousodopadrãoMVCedepatternscomoFrontController.Eramuitocomum as empresas implementarem esses padrões e criarem soluções baseadas emmini-frameworkscaseiros.
Mas logosepercebeuqueo retrabalhoeramuitograndedeprojetoparaprojeto,deempresaparaempresa.UsarMVCerabeminteressante,masreimplementaropadrãotodoacadaprojetocomeçouaserinviável.
OStrutsfoiumdosprimeirosframeworksMVCcomaideiadesecriarumcontroladorreutilizávelentre projetos. Ele foi lançado no ano 2000 com o objetivo de tornar mais simples a criação deaplicaçõesWebcomalinguagemJavaaodisponibilizarumasériedefuncionalidadesjáprontas.
Isso fez com que muitas pessoas o utilizassem para desenvolver suas aplicações, tornando-orapidamenteaprincipalsoluçãoMVCnomercadoJava.Umadasconsequênciasdissoéquehojeemdiaeleéumdosmaisutilizadosnomercado.
Noentanto,hoje,eleévistocomoumframeworkquedemandamuitotrabalho,justamenteportersidocriadohámuitotempo,quandomuitasdasfacilidadesdalinguagemJavaaindanãoexistiam.
PorissosurgiramoutrosframeworksMVC.AcomunidadedoStruts,porexemplo,uniuforçascoma de outro framework que começava a ganhar espaço no mercado, que era oWebWork. Ambas ascomunidadessefundiramedesenvolveramoStruts2,quevemaserumaversãomais simplesdesetrabalhardoqueoStruts1,ecomaindamaisrecursosefuncionalidades.
EditoraCasadoCódigocomlivrosdeumaformadiferente
11.2UMPOUCODEHISTÓRIA
11.2UMPOUCODEHISTÓRIA 147
Um dos frameworks mais famosos no mercado é o Spring MVC. Spring é um framework queinicialmentenãofoicriadoparaodesenvolvimentoweb.NaessênciaoSpringéumcontainerlevequevisafornecerserviçosparasuaaplicaçãocomoporexemploogerenciamentodeobjetosoutransação.Mas como tempo a comunidade Spring entendeu que o Struts era ultrapassado e começou criar umframeworkMVC próprio. O SpringMVC é um framework moderno que usa os recursos atuais dalinguagem além de usar todo poder do container Spring. Nesse capítulo veremos as funcionalidadesdesseframeworkpoderoso.
STRUTS1
Embora bastante antigo, o Struts 1 ainda é usado em muitas empresas. Os conceitos efundamentossãomuitoparecidosentreasversões,masaversãoantigaémaistrabalhosaepossuiformasparticularesdeuso.
STRUTS2
Apesardonomefamoso,oStruts2nuncafoitãoutilizadoquantooStruts1.EnquantooStruts1 erapioneirona época e se tornouopadrãonodesenvolvimentoweb Java, oStruts 2 eraumaescolhaentreváriosframeworksMVCqueofereciamasmesmasfacilidades.
ParaquepossamosaprenderoSpringMVC,vamoscriarumsistemadelistadetarefas.EoprimeiropassoqueprecisamosdaréteroSpringMVCparaadicionarmosemnossaaplicação.SpringMVCvemjuntocomasbibliotecasdo frameworkSpringquepodemosencontrarno sitehttps://spring.io/.Lá, épossívelencontrardiversasdocumentaçõesetutoriais,alémdosJARsdoprojeto.
Uma vez que adicionamos os JARs do SpringMVC em nosso projeto dentro do diretórioWEB-
INF/lib, precisamos declarar umServlet, que fará o papel deFrontController da nossa aplicação,
recebendoasrequisiçõeseasenviandoàslógicascorretas.ParadeclararmosaServletdoSpringMVC,bastaadicionarmosnoweb.xmldanossaaplicação:
<servlet><servlet-name>SpringMVCDispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>
11.3CONFIGURANDOOSPRINGMVC
148 11.3CONFIGURANDOOSPRINGMVC
/WEB-INF/spring-context.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>
<servlet-mapping><servlet-name>SpringMVCDispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
ReparequeéumaconfiguraçãonormaldeServlet,comservlet-classeurl-pattern,comoas
quefizemosantes.Temapenasumelementonovo,oinit-param.Esteparâmetroéumaconfiguração
quepodeserpassadaparaoservletpeloweb.xml.Aquidefinimosonomedoarquivodeconfiguração
do frameworkSpring,ospring-context.xml.QuandooServlet é carregado, elevaiprocurar esse
spring-context.xmldentrodapastaWEB-INF.
OframeworkSpringpossuisuaprópriaconfiguraçãoXML.OSpring,porsermuitomaisdoqueumcontroladorMVC,poderiaserutilizadoemambientesnãoWeb,ousejanemsempreoSpringpodesebasearnoweb.xml.Porestemotivo,masnãosomenteeste,oSpringdefiniuoseupróprioXMLcom
váriasopçõesparaconfiguraraaplicação.
A primeira coisa que faremos nesse arquivo é habilitar o uso de anotações do Spring MVC econfigurarpacotebasedaaplicaçãowebparaoSpringacharasnossasclasses:
<mvc:annotation-driven/><context:component-scanbase-package="br.com.caelum.tarefas"/>
Alémdisso,éprecisoinformaraoSpringolocalondecolocaremososarquivosJSP.ParaissoSpringMVC oferece uma classe especial que recebe o nome da pasta dos JSPs e a extensão dos arquivos.VamoscriartodososJSPsnapasta/WEB-INF/views/:
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/views/"/><propertyname="suffix"value=".jsp"/></bean>
IssojáésuficienteparacomeçarcomoSpringMVC.Oarquivocompleto,comtodososcabeçalhos,ficaentãocomo:
<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd
XMLespecíficodoSpring
11.3CONFIGURANDOOSPRINGMVC 149
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scanbase-package="br.com.caelum.tarefas"/><mvc:annotation-driven/>
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><propertyname="prefix"value="/WEB-INF/views/"/><propertyname="suffix"value=".jsp"/></bean>
</beans>
No nosso framework MVC que desenvolvemos anteriormente, para criarmos nossas lógicas,criávamos uma classe que implementava uma interface. Existem diversas abordagens seguidas pelosframeworks para possibilitar a criação de lógicas. Passando por declaração em um arquivo XML,herdando de alguma classe do framework e, a partir do Java 5, surgiram as anotações, que são umaformadeintroduzirmetadadosdentrodenossasclasses.
OSpringMVCpermitecriaraslógicasdeformasdiferentes,usandoconvençõesoudeclarartudonoXML,porémnaversão3amaneiraindicadaéutilizandoanotações.
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
Paracriarmosnossaprimeiralógica,vamoscriarumaclassechamadaOlaMundoController.Nela
vamoscolocarmétodosquesãoasações(Action).OsufixoControllernãoéobrigatórioparao
Spring MVC porém é uma convenção do mercado. Como utilizaremos Spring MVC baseado emanotações,éobrigatórioqueoseuControllerestejadentrodopacotebr.com.caelum.tarefasouem um subpacote. No nosso caso, criaremos a classe dentro do pacote:
11.4CRIANDOASLÓGICAS
JáconheceoscursosonlineAlura?
11.5ALÓGICAOLÁMUNDO!
150 11.4CRIANDOASLÓGICAS
br.com.caelum.tarefas.controller.
Dentro dessa nossa nova classe, vamos criar um método que imprimirá algo no console e, emseguida,iráredirecionarparaumJSPcomamensagem"Olámundo!".Aclassedeveseranotadacom@Controller,umaanotaçãodoSpringMVC.ElaindicaaoSpringqueosmétodosdessaclassesão
ações(Action).Podemoscriarummétododequalquernomedentrodessaclasse,desdequeeleesteja
com a anotação @RequestMapping. A anotação @RequestMapping recebe um atributo chamado
valuequeindicaqualseráaURLutilizadaparainvocarométodo,comoesseatributojáéopadrão
não precisamos definir. Portanto, se colocarmos o valor olaMundoSpring acessaremos o método
dentrodonosso@ControllerpelaURLhttp://localhost:8080/fj21-tarefas/olaMundoSpring.
Vamoschamarométodoexecute,masnovamentepoderiaserqualquernome.Essemétododeve
retornarumaString que indicaqual JSPdeve ser executadoapós a lógica.Por exemplo,podemos
retornar"ok"paraenviarousuárioparaumapáginachamadaok.jsp.Ométodonaodeveretornaro
sufixo da página, já que isso foi configurado noXML do Spring. Também lembrando que o SpringMVCprocuraaspáginasJSPdentrodapastaWEB-INF/views.
Dessaforma,teríamosaseguinteclasse:
@ControllerpublicclassOlaMundoController{
@RequestMapping("/olaMundoSpring")publicStringexecute(){System.out.println("ExecutandoalógicacomSpringMVC");return"ok";}}
Um ponto importante a se notar é que podemos criar outrosmétodos que respondam por outrasURL's,ouseja,váriosaçõesdentrodessaclasse(dentrodomesmo@Controller).Bastaria quenós
utilizássemosnovamenteaanotação@RequestMappingessesmétodos.
Porfim,sóprecisamoscriaroJSPquemostraráamensagem"Olámundo!".Bastacriaroarquivook.jspdentrodapastaWEB-INF/views/,quemapeamosanteriormentenoXMLdoSpring.
OJSPteráoseguinteconteúdo:
<html><body><h2>OlámundocomSpringMVC!</h2></body></html>
PodemosacessarnossométodopelaURLhttp://localhost:8080/fj21-tarefas/olaMundoSpring.OqueaconteceéqueapósaexecuçãodométodooSpringMVCverificaqualfoioresultadoretornadopeloseumétodoeprocuradespachararequisiçãoparaapáginaindicada.
11.5ALÓGICAOLÁMUNDO! 151
Caso você esteja em casa acesse a pasta21/projeto-tarefas e copie todos os arquivos das pastasmysql,logging,springframeworkeservletecoledentrodapastadoWEB-INF/libdeum
novoDynamicWebProject.Todososjarsnecessáriosestãolistadosaseguir:
commons-logging-1.x.jarjakarta.servlet-api-4.0.x.jarlog4j-1.2-api-2.13.x.jarmysql-connector-java-8.0.x.jarslf4j-api-2.0.x-alpha0.jarslf4j-log4j12-2.0.x-alpha0.jarspring-aop-5.x.x.RELEASE.jarspring-aspects-5.x.x.RELEASE.jarspring-beans-5.x.x.RELEASE.jarspring-context-5.x.x.RELEASE.jarspring-core-5.x.x.RELEASE.jarspring-expression-5.x.x.RELEASE.jarspring-jdbc-5.x.x.RELEASE.jarspring-web-5.x.x.RELEASE.jarspring-webmvc-5.x.x.RELEASE.jar
VocêtambémpodeoptarporseguiroexercícioaseguirnoqualcriaremosumnovoprojetodotipoDynamicWebProject e importaremos todas as dependências listadas acima através de um arquivocompactado.
1. VamosconfiguraroSpringMVCemumnovoprojeto.
Crieumnovoprojetoweb:File->New->Project...->DynamicWebProjectchamadofj21-tarefas.
NaabaServers,cliquecomobotãodireitonoTomcateváemAddandRemove...:
Bastaselecionaronossoprojetofj21-tarefaseclicaremAdd:
Vamoscomeçarimportandoasclassesqueserãonecessáriasaonossoprojeto,comoomodelodeTarefaseoDAO.
Cliquecomobotãodireitonoprojetofj21-tarefaseescolhaaopçãoImport.
11.6PARASABERMAIS:CONFIGURANDOOSPRINGMVCEMCASA
11.7EXERCÍCIOS:CONFIGURANDOOSPRINGMVCETESTANDOACONFIGURAÇÃO
152 11.6PARASABERMAIS:CONFIGURANDOOSPRINGMVCEMCASA
SelecioneGeneral->ArchiveFile
Escolhaoarquivoprojeto-tarefas.zip que está em21/projeto-tarefas/ e confirme a
importação.
Abraoarquivoweb.xmlparafazermosadeclaraçãodoservletdoSpringMVC:
<servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring-context.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>
<servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
1. VamosfazerumsimplesOláMundo,paratestarmosnossaconfiguração:
2. Crie uma nova classe chamada OlaMundoController no pacotebr.com.caelum.tarefas.controller
3. Adicionenessaclasseoseguinteconteúdo:
@ControllerpublicclassOlaMundoController{
@RequestMapping("/olaMundoSpring")publicStringexecute(){System.out.println("ExecutandoalógicacomSpringMVC");return"ok";}}
Dica:UseCtrl+Shift+Oparaimportarasclasses.
4. Precisamos preparar a camada de visualização.Crie uma pastaviews para nossos JSPs que deveficardentrodapastaWebContent/WEB-INF.
5. Falta o JSP que será exibido após a execução da nossa lógica.Crie o JSPok.jsp no diretório
WebContent/WEB-INF/viewsdoprojetocomoconteúdo:
<html><body><h2>OlámundocomSpringMVC!</h2></body></html>
11.7EXERCÍCIOS:CONFIGURANDOOSPRINGMVCETESTANDOACONFIGURAÇÃO 153
6. Reinicie o Tomcat e acesse no seu navegador o endereço http://localhost:8080/fj21-
tarefas/olaMundoSpring.Oresultadodeveseralgoparecidocom:
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
Vamos começar a criar o nosso sistema de tarefas.Guardaremos uma descrição da tarefa, e umaindicação informando se a tarefa já foi finalizada ou não e quando foi finalizada. Esse sistema écompostopeloseguintemodelo:
publicclassTarefa{privateLongid;privateStringdescricao;privatebooleanfinalizado;privateCalendardataFinalizacao;
//gettersesetters
VocêpodetambémfazerocursodatadessaapostilanaCaelum
11.8ADICIONANDOTAREFASEPASSANDOPARÂMETROS
154 11.8ADICIONANDOTAREFASEPASSANDOPARÂMETROS
}
Vamoscriarafuncionalidadedeadiçãodenovastarefas.Paraisso,teremosumatelacontendoumformuláriocomcamposparaserempreenchidos.Queremosque,aocriarmosumanovatarefa,amesmavenhaporpadrãocomonãofinalizadae,consequentemente,semadatadefinalizaçãodefinida.Dessaforma, nosso formulário terá apenas o campo descricao . Podemos criar um JSP chamado
formulario.jspcontendosomenteocampoparadescrição:
<html><body><h3>Adicionartarefas</h3><formaction="adicionaTarefa"method="post">Descrição:<br/><textareaname="descricao"rows="5"cols="100"></textarea><br/>
<inputtype="submit"value="Adicionar"></form></body></html>
Onosso form, ao ser submetido, chamaumaação,ummétododentrodeum@Controller que
respondepelaURLadicionaTarefa.Essemétodoprecisareceberosdadosdarequisiçãoegravara
tarefaqueousuárioinformounatela.Vamoschamaressemétodoadicionaecolocardentrodaclasse
TarefasController:
@ControllerpublicclassTarefasController{
@RequestMapping("adicionaTarefa")publicStringadiciona(){JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa-adicionada";}}
Mas,comomontaremosoobjetotarefaparapassarmosaonossoDAO?Dentrodessanossaclasse
TarefasController em nenhum momento temos um HttpServletRequest para pegarmos os
parâmetrosenviadosnarequisiçãoemontarmosoobjetotarefa.
Uma das grandes vantagens de frameworksmodernos é que eles conseguempopular os objetosparanós.Bastaquedealgumaforma,nósfaçamosumaligaçãoentreocampoqueestánatelacomoobjetoquequeremospopular.EcomoSpringMVCnãoédiferente.
Essaligaçãoéfeitaatravésdacriaçãodeumparâmetrodométodoadiciona.Esseparâmetroéo
objeto que deverá ser populadopeloSpringMVCcomos dados que vieramda requisição. Portanto,vamoscriarnonossométodoumnovoparâmetrochamadotarefa:
@ControllerpublicclassTarefasController{
@RequestMapping("adicionaTarefa")
11.8ADICIONANDOTAREFASEPASSANDOPARÂMETROS 155
publicStringadiciona(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa-adicionada";}}
Queremosqueocampodetextoquecriamosnonossoformuláriopreenchaadescriçãodessatarefa.Parafazermosisso,bastadarmosonomecomocaminhodapropriedadequequeremosdefinir.Portanto,sedentrodoobjetotarefaqueremosdefinirapropriedadedescricao,bastanomearmosoinput
comdescricao.OSpringMVCcriaoobjetotarefaparanósepreencheesseobjetocomosdados
darequisição,nessecasocomoparâmetrodescricaodarequisição.ComoaclasseTarefa é um
JavaBeanepossuiconstrutorsemargumentosegetters/settersissonãoseráproblema.
Por fim, basta exibirmos a mensagem de confirmação de que a criação da tarefa foi feita comsucesso.Criamosoarquivotarefa-adicionada.jspcomoseguinteconteúdoHTML:
<html><body>Novatarefaadicionadacomsucesso!</body></html>
Anossaaplicaçãoteráváriaspáginasrelacionadascomumaoumaistarefas.Queremosorganizaranossa aplicaçãodesde início e separaros arquivos JSPs relacionadas em subpastas.Ou seja, todas aspáginasJSPrelacionadascomomodelotarefaficarãonapastatarefa.Porisso,vamoscriarumanova
pastatarefadentrodapastaWEB-INF/viewsparaosJSPsqueoTarefasControllervaiusar.Por
padrão,oSpringMVCnãoprocura emsubpastas, procura apenasnapastaviews.Vamosmudar o
retornodométodoadicionaedevolveronomedasubpastaeonomedapáginaJSP.Nessecasoo
retornoficacomotarefa/adicionada.
Portanto,ocódigocompletodométodoadicionafica:
@ControllerpublicclassTarefasController{
@RequestMapping("adicionaTarefa")publicStringadiciona(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";}}
Na pasta WEB-INF/views/tarefa também deve ficar o formulário para adicionar uma tarefa.
Como discutimos antes, todos os JSPs da tarefa na mesma subpasta. Porém aqui surge um novoproblema:ÉprecisocarregaroJSPnonavegador,masoacessodiretoaopastaWEB-INF é proibido
MelhoraraorganizaçãodosarquivosJSPs
156 11.8ADICIONANDOTAREFASEPASSANDOPARÂMETROS
peloservlet-containereconsequentementenãoépossívelacessaroformulário.Pararesolverissovamoscriarumanovaação(umnovométodo)dentrodaclasseTarefasControllerquetemafinalidadede
chamar o formulário apenas. O método usa também a anotação @RequestMappping e retorna um
Stringparachamaroformulário.
AbaixoocódigocompletodoTarefasControllercomosdoismétodos:
@ControllerpublicclassTarefasController{
@RequestMapping("novaTarefa")publicStringform(){return"tarefa/formulario";}
@RequestMapping("adicionaTarefa")publicStringadiciona(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";}}
AestruturadaspastaWEB-INFficacomo:
WEB-INF-views-tarefa-formulario.jsp-adicionada.jsp
ParachamaroformuláriousaremosaURL:http://localhost:8080/fj21-tarefas/novaTarefa
Vamoscriaroformulárioenossaaçãoparafazeragravaçãodastarefas.
1. Oprimeiropassoécriarnossoformulárioparaadicionarumatarefa.ParaissocrieumapastatarefadentrodapastaWebContent/WEB-INF/views.Dentrodapasta tarefa adicioneumnovo arquivoformulario.jsp.
<html><body><h3>Adicionartarefas</h3><formaction="adicionaTarefa"method="post">Descrição:<br/><textareaname="descricao"rows="5"cols="100"></textarea><br/><inputtype="submit"value="Adicionar"></form></body></html>
1. Agoraprecisamosummétodo(action)dentrodeum@Controller paraacessaro JSP.Crieuma
11.9EXERCÍCIOS:CRIANDOTAREFAS
11.9EXERCÍCIOS:CRIANDOTAREFAS 157
novaclassenopacotebr.com.caelum.tarefas.controllerchamadaTarefasController.
Nossaclasseprecisa terummétodoparaacessaro JSP.Vamoschamarométodo form()eusaraanotação@RequestMapping:
@ControllerpublicclassTarefasController{
@RequestMapping("novaTarefa")publicStringform(){return"tarefa/formulario";}
}
UseCtrl+Shift+Oparaimportarasclasses.
2. Aindafaltaométodoquerealmenteadicionaatarefanobancodedados.Essemétodoéchamadopelo nosso formulário e recebe uma tarefa como parâmetro. Ele novamente usa a anotação@RequestMappingparadefiniraURL.
DentrodaclasseTarefasControllercrieométodoadicionaquerecebeumatarefa.Nométodo
usamosaJdbcTarefaDaoparapersistirosdados.Oretornodométododefineolocalenomedo
JSP.
Ocódigodeveficar:
@RequestMapping("adicionaTarefa")publicStringadiciona(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";}
3. E, por fim, criamos o arquivo adicionada.jsp na pasta tarefa que mostrará uma mensagem deconfirmaçãodequeatarefafoiefetivamenteadicionada.
<html><body>Novatarefaadicionadacomsucesso!</body></html>
1. ReinicieoTomcat.
Acesse no seu navegador o endereço http://localhost:8080/fj21-tarefas/novaTarefa e
adicioneumanovatarefa.
158 11.9EXERCÍCIOS:CRIANDOTAREFAS
Casoaconteçaumaexceção informandoquea tabelanãoestácriada,crie-acomoscriptabaixoetenteinserirnovamenteatarefa.Abraoterminaledigite:
mysql-uroot
Lembrandoque,sehouversenhaparalogarcomobanco,éprecisoescrevermysql-uroot-pe,
emseguida,digitarasenhadobancodedados.
DentrodoMySQL,digite:
usefj21;
Emseguida,digite:
createtabletarefas(idBIGINTNOTNULLAUTO_INCREMENT,descricaoVARCHAR(255),finalizadoBOOLEAN,dataFinalizacaoDATE,primarykey(id));
(Dica: todo código usado para crição da estrutura do banco de dados encontra-se no arquivocriacao-tabelas.sqlnapasta21/projeto-tarefas/mysql)
11.9EXERCÍCIOS:CRIANDOTAREFAS 159
Já conseguimos adicionar novas tarefas emnossa aplicação. Porém, o que impede algumusuáriodesatento incluiruma tarefa semdescrição?Atéagora,nada.Nósqueremosqueumusuárionão sejacapaz de adicionar uma tarefa sem descrição, para isso precisamos incluir algum mecanismo devalidaçãoemnossaaçãodeadicionartarefas.Avalidaçãodeveserexecutadanoladodoservidor,afimdegarantirqueosdadosserãovalidadosemumlugarondeousuárionãoconsigainterferir.
A maneira mais fácil de validar a tarefa é usar vários ifs no método adiciona da classe
TarefasController antes de chamar dao.adiciona(tarefa) , executando a validação
programaticamente.Ocódigoseguintemostraaideia:
@RequestMapping("adicionaTarefa")publicStringadiciona(Tarefatarefa){
if(tarefa.getDescricao()==null||tarefa.getDescricao().equals("")){return"tarefa/formulario";}
JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";}
O problema aqui é quantomais atributos na tarefamais ifs teremos. É provável também que
vamosrepetirumifououtroquandovalidarmosatarefaemmétodosdiferentes,porexemplo,para
adicionaroualteraratarefa.Sabemosquecopiarecolarcódigonãoéumaboamaneiradereaproveitarcódigo.Oqueprecisamosédealgumartifícioquesejaigualparaqualquermétodo,algoqueajudenavalidaçãodosdados.
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
11.10INCLUINDOVALIDAÇÃONOCADASTRODETAREFAS
Validandoprogramaticamente
Seuslivrosdetecnologiaparecemdoséculopassado?
160 11.10INCLUINDOVALIDAÇÃONOCADASTRODETAREFAS
A partir do Java EE 6 temos uma especificação que resolve este problema.A JSR 303, tambémconhecidacomoBeanValidation,defineumasériedeanotaçõeseumaAPIparacriaçãodevalidaçõesparaseremutilizadasemJavaBeans,quepodemservalidadosagoraemqualquercamadadaaplicação.
Com o BeanValidation declaramos através de anotações as regras de validação dentro do nossomodelo,porexemplo,nanossatarefa:
publicclassTarefa{
privateLongid;
@Size(min=5)privateStringdescricao;
privatebooleanfinalizado;privateCalendardataFinalizacao;
//...}
Pronto!Comessas anotações, qualquer objeto do tipoTarefa pode ser validado na camada de
controller.SófaltaavisaroSpringMVCquerealmentequeremosexecutaravalidação.IssoéfeitopelaanotaçãoValidquedevemosusarnaantesdoparâmetrodaação:
@RequestMapping("adicionaTarefa")publicStringadiciona(@ValidTarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";}
ComoestamosfalandodeSpringMVC,antesdachamadadométodoéexecutadaavalidação,ousejaseráverificadoseadescriçãodatarefanãoestávazia.Seestiver,serálançadaumaexceçãodotipoConstraintViolationExceptionquepossuiadescriçãodoerro.
Não queremos mostrar uma exceção para o usuário e sim apenas voltar para o formulário paramostrarumamensagemqueavalidaçãofalhou.OSpringMVCpodeguardaroresultado(oserrosdevalidação)emumobjetodo tipoBindingResult.Assimnão será lançadoumexceção.Este objeto
BindingResultsetornaumparâmetrodaação.Entãosóéprecisoperguntarparaeleseexisteumerro
devalidaçãoeseexistir,voltarparaoformulário.Vejaocódigo:
@RequestMapping("adicionaTarefa")publicStringadiciona(@ValidTarefatarefa,BindingResultresult){
if(result.hasFieldErrors("descricao")){return"tarefa/formulario";}
JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";
11.11VALIDAÇÃOCOMBEANVALIDATION
11.11VALIDAÇÃOCOMBEANVALIDATION 161
}
Nocódigoacimaverificamosseexisteumdeerrovalidaçãorelacionadocomoatributodescricao
datarefa.Tambémpodemosconferirseexistealgumerrodevalidação,maisgenérico:
@RequestMapping("adicionaTarefa")publicStringadiciona(@ValidTarefatarefa,BindingResultresult){
if(result.hasErrors()){return"tarefa/formulario";}//...}
ParaexibirasmensagensdevalidaçãonoJSPusamosumtagespecialqueoSpringMVCoferece.Otagsechamaform:errors:
<form:errorspath="tarefa.descricao"/>
Oatributopathindicacomqueatributoessamensagemestárelacionada.
Abaixoestáocódigocompletodoformulárioformulario.jspdapastatarefa.Reparequeépreciso
importarotaglibdoSpringMVC:
<%@tagliburi="http://www.springframework.org/tags/form"prefix="form"%><html><body><h3>Adicionartarefas</h3><formaction="adicionaTarefa"method="post">Descrição:<br/><textarearows="5"cols="100"name="descricao"></textarea><br/><form:errorspath="tarefa.descricao"cssStyle="color:red"/><br/><inputtype="submit"value="Adicionar"/></form></body></html>
Para deixar as mensagens de nossa aplicação mais fáceis de alterar, é comum criar um arquivoseparado do HTML que possui apenas mensagens. Este arquivo é normalmente chamadomensagens.propertiesoumessages.properties.
NapastaWEB-INFdoprojetopodemosentãocriarestearquivocomoseguinteconteúdo:
tarefa.adicionada.com.sucesso=Tarefaadicionadacomsucesso!...
Reparequedefinimosasmensagensemumestilode:<chave>=<valor>.
Mostrandoasmensagensdevalidação
Mensagensinternacionalizadas
162 11.11VALIDAÇÃOCOMBEANVALIDATION
OSpringMVCpodecarregarautomaticamenteestearquivo,desdequealinhaabaixosejaincluídanoarquivospring-context.xml:
<beanid="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><propertyname="basename"value="/WEB-INF/mensagens"/></bean>
Basta então usar a taglibfmt paramostramensagens do arquivo mensagens.properties na
páginaHTML:
<fmt:messagekey="tarefa.adicionada.com.sucesso"/>
Podemos ainda personalizar as mensagens de validação do Bean Validation, escrevendo nossasmensagensdentrodoatributomessagedasanotações:
publicclassTarefa{
privateLongid;
@Size(min=5,message="Descriçãodeveterpelomenos5carateres")privateStringdescricao;//...}
Para não deixar as mensagens de validação espalhadas em nossas classes, podemos isolar estasmensagens no arquivo padrão de mensagens do Bean Validation, chamadoValidationMessages.properties:
tarefa.descricao.pequena=Descriçãodeveconterpelomenos{min}caracteres...
O arquivo deve ficar dentro da pasta src. Depois podemos referenciar as chaves das mensagensdentrodasanotaçõestambémpeloatributomessage:
publicclassTarefa{
privateLongid;
@Size(min=5,message="{tarefa.descricao.pequena}")privateStringdescricao;}
1. ParaconfiguraroframeworkBeanValidationéprecisocopiarcincojars.
Primeiro, acesse a pasta do arquivos do curso, mais específicamente 21/projeto-tarefas/validator.
Personalizandoasmensagensdeerros
11.12EXERCÍCIOS:VALIDANDOTAREFAS
11.12EXERCÍCIOS:VALIDANDOTAREFAS 163
HaverácincoJARs:
classmate-1.3.X.jarhibernate-validator-6.1.X.Final.jarjakarta.el-3.0.X.jarjakarta.validation-api-2.0.X.jarjboss-logging-3.3.X.Final.jar
Copie-os (CTRL+C) e cole-os (CTRL+V) dentro do workspace do eclipse na pasta fj21-tarefas/WebContent/WEB-INF/lib
2. Abra a classe Tarefa. Nela é preciso definir as regras de validação através das anotações do
frameworkBeanvalidation.Aatributodescricaodeveterpelomenos5caracteres:
publicclassTarefa{
privateLongid;
@Size(min=5)privateStringdescricao;
1. AbraaclasseTarefasControllereprocureométodoadiciona.Coloqueaanotação@Valid
nafrentedoparâmetroTarefatarefaeadicioneoparâmetroBindingResultnaassinaturado
método.
Alémdisso,nomesmométodo,adicioneaverificaçãoseháerrosdevalidação.Ométodocompletofica:
@RequestMapping("adicionaTarefa")publicStringadiciona(@ValidTarefatarefa,BindingResultresult){
if(result.hasFieldErrors("descricao")){return"tarefa/formulario";}
JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";}
1. AbraoJSPformulario.jsp(dapastaWEB-INF/views/tarefa).Adicionenoiníciodapáginaa
declaraçãodataglibdoSpringMVC:
<%@tagliburi="http://www.springframework.org/tags/form"prefix="form"%>
DentrodoHTMLadicioneapenastagform:errorsacimadotagform:
<form:errorspath="tarefa.descricao"/><formaction="adicionaTarefa"method="post">
1. Reinicie o Tomcat e acesse no seu navegador o endereço http://localhost:8080/fj21-
164 11.12EXERCÍCIOS:VALIDANDOTAREFAS
tarefas/novaTarefaEnvieumarequisiçãoSEMpreencheradescriçãodatarefa,amensagemde
validaçãodeveaparecer.
Comojáconseguimosadicionartarefasemnossaaplicação,precisamossaberoquefoiadicionado.Para isso precisamos criar uma nova funcionalidade que lista as tarefas. A função dessa ação seráinvocaroJdbcTarefaDao para conseguir a listadas tarefasque estãonobancodedados.Podemos
adicionarumnovométododentrodaclasseTarefasController:
@RequestMapping("listaTarefas")publicStringlista(){JdbcTarefaDaodao=newJdbcTarefaDao();List<Tarefa>tarefas=dao.lista();return"tarefa/lista";}
Essa lista de tarefas deverá ser disponibilizadapara o JSP fazer sua exibição.Para quepossamosdisponibilizarumobjetoparao JSP, temosquealteraro retornodométodolista.A ideia é queo
SpringMVCnãosórecebeonomedapáginaJSP(tarefa/lista)quandochamaométodolista,o
SpringMVCtambémrecebeosdadosparaoJSP.OsdadosparaaexibiçãonatelaeonomedapáginaJSP foram encapsulados pelo SpringMVC em uma classe especial que se chama ModelAndView.
VamoscriarumobjetodotipoModelAndView epreencheressemodelocomnossa listade tarefase
definir o nome da página JSP. O método lista deve retornar esse objeto, não mais apenas um
String.Vejacomoficaocódigo:
@RequestMapping("listaTarefas")
11.13LISTANDOASTAREFASEDISPONIBILIZANDOOBJETOSPARAAVIEW
11.13LISTANDOASTAREFASEDISPONIBILIZANDOOBJETOSPARAAVIEW 165
publicModelAndViewlista(){JdbcTarefaDaodao=newJdbcTarefaDao();List<Tarefa>tarefas=dao.lista();
ModelAndViewmv=newModelAndView("tarefa/lista");mv.addObject("tarefas",tarefas);returnmv;}
Dessaforma,serádisponibilizadoparaoJSPumobjetochamadotarefasquepodeseracessado
viaExpressionLanguagecomo${tarefas}.Poderíamosemseguidaiterarsobreessalistautilizandoa
TagforEachdaJSTLcore.
VendoocódigodaaçãolistapodeaparecerestranhoinstanciarumaclassedoframeworkSpring
(ModelAndView) dentro do nosso controlador. Isso até influencia negativamente a testabilidade do
método. Por isso, O Spring MVC dá uma outra opção, oferece uma alternativa ao uso da classeModelAndView.NanossaaçãopodemosreceberumobjetoquerepresentaomodeloparaonossoJSP.
SpringMVCpodepassar umparâmetropara ométododo controlador que tema funçãodomodelo.Essemodelo podemos preencher com a lista de tarefas.Assim também continuaremos devolver umaStringcomoretornodométodoqueindicaocaminhoparaoJSP:
@RequestMapping("listaTarefas")publicStringlista(Modelmodel){JdbcTarefaDaodao=newJdbcTarefaDao();List<Tarefa>tarefas=dao.lista();model.addAttribute("tarefas",tarefas);return"tarefa/lista";}
Dessamaneiranãoéprecisoinstanciaromodelo,esimapenasdisponibilizaralista.
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
1. Vamoscriaralistagemdasnossastarefasmostrandoseamesmajáfoifinalizadaounão.
Agoraéamelhorhoradeaprenderalgonovo
11.14EXERCÍCIOS:LISTANDOTAREFAS
166 11.14EXERCÍCIOS:LISTANDOTAREFAS
Na classe TarefasController adicione o método lista que recebe um Model como
parâmetro:
@RequestMapping("listaTarefas")publicStringlista(Modelmodel){JdbcTarefaDaodao=newJdbcTarefaDao();model.addAttribute("tarefas",dao.lista());return"tarefa/lista";}
Parafazeralistagem,vamosprecisardaJSTL(iremosfazerumforEach),portantoprecisamos
importá-la.Primeiro,entrenodiretóriodosarquivosdocursoedepoisem21/projeto-tarefas/jstl.
HaverãodoisJARs:
jakarta.servlet.jsp.jstl-1.2.x.jarjakarta.servlet.jsp.jstl-api-1.2.x.jar.
Copie-os (CTRL+C) e cole-os (CTRL+V) dentro do workspace do eclipse na pasta fj21-tarefas/WebContent/WEB-INF/lib
NoEclipse,dêumF5noseuprojeto.Pronto,aJSTLjáestáemnossoprojeto.
CrieoJSPquefaráaexibiçãodastarefasdentrodapastaWebContent/WEB-INF/views/tarefa.
Chame-odelista.jspeadicioneoseguinteconteúdo:
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%><%@tagliburi="http://java.sun.com/jsp/jstl/fmt"prefix="fmt"%><html><body>
<ahref="novaTarefa">Criarnovatarefa</a>
<br/><br/>
<table><tr><th>Id</th><th>Descrição</th><th>Finalizado?</th><th>Datadefinalização</th></tr><c:forEachitems="${tarefas}"var="tarefa"><tr><td>${tarefa.id}</td><td>${tarefa.descricao}</td><c:iftest="${tarefa.finalizadoeqfalse}"><td>Nãofinalizado</td></c:if><c:iftest="${tarefa.finalizadoeqtrue}"><td>Finalizado</td></c:if><td><fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/>
11.14EXERCÍCIOS:LISTANDOTAREFAS 167
</td></tr></c:forEach></table></body></html>
Reinicie o Tomcat e acesse o endereço http://localhost:8080/fj21-tarefas/listaTarefas e veja oresultado.
Tarefas podem ser adicionadas por engano, ou pode ser que não precisemosmais dela, portanto,queremos removê-las. Para fazer essa remoção, criaremos um link na listagem que acabamos dedesenvolverque,aoserclicado,invocaráanovaaçãoparaaremoçãodetarefaspassandoocódigodatarefaparaserremovida.
OlinkpodeserfeitocomHTMLnalistadetarefasdaseguinteforma:
<td><ahref="removeTarefa?id=${tarefa.id}">Remover</a></td>
Podemos desenvolver ummétodo para fazer a remoção.A lógica não possui nenhuma novidade,basta recuperarmosoparâmetro comoaprendemosnesse capítulo e invocarmosoDAO para fazer a
remoção:
@RequestMapping("removeTarefa")publicStringremove(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.remove(tarefa);return"paraondeir???";}
Aquestãoé:Paraquallugarredirecionarousuárioapósaexclusão?
PoderíamoscriarumnovoJSPcomumamensagemdeconfirmaçãoda remoção,masusualmenteisso não costuma ser bom, porque precisaríamos navegar até a lista das tarefas novamente casotenhamos que remover outra tarefa. Seria muito mais agradável para o usuário que ele fosseredirecionadodiretoparaalistadastarefas.
11.15REDIRECIONANDOAREQUISIÇÃOPARAOUTRAAÇÃO
168 11.15REDIRECIONANDOAREQUISIÇÃOPARAOUTRAAÇÃO
Umadasformasquepoderíamosfazeresseredirecionamentoéenviarousuáriodiretamenteparaapágina que lista as tarefas (tarefa/lista.jsp). Mas, essa não é uma boa abordagem, porque
precisaríamos,outravez,disponibilizara listadas tarefasparaoJSP,algoque já fazemosnaaçãodelistarastarefas,ométodolistanaclasseTarefasController.
Jáqueométodolistafazessetrabalho,poderíamos,aoinvésderedirecionaraexecuçãoparao
JSP, enviá-la para essa ação. Para isso, o retorno dométodo deve ser um poucomodificado.VamoscontinuardevolvendoumaStringmasessaStringdeve indicarquequeremoschamarumaoutra
ação.Podemosfazerumredirecionamentonaladodoservidor(forward)oupelonavegador,noladodocliente(redirect).
Parafazerumredirecionamentonoladodoservidorbastausaroprefixoforwardnoretorno:
@RequestMapping("removeTarefa")publicStringremove(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.remove(tarefa);return"forward:listaTarefas";}
Parafazerumredirecionamentonoladodoclienteusamosoprefixoredirect:
@RequestMapping("removeTarefa")publicStringremove(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.remove(tarefa);return"redirect:listaTarefas";}
1. Vamosfazerafuncionalidadederemoçãodetarefas.
Adicionenoarquivolista.jspumacolunacomumlinkqueaoserclicadoinvocaráaAction
pararemovertarefa.
<td><ahref="removeTarefa?id=${tarefa.id}">Remover</a></td>
CrieumnovométodoremovenaclasseTarefasControllercomocódigo:
@RequestMapping("removeTarefa")publicStringremove(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.remove(tarefa);return"redirect:listaTarefas";}
Acesse a lista de tarefas em http://localhost:8080/fj21-tarefas/listaTarefas e remova algumastarefas.
1. Criaremos a tela para fazer a alteração das tarefas, como por exemplo, marcá-la como
11.16EXERCÍCIOS:REMOVENDOEALTERANDOTAREFAS
11.16EXERCÍCIOS:REMOVENDOEALTERANDOTAREFAS 169
finalizadaedefinirmosadatadefinalização.
Primeirovamoscriarumnovolinknanossalistagemqueenviaráousuárioparaatelacontendoosdadosdatarefaselecionada:
<td><ahref="mostraTarefa?id=${tarefa.id}">Alterar</a></td>
Vamoscriarumanovaaçãoquedadoumid,devolveráaTarefacorrespondenteparaumJSP,
quemostraráosdadosparaqueaalteraçãopossaserfeita.
CrieumnovométodomostranaclasseTarefasController:
@RequestMapping("mostraTarefa")publicStringmostra(Longid,Modelmodel){JdbcTarefaDaodao=newJdbcTarefaDao();model.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/mostra";}
CrieoJSPmostra.jspdentrodapastaviews/tarefaparamostraratarefaescolhida:
<%@taglibprefix="fmt"uri="http://java.sun.com/jsp/jstl/fmt"%>
<html><body><h3>Alterartarefa-${tarefa.id}</h3><formaction="alteraTarefa"method="post">
<inputtype="hidden"name="id"value="${tarefa.id}"/>
Descrição:<br/><textareaname="descricao"cols="100"rows="5">${tarefa.descricao}</textarea><br/>
Finalizado?<inputtype="checkbox"name="finalizado"value="true"${tarefa.finalizado?'checked':''}/><br/>
Datadefinalização:<br/><inputtype="text"name="dataFinalizacao"value="<fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/>"/><br/>
<inputtype="submit"value="Alterar"/>
</form></body></html>
Para o Spring MVC saber converter automaticamente a data no formato brasileiro para umCalendar é precisousar a anotaçã[email protected] a classeTarefa e adicione a
anotaçãoacimadoatributodataFinalizacao:
@DateTimeFormat(pattern="dd/MM/yyyy")
170 11.16EXERCÍCIOS:REMOVENDOEALTERANDOTAREFAS
privateCalendardataFinalizacao;
Faltacriarummétodoquecuidarádaalteraçãodatarefa.NaclasseTarefasControlleradicione
essemétodo:
@RequestMapping("alteraTarefa")publicStringaltera(Tarefatarefa){JdbcTarefaDaodao=newJdbcTarefaDao();dao.altera(tarefa);return"redirect:listaTarefas";}
Acessealistadetarefasemhttp://localhost:8080/fj21-tarefas/listaTarefasealterealgumastarefas.
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
1. AdicioneocampocomcalendárioquefizemosnocapítulodecriaçãodeTagsemnossoprojetoeutilize-onoformuláriodealteração.
Sempre que precisamos finalizar uma tarefa, precisamos entrar na tela de alteração da tarefa quequeremoseescolheradatadefinalização.Essadatadefinalizaçãonamaioriadasvezeséaprópriadataatual.
Para facilitar a usabilidade para o usuário, vamos adicionar umnovo link na nossa tabela que sechamará"Finalizaragora".Aoclicarnesselink,umaaçãoseráinvocadaparafinalizarmosatarefanodiaatual.
A questão é que não queremos navegar para lugar algum ao clicarmos nesse link. Queremospermanecernamesmatela,semquenadaaconteça,nemsejarecarregado.
EditoraCasadoCódigocomlivrosdeumaformadiferente
11.17DESAFIO-CALENDÁRIO
11.18MELHORANDOAUSABILIDADEDANOSSAAPLICAÇÃO
11.17DESAFIO-CALENDÁRIO 171
Ouseja,dealgumaformaprecisamosmandararequisiçãoparaaação,masaindaassimprecisamosmanter apáginado jeitoqueela estavaaoclicarno link.Podemos fazer isso atravésdeuma técnicachamadaAJAX,quesignificaAsynchronousJavascriptandXML.
AJAX nadamais é do que uma técnica que nos permite enviar requisições assíncronas, ou seja,manter a página que estava aberta intacta, e recuperar a resposta dessa requisição para fazermosqualquer processamento com eles. Essas respostas costumam ser XML, HTML ou um formato detransferênciadedadoschamadoJSON(JavascriptObjectNotation).
PararealizarmosumarequisiçãoAJAX,precisamosutilizarJavascript.EnocursovamosutilizarosuportequeojQuerynosforneceparatrabalharcomAJAX.
ParafazermosumarequisiçãoparaumdeterminadoendereçocomojQuery,bastadefinirmosqualmétodoutilizaremosparaenviaressarequisição(POSTouGET).OjQuerynosforneceduasfunções:$.poste$.get,cadaumaparacadamétodo.
Paraasfunçõesbastapassarmosoendereçoquequeremosinvocar,comoporexemplo:
$.get("minhaPagina.jsp")
Nessecaso,estamosenviandoumarequisiçãoviaGETparaoendereçominhaPagina.jsp.
Sabendoisso,vamoscriarumlinkqueinvocaráumafunçãoJavascriptefarárequisiçãoAJAXparaumaaçãoquefinalizaráatarefa:
<td><ahref="#"onclick="finalizaAgora(${tarefa.id})">Finalizaragora</a></td>
Vamos criar a funçãofinalizaAgora que recebe o id da tarefa que será finalizada e a passará
comoparâmetroparaaação:
<scripttype="text/javascript">functionfinalizaAgora(id){$.get("finalizaTarefa?id="+id);}</script>
Por fim, basta criarmos a nossa ação que receberá o parâmetro e invocará um método doJdbcTarefaDaoparafazerafinalizaçãodatarefa.Noentanto,arequisiçãoqueestamosfazendonão
gerarárespostanenhumaenóssempreretornamosumaStringoresultadoquedeterminaqualJSPseráexibido.Dessavez,nãoexibiremosnemumJSPeneminvocaremosoutraAction.OprotocoloHTTP
sempre retorna um código indicando qual é o estado dessa resposta, ou seja, se foi executado comsucesso,seapáginanãofoiencontrada,sealgumerroaconteceueassimpordiante.
OprotocoloHTTPdefinequeocódigo200indicaqueaexecuçãoocorreucomsucesso,portanto,vamosapenasindicarnanossarespostaocódigo,semdevolvernadanocorpodanossaresposta.Para
172 11.18MELHORANDOAUSABILIDADEDANOSSAAPLICAÇÃO
setarocódigodarespostaprogramaticamenteprecisamosdoobjetoHttpServletResponse.PodemosreceberarespostaHTTPcomoparâmetrodequalquermétodoqueéumaação.ComarespostanamãopodemoschamarométodosetStatus(200).
Dessaforma,poderíamosterummétodonaclasseTarefasControllerparafazerafinalizaçãoda
tarefacomoseguintecódigo:
@RequestMapping("finalizaTarefa")publicvoidfinaliza(Longid,HttpServletResponseresponse){JdbcTarefaDaodao=newJdbcTarefaDao();dao.finaliza(id);response.setStatus(200);}
Oprimeiroparâmetroéa idda tarefaquevematravésdarequisição,osegundoéa respostaparasetarocódigoHTTP.
Quando estamos usando um Framework MVC como o Spring, queremos que toda a parte demanipulaçãoderequesteresponsefiqueporcontadoframework.Daformaquemostramosantesé
possível obter o resultado que esperamos,masmanipular o status da resposta é uma tarefa que nãoqueremos fazer diretamente. O Spring nos ajuda nessa tarefa, fazendo com que o retorno do nossométodosejaHTTP200,excetoemcasodealgumaexception.Paraisso,[email protected]óprionomeda annotation fala, tudoque for retornado, será o
corpodanossaresposta.Ousenadaforretornado,entãoarespostaserávaziaporémostatusHTTPserá200.Nossocódigopodeficarmaisenxutoesemamanipulaçãodoresponse:
@ResponseBody@RequestMapping("finalizaTarefa")publicvoidfinaliza(Longid){JdbcTarefaDaodao=newJdbcTarefaDao();dao.finaliza(id);}
Através do jQuery, podemos enviar uma requisição AJAX para o servidor, e na ação vamosmanipulararespostaesetarapenasostatus200.Tambémseriaútilnotificarousuáriodaaplicaçãoquearequisiçãofinalizoucomsucesso.OjQueryjáforneceumjeitomuitosimplesdeimplementarisso.ÉprecisoadicionarnoJavaScriptumafunçãoqueéchamadaquandoa requisição terminacomsucesso(status200).
<scripttype="text/javascript">functionfinalizaAgora(id){$.get("finalizaTarefa?id="+id,function(dadosDeResposta){alert("TarefaFinalizada!");});}</script>
11.19 UTILIZANDO AJAX PARA MARCAR TAREFAS COMOFINALIZADAS
11.19UTILIZANDOAJAXPARAMARCARTAREFASCOMOFINALIZADAS 173
Repare que $.get recebe mais uma função como parâmetro (também chamado callback de
sucesso).Nela,definimosapenasumsimplesalertparamostrarumamensagemaousuário.TambémépossívelmanipularoHTMLdapáginadinamicamente.OjQueryoferecerecursospoderososparaalterarqualquerelementoHTMLdentrodonavegador.
Por exemplo, podemos selecionar um elemento da página pela id e mudar o conteúdo desse
elemento:
$("#idDoElementoHTML").html("NovoconteúdoHTMLdesseelemento");
Para o nosso exemplo, então é interessante atualizar a coluna da tarefa para indicar que ela foifinalizada:
$("#tarefa_"+id).html("Tarefafinalizada");
LeiamaissobreojQuerynadocumentação:
http://api.jquery.com/jQuery.get/http://api.jquery.com/id-selector/
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
OcontroladordoSpringMVC,ousejaoservletnoweb.xml,foiconfiguradopararecebertodasasrequisições incluindo essas que foram enviadas para receber o conteúdo de arquivos comuns comoimagens, css ou scripts. Queremos que o controlador não atenda essas requisições que não são paraações.Paraissoéprecisoadicionarnoarquivospring-context.xmlummapeamentoqueinformaao
SpringMVCqueeledeveignorartodoacessoaconteúdoestático.
<mvc:default-servlet-handler/>
JáconheceoscursosonlineAlura?
11.20 CONFIGURAR O SPRING MVC PARA ACESSAR ARQUIVOSCOMUNS
174 11.20CONFIGURAROSPRINGMVCPARAACESSARARQUIVOSCOMUNS
1. Abraoarquivospring-context.xmleacrescente:
<mvc:default-servlet-handler/>
Na pasta WebContent crie uma nova pasta resources, vamos colocar nela tudo relativo a
conteúdoestáticodonossosistema.
1. Vamos adicionar AJAX na nossa aplicação. Para isso, utilizaremos o jQuery que precisamosimportarparanossoprojetoeemnossaspáginas.
Váatéapastadearquivosdocurso,em21/projeto-agenda/;
Copieodiretóriojsecole-odentrodeWebContent/resources no seuprojeto fj21-tarefas;
Casovocêestejaemcasa,façaodownloademhttps://jquery.com/download/
PrecisamosimportarojQueryemnossapáginadelistagem.Paraisso,adicionelogoapósaTag<html>oseguintecódigonoarquivolista.jsp:
<head><scripttype="text/javascript"src="resources/js/jquery.js"></script></head>
Pronto,importamosojQueryparanossaaplicação.
2. Casoatarefanãoestejafinalizada,queremosqueelapossuaumnovolinkquesechamará"Finalizaragora".Aoclicarnele,seráchamadaviaAJAXumaActionquemarcaráatarefacomofinalizada
eadatadehojeserámarcadacomoadatadefinalizaçãodamesma.
Altereacolunaquemostraatarefacomonão finalizadanoarquivolista.jsp.Adicioneumlinkqueaoserclicada,chamaráumafunçãoJavascriptpassandooidda tarefaparafinalizar.
Tambémadicioneumaidparacadaelemento<td>.
Noarquivoprocureoc:ifparatarefasnãofinalizadas,altereoelementotddentroc:if:
<c:iftest="${tarefa.finalizadoeqfalse}"><tdid="tarefa_${tarefa.id}"><ahref="#"onClick="finalizaAgora(${tarefa.id})">Finalizaagora!</a></td></c:if>
Criea funçãoJavascriptfinalizaAgora para chamar a açãoque criaremos a seguir via uma
requisiçãoPOST:
<!--ComeçodapáginacomoimportdoJavascript--><body><scripttype="text/javascript">functionfinalizaAgora(id){
11.21EXERCÍCIOS:AJAX
11.21EXERCÍCIOS:AJAX 175
$.post("finalizaTarefa",{'id':id},function(){//selecionandooelementohtmlatravésda//IDealterandooHTMLdele$("#tarefa_"+id).html("Finalizado");});}</script>
<ahref="novaTarefa">Criarnovatarefa</a><br/><br/>
<table><!--Restodapáginacomatabela-->
3. Vamos criar o método para finalizar a tarefa. Após o mesmo ser executado, ele não deverá nosredirecionarparalugarnenhum,apenasindicarqueaexecuçãoocorreucomsucesso.
AbraaclasseTarefasControllerAdicioneométodofinalizacomoconteúdo:
@ResponseBody@RequestMapping("finalizaTarefa")publicvoidfinaliza(Longid){JdbcTarefaDaodao=newJdbcTarefaDao();dao.finaliza(id);}
Acesse a listagem http://localhost:8080/fj21-tarefas/listaTarefas e clique no novo link parafinalizartarefa.Atelamudasemprecisarumaatualizaçãointeiradapágina.
4. (Opcional,Avançado)Nomesmoestilodoexercícioanterior,useo jQueryparaacionarométodoremoveTarefaquandoclicadoemumbotãode"excluir".Paraisso,crieumanovacolunanatabela
com um link que o onClick vai chamar o endereço associado a removeTarefa, e via AJAX
devemosremoveralinhadatabela.PraissovocêpodeusarumrecursopoderosodojQueryepedirquesejaescondidaalinhadeondeveiooclique:
$(elementoHtml).closest("tr").hide();
Dessamaneiravocênemprecisariausaridsnastrs.
176 11.21EXERCÍCIOS:AJAX
AgoraaofinalizarnossatarefaviaAJAXousuáriotemumfeedbacknaalteraçãodoHTMLdeNãoFinalizadoparaFinalizado.Porémtodasastarefasfinalizadaspossuemacolunadedatadefinalizaçãopreenchidas,menosasqueacabamosdefinalizar.
Pararesolveresseproblema,dealgumaformaonossoControllerdeveriapassarumadataparanossojQuery.Mascomo?Umasoluçãopossívelseriaescrevê-ladiretonoresponse.Algoparecidocomisso:
@RequestMapping("finalizaTarefa")publicvoidfinaliza(Longid,HttpServletResponseresponse)throwsIOException{JdbcTarefaDaodao=newJdbcTarefaDao();dao.finaliza(id);DatedataDeFinalizacao=dao.buscaPorId(id).getDataFinalizacao().getTime();Stringdata=newSimpleDateFormat("dd/MM/yyyy").format(dataDeFinalizacao);response.getWriter().write(data);response.setStatus(200);}
ResolvenossoproblemamasaindaassimteríamosqueficartrabalhandodiretoemumcamadamaisbaixaquesãoasclassesdeHTTPServletRequesteHTTPServletResponse.Agoraalémdefinalizar
uma tarefa nossa action tem as responsabilidades de buscar pela data finalizada, formatar a data,escrevernoresponseemudarostatuspara200.Responsabilidadedemais,nãoémesmo?
RetornarumaJSPjánostrazobenefíciodostatus200,necessárioparanossafunçãodecallback
no jQuery. Sabendo disso usaremos uma JSP para renderizar a data formatada.Mas antes é precisopassaressadataanossaJSP,ousimplesmentepassarumaTarefaparaelaedepoisfazercomquea
ActionretorneaStringreferenteaJSP.
@RequestMapping("finalizaTarefa")publicStringfinaliza(Longid,Modelmodel){JdbcTarefaDaodao=newJdbcTarefaDao();dao.finaliza(id);model.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/dataFinalizada";}
AgorasófaltarealmentecriarmosoarquivodataFinalizadanapasta/WEB-INF/views/tarefa
eformataradatausandoatag.
<%@tagliburi="http://java.sun.com/jsp/jstl/fmt"prefix="fmt"%>
<fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/>
Legal, já renderizamos uma data formatada e por consequência o status 200 é enviado para o
jQuery.SóqueatéagoranãofizemosnadacomessadataqueoJSPrenderizou.Paraquesepeguealgoenviado pelo nosso servidor, a função de callback deve receber um parâmetro com esse conteúdo.Vamosexecutarumalertcomestavariável.
<scripttype="text/javascript">
11.22PARASABERMAIS:ALTERANDOVALORDADATACOMAJAX
11.22PARASABERMAIS:ALTERANDOVALORDADATACOMAJAX 177
functionfinalizaAgora(id){$.post("finalizaTarefa",{'id':id},function(resposta){$("#tarefa_"+id).html("Finalizado");alert(resposta);});}</script>
Aexecuçãodessealertnosmostraadata,faltaapenasinseri-laemlugarapropriado.Vamosusar
amesmaestratégiaqueusamosparamudardeNãoFinalizadoparaFinalizado,atrelandoumidanossa<td>referenteaocampodededata.
<tdid="tarefa_data_${tarefa.id}"><fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/></td>
Fazendoissobastatrocarmosoconteúdodo<td>nafunção.
<scripttype="text/javascript">functionfinalizaAgora(id){$.post("finalizaTarefa",{'id':id},function(resposta){$("#tarefa_"+id).html("Finalizado");$("#tarefa_data_"+id).html(resposta);});}</script>
Onosso desafio foi cumprido de forma elegante,mas ainda assim poderíamosmelhorar o nossocódigo.AfunçãodecallbackdoAJAXtemquemodificardois<td>'se issotornariarepetitivopara
modeloscommaisalteraçõesqueanossaTarefa.Sabendoqueafunçãorecebeapenasumparâmetro
deresposta,teríamosmaisproblemasaoterquepassarmaisdeumparâmetroaela.Comoresolverestaquestão?Umasoluçãoviávelépassaraprópria<tr>, completa, comas alteraçãonecessárias.Para
issoumaalteraçãonaJSPsefaznecessária.
<%@tagliburi="http://java.sun.com/jsp/jstl/fmt"prefix="fmt"%>...<td>${tarefa.id}</td><td>${tarefa.descricao}</td><td>Finalizada</td><td><fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/></td><td><ahref="removeTarefa?id=${tarefa.id}">Remover</a></td><td><ahref="mostraTarefa?id=${tarefa.id}">Alterar</a></td>
UmaalteraçãononomedoarquivodataFinalizada.jspseriamaisdoquerecomendada,jáque
agoranãopossui apenasumadata e sim todas as alteraçõesdeuma<tr>.Vamos renomeá-lopara
finalizada.jsp . Sem esquecer de modificar o retorno da action de tarefa/dataFinalizada para
tarefa/finalizada:
@RequestMapping("finalizaTarefa")publicStringfinaliza(Longid,Modelmodel){...return"tarefa/finalizada";
178 11.22PARASABERMAIS:ALTERANDOVALORDADATACOMAJAX
}
Agoraqueretornamosuma<tr>comtodasasalteraçõesvamosmodificarafunçãodecallback,
para que ela apenasmodifique o conteúdo da <tr> correspondente a tarefa finalizada. Para isso é
precisodiferenciarum<tr>dooutro.Vamosutilizaragoraumidnaprópria<tr>eremoveremosos
idsdesnecessáriosdos<td>'s.
<table>...<c:forEachitems="${tarefas}"var="tarefa"><trid="tarefa_${tarefa.id}"><td>${tarefa.id}</td><td>${tarefa.descricao}</td>
<c:iftest="${tarefa.finalizadoeqtrue}"><td>Finalizado</td></c:if>
<c:iftest="${tarefa.finalizadoeqfalse}"><td><ahref="#"onClick="finalizaAgora(${tarefa.id})">Finalizar</a></td></c:if>
<td><fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/></td>
<td><ahref="removeTarefa?id=${tarefa.id}">Remover</a></td><td><ahref="mostraTarefa?id=${tarefa.id}">Alterar</a></td>
</tr>
</c:forEach>....
</table>
Eporúltimo,faltamudarafunçãodecallbackparaqueestamodifiqueoconteúdodo<tr>.
...$.post("finalizaTarefa",{'id':id},function(resposta){$("#tarefa_"+id).html(resposta);});...
Agorasim,temosumcódigosimplesefácildemanter.Tudooqueumbomprogramadorgostariadeencontrar.
11.22PARASABERMAIS:ALTERANDOVALORDADATACOMAJAX 179
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
1. Vamos buscar uma tarefa e passá-la para nossa JSP através do Model. Abra oTarefasController.javaemodifiqueaactionquefinalizaumatarefaparaoquesegue:
@RequestMapping("finalizaTarefa")publicStringfinaliza(Longid,Modelmodel){JdbcTarefaDaodao=newJdbcTarefaDao();dao.finaliza(id);model.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/finalizada";}
1. Agorafaltacriarmosoarquivofinalizada.jspdentrododiretório:/WEB-INF/views/tarefa/.
Quedeveráteroseguinteconteúdodarelacionadaatarefafinalizada.
<%@tagliburi="http://java.sun.com/jsp/jstl/fmt"prefix="fmt"%>...<td>${tarefa.id}</td><td>${tarefa.descricao}</td><td>Finalizada</td><td><fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/></td><td><ahref="removeTarefa?id=${tarefa.id}">Remover</a></td><td><ahref="mostraTarefa?id=${tarefa.id}">Alterar</a></td>
1. Porúltimodevemosmodificaroarquivotarefa/lista.jspparaqueele tenhaumidentificador
decadalinha,ouseja,elementodatabela.Demaneiraanálogaaoquefoifeitonoexercícioanteriorvamosconcatenarcomoidda tarefaumvalordetarefa_.Lembre-sede removeros idsdos
outros<td>'sjáqueelesnãoserãonecessáriosepodemestarcomomesmonomedoidentificadorda<tr>.
....<c:forEachitems="${tarefas}"var="tarefa"><trid="tarefa_${tarefa.id}"><td>${tarefa.id}</td><td>${tarefa.descricao}</td>
VocêpodetambémfazerocursodatadessaapostilanaCaelum
11.23EXERCÍCIOSOPCIONAIS:MELHORANDONOSSOAJAX
180 11.23EXERCÍCIOSOPCIONAIS:MELHORANDONOSSOAJAX
<c:iftest="${tarefa.finalizadoeqtrue}"><td>Finalizado</td></c:if>
<c:iftest="${tarefa.finalizadoeqfalse}"><td><ahref="#"onClick="finalizaAgora(${tarefa.id})">Finalizar</a></td></c:if>
<td><fmt:formatDatevalue="${tarefa.dataFinalizacao.time}"pattern="dd/MM/yyyy"/></td>....</tr>.....
1. EagoraparafazerusodoconteúdorenderizadopelaJSP,énecessárioqueafunçãodecallbackdoAJAX receba como parâmetro esse conteúdo. Vamos alterar a função do finalizaAgora no
mesmoarquivolista.jsp,paraoquesegue:
<scripttype="text/javascript">functionfinalizaAgora(id){$.post("finalizaTarefa",{'id':id},function(resposta){$("#tarefa_"+id).html(resposta);});}</script>
1. Reinicieoservidoreverifiquequeagoraaoclicarno linkFinalizar o usuário tema alteração
tantodeNãoFinalizadaparaFinalizadaquandoumamudançanadatadefinalização.
11.23EXERCÍCIOSOPCIONAIS:MELHORANDONOSSOAJAX 181
CAPÍTULO12
"Experiênciaéapenasonomequedamosaosnossoserros."--OscarWilde
Nessecapítulo,vocêaprenderá:
Oescopodesessãoecomoelefunciona;Oquesãointerceptadores;Implantarsuaaplicaçãoemqualquercontainer.
Écomumhojeemdiaacessarmosalgumsitequepeçaparafazermosnossologinparapodermosteracesso a funcionalidades da aplicação. Esse processo também é conhecido como autenticação.Mas,comoositesabe,nasrequisiçõesseguintesquefazemos,quemsomosnós?
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
OprotocoloHTTPutilizadoparaoacessoàpáginasélimitadopornãomanterdetalhescomoqueméquementreumaconexãoeoutra.Pararesolverisso,foiinventadoumsistemaparafacilitaravidados
SPRINGMVC:AUTENTICAÇÃOEAUTORIZAÇÃO
12.1AUTENTICANDOUSUÁRIOS:COMOFUNCIONA?
Seuslivrosdetecnologiaparecemdoséculopassado?
12.2COOKIES
182 12SPRINGMVC:AUTENTICAÇÃOEAUTORIZAÇÃO
programadores.
Umcookieénormalmenteumpardestringsguardadonocliente,assimcomoummapadestrings.Essepardestringspossuidiversaslimitaçõesquevariamdeacordocomoclienteutilizado,oquetornaa técnicadeutilizá-los algodoqualnão sedevaconfiarmuito. Jáqueas informaçõesdocookie sãoarmazenadas no cliente, o mesmo pode alterá-la de alguma maneira... sendo inviável, por exemplo,guardaronomedousuáriologado...
Quando um cookie é salvo no cliente, ele é enviado de volta ao servidor toda vez que o clienteefetuarumanovarequisição.Destaforma,oservidorconsegueidentificaraqueleclientesemprecomosdadosqueocookieenviar.
Umexemplodebomusodecookiesénatarefadelembraronomedeusuárionapróximavezqueelequiserselogar,paraquenãotenhaqueredigitaromesmo.
Cadacookiesóéarmazenadoparaumwebsite.Cadawebsitepossuiseuspróprioscookieseestesnãosãovistosemoutrapágina.
UsarCookies parece facilitarmuito a vida,mas através de um cookie não é possívelmarcar umclientecomumobjeto,somentecomStrings.Imaginegravarosdadosdousuáriologadoatravésde
cookies.Serianecessárioumcookieparacadaatributo:usuario,senha,id,datadeinscrição,
etc.Semcontarafaltadesegurança.
OCookietambémpodeestardesabilitadonocliente,sendoquenãoserápossívellembrarnadaqueousuáriofez...
Umasessãofacilitaavidadetodosporpermitiratrelarobjetosdequalquer tipoaumcliente,nãosendolimitadasomenteàstringseéindependentedecliente.
A abstração daAPI facilita o trabalho do programador pois ele não precisa saber como é que asessãofoiimplementadanoservletcontainer,elesimplesmentesabequeafuncionalidadeexisteeestáláparaouso.Seoscookiesestiveremdesabilitados,asessãonãofuncionaráedevemosrecorrerparaumatécnica(trabalhosa)chamadaurl-rewriting.
A sessão nadamais é que um tempo que o usuário permanece ativo no sistema. A cada páginavisitada, o tempo de sessão é zerado. Quando o tempo ultrapassa um limite demarcado no arquivoweb.xml,oclienteperdesuasessão.
Paraconfigurar3minutoscomoopadrãode tempoparaousuárioperderasessãobasta incluiro
12.3SESSÃO
12.4CONFIGURANDOOTEMPOLIMITE
12.3SESSÃO 183
seguintecódigonoarquivoweb.xml:
<session-config><session-timeout>3</session-timeout></session-config>
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
Podemoscriarumaaçãoquefaráo logindaaplicação.Casoousuário informadona telade loginexista,guardaremosomesmonasessãoeentraremosnosistema,ecasonãoexistavamosvoltarparaapáginadelogin.
OSpringMVCnospossibilitareceberasessãoemqualquermétodo,sóéprecisocolocaroobjetoHttpSessioncomoparâmetro.Porexemplo:
@ControllerpublicclassLoginController{
publicStringefetuaLogin(HttpSessionsession){//....}}
AsessãoéparecidacomumobjetodotipoMap<String,Object>,podemosguardarnelaqualquer
objeto que quisermos dando-lhes uma chave que é umaString. Portanto, para logarmos o usuário naaplicação poderíamos criar uma ação que recebe os dados do formulário de login e a sessãoHTTP,guardandoousuáriologadodentrodamesma:
@RequestMapping("efetuaLogin")publicStringefetuaLogin(Usuariousuario,HttpSessionsession){if(newJdbcUsuarioDao().existeUsuario(usuario)){session.setAttribute("usuarioLogado",usuario);return"menu";}else{//....}}
Agoraéamelhorhoradeaprenderalgonovo
12.5REGISTRANDOOUSUÁRIOLOGADONASESSÃO
184 12.5REGISTRANDOOUSUÁRIOLOGADONASESSÃO
1. Vamos criar o formulário de Login, uma ação para chamar este formulário e uma outra querealmenteautenticaousuário.
Crie a página formulario-login.jsp dentro de WebContent/WEB-INF/views com o
conteúdo:
<html><body><h2>PáginadeLogindasTarefas</h2><formaction="efetuaLogin"method="post">Login:<inputtype="text"name="login"/><br/>Senha:<inputtype="password"name="senha"/><br/><inputtype="submit"value="Entrarnastarefas"/></form></body></html>
Crie uma nova classe chamada LoginController no pacote
br.com.caelum.tarefas.controller . Crie um método para exibir o formulario-
login.jsp:
@ControllerpublicclassLoginController{
@RequestMapping("loginForm")publicStringloginForm(){return"formulario-login";}}
Namesma classe LoginController coloque o método que verifica o existência do usuario.
AcrescenteométodoefetuaLogin:
@RequestMapping("efetuaLogin")publicStringefetuaLogin(Usuariousuario,HttpSessionsession){if(newJdbcUsuarioDao().existeUsuario(usuario)){session.setAttribute("usuarioLogado",usuario);return"menu";}return"redirect:loginForm";}
Apósousuáriose logar,eleseráredirecionadoparaumapáginaqueconterá linksparaasoutraspáginasdosistemaeumamensagemdeboasvindas.
Crieapáginamenu.jspemWebContent/WEB-INF/viewscomocódigo:
<html><body><h2>PáginainicialdaListadeTarefas</h2><p>Bemvindo,${usuarioLogado.login}</p><ahref="listaTarefas">Cliqueaqui</a>paraacessaralistadetarefas
12.6EXERCÍCIO:FAZENDOOLOGINNAAPLICAÇÃO
12.6EXERCÍCIO:FAZENDOOLOGINNAAPLICAÇÃO 185
</body></html>
Acesseapáginadeloginemhttp://localhost:8080/fj21-tarefas/loginFormeseloguenaaplicação.
Verifiqueobancodedadosparaterumloginesenhaválidos.Paraisso,noterminalfaça:
mysql-urootusefj21;select*fromusuarios;
Sehouversenhanobanco,troqueoprimeirocomandopormysql-uroot-p,usandoasenha
corretaprobanco.Seatabelanãoexistir,vocêpodecriá-laexecutandoocomando:
createtableusuarios(loginVARCHAR(255),senhaVARCHAR(255));
(Dica: o códigopara criar essa tabela pode ser encontradono arquivocriacao-tabela.sql na
pasta21/projeto-tarefas/mysql)
Casonãoexistausuárioscadastrados,cadastrealgumutilizandoomesmoterminalabertoantesdaseguintemaneira:
insertintousuarios(login,senha)values('seu_usuario','sua_senha');
Nãopodemospermitirquenenhumusuárioacesseastarefassemqueeleestejalogadonaaplicação,pois essa não é uma informação pública. Precisamos portanto garantir que antes de executarmosqualqueraçãoousuárioestejaautenticado,ouseja,armazenadonasessão.
UtilizandooSpringMVC,podemosutilizaro conceitodos Interceptadores, que funcionamcomoFiltrosqueaprendemosanteriormente,mascomalgumasfuncionalidadesamaisqueestãorelacionadasaoframework.
Para criarmos um Interceptador basta criarmos uma classe que implemente a interface org.springframework.web.servlet.HandlerInterceptor . Ao implementar essa interface,
precisamosimplementar3métodos:preHandle,postHandleeafterCompletion.
OsmétodospreHandleepostHandleserãoexecutadosantesedepois,respectivamente,daação.
EnquantoométodoafterCompletionéchamadonofinaldarequisição,ousejaapósterrenderizadoo
JSP.
Paranossaaplicaçãoqueremosverificaroacessoantesdeexecutarumaação,porissovamosusarapenas o método preHandle da interface HandlerInterceptor . Porém, usando a interface
12.7 BLOQUEANDO ACESSOS DE USUÁRIOS NÃO LOGADOS COMINTERCEPTADORES
186 12.7BLOQUEANDOACESSOSDEUSUÁRIOSNÃOLOGADOSCOMINTERCEPTADORES
HandlerInterceptor somos obrigados implementar todos os métodos definidos na interface.
Queremos usar apenas o preHandle . Por isso o Spring MVC oferece uma classe auxiliar
(HandlerInterceptorAdapter) que já vem com uma implementação padrão para cadamétodo da
interface.Entãoparafacilitarotrabalhovamosestenderessaclasseesobrescreverapenasométodoqueédonossointeresse:
publicclassAutorizadorInterceptorextendsHandlerInterceptorAdapter{
@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objectcontroller)throwsException{//....response.sendRedirect("loginForm");returnfalse;}}
O método preHandle recebe a requisição e a resposta, além do controlador que está sendo
interceptado. O retorno é um booleano que indica se queremos continuar com a requisição ou não.Portanto,aclasseAutorizadorInterceptorsódevedevolvertrueseousuárioestálogado.Casoo
usuárionãoestejaautorizadovamosredirecionarparaoformuláriodelogin.
ParapegarousuáriologadoéprecisoacessarasessãoHTTP.Oobjetorequestpossuiummétodo
quedevolveasessãodousuárioatual:
HttpSessionsession=request.getSession();
DessamaneirapodemosverificarnoInterceptadoraexistênciadoatributousuarioLogado:
publicclassAutorizadorInterceptorextendsHandlerInterceptorAdapter{
@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objectcontroller)throwsException{
if(request.getSession().getAttribute("usuarioLogado")!=null){returntrue;}
response.sendRedirect("loginForm");returnfalse;}}
Falta só mais uma verificação. Existem duas ações na nossa aplicação que não necessitam deautorização.SãoasaçõesdoLoginController,necessáriasparapermitiraautenticaçãodousuário.
Alémdisso,vamosgarantirtambémqueapastaderesourcespodeseracessadamesmosemlogin.
Essa pasta possui as imagens, css e arquivos JavaScript. No entanto a classeAutorizadorInterceptorfica:
12.7BLOQUEANDOACESSOSDEUSUÁRIOSNÃOLOGADOSCOMINTERCEPTADORES 187
publicclassAutorizadorInterceptorextendsHandlerInterceptorAdapter{
@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objectcontroller)throwsException{
Stringuri=request.getRequestURI();if(uri.endsWith("loginForm")||uri.endsWith("efetuaLogin")||uri.contains("resources")){returntrue;}
if(request.getSession().getAttribute("usuarioLogado")!=null){returntrue;}
response.sendRedirect("loginForm");returnfalse;}}
Ainda precisamos registrar o nosso novo interceptador. Mas o Spring MVC não permite quefaçamos issovia anotações, entãousaremos a configuraçãoviaXMLnesse caso. Jávimoso arquivo spring-context.xml , nele vamos usar o tag mvc:interceptors para cadastrar a classe
AutorizadorInterceptor:
<mvc:interceptors><beanclass="br.com.caelum.tarefas.interceptor.AutorizadorInterceptor"/></mvc:interceptors>
Caso seja necessário alguma ordem na execução de diversos interceptors, basta registrá-los nasequênciadesejadadentrodatagmvc:interceptors.
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
EditoraCasadoCódigocomlivrosdeumaformadiferente
12.8EXERCÍCIOS:INTERCEPTANDOASREQUISIÇÕES
188 12.8EXERCÍCIOS:INTERCEPTANDOASREQUISIÇÕES
1. VamoscriarumInterceptorquenãopermitiráqueousuárioacesseasaçõessemantesterlogado
naaplicação.
CrieaclasseAutorizadorInterceptornopacotebr.com.caelum.tarefas.interceptor
Estenda a classe HandlerInterceptorAdapter do package
org.springframework.web.servlet.handler
SobrescreveométodopreHandle.OusuáriosópodeacessarosmétodosdoLoginController
SEMterfeitoo login.Casooutra lógicasejachamadaéprecisoverificarseousuárioexistenasessão.Existindonasessão,seguiremosofluxonormalmente,casocontrário indicaremosqueousuário não está logado e que deverá ser redirecionado para o formulário de login. O códigocompletodointerceptadorfica:
publicclassAutorizadorInterceptorextendsHandlerInterceptorAdapter{
@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objectcontroller)throwsException{
Stringuri=request.getRequestURI();if(uri.endsWith("loginForm")||uri.endsWith("efetuaLogin")||uri.contains("resources")){returntrue;}
if(request.getSession().getAttribute("usuarioLogado")!=null){returntrue;}
response.sendRedirect("loginForm");returnfalse;}}
Temos que registrar o nosso novo interceptador noXML do spring.Abra o arquivo spring-
context.xml.Dentrodatag<beans>adicione:
<mvc:interceptors><beanclass="br.com.caelum.tarefas.interceptor.AutorizadorInterceptor"/></mvc:interceptors>
2. Reinicieoservidoretenteacessaralistadetarefasemhttp://localhost:8080/fj21-tarefas/listaTarefas.Vocêdeveráserredirecionadoparaoformuláriodelogin.
1. Façaologoutdaaplicação.Crieumlinknomenu.jspqueinvocaráummétodoqueremoveráo
12.9EXERCÍCIOSOPCIONAIS:LOGOUT
12.9EXERCÍCIOSOPCIONAIS:LOGOUT 189
usuáriodasessãoeredirecioneanavegaçãoparaaactiondoformuláriodelogin(loginForm).
Nomenu.jspacrescente:
<ahref="logout">Sairdosistema</a>
NaclasseLoginControlleradicioneométodoparaologouteinvalideasessãodousuário:
@RequestMapping("logout")publicStringlogout(HttpSessionsession){session.invalidate();return"redirect:loginForm";}
190 12.9EXERCÍCIOSOPCIONAIS:LOGOUT
CAPÍTULO13
"Fazertroçadafilosofiaé,naverdade,filosofar"--BlaisePascal
Nessecapítulo,vocêaprenderá:
OqueéumcontainerdeinversãodecontrollerComogerenciarqualquerobjetoeinjetardependênciascomoSpringImplantarsuaaplicaçãoemqualquercontainer.
NanossaclasseTarefasControllerusamosoJdbcTarefaDaoparaexecutarosmétodosmais
comunsnobancodedados relacionado comaTarefa. Em cadamétodo do controller criamos um
JdbcTarefaDaoparaacessarobanco.ReparenocódigoabaixoquantasvezesinstanciamosoDAOna
mesmaclasse:
@ControllerpublicclassTarefasController{
@RequestMapping("mostraTarefa")publicStringmostra(Longid,Modelmodel){JdbcTarefaDaodao=newJdbcTarefaDao();model.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/mostra";}
@RequestMapping("listaTarefas")publicStringlista(Modelmodel){JdbcTarefaDaodao=newJdbcTarefaDao();model.addAttribute("tarefas",dao.lista());return"tarefa/lista";}
@RequestMapping("adicionaTarefa")publicStringadiciona(@ValidTarefatarefa,BindingResultresult){
if(result.hasFieldErrors("descricao")){return"tarefa/formulario";}
JdbcTarefaDaodao=newJdbcTarefaDao();dao.adiciona(tarefa);return"tarefa/adicionada";
SPRINGIOCEDEPLOYDAAPLICAÇÃO
13.1 MENOS ACOPLAMENTO COM INVERSÃO DE CONTROLE EINJEÇÃODEDEPENDÊNCIAS
13SPRINGIOCEDEPLOYDAAPLICAÇÃO 191
}
//outrosmétodosremove,alteraefinalizatambémcriamaJdbcTarefaDao}
AclasseTarefasControllerinstanciaumobjetodotipoJdbcTarefaDaomanualmente.Estamos
repetindoacriaçãodoDAOemcadamétodo.Podemosmelhorarocódigo,criaroJdbcTarefaDaono
construtordaclasseTarefasControllereusarumatributo.Assimpodemosapagartodasaslinhasde
criaçãodoDAO:
@ControllerpublicclassTarefasController{
privateJdbcTarefaDaodao;
publicTarefasController(){this.dao=newJdbcTarefaDao();}
@RequestMapping("mostraTarefa")publicStringmostra(Longid,Modelmodel){//daojáfoicriadomodel.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/mostra";}
@RequestMapping("listaTarefas")publicStringlista(Modelmodel){//daojáfoicriadomodel.addAttribute("tarefas",dao.lista());return"tarefa/lista";}
//outrosmétodostambémaproveitamoatributodao
}
Onossocódigomelhorou,poistemosmenoscódigoparamanter,mashámaisumproblema.ReparequecontinuamosresponsáveispelacriaçãodoDAO.AclassequefazusodesteDAOestáintimamenteligada com a maneira de instanciação do mesmo, fazendo com que ela mantenha um alto grau deacoplamento.
DizemosqueaclasseTarefasControllertemumadependênciacomoJdbcTarefaDao.VáriosmétodosdeladependemdoJdbcTarefaDao.Nocódigoacimacontinuamosadarnewdiretamentena
implementação JdbcTarefaDao para usá-la. Nossa aplicação cria a dependência, chamandodiretamenteaclasseespecífica.
O problema em gerenciar a dependência diretamente na aplicação fica evidente se analisamos aclasse JdbcTarefaDao . Se a classe precisar de um processo de construção mais complicado,
precisaremosnospreocuparcomissoemtodososlugaresondecriamosoJdbcTarefaDao.
ParaentenderissovamosverificaroconstrutordoDAO:
192 13.1MENOSACOPLAMENTOCOMINVERSÃODECONTROLEEINJEÇÃODEDEPENDÊNCIAS
publicclassJdbcTarefaDao{
privatefinalConnectionconnection;
publicJdbcTarefaDao(){try{this.connection=newConnectionFactory().getConnection();}catch(SQLExceptione){thrownewRuntimeException(e);}}//métodosomitidos}
Reparequeaquiexisteomesmoproblema.ODAOtambémresolveasuadependênciaecriaatravésda ConnectionFactory, a conexão. Estamos acoplados à classe ConnectionFactory enquanto
apenasqueremosumaconexão.Aclasseestácomaresponsabilidadedeprocuraradependência.Essaresponsabilidadedeveestaremoutrolugar.Paramelhorar,vamosentãodeclararadependênciaemumlugar natural que é o construtor. O código fica muito mais simples e não precisa mais daConnectionFactory:
publicclassJdbcTarefaDao{
privatefinalConnectionconnection;
publicJdbcTarefaDao(Connectionconnection){this.connection=connection;}
//métodosomitidos}
Jácomessapequenamudança,nãopodemoscriaroJdbcTarefaDaosemnospreocuparmoscoma
conexãoantes.VejacomoficariaocódigodaclasseTarefasController:
@ControllerpublicclassTarefasController{
privateJdbcTarefaDaodao;
publicTarefasController(){try{Connectionconnection=newConnectionFactory().getConnection();this.dao=newJdbcTarefaDao(connection);}catch(SQLExceptione){thrownewRuntimeException(e);}}//métodosomitidos}
AclasseTarefasControllernãosócriaráoDAO,comotambémaconexão.Jádiscutimosque
nãoqueremosserresponsáveis,entãovamosagirigualaoJdbcTarefaDaoedeclararadependênciano
construtor.Novamentevaisimplificardemaisonossocódigo:
@Controller
13.1MENOSACOPLAMENTOCOMINVERSÃODECONTROLEEINJEÇÃODEDEPENDÊNCIAS 193
publicclassTarefasController{
privateJdbcTarefaDaodao;
publicTarefasController(JdbcTarefaDaodao){this.dao=dao;}
//métodosomitidos}
Assimcadaclassedelegaadependênciaparacimaenãoseresponsabilizapelacriação.Quemforusar essa classeTarefasController, agora, vai precisar satisfazer as dependências.Mas quem no
finalsepreocupacomacriaçãodoDAOecomaconexão?
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
OpadrãodeprojetosDependencyInjection(DI)(Injeçãodedependências),procuraresolveressesproblemas.Aideiaéqueaclassenãomaisresolvaassuasdependênciasporcontaprópriamasapenasdeclaraquedependedealgumaoutraclasse.Edealgumaoutraformaquenãosabemosainda,alguémresolverá essa dependência para nós. Alguém pega o controle dos objetos que liga (ou amarra) asdependências.Nãoestamosmaisnocontrole,háalgumcontainerquegerenciaasdependênciaseamarratudo.Essecontainerjáexistiaejáusamoselesemsaber.
Reparequecom@Controller jádefinimosqueanossaclasse fazpartedoSpringMVC,masa
anotaçãovaialémdisso.TambémdefinimosquequeremosqueoSpringcontroleoobjeto.Springénofundoumcontainerquedánewparanóse tambémsabe resolvere ligarasdependências.Por isso,oSpringtambéméchamadoContainerIoC(InversionofControl)ouContainerDI.
Essas são as principais funcionalidades de qualquer container de inversão de controle/injeção dedependência. O Spring é um dos vários outros containers disponíveis no mundo Java. Segue uma
JáconheceoscursosonlineAlura?
13.2CONTAINERDEINJEÇÃODEDEPENDÊNCIAS
194 13.2CONTAINERDEINJEÇÃODEDEPENDÊNCIAS
pequenalistacomoscontainersmaisfamosos:
SpringIoCousóSpring basedequalquerprojetoSpring,popularizouDI eodesenvolvimentocomPOJOsJBossSeam2ContainerDIqueseintegramuitobemcomJSF,principalmentenaversão1.2doJSFGuiceContainerDIcriadopeloGooglequefavoreceuconfiguraçõesprogramáticassemXMLCDIEspecificaçãoqueentroucomoJavaEE6,fortementeinfluenciadopeloJBossSeameGuicePicoContainermuitoleveeflexível,foipioneironessaáreaEJB3ContainerJavaEEcomumsuportelimitadoaoDI
OSpringContainersabecriaroobjeto,mastambémligaasdependênciasdele.ComooSpringestánocontrole,eleadministratodoociclodavida.Paraissoacontecerseráprecisodefinirpelomenosduasconfigurações:
declararoobjetocomocomponenteligaradependência
ParareceberoDAOemnossocontrolador,usaremosaanotação@Autowiredacimadoconstrutor
(wire-amarrar).IssoindicaaoSpringqueeleprecisaresolvereinjetaradependência:
@ControllerpublicclassTarefasController{
privateJdbcTarefaDaodao;
@AutowiredpublicTarefasController(JdbcTarefaDaodao){this.dao=dao;}
//métodosomitidos}
ParaoSpringconseguircriaroJdbcTarefaDaovamosdeclararaclassecomocomponente.Aquio
Springpossuiaanotaçã[email protected]émdisso,
vamos também amarrar a conexão com @Autowired . Veja o código similar a classe
TarefasController:
@RepositorypublicclassJdbcTarefaDao{
privatefinalConnectionconnection;
@AutowiredpublicJdbcTarefaDao(Connectionconnection){this.connection=connection;
13.3CONTAINERSPRINGIOC
13.3CONTAINERSPRINGIOC 195
}
//métodosomitidos}
Aousar@Autowirednoconstrutor,oSpringtentadescobrircomoabrirumaconexão,masclaro
que oContainer não faz ideia comqual bancoqueremosnos conectar. Para solucionar isso oSpringoferece uma configuração de XML que define um provedor de conexões. No mundo JavaEE, esteprovedoréchamadoDataSourceeabstraiasconfiguraçõesdeDriver,URL,etcdaaplicação.
Sabendo disso, devemos declarar um DataSource no XML do Spring, dentro do spring-
context.xml:
<beanid="mysqlDataSource"class="org.apache.commons.dbcp2.BasicDataSource"><propertyname="driverClassName"value="com.mysql.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost/fj21"/><propertyname="username"value="root"/><propertyname="password"value="<SENHADOBANCOAQUI>"/></bean>
Definimosumbean noXML.Umbean é apenas um sinônimo para componente.Ao final, cadabean se tornaumobjetoadministradopeloSpring.ParaoSpringContainer,amysqlDataSource, o
JdbcTarefaDao e TarefasController são todos componentes(ou beans) que foram
ligados/amarrados.Ouseja,umdependeaooutro.OSpringvaicriartodoseadministrarociclodavidadeles.
Com a mysqlDataSource definida, podemos injetar ela na JdbcTarefaDao para recuperar a
conexãoJDBC:
@RepositorypublicclassJdbcTarefaDao{
privatefinalConnectionconnection;
@AutowiredpublicJdbcTarefaDao(DataSourcedataSource){try{this.connection=dataSource.getConnection();}catch(SQLExceptione){thrownewRuntimeException(e);}}
//métodosomitidos}
Pronto!Reparequenonossoprojetoocontrolador -- dependedo -->daoque --dependedo -->datasource.OSpringvaiprimeirocriaramysqlDataSource,depoisoDAOenofinalocontrolador.
Eleéoresponsávelpelacriaçãodetodaacadeiadedependências.
196 13.3CONTAINERSPRINGIOC
SPRINGIOC
Vimos apenas uma parte do poderoso framework Spring. A inversão de controle não só selimitaainjeçãodedependências,oSpringtambémsabeassumiroutraspreocupaçõescomunsentreaplicaçõesdenegócios,comoporexemplo,tratamentodetransaçãoousegurança.
Além disso, é comum integrar outros frameworks e bibliotecas com Spring. Não é raroencontrarprojetosqueusamcontroladoresMVCcomoStrutsouJSFcomSpring.OSpringémuitoflexívelesabegerenciarqualquercomponente,sejaelesimples,comoumaclassedonossoprojeto,ouumframeworksofisticadocomooHibernate.OtreinamentoFJ-27mostracomoaproveitaromelhordoSpring.
Vimos como injetar uma dependência com Spring usando o construtor da classe junto com aanotaçã[email protected]éumpontodeinjeção quedeixaclaroqueessadependência éobrigatóriajáquenãohácomoinstanciaroobjetosempassarpeloseuconstrutor.
Háoutrospontosdeinjeçãocomo,porexemplo,oatributo.Podemosusaraanotação@Autowired
diretamente no atributo. Assim o construtor não é mais necessário, por exemplo podemos injetar oJdbcTarefaDaonaclasseTarefasController:
@ControllerpublicclassTarefasController{
@AutowiredprivateJdbcTarefaDaodao;
//semconstrutor}
Outra forma é criar um método dedicado para dependência, normalmente é usado um setteraplicandoaanotaçãoemcimadométodo:
@ControllerpublicclassTarefasController{
privateJdbcTarefaDaodao;
//semconstrutor
@AutowiredpublicvoidsetTarefaDao(JdbcTarefaDaodao){this.dao=dao;}}
13.4OUTRASFORMASDEINJEÇÃO
13.4OUTRASFORMASDEINJEÇÃO 197
Hávantagensedesvantagensdecadaforma,masemgeraldevemosfavoreceroconstrutor jáqueissoéolugarnaturalparaadependência.Noentantoéimportanteconhecerosoutrospontosdeinjeçãopoisalgunsframeworks/bibliotecasexigemoconstrutorsemparâmetro,obrigandoodesenvolvedorusaro atributo ou método. Como veremos no apêndice até mesmo o Spring precisa em alguns casos oconstrutorsemargumentos.
PARASABERMAIS:@INJECT
AinjeçãodedependênciaéumtópicobastantedifundidonaplataformaJava.Existemvárioscontainercomessepoder,tantoqueissoéopadrãoparaamaioriadosprojetosJavahojeemdia.
CDI(ContexteDependencyInjection,JSR-299)éaespecificaçãoqueestásepopularizando.OSpringsódásuportelimitadoaoCDImasaproveitaasanotaçõespadronizadaspelaespecificaçãoJSR-330.DessamaneirabastaadicionaroJARdaespecificação(java.inject) no classpath e
assimpodemossubstituiraanotação@Autowiredcom@Inject:
@ControllerpublicclassTarefasController{
privateJdbcTarefaDaodao;
@InjectpublicTarefasController(JdbcTarefaDaodao){this.dao=dao;}
//métodosomitidos}
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
VocêpodetambémfazerocursodatadessaapostilanaCaelum
13.5 EXERCÍCIOS: INVERSÃO DE CONTROLE COM O SPRING
198 13.5EXERCÍCIOS:INVERSÃODECONTROLECOMOSPRINGCONTAINER
1. ParaconfiguraraDatasourceéprecisocopiardoisJARs.
Primeiro,váaoDesktop,eentrenodiretório21/datasource.
HaverádoisJARs:
commons-dbcp2-2.7.x.jar
commons-pool2-2.8.x.jar.
Copie-os (CTRL+C) e cole-os (CTRL+V) dentro de workspace/fj21-
tarefas/WebContent/WEB-INF/lib
2. Noarquivospring-context.xmladicioneaconfiguraçãodaDatasource:
(Dica: um exemplo dessa configuração encontra-se em 21/datasource/spring-context-
confs.xml)
<beanid="mysqlDataSource"class="org.apache.commons.dbcp2.BasicDataSource"><propertyname="driverClassName"value="com.mysql.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost/fj21"/><propertyname="username"value="root"/><propertyname="password"value=""/></bean>
Repare que definimos as propriedades da conexão, igual a antiga classeConnectionFactory e
lembre-sequeousuáriorootestáconfiguradocomasenha""(aspasvazias).
3. VamosconfiguraraclasseJdbcTarefaDaocomocomponente(Bean)doSpring.Paraisso,adicione
aanotação@Repositoryemcimadaclasse:
@RepositorypublicclassJdbcTarefaDao{...}
UseCtrl+Shift+Oparaimportaraanotação.
1. Alémdisso,altereoconstrutordaclasseJdbcTarefaDao.Useaanotação@Autowiredemcima
doconstrutorecoloqueaDataSourcenoconstrutor.Atravésdelaobteremosumanovaconexão.A
classeDataSourcevemdopackagejavax.sql.
AltereoconstrutordaclasseJdbcTarefaDao:
@AutowiredpublicJdbcTarefaDao(DataSourcedataSource){try{this.connection=dataSource.getConnection();}catch(SQLExceptione){
CONTAINER
13.5EXERCÍCIOS:INVERSÃODECONTROLECOMOSPRINGCONTAINER 199
thrownewRuntimeException(e);}}
AoalteraroconstrutorvãoaparecererrosdecompilaçãonaclasseTarefasController.
2. AbraaclasseTarefaController.NelavamoscriarumatributoparaaJdbcTarefaDaoegeraro
construtorquerecebeoDAO,novamenteusandoaanotação@Autowired:
@ControllerpublicclassTarefasController{
privatefinalJdbcTarefaDaodao;
@AutowiredpublicTarefasController(JdbcTarefaDaodao){this.dao=dao;}}
3. AlteretodososmétodosquecriamumainstânciadaclasseJdbcTarefaDao.São justamenteesse
métodosquepossuemerrosdecompilação.
Removatodasaslinhascomo:
JdbcTarefaDaodao=newJdbcTarefaDao();
Porexemplo,ométodoparalistarastarefasficacomo:
@RequestMapping("listaTarefas")publicStringlista(Modelmodel){model.addAttribute("tarefas",dao.lista());return"tarefa/lista";}
Arrume os outros erros de compilação, apague todas as linhas que instanciam a classeJdbcTarefaDao.
1. ReinicieoTomcateacesseaaplicação.Tudodevecontinuarfuncionando.2. (opcional)AclasseConnectionFactoryaindaénecessária?
Melhoramosgradativamente anossa aplicaçãomodificandoclasses eutilizando frameworks, tudono lado do servidor, mas o que o usuário final enxerga é o HTML gerado que é exibido em seunavegador.Utilizamosalgumastagsparaestruturarasinformações,masnossaapresentaçãoaindadeixaadesejar.Nãoháatrativoestético.
Paraaprimorarovisual,vamosaplicarCSSnasnossaspáginas.CSSéumacrônimoparaCascadingStyleSheets,quepodemostraduzirparaFolhasdeEstiloemCascata.Osestilosdefinemolayout,porexemplo,acor,otamanho,oposicionamentoesãodeclaradosparaumdeterminadoelementoHTML.O
13.6APRIMORANDOOVISUALATRAVÉSDECSS
200 13.6APRIMORANDOOVISUALATRAVÉSDECSS
CSSalteraaspropriedadesvisuaisdaqueleelementoe,porcascata,todososseuselementosfilhos.Emgeral, o HTML é usado para estruturar conteúdos da página (tabelas, parágrafos, div etc) e o CSSformataedefineaaparência.
AsintaxedoCSStemumaestruturasimples:éumadeclaraçãodepropriedadesevaloresseparadospor um sinal de dois pontos(:), e cada propriedade é separada por um sinal de ponto e vírgula(;), daseguintemaneira:
background-color:yellow;color:blue;
Como estamos declarando as propriedades visuais de um elemento em outro lugar do nossodocumento, precisamos indicar de alguma maneira a qual elemento nos referimos. Fazemos issoutilizandoumseletorCSS.
Noexemploaseguir,usaremososeletorp,quealterarátodososparágrafosdodocumento:
p{color:blue;background-color:yellow;}
Existem várias lugares que podemos declarar os estilos, mas o mais comum é usar um arquivoexterno, com a extensão.css. Para que seja possível declarar nossoCSS em um arquivo à parte,
precisamosindicaremnossodocumentoHTMLumaligaçãoentreeleeafolhadeestilo.
Aindicaçãodeusodeumafolhadeestilosexternadeveserfeitadentrodatag<head>donosso
documentoHTML:
<!DOCTYPEhtml><html><head><linktype="text/css"rel="stylesheet"href="tarefas.css"></head><body><p>Oconteúdodestatagseráexibidoemazulcomfundoamarelo!</p>
</body></html>
Edentrodoarquivotarefas.css:
p{color:blue;background-color:yellow;}
SintaxeeinclusãodeCSS
Declaraçãonoarquivoexterno
13.6APRIMORANDOOVISUALATRAVÉSDECSS 201
PROGRAMAÇÃOFRONT-END
Apreocupaçãocomaorganizaçãodocódigonãoéexclusivadoprogramadorback-end,istoé,aquelequeprocessaseucódigonoservidor.
Hátambémoprogramadorfront-end,aqueleresponsávelpelocódigoquerodanonavegadordocliente, que também deve se preocupar com a organização de seu código, inclusive com aapresentaçãoatravésdeCSSedeumHTMLbemestruturado.
A Caelum oferece os treinamentosWD-43 eWD-47 para quem quer dominar as melhorestécnicas de desenvolvimentoWeb com semântica perfeita, estilos CSS poderosos e JavaScriptscorretosefuncionais.
1. Noseuprojeto,crieumanovapastacssdentrodapastaWebContent/resources.
2. VáaoDesktopeentrenodiretório21/projeto-tarefas/css.Copieoarquivotarefas.css(CTRL+C)paraapastaWebContent/resources/css(CTRL+V).
3. Abraapáginaformulario-login.jspqueestádentrodapastaWebContent/WEB-INF/views.
4. NapáginaJSP,adicioneumcabeçalho(<head></head>)comolinkparaoarquivoCSS.Adicione
ocabeçalhoentredatag<html>e<body>:
<head><linktype="text/css"href="resources/css/tarefas.css"rel="stylesheet"/></head>
AtravésdolinkdefinimosocaminhoparaoarquivoCSS.
Cuidado:Napáginadeveexistirapenasumcabeçalho(<head></head>).
1. ReinicieoTomcatechamaapáginadelogin:
http://localhost:8080/fj21-tarefas/loginForm
Apáginajádeveaparecercomumvisualnovo.
2. Aplique o mesmo arquivo CSS em todas as outras páginas. Tenha cuidado para não repetir ocabeçalho,algumaspáginasjápossuematag<head>,outrasnão.
Verifiqueoresultadonaaplicação.
13.7EXERCÍCIOSOPCIONAIS:APLICANDOCSSNASPÁGINAS
202 13.7EXERCÍCIOSOPCIONAIS:APLICANDOCSSNASPÁGINAS
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
Geralmente,aodesenvolvermosumaaplicaçãoWeb,possuímosumambientededesenvolvimentocomumservidorqueestáemnossasmáquinaslocais.Depoisqueodesenvolvimentoestáfinalizadoenossaaplicaçãoestáprontaparairaoar,precisamosenviá-laparaumambientequecostumamoschamarde ambiente de produção. Esse processo de disponibilizarmos nosso projeto em um determinadoambienteéoquechamamosdedeploy(implantação).
Oservidordeproduçãoéolocalnoqualoprojetoestaráhospedadoesetornaráacessívelparaosusuáriosfinais.
Mascomofazemosparaenviarnossoprojetoqueestáemnossasmáquinaslocaisparaumservidorexterno?
OprocessopadrãodedeploydeumaaplicaçãowebemJavaéodecriarumarquivocomextensão.war,quenadamaiséqueumarquivozipcomodiretóriobasedaaplicaçãosendoaraizdozip.
No nosso exemplo, todo o conteúdo do diretórioWebContent pode ser incluído em um arquivotarefas.war.Apóscompactarodiretório,efetuaremosodeploy.
Seuslivrosdetecnologiaparecemdoséculopassado?
13.8DEPLOYDOPROJETOEMOUTROSAMBIENTES
13.8DEPLOYDOPROJETOEMOUTROSAMBIENTES 203
NoTomcat,bastacopiaroarquivo.warnodiretórioTOMCAT/webapps/.Eleserádescompactadopelo container e um novo contexto chamado tarefas estará disponível. Repare que o novo contextogeradopeloTomcatadotaomesmonomequeoseuarquivo.war,ouseja,nossoarquivochamava-se
tarefas.wareocontextosechamatarefas.
Ao colocarmos o war no Tomcat, podemos acessar nossa aplicação pelo navegador através do
endereço:http://localhost:8080/tarefas/loginForm
1. Vamospraticarcriandoumwareutilizando-o,mas,antesdecomeçarmos,certifique-sedequeo
Tomcatestejanoar.
CliquecomobotãodireitonoprojetoeváemExport
Digitenabuscawarfile
CliquenobotãoBrowseeescolhaapastadoseuusuárioeonometarefas.war.
13.9EXERCÍCIOS:DEPLOYCOMWAR
204 13.9EXERCÍCIOS:DEPLOYCOMWAR
CliqueemFinish
Pronto,nossowarestácriado!
2. Vamosinstalarnossowar!
AbraoFileBrowser
Cliquedadireitanoarquivotarefas.wareescolhaCut(Recortar).
Vá para o diretórioapache-tomcat, Certifique-se de que seuTomcat esteja rodando, para issoacesseodiretóriobin/dotomcatviaterminaleexecuteoscriptstartup.sh.
Acesseodiretóriowebapps.
Coleoseuarquivoaqui:(Edit->Paste).Reparequeodiretóriotarefasfoicriado.
13.9EXERCÍCIOS:DEPLOYCOMWAR 205
PodemosacessaroprojetoatravésdaURL:http://localhost:8080/tarefas/loginForm
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
13.10 DISCUSSÃO EM AULA: LIDANDO COM DIFERENTES NOMESDECONTEXTO
Agoraéamelhorhoradeaprenderalgonovo
206 13.10DISCUSSÃOEMAULA:LIDANDOCOMDIFERENTESNOMESDECONTEXTO
CAPÍTULO14
"É uma experiência eterna de que todos os homens com poder são tentados a abusar." -- Baron deMontesquieu
Nestecapítulo,vocêaprenderáa:
EntenderadiferençaentreaJPAeoHibernate;UsaraferramentadeORMJPAcomHibernate;Gerarastabelasemumbancodedadosqualquerapartirdesuasclassesdemodelo;InserirecarregarobjetospeloJPAnobanco;BuscarváriosobjetospeloJPA;
Com a popularização do Java em ambientes corporativos, logo se percebeu que grande parte dotempo do desenvolvedor era gasto na codificação de queries SQL e no respectivo código JDBCresponsávelportrabalharcomelas.
Alémdeumproblemadeprodutividade,algumasoutraspreocupaçõesaparecem:SQLque,apesarde ter umpadrãoANSI, apresenta diferenças significativas dependendodo fabricante.Não é simplestrocarumbancodedadospelooutro.
Há ainda amudança do paradigma.A programação orientada a objetos diferemuito do esquemaentidade relacional e precisamos pensar das duas maneiras para fazer um único sistema. Pararepresentarmosasinformaçõesnobanco,utilizamostabelasecolunas.Astabelasgeralmentepossuemchave primária (PK) e podem ser relacionadas pormeio da criação de chaves estrangeiras (FK) emoutrastabelas.
Quando trabalhamos com uma aplicação Java, seguimos o paradigma orientado a objetos, onderepresentamos nossas informações por meio de classes e atributos. Além disso, podemos utilizartambémherança,composiçãopararelacionaratributos,polimorfismo,enumerações,entreoutros.Esseburaco entre esses dois paradigmas gera bastante trabalho: a todo momento devemos "transformar"objetosemregistroseregistrosemobjetos.
UMAINTRODUÇÃOPRÁTICAAOJPACOMHIBERNATE
14.1MAPEAMENTOOBJETORELACIONAL
14UMAINTRODUÇÃOPRÁTICAAOJPACOMHIBERNATE 207
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
Ferramentas para auxiliar nesta tarefa tornaram-se populares entre os desenvolvedores Java e sãoconhecidas como ferramentas de mapeamento objeto-relacional (ORM). O Hibernate é umaferramentaORM open source e é a líder demercado, sendo a inspiração para a especificação JavaPersistenceAPI (JPA).OHibernatenasceu semJPAmashoje emdia é comumacessaroHibernatepela especificação JPA. Como toda especificação, ela deve possuir implementações. Entre asimplementaçõesmaiscomuns,podemoscitar:HibernatedaRedHat,EclipseLinkdaEclipseFoundationeoOpenJPAdaApache.ApesardoHibernate teroriginadoa JPA,oEclipseLinkéa implementaçãoreferencial.
OHibernate abstrai o seu códigoSQL, toda a camada JDBC e o SQL será gerado em tempo deexecução.Maisque isso,elevaigeraroSQLqueserveparaumdeterminadobancodedados, jáquecadabancofalaum"dialeto"diferentedessalinguagem.AssimhátambémapossibilidadedetrocardebancodedadossemterdealterarcódigoJava,jáqueissoficacomoresponsabilidadedaferramenta.
Comousaremos JPAabstraímosmais ainda, podemosdesenvolver semconhecerdetalhes sobreoHibernateeatétrocaroHibernatecomumaoutraimplementaçãocomoOpenJPA:
VamosusaroJPAcomHibernate,ouseja,precisamosbaixarosJARsnositedoHibernate.Ositeoficial do Hibernate é o www.hibernate.org, onde você baixa a última versão na seção ORM eDownload.
Com o ZIP baixado emmãos, vamos descompactar o arquivo. Dessa pasta vamos usar todos osJARsobrigatórios(required).NãopodemosesqueceroJARdaespecificaçãoJPAqueseencontrana
EditoraCasadoCódigocomlivrosdeumaformadiferente
14.2JAVAPERSISTENCEAPIEFRAMEWORKSORM
14.3BIBLIOTECASDOHIBERNATEEJPA
208 14.2JAVAPERSISTENCEAPIEFRAMEWORKSORM
pastajpa.
ParausaroHibernateeJPAnoseuprojetoénecessáriocolocartodosessesJARsnoclasspath.
OHibernate vai gerar o código SQL para qualquer banco de dados. Continuaremos utilizando obancoMySQL,portantotambémprecisamosoarquivo.jarcorrespondenteaodriverJDBC.
Paraestecapítulo,continuaremosutilizandoaclassequerepresentaumatarefa:
packagebr.com.caelum.tarefas.modelo;
publicclassTarefa{privateLongid;privateStringdescricao;privatebooleanfinalizado;privateCalendardataFinalizacao;}
Criamososgetterse settersparamanipularoobjeto,mas fiqueatentoquesódevemosusaressesmétodosserealmentehouvernecessidade.
EssaéumaclassecomoqualqueroutraqueaprendemosaescreveremJava.PrecisamosconfiguraroHibernateparaqueelesaibadaexistênciadessaclassee,destaforma,saibaquedeveinserirumalinhanatabelaTarefatodavezqueforrequisitadoqueumobjetodessetiposejasalvo.Emvezdeusarmos
otermo"configurar",falamosemmapearumaclasseatabela.
Para mapear a classe Tarefa , basta adicionar algumas poucas anotações em nosso código.AnotaçãoéumrecursodoJavaquepermite inserirmetadadosemrelaçãoanossaclasse,atributosemétodos.Essasanotaçõesdepoispoderãoserlidasporframeworksebibliotecas,paraqueelestomemdecisõesbaseadasnessaspequenasconfigurações.
Paraessanossaclasseemparticular,precisamosdeapenasquatroanotações:
@EntitypublicclassTarefa{
@Id@GeneratedValueprivateLongid;
privateStringdescricao;privatebooleanfinalizado;
@Temporal(TemporalType.DATE)privateCalendardataFinalizacao;
//métodos...}
14.4 MAPEANDO UMA CLASSE TAREFA PARA NOSSO BANCO DEDADOS
14.4MAPEANDOUMACLASSETAREFAPARANOSSOBANCODEDADOS 209
@Entity indicaqueobjetosdessaclassesetornem"persistível"nobancodedados.@Id indica
queoatributoid énossachaveprimária (vocêprecisa terumachaveprimáriaem todaentidade)e
@GeneratedValuedizquequeremosqueestachavesejapopuladapelobanco(istoé,quesejausado
umautoincrementousequence,dependendodobancodedados).Com@Temporalconfiguramos
comomapearumCalendarparaobanco,aquiusamosapenasadata(semhora),maspoderíamoster
usado apenas a hora (TemporalType.TIME) ou timestamp (TemporalType.TIMESTAMP ). Essas
anotaçõesprecisamdosdevidosimports,epertencemaopacotejavax.persistence.
Masemquetabelaessaclasseserágravada?Emquaiscolunas?Quetipodecoluna?Naausênciadeconfiguraçõesmais específicas, oHibernate vai usar convenções: a classeTarefa será gravada na
tabela de nome tambémTarefa, e o atributo descricao em uma coluna de nome descricao
também!
Se quisermos configurações diferentes das convenções, basta usarmos outras anotações, que sãocompletamente opcionais. Por exemplo, para mapear o atributo dataFinalizacao numa coluna
chamadadata_finalizadofaríamos:
@Column(name="data_finalizado",nullable=true)privateCalendardataFinalizacao;
Parausarumatabelacomonometarefas:
@Entity@Table(name="tarefas")publicclassTarefa{
Reparequenasentidadeshátodasasinformaçõessobreastabelas.Baseadonelaspodemosatépedirgerarastabelasnobanco,masparaissoéprecisoconfiguraroJPA.
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
JáconheceoscursosonlineAlura?
210 14.4MAPEANDOUMACLASSETAREFAPARANOSSOBANCODEDADOS
EmqualbancodedadosvamosgravarnossasTarefass?Qualéologin?Qualéasenha?OJPA
necessitadessasconfigurações,eparaissocriaremosoarquivopersistence.xml.
AlgunsdadosquevãonessearquivosãoespecíficosdoHibernateepodemserbemavançados,sobrecontroledecache,transações,connectionpooletc,tópicosquesãoabordadosnocursoFJ-25.
Paranossosistema,precisamosdequatro linhascomconfiguraçõesque jáconhecemosdoJDBC:string de conexão com o banco, o driver, o usuário e senha. Além dessas quatro configurações,precisamos dizer qual dialeto de SQL deverá ser usado nomomento que as queries são geradas; nonosso caso, MySQL. Vamos também mostrar o SQL no console para acompanhar o trabalho doHibernate.
Vamostambémconfiguraracriaçãodastabelasbaseadonasentidades.
Segueumaconfiguraçãocompletaquedefineumaunidadedepersistência(persistence-unit)comonometarefas,seguidospeladefiniçãodoprovedor,entidadeseproperties:
<persistencexmlns="http://java.sun.com/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"version="2.0">
<persistence-unitname="tarefas">
<!--provedor/implementacaodoJPA--><provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--entidademapeada--><class>br.com.caelum.tarefas.modelo.Tarefa</class>
<properties><!--dadosdaconexao--><propertyname="javax.persistence.jdbc.driver"value="com.mysql.jdbc.Driver"/><propertyname="javax.persistence.jdbc.url"value="jdbc:mysql://localhost/fj21"/><propertyname="javax.persistence.jdbc.user"value="root"/><propertyname="javax.persistence.jdbc.password"value="<SENHADOBANCOAQUI>"/>
<!--propriedadesdohibernate--><propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQL8Dialect"/><propertyname="hibernate.show_sql"value="true"/><propertyname="hibernate.format_sql"value="true"/>
<!--atualizaobanco,geraastabelasseforpreciso--><propertyname="hibernate.hbm2ddl.auto"value="update"/></properties></persistence-unit></persistence>
14.5CONFIGURANDOOJPACOMASPROPRIEDADESDOBANCO
14.5CONFIGURANDOOJPACOMASPROPRIEDADESDOBANCO 211
É importante saber que o arquivo persistence.xml deve ficar na pasta META-INF do seu
classpath.
ParausaroJPAnonossocódigoJava,precisamosutilizarsuaAPI.Elaébastantesimplesediretaerapidamentevocêestaráhabituadocomsuasprincipaisclasses.
Nosso primeiro passo é fazer com que o JPA leia a nossa configuração: tanto o nosso arquivopersistence.xmlquantoasanotaçõesquecolocamosnanossaentidadeTarefa.Paratal,usaremos
aclassecomomesmonomedoarquivoXML:Persistence.ElaéresponsáveldecarregaroXMLe
inicializarasconfigurações.ResultadodessaconfiguraçãoéumaEntityManagerFactory:
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");
Estamosprontos para usar o JPA.Antes degravar umaTarefa, precisamosque exista a tabela
correspondentenonossobancodedados.Emvezdecriarmososcriptquedefineoschema(ouDDLdeum banco, data definition language) do nosso banco (os famosos CREATE TABLE ....) podemos
deixar isto a cargo do próprioHibernate. Ao inicializar a EntityManagerFactory também já será
geradaumatabelaTarefaspoisconfiguramosqueobancodeveseratualizadapelapropriedadedo
Hibernate:hbm2ddl.auto.
Caso você esteja fazendo esse passo de casa. É preciso baixar o ZIP do Hibernate ORM 5.x(http://hibernate.org),extraí-lo,ecopiartodososJARsdaspastaslib/requiredelib/jpa.
Paraessaaplicaçãousaremosasseguintesversões:
antlr-2.7.x.jarbyte-buddy-1.9.1x.jardom4j-2.1.x.jarhibernate-commons-annotations-5.1.x.Final.jarhibernate-core-5.4.x.Final.jarjavassist-3.24.x-GA.jarjavax.persistence-api-2.x.jarjaxb-runtime-2.3.x.jarjboss-transaction-api_1.2_spec-1.0.x.Final.jar
14.6USANDOOJPA
14.7 PARA SABER MAIS: CONFIGURANDO O JPA COM HIBERNATEEMCASA
212 14.6USANDOOJPA
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
1. Vamosprepararnossoprojetofj21-tarefascomasdependênciasdoHibernate.
CopieosJARsdoHibernateparaapastaWebContent/WEB-INF/libdoseuprojetodessaforma:
Váatéodiretório21/projeto-tarefas/jpahibernate;
SelecionetodososJARs,cliquecomobotãodireitoeescolhaCopy(ouCTRL+C);
ColetodososJARsnapastaWebContent/WEB-INF/libdoprojetofj21-tarefas(CTRL+V)
2. AnoteaclasseTarefacomoumaentidadedebancodedados.Lembre-sequeessaéumaanotação
dopacotejavax.persistence.
Obs:Nãoapaguenenhumaanotação,apenasadicioneasanotaçõesdoJPA.
@EntitypublicclassTarefa{
}
Namesmaclasseanoteseuatributoidcomochaveprimáriaecomocampodegeraçãoautomática:
@Id@GeneratedValueprivateLongid;
Agora é precisomapear o atributo dataFinalizacao para gravar apenas a data (sem hora) no
banco:
@Temporal(TemporalType.DATE)privateCalendardataFinalizacao;
VocêpodetambémfazerocursodatadessaapostilanaCaelum
14.8 EXERCÍCIOS: CONFIGURANDOO JPA E GERANDOO SCHEMADOBANCO
14.8EXERCÍCIOS:CONFIGURANDOOJPAEGERANDOOSCHEMADOBANCO 213
1. Cliquecomobotãodireitonapastasrcdoprojetodofj21-tarefaseescolhaNew->Folder.
EscolhaMETA-INFcomonomedessapasta.
Váaodiretório21/projeto-tarefas/jpahibernate e copieoarquivopersistence.xml para apastaMETA-INF.
O arquivo é apenas um esboço, falta configurar a unidade de persistência com o provedor,entidadesepropriedades.Adicionedentrodoelementopersistence:
<persistence-unitname="tarefas">
<provider>org.hibernate.ejb.HibernatePersistence</provider><class>br.com.caelum.tarefas.modelo.Tarefa</class>
<properties><propertyname="javax.persistence.jdbc.driver"value="com.mysql.jdbc.Driver"/><propertyname="javax.persistence.jdbc.url"value="jdbc:mysql://localhost/fj21"/><propertyname="javax.persistence.jdbc.user"value="root"/><propertyname="javax.persistence.jdbc.password"value=""/>
<propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQL8Dialect"/>
214 14.8EXERCÍCIOS:CONFIGURANDOOJPAEGERANDOOSCHEMADOBANCO
<propertyname="hibernate.show_sql"value="true"/><propertyname="hibernate.format_sql"value="true"/><propertyname="hibernate.hbm2ddl.auto"value="update"/></properties></persistence-unit>
Asduaspropriedadesshow_sqleformat_sqlfazemcomquetodoSQLgeradopeloHibernate
apareçanoconsole.
1. CrieaclasseGeraTabelasnopacotebr.com.caelum.tarefas.jpa.
packagebr.com.caelum.tarefas.jpa;
//importsomitidos
publicclassGeraTabelas{
publicstaticvoidmain(String[]args){EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");
factory.close();}}
1. CriesuatabelaexecutandoaclasseGeraTabelas.Paraisso,cliquedadireitanocódigoeváem
RunAs->JavaApplication.
NãoéprecisorodaroTomcatparaesseexercício.
2. Agora,abraumanovaabanoTerminaledigitemysql-uroot se nãohouver senhadobanco, oumysql-uroot-p,sehouversenha,informandoasenhacorretaemseguida.Apósisto,digiteusefj21;e em seguida, show tables;. Se seu banco de dados já existia, e não foi preciso criá-lo no passoanterior,vocêvainotarapresençadeumatabelachamadatarefas.Nãoéestaatabelaquequeremos.ProcuramospelatabelaTarefa,comT,emmaiúsculo(igualaonomedaclasseTarefa).
3. (opcional)Casoalgumerrotenhaocorrido,épossívelqueoHibernatetenhalogadoumamensagem,mas você não a viu dado que oLog4J não está configurado.Mesmo que tudo tenha ocorrido demaneiracorreta,émuitoimportanteteroLog4Jconfigurado.
Paraisso,adicionenoarquivolog4j.propertiesdentrodapastasrc para que todoo logdo
nívelinfoouacimasejaenviadoparaoconsoleappenderdoSystem.out(defaultdoconsole):
14.8EXERCÍCIOS:CONFIGURANDOOJPAEGERANDOOSCHEMADOBANCO 215
log4j.logger.org.hibernate=info
ParasecomunicarcomoJPA,precisamosdeumainstânciadeumobjetodotipoEntityManager.
AdquirimosumaEntityManageratravésdafábricajáconhecida:EntityManagerFactory.
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");
EntityManagermanager=factory.createEntityManager();
manager.close();factory.close();
AtravésdeumobjetodotipoEntityManager,épossívelgravarnovosobjetosnobanco.Paraisto,
bastautilizarométodopersistdentrodeumatransação:
Tarefatarefa=newTarefa();tarefa.setDescricao("EstudarJPA");tarefa.setFinalizado(true);tarefa.setDataFinalizacao(Calendar.getInstance());
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");EntityManagermanager=factory.createEntityManager();
manager.getTransaction().begin();manager.persist(tarefa);manager.getTransaction().commit();
System.out.println("IDdatarefa:"+tarefa.getId());
manager.close();
CUIDADOSAOUSAROJPA
AousaroJPAprofissionalmente,fiqueatentocomalgunscuidadosquesãosimples,masnãodada a devida atenção podem gerar gargalos de performance. Abrir EntityManager s
indiscriminadamenteéumdessesproblemas.EssepostdaCaelumindicaoutros:
http://bit.ly/bRc5g
EssesdetalhesdodiaadiaassimcomoJPAcomHibernateavançadosãovistosnocursoFJ-25daCaelum,deHibernateeJPAavançados.
Parabuscarumobjetodadasuachaveprimária,nocasooseuid,utilizamosométodofind,
14.9TRABALHANDOCOMOSOBJETOS:OENTITYMANAGER
Persistindonovosobjetos
Carregarumobjeto
216 14.9TRABALHANDOCOMOSOBJETOS:OENTITYMANAGER
conformeoexemploaseguir:
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");EntityManagermanager=factory.createEntityManager();
Tarefaencontrada=manager.find(Tarefa.class,1L);
System.out.println(encontrada.getDescricao());
1. CrieumaclassechamadaAdicionaTarefanopacotebr.com.caelum.tarefas.jpa,elavaicriar
umobjetoeadicioná-loaobanco:
packagebr.com.caelum.tarefas.jpa;
//importsomitidos
publicclassAdicionaTarefa{
publicstaticvoidmain(String[]args){
Tarefatarefa=newTarefa();tarefa.setDescricao("EstudarJPAeHibernate");tarefa.setFinalizado(true);tarefa.setDataFinalizacao(Calendar.getInstance());
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");EntityManagermanager=factory.createEntityManager();
manager.getTransaction().begin();manager.persist(tarefa);manager.getTransaction().commit();
System.out.println("IDdatarefa:"+tarefa.getId());
manager.close();}}
1. RodeaclasseAdicionaTarefaeadicionealgumastarefasnobanco.Saídapossível:
2. Verifiqueosregistrosnobanco,seconectecomoclientedomysql:
mysql-urootusefj21;select*fromTarefa;
14.10EXERCÍCIOS:GRAVANDOECARREGANDOOBJETOS
14.10EXERCÍCIOS:GRAVANDOECARREGANDOOBJETOS 217
Sehouversenhanobanco, troqueoprimeirocomandopormysql-uroot-p, usando a senha
corretaprobanco.RodenovamenteaclasseAdicionaTarefaeverifiqueobanco.
1. Vamoscarregartarefaspelachaveprimária.
CrieumaclassechamadaCarregaTarefanopacotebr.com.caelum.tarefas.jpa:
packagebr.com.caelum.tarefas.jpa;
//importsomitidos
publicclassCarregaTarefa{
publicstaticvoidmain(String[]args){
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");EntityManagermanager=factory.createEntityManager();
Tarefaencontrada=manager.find(Tarefa.class,1L);System.out.println(encontrada.getDescricao());
manager.close();}}
1. RodeaclasseCarregaTarefaeverifiqueasaída.
Casorecebeuumaexception,verifiqueoiddatarefa.Eledeveexistirnobanco.
218 14.10EXERCÍCIOS:GRAVANDOECARREGANDOOBJETOS
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
Remover e atualizar os objetos com JPA também é muito simples. O EntityManager possui
métodosparacadaoperação.Pararemoveréprecisocarregaraentidadeantesedepoisusarométodoremove:
EntityManagermanager=//abrirumEntityManagerTarefaencontrada=manager.find(Tarefa.class,1L);
manager.getTransaction().begin();manager.remove(encontrada);manager.getTransaction().commit();
Paraatualizarumentidadequejápossuiumidexisteométodomerge,porexemplo:
Tarefatarefa=newTarefa();tarefa.setId(2);//esseidjáexistenobancotarefa.setDescricao("EstudarJPAeHibernate");tarefa.setFinalizado(false);tarefa.setDataFinalizacao(null);
EntityManagermanager=//abrirumEntityManager
manager.getTransaction().begin();manager.merge(tarefa);manager.getTransaction().commit();
OJPApossuiumalinguagemprópriadequeriesparafacilitarabuscadeobjetoschamadadeJPQL.Ocódigoaseguirmostraumapesquisaqueretornatodasastarefasnãofinalizadas:
EntityManagermanager=//abrirumEntityManagerList<Tarefa>lista=manager.createQuery("selecttfromTarefaastwheret.finalizado=false")
Seuslivrosdetecnologiaparecemdoséculopassado?
14.11REMOVENDOEATUALIZANDOOBJETO
14.12BUSCANDOCOMUMACLÁUSULAWHERE
14.11REMOVENDOEATUALIZANDOOBJETO 219
.getResultList();
for(Tarefatarefa:lista){System.out.println(tarefa.getDescricao());}
Também podemos passar um parâmetro para a pesquisa. Para isso, é preciso trabalhar com umobjetoquerepresentaapesquisa(javax.persistence.Query):
EntityManagermanager=//abrirumEntityManager
Queryquery=manager.createQuery("selecttfromTarefaast"+"wheret.finalizado=:paramFinalizado");query.setParameter("paramFinalizado",false);
List<Tarefa>lista=query.getResultList();
Esteéapenasocomeço.OJPAémuitopoderosoe,nocursoFJ-25,veremoscomofazerqueriescomplexas,comjoins,agrupamentos,projeções,demaneiramuitomaissimplesdoquesetivéssemosdeescreverasqueries.Alémdisso,oJPAcomHibernateémuitocustomizável:podemosconfigurá-loparaquegereasqueriesdeacordocomdicasnossas,dessaformaotimizandocasosparticularesemqueasqueriesqueelegeraporpadrãonãosãodesejáveis.
UmaconfusãoquepodeserfeitaaprimeiravistaépensarqueoJPAcomHibernateélento,pois,eleprecisagerarasnossasqueries,lerobjetosesuasanotaçõeseassimpordiante.Naverdade,oHibernatefazumasériedeotimizaçõesinternamentequefazemcomqueoimpactodessastarefassejapróximoanada.Portanto,oHibernateé,sim,performático,ehojeemdiapodeserutilizadoemqualquerprojetoquesetrabalhacombancodedados.
1. CrieumaclassechamadaBuscaTarefasnopacotebr.com.caelum.tarefas.jpa:
Cuidado,aclasseQueryvemdopacotejavax.persistence:
packagebr.com.caelum.tarefas.jpa;
importjavax.persistence.Query;//outrosimportsomitidos
publicclassBuscaTarefas{
publicstaticvoidmain(String[]args){
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");EntityManagermanager=factory.createEntityManager();
//cuidado,useoimportjavax.persistence.QueryQueryquery=manager.createQuery("selecttfromTarefaast"+"wheret.finalizado=:paramFinalizado");
14.13EXERCÍCIOS:BUSCANDOCOMJPQL
220 14.13EXERCÍCIOS:BUSCANDOCOMJPQL
query.setParameter("paramFinalizado",true);
List<Tarefa>lista=query.getResultList();
for(Tarefat:lista){System.out.println(t.getDescricao());}
manager.close();}}
1. RodeaclasseBuscaTarefaseverifiqueasaída.
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
Agoraéamelhorhoradeaprenderalgonovo
14.13EXERCÍCIOS:BUSCANDOCOMJPQL 221
CAPÍTULO15
"Anucaéummistérioparaavista."--PaulValéry
O curso FJ-21, Java para desenvolvimento Web, procura abordar as principais tecnologias domercadoWebemJavadesdeseusfundamentosatéosframeworksmaisusados.
Maseagora,paraondedirecionarseusestudosesuacarreira?
Ospróximoscapítulosdaapostilasãoapêndicesextrasparaexpandirseuestudoemalgumasáreasquepodemserdeseuinteresse:
JPAeSpring-MostracomointegrarJPAcomSpringparautilizarJPAnacamadadepersistêncianoprojetofj21-tarefas.
TópicosdaServletAPI-AbordaváriostópicossobreServletseJSPsquenãoabordamosantes.São detalhes a mais e alguns recursos mais avançados. É interessante para expandir seuconhecimentoeajudarentendermelhoropadrãoServlets.
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
OmercadodeframeworksJavaéimenso.Hámuitasopçõesboasdisponíveisnomercadoemuitos
EAGORA?
15.1OSAPÊNDICESDESSAAPOSTILA
VocêpodetambémfazerocursodatadessaapostilanaCaelum
15.2FRAMEWORKSWEB
222 15EAGORA?
pontosaconsiderarnaadoçãoouestudodealgumnovoframework.
OStrutscomcertezaéoframeworkcommaiorunanimidadenomercado,emespecialporcausadesuaversão1.x.Aversão2.0nãotemtantaforçamaséumdosmaisimportantes.
OVRaptor,criadonaUSPem2004emantidohojepelaCaelumeporumagrandecomunidade,não tem o tamanho do mercado de outros grandes frameworks. Mas tem a vantagem da extremasimplicidadeegrandeprodutividade,alémdeserbemfocadonomercadobrasileiro,comdocumentaçãoemportuguêsemuitomaterialdisponível.ACaeluminclusivedisponibilizaumcursodeVRaptor,oFJ-28quepossuisuaapostiladisponívelparadownloadgratuitonaInternetem:
http://www.caelum.com.br/apostilas
Um dos frameworks mais usados hoje é o JavaServer Faces - JSF. Seu grande apelo é ser oframeworkoficialdoJavaEEparaWeb,enquantoquetodososoutrossãodeterceiros.OJSFtemaindamuitascaracterísticasdiferentesdoStrutsoudoVRaptorquevimosnessecurso.
Emespecial,oJSFéditoumframeworkcomponent-based,enquantoqueStruts(1e2)eVRaptorsãoditosrequest-basedouaction-based.AideiaprincipaldeumframeworkdecomponenteséabstrairmuitosdosconceitosdaWebedoprotocoloHTTPprovendoumaformadeprogramaçãomaisparecidacomprogramaçãoparaDesktop.OJSFtemcomponentesvisuaisricosjáprontos,funcionaatravésdetratamentodeeventoseéstateful(aocontráriodaWeb"normal"ondetudoéstateless).NaCaelum,ocursoFJ-26tratadeJavaServerFaces:
http://www.caelum.com.br/curso/fj26
Maso JSFnão é único frameworkbaseado em componentes.Há outras opções (menos famosas)comooGoogleWebToolkit-GWTouoApacheWicket.Damesma forma,háoutros frameworksrequest-based alémdeStrutseVRaptor, comooStripesouoSpringMVC.NaCaelum,oSpring eSpringMVCétratadoemdetalhesnocursoFJ-27,juntocomoutrasfuncionalidadesdoSpring:
http://www.caelum.com.br/curso/fj27
Todaessapluralidadedeframeworkséboaparafomentarcompetiçãoeinovaçãonomercado,maspodeconfundirmuitooprogramadorqueestá iniciandonessemeio.Umbomcaminhoa seguir apósesse curso FJ-21 é continuar aprofundando seus conhecimentos no SpringMVC e noVRaptor (comajudadaapostilaaberta)epartirdepoisparaoestudodoJSF.Osoutros frameworksvocêvaiacabareventualmente encontrando algum dia em sua carreira, mas não se preocupe em aprender todos nocomeço.
Quando falamos de frameworks voltados para persistência e bancos de dados, felizmente, não há
15.3FRAMEWORKSDEPERSISTÊNCIA
15.3FRAMEWORKSDEPERSISTÊNCIA 223
tanta dúvida quanto no mundo dos frameworksWeb. OHibernate é praticamente unanimidade nomercadoJava,principalmenteseusadocomoimplementaçãodaJPA.Háoutraspossibilidades,comooToplink, o EclipseLink, oOpenJPA, oDataNucleus, o JDO, o iBatis etc.Mas oHibernate é omaisimportante.
NaCaelum,abordamosJPAeHibernateavançadonocursoFJ-25:
http://www.caelum.com.br/curso/fj25
HáaindalivrosedocumentaçõesdisponíveissobreHibernate.RecomendamosfortementequevocêaprofundeseusestudosnoHibernatequeémuitousadoepedidonomercadoJavahoje.
ACaelumdisponibiliza para downloadgratuito algumasdas apostilas de seus treinamentos.Vocêpodebaixardiretonosite:
http://www.caelum.com.br/apostilas
OBlogdaCaeluméaindaoutraformadeacompanharnovidadeseexpandirseusconhecimentos:
http://blog.caelum.com.br
Diversasrevistas,noBrasilenoexterior,estudamomundoJavacomoninguémepodemajudaroinicianteaconhecermuitodoqueestáacontecendoláforanasaplicaçõescomerciais.
LembrandoqueaeditoraCasadoCódigopossuilivrosdeJSFeJPAquepodemteauxiliartambémnacontinuaçãoereforçodosseusestudos:
http://www.CasaDoCodigo.com.br
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
15.4ONDESEGUIRSEUSESTUDOS
Seuslivrosdetecnologiaparecemdoséculopassado?
224 15.4ONDESEGUIRSEUSESTUDOS
CAPÍTULO16
"Ocaminhodoinfernoestápavimentadodeboasintenções."--Marx
Nestecapítulo,vocêaprenderáa:
IntegrarJPAcomSpring;InjetaroEntityManagerdentrodoDAO;GerenciarastransaçõescomSpring;
DentreasdiversascaracterísticasdoSpringumadasquemaischamaaatençãoéaintegraçãonativacomdiversastecnologiasimportantesdomercado.PodemoscitaroHibernateeoJPA.
VimosqueoSpringéumcontainerquecontrolaobjetos(oucomponentes),administrandoociclodavidadeles, inclusive ligandounsaosoutros (amarrando-os).NocasodoJPA,queremosqueoSpringcuidedaaberturaedofechamentodaEntityManagerFactoryedoEntityManager.Comisso,todosos componentes do Spring podem receber como dependência um EntityManager , mas agora
controladopeloSpring.Novamenteaplicandoainversãodecontrole.
Mas a inversão de controle não se limita a inicialização de objetos. O Spring também tira dodesenvolvedoraresponsabilidadedecontrolaratransação.AodelegarocontroledoEntityManager
paraoSpring,eleconsegueabrirefechartransaçõesautomaticamente.
VejacomoéainicializaçãodoJPAsemaajudadoSpring:
EntityManagerFactoryfactory=Persistence.createEntityManagerFactory("tarefas");EntityManagermanager=factory.createEntityManager();
manager.getTransaction().begin();
//aquiusaoEntityManager
manager.getTransaction().commit();manager.close();
NessepequenotrechodecódigopodemosvercomoétrabalhosoinicializaroJPAmanualmente.É
APÊNDICE-INTEGRAÇÃODOSPRINGCOMJPA
16.1GERENCIANDOOENTITYMANAGER
16APÊNDICE-INTEGRAÇÃODOSPRINGCOMJPA 225
precisoabrire fechar todosos recursospara realmentecomeçarausaroEntityManager.OSpring
deveassumiressasresponsabilidadesefacilitarassimousodoJPA.
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
Para integrar-se com o JPA, o Spring disponibiliza umBean que devemos cadastrar no arquivoXML. Ele representa a EntityManagerFactory, mas agora gerenciada pelo Spring. Ou seja, toda
inicializaçãodafábricaficaaoencargodoSpring:
<beanid="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><propertyname="dataSource"ref="mysqlDataSource"/><propertyname="jpaVendorAdapter"><beanclass="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/></property></bean>
ReparequeoBeandefineHibernate como implementaçãodoJPAe recebeamysqlDataSource
quejádefinimosanteriormentedentrodoXMLdoSpring.ComoanossaDatasourcejásabeosdados
dodriver,loginesenha,oarquivopersistence.xmldoJPAtambémficamaissimples:
<persistencexmlns="http://java.sun.com/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"version="2.0">
<persistence-unitname="tarefas">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>br.com.caelum.tarefas.modelo.Tarefa</class>
<properties>
JáconheceoscursosonlineAlura?
16.2CONFIGURANDOOJPANOSPRING
226 16.2CONFIGURANDOOJPANOSPRING
<!--SEMaspropriedadesURL,login,senhaedriver-->
<propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQL5InnoDBDialect"/><propertyname="hibernate.show_sql"value="true"/><propertyname="hibernate.format_sql"value="true"/><propertyname="hibernate.hbm2ddl.auto"value="update"/></properties></persistence-unit>
</persistence>
ComJPAconfiguradopodemosaproveitarainversãodecontroleeinjetaroEntityManagerdentro
dequalquercomponenteadministradopeloSpring.
VamoscriarumaclasseJpaTarefaDao para usar oEntityManager.A classeJpaTarefaDao
precisadoEntityManager.ComoJPAéumaespecificação,oSpringtambémaproveitaumaanotação
deespecificaçãoparareceberadependência.Nessecasonãopodemosusaraanotação@Autowireddo
[email protected]ção@PersistenceContext não funciona
comconstrutores,exigindoumoutropontodeinjeção.Usaremosoatributoparadeclararadependência:
@RepositorypublicclassJpaTarefaDao{
@PersistenceContextprivateEntityManagermanager;
//semconstrutor
//aquivemosmétodos}
Nos métodos do JpaTarefaDao faremos o uso do EntityManager usando os métodos já
conhecidoscomopersist(..)parapersistirumatarefaouremove(..)pararemover:
@RepositorypublicclassJpaTarefaDao{
@PersistenceContextprivateEntityManagermanager;
//semconstrutor
publicvoidadiciona(Tarefatarefa){manager.persist(tarefa);}
publicvoidaltera(Tarefatarefa){manager.merge(tarefa);}
publicList<Tarefa>lista(){
16.3INJETANDOOENTITYMANAGER
16.3INJETANDOOENTITYMANAGER 227
returnmanager.createQuery("selecttfromTarefat").getResultList();}
publicTarefabuscaPorId(Longid){returnmanager.find(Tarefa.class,id);}
publicvoidremove(Tarefatarefa){TarefatarefaARemover=buscaPorId(tarefa.getId());manager.remove(tarefaARemover);}
publicvoidfinaliza(Longid){Tarefatarefa=buscaPorId(id);tarefa.setFinalizado(true);tarefa.setDataFinalizacao(Calendar.getInstance());manager.merge(tarefa);}}
A implementação do JpaTarefaDao ficou muito mais simples se comparada com o
JdbcTarefaDao,emalgunsmétodoséapenasumalinhadecódigo.
VoltandoparanossaclasseTarefasControllervamoslembrarqueinjetamosoJdbcTarefaDao
paratrabalharcomobancodedados:
@ControllerpublicclassTarefasController{
privatefinalJdbcTarefaDaodao;
@AutowiredpublicTarefasController(JdbcTarefaDaodao){this.dao=dao;}
@RequestMapping("mostraTarefa")publicStringmostra(Longid,Modelmodel){model.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/mostra";}
//outrosmétodosomitidos}
Quandoolhamosparaosmétodospropriamenteditos,porexemplo,mostra,percebemosqueoque
é realmente necessário para executá-lo é algum DAO. A lógica em si é independente da instânciaespecíficaqueestamosinstanciando,ousejaparaaclasseTarefasControllernãoimportaseestamos
usando JDBC ou JPA.Queremos um desacoplamento da implementação doDAO especifico, e algodevedecidirqualimplementaçãousaremosporbaixodospanos.
Oproblemadesse tipodechamadaéque,nodiaemqueprecisarmosmudara implementaçãodoDAO,precisaremosmudarnossaclasse.Agoraimaginequetenhamos10controladoresnosistemaque
16.4BAIXOACOPLAMENTOPELOUSODEINTERFACE
228 16.4BAIXOACOPLAMENTOPELOUSODEINTERFACE
usem nosso JdbcTarefaDao. Se todas usam a implementação específica, quando formos mudar a
implementaçãoparausarJPAparapersistênciaporexemplo,digamos,JpaTarefaDao, precisaremos
mudaremvárioslugares.
OqueprecisamosentãoéapenasumaTarefaDaodentrodaclasseTarefasController, então
vamosdefinir(extrair)umanovainterfaceTarefaDao:
publicinterfaceTarefaDao{
TarefabuscaPorId(Longid);List<Tarefa>lista();voidadiciona(Tarefat);voidaltera(Tarefat);voidremove(Tarefat);voidfinaliza(Longid);}
EaclasseJdbcTarefaDaoimplementaráessainterface:
@RepositorypublicclassJdbcTarefaDaoimplementsTarefaDao{
//implementaçãodonossodaousandojdbc}
Dessa maneira vamos injetar uma implementação compatível com a interface dentro da classeTarefasController
@ControllerpublicclassTarefasController{
privateTarefaDaodao;//usandoainterfaceapenas!
@AutowiredpublicTarefasController(TarefaDaodao){this.dao=dao;}
//métodosomitidos}
Agora a vantagem dessa abordagem é que podemos usar uma outra implementação da interfaceTarefaDaosemalteraraclasseTarefasController,nonossocasovamosusarJpaTarefaDao:
@RepositorypublicclassJpaTarefaDaoimplementsTarefaDao{
@PersistenceContextEntityManagermanager;
//semconstrutor
//métodosomitidos}
Repare que o DAO também vai implementar a interface TarefaDao . Assim temos duas
16.4BAIXOACOPLAMENTOPELOUSODEINTERFACE 229
implementaçõesdamesmainterface:
Como o Spring não sabe qual das duas implementações deve ser utilizada é preciso qualificar adependência:
@ControllerpublicclassTarefasController{
privateTarefaDaodao;//usaapenasainterface!
@Autowired@Qualifier("jpaTarefaDao")publicTarefasController(TarefaDaodao){this.dao=dao;}
//métodosomitidos}
DessamaneiraoSpringinjetaráoJpaTarefaDao.
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
Paraosuporteà transaçãoserativadoprecisamosfazerduasconfiguraçõesnoXMLdoSpring.Aprimeiraéhabilitarogerenciadordetransação(TransactionManager).Porém,comooSpringpodecontrolar JPA, Hibernate e outros, devemos configurar o gerenciador exatamente para uma dessastecnologias. No caso do JPA, a única dependência que o JpaTransactionManager precisa é uma
entityManagerFactory:
<beanid="transactionManager"
VocêpodetambémfazerocursodatadessaapostilanaCaelum
16.5GERENCIANDOATRANSAÇÃO
230 16.5GERENCIANDOATRANSAÇÃO
class="org.springframework.orm.jpa.JpaTransactionManager"><propertyname="entityManagerFactory"ref="entityManagerFactory"/></bean>
entityManagerFactoryéonomedoBeanconfiguradoanteriormente:
Asegundaparteéavisarqueocontroledetransaçõesseráfeitoviaanotação,parecidocomaformadehabilitarousodeanotaçõesparaoSpringMVC.
<tx:annotation-driven/>
Por fim, podemos usar o gerenciamento da transação dentro das nossas classes. Aqui ficamuitosimples,ésóusaraanotação@Transactionalnométodoqueprecisadeumatransação,porexemplo
nométodoadicionadaclasseTarefasController:
@Transactional@RequestMapping("adicionaTarefa")publicStringadiciona(@ValidTarefatarefa,BindingResultresult){
if(result.hasFieldErrors("descricao")){return"tarefa/formulario";}
dao.adiciona(tarefa);return"redirect:listaTarefas";}
Amesma anotação também pode ser utilizada na classe. Isso significa que todos osmétodos daclasseserãoexecutadosdentrodeumatransação:
@Transactional@ControllerpublicclassTarefasController{
Repareaquiabelezadainversãodecontrole.Nãohánecessidadedechamarbegin(),commit()
ourollback(),bastausar@Transactional eoSpringassumea responsabilidadeemgerenciar a
transação.
Há um problema ainda, usando o gerenciamento de transação pelo Spring exige a presença doconstrutorpadrãosemparâmetros.Vamostrocaraquitambémopontodeinjeçãodoconstrutorparaoatributo.Aclassecompletaficacomo:
packagebr.com.caelum.tarefas.controller;
//imports
@Controller@TransactionalpublicclassTarefasController{
@AutowiredTarefaDaodao;
@RequestMapping("novaTarefa")publicStringform(){
16.5GERENCIANDOATRANSAÇÃO 231
return"tarefa/formulario";}
@RequestMapping("adicionaTarefa")publicStringadiciona(@ValidTarefatarefa,BindingResultresult){
if(result.hasFieldErrors("descricao")){return"tarefa/formulario";}
dao.adiciona(tarefa);return"tarefa/adicionada";}
@RequestMapping("listaTarefas")publicStringlista(Modelmodel){model.addAttribute("tarefas",dao.lista());return"tarefa/lista";}
@RequestMapping("removeTarefa")publicStringremove(Tarefatarefa){dao.remove(tarefa);return"redirect:listaTarefas";}
@RequestMapping("mostraTarefa")publicStringmostra(Longid,Modelmodel){model.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/mostra";}
@RequestMapping("alteraTarefa")publicStringaltera(Tarefatarefa){dao.altera(tarefa);return"redirect:listaTarefas";}
@RequestMapping("finalizaTarefa")publicStringfinaliza(Longid,Modelmodel){dao.finaliza(id);model.addAttribute("tarefa",dao.buscaPorId(id));return"tarefa/finalizada";}}
1. VamosusarJPAatravésdoSpring.ParaissoéprecisocopiarosJARseguintes:
spring-orm-5.2.x.RELEASE.jar
spring-tx-5.2.x.RELEASE.jar
Paraisso:
VáaoDesktop,eentrenodiretório21/projeto-tarefas/springjpa.Copie os JARs ( CTRL+C ) e cole-o ( CTRL+V ) dentro de workspace/fj21-
16.6EXERCÍCIOS:INTEGRANDOJPACOMSPRING
232 16.6EXERCÍCIOS:INTEGRANDOJPACOMSPRING
tarefas/WebContent/WEB-INF/lib
2. No projeto fj21-tarefas , vá a pasta WebContent/WEB-INF e abra o arquivo spring-
context.xml.
NeleéprecisodeclararoentityManagerFactoryeogerenciadordetransações.
Você pode copiar essa parte do XML do arquivo Caelum/21/apendice/spring-context-
confs.xml.Copieoconteúdodoarquivoecoledentrodospring-context.xml:
<!--gerenciamentodejpapelospring--><beanid="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><propertyname="dataSource"ref="mysqlDataSource"/><propertyname="jpaVendorAdapter"><beanclass="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/></property></bean>
<!--gerenciamentodatransaçãopelospring--><beanid="transactionManager"class="org.springframework.orm.jpa.JpaTransactionManager"><propertyname="entityManagerFactory"ref="entityManagerFactory"/></bean>
<tx:annotation-driven/>
Comessasdeclarações,SpringgerenciaaEntityManagerFactoryehabilitaogerenciamentode
transações. Podemos também remover ou comentar as configurações de conexão nopersistence.xml:
<persistence-unitname="tarefas">
<provider>org.hibernate.ejb.HibernatePersistence</provider><class>br.com.caelum.tarefas.modelo.Tarefa</class>
<properties><!--<propertyname="javax.persistence.jdbc.driver"value="com.mysql.jdbc.Driver"/><propertyname="javax.persistence.jdbc.url"value="jdbc:mysql://localhost/fj21"/><propertyname="javax.persistence.jdbc.user"value="root"/><propertyname="javax.persistence.jdbc.password"value=""/>-->
<propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQL8Dialect"/><propertyname="hibernate.show_sql"value="true"/><propertyname="hibernate.format_sql"value="true"/><propertyname="hibernate.hbm2ddl.auto"value="update"/></properties></persistence-unit>
1. O próximo passo é criar a interface TarefaDao. Crie uma nova interface dentro do package
br.com.caelum.tarefas.dao:
16.6EXERCÍCIOS:INTEGRANDOJPACOMSPRING 233
packagebr.com.caelum.tarefas.dao;
//importsomitidos
publicinterfaceTarefaDao{
TarefabuscaPorId(Longid);List<Tarefa>lista();voidadiciona(Tarefat);voidaltera(Tarefat);voidremove(Tarefat);voidfinaliza(Longid);}
1. Crie uma classe JpaTarefaDao que recebe o EntityManager . Implemente a interface
TarefaDaocomtodososmétodos.
packagebr.com.caelum.tarefas.dao;
//importsomitidos
@RepositorypublicclassJpaTarefaDaoimplementsTarefaDao{
@PersistenceContextEntityManagermanager;
//semconstrutor
publicvoidadiciona(Tarefatarefa){manager.persist(tarefa);}
publicvoidaltera(Tarefatarefa){manager.merge(tarefa);}
publicList<Tarefa>lista(){returnmanager.createQuery("selecttfromTarefat").getResultList();}
publicTarefabuscaPorId(Longid){returnmanager.find(Tarefa.class,id);}
publicvoidremove(Tarefatarefa){TarefatarefaARemover=buscaPorId(tarefa.getId());manager.remove(tarefaARemover);}
publicvoidfinaliza(Longid){Tarefatarefa=buscaPorId(id);tarefa.setFinalizado(true);tarefa.setDataFinalizacao(Calendar.getInstance());}}
1. AgorafaçacomqueaclasseJdbcTarefaDaoimplementeainterfaceTarefaDao, adaptandoos
métodosexistentescasosejanecessário:
234 16.6EXERCÍCIOS:INTEGRANDOJPACOMSPRING
@RepositorypublicclassJdbcTarefaDaoimplementsTarefaDao{
//implementaçãodonossodaousandojdbc}
1. Altere a classe TarefasController , use a interface TarefaDao apenas. Assim a classe
TarefasControllerficadesacopladodaimplementação.Nãoesquecedeapagaroconstrutor:
@ControllerpublicclassTarefasController{
@AutowiredTarefaDaodao;//usaapenasainterface!
//semconstrutor
//métodosomitidos,semmudança}
1. Como temos dois possíveis candidatos para o processo de injeção que implementam a interfaceTarefaDaooSpringnãosabequalescolher.Temosqueadicionarumqualificadorna injeçãoda
dependênciaquediráqualimplementaçãoqueremosusar:
@Autowired@Qualifier("jpaTarefaDao")TarefaDaodao;//usaapenasainterface!
1. Por fim, vamos habilitar o gerenciamento de transação para qualquer método da classeTarefasController.
Abra a classe e use a anotação @Transactional (do pacote
org.springframework.transaction.annotation)emcimadaclasse:
@Transactional@ControllerpublicclassTarefasController{
2. ReinicieoTomcateacesseaaplicaçãodetarefasemhttp://localhost:8080/fj21-tarefas/listaTarefas.
1. Podemos integrar a entidade User ao JPA, para isso primeiro temos que colocar as anotaçõesnecessáriasnanossaclassededomínio:
//importsomitidos
@EntitypublicclassUsuario{
@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;
16.7 EXERCÍCIOS OPCIONAIS: INTEGRANDO A ENTIDADE USERCOMOJPA
16.7EXERCÍCIOSOPCIONAIS:INTEGRANDOAENTIDADEUSERCOMOJPA 235
//atributosemétodosomitidos}
1. AgoravamosdefinirumanovainterfacechamadaUsuarioDao:
packagebr.com.caelum.tarefas.dao;
importbr.com.caelum.tarefas.modelo.Usuario;
publicinterfaceUsuarioDao{publicbooleanexisteUsuario(Usuariousuario);}
1. DiremosqueaclasseJdbcUsuarioDaoimplementaessainterface:
//importsomitidos
@RepositorypublicclassJdbcUsuarioDaoimplementsUsuarioDao{//métodosomitidos}
1. AgoracriaremosaclasseJpaUsuarioDaoqueimplementaráainterfacequeacabamosdecriar:
packagebr.com.caelum.tarefas.dao;
importorg.springframework.stereotype.Repository;importbr.com.caelum.tarefas.modelo.Usuario;
@RepositorypublicclassJpaUsuarioDaoimplementsUsuarioDao{
@OverridepublicbooleanexisteUsuario(Usuariousuario){//TODOAuto-generatedmethodstubreturnfalse;}
}
Precisamos agora adaptar ométodoexisteUsuario daJdbcUsuarioDao para que ele use o
EntityManager . Faremos a injeção do EntityManager através da anotação
@PersistenceContext,emseguidamontaremosumaquerybuscaránobancodedadosumalista
deusuárioscomumlogineumasenhafixa:
@RepositorypublicclassJpaUsuarioDaoimplementsUsuarioDao{
@PersistenceContextEntityManagermanager;
@OverridepublicbooleanexisteUsuario(Usuariousuario){manager.createQuery("selectufromUsuariou"+"whereu.login=:loginandu.senha=:senha",Usuario.class).setParameter("login",usuario.getLogin()).setParameter("senha",usuario.getSenha())}
236 16.7EXERCÍCIOSOPCIONAIS:INTEGRANDOAENTIDADEUSERCOMOJPA
}
Diremos ao JPA que queremos uma lista como resultado, adicionando o méotodogetResultList()ecapturaremosesseretornoemumavariávelchamadalistaDeUsuarios:
@RepositorypublicclassJpaUsuarioDaoimplementsUsuarioDao{
@PersistenceContextEntityManagermanager;
@OverridepublicbooleanexisteUsuario(Usuariousuario){List<Usuario>listaDeUsuarios=manager.createQuery("selectufromUsuariou"+"whereu.login=:loginandu.senha=:senha",Usuario.class).setParameter("login",usuario.getLogin()).setParameter("senha",usuario.getSenha()).getResultList();}}
SetivermospassadoascredenciaiscorretasdeumusuárioalistalistaDeUsuariosdeveráconteraomenos1registro,ouseja,devemosretornartrue;casonenhumusuáriosejaencontradoentãoa
listaévazia,eretornaremosfalse.Ométodocompletoficaráassim:
@PersistenceContextEntityManagermanager;
@OverridepublicbooleanexisteUsuario(Usuariousuario){List<Usuario>listaDeUsuarios=manager.createQuery("selectufromUsuariou"+"whereu.login=:loginandu.senha=:senha",Usuario.class).setParameter("login",usuario.getLogin()).setParameter("senha",usuario.getSenha()).getResultList();
if(listaDeUsuarios.isEmpty())returnfalse;elsereturntrue;}
1. AgoratemosquesubstuirnoLoginControlleraclassepelainterface,pedirparaoSpringfazera
injeção da dependência e auxiliar ele na escolha de qual implementação da interface deverá serusada,atravésdaanotação@Qualifier("jpaUsuarioDao"):
//importsomitidos
@ControllerpublicclassLoginController{
@Autowired@Qualifier("jpaUsuarioDao")privateUsuarioDaodao;
//métodosomitidos}
16.7EXERCÍCIOSOPCIONAIS:INTEGRANDOAENTIDADEUSERCOMOJPA 237
Caso a tabelaUsuario não exista, descomenteo código comentado empersistence.xml e
adicioneaclasseUsuario,deformasemelhanteaadiçãodaclasseTarefa
Paracadastrarumnovousuário,noterminalabertonomysql,useocomandoabaixo:
insertintoUsuario(login,senha)values('seu_usuario','sua_senha');
1. ReinicieoTomcatetenteselogarnaaplicaçãoemhttp://localhost:8080/fj21-tarefas/loginForm.
Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.
CasadoCódigo,LivrosdeTecnologia.
Seuslivrosdetecnologiaparecemdoséculopassado?
238 16.7EXERCÍCIOSOPCIONAIS:INTEGRANDOAENTIDADEUSERCOMOJPA
CAPÍTULO17
"Measuring programming progress by lines of code is like measuring aircraft building progress byweight."--BillGates
Este capítulo aborda vários outros pequenos assuntos da Servlet API ainda não tratados, muitosdelesimportantesparaacertificaçãoSCWCD.
Podemosconfigurarnoweb.xmlalgunsparâmetrosquedepoisvamosleremnossaaplicação.Umaforma é passarmos parâmetros especificamente para uma Servlet ou um filtro usando a tag <init-
param>comonosexemplosabaixo:
<!--emservlet--><servlet><servlet-name>MinhaServlet</servlet-name><servlet-class>pacote.MinhaServlet</servlet-class><init-param><param-name>nome</param-name><param-value>valor</param-value></init-param></servlet>
<!--emfilter--><filter><filter-name>MeuFiltro</filter-name><filter-class>pacote.MeuFiltro</filter-class><init-param><param-name>nome</param-name><param-value>valor</param-value></init-param></filter>
Podemos, inclusive, ter vários parâmetros namesma servlet ou filtro.Depois, no código Java daServletoudofiltroespecífico,podemosrecuperaressesparâmetrosusando:
//emservletStringvalor=getServletConfig().getInitParameter("nome");
//emfiltro,noinitStringvalor=filterConfig.getInitParameter("nome")
Outra possibilidade é configurar parâmetros para o contexto inteiro e não apenas uma servletespecífica.Podemosfazerissocomatag<context-param>,comoabaixo:
APÊNDICE-TÓPICOSDASERVLETAPI
17.1INIT-PARAMSECONTEXT-PARAMS
17APÊNDICE-TÓPICOSDASERVLETAPI 239
<context-param><param-name>nome</param-name><param-value>param</param-value></context-param>
E,nocódigoJavadeumaServlet,porexemplo:
Stringvalor=getServletContext().getInitParameter("nome");
Muitos frameworks usam parâmetros no web.xml para configurar. O VRaptor e o Spring sãoexemplosdeusodesse recurso.Maspodemosusar issoemnossas aplicações tambémpara retirardocódigoJavacertasconfiguraçõesparametrizáveis.
Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende
Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!
ConheçaaAluraCursosOnline.
Épossívelconfigurarnoweb.xmlqualarquivodeveserchamadoquandoalguémacessarumaURLraiznoservidor,comoporexemplo:
http://localhost:8080/fj-21-agenda/http://localhost:8080/fj-21-agenda/uma-pasta/
Sãoosarquivosindexquenormalmenteusamosemoutrasplataformas.MasnoJavaEEpodemoslistarosnomesdearquivosquedesejamosque sejamoswelcomefiles.Basta defini-los noXMLeoservidorvaitentá-losnaordemdedeclaração:
<welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list>
Comodizerqualoencodingdenossosarquivosjspdeumamaneiraglobal?Comonosprotegerde
Agoraéamelhorhoradeaprenderalgonovo
17.2WELCOME-FILE-LIST
17.3PROPRIEDADESDEPÁGINASJSP
240 17.2WELCOME-FILE-LIST
programadores iniciantes em nossa equipe e desabilitar o código scriptlet? Como adicionar um
arquivo antes e/ou depois de todos os arquivos JSPs? Ou de todos os JSPs dentro de determinadodiretório?
Pararesponderessaseoutrasperguntas,aAPIdejspresolveupossibilitardefiniralgumastagsnonossoarquivoweb.xml.
Porexemplo,paradesativarscripting(osscriptlets):
<scripting-invalid>true</scripting-invalid>
Ativarexpressionlanguage(quejávemativado):
<el-ignored>false</el-ignored>
Determinaroencodingdosarquivosdeumamaneiragenérica:
<page-encoding>UTF-8</page-encoding>
IncluirarquivosestaticamenteantesedepoisdeseusJSPs:
<include-prelude>/antes.jspf</include-prelude><include-coda>/depois.jspf</include-coda>
OcódigoaseguirmostracomoaplicartaiscaracterísticasparatodososJSPs,reparequeatagurl-
patterndeterminaogrupodearquivoscujosatributosserãoalterados:
<jsp-config><jsp-property-group><display-name>todososjsps</display-name><description>configuracoesdetodososjsps</description><url-pattern>*.jsp</url-pattern><page-encoding>UTF-8</page-encoding><scripting-invalid>true</scripting-invalid><el-ignored>false</el-ignored><include-prelude>/antes.jspf</include-prelude><include-coda>/depois.jspf</include-coda></jsp-property-group></jsp-config>
ExisteumamaneiraemumarquivoJSPdeincluirumoutroarquivoestaticamente.Istofazcomqueo arquivo a ser incluído seja literalmente copiado e colado dentro do seu arquivo antes da primeirainterpretação(compilação)doseujsp.
A vantagem é que como a inclusão é feita uma única vez antes do arquivo ser compilado, essainclusão é extremamente rápida, porém vale lembrar que o arquivo incluído pode ou não funcionarseparadamente.
<%@includefile="outra_pagina.jsp"%>
17.4INCLUSÃOESTÁTICADEARQUIVOS
17.4INCLUSÃOESTÁTICADEARQUIVOS 241
Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.
CasadoCódigo,ebookcompreçodeebook.
ComotratarpossíveisexceptionsemnossapáginaJSP?NossosexercíciosdelistagemdecontatostantocomscriptletsquantocomJSTLusamoContatoDaoquepodelançarumaexceçãoseobancode
dadosestiverforadoar,porexemplo.Comotratar?
Se nosso JSP é um imenso scriptlet de código Java, o tratamento é o mesmo de códigos Javanormais:trycatch:
<html><%try{ContatoDaodao=newContatoDao();//...etc...}catch(Exceptionex){%>Ocorreualgumerroaoacessarobancodedados.<%}%></html>
Nãoparecemuitoelegante.Masequandousamostags,háumaformamelhor?Poderíamosusaratagc:catch,comomesmotipodeproblemadasoluçãoanterior:
<c:catchvar="error"><jsp:useBeanid="dao"class="br.com.caelum.jdbc.dao.ContatoDao"/><c:forEachvar="contato"items="${dao.lista}">....</c:forEach></c:catch><c:iftest="${notemptyerror}">Ocorreualgumerroaoacessarobancodedados.</c:if>
ReparequeaprópriaJSTLnosapresentaumasoluçãoquenãosemostraboaparaessetipodeerro
EditoraCasadoCódigocomlivrosdeumaformadiferente
17.5TRATAMENTODEERROEMJSP
242 17.5TRATAMENTODEERROEMJSP
quequeremos tratar.É importantedeixarclaroquedesejamostrataro tipodeerroquenão temvolta,devemosmostrarumamensagemdeerroparaoclienteepronto,porexemploquandoaconexãocomobancocaiouquandoocorrealgumerronoservidor.
Quandoestávamos trabalhandocomServlets,haviauma solução simplese elegante:não tratar asexceçõesde formaespalhadamas simcriar umapágina centralizadade tratamentode erros.Naquelecaso,conseguimosissocomo<error-page>.
ComJSPs,conseguimosomesmoresultadomassemXML.UsamosumadiretivanotopodoJSPque indica qual é a página central de tratamento de erro. E nesse caso não precisamos nem detry/catchnemde<c:catch>:
<%@pageerrorPage="/erro.html"%>...<jsp:useBeanid="dao"class="br.com.caelum.jdbc.dao.ContatoDao"/>...
ParalertodososparâmetrosdorequestbastaacessarométodogetParameterMapdorequest.
Map<String,Object>parametros=request.getParameterMap();for(Stringparametro:parametros.keySet()){//facaalgocomoparametro}
ÀsvezesnãoésimplestrabalharcomlinkspoistemosquepensarnaURLqueoclienteacessaaovisualizaranossapágina.
AJSTLresolveesseproblema:supondoqueasuaaplicaçãosechamejspteste,ocódigoabaixo
geraastring/jspteste/imagem/banner.jpg.
<c:urlvalue="/imagem/banner.jpg"/>
Ébastanteútilaomontarmenusúnicosincluídosemváriaspáginasequeprecisamlidarcomlinksabsolutos.
17.6DESCOBRINDOTODOSOSPARÂMETROSDOREQUEST
17.7TRABALHANDOCOMLINKSCOMAC:URL
17.6DESCOBRINDOTODOSOSPARÂMETROSDOREQUEST 243
AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,
Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!
ConheçaoscursosonlineAlura.
SabemosquepodemosexecutarcódigonomomentoqueumaServletouumfiltrosãoinicializadosatravésdosmétodosinitdecadaumdeles.Masesequisermosexecutaralgonoiníciodaaplicação
Web(docontextoWeb),independentedetermosounãoServletefiltrosedonúmerodeles?
Paraissoexistemoscontextlisteners.VocêpodeescreverumaclasseJavacommétodosqueserãochamados automaticamente no momento que seu contexto for iniciado e depois desligado. Bastaimplementar a interface ServletContextListener e usar a tag <listener> no web.xml para
configurá-la.
Porexemplo:
publicclassMeuListenerimplementsServletContextAttributeListener{publicvoidcontextInitialized(ServletContextEventevent){System.out.println("Contextoiniciado...");}
publicvoidcontextDestroyed(ServletContextEventevent){System.out.println("Contextodesligado...");}}
EdepoisnoXML:
<listener><listener-class>pacote.MeuListener</listener-class></listener>
AsaplicaçõesWebemJavatêm3escopospossíveis.Jávimoseusamosdoisdeles:oderequesteodesessão.Podemoscolocarumatributonorequestcomrequest.setAttribute(..,..)eeleestará
JáconheceoscursosonlineAlura?
17.8CONTEXTLISTENER
17.9OSERVLETCONTEXTEOESCOPODEAPLICAÇÃO
244 17.8CONTEXTLISTENER
disponívelparatodoorequest(desdeaActionatéoJSP,osfiltrosetc).
Da mesma forma, podemos pegar a HttpSession e colocar um atributo com
session.setAttribute(..,..) e ela estará disponível na sessãodaquele usuário através devários
requests.
Oterceiroescopoéumescopoglobal,ondeosobjetossãocompartilhadosnaaplicaçãointeira,portodos os usuários em todos os requests. É o chamado escopo de aplicação, acessível peloServletContext.
Podemos,emumaServlet,setaralgumatributousando:
getServletContext().setAttribute("nomeGlobal","valor");
Depois,podemosrecuperaressevalorcom:
Objectvalor=getServletContext().getAttribute("nomeGlobal");
Umbomusoécompartilharconfiguraçõesglobaisdaaplicação,comoporexemplousuárioesenhadeumbancodedados,oualgumobjetodecachecompartilhadoetc.Vocêpode,porexemplo,inicializaralgum objeto global usando um ServletContextListener e depois disponibilizá-lo no
ServletContextparaorestodaaplicaçãoacessar.
EcomofazemosparaacessaroescopodeaplicaçãononossoJSP?Simples,umadasvariáveisquejáexisteemumJSPsechamaapplication,algocomo:
ServletContextapplication=getServletContext();
Portantopodemosutilizá-laatravésdescriptlet:
<%=application.getAttribute("nomeGlobal")%><br/>
Como já vimos anteriormente, o código do tipo scriptlet pode sermaléfico para nossa aplicação,sendoassimvamosutilizarExpressionLanguageparaacessarumatributodoescopoaplicação:
AcessandocomEL:${nomeGlobal}<br/>
ReparequeaExpressionLanguageprocurarátalatributonãosónoescopodoapplication,como
veremosmaisafrente.Paradeixarclaroquevocêprocuraumavariáveldoescopodeaplicação,usamosavariávelimplícitachamadaapplicationScope:
Acessandoescopoapplication:${applicationScope['nomeGlobal']}<br/>
17.9OSERVLETCONTEXTEOESCOPODEAPLICAÇÃO 245
MÉTODOSNOSERVLETCONTEXT
AlémdacaracterísticadeescopoglobalcomosmétodosgetAttributeesetAttribute,
outrosmétodosúteisexistemnaServletContext.ConsulteoJavadocparamaisinformações.
HáaindaoutroslistenersdisponíveisnaAPIdeServletsparacapturaroutrostiposdeeventos:
HttpSessionListener - provê métodos que executam quando uma sessão é criada
(sessionCreated),destruída(sessionDestroyed);
ServletContextAttributeListener - permite descobrir quando atributos sãomanipuladosno
ServletContext com os métodos attributeAdded , attributeRemoved e
attributeReplaced;
ServletRequestAttributeListener - tem os mesmos métodos que o
ServletContextAttributeListener mas executa quando os atributos do request são
manipulados;
HttpSessionAttributeListener - tem os mesmos métodos que o
ServletContextAttributeListener mas executa quando os atributos da HttpSession são
manipulados;
ServletRequestListener - permite executar códigos nosmomentos que um request chega e
quandoeleacabadeserprocessado(métodosrequestDestroyederequestInitialized);
Outrosmenosusados:HttpSessionActivationListenereHttpSessionBindingListener.
Aconfiguraçãodequalquerumdesseslistenerséfeitacomatag<listener>comovimosacima.
É possível inclusive que uma mesma classe implemente várias das interfaces de listeners mas sejaconfiguradaapenasumavezoweb.xml.
17.10OUTROSLISTENERS
246 17.10OUTROSLISTENERS
Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.
ConsulteasvantagensdocursoJavaparaDesenvolvimentoWeb
VocêpodetambémfazerocursodatadessaapostilanaCaelum
17.10OUTROSLISTENERS 247