[英]Java: Thread-Safe Dataset implementation
I have created a custom SQLDataset implementation where it takes a SQL query and returns a List of LinkedHashmap back to the requestcontroller to be displayed in JSP or download in Excel format. 我创建了一个自定义SQLDataset实现,该实现将执行SQL查询并将LinkedHashmap列表返回给requestcontroller,以JSP格式显示或以Excel格式下载。
Could you please let me know if the approach is thread safe? 您能否让我知道该方法是否线程安全?
SqlDataset.java SqlDataset.java
package com.sqle.core;
import com.util.QueryProcessor;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class SqlDataset implements Serializable {
private String query;
private QueryProcessor qp;
private ResultSet rsSet;
private List<LinkedHashMap<String, Object>> rsList = new ArrayList<>();
private ArrayList<String> dataHeader = new ArrayList<>();
public SqlDataset() {}
public SqlDataset(String uquery) {
this.query = uquery;
}
private ResultSet getQueryResult() throws Exception {
qp = new QueryProcessor(query);
this.rsSet = qp.getQueryResultSet();
return this.rsSet;
}
public List<LinkedHashMap<String, Object>> getResult() throws Exception {
return this.getValues(this.getQueryResult());
}
public List<LinkedHashMap<String, Object>> getResult(String query) throws Exception {
this.query = query;
return this.getValues(this.getQueryResult());
}
public int getRowCount() {
return this.rsList.size();
}
public ArrayList getHeaders() {
for (LinkedHashMap<String, Object> aRsList : this.rsList) {
for (Map.Entry<String, Object> dh : aRsList.entrySet()) {
if (!this.dataHeader.contains(dh.getKey()))
this.dataHeader.add(dh.getKey());
}
}
return this.dataHeader;
}
private List<LinkedHashMap<String, Object>> getValues(ResultSet rs) throws SQLException {
ResultSetMetaData rmd = rs.getMetaData();
int columns = rmd.getColumnCount();
while (rs.next()) {
LinkedHashMap<String, Object> row = new LinkedHashMap<>(columns);
for (int i = 1; i <= columns; ++i) {
row.put(rmd.getColumnName(i), rs.getObject(i));
}
this.rsList.add(row);
}
return this.rsList;
}
}
Below is the code written in request controller: 下面是在请求控制器中编写的代码:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String act = request.getParameter("act").toLowerCase();
RequestDispatcher rd = request.getRequestDispatcher("sqleditor.jsp");
try {
if (act.equalsIgnoreCase("exec")) {
String uqry = request.getParameter("isql");
if (!uqry.isEmpty()) {
SqlDataset sd = new SqlDataset(uqry);
rslist = sd.getResult();
if (sd.getRowCount() > 0) {
headRow = sd.getHeaders();
request.setAttribute("resHead", headRow);
request.setAttribute("result", rslist);
} else {
throw new NoDataException("No data found to display");
}
} else {
throw new NoDataException("Please enter a query");
}
rd.forward(request, response);
} else if (act.equalsIgnoreCase("excel")) {
String uqry = request.getParameter("isql");
if (!uqry.isEmpty()) {
try {
SqlDataset sd = new SqlDataset();
rslist = sd.getResult(uqry);
if (sd.getRowCount() > 0) {
headRow = sd.getHeaders();
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=\"" + FILENAME + "\"");
ExcelWriter ew = new ExcelWriter();
ew.initExcelfile(rslist, headRow, response.getOutputStream());
} else {
throw new NoDataException("No data found to download");
}
} catch (Exception evar1) {
throw new AppException(evar1.getMessage());
}
} else {
throw new NoDataException("Please enter a query");
}
}
} catch (SQLException evar2) {
request.setAttribute("errormsg", evar2.getMessage());
rd.forward(request, response);
} catch (NullPointerException evar3) {
request.setAttribute("errormsg", evar3.getMessage());
rd.forward(request, response);
} catch (Exception evar4) {
request.setAttribute("errormsg", evar4.getMessage());
rd.forward(request, response);
}
}
Will this code work is multiple users use this application and running different queries successively? 如果多个用户使用此应用程序并依次运行不同的查询,此代码是否有效?
Modified SQLdataset class: 修改后的SQLdataset类:
package com.sqle.core;
import com.util.QueryProcessor;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class SqlDataset implements Serializable {
private List<LinkedHashMap<String, Object>> rsList = new ArrayList<>();
public SqlDataset() {}
private ResultSet getQueryResult(String query) throws Exception {
QueryProcessor qp = new QueryProcessor(query);
ResultSet rsSet = qp.getQueryResultSet();
return rsSet;
}
public List<LinkedHashMap<String, Object>> getResult(String query) throws Exception {
return this.getValues(this.getQueryResult(query));
}
public ArrayList getHeaders() {
ArrayList<String> dataHeader = new ArrayList<>();
for (LinkedHashMap<String, Object> aRsList : this.rsList) {
for (Map.Entry<String, Object> dh : aRsList.entrySet()) {
if (!dataHeader.contains(dh.getKey()))
dataHeader.add(dh.getKey());
}
}
return dataHeader;
}
private List<LinkedHashMap<String, Object>> getValues(ResultSet rs) throws SQLException {
ResultSetMetaData rmd = rs.getMetaData();
int columns = rmd.getColumnCount();
while (rs.next()) {
LinkedHashMap<String, Object> row = new LinkedHashMap<>(columns);
for (int i = 1; i <= columns; ++i) {
row.put(rmd.getColumnName(i), rs.getObject(i));
}
this.rsList.add(row);
}
return this.rsList;
}
public int getRowCount() {
return this.rsList.size();
}
}
It depends on how you use this class. 这取决于您如何使用此类。
With your doPost
method in controller it is thread safe for multiple users because you create new SqlDataset
object every time. 在控制器中使用
doPost
方法时,它对于多个用户而言是线程安全的,因为每次都创建new SqlDataset
对象。
It means it will be used only by thread which processes a single request. 这意味着它将仅由处理单个请求的线程使用。
Your Controller code is re-entrant and thread safe. 您的控制器代码是可重入且线程安全的。
BTW in case if you plan to use your SqlDataset
as singleton (eg Spring bean or such) - it is not thread safe. 顺便说一句,如果您打算将
SqlDataset
用作单例(例如Spring bean等),则它不是线程安全的。 It has instance variables used in process - it means SqlDataset
methods are not re-entrant. 它具有在过程中使用的实例变量-这意味着
SqlDataset
方法不可重入。
just think about them... 想想看...
do you really need private QueryProcessor qp;
您是否真的需要
private QueryProcessor qp;
while you create new instance every time in the getQueryResult()
method? 每次在
getQueryResult()
方法中创建新实例时?
do you really need private ArrayList<String> dataHeader = new ArrayList<>();
您是否真的需要
private ArrayList<String> dataHeader = new ArrayList<>();
while you just return it from getHeaders()
- why do not just create new ArrayList
before for
loops inside the method. 虽然您只是从
getHeaders()
返回它-为什么不在方法内部的for
循环之前创建新的ArrayList
。 ...and so on... ...等等...
If you make everything passed to methods as parameters and return everything created inside methods it will be fully thread safe. 如果将所有内容都作为参数传递给方法,并返回在方法内部创建的所有内容,则它将是完全线程安全的。
Singletons may have only immutable instance variables (logically almost constants) to keep some settings or properties applicable to any threads which use it. 单例可能仅具有不可变的实例变量(逻辑上几乎是常量),以使某些设置或属性适用于使用它的任何线程。
The SqlDataset.java
itself is not thread safe as you have instance variables in it. SqlDataset.java
本身不是线程安全的,因为其中包含实例变量。
However if you only use it in some of your request controller
's methods then there will be no problems. 但是,如果仅在
request controller
的某些方法中使用它,则不会有问题。 This is because a Servlet
is not thread safe but the Servlets methods are that. 这是因为
Servlet
不是线程安全的,但Servlets方法是安全的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.