如何在 Java servlet 中处理多个请求
How to handle multiple request in Java servlet
我有一个 UI,其中 HTML table 的输入字段作为单元格,一些输入字段在 HTML table 之外,所以有我正在输入数据,然后使用 servlets
将保存数据保存到我的数据库中
一次只有一个用户使用它时工作正常,但当两个用户同时单击保存时会出现问题
我做的是:
- 点击保存后,我调用我的
servlet
class,我在其中编写了将数据插入数据库的代码
- 所以在我的
servlet
class 中,首先我是 运行 查询以获得 max+1 号。来自 db 的列,然后保存它并在我的插入查询中进一步使用该值
我面临的问题是:
当两个用户同时点击保存时,获取 max+1 的查询运行,下一个插入数据的查询运行,但第二个抛出错误 java.sql.BatchUpdateException: Duplicate entry '2' for key 'PRIMARY'
因为那个是数据库中的主键列,不能重复
但是为什么它只需要 2
就像只有一个没有当两个用户点击保存按钮时它应该需要 2,3 我想在这里我没有处理我的多个请求servlet
我什至不知道如何处理,我在谷歌上搜索了很多并在那里读到它自己一次管理多个处理的 servlet
所以我的问题是当多个用户同时单击保存按钮时我想将数据插入我的数据库,如果有两个用户同时单击保存会导致问题
我的代码
int grnNo;
Connection con = null;
Statement statement = null;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String[] itemCode = request.getParameterValues("itemCodetd");
String[] unitCode = request.getParameterValues("unittd");
try {
con = DBConnection.createConnection();
statement = con.createStatement();
String grnNoSql = "select max(GRNNUMBER)+1 as GRNNo from egoodsreceived";
ResultSet resultSet1 = statement.executeQuery(grnNoSql);
while (resultSet1.next()) {
int grnNoLocal = resultSet1.getInt("GRNNo");
if (grnNo != 0) {
grnNo = grnNoLocal;
System.out.println("in if :" + grnNo);
} else {
grnNo = 1;
System.out.println("in else :" + grnNo);
}
}
String query1 = "query to insert data";
PreparedStatement ps = con.prepareStatement(query1);
for (int i = 0; i < itemCode.length; i++) {
if (itemCode[i] != "") {
System.out.println("in for :" + grnNo);
ps.setInt(1, grnNo);
ps.setString(2, itemCode[i]);
ps.setString(3, unitCode[i]);
ps.addBatch();
}
}
ps.executeBatch(); // here getting erro as duplicate primary key
} catch (SQLException e) {
System.out.println("SQL EXCPTION 91");
e.printStackTrace();
}
doGet(request, response);
}
问题是您在 servlet 中使用了实例变量。 JVM 中每个 servlet 只有一个实例,因此并发请求将尝试同时使用同一个变量。
将 grnNo
、con
和 statement
重构为局部变量,然后包装 select 并插入到单个事务中。
您应该定义要使用 MySQL 的自动递增功能的列:
https://dev.mysql.com/doc/refman/8.0/en/example-auto-increment.html
CREATE TABLE X(
id MEDIUMINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
然后您不需要在插入语句中指定列或其值:该值将由数据库自动分配。
另一个答案提出了关于 Servlet 中实例变量的线程安全的有效观点,但是,在不添加同步块的情况下,使grnNo
局部变量无法解决问题:
//Thread T1 executes the below and get the next val
//before T1 has written its changes, T2 executes the below and gets the next val
String grnNoSql = "select max(GRNNUMBER)+1 as GRNNo from egoodsreceived";
如果你需要在应用程序中插入后生成的标识符,那么你可以使用java.sql.statement的getGeneratedKeys()
方法:
https://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#getGeneratedKeys()
我有一个 UI,其中 HTML table 的输入字段作为单元格,一些输入字段在 HTML table 之外,所以有我正在输入数据,然后使用 servlets
一次只有一个用户使用它时工作正常,但当两个用户同时单击保存时会出现问题
我做的是:
- 点击保存后,我调用我的
servlet
class,我在其中编写了将数据插入数据库的代码 - 所以在我的
servlet
class 中,首先我是 运行 查询以获得 max+1 号。来自 db 的列,然后保存它并在我的插入查询中进一步使用该值
我面临的问题是:
当两个用户同时点击保存时,获取 max+1 的查询运行,下一个插入数据的查询运行,但第二个抛出错误
java.sql.BatchUpdateException: Duplicate entry '2' for key 'PRIMARY'
因为那个是数据库中的主键列,不能重复但是为什么它只需要
2
就像只有一个没有当两个用户点击保存按钮时它应该需要 2,3 我想在这里我没有处理我的多个请求servlet
我什至不知道如何处理,我在谷歌上搜索了很多并在那里读到它自己一次管理多个处理的 servlet所以我的问题是当多个用户同时单击保存按钮时我想将数据插入我的数据库,如果有两个用户同时单击保存会导致问题
我的代码
int grnNo;
Connection con = null;
Statement statement = null;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String[] itemCode = request.getParameterValues("itemCodetd");
String[] unitCode = request.getParameterValues("unittd");
try {
con = DBConnection.createConnection();
statement = con.createStatement();
String grnNoSql = "select max(GRNNUMBER)+1 as GRNNo from egoodsreceived";
ResultSet resultSet1 = statement.executeQuery(grnNoSql);
while (resultSet1.next()) {
int grnNoLocal = resultSet1.getInt("GRNNo");
if (grnNo != 0) {
grnNo = grnNoLocal;
System.out.println("in if :" + grnNo);
} else {
grnNo = 1;
System.out.println("in else :" + grnNo);
}
}
String query1 = "query to insert data";
PreparedStatement ps = con.prepareStatement(query1);
for (int i = 0; i < itemCode.length; i++) {
if (itemCode[i] != "") {
System.out.println("in for :" + grnNo);
ps.setInt(1, grnNo);
ps.setString(2, itemCode[i]);
ps.setString(3, unitCode[i]);
ps.addBatch();
}
}
ps.executeBatch(); // here getting erro as duplicate primary key
} catch (SQLException e) {
System.out.println("SQL EXCPTION 91");
e.printStackTrace();
}
doGet(request, response);
}
问题是您在 servlet 中使用了实例变量。 JVM 中每个 servlet 只有一个实例,因此并发请求将尝试同时使用同一个变量。
将 grnNo
、con
和 statement
重构为局部变量,然后包装 select 并插入到单个事务中。
您应该定义要使用 MySQL 的自动递增功能的列:
https://dev.mysql.com/doc/refman/8.0/en/example-auto-increment.html
CREATE TABLE X(
id MEDIUMINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
然后您不需要在插入语句中指定列或其值:该值将由数据库自动分配。
另一个答案提出了关于 Servlet 中实例变量的线程安全的有效观点,但是,在不添加同步块的情况下,使grnNo
局部变量无法解决问题:
//Thread T1 executes the below and get the next val
//before T1 has written its changes, T2 executes the below and gets the next val
String grnNoSql = "select max(GRNNUMBER)+1 as GRNNo from egoodsreceived";
如果你需要在应用程序中插入后生成的标识符,那么你可以使用java.sql.statement的getGeneratedKeys()
方法:
https://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#getGeneratedKeys()