数据库连接受并发线程的影响

时间:2015-06-12 07:50:42

标签: java java-ee servlets database-connection servlet-3.0

我最近开始研究由公司内部开发人员开发的java webapp(JSP / Servlet)。

这个应用程序随机地不返回数据,并检查日志我发现了一些与类相关的NullPointerExceptions'保存数据库连接的成员变量。在堆栈跟踪之后,似乎第二个线程在结束任务后关闭连接,而第一个线程没有连接。

根据公司的需要,应用程序使用不同的数据库,一个用于规范appdata,另一个用于包含应用必须检索的数据。因此,附加到主servlet的每个类都可以连接到一个或多个数据库,具体取决于它必须完成的任务。

我不熟悉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);
    }
}

所有servlet类所基于的类Task是一个简单的抽象类:

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);
}

在调用db.disconnect()期间抛出NullPointerExceptions的类。这个类通过AJAX 4快速调用,或者用JS编写的接口调用5次。

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);
    }
}

如果webapp只使用一个数据库,它可以重写为Singleton,但是这样我就不知道如何处理不同数据库的不同连接。 如何避免这些例外?

1 个答案:

答案 0 :(得分:0)

问题是连接对象被声明为成员。 在已解决的方法中移动变量。