How do I use use data from a Resultset in a JTable? - java

I am doing a school project and I am having trouble with storing the data from a resultset in a JTable. Previously I had used DButils but now I am wondering if there is a way to do the same thing without having to used external class files or if it is easier to use DButils.
The data is coming from only one table and all that needs to happen is the data must be displayed in the JTable.
I would post my code here but I have looked and the only tutorials I could find were ones on how to populate a JTable using and Object [][]. I am using JDBC to create the connection.
Thanks in advance.

Well this will require several steps.
I will explain my way, which is good for very large sets, but a little complicated if you only want to show a few lines. Still I'm sure it will help you. This method will load the required records on the fly, not the whole set before hand. It creates the illusion of having the whole set, but with out having to wait for a lengthy load.
1) Ok, first, let's assume that we have a nice JFrame that you can display, to start with. So first I will add a JScrollPane, and inside it I will add a JTable. Run it and make sure you have a nice window with an empty JTable inside scroll bars.
2) So next you need a data source for the JTable. Since a JTable is a very generic component not made specifically for SQL resultSets, it requires a data source that implements javax.swing.table.AbstractTableModel which has nothing to do with SQL. So we will now create a TableModelClass which will implement AbstractTableModel, then we will add this to the JTable and it will start working. Of course, the trick is to implement all of AbstractTableModel's methods that get data by using our SQL result set, and this is up to you. From here is my suggestion ->
3) Since this will be dynamic, we will not need to load all the data before hand, but we need an initial set to display. I will have a Object [][] of a fixed size, lets say 200 - 300 rows. So I will initially execute the SQL and fill the array with the buffer size of 200-300 rows. How much to cache will depend on 2 things: 1 It shall be enough to get all the data for the current display size of the JTable, and 2, it should be small enough so that as we scroll and get subsequent caches it executes very fast.
4) Now let's begin implementing all AbstractTableModel's interface methods.
5) First we look at the initial result set and report the number of columns. Just add a class variable, set the column count and return it using: public int getColumnCount( ). This will not change from now.
6) Also looking at the result set metadata, make a list variable in the class and add the column names returned in the meta data. Using this list return the column names in "getColumnName( int col )". Of course, the col index is the column position in the result set.
7) Now lets do "int getRowCount( )". Inside the TableModelClass keep a variable to contain the rowCount and return it in this method. TIP: Don’t worry for now, set it to a fixed large number like 65000, this will let scroll as you dynamically load the records. Once we hit the end we will set the number to the real value and the scroll pane will adjust to the correct proportions. Trust me, it works ok.
8) Now comes the fun part. As the JTable presents the first "page" of the table and as the user scrolls it will begin calling "getValueAt( int row, int col )". This will map directly to our Object[][], but since we only have a cache, and not the whole table, as the user scrolls down we will need to fetch more data. I do this:
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 ){
…
In case of a cache miss you need to reload a cache of data. I will normally load some rows before the requested row and some beyond, at least for a JTable page size, so that we only go once to the db to render a screen. The bigger the cache the more scrolling before loading, but the larger the time it takes to load a cache. If you fine tune it, the cache processing might be almost unnoticeable.
Here is the implementation of "load":
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 );
}
Ok this will dynamically load the data in small caches which will give the impression of having the whole set.
If the user scrolls to the middle or all the way to the end, the JTable will ask only for the data need to display not all the rows as it moves, so, if you have a 10K row table, but the JTable is only 20 rows high, a scroll to the end will only take 40 - 50 rows to load. Pretty nice. Your users will be impressed.
Now the thing is that the load assumes that you have a SQL cursor that moves forward and backwards by row number. This simple thing is quite a challenge in SQL. For Oracle check : http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html
Ok, hope that helps.--

Of course there's a way: iterate through the ResultSet and add what you find to the Object [][] array that gets passed to the JTable. There's one row in the 2D array for each row in the ResultSet; the columns are the values.
The problem you'll have is that you won't know how many rows came back without iterating through it. That's why loading it into a Map<String, Object> might be a better idea.
Here's an example showing how to do it. You'll find that method (and more) at my answer to this question:
java sql connections via class
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;
}

Related

Can I compare resultsets like this? I'm facing the below error

I have 2 ResultSets. 1st ResultSet contains the records from table1 from database1 and 2nd ResultSet contains the records from table2 from database2. I need a list of records from resultset1 which are not present in resultSet2. For this I wrote this logic but it is not working and throwing me the following error.
java.sql.SQLException: Invalid operation for read only resultset: deleteRow
if ( table1ResultSet != null )
{
while ( table1ResultSet.next() )
{
final String table1Record = table1ResultSet.getString( 1 );
if ( table2ResultSet != null )
{
while ( table2ResultSet.next() )
{
final String table2Record = table2ResultSet.getString( 1 );
if ( table1Record.toString().equalsIgnoreCase( table2Record.toString() ) )
{
table1ResultSet.deleteRow();
break;
}
}
}
}
}
return table1ResultSet;
That exception says what the problem is - your result set doesn't support delete. In order to have updateable result set there are some requirements:
When you prepare statement did you make it with ResultSet.CONCUR_UPDATABLE?
A query can select from only a single table without any join operations.
The query must select all non-nullable columns and all columns that do not have a default value. A query cannot use "SELECT * ". Cannot select derived columns or aggregates such as the SUM or MAX of a set of columns.
You might want to move the results sets into Java sets before working doing what you are doing though because using deleteRow will actually delete the row from the database (unless that's the expected result)
There is another problem with your code though. Even if delete works your code will fail on the second iteration of result set 1 because you never reset table2ResultSet and for the second iteration there won't be more results in table2resulset.
But on top of all that. Why would you go through all that hussle and get all that rows that you don't need instead of doing it with one single query like:
select * from table 1 where id not in select id from table 2
or
delete from table 1 where id not in select id from table 2
if that's the goal
Your logic:
Assumes the records come in some order (which may or may not be true, depending on your SQL)
Consumes the entire result set 2 for each row of result set 1, which is unlikely your intent
Deletes things, which is also not what you mentioned in the question
Your question can be implemented easily as such:
Set<String> list1 = new HashSet<>();
while (table1ResultSet.next())
list1.add(table1ResultSet.getString(1).toLowerCase());
while (table2ResultSet.next())
list1.remove(table2ResultSet.getString(1).toLowerCase());
System.out.println(list1);
This will print all the values (without duplicates) that are present in the first result set, but not in the second.

Insert checkbox in DefaultTableModel using database data

I'm using DefaultTableModel in NetBeans in showing my records from MySQL database. My data is able to display, but what I want is that to display a checkbox column at the end of my table.
I understand it needs to be overridden, but I don't know how and where to start. I see tons of example from the internet but they are using static string data, not from database. Until now I still don't get it. A help will be much appreciated.
Below is my sample code.
try {
conn = DatabaseConnect.connect();
ps = conn.prepareStatement("SELECT productID, name, quantity, price, checked FROM tbl_inventory");
rs = ps.executeQuery();
jTable2.setModel(DbUtils.resultSetToTableModel(rs));
} catch(SQLException ex) {
}
jTable2 is able to display my records from tbl_inventory. The "checked" column from my database table tbl_inventory has default boolean value of 0. But I don't know how to display it as checkbox in my JTtable.
The "checked" column from my database table tbl_inventory has default boolean value of 0. But I don't know how to display it as checkbox in my JTtable.
The easiest way is to convert the "checked" data to a Boolean value as you create the TableModel. Then the default renderer/editor will be used.
jTable2.setModel(DbUtils.resultSetToTableModel(rs));
This means you can't use the above method. You need to copy the data yourself and do the conversion on the checked column.
Check out Table From DataBase. The last example Table From Database Example shows how to copy the data without any conversion.
You will need to modify the code with something like:
while (rs.next())
{
Vector<Object> row = new Vector<Object>(columns);
for (int i = 1; i <= columns; i++)
{
if (i == ?) // convert checked column
{
int value = rs.getInt(i);
row.addElement( value == 0 ? Boolean.FALSE : Boolean.TRUE );
}
else
row.addElement( rs.getObject(i) );
}
data.addElement( row );
}

Function works only with hard coded values

This is the code I am working on:
if(connection.doDatabaseRead(findSQL))
{
ResultSet retRES = connection.getResultSet();
int i = 0;
// did we find anything
while( retRES.next() )
{
//read result from query
suiteNum.add(retRES.getString(i)); // this is the problem
i++;
//let other threads breathe
Thread.yield();
}
}
suiteNum is a string vector
When I try to add the database results to the vector the code crashes with this error.
java.sql.SQLException: Column Index out of range, 0 > 1.
I have the same piece of code working elsewhere in the program but I use real numbers like 0, 1 and 2 instead of i and it works fine.
As I do not know how many results the database request will have I need it to be dynamic but it will only work hard coded.
How can I make it work with i ?
The argument to getString is the column index, not the row index as you seem to think. The function returns the value of the given column in the current row, while next advances the cursor to the next row.
You probably mean:
suiteNum.add(retRES.getString(1));
in which case you can lose i altogether.
Java ResultSet objects are 1-indexed in this regard. The first element is at 1, not 0. See the javadoc.
EDIT: That's true too, but indeed the problem is this appears to be used as a row index! it's certainly the column.
This is your problem:
i = 0;
...
retRES.getString(i);
ResultSet.getString(i) gets a String from column number i
You want something like
while(retRes.next()) {
add(retRes.getString(1);
}
column index starts from 1
As I do not know how many results the database request will have I need it to be dynamic but it will only work hard coded. How can I make it work with i
ResultSetMetaData rsMetaData = rs.getMetaData();
int numberOfColumns = rsMetaData.getColumnCount();
See Also
ResultSetMetaData
Let your i start with 1 as specified in the API docs
if(connection.doDatabaseRead(findSQL))
{
ResultSet retRES = connection.getResultSet();
int i = 1;
// did we find anything
while( retRES.next() )
{
//read result from query
suiteNum.add(retRES.getString(i)); // this is the problem
i++;
//let other threads breathe
Thread.yield();
}
}

How can Selenium RC process dynamic rows?

I have a table similar to the following:
Part #
Price
Status
1st Part #
$1.00
OK
2nd Part #
$2.00
Discontinued
Nth Part #
$N.00
Reordered
My java code will be looking for the status of "Nth Part #" where I have no idea how big the table is, how many columns it has, and no idea what N is (until run time). In Ruby/WATIR, I would have used the table's id to grab it's HTML, and then used Ruby to iterate over the rows until the part # matched, and then check that row's corresponding status in the Status column (whichever column that might be, but it's set in the hd header's row).
Selenium's standard table lookup function selenium.getTable("table.1.2") only works for static tables that contain the same contents for each test. The overkill selenium.get_html_source is a waste since selenium knows how to find the table already, plus then I have to parse the entire web page.
Any ideas on how I can grab the html of the table, and what would be the best way to iterate over the rows and/or columns?
Thanks in advance.
The easiest thing to do would be to use getTable like this
selenium.getTable("table." + (1 + n) + ".3")
to get the "Status" cell for the nth row if you know what n will be at runtime.
If you are trying to iterate over all of the rows in the table, you could do something like this
try {
for(int n = 1; true; n++) {
String cellContents = selenium.getTable("table." + n + ".3");
//do something with n
}
}
catch {
//handle end of table
}
or, alternatively
final int rowCount = (int)selenium.getXPathCount("id('table')/tbody/tr");
for(int n = 1; n < rowCount; n++) {
String cellContents = selenium.getTable("table." + n + ".3");
}
Remember that in getTable(locator.row.column), row and column start at 1.
Not exactly what you're asking for, but I solved a similar problem by assigning the unique id (part number it sounds like in your case) to be the html id of the tr. Then I used the Selenium xpath locators to get the row and columns I needed for my test.

Creating JTable Row Header

I'm new to JTable.I'm working in Swings using JTable & Toplink(JPA). I have two buttons "add Row", "Del Row" and I have some records displayed from db. when ever "add row" is clicked a new record, row header should be added to JTable with an auto increment number displayed in sequential order to the JTable Row Header.
During deletion using "Del row " button the record has to be deleted & not its corresponding header so that next rows got updated to the previous headers & the auto increment number remain unchanged and always be in sequence.
please help me on this regard.
Thanks in advance,
Chandu
As you are new to JTable, I'd start with How to Use Tables. As suggested there, I'd extend AbstractTableModel, overriding getValueAt(), setValueAt(), getRowCount(), etc. to use the corresponding JPA entity and controller classes and methods. The NetBeans IDE, for example, can generate such classes automatically. There is also a feature to bind the classes to Swing components, including JTable and JList, but I haven't tried it.
If the row id is an auto increment number managed by the database, I'm not sure there's a reliable way to maintain the appearance of sequential order in a multi-user environment. If the field is displayed at all, isCellEditable() should probably return false for that column. Is that what you mean by Row Header?
Addendum: See also What’s the best way get the ID of the item just inserted?
Do you mean like this?
likeThis http://img705.imageshack.us/img705/8920/screenshot7ry.png
When a new record is added its id is set as a row header ( as in records, 1,2,3 ) and when a record is deleted, the row header remains, but the information is gone ( as in records 4,5 which use to have data, but after being removed only leave the id )
IF that's what you mean.
What you have to do is create two table models, one wrapping the other.
The only functionality of the wrapper will be to show an extra column ( the id ) and keep an internal "copy" of the data.
When a row is added, the wrapper will get a new record, it will assign a new id and then it will take all the fields and copy the value into his own data and then will forward it to the wrapped table model.
When a row is removed, the wrapper will not remove its own record, it will only clean up ( set to "" ) the column values and will keep the id column. Then it will forward the event to the wrapped column which in turn will actually remove the row.
Something like the following noncompiling-java
// Wrapper table model
class IdTableModel implements TableModelListener {
TableModel original;
// Analog to: Object[][]
List<List<Object> data = new ArrayList<List<Object>();
// will take a copy of the data in original
public IdTableModel( TableModel original ) {
this.original = original;
// fillData with values from original
for( int row = 0 ; row < original.getRowCount(); row++ ) {
List<Object> shadowRow = new ArrayList<Object>();
shadowRow.add( row ); // the id
// copy the values from original to this shadow
for( int column = 0 ; column < original.getColumnCount(); column++ ) {
shadowRow.add( original.getValueAt(row,i));
}
// add the shadow to the data
data.add( shadowRow );
}
original.addTableModelListener( this );
}
// for Object getValueAt( x, y ) get the data from your "data" not from the original
....
//Here's the magic. this wapper will be a listener of the original
// then this method will be invoked when the original gets more data
// or when it is removed.....
public void tableChanged( TableEvent e ) {
int row = e.getFirstRow();
List<Object> shadowRow = data.get( row);
switch( e.getType() ) {
case TableEvent.INSERT:
if( ( shadowRow =) == null ) {
// there wasn't shadow data at that position
data.add( ( shadowRow = new ArrayList<Object>()));
}
// takevalues from original
shadowRow.add( row ); // the id
for( int i = 0 ; i < original.getColumnCount();i++ ) {
shadowRow.add( original.getValueAt(row,i));
}
break;
case TableEvent.UPDATE:
// similar to insert, but you don't create a new row
// takevalues from original
for( int i = 0 ; i < original.getColumnCount();i++ ) {
shadowRow.set( i+1, original.getValueAt(row,i));
}
break;
case TableEvent.DELETE:
// You don't delete your shadow rows, but just clean it up
for( int i = 0 ; i < original.getColumnCount();i++ ) {
shadowRow.set( i+1, "");
}
break;
}
// forward calls to the original
public int getColumnCount(){
return original.getColumnCount()+1;
}
public String getColumnName( int columnIndex ) {
if( columnIndex == 0 ) {
return "id";
} else {
return original.getColumnName( columnIndex -1 );
}
}
// and so on for, isEditable etc. etc
}
Well that was much more code that what I initially wanted.
The idea is to have a table model wrapper that adds that fictional id column for you and don't delete it, you just use it like this:
JTable yourTable = new JTable( new IdTableModel( originalTableModel ));
If nothing of what I said make sense to you, start reading this: How to use tables
This page might be what you're looking for: http://www.chka.de/swing/table/row-headers/JTable.html

Categories

Resources