PDA

View Full Version : [JSP - JAVABEAN - SERVLET]


evil_stefano
22-03-2014, 20:16
Ciao,

sto creando una web app, senza utilizzare framework, cercando di seguire la struttura MVC in single page application.

Nella pagina principale ho una tabella fatta con Datatable, su di cui è possibile fare le solite azioni "Edit" "Delete" "Insert". Premetto che non voglio mettere i bottoni delete e edit nella tabella (né avere le righe della datatable editabili), ho creato una toolbar sopra la tabella con dentro i bottoni.

L'azione Delete l'ho realizzata con javascript e facendo un ajax call alla servlet "eseguiAzione" passando per parametro l'azione di delete, che si occupa di gestire le azioni possibili sulla tabella, passandogli l'id della riga e l'azione da fare (delete).


$.ajax({
type: "POST",
cache: false,
url: "../ActionOnRowTable",
dataType: "text",
data: { "action":"delete", "id":JSON.stringify(id_row_selected) },
success: function(dataItem) {
oTable.fnReloadAjax();
$.growlUI('Delete notification', 'Record successfully deleted.');
},
complete: function(){
$('#main_content_table').unblock();
}
});



Per le azioni di Insert/Edit ho mille dubbi..
Quello che volevo fare era: uno clicca il pulsante Insert o di Edit, si apre una finestra nuova con una form contenente tutti i campi (nel caso del bottone Edit i campi sono già compilati con i dati del db), si compila/modifica, si salva e la finestra si chiude.


Il problema è: la finestra la apro da JS o dalla servlet "openPagina"?

Nel caso che la apro dalla JS, quando carico la pagina jsp, come fa a prendere i dati? instanzia lei i javabean e usa le apposite classi per recuperare i dati? ma così facendo la jsp che è la View non diventa anche il Controller?

Nel caso che apro la nuova pagina dalla servlet "openPagina", passo i dati sotto forma di Bean alla pagina jsp con una forward, corretto?


Altra cosa: come passo poi i dati dalla jsp alla servlet "eseguiAzione" per eseguire le azioni richieste?
in teoria volevo memorizzare i dati della form in un bean con scope require sulla jsp e poi chiamare in ajax call la servlet "eseguiAzione", e da lì recuperare il bean. E' un approccio corretto?


Spero di aver reso l'idea del problema, mi rendo conto che è difficile spiegarlo..

Grazie ! ;)

Daniels118
25-03-2014, 08:08
Le view sono il canale di input/output nel modello mvc, quando apri la finestra le passi l'id del record da modificare, la view quindi chiede al controller di fornirle il bean con i dati del record. Una volta premuto il pulsante di conferma, i dati vengono inviati al controller che provvede a consolidarli e restituisce alla view l'esito dell'operazione.
La finestra consiste quindi in due view e altrettante action.

evil_stefano
25-03-2014, 18:07
Grazie per il consiglio !

attualmente ho fatto così:

Index.jsp (View) -> PreparePageAction (Servlet) -> GetDevicePage (Servlet) -> managedevice.jsp (View)

# utente modifica e clicca salva

managedevice.jsp (View) --- AJAX ---> ActionOnRowTable (Servlet) -> return result.



quindi dalla pagina principale premo il tasto INSERT che richiama la funzione "table_row_insert":


function table_row_insert () {
popupwindow('../PreparePageAction?action=insert&entity=device&id=null', 'InsertNewObject', '850', '530');
return false;
};


PreparePageAction è una servlet che serve solo per indirizzare la chiamata alla servlet che gestisce l'oggetto (cioè in base alla entity chiama una servlet o un'altra).


if ( entity.equalsIgnoreCase("device")){
RequestDispatcher dispatcher = request.getRequestDispatcher("GetDevicePage");
dispatcher.forward(request, response);
}


GetDevicePage è una servlet che si occupa, in basa all'azione richiesta, di recuperare i dati e passarla alla view.


if ( action.equalsIgnoreCase("edit")){
device = DeviceDaoBean.getDevice( Integer.parseInt(id_device) );
session.setAttribute("currentDevice", device);
session.setAttribute("action", "edit");
}
else if ( action.equalsIgnoreCase("insert")){
session.setAttribute("action", "insert");
}
response.sendRedirect(request.getContextPath() + "/app/managedevice.jsp");
}


La view managedevice.jsp crea la form e, in base all'azione richiesta, popola o meno il contenuto dei campi.



Questo approccio l'ho usato principalmente per due motivi:
- volevo un'unica pagina per inserire/editare/visualizzare i dati del bean.
- volevo una struttura abbastanza flessibile per poter gestire diverse entity da dover inserire (nella tabella Datatable al momento ci sono sono entity di tipo Device, ma in futuro ne vorrei mettere anche altre, senza dover poi cambiare il JS sotto i pulsanti di INS/EDIT/DEL).


Sono sicuro che questo non sia il metodo più efficiente, ma non ho trovato altro metodo per far prendere i dati alla view e farli visualizzare.

O meglio, probabilmente non ho ben chiaro chi sia il mio model e chi il controller: io penso sempre che la classe del Bean e la classe Dao per reperire i dati del bean sul db sia il model, mentre il controller è la servlet che poi richiama la classe Dao in base alle richieste.


Con l'approccio che mi suggerisci dovrei dal JS chiamare la view managedevice.jsp passandogli l'id dell'oggetto, la view dovrebbe richiedere alla classe Dao il bean, poi creare i field in base ai campi del bean e poi popolarli (nel caso di edit).
Cioè dovrei eleiminare le servlet giusto?
ma così facendo, quello che io intendo come model, non diventa anche controller?

Grazie !

Daniels118
26-03-2014, 09:11
La view non deve parlare con i dao; la view deve dire al controller tramite una action "iniziamo a modificare l'oggetto x", il controller fa tutto quello di cui c'è bisogno, verifica ad esempio l'autenticazione e le abilitazioni necessarie ad eseguire questa particolare azione, dopodiché - se l'utente è autorizzato - recupera i dati dal dao, quindi passa il bean alla view che contiene i campi per la modifica. Altri operazioni che il controller deve fare sono, ad esempio, produrre una view diversa come output nel caso in cui l'oggetto sul quale si vuole fare la modifica non esista, oppure ancora una view diversa se l'utente non è autorizzato.

evil_stefano
26-03-2014, 22:18
mmm.. un paio di domande:

la modifica dei dati, e quindi l'azione di contattare la servlet dalla view, la farei attraverso ajax e non sulla action della form, è corretto logicamente?

Cioè, al posto di:
<form id="device-form" class="device-form" name="device-form" action="modifica_tutto" method="post">


farei:

<form id="device-form" class="device-form" name="device-form" action="" method="post">

$("#device-form").submit(function(ev) {
ev.preventDefault(); // to stop the form from submitting

var obj = {};
obj.campo1 = $("#input-type-descrizione").val();
obj.campo2 = $("#input-type-nome").val();
........

$.ajax({
type: "POST",
cache: false,
url: "../modificatutto",
dataType: "text",
data: { "action":"<%=action%>", "device":JSON.stringify(obj) },
success: function(dataItem) {
},
complete: function(){
setTimeout("window.close();", 2000);
this.submit(); // If all the validations succeeded
}
});
});


il difetto che ho riscontrato è che poi devo passare i campi con un json alla servlet, al posto di farli prendere alla servlet con request.getAttribute in seguito alla submit della form.

sto sbagliando approccio?




altra domanda: i campi input-type dell'oggetto sono fissi nella view o deve creare dinamicamente la servlet?
Cioè, la view ha fissi ad esempio

<label for="campo1" > campo1 </label>
<input id="campo1" name="campo1"/>
<label for="campo2" > campo1 </label>
<input id="campo2" name="campo1"/>
......

e poi popola i valori dei campi in base alla servlet, oppure la servlet in qualche modo comunica alla view anche l'elenco dei campi da creare (specificando tipologie e cosa metterci dentro come dati) ?


Grazie !

Daniels118
27-03-2014, 08:20
Il fatto di utilizzare ajax è del tutto indifferente, tranne per il fatto che con quest'ultimo la view non contiene codice html ma solo dati.

L'utilizzo di ajax non ti obbliga a passare i dati in formato json, puoi mappare i campi di obj direttamente nell'attributo data. In javascript gli oggetti hanno anche un'interfaccia che permette di trattarli come se fossero array associativi, pertanto puoi fare una cosa del genere:
var vdata = {};
for (campo in obj) {
data[campo] = obj[campo];
}
$.ajax({...
data: vdata
});
Però personalmente troverei più comodo tenere distinti i campi del form dal parametro action, quindi il tuo approccio mi sembra più corretto.

La servlet non deve dire alla view di creare i campi; la servlet deve passare alla view un oggetto contenente i dati da visualizzare, è la view che deve preoccuparsi di come presentarli. La servlet potrebbe utilizzare delle view distinte in base al tipo di oggetto da rappresentare. Il punto è che la servlet deve solo produrre dati per la view, non deve impartirle degli ordini.
La view può decidere il tipo di campo da creare in base al tipo di dato per il quale lo sta creando; meglio ancora, la servlet può passare alla view, oltre all'oggetto, un descrittore che specifica per ogni campo il tipo di dato e il suo dominio.

evil_stefano
27-03-2014, 08:50
ok, grazie per i consigli.

il mio dubbio sull'ajax call parte dal fatto che la pagina jsp può accedere direttamente alle classi java dei dati e dei metodi (i model), ma per aderire al pattern MVC devo chiamare la servlet, quindi ho pensato di fare la ajax call.
Se non facessi la ajax call come dovrei chiamare la servlet dalla jsp quando inizia a caricarsi?

Quindi in definitiva la struttura dovrebbe essere:

index.jsp (si clicca il bottone) ---> window.open( managedevice.jsp (View) )

quando la View si apre, chiede alla servlet GetDevice(Servlet) i dati, e quando riceve la risposta li presenta.




PS:
ho notato che faccio un uso scorretto della dichiarazione dei bean nelle pagine jsp, solitamento importo la classe e poi con jsp:usebean dichiaro il bean, solo che per recuperare il bean dalla sessione poi ne dichiaro uno nuovo.. non so quale sia il metodo giusto, usare useBean o getAttribute ?

Più che altro perché poi pensavo di fare una cosa simile per la pagina di managedevice.jsp (View), all'inizio della pagina dichiaro un beanDevice e chiedo alla servlet di darmi i dati, solo che non so come me li dovrebbe restituire, cioè, la servlet li mette in un attributo della request? oppure come li passa?


<%@page import="it.evil.wam.UserBean"%>
<jsp:useBean id="user" class="it.evil.wam.UserBean" scope="session" />
<% UserBean currentUser = (UserBean) (session.getAttribute("currentSessionUser")); %>


questo è quello che pensavo:

currentDevice = (DeviceBean) (session.getAttribute("currentDevice"));
session.removeAttribute("currentDevice");


prendere il dato e poi rimuoverlo.


Grazie ! ;)

evil_stefano
27-03-2014, 13:20
ulteriore domanda su Bean:

se il DeviceBean contiene campi che sono riferimenti a altre tabelle, ad esempio lo stato dell'apparato che è un riferimento alla tabella stato (quindi non un char ma un int con Fkey alla tabella stato), se la servlet deve passare il DeviceBean alla view, come si deve comprotare la view quando riceve il bean?
per ogni campo che è un Fkey a altre tabelle deve richiederne il valore alla servlet?
oppure la servlet prende il DeviceBean, e quando trova campi Fkey chiede al model dello stato il valore Char?
Ma in questo caso non saprei che oggetto poi passa la servlet alla view, perché il DeviceBean contiene i campi int (fkey) e non posso passare per ogni DeviceBean altri Bean dei vari campi Fkey (StatoBean etc etc)..

bho.. mi sfugge come fare. :mc:

Daniels118
27-03-2014, 13:55
Quando apri il popup, nell'url aggiungi il parametro che identifica l'oggetto da modificare; nel file di configurazione del tuo framework mvc avrai definito che quell'url corrisponde ad una determinata action e che tale action invoca una determinata servlet. Tale servlet recupera il parametro dall'url e lo utilizza nei confronti del dao. La servlet quindi prepara un bean e lo passa alla view.

La view deve utilizzare solo il bean passatole dalla servlet, non deve mai richiamare i dao; è compito della servlet (o del dao) risolvere le relazioni tra tabelle. In altre parole, il bean che arriva alla view non deve contenere un campo che contiene la fkey, ma tale campo deve essere già valorizzato con l'oggetto estratto dall'altra tabella.

evil_stefano
27-03-2014, 14:55
ok, quindi l'approccio iniziale che ho usato non è del tutto scorretto.

Index.jsp (View) -> clicco il bottone

azione sotto il bottone:
window.open(PreparePageAction (Servlet))

PreparePageAction (Servlet) chiama -> GetDevicePage (Servlet) che reindirizza alla -> managedevice.jsp (View)


l'unica cosa che non ho ancora capito è: ok la view non chiama i dao, la servlet risolve le dipendenze tra tabelle, ma la servlet che tipo di bean passa alla view?

perché se gli passo DeviceBean contiene dei fkey, a questo punto oltre a DeviceBean dovrei creare un nuovo bean DeviceViewBean dove contiene solo char e passare quello alla vista.
La domanda che mi sorge spontanea è: è corretto usare 2 bean per lo stesso dato? perché alla fin della fiera conterranno il 70-80 degli stessi campi (tranne i fkey). Quindi pensavo di non fare un nuovo Bean ma di ampliare quello esistente mettendo anche i campi in formato char.

Esempio:

DeviceBean
id int,
descrizione char,
..
id_stato int,
stato char,
...

solo che così ripeto i campi 2 volte.. bho..

Daniels118
27-03-2014, 18:16
Hai abbastanza libertà su come strutturare i bean, comunque non vedo alcun campo ripetuto nell'esempio che hai illustrato.

Una cosa che mi sono dimenticato di dirti: l'oggetto session si usa per rendere dei dati persistenti tra una richiesta e l'altra, quando invece devi passare un bean tra il controller e una view i dati rimangono all'interno della stessa richiesta, pertanto bisogna usare l'oggetto request.

evil_stefano
27-03-2014, 19:03
si, ho fatto un uso improprio di session, infatti volevo mettere a posto anche questo.

Il campo ripetuto è "stato" con "id_stato". contengono lo stesso dato ma uno in forma int (fkey) e l'altro contiene il valore char della tabella stato per quel ID.

Alla fine ho pensato a delle varianti su come gestire questa cosa e ho trovato varie soluzioni:

A) la servlet passa alla view non un ben ma un json. (soluzione poco elegante).

B) uso un bean tra la view e la servlet con tutti i campi in char (ad esempio stato), poi la servlet si occuperà di gestire il bean e convertire i campi fkey in int con gli opportuni dao (ad esempio StatoDaoBean). in questo modo il campo è trasparente alla view, il problema è un carico più alto sul controller, e il fatto di avere 2 bean per la stessa cosa (DeviceBean della servlet con i fkey int, e DeviceBeanView da mandare alla view con tutti char).

C) come già detto duplico i campi sul bean. (non mi piace come approccio).