我正在做一个学校项目,我无法将结果集中的数据存储在 JTable 中。以前我使用过DButils,但现在我想知道是否有一种方法可以在不必使用外部类文件的情况下做同样的事情,或者使用 DButils 是否更容易。
数据仅来自一个表,需要发生的只是数据必须显示在 JTable 中。
我会在这里发布我的代码,但我已经看过了,我能找到的唯一教程是关于如何使用和 Object [] [] 填充 JTable 的教程。我正在使用 JDBC 创建连接。
提前致谢。
那么这将需要几个步骤。
我将解释我的方式,这适用于非常大的集合,但如果您只想显示几行,则有点复杂。不过我相信它会帮助你。此方法将即时加载所需的记录,而不是预先加载整个集合。它创造了拥有整套设备的错觉,但不必等待很长时间。
1) 好的,首先,让我们假设我们有一个可以显示的漂亮 JFrame,开始。所以首先我将添加一个 JScrollPane,然后在其中添加一个 JTable。运行它并确保您有一个漂亮的窗口,其中滚动条内有一个空的 JTable。
2) 所以接下来你需要一个 JTable 的数据源。由于 JTable 是一个非常通用的组件,不是专门为 SQL 结果集制作的,因此它需要一个实现与 SQL 无关的 javax.swing.table.AbstractTableModel 的数据源。所以我们现在将创建一个实现 AbstractTableModel 的 TableModelClass,然后我们将它添加到 JTable 中,它就会开始工作。当然,诀窍是使用我们的 SQL 结果集来实现 AbstractTableModel 的所有获取数据的方法,这取决于您。从这里是我的建议->
3)由于这将是动态的,我们不需要事先加载所有数据,但我们需要一个初始设置来显示。我将有一个固定大小的 Object [][],比如说 200 - 300 行。所以我将最初执行 SQL 并用 200-300 行的缓冲区大小填充数组。缓存多少取决于两件事:1 它应该足以获取 JTable 当前显示大小的所有数据,2,它应该足够小,以便我们滚动并获取后续缓存时它执行得非常快.
4) 现在让我们开始实现 AbstractTableModel 的所有接口方法。
5)首先我们查看初始结果集并报告列数。只需添加一个类变量,设置列数并使用以下命令返回它:public int getColumnCount()。这从现在起不会改变。
6) 还要查看结果集元数据,在类中创建一个列表变量,并添加元数据中返回的列名。使用此列表返回“getColumnName(int col)”中的列名。当然,col 索引是结果集中的列位置。
7) 现在让我们执行“int getRowCount()”。在 TableModelClass 中保留一个变量来包含 rowCount 并在此方法中返回它。提示:暂时不用担心,将其设置为固定的大数字,如 65000,这将让您在动态加载记录时滚动。一旦我们到达终点,我们会将数字设置为实际值,滚动窗格将调整为正确的比例。相信我,它工作正常。
8)现在是有趣的部分。当 JTable 呈现表格的第一个“页面”并且用户滚动时,它将开始调用“getValueAt(int row, int col)”。这将直接映射到我们的 Object[][],但由于我们只有一个缓存,而不是整个表,当用户向下滚动时,我们将需要获取更多数据。我这样做:
public Object getValueAt( int row, int col )
{
// load failed before, no more trying...
if( loadExceptionOccur || ( row >= visualTableSize ) ) return( "" );
// check if requested row is OUT of cache …
try{
if(
// less than cache lower limit...
( ( row < startRow )
||
// Beyond cache upper limit...
( row >= startRow + tableDataCache.size()) )
// Stop unnecessary loading, because of Jtable readjusting
// its visual table size and redrawing the entire table.
&& !tableRedraw
// yes, get new cache...
){
load( row ); // <- below is code
}
// now we now the row is in cache, so ->
// verify requested cell in cache, or beyond data rows,
if(
// greater than lower limit
( row >= startRow )
&&
// less than upper limit...
( row < ( startRow + tableDataCache.size() ) )
){
tableRedraw = false;
// just get the data from the cache. tableDataCache is just your Object[][] array…
Object cellValue = ( ( recordClass ) tableDataCache.get( row-startRow ) ).getValueAt( col );
return ( cellValue );
}
else{
// just show as blank
return( "" );
}
}
catch( Exception error ){
…
如果缓存未命中,您需要重新加载数据缓存。我通常会在请求的行之前加载一些行,而在请求行之前加载一些行,至少对于 JTable 页面大小,这样我们只去一次数据库来渲染屏幕。缓存越大,加载前滚动越多,但加载缓存所需的时间越长。如果您对其进行微调,缓存处理可能几乎不会引起注意。
这是“加载”的实现:
public void load( int rowIndex )
throws KExceptionClass
{
// calculate start of new cache, if not enough rows for top half of cache
// then start from 0
int halfCache = cacheSize / 2 ;
int DBStartRow = 0;
if( rowIndex > halfCache ) DBStartRow = rowIndex - halfCache;
//Do query to DB
try{
SQLP.load( DBStartRow, cacheSize ); // <- using jdbc load from DbsartRow as many rows as cacheSize. Some sample SQL paging code below ->
}catch( Exception loadError ){
// if the database fails or something do this, so you don’t get a billion errors for each cell. ->
//set load failed flag, kill window
loadExceptionOccur = true;
visualTableSize = 0;
tableDataCache = new ArrayList< recordClass >();
fireTableDataChanged(); // clear the Jtable
// log error
log.log( this, KMetaUtilsClass.getStackTrace( loadError ) );
// show error message
throw new KExceptionClass( "Could not load table data! " , loadError );
}
//Load rows into the cache list.
//Key field values are in the cache list as the last field in each record.
tableDataCache.clear(); // the Object [][], wrapped in class
while( SQLPreprocessor.nextRowValue() ) {
SQL.fetch( record ); //<- get JDBC rows to table cache
tableDataCache.add( record ); // this uses my library, change to JDBC or what ever you use to access SQL
}
log.log( this, "cacheList size = " + tableDataCache.size());
//---------
if(
// Last requested row number
( DBStartRow + cacheSize ) >
// Last replied row number
( SQLPreprocessor.getloadedStartRowIndex() + SQLPreprocessor.getloadedRowCount() )
){
// It is the end of table.
// The visual table is readjusted accordingly.
visualTableSize = SQLPreprocessor.getloadedStartRowIndex() + SQLPreprocessor.getloadedRowCount();
fireTableDataChanged();
tableRedraw = true;
}
startRow = SQLPreprocessor.getloadedStartRowIndex();
log.log( this, "visualTableSize = " + visualTableSize );
}
好的,这将在小缓存中动态加载数据,这将给人一种拥有整套的印象。如果用户滚动到中间或一直滚动到最后,JTable 将只询问需要显示的数据,而不是在移动时显示所有行,所以,如果你有一个 10K 行的表,但 JTable 只有 20行高,滚动到最后只需要 40 - 50 行来加载。挺棒的。您的用户会印象深刻。
现在的问题是负载假定您有一个 SQL 游标,它按行号向前和向后移动。这个简单的事情在 SQL 中是一个相当大的挑战。对于 Oracle 检查:http ://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html
好的,希望对您有所帮助--
当然有一种方法:遍历ResultSet
并将找到的内容添加到Object [][]
传递给JTable
. 对于 ;中的每一行,二维数组中都有一行ResultSet
。列是值。
您将遇到的问题是,如果不遍历它,您将不知道返回了多少行。这就是为什么将它加载到 aMap<String, Object>
中可能是一个更好的主意。
这是一个示例,展示了如何做到这一点。您会在我对这个问题的回答中找到该方法(以及更多方法):
public static List<Map<String, Object>> map(ResultSet rs) throws SQLException {
List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
try {
if (rs != null) {
ResultSetMetaData meta = rs.getMetaData();
int numColumns = meta.getColumnCount();
while (rs.next()) {
Map<String, Object> row = new HashMap<String, Object>();
for (int i = 1; i <= numColumns; ++i) {
String name = meta.getColumnName(i);
Object value = rs.getObject(i);
row.put(name, value);
}
results.add(row);
}
}
} finally {
close(rs);
}
return results;
}