Domanda Connessione sincrona a database

System_error

Utente Electrum
11 Aprile 2012
350
69
24
187
Ciao ragazzi, sto facendo il progetto per l'esame di tecnologie software per il web e sto impazzendo per il seguente problema:
Riesco ad inserire e cancellare entità dal database tramite la web application, alcune volte però quando elimino delle entità, aggiornando la pagina ricompaiono, se aggiorno di nuovo scompaiono e così via, compaiono e scompaiono a caso ogni volta che aggiorno la pagina e non so più cosa fare, ho provato con i blocchi synchronized per i metodi che accedono al db, ho provato a dichiarare gli oggetti model che sono nelle servlet come static, ma niente, quando sembra che sia la volta buona in realtà non lo è.
Il progetto lo sto realizzando con Java ee, utilizzando le servlet e le jsp. Nel progetto utilizzo lo schema MVC.
Al momento l'unico modo per farlo funzionare è quello di scrivere le operazioni di lettura direttamente nella servlet che li utilizza, il che però va contro il pattern MVC. Le operazioni di inserimento e cancellazione sono scritte correttamente dove dovrebbero, cioè nel model, solo i metodi di lettura delle immagini sono nella servlet, perché se li sposto nel model succedono cose strane. Come posso fare?

Inviato dal mio ONEPLUS A6013 utilizzando Tapatalk
 
Ultima modifica:
Si, hai ragione scusami.

Java:
package control;

import java.io.IOException;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import eccezioni.LoginException;
import model.CopertinaModel;
import model.DriverManagerConnectionPool;
import model.Immagine;
import model.ImmagineModel;
import model.Key;
import model.Marca;
import model.MarcaModel;

public class ImgServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private String URL = "";
    private static ImmagineModel imgModel;
    private static MarcaModel marcaModel;
    private static CopertinaModel copertinaModel;
  
    static {
        imgModel = new ImmagineModel();
        marcaModel = new MarcaModel();
        copertinaModel = new CopertinaModel();
    }
  
    public void init(ServletConfig config) throws ServletException {     
        URL = (String) config.getServletContext().getInitParameter("URL");
        super.init(config);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        byte[] buffer = null;
        String marca = request.getParameter("Marca");
        String copertina = request.getParameter("Copertina");
        String ID = request.getParameter("ID");
        String action = request.getParameter("action");
        String targa = request.getParameter("Targa");
        if (action == null) action = "";
        //System.out.println("Marca: " + marca + ", Copertina: " + copertina);
      
        if (action.equals("delete")) {
            try {
                Key key = new Key(targa, ID);
                Immagine immagine = new Immagine();
                immagine = imgModel.selectByKey(key);
                imgModel.delete(immagine);             
                response.sendRedirect(response.encodeURL(URL + "admin/veicolo?Targa=" + targa));
                return;
            } catch (SQLException | LoginException ex) {
                System.err.println(ex.getMessage());
            }
            return;
        }
        else if (marca != null) {
            try {
                Marca m = marcaModel.selectByKey(marca);
                buffer = m.getLogo().getBytes(1, (int)m.getLogo().length());
            } catch (LoginException | NullPointerException | SQLException ex) {
                System.err.println("Marca: " + ex.getMessage());
            }
        }
        else if (copertina != null) {
            try {
                Immagine immagine = copertinaModel.selectByKey(copertina);
                buffer = immagine.getImmagine().getBytes(1, (int)immagine.getImmagine().length());
            } catch (LoginException | NullPointerException | SQLException ex) {
                System.err.println("Copertina: " + ex.getMessage());
            }
        }
        else {
            try {
                Key key = new Key(targa, ID);
                Immagine img = imgModel.selectByKey(key);
                buffer = img.getImmagine().getBytes(1, (int)img.getImmagine().length());
            } catch (LoginException | NullPointerException | SQLException ex) {
                System.err.println("Altro: " + ex.getMessage());
            }

        }
      
        OutputStream out = response.getOutputStream();
        response.setContentType("image/gif");
        if (buffer != null) {
            out.write(buffer);
        }
        out.close();     
    }
  
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
  
     /*public synchronized static byte[] scaricaImmagine(String id) {
         Connection connection = null;
         PreparedStatement statement = null;
         ResultSet result = null;
      
         byte[] buffer = null;
      
         try {
             connection = DriverManagerConnectionPool.getConnection();
             String sql = "SELECT foto FROM IMMAGINI WHERE ID = ?";
             statement = connection.prepareStatement(sql);
             statement.setString(1, id);
             result = statement.executeQuery();
          
             if (result.next()) {
                 buffer = result.getBytes("foto");
             }
         } catch (SQLException ex) {
             System.err.println(ex.getMessage());
         } finally {
             try {
                 if (statement != null) statement.close();
             } catch (SQLException ex) {
                 System.err.println(ex.getMessage());
             } finally {
                 try {
                     if (connection != null) DriverManagerConnectionPool.releaseConnection(connection);
                 } catch (SQLException ex) {
                     System.err.println(ex.getMessage());
                 }
             }
         }
      
         return buffer;
     }*/
  
    public synchronized static byte[] cercaLogo(String nome) {
         Connection connection = null;
         PreparedStatement statement = null;
         ResultSet result = null;
      
         byte[] buffer = null;
      
         try {
             connection = DriverManagerConnectionPool.getConnection();
             String sql = "SELECT Logo FROM MARCA WHERE Nome = ?";
             statement = connection.prepareStatement(sql);
             statement.setString(1, nome);
             result = statement.executeQuery();
          
             if (result.next()) {
                 buffer = result.getBytes("Logo");
             }
         } catch (SQLException ex) {
             System.err.println(ex.getMessage());
         } finally {
             try {
                 if (statement != null) statement.close();
             } catch (SQLException ex) {
                 System.err.println(ex.getMessage());
             } finally {
                 try {
                     if (connection != null) DriverManagerConnectionPool.releaseConnection(connection);
                 } catch (SQLException ex) {
                     System.err.println(ex.getMessage());
                 }
             }
         }
      
         return buffer;
     }
  
     public synchronized static byte[] cercaCopertina(String targa) {
         Connection connection = null;
         PreparedStatement statement = null;
         ResultSet result = null;
      
         byte[] buffer = null;
      
         try {
             connection = DriverManagerConnectionPool.getConnection();
             String sql = "SELECT foto FROM VEICOLO JOIN IMMAGINI WHERE VEICOLO.COPERTINA = IMMAGINI.ID AND VEICOLO.Targa=?";
             statement = connection.prepareStatement(sql);
             statement.setString(1, targa);
             result = statement.executeQuery();
          
             if (result.next()) {
                 buffer = result.getBytes("foto");
             }
         } catch (SQLException ex) {
             System.err.println(ex.getMessage());
         } finally {
             try {
                 if (statement != null) statement.close();
             } catch (SQLException ex) {
                 System.err.println(ex.getMessage());
             } finally {
                 try {
                     if (connection != null) DriverManagerConnectionPool.releaseConnection(connection);
                 } catch (SQLException ex) {
                     System.err.println(ex.getMessage());
                 }
             }
         }
      
         return buffer;
     }
}

Nel metodo doGet ci sono 4 if in cascata che sono quelli che mi causano il problema.
L'ho momentaneamente risolto sostituendo quei quattro if in questo modo:

Java:
if (action.equals("delete")) {
            try {
                Key key = new Key(targa, ID);
                Immagine immagine = new Immagine();
                immagine = imgModel.selectByKey(key);
                imgModel.delete(immagine);             
                response.sendRedirect(response.encodeURL(URL + "admin/veicolo?Targa=" + targa));
                return;
            } catch (SQLException | LoginException ex) {
                System.err.println(ex.getMessage());
            }
            return;
        }
        else if (marca != null) {
            buffer = ImgServlet.cercaLogo(marca);
        }
        else if (copertina != null) {
            buffer = ImgServlet.cercaCopertina(copertina);
        }
        else {
            try {
                Key key = new Key(targa, ID);
                Immagine img = imgModel.selectByKey(key);
                buffer = img.getImmagine().getBytes(1, (int)img.getImmagine().length());
            } catch (LoginException | NullPointerException | SQLException ex) {
                System.err.println("Altro: " + ex.getMessage());
            }
        }


Credo che il problema sia dovuto al fatto che sulla home ci sono molte chiamate al database, perchè per ogni prodotto che carica dal db (nel mio caso automobili) vengono caricate due immagini, una di copertina e una relativa al logo della casa costruttrice.
Dopo giorni di prove ho notato che nella home vengono create due connessioni dalla connection pool. Ti spiego cosa succede.
Avvio il server, apro la pagina che mi permette di aggiungere e rimuovere nuove immagini (veicolo.jsp) SENZA PASSARE PER LA HOME, e posso aggiungere e rimuovere le immagini senza nessun problema, tutto ok. Se invece poi clicco su home e torno sulla pagina di prima succedono macelli. Elimino un'immagine ma nella tabella mi compare comunque però senza l'immagine (esce l'icona dell'immagine non disponibile), se aggiorno scompare tutto, se aggiorno di nuovo c'è di nuovo tutto compresa l'immagine che ho eliminato.
Ho notato che passando per la home vengono create due connessioni nella connection pool e quando vado su veicolo.jsp, le immagini vengono caricate una con la prima connessione creata, e una con la seconda. Una delle due evidentemente è meno aggiornata dell'altra per cui per una connessione risulterà che le immagini sono 5 mentre per un altra sono 4. Se capita che viene presa la connessione per la quale le immagini sono 5, proverà a leggere l'immagine che però non trova. Questo è quello che CREDO succeda, sono ormai mesi che ho questo problema, ho chiesto al professore e mi ha detto "è un problema che succede quando si fa il testing sulla macchina in locale, prova ad aggiungere un timer di attesa" ma non mi ha detto dove devo aggiungerlo e sto letteralmente impazzendo.
Tutto questo però mi accade se uso il "model" (DOM). Cioè ho fatto tre classi ImmagineModel, CopertinaModel e MarcaModel che si occupano ognuna delle rispettive connessioni al database. Se non le uso e quindi grezzamente scrivo una funzione che fa la connessione e preleva i dati non succede. Non succede neanche se utilizzo il "model" ma non la connection pool. Non riesco a capire dove sia il problema.

Il codice della JSP invece per quanto riguarda la home è

HTML:
<div class="corpo">
            <% for (Veicolo v : veicoli) { %>
                <div class="container">
                    <div class="box">
                        <div class="content">
                            <img class="img" src="<%= response.encodeURL("img?Copertina=" + v.getTarga())%>">
                            <br>Colore: <%= v.getColore() %><br>
                            <p class="addCart">Aggiungi al carrello</p><br>
                            <p class="prezzo">EURO <%=v.getPrezzo()%></p>
                        </div>
                        <div class="info">
                            <div class="img">
                                <img src="<%= response.encodeURL("img?Marca=" + v.getMarca())%>">
                            </div>
                            <h3><%= v.getModello() %></h3>
                        </div>
                    </div>
                </div>
            <% } %>
        </div>

mentre per la pagina veicolo.jsp è

HTML:
<table class="container">
            <tr>
                <th>ID</th>
                <th>Targa</th>
                <th>Foto</th>
                <th>Action</th>
            </tr>
          
            <% int i = 0;
               String classe = "pari";
               for (Immagine imm : immagini) {
                       if (i % 2 == 0) classe = "pari";
                    else classe = "dispari";
                    i++;
                    System.out.println("ID immagine " + imm.getID());
            %>
                    <tr>
                        <td class="<%=classe%>"><%=imm.getID()%></td>
                        <td class="<%=classe%>"><%=imm.getTarga()%></td>
                        <td class="<%=classe%>"><img class="auto" src="<%=response.encodeURL(URL + "img?ID=" + imm.getID())%>"></td>
                        <td class="<%=classe%>">
                            <a href="<%=response.encodeURL("../img?action=delete&ID=" + imm.getID() + "&Targa=" + imm.getTarga())%>">Elimina</a>
                            <a href="<%=response.encodeURL("veicolo?action=copertina&Targa="  +veicolo.getTarga() + "&ID=" + imm.getID())%>">Imposta copertina</a>
                        </td>
                    </tr>
            <%
                } if (immagini.size() == 0) {%>
                    <tr>
                        <td class="vuoto" colspan="4">Non ci sono foto</td>
                    </tr>
            <% } %>
        </table>

è tantissimo codice, scusami.
Le immagini sono salvate nel database come blob, quindi ho una servlet che legge dal database e scrive l'immgine a video. Questa servlet la richiamo nei tag <img>.
Se rimuovo i tag <img> il problema non si pone.
Una cosa che non ho capito è se va bene dichiarare i model in maniera statica o se posso crearne uno nuovo ogni volta che mi serve.

===========EDIT=============
mi sono dimenticato il connection pool

Java:
package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;


public class DriverManagerConnectionPool {

    private static List<Connection> freeDbConnections;

    static {
        freeDbConnections = new LinkedList<Connection>();
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("DB driver not found:"+ e.getMessage());
        }
    }
   
    public static synchronized Connection createDBConnection() throws SQLException {
        Connection newConnection = null;
        String ip = "localhost";
        String port = "3306";
        String db = "takeyourcar";
        String username = "takeyourcar";
        String password = "root";

        newConnection = DriverManager.getConnection("jdbc:mysql://"+ ip+":"+ port+"/"+db+"?serverTimezone=UTC", username, password);

        System.out.println("Create a new DB connection");
        newConnection.setAutoCommit(false);
        return newConnection;
    }  
   
    public static synchronized Connection getConnection() throws SQLException {
        Connection connection;

        if (!freeDbConnections.isEmpty()) {
            connection = (Connection) freeDbConnections.get(0);
            freeDbConnections.remove(0);

            try {
                if (connection.isClosed()) {
                    connection = getConnection();
                }
            } catch (SQLException e) {
                connection.close();
                connection = getConnection();
            }
        } else {
            connection = createDBConnection();      
        }

        return connection;
    }
   
    public static synchronized void releaseConnection(Connection connection) throws SQLException {
        if(connection != null)
            freeDbConnections.add(connection);
    }  
}

Ho provato ora ora a modificare il connection pool. Ho cambiato il metodo releaseConnection con questo

Java:
public static synchronized void releaseConnection(Connection connection) throws SQLException {
        if(connection != null && freeDbConnections.size() == 0)
            freeDbConnections.add(connection);
        else if (connection != null)
            connection.close();
    }

e non ci sono più problemi. Questo rafforza la mia ipotesi che il problema sia dovuto al fatto che ci sono due connessioni diverse che vedono dati diversi.
In sostanza la modifica che ho fatto fa in modo che nella lista di connessioni sia presente sempre una connessione soltanto. Quelle create in più vengono eliminate. Non credo però che sia la soluzione corretta, anzi ne sono sicuro perchè altrimenti il connection pool non avrebbe senso.