Java:线程安全数据集实现

时间:2019-02-12 14:53:58

标签: java java-ee

我创建了一个自定义SQLDataset实现,该实现接受SQL查询并将LinkedHashmap列表返回给requestcontroller,以JSP格式显示或以Excel格式下载。

能否让我知道该方法是否线程安全?

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

下面是在请求控制器中编写的代码:

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

如果多个用户使用此应用程序并依次运行不同的查询,这些代码是否起作用?

修改后的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();
    }
}

2 个答案:

答案 0 :(得分:1)

这取决于您如何使用此类。

在控制器中使用doPost方法时,它对多个用户而言是线程安全的,因为您每次都创建new SqlDataset对象。

这意味着它将仅由处理单个请求的线程使用。

您的控制器代码是可重入的并且是线程安全的。

顺便说一句,如果您打算将SqlDataset用作单例(例如Spring bean等)-这不是线程安全的。它具有在过程中使用的实例变量-这意味着SqlDataset方法不会重新进入。

只需考虑一下...

  • 每次使用private QueryProcessor qp;方法创建新实例时,您真的需要getQueryResult()吗?

  • 您真的需要private ArrayList<String> dataHeader = new ArrayList<>();吗? 尽管您只是从getHeaders()返回它-为什么不在方法内部的ArrayList循环之前不创建新的for。 ...等等...

如果将所有传递给方法的参数作为参数并返回在方法内部创建的所有东西,则将是完全线程安全的。

Singletons可能仅具有不可变的实例变量(逻辑上几乎是常量),以使某些设置或属性适用于使用它的任何线程。

答案 1 :(得分:1)

class Scratch { public String field = "class field"; public void example() { Function<String, Integer> funcWithField = s -> { field = "New field value from lambda"; // IT`s OK here return field.length(); }; String variable = "var"; Function<String, Integer> funcWithVariable = s -> { // Though such things are restricted! // variable = "some other loooong value for variable"; return variable.length(); }; } } 本身不是线程安全的,因为其中包含实例变量。

但是,如果仅在SqlDataset.java的某些方法中使用它,则不会有问题。这是因为request controller不是线程安全的,但是Servlets方法就是线程安全的。