我为每个生成的订单项调用了以下函数。有没有人有任何想法如何加快这一点?
private String getDetails(String doc){
String table="";
java.sql.ResultSet rs = qw.DBquery("select " +
"id,LineType, QtyTotal, ManufacturerPartNumber, Description, UnitCost,UnitPrice " +
"From DocumentItems " +
"where DocID="+doc+" order by linenumber " +
"");
table+= "<table class=inner><thead><colgroup><col id='col1'><col id='col2'><col id='col3'><col id='col4'><col id='col5'></colgroup>" +
"<tr class='enetBlue'><th>Qty</th><th>Part Num</th><th>Description</th><th>Unit Cost</th><th>Unit Price</th></tr></thead>" +
"<tbody>";
try{
int odd = 0;
while(rs.next()){
int lineType = rs.getInt("LineType");
int qty = rs.getInt("QtyTotal");
String part = rs.getString("ManufacturerPartNumber");
String desc = rs.getString("Description");
float cost = rs.getFloat("UnitCost");
float price = rs.getFloat("UnitPrice");
String id = rs.getString("id");
String clas="";
if (odd==0) odd=1; else odd=0;
clas="red";
if (lineType==2) clas="yellow";
if (lineType==3) clas="yellow";
if (lineType==4) clas="yellow";
if (qty==0) clas="yellow";
java.sql.ResultSet rs2 = mas.DBquery("select itemkey from timitem where itemid = '"+part+"'");
while (rs2.next())
{
if (odd==1) clas="odd";
if (odd==0) clas="even";
}
table+="<tr class='"+clas+"'><td>"+qty+"</td>\n"+
"<td>"+part+"</td>\n"+
"<td>"+desc+"</td>\n"+
"<td>"+cost+"</td>\n"+
"<td>"+price+"</td></tr>\n";
//if clas=red | means item is not found in MAS, gear for insert.
if (clas=="red") {
table+="<tr ><td colspan=5><table border=1><tr><td colspan=2>\n";
//get unit measure key
try {
table+="<form name=masinsert"+id+" method=get action=MASInsert>\n";
table+="<input type=hidden name=\"partnumber"+id+"\" value=\""+part+"\">\n";
table+="<input type=hidden name=\"itemcost"+id+"\" value=\""+cost+"\">\n";
table+="<input type=hidden name=\"itemlistprice"+id+"\" value=\""+price+"\">\n";
table+="<input type=hidden name=\"itemdescription"+id+"\" value=\""+desc+"\">\n";
table+="</td><tr>\n";
java.sql.ResultSet rsUM = mas.DBquery("select * from tciUnitMeasure where companyid like 'ENS' ");
table+="<tr bgcolor=#990033><td align=left valign=top>Unit Measure</td><td align=left valign=top><select name=\"UnitMeasKey\">";
while(rsUM.next())
{
table+="<option value=\"" + rsUM.getString("UnitMeasKey") + "\">" + rsUM.getString("UnitMeasID") + "</option>\n";
}//end while rs1
table+="</select></td></tr>\n";
//build ItemClass options from mas: Puchase ProductLine
java.sql.ResultSet rsPP = mas.DBquery("select * from timPurchProdLine where companyID = 'ENS'");
int k = 0;
table+= "<tr bgcolor=#990033><td align=left valign=top>Purchase Product Line</td><td align=left valign=top><select name=\"PurchProdLine\">\n";
while(rsPP.next())
{
table+="<option value=\"" + rsPP.getString("PurchProdLineKey") + "\">" + rsPP.getString("Description") + "</option>\n";
}//end while rsPP
table+="</select></td></tr>\n";
//build item classkey options
java.sql.ResultSet rsIC = mas.DBquery("select * from timItemClass where companyID = 'ENS' order by itemclassname desc");
table+= "<tr bgcolor=#990033><td align=left valign=top>Item Class :</td><td align=left valign=top><select name=\"itemclasskey\">\n";
while(rsIC.next())
{
table+="<option value=\"" + rsIC.getString("itemclasskey") + "\">" + rsIC.getString("ItemClassName") + "</option>\n";
}//end while rs1
table+="</select></td></tr>";
table+="<tr><td colspan=2><input id='m"+id+"' type=\"button\" onclick=\"masinsert('"+ id +"')\" value=\"Add to MAS\"></td></tr>";
table+="</table>\n";
}catch(Exception e){} //end try
table+="</form>\n";
table+="</td></tr>";
}//end if clas=red
}//end while
}catch(java.sql.SQLException e){
e.printStackTrace();}
table+="</tbody></table>";
return table;
}
提前致谢
答案 0 :(得分:8)
使用预编译的参数化PreparedStatment,而不是每次都使用String连接构建它。这也将解决当前代码(如果 doc 是用户输入的变量)易受SQL注入攻击的事实。
答案 1 :(得分:7)
了解如何编写JSP并停止在servlet中嵌入HTML和CSS。这在1998年是个坏主意,当时很常见;从那以后,我们取得了长足的进步。
当循环遍历一个ResultSet并在循环内执行查询时,这意味着对于您带回第一个查询的每一行的网络往返。这肯定是你问题中最大的瓶颈。消除对于初学者。
我建议将数据库代码移出servlet并转移到可以测试的持久性对象中,而无需启动servlet引擎。
根本没有关于这种方法的面向对象。看起来像发票或BillOfMaterials,但我没有看到任何对象。
答案 2 :(得分:5)
使用'+'运算符追加字符串非常慢,因为由于字符串的不变性,JVM需要迭代每个追加的字符串中的每个字符。
您也可以使用SQL JOIN,而不必迭代每条记录并制定新查询。你基本上只需要打一次数据库。
您的循环内部也有一个查询,它始终返回相同的数据:
java.sql.ResultSet rsUM = mas.DBquery("select * from tciUnitMeasure where companyid like 'ENS' ");
这应该移到循环之外,但我怀疑你可以使用JOIN消除这个和其他嵌套查询。
答案 3 :(得分:2)
您在每次迭代中都有一条select * from timPurchProdLine where companyID = 'ENS'
行。该表的内容是否会随着每次迭代而改变?因为如果它们是静态的,如果你只在循环之前读取该表的内容一次,那么你将节省很多时间,从而创建你需要的子字符串。然后,您只需在需要时将该字符串附加到循环中。 select * from timItemClass where companyID = 'ENS' order by itemclassname desc
查询也是如此。
不要使用“+”来连接字符串。使用StringBuilder或者如果您使用的是Java 5或6,请使用String.format()。此外,我认为使用ResultSet方法更快,这些方法采用列号而不是参数的列名,也就是说,getString(1)比getString(“id”)更快。
像odd = 1-odd
这样的东西可以节省几个CPU周期,但它可能不会有明显的改进;但如果你使用布尔值而做odd = !odd
而不是if (odd==0) odd=1; else odd=0
的东西,那就更容易理解了。
答案 4 :(得分:2)
我根本没有得到第二个查询。
java.sql.ResultSet rs2 = mas.DBquery(...);
while (rs2.next())
{
if (odd==1) clas="odd";
if (odd==0) clas="even";
}
你执行查询并迭代它但你没有从rs2中得到任何东西,你根据odd的值设置clas,但不要修改循环内的odd值,这意味着没有任何意义int while while while循环,更不用说查询了。此外,如果查询没有结果,则偶数/奇数逻辑根本不会运行。
编辑:
'奇怪'处理是奇怪的。使用布尔值...
boolean odd = false;
...
odd = !odd; // instead of your if statement
...
clas = odd ? "odd" : "even"; // inside the while loop.
您的代码包含
String clas = "";
...
clas = "red";
在第一次使用时声明clas并使用合理的默认值初始化它。
// String clas = ""; remove this line
...
String clas = "red";
此行不好
if (clas == "red") {
首先,与字符串进行参考比较是不好的形式。我使用idiomn
if ("red".equals(clas))
但即便如此,你最好还是存储一个布尔值,看看你是否找到了一个值并对其进行测试而不是字符串比较。
java.sql.ResultSet rsPP = mas.DBquery("select * from timPurchProdLine where companyID = 'ENS'");
如其他地方所述,在循环内做一堆不依赖循环变量的查询是一个巨大的浪费。事先做所有查询并缓存结果值或HTML。
答案 5 :(得分:2)
代码有很多改进。
我会尝试解决其中一些最相关的问题。
最相关的。这应该使用jsp / servlet / beans。
但由于代码的结构不是那样,我不会进一步解释。更好的是解释servlet的改进。
该方法在一个地方做了太多事情。为了使它更好,需要几种实用方法,虽然这并不能提高它们的性能,但绝对有助于维护代码并更清楚地了解方法的目的(允许修改代码以进行改进。
代码的某些部分根本不会改变。它们应该声明为常量。例如,有三个组合永远不会改变。它们应该是常量,这样,如果代码使用String“+”并不重要,因为它们只被调用一次。
打开嵌套的结果集。在打开并使用 ResultSet 之前,应该使用并关闭之前的结果。这样连接就有机会节省数据库资源。
qw.DBquery 也许这个代码中真正存在的问题就是这个对象。我不知道代码是什么样的,但可能它不使用连接池,也不会在每次执行查询时关闭连接。这可能意味着对于给定的代码,最多可以打开5个同时连接。虽然只需要一个。下一次更糟糕的是(如果连接没有关闭)可以使用另一个新的5个连接,可能一个连接可以完成几个小时的所有工作。我们不知道。
除了使用“+”替换字符串连接之外,我认为数据库资源的使用正在扼杀这个servlet的性能。
最后,我尝试对代码进行重大的重构。
我希望这有助于更好地理解应用程序应该如何完成。我没有编译它,所以它不会工作。这样做只是为了显示如何修复上述项目。
import java.sql.ResultSet;
import java.util.List;
import java.util.ArrayList;
// etc etc. etc
首先,有许多字符串可以声明为常量。
public class SomeServlet extends HttpServlet {
// ALL THE FOLLOWING ARE CONSTANTA IN THE SERVLET.
// THEIR VALUE DOES'T CHANGE DURING EXECUTION
private static final String SELECT_DOCUMENT_QUERY =
"SELECT \n" +
" id,LineType, QtyTotal, ManufacturerPartNumber, Description, UnitCost,UnitPrice \n" +
" FROM DocumentItems \n" +
" WHERE DocID=%s order by linenumber ";
private static final String HTML_HEADER_TABLE =
"<table class=inner><thead><colgroup><col id='col1'>"+
"<col id='col2'><col id='col3'><col id='col4'><col id='col5'></colgroup>" +
"<tr class='enetBlue'><th>Qty</th><th>Part Num</th><th>Description</th>"+
"<th>Unit Cost</th><th>Unit Price</th></tr></thead><tbody>";
// These constants are the HTML Commbo for the given queries.
// If they are initialized once at the beginning will increase
// the servlets performace considerabily.
private static final String UNIT_MEASURES_COMBO =
getCombo( "Unit Measure", "UnitMeasKey",
getListFrom( "select * from tciUnitMeasure where companyid like 'ENS' ") );
private static final String ITEM_CLASS_COMBO =
getCombo( "Purchase Product Line", "PurchProdLine",
getListFrom( "select * from timPurchProdLine where companyID = 'ENS' ") );
private static final String CLASS_KEY_COMBO =
getCombo( "Item Class :", "itemclasskey",
getListFrom( "select * from timItemClass where companyID = 'ENS' order by itemclassname desc") );
然后,尽管这绝对不是正确的方法,但是servlet可能会定义一些bean来帮助他完成这项工作,我们可以创建这样的类:
// Use a class to separete domain objects from presentation
// This class becomes handy do pass data through methods.
class Document {
int lineType;
int qty;
String part;
String desc;
float cost;
float price;
String id;
}
// simple clas that holds a key/value pair
class ComboPair {
String key;
String value;
public ComboPair( String key, String value ) {
this.key = key;
this.value = value;
}
}
最后重构的代码。当一个方法做了太多事情时,它可以将工作委托给其他人。当两段代码看起来相同时,它们应该使用辅助方法。
/*
* Finally the fixed code.
*
* Basically a method should do only one thig. If the method is doing a lot of things
* several functionality at the same time, it should delegate the details of the subfunctionality
* to another function. This way each function or method is easier to read/maintain/understand.
* This method should read like this:
*
* For a given docId,
* - query the database and display details of the document
* - if the document is not present in mas?
* - create a form to inser it
*
* differntiate each record with different style.
*/
private String getDetails(String doc){
// Close each result set before openning a new one.
ResultSet rs = qw.DBquery( String.format( SELECT_DOCUMENT_QUERY, doc ));
List<Document> documents = new ArrayList<Document>();
while(rs.next()){
documents.add( createDocumentFrom( rs ));
}
// Iterate through
StringBuilder resultingTable = new StringBuilder( HTML_HEADER_TABLE );
boolean isEven = false;// starts as odd
for( Document doc : documents ) {
String clazz = getClassFor( doc , isEven );
isEven = !isEven;
resultingTable.append("<tr class='"+clazz+"'>"+
"<td>"+doc.qty+"</td>\n"+
"<td>"+doc.part+"</td>\n"+
"<td>"+doc.desc+"</td>\n"+
"<td>"+doc.cost+"</td>\n"+
"<td>"+doc.price+"</td></tr>\n");
if( needsInsertForm( clazz ) ) {
resultingTable.append( getInsertForm( document ) );
}
resultingTable.append("</tbody></table>");
}
return table;
}
/**
* This methods craates an instance of "Document".
* Instead of mixing the fetch code with the render code
* this method allows to separete the data from its presentation
*/
private Document createDocumentFrom( ResultSet rs ) {
Document document = new Document();
document.lineType = rs.getInt("LineType");
document.qty = rs.getInt("QtyTotal");
document.part = rs.getString("ManufacturerPartNumber");
document.desc = rs.getString("Description");
document.cost = rs.getFloat("UnitCost");
document.price = rs.getFloat("UnitPrice");
document.id = rs.getString("id");
return document;
}
// Computes the correct css class for the given document.
private static String getClassFor( Document document, boolean isEven ) {
String clazz = "red";
switch( document.lineType ) {
case 2:
case 3:
case 4: clazz ="yellow";
}
if( document.qty == 0 ) {
clazz = "yellow";
}
ResultSet rs = mas.DBquery( String.format("select itemkey from timitem where itemid = '%s'", document.part);
while (rs.next()) {
clazz = isEven? "even" : "odd";
}
rs.close();
return clazz;
}
// Creates the inser form for the given document
private static String getInsertForm( Document document ) {
StringBuilder form = new StringBuilder();
form.append("<tr ><td colspan=5><table border=1><tr><td colspan=2>\n");
form.append("<form name=masinsert"+document.id+" method=get action=MASInsert>\n");
form.append("<input type=hidden name=\"partnumber"+document.id+"\" value=\""+document.part+"\">\n");
form.append("<input type=hidden name=\"itemdescription"+document.id+"\" value=\""+document.desc+"\">\n");
form.append("<input type=hidden name=\"itemcost"+document.id+"\" value=\""+document.cost+"\">\n");
form.append("<input type=hidden name=\"itemlistprice"+document.id+"\" value=\""+document.price+"\">\n");
form.append("</td><tr>\n");
//----------------------------------------------------------------------------------------------------------------------------
//get unit measure key
form.append(UNIT_MEASURES_COMBO);
//build ItemClass options from mas: Puchase ProductLine
form.append(ITEM_CLASS_COMBO);
//build item classkey options
form.append( CLASS_KEY_COMBO);
//----------------------------------------------------------------------------------------------------------------------------
form.append("<tr><td colspan=2><input id='m"+document.id+"' type=\"button\" onclick=\"masinsert('"+ document.id +"')\" value=\"Add to MAS\"></td></tr>");
form.append("</table>\n";
form.append("</form>\n";
form.append("</td></tr>");
return form.toString();
}
// This is an utility method that reads bettern when used in the
// main method.
// if( needsInsertForm( clazzz ) ) {
// is much clearer
private static boolean needsInsertForm( String clazz ) {
return "red".equals(clazz);
}
此处不包括两种方法。它们仍然很混乱,但它们有助于创建常量。这样,它们不是每次都创建这些字符串,而是在servlet生命期间创建一次。
完整的代码在http://pastebin.com/f2ad1510d
我希望它有所帮助。
答案 6 :(得分:2)
这是学习使用分析器的好时机。这将告诉您实际花费的时间,以便了解性能改进的内容。
如果您使用最新的java 6,那么我建议您查看visualvm。 (jvisualvm在JDK bin目录中)。
它可以分析已经运行的java程序!
答案 7 :(得分:2)
从循环中获取查询。加入循环外的所有必要表。仅此一项就可以为您提供最佳性能提升。您可以使用StringBuffer resp节省几个额外的纳秒。 StringBuilder的。
答案 8 :(得分:1)
请使用模板引擎,你在那里做什么这是一个可怕的犯罪:)
我建议Apache Velocity,在任何java应用程序中迭代都很容易......当然也会尝试优化检索数据的方式......
答案 9 :(得分:0)
我会缓存在doc参数上键入的函数的输出。根据应用程序使用模式,它可以极大地加速您的应用程序。
答案 10 :(得分:0)
这里有一些代码被提取到自己的函数中并适合使用StringBuffer和String.format()。
private String timItemClassTable() throws SQLException {
StringBuffer sb = new StringBuffer();
ResultSet rs = mas.DBquery("select * from timItemClass where companyID = 'ENS' order by itemclassname desc");
sb.append("<tr bgcolor=#990033><td align=left valign=top>Item Class :</td><td align=left valign=top><select name=\"itemclasskey\">\n");
while (rs.next())
sb.append(String.format("<option value=\"%s\">%s</option>\n", rs.getString("itemclasskey"), rs.getString("ItemClassName")));
sb.append("</select></td></tr>");
return sb.toString();
}
使用这样的较小方法可以更容易地找到代码中的瓶颈,并且更容易调整代码以使其更快(例如通过尽可能在循环外移动方法调用)。较小的方法也更容易测试和调试。
对代码进行重构本身并不会使代码更快,但它会让您的代码更易于使用,它可以让您更轻松地使代码更快。