Я недавно начал работать над 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, но в этом случае я не имею ни малейшего представления о том, как обрабатывать различные соединения для различных баз данных. Как избежать этих исключений?
В принципе, это неправильное решение для обработки таких случаев вручную. Почему бы не использовать некоторые распространенные методы, например, использовать Spring и Spring JDBC для обработки всего этого. Вы делаете много работы вручную, которую вы не должны делать. Взгляните на это: https://spring.io/guides/gs/relational-data-access/ –
Я просто прочитал руководство, которое вы связали. Так что мне нужно переписать весь webapp для использования Spring? Кроме того, это приложение не основано на Gradle, Maven или что-то еще. – HelLViS69
Это просто возможный и популярный подход, если вы планируете увеличить свое приложение. Но если это не так, продолжайте читать и прочитайте этот пост, пожалуйста. http://stackoverflow.com/questions/5003142/jsp-using-mvc-and-jdbc/5003701#5003701 –