2015-06-12 3 views
0

Я недавно начал работать над java webapp (JSP/Servlet), разработанным внутренним разработчиком компании.Соединение с базой данных страдает от одновременных потоков

Это приложение случайно не возвращает данные и проверяет журнал. Я нашел некоторые NullPointerExceptions, связанные с переменной-членом класса, которая содержит соединение с базой данных. После трассировки стека кажется, что второй поток закрывает соединение после того, как он закончил свою задачу, оставив первый поток без соединения.

В соответствии с потребностями компании приложение использует разные базы данных, которые управляют приложением и другими, которые содержат данные, которые приложение должно извлечь. Таким образом, каждый класс, подключенный к основному сервлету, может подключаться к одной или нескольким базам данных в зависимости от задачи, которую он должен выполнить.

Я не знаком с JavaEE, но взглянув на класс подключения к базе данных, я не вижу ничего, что защищает потоки от конфликтов друг с другом.

Каков правильный способ обработки таких соединений?

Это код обработчика данных:

package it.metmi.mmasgis.utils; 

import java.sql.*; 
import java.util.ArrayList; 
import java.util.HashMap; 

import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 

public class DBManager 
{ 
    private String szDatabase; 
    private String szUsername; 
    private String szPassword; 
    private String szError; 
    private Connection db; 
    private boolean bConnected; 
    private Logger logger; 

    public DBManager(String szDBName) 
    { 
     this(szDBName, "", ""); 
    } 

    public DBManager(String szDBName, String szName, String szPass) 
    { 
     szDatabase = szDBName; 
     szUsername = szName; 
     szPassword = szPass; 
     bConnected = false; 
     szError = ""; 
     logger = LogManager.getFormatterLogger(DBManager.class.getName()); 
    } 

    public boolean connect() 
    { 
     logger.entry(); 

     try { 
      Class.forName("com.mysql.jdbc.Driver"); 

      if(!szDatabase.isEmpty()) 
      { 
       String szCon = "jdbc:mysql://localhost/" + szDatabase; 

       if(!szUsername.isEmpty()) 
       { 
        szCon += "?user=" + szUsername; 

        if(!szPassword.isEmpty()) 
         szCon += "&password=" + szPassword; 
       } 

       db = DriverManager.getConnection(szCon); 
       bConnected = true; 
      } else { 
       logger.error("No database name!!"); 
       System.exit(0); 
      } 
     } catch(SQLException | ClassNotFoundException e) { 
      szError = e.getMessage(); 
      e.printStackTrace(); 
      logger.error("Can't connect: %s", e); 
     } 

     return logger.exit(bConnected); 
    } 

    public void disconnect() 
    { 
     logger.entry(); 

     try { 
      db.close(); 
      bConnected = false; 
     } catch(SQLException e) { 
      e.printStackTrace(); 
      logger.error("Can't disconnect: %s", e); 
     } 

     logger.exit(); 
    } 

    public boolean isConnected() 
    { 
     return bConnected; 
    } 

    public String getError() 
    { 
     return szError; 
    } 

    public ArrayList<HashMap<String,String>> query(String szQuery) 
    { 
     logger.entry(szQuery); 

     ArrayList<HashMap<String,String>> aResults = new ArrayList<HashMap<String,String>>(); 

     int iCols = 0; 
     try { 
      Statement stmt = db.createStatement(); 

      logger.info("Query: %s", szQuery); 
      ResultSet rs = stmt.executeQuery(szQuery); 
      ResultSetMetaData rsmd = rs.getMetaData(); 
      iCols = rsmd.getColumnCount(); 

      while(rs.next()) 
      { 
       HashMap<String,String> pv = new HashMap<String,String>(); 
       for(int i = 0; i < iCols; i++) 
       { 
        String szCol = rsmd.getColumnLabel(i + 1); 
        String szVal = rs.getString(i + 1); 
        pv.put(szCol, szVal); 
       } 
       aResults.add(pv); 
      } 
      rs.close(); 
      stmt.close(); 
     } catch(SQLException e) { 
      e.printStackTrace(); 
      szError = e.getMessage(); 
      logger.error("Error executing query: %s", e); 
     } 

     return logger.exit(aResults); 
    } 

    public boolean update(String szQuery) 
    { 
     logger.entry(szQuery); 

     boolean bResult = false; 

     try { 
      Statement stmt = db.createStatement(); 

      logger.info("Query: %s", szQuery); 
      stmt.executeUpdate(szQuery); 

      bResult = true; 
      stmt.close(); 
     } catch(SQLException e) { 
      e.printStackTrace(); 
      szError = e.getMessage(); 
      bResult = false; 
      logger.error("Error executing query: %s", e); 
     } 
     return logger.exit(bResult); 
    } 
} 

Целевая класс, который все классы сервлетов основаны на, является простой абстрактный класс:

package it.metmi.mmasgis.servlet; 

import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

public abstract class Task 
{ 
    public abstract void doTask(HttpServletRequest request, HttpServletResponse response); 
} 

Класс, который бросает NullPointerExceptions это это, во время вызова db.disconnect(). Этот класс называется быстро через AJAX 4 или 5 раз из интерфейса, написанного в JS.

package it.metmi.mmasgis.servlet.params; 

import it.metmi.mmasgis.servlet.Task; 
import it.metmi.mmasgis.utils.Const; 
import it.metmi.mmasgis.utils.DBManager; 
import it.metmi.mmasgis.utils.Query; 
import it.metmi.mmasgis.utils.Utility; 

import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import java.io.IOException; 
import java.io.PrintWriter; 
import java.util.ArrayList; 
import java.util.HashMap; 

import com.google.gson.Gson; 
import com.google.gson.GsonBuilder; 

import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 

public class ClassType extends Task 
{ 
    private DBManager db = null; 
    private Logger logger = LogManager.getFormatterLogger(ClassType.class.getName()); 

    @Override 
    public void doTask(HttpServletRequest request, HttpServletResponse response) 
    { 
     logger.entry(request, response); 

     String szCensimento = Utility.getParameter(request, "censimento"); 
     String szCategoria = Utility.getParameter(request, "category"); 
     ArrayList<HashMap<String,String>> aClasses = new ArrayList<HashMap<String,String>>(); 
     PrintWriter out = null; 

     logger.debug("Census: %s", szCensimento); 
     logger.debug("Category: %s", szCategoria); 

     db = new DBManager(szCensimento, Const.DB_USER, Const.DB_PASS); 

     if(db.connect()) 
     { 
      String szQuery = String.format(Query.classes, szCategoria, szCategoria); 
      aClasses = db.query(szQuery); 

      db.disconnect(); 
     } 

     try { 
      out = response.getWriter(); 
      jsonEncode(aClasses, out); 
     } catch(IOException e) { 
      e.printStackTrace(); 
      logger.error("Failed to encode JSON: %s", e); 
     } 

     logger.exit(); 
    } 

    private void jsonEncode(ArrayList<HashMap<String,String>> aData, PrintWriter out) 
    { 
     HashMap<String,Object> result = new HashMap<String,Object>(); 
     result.put("results", aData); 
     result.put("success", true); 

     Gson gson = new GsonBuilder().create(); 
     gson.toJson(result, out); 
    } 
} 

Если веб-приложение будет использовать только одну базу данных, это может быть переписано как Singleton, но в этом случае я не имею ни малейшего представления о том, как обрабатывать различные соединения для различных баз данных. Как избежать этих исключений?

+0

В принципе, это неправильное решение для обработки таких случаев вручную. Почему бы не использовать некоторые распространенные методы, например, использовать Spring и Spring JDBC для обработки всего этого. Вы делаете много работы вручную, которую вы не должны делать. Взгляните на это: https://spring.io/guides/gs/relational-data-access/ –

+0

Я просто прочитал руководство, которое вы связали. Так что мне нужно переписать весь webapp для использования Spring? Кроме того, это приложение не основано на Gradle, Maven или что-то еще. – HelLViS69

+0

Это просто возможный и популярный подход, если вы планируете увеличить свое приложение. Но если это не так, продолжайте читать и прочитайте этот пост, пожалуйста. http://stackoverflow.com/questions/5003142/jsp-using-mvc-and-jdbc/5003701#5003701 –

ответ

0

Проблема заключалась в том, что объект соединения был объявлен как член. Перемещение переменной внутри методов.

Смежные вопросы