- turn off alternate color row striping to improve rendering performance
- use database paging and sorting for high volume table
This commit is contained in:
Heng Sin Low 2009-12-14 04:44:47 +00:00
parent 9ae8675b13
commit 8d11fd5139
4 changed files with 241 additions and 117 deletions

View File

@ -51,6 +51,8 @@ public class ListModelTable extends ListModelList implements ListModelExt
/** The number of columns in the table. */ /** The number of columns in the table. */
private int m_noColumns; private int m_noColumns;
private ListModelExt sorter = null;
/** /**
* Default constructor. * Default constructor.
* *
@ -324,6 +326,9 @@ public class ListModelTable extends ListModelList implements ListModelExt
*/ */
public void sort(Comparator cmpr, boolean ascending) public void sort(Comparator cmpr, boolean ascending)
{ {
if (sorter != null)
sorter.sort(cmpr, ascending);
else
Collections.sort(this.getInnerList(), cmpr); Collections.sort(this.getInnerList(), cmpr);
WTableModelEvent event = new WTableModelEvent(this, WTableModelEvent event = new WTableModelEvent(this,
@ -382,4 +387,9 @@ public class ListModelTable extends ListModelList implements ListModelExt
fireEvent(ListDataEvent.CONTENTS_CHANGED, fromRow, toRow); fireEvent(ListDataEvent.CONTENTS_CHANGED, fromRow, toRow);
} }
} }
public void setSorter(ListModelExt lme)
{
sorter = lme;
}
} }

View File

@ -44,9 +44,13 @@ public class Listbox extends org.zkoss.zul.Listbox implements EventListener
private List<EventListener> doubleClickListeners = new ArrayList<EventListener>(); private List<EventListener> doubleClickListeners = new ArrayList<EventListener>();
private List<EventListener> onDropListeners = new ArrayList<EventListener>(); private List<EventListener> onDropListeners = new ArrayList<EventListener>();
private boolean draggable; private boolean draggable;
private String oddRowSclass;
public Listbox() { public Listbox() {
super(); super();
//cache default
oddRowSclass = super.getOddRowSclass();
super.setOddRowSclass(oddRowSclass);
} }
public Listbox(KeyNamePair[] pairs) { public Listbox(KeyNamePair[] pairs) {
@ -56,6 +60,9 @@ public class Listbox extends org.zkoss.zul.Listbox implements EventListener
this.appendItem(pair.getName(), pair.getKey()); this.appendItem(pair.getName(), pair.getKey());
} }
} }
//cache default
oddRowSclass = super.getOddRowSclass();
super.setOddRowSclass(oddRowSclass);
} }
public void setEnabled(boolean enabled) public void setEnabled(boolean enabled)
@ -303,4 +310,21 @@ public class Listbox extends org.zkoss.zul.Listbox implements EventListener
} }
return b; return b;
} }
@Override
public String getOddRowSclass() {
if (oddRowSclass == null)
return null;
else
return super.getOddRowSclass();
}
@Override
public void setOddRowSclass(String scls) {
if (scls != null && scls.length() == 0)
oddRowSclass = null;
else
oddRowSclass = scls;
super.setOddRowSclass(scls);
}
} }

View File

@ -533,22 +533,33 @@ public class WListItemRenderer implements ListitemRenderer, EventListener, Listi
*/ */
protected Comparator<Object> getColumnComparator(boolean ascending, final int columnIndex) protected Comparator<Object> getColumnComparator(boolean ascending, final int columnIndex)
{ {
Comparator<Object> comparator; return new ColumnComparator(ascending, columnIndex);
final MSort sort = new MSort(0, null); }
sort.setSortAsc(ascending); public static class ColumnComparator implements Comparator<Object>
comparator = new Comparator<Object>()
{ {
private int columnIndex;
private MSort sort;
public ColumnComparator(boolean ascending, int columnIndex)
{
this.columnIndex = columnIndex;
sort = new MSort(0, null);
sort.setSortAsc(ascending);
}
public int compare(Object o1, Object o2) public int compare(Object o1, Object o2)
{ {
Object item1 = ((List<?>)o1).get(columnIndex); Object item1 = ((List<?>)o1).get(columnIndex);
Object item2 = ((List<?>)o2).get(columnIndex); Object item2 = ((List<?>)o2).get(columnIndex);
return sort.compare(item1, item2); return sort.compare(item1, item2);
} }
};
return comparator; public int getColumnIndex()
{
return columnIndex;
}
} }
/** /**

View File

@ -23,6 +23,8 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
@ -31,6 +33,7 @@ import java.util.logging.Level;
import org.adempiere.webui.apps.AEnv; import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.component.ConfirmPanel; import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.ListModelTable; import org.adempiere.webui.component.ListModelTable;
import org.adempiere.webui.component.WListItemRenderer;
import org.adempiere.webui.component.WListbox; import org.adempiere.webui.component.WListbox;
import org.adempiere.webui.component.Window; import org.adempiere.webui.component.Window;
import org.adempiere.webui.event.ValueChangeEvent; import org.adempiere.webui.event.ValueChangeEvent;
@ -49,6 +52,7 @@ import org.compiere.util.Msg;
import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.ListModelExt;
import org.zkoss.zul.Paging; import org.zkoss.zul.Paging;
import org.zkoss.zul.event.ZulEvents; import org.zkoss.zul.event.ZulEvents;
@ -62,7 +66,7 @@ import org.zkoss.zul.event.ZulEvents;
* @author Elaine * @author Elaine
* @version Info.java Adempiere Swing UI 3.4.1 * @version Info.java Adempiere Swing UI 3.4.1
*/ */
public abstract class InfoPanel extends Window implements EventListener, WTableModelListener public abstract class InfoPanel extends Window implements EventListener, WTableModelListener, ListModelExt
{ {
/** /**
* *
@ -272,6 +276,8 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
this.setMaximizable(true); this.setMaximizable(true);
this.addEventListener(Events.ON_OK, this); this.addEventListener(Events.ON_OK, this);
contentPanel.setOddRowSclass(null);
} // init } // init
protected ConfirmPanel confirmPanel; protected ConfirmPanel confirmPanel;
/** Master (owning) Window */ /** Master (owning) Window */
@ -286,7 +292,8 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
protected String p_whereClause = ""; protected String p_whereClause = "";
protected StatusBarPanel statusBar = new StatusBarPanel(); protected StatusBarPanel statusBar = new StatusBarPanel();
/** */ /** */
private Vector<Object> line; private List<Object> line;
private boolean m_ok = false; private boolean m_ok = false;
/** Cancel pressed - need to differentiate between OK - Cancel - Exit */ /** Cancel pressed - need to differentiate between OK - Cancel - Exit */
private boolean m_cancel = false; private boolean m_cancel = false;
@ -302,6 +309,7 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
private String m_sqlCount; private String m_sqlCount;
/** Order By Clause */ /** Order By Clause */
private String m_sqlOrder; private String m_sqlOrder;
private String m_sqlUserOrder;
/**ValueChange listeners */ /**ValueChange listeners */
private ArrayList<ValueChangeListener> listeners = new ArrayList<ValueChangeListener>(); private ArrayList<ValueChangeListener> listeners = new ArrayList<ValueChangeListener>();
/** Loading success indicator */ /** Loading success indicator */
@ -317,6 +325,10 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
protected WListbox contentPanel = new WListbox(); protected WListbox contentPanel = new WListbox();
protected Paging paging; protected Paging paging;
protected int pageNo; protected int pageNo;
private int m_count;
private int cacheStart;
private int cacheEnd;
private boolean m_useDatabasePaging = false;
private static final String[] lISTENER_EVENTS = {}; private static final String[] lISTENER_EVENTS = {};
@ -355,16 +367,24 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
String orderBy) String orderBy)
{ {
String sql =contentPanel.prepareTable(layout, from, String sql =contentPanel.prepareTable(layout, from,
where.toString(),p_multipleSelection, where,p_multipleSelection,
getTableName(),false); getTableName(),false);
p_layout = contentPanel.getLayout(); p_layout = contentPanel.getLayout();
m_sqlMain = sql.toString(); m_sqlMain = sql;
m_sqlCount = "SELECT COUNT(*) FROM " + from + " WHERE " + where; m_sqlCount = "SELECT COUNT(*) FROM " + from + " WHERE " + where;
// //
m_sqlOrder = ""; m_sqlOrder = "";
m_sqlUserOrder = "";
if (orderBy != null && orderBy.length() > 0) if (orderBy != null && orderBy.length() > 0)
m_sqlOrder = " ORDER BY " + orderBy; m_sqlOrder = " ORDER BY " + orderBy;
int p = from.indexOf(" ");
String tableName = p > 0 ? from.substring(0, p) : from;
MTable table = MTable.get(Env.getCtx(), tableName);
if (table != null)
{
m_useDatabasePaging = table.isHighVolume() && DB.getDatabase().isPagingSupported();
}
} // prepareTable } // prepareTable
@ -373,39 +393,24 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
*/ */
protected void executeQuery() protected void executeQuery()
{ {
line = new Vector<Object>(); line = new ArrayList<Object>();
if (!testCount()) cacheStart = -1;
cacheEnd = -1;
if (m_useDatabasePaging)
{ {
testCount();
return ; return ;
} }
PreparedStatement m_pstmt = null; else
ResultSet m_rs = null;
long start = System.currentTimeMillis();
//
String dynWhere = getSQLWhere();
StringBuffer sql = new StringBuffer (m_sqlMain);
if (dynWhere.length() > 0)
sql.append(dynWhere); // includes first AND
sql.append(m_sqlOrder);
String dataSql = Msg.parseTranslation(Env.getCtx(), sql.toString()); // Variables
dataSql = MRole.getDefault().addAccessSQL(dataSql, getTableName(),
MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
log.finer(dataSql);
try
{
m_pstmt = DB.prepareStatement(dataSql, null);
setParameters (m_pstmt, false); // no count
log.fine("Start query - " + (System.currentTimeMillis()-start) + "ms");
m_rs = m_pstmt.executeQuery();
log.fine("End query - " + (System.currentTimeMillis()-start) + "ms");
while (m_rs.next())
{ {
readLine(0, -1);
}
}
private void readData(ResultSet rs) throws SQLException {
int colOffset = 1; // columns start with 1 int colOffset = 1; // columns start with 1
Vector<Object> data = new Vector<Object>(); List<Object> data = new ArrayList<Object>();
for (int col = 0; col < p_layout.length; col++) for (int col = 0; col < p_layout.length; col++)
{ {
Object value = null; Object value = null;
@ -413,82 +418,60 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
int colIndex = col + colOffset; int colIndex = col + colOffset;
if (c == IDColumn.class) if (c == IDColumn.class)
{ {
value = new IDColumn(m_rs.getInt(colIndex)); value = new IDColumn(rs.getInt(colIndex));
} }
else if (c == Boolean.class) else if (c == Boolean.class)
value = new Boolean("Y".equals(m_rs.getString(colIndex))); value = new Boolean("Y".equals(rs.getString(colIndex)));
else if (c == Timestamp.class) else if (c == Timestamp.class)
value = m_rs.getTimestamp(colIndex); value = rs.getTimestamp(colIndex);
else if (c == BigDecimal.class) else if (c == BigDecimal.class)
value = m_rs.getBigDecimal(colIndex); value = rs.getBigDecimal(colIndex);
else if (c == Double.class) else if (c == Double.class)
value = new Double(m_rs.getDouble(colIndex)); value = new Double(rs.getDouble(colIndex));
else if (c == Integer.class) else if (c == Integer.class)
value = new Integer(m_rs.getInt(colIndex)); value = new Integer(rs.getInt(colIndex));
else if (c == KeyNamePair.class) else if (c == KeyNamePair.class)
{ {
String display = m_rs.getString(colIndex); String display = rs.getString(colIndex);
int key = m_rs.getInt(colIndex+1); int key = rs.getInt(colIndex+1);
value = new KeyNamePair(key, display); value = new KeyNamePair(key, display);
colOffset++; colOffset++;
} }
else else
{ {
value = m_rs.getString(colIndex); value = rs.getString(colIndex);
} }
data.add(value); data.add(value);
} }
line.add(data); line.add(data);
} }
}
catch (SQLException e)
{
log.log(Level.SEVERE, dataSql, e);
}
try
{
if (m_rs != null)
m_rs.close();
if (m_pstmt != null)
m_pstmt.close();
}
catch (SQLException e)
{
log.log(Level.SEVERE, "closeRS", e);
}
m_rs = null;
m_pstmt = null;
}
protected void renderItems() protected void renderItems()
{ {
Vector<String> columnHeader = getColumnHeader(p_layout); Vector<String> columnHeader = getColumnHeader(p_layout);
if (line != null) if (m_count > 0)
{ {
if (line.size() > PAGE_SIZE) if (m_count > PAGE_SIZE)
{ {
if (paging == null) if (paging == null)
{ {
paging = new Paging(); paging = new Paging();
paging.setPageSize(PAGE_SIZE); paging.setPageSize(PAGE_SIZE);
paging.setTotalSize(line.size()); paging.setTotalSize(m_count);
paging.setDetailed(true); paging.setDetailed(true);
paging.addEventListener(ZulEvents.ON_PAGING, this); paging.addEventListener(ZulEvents.ON_PAGING, this);
insertPagingComponent(); insertPagingComponent();
} }
else else
{ {
paging.setTotalSize(line.size()); paging.setTotalSize(m_count);
paging.setActivePage(0); paging.setActivePage(0);
} }
List<Object> subList = line.subList(0, PAGE_SIZE); List<Object> subList = readLine(0, PAGE_SIZE);
model = new ListModelTable(subList); model = new ListModelTable(subList);
model.setSorter(this);
model.addTableModelListener(this); model.addTableModelListener(this);
contentPanel.setData(model, null); contentPanel.setData(model, null);
@ -498,22 +481,103 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
{ {
if (paging != null) if (paging != null)
{ {
paging.setTotalSize(line.size()); paging.setTotalSize(m_count);
paging.setActivePage(0); paging.setActivePage(0);
pageNo = 0; pageNo = 0;
} }
model = new ListModelTable(line); model = new ListModelTable(readLine(0, -1));
model.setSorter(this);
model.addTableModelListener(this); model.addTableModelListener(this);
contentPanel.setData(model, null); contentPanel.setData(model, null);
} }
} }
int no = line.size(); int no = m_count;
setStatusLine(Integer.toString(no) + " " + Msg.getMsg(Env.getCtx(), "SearchRows_EnterQuery"), false); setStatusLine(Integer.toString(no) + " " + Msg.getMsg(Env.getCtx(), "SearchRows_EnterQuery"), false);
setStatusDB(Integer.toString(no)); setStatusDB(Integer.toString(no));
addDoubleClickListener(); addDoubleClickListener();
//workaround for scrollbar position problem }
contentPanel.renderAll();
private List<Object> readLine(int start, int end) {
//cacheStart & cacheEnd - 1 based index, start & end - 0 based index
if (cacheStart >= 1 && cacheEnd > cacheStart)
{
if (start+1 >= cacheStart && end+1 <= cacheEnd)
{
return end == -1 ? line : line.subList(start, end);
}
}
if (!testCount())
{
line = new ArrayList<Object>();
return line;
}
cacheStart = start + 1 - (PAGE_SIZE * 4);
if (cacheStart <= 0)
cacheStart = 1;
if (end == -1)
{
cacheEnd = m_count;
}
else
{
cacheEnd = end + 1 + (PAGE_SIZE * 4);
if (cacheEnd > m_count)
cacheEnd = m_count;
}
line = new ArrayList<Object>();
PreparedStatement m_pstmt = null;
ResultSet m_rs = null;
long startTime = System.currentTimeMillis();
//
String dynWhere = getSQLWhere();
StringBuffer sql = new StringBuffer (m_sqlMain);
if (dynWhere.length() > 0)
sql.append(dynWhere); // includes first AND
if (m_sqlUserOrder != null && m_sqlUserOrder.trim().length() > 0)
sql.append(m_sqlUserOrder);
else
sql.append(m_sqlOrder);
String dataSql = Msg.parseTranslation(Env.getCtx(), sql.toString()); // Variables
dataSql = MRole.getDefault().addAccessSQL(dataSql, getTableName(),
MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
if (end > start && DB.getDatabase().isPagingSupported())
{
dataSql = DB.getDatabase().addPagingSQL(dataSql, cacheStart, cacheEnd);
}
log.finer(dataSql);
try
{
m_pstmt = DB.prepareStatement(dataSql, null);
setParameters (m_pstmt, false); // no count
log.fine("Start query - " + (System.currentTimeMillis()-startTime) + "ms");
m_rs = m_pstmt.executeQuery();
log.fine("End query - " + (System.currentTimeMillis()-startTime) + "ms");
while (m_rs.next())
{
readData(m_rs);
}
}
catch (SQLException e)
{
log.log(Level.SEVERE, dataSql, e);
}
finally
{
DB.close(m_rs, m_pstmt);
}
return line;
} }
private void addDoubleClickListener() { private void addDoubleClickListener() {
@ -556,7 +620,7 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
countSql = MRole.getDefault().addAccessSQL (countSql, getTableName(), countSql = MRole.getDefault().addAccessSQL (countSql, getTableName(),
MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO); MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
log.finer(countSql); log.finer(countSql);
int no = -1; m_count = -1;
try try
{ {
@ -565,7 +629,7 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
ResultSet rs = pstmt.executeQuery(); ResultSet rs = pstmt.executeQuery();
if (rs.next()) if (rs.next())
no = rs.getInt(1); m_count = rs.getInt(1);
rs.close(); rs.close();
pstmt.close(); pstmt.close();
@ -573,10 +637,10 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
catch (Exception e) catch (Exception e)
{ {
log.log(Level.SEVERE, countSql, e); log.log(Level.SEVERE, countSql, e);
no = -2; m_count = -2;
} }
log.fine("#" + no + " - " + (System.currentTimeMillis()-start) + "ms"); log.fine("#" + m_count + " - " + (System.currentTimeMillis()-start) + "ms");
//Armen: add role checking (Patch #1694788 ) //Armen: add role checking (Patch #1694788 )
//MRole role = MRole.getDefault(); //MRole role = MRole.getDefault();
@ -956,15 +1020,12 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
pageNo = pgNo; pageNo = pgNo;
int start = pageNo * PAGE_SIZE; int start = pageNo * PAGE_SIZE;
int end = start + PAGE_SIZE; int end = start + PAGE_SIZE;
if (end > line.size()) List<Object> subList = readLine(start, end);
end = line.size();
List<Object> subList = line.subList(start, end);
model = new ListModelTable(subList); model = new ListModelTable(subList);
model.setSorter(this);
model.addTableModelListener(this); model.addTableModelListener(this);
contentPanel.setData(model, null); contentPanel.setData(model, null);
//workaround for scrollbar position problem
contentPanel.renderAll();
contentPanel.setSelectedIndex(0); contentPanel.setSelectedIndex(0);
} }
} }
@ -1045,5 +1106,23 @@ public abstract class InfoPanel extends Window implements EventListener, WTableM
this.detach(); this.detach();
} // dispose } // dispose
public void sort(Comparator cmpr, boolean ascending) {
WListItemRenderer.ColumnComparator lsc = (WListItemRenderer.ColumnComparator) cmpr;
if (m_useDatabasePaging)
{
int col = lsc.getColumnIndex();
m_sqlUserOrder = " ORDER BY " + p_layout[col].getColSQL();
if (!ascending)
m_sqlUserOrder += " DESC ";
executeQuery();
renderItems();
}
else
{
Collections.sort(line, lsc);
renderItems();
}
}
} // Info } // Info