![]() |
Pràctica 9:Afegint funcionalitat al portal ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Fins aquesta pràctica has fet la feina més feixuga: has completat les instal·lacions i configuracions i has escrit les classes utilitàries del portal. Ara és l'hora de treure el partit d'aquesta feina, de dotar el programa de capacitats de comunicació amb l'usuari. Repartiràs aquesta feina en dues pràctiques. En aquesta primera afegiràs les següents capacitats al portal de notícies ACME: 1) Mantenir un comptador de visites. 2) Elaborar una llista dels darrers articles publicats 3) Fer una pàgina que presenti un article A la darrera pràctica aprendràs a utilitzar els formularis per a enviar dades des de la pàgina web al servidor Resin. |
||||||||||||||
Primera tasca: El comptador de visites | ||||||||||||||
A més a més, vols que en el portal s'imprimeixin el número de visites totals que ha rebut la pàgina i el número de connexions en el dia actual. Tens, doncs, les següents necessitats: 1) Has de pensar en una taula que vagi enregistrant tots els accessos. Anem fent aquestes feina: La taula:
|
||||||||||||||
Obre el Query Browser i construeix una taula amb el nom visites i la següent estructura de camps:
|
||||||||||||||
El camp DATA contindrà el dia i l'hora en què es fa la connexió. El faràs del tipus TIMESTAMP per a ferte fàcil el manteniment de la taula: els camps TIMESTAMP s'omplen per defecte amb el dia i l'hora en que es fa la inserció del registre. No cal, doncs, que l'omplis tú des del programa de Java, el MySQL ho farà sol. El camp IP contindrà l'adreça IP del client que fa la connexió. Com les IPs no són més llargues de 15 caràcters, en fem del camp un a VARCHAR(15), camp variable de fins a 15 caràcters. Indexa els dos camps DATA i IP per a facilitar les tasques de consulta.
El POJO: Ara és l'hora de crear una classe Visita.java que representi en llenguatge Java la taula de visites. Obre el BlueJ i, en el paquet acme, escriu el següent codi:
|
||||||||||||||
![]() |
package acme; import java.util.Date; /** |
|||||||||||||
Les Consultes a la base de dades: Necessitaràs fer tres accions sobre la base de dades: afegir una visita quan algú es connecti al portal; fer el recompte de visites del dia i, finalment, fer el recompte de visites totals al portal. Has de crear, doncs, tres consultes. Obre amb el BlueJ el dipòsit de consultes (el tens a acme.util.Consultes). Afegeix les següents consultes: |
||||||||||||||
A) Consulta per a afegir una visita a la taula "visites": |
||||||||||||||
![]() |
public void addVisita(Visita visita) throws SQLException { |
|||||||||||||
Observa que hi ha, però, algunes diferències remarcables:
B) Consulta per a fer el recompte de totes les visites rebudes pel portal: |
||||||||||||||
![]() |
public int getVisites() throws SQLException { int visites = 0; Connection connexio = DAOUtils.getDBConnection(); PreparedStatement stmt = connexio.prepareStatement("SELECT COUNT(*) AS NUMERO FROM VISITES"); ResultSet rs = stmt.executeQuery(); if (rs.next()) { visites = rs.getInt("NUMERO"); } rs.close(); stmt.close(); connexio.close(); return visites; } |
|||||||||||||
El mètode retorna un número enter que és el recompte del número de registres que hi ha a la taula "visites". En llenguatge SQL per a saber el número de registres d'una taula escrivim "SELECT COUNT(*) FROM la_taula_que_sigui". Com necessites donar-li un nom al número resultant escrius "SELECT COUNT(*) AS NUMERO FROM VISITES" i després reculls el valor d'aquest enter a la sentència visites = rs.getInt("NUMERO");
C) Consulta per a saber el número de visites del dia actual: |
||||||||||||||
![]() |
public int getVisitesDia() throws SQLException { |
|||||||||||||
Observa el tractament de les dates en Java-MySQL: per a passar un paràmetre a una consulta en forma de data: stmt.setDate(1,new java.sql.Date(new java.util.Date().getTime())); Has de construir un objecte java.sql.Date, no et serveix un java.util.Date (són objectes diferents). Pots, però, construir un objecte java.sql.Date a partir d'un java.util.Date() com ho fa el codi precedent.. En pseudocodi, la sentència anterior seria equivalent a "crea un nou objecte java.util.Date() amb la data d'avui i, a través d'aquest objecte crea un nou java.sql.Date". A la senténcia SQL també has de fer una conversió del camp DATA: CAST(DATA AS DATE). Com DATA és un camp TIMESTAMP, té una precisió de milisegons i no li pots passar directament el paràmetre java.sql.Date que acabes de crear. Si li passes i es compara la data '01/01/2005 12:32' del paràmetre amb la data '01/01/2005 08:45' de la taula, el servidor MySQL no les considerarà iguals perquè hi ha una variació en les hores: Has d'eliminar aquesta informació sobre l'hora. Això és el que fa el CAST(DATA AS DATE). El tipus DATE de MySQL només conté la informació del dia. Expressada la consulta d'aquesta forma les dates '01/01/2005 12:32' i '01/01/2004 08:45' són idèntiques perquè el servidor n'extreu l'hora i les tracta com a '01/01/2005' i '01/01/2005'.
La pàgina web
Obre amb el teu editor de pàgines web la pàgina top.jsp que tens a l'arrel del projecte noticiesacme (c:/java/resin-3.0.9/webapps/noticiesacme/). Modifica-la amb el següent codi marcat en negreta: |
||||||||||||||
![]() |
<%@ page language="java" import ="java.util.GregorianCalendar,acme.util.*,acme.*"%> <% Consultes consultes = new Consultes(); Visita visita = new Visita(); visita.setIp(request.getRemoteAddr()); consultes.addVisita(visita); int visitesavui = consultes.getVisitesDia(); <table width="95%" border="0" cellpadding="1"> |
|||||||||||||
Verifica el funcionament del programa. Activa el Resin i accedeix al portal: http://localhost . Has d'obtenir el següent resultat: |
||||||||||||||
![]() |
||||||||||||||
Refresca el navegador i observa com va sumant el número de visites. Si obres el Query Browser del MySQL i obres la taula de visites (SELECT * FROM VISITES) veuràs com s'han anat enregistrant les teves entrades al portal.
|
||||||||||||||
Segona tasca: Llista de darrers articles publicats i Llista d'articles més votats | ||||||||||||||
Per a llistar els darrers articles publicats necessites tres peces:
El POJO: A hores d'ara, estudiant la taula que enregistra els articles a la base de dades, ja series capaç d'escriure la seva representació en Java. Per si tens dubtes, aquí tens el codi. Obre el BlueJ i escriu una nova classe que es digui Article.java amb el següent contingut: |
||||||||||||||
![]() |
package acme;
import java.util.Date; /** public Article() { public int getId() { |
|||||||||||||
La consulta: Has d'afegir un nou mètode a la classe contenidora de consultes acme.util.Consultes. Es dirà getDarrersArticles(). Actualitza, en primer lloc, les importacions que fa la la classe, afegeix a Consultes aquestes dues sentències d'importació. import acme.Article; Seguidament, afegeix el mètode: |
||||||||||||||
![]() |
public Article[] getDarrersArticles(int numarticles) throws SQLException { |
|||||||||||||
Per la resta, actues com ja has fet abans: crees un vector d'articles que vas carregant tot recorrent el ResultSet sequencialment i després tradueixes el vector a una matriu d'articles que és el valor que retornes.
Modificacions de la pàgina index.jsp Obre la pàgina index.jsp que és a la carpeta arrel del projecte (c:/java/resin-3.0.9/webapps/noticiesacme/). Modifica-la afegint el codi assenyalat en negreta: |
||||||||||||||
![]() |
<html> <head> <title>Agència de Notícies ACME</title> </head> <body> <%@ include file="top.jsp"%> <% Article[] darrersarticles = consultes.getDarrersArticles(10); %> <h2>Portal de l'Agència de Notícies ACME. </h2> <table width="100%" border="0" cellpadding="1"> <tr> <td> </td> <td><b>Darrers Articles</b><br><br> <% if (darrersarticles!=null) for (int n=0;n<darrersarticles.length;n++) {%> <%=darrersarticles[n].getTitol()%><br> <% } %> </td> </tr> </table> <p> </p> <%@ include file="peu.jsp"%> </body> </html> |
|||||||||||||
La quantitat de codi que has d'afegir és minima (recorda que aquest és l'objectiu: posar el mínim de codi de programa a les pàgines web, separant la presentació de dades del seu modelat). Senzillament crees i carregues una matriu amb els darrers 10 articles: Article[] darrersarticles = consultes.getDarrersArticles(10); i posteriorment en fas un bucle d'impressió: <% if (darrersarticles!=null) Observaràs que en aquesta pàgina no instancies la classe Consultes: pensa que ja ho has fet a la pàgina top.jsp. Com aquesta darrera pàgina s'incrusta a index.jsp a l'afegir l'etiqueta < %@ include file="top.jsp"%>, tot el codi font de les dues pàgines queda compartit. Per a veure si la consulta funciona correctement, afegeix un parell d'articles amb el Query Browser del MySQL. Pots escriure i executar dues consultes similars a aquestes: INSERT INTO ARTICLES (AUTOR,TITOL,CONTINGUT) VALUES ('reverte','Cabo Trafalgar','Contingut de l''article') INSERT INTO ARTICLES (AUTOR,TITOL,CONTINGUT) values ('obrian','La fragata Surprise','Contingut de l''article') Activa el Resin si encara no ho has fet i crida el portal: http://localhost. Has d'obtenir una sortida com aquesta: |
||||||||||||||
![]() |
||||||||||||||
Tercera tasca: Veure un article | ||||||||||||||
Ara donaràs una mica de vida a la llista d'articles: enllaçaràs el títol de l'article a una nova pàgina dinàmica que en presentarà el contingut. Quan l'usuari faci clic sobre el títol, s'obrirà una nova finestra amb el text de l'article. Necessitaràs:
Modificacions a la pàgina index.jsp Revisa la pàgina index.jsp actualitzant el cos del bucle d'impressió segons et suggerim en el fragment de codi assenyalat en negreta: |
||||||||||||||
![]() |
<html> <head> <title>Agència de Notícies ACME</title> </head> <body> <%@ include file="top.jsp"%> <% Article[] darrersarticles = consultes.getDarrersArticles(10); %> <h2 align="center">Portal de l'Agència de Notícies ACME. </h2> <table width="100%" border="0" cellpadding="1"> <tr> <td> </td> <td><strong>Darrers Articles</strong><br> <br> <% if (darrersarticles!=null) for (int n=0; n<darrersarticles.length; n++) {%> <a href="article.jsp?id=<%=darrersarticles[n].getId()%>" target="_blank"> <%=darrersarticles[n].getTitol()%> </a><br> <% } %> </td> </tr> </table> <p> </p> <%@ include file="peu.jsp"%> </body> </html> |
|||||||||||||
<a href="direccio_que_s'obre_al_fer_click?parametre=valor">Text que apareix a l'enllaç</a> Observa que podem afegir paràmetres a un enllaç. Aquests paràmetres arriven al servidor i poden ser capturats pels teus programes. Ara en veuràs un exemple. Pots passar més d'un paràmetre en un adreça web. Només et cal encadenar-los així: http://www.domini.com?param1=valor1¶m2=valor2¶m3=valor3 A la pàgina index.jsp el que fas és utilitzar les etiquetes <%=%> per anar construint dinàmicament l'enllaç. Per exemple, per a què es representi a la pàgina jsp aquest text <a href="article.jsp?id=1"></a> que serveix per a crear un enllaç que porta el paràmetre id amb el valor 1 ("llegeix l'article el número id del qual és 1") utilitzes <a href="article.jsp?id=<%=darrersarticles[n].getId()%> Fas anar les dades del programa per a parametritzar els enllaços. Ja tens preparat l'enllaç, ara hauries de fer la consulta a la base de dades per a llegir les dades de l'article solicitat per l'usuari. Modificacions a la classe Article.java. Tens, però, un petit problema. Quan presentis l'article als usuaris del teu programa, segurament voldràs que aparegui el nom complet de l'autor a la pàgina web (que surti imprés "Arturo Pérez Reverte" com a nom de l'autor i no "reverte"). Aquesta informació no la tens enregistrada a la taula d'articles, on només en deses el nick. La guardes a la taula d'autors. Per tant, per a generar la informació completa sobre l'article hauràs de fer una consulta creuan les dues taules "autors" i "articles". Com el nom complet de l'autor no forma part de l'estructura de la taula "articles", no en vas crear cap camp en el POJO corresponent. Es que faràs, en primer lloc, és adaptar la classe article.java a aquesta nova necessitat. Crearàs un camp nou la funció del qual serà desar el nom complet de l'autor d'un article. Obre el BlueJ i busca la classe Article.java en el paquet acme. Afegeix el camp autorComplet i publica'l amb els mètodes getter i setter: |
||||||||||||||
![]() |
private String autorComplet; public void setAutorComplet(String autorComplet) { this.autorComplet = autorComplet; } public String getAutorComplet() { return this.autorComplet; } |
|||||||||||||
Ara sí, ja pots continuar amb la consulta. La Consulta Crea un mètode getArticle() a la classe acme.util.Consultes. El mètode rebrà com a paràmetre un enter, el número id de l'article, i retornarà un objecte Article amb totes les dades de la publicació solicitada. El contingut del mètode pot ser aquest: |
||||||||||||||
![]() |
public Article getArticle(int articleid) throws SQLException { |
|||||||||||||
article.setAutorComplet(
"SELECT AR.*,AU.NOM,AU.COGNOM1,AU.COGNOM2 FROM ARTICLES AR, AUTORS AU WHERE AU.AUTOR=AR.AUTOR AND AR.ID=?" |
||||||||||||||
![]() |
||||||||||||||
i la resposta del MySQL a la nostra consulta serà aquesta taula : | ||||||||||||||
![]() |
||||||||||||||
Ara ja només et resta escriure la pàgina web que ha d'imprimir el contingut de l'article. La pàgina article.jsp Escriu una nova pàgina web amb el nom article.jsp a la carpeta arrel de l'aplicació amb el següent contingut:
|
||||||||||||||
![]() |
<%@ page language="java" import ="acme.util.*,acme.*"%> <% int articleid = 0; try { articleid = Integer.parseInt(request.getParameter("id")); }catch(Exception e) { } Consultes consultes = new Consultes(); Article article = consultes.getArticle(articleid); %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>ACME - <%=article.getTitol()%></title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head> <body> <h3><%=article.getTitol()%></h3> Per <%=article.getAutorComplet()%> <hr> <%=article.getContingut()%> <hr> <%@ include file="peu.jsp"%> </body> </html> |
|||||||||||||
Procedeixes recollint el paràmetre id que has creat a la tasca anterior, protegint la seva lectura per si arriva al servidor en un format erroni (ha de ser un número enter): int articleid = 0; Seguidament obres una instancia de la classe de consultes i en carregues les dades de l'article corresonent: Consultes consultes = new Consultes(); |
||||||||||||||
Verifica el funcionament d'aquesta part del programa. Activa el Resin, obre el portal (http.//localhost)i pica sobre el nom d'algun article de la llista de darreres publicacions. S'ha d'obrir una nova finestra amb un contingut similar a aquest: |
||||||||||||||
![]() |
||||||||||||||
Quarta tasca: Votacions d'Articles | ||||||||||||||
Utilitzaràs una metodologia elemental: quan un lector acaba de llegir un article, si li ha interessat podrà votar per ell fent un clic sobre un enllaç. Quans més vots reculli l'article, més alt estarà a la llista de popularitat. Això vol dir que has de posar un enllaç de votació a la pàgina article.jsp i que aquest enllaç ha de portar a una pàgina que enregistri el vot de l'usuari. Els vots es van sumant a una nova taula de votacions que també has de crear. Finalment, necessitaràs crear nous mètodes de consulta a la base de dades per a enregistrar i comptar vots. Gràficament el control del procés que en fa l'aplicació jsp és aquest:
|
||||||||||||||
![]() |
||||||||||||||
Al fer clic sobre l'enllaç "vota per aquest article" obriràs una pàgina jsp, vota.jsp, que només conté codi de programa. No és visible a l'usuari. Aquesta pàgina enregistrarà la votació i reenviarà la petició a la pàgina article.jsp utilitzant una etiqueta especial, l'etiqueta forward. Posa't a la feina. En primer lloc crearàs la taula de vots. La taula de vots Obre el Query Browser del MySQL i crea una nova taula que es digui "votacions" i que tingui la següent estructura: |
||||||||||||||
|
||||||||||||||
El camp ID és enter autoincremental i fa de clau primària. El camp ARTICLE_ID recollirà el número ID de l'article votat i, al camp DATA, enregistraràs quan es va produir la votació. Si t'interessés saber l'adreça IP del votant també la podries afegir, tot i que ara no t'ho proposem.
Les consultes Necessitaràs un mètode per a enregistrar les votacions i un altre mètode per a saber quants vots ha obtingut un article. Obre la classe acme.util.Consultes i afegeix els següents dos mètodes: |
||||||||||||||
El mètode per a votar | ||||||||||||||
![]() |
public void vota(int articleid) throws SQLException { int visites = 0; Connection connexio = DAOUtils.getDBConnection(); PreparedStatement stmt = connexio.prepareStatement("INSERT INTO VOTACIONS (ARTICLE_ID) VALUES (?)"); stmt.setInt(1,articleid); stmt.execute(); stmt.close(); connexio.close(); } |
|||||||||||||
Al mètode li entra un enter (articleid) que és el número ID de l'article votat. A l'inserir el registre el MySQL anotarà automàticament la data i hora de votació. El mètode per a saber el número de vots d'un article: |
||||||||||||||
![]() |
public int getVotacionsArticle(int articleid) throws SQLException { int votacions=0; Connection connexio = DAOUtils.getDBConnection(); PreparedStatement stmt = connexio.prepareStatement("SELECT COUNT(*) AS NUMERO FROM VOTACIONS WHERE ARTICLE_ID=?"); stmt.setInt(1,articleid); ResultSet rs = stmt.executeQuery(); if (rs.next()) { votacions = rs.getInt("NUMERO"); } rs.close(); stmt.close(); connexio.close(); return votacions; } |
|||||||||||||
En aquest cas, a partir del número ID de l'article, el mètode retorna un enter amb el número de votacions que ha recollit.
Ara has de modificar el fitxer article.jsp, situat a la carpeta arrel de l'aplicació web. Afegeix, immediatament abans de l'etiqueta <%@ include file="peu.jsp"%> el següent codi: |
||||||||||||||
![]() |
<p><a href="vota.jsp?id=<%=article.getId()%>"> Vota per aquest article</a>. (<%=consultes.getVotacionsArticle(articleid)%>) votacions obtingudes </p> |
|||||||||||||
crea un enllaç cap a la pàgina que ha de gestionar el vot: i imprimeix el número de votacions que té l'article:
Escriu a l'arrel de l'aplicació web una pàgina que es digui vota.jsp amb el següent contingut: |
||||||||||||||
![]() |
<%@ page language="java" import ="acme.util.*,acme.*"%> <% int articleid = 0; try { articleid = Integer.parseInt(request.getParameter("id")); }catch(Exception e) { } Consultes consultes = new Consultes(); consultes.vota(articleid); %> <jsp:forward page="/article.jsp"> <jsp:param name="id" value="<%=articleid%>" /> </jsp:forward> |
|||||||||||||
Reculls el paràmetre id amb el número d'article a votar: int articleid = 0; Crees una instància de la classe de consultes a la base de dades: Consultes consultes = new Consultes(); consultes.vota(articleid); I, finalment, reenvies l'usuari a la pàgina d'origen, article.jsp amb aquesta etiqueta: <jsp:forward page="/article.jsp"> Verifica el funcionament de la tasca. Activa el Resin i crida al portal (http:/localhost) . Quan demanis un article t'apareixerà un enllaç per a votar-lo i la suma de votacions obtingudes fins ara. Vota vàries vegades un article per a comprovar que es van afegint els vots: |
||||||||||||||
![]() |
||||||||||||||
![]() |
![]() |