简体   繁体   English

Java:线程安全数据集实现

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM