Click to See Complete Forum and Search --> : Problems with JTable and saving data to DB


theNikki
10-24-2005, 08:38 AM
hey, I tried to build an application with Java. In this application "products" are fetched from the DB and presented in javax.swing.JTable-element. The "engine" behind this presentation consists of two classes: Class "Product" holds only accessors to "Product" type of objects. and class "ProductTable" (extended from javax.swing.AbstractTableModel) is used to call these accessors. When user presses JButton called "Save" information is saved (updated) to the database, but if user hasn't "confirmed" changes (eg moved away from the cell he has edited/pressed enter) then the actual data isn't updated in the "ProductTable" class. I wonder how to solve this kind of problem? The functionality should be that when user presses "Save" everything what he sees is saved (not the information which is hold in the "ProductTable" object)..

Another problem is that when user enters invalid value for price-cell. Some sort of pop-up should warn the user about invalid value and then system should choose that cell..I think there's some method in JTable-class to choose a cell to be edited, but I think that actually this check-out should be made in the setValueAt(object,int,int) method in the "ProductTable"-class and so it doesn't "know" which cell to be chosen to be edited, one solution could be that "ProductTable"-class should be inner class of the JTable-class and so setValueAt(object,int,int) could be used as following (in the ProductTable-class (which is now inner class of the JTable"))

public void setValueAt(Object o, int row, int column){
if(isNumeric(o)){ // if o is numeric then
// value is valid
products[row].setPrice(o);
}
else{ //else value is not numeric
//, pop-up window and set focus on // cell
popUpWindow("Invalid value");
JTable.setFocusOn(row,column); // set focus on the invalid cell
// (by calling the method of
// the "outer" class(JTable))
}
}

I just wonder is there some other way to pop-up a window and alert the user about wrong value in the cell and then set focus on this cell?

I would be thankful if anyone could help me..

BigDog
10-24-2005, 04:16 PM
Sorry, the only thing I know about Swing is how to spell it....

Waylander
10-24-2005, 09:53 PM
With validation you really want to have built your own methods that return booleans or error messages so you can call them before you call any setters or code that processes the new values.

for example with something like price:

if (validatePrice(nameValue))
//set new validvalue
else
//alert and focus

There are some classes that can get you some good alert boxes (i think its like JOptionPane or something like that) or you can just make a class that extends JFrame and make your own customizable alert box and add your own buttons and labels to it.

Its a little hard without actually seeing what you have written, what does your save button actually do? did you write its event handler or does it just call in inbuilt function within your product object?

If code is being run when you move out of the object your talking about, you should be able to at least look at what it does and do the same kind of thing in your save event code if that particular situation arises.

As for testing the values I would suggest looking into using regular expressions ive used them quite alot they are indeed very valuable.

Let me know if you need more help I dont mind working through the problems, but asking one at a time with the relevant code pasted would help both of us.

Waylander.

Oak
10-25-2005, 04:55 PM
Component includes the requestFocus method and since all JComponents are subclasses of Component, they too can access the method.

There is nothing (in this context) that can be done with an inner class that cant be done with two separate classes.

How does the TableModel normally get updated? Perhaps you can update it when each field registers a change so that it is always up to date?

theNikki
10-26-2005, 02:39 PM
Ok, here comes the code for the first problem (which is that JTable and model has different values when button is pressed). I've searched the Internet and there should be some other solution for the another problem :)

************
file:Product.java
//this class is only simple "Product" class with accessors

class Product{
String name;
double price;
public Product(String n, double p){
name=n;
price=p;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
public void setPrice(double newPrice){
price=newPrice;
}
}
// end of file

**********

file:ProductTable.java

// this class extends AbstractTableModel.
// To make things simple there are always exactly
// 4 products in the database. (testing purposes only)
// This class also constructs method "save", which UPDATES
// values in the DB. The save method is called when saveButton
// is pressed on the testTable-class

class ProductTable extends AbstractTableModel{
public product Products []=new product[4];
public String names []=new String[]{
"name","price"
};
public Class [] classes=new Class[]{
String.class,Double.class
};
// constructor for ProductTable. Note that database table
// has always 4 rows
public ProductTable(){
try{
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
Connection c=DriverManager.getConnection("jdbc:mysql://localhost/XXX/","XXX","XXX");
Statement s =c.createStatement();
int index=0;
ResultSet r=s.executeQuery("select * from products");
while(r.next()){
Products[index]=new product(r.getString(1),r.getDouble(2));
index++;
}
}
catch (Exception E){
System.err.println("Error on database connection");
E.printStackTrace();
}
}
public void save(){
// this method only constructs & executes sql-queries
// to update the products-table(which has always 4
// rows)
try{
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
Connection c=DriverManager.getConnection("jdbc:mysql://localhost/XXX/","XXX","XXX");
Statement s =c.createStatement();
for(int i=0;i<4;i++){
StringBuffer sql=new StringBuffer(512);
String name=Products[i].getName();
double price=Products[i].getPrice();
sql.append("update products set price=");
sql.append(price);
sql.append(" where name='");
sql.append(name);
sql.append("'");
s.executeUpdate(sql.toString());
}
}
catch (Exception E){
System.err.println("Error in the database conn.");
E.printStackTrace();
}
}
public int getColumnCount(){return 2;}
public int getRowCount(){return Products.length;}
public String getColumnName(int number){
return names[number];
}
public Class getColumnClass(int number){
return classes[number];
}
public Object getValueAt(int row,int column){
switch(column){
case 0:
return Products[row].getName();
case 1:
return new Double(Products[row].getPrice());
default:
return null;
}
}
// isCellEditable returns true if cell is 2 (price)
public boolean isCellEditable(int row, int column){
if(column==2){
return true;
}
else{return false;}
}
// set value on the "Price" column
public void setValueAt(Object value,int row,int column){
switch(column){
case 2: Products[row].setPrice(Double.parseDouble(value.toString()));
fireTableDataChanged();
break;
default: return;
}
}
}
//end of file

**********

file: testTable.java

// this class constructs JTable with information from ProductTable

public class testTable{
public ProductTable model;
JTable table;
JFrame frame;
JButton saveButton;
public testTable(){
model=new ProductTable();
table=new JTable(model);
JFrame frame=new JFrame("Products");
JButton saveButton=new JButton("SAVE");
saveButton.addActionListener(new buttonListener));
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(new JScrollPane(table), BorderLayout.EAST);
frame.getContentPane().add(saveButton, BorderLayout.WEST);
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
frame.setSize(600,300);
frame.setVisible(true);
}
class buttonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
// call "save"-method of the "outer class" (model)
System.out.println("Saving..");
model.save();
}
}
public static void main(String[] args){
new testTable();
}
}
//end of file

Waylander
10-26-2005, 10:43 PM
Your going to have to edit that post so we can get some code tags around it, there is a little button for it on the post screen.

theNikki
10-27-2005, 08:12 AM
<pre> file:Product.java
//this class is only simple "Product" class with accessors

class Product{
String name;
double price;
public Product(String n, double p){
name=n;
price=p;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
public void setPrice(double newPrice){
price=newPrice;
}
}
// end of file

**********

file:ProductTable.java

// this class extends AbstractTableModel.
// To make things simple there are always exactly
// 4 products in the database. (testing purposes only)
// This class also constructs method "save", which UPDATES
// values in the DB. The save method is called when saveButton
// is pressed on the testTable-class

class ProductTable extends AbstractTableModel{
public product Products []=new product[4];
public String names []=new String[]{
"name","price"
};
public Class [] classes=new Class[]{
String.class,Double.class
};
// constructor for ProductTable. Note that database table has always 4 rows
public ProductTable(){
try{
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
Connection c=DriverManager.getConnection("jdbc:mysql://localhost/XXX/","XXX","XXX");
Statement s =c.createStatement();
int index=0;
ResultSet r=s.executeQuery("select * from products");
while(r.next()){
Products[index]=new product(r.getString(1),r.getDouble(2));
index++;
}
}
catch (Exception E){
System.err.println("Error on database connection");
E.printStackTrace();
}
}
public void save(){
// this method only constructs & executes sql-queries
// to update the products-table(which has always 4 rows)
try{
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
Connection c=DriverManager.getConnection("jdbc:mysql://localhost/XXX/","XXX","XXX");
Statement s =c.createStatement();
for(int i=0;i<4;i++){
StringBuffer sql=new StringBuffer(512);
String name=Products[i].getName();
double price=Products[i].getPrice();
sql.append("update products set price=");
sql.append(price);
sql.append(" where name='");
sql.append(name);
sql.append("'");
s.executeUpdate(sql.toString());
}
}
catch (Exception E){
System.err.println("Error in the database conn.");
E.printStackTrace();
}
}
public int getColumnCount(){return 2;}
public int getRowCount(){return Products.length;}
public String getColumnName(int number){
return names[number];
}
public Class getColumnClass(int number){
return classes[number];
}
public Object getValueAt(int row,int column){
switch(column){
case 0:
return Products[row].getName();
case 1:
return new Double(Products[row].getPrice());
default:
return null;
}
}
// isCellEditable returns true if cell is 2 (price)
public boolean isCellEditable(int row, int column){
if(column==2){
return true;
}
else{return false;}
}
// set value on the "Price" column
public void setValueAt(Object value,int row,int column){
switch(column){
case 2: Products[row].setPrice(Double.parseDouble(value.toString()));
fireTableDataChanged();
break;
default: return;
}
}
}
//end of file

**********

file: testTable.java

// this class constructs JTable with information from ProductTable

public class testTable{
public ProductTable model;
JTable table;
JFrame frame;
JButton saveButton;
public testTable(){
model=new ProductTable();
table=new JTable(model);
JFrame frame=new JFrame("Products");
JButton saveButton=new JButton("SAVE");
saveButton.addActionListener(new buttonListener));
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(new JScrollPane(table), BorderLayout.EAST);
frame.getContentPane().add(saveButton, BorderLayout.WEST);
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
frame.setSize(600,300);
frame.setVisible(true);
}
class buttonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
// call "save"-method of the "outer class" (model)
System.out.println("Saving..");
model.save();
}
}
public static void main(String[] args){
new testTable();
}
}
//end of file
</pre>

Waylander
10-27-2005, 11:45 PM
So the values you enter are not being entered into the database properly?

we need to narrow down the possible line locations of the error, what would be the next step is to put some lines of manual debug into your code. In each of the classes and methods where the data is handled put in:

System.out.println(<variable1>+" "+<variable1> );

using the actual variable names with out the brackets, then paste the output here, at the moment its hard to fix the problem, your question is still a little vague, we are getting there but we just need more specifics and to narrow down its location.

theNikki
10-28-2005, 12:48 PM
hey,

I tried following code on my "save" button and it worked
public void actionPerformed(ActionEvent e){
javax.swing.table.TableCellEditor editori=taulu.getCellEditor();
if(editori!=null){
editori.stopCellEditing();
}
}

And the other problem, well I think I'll just have to make a subclass for the JTable-class and then edit it's setValueAt(object value, int row, int column)-method as following:

setValueAt(object value, int row, int column){
if(column==price){
if(!isNumeric(value)){
raiseError();
return;
}
}
super.setValueAt(object value, int row, int column);
}

thanks for your interest anyhow