Professional Documents
Culture Documents
Existing Connection
Add/Modify/Delete
public class CustomerOrderDemo extends JFrame implements ActionListener { DBTable dBTable1; JButton printButton; JButton previewButton; JButton findButton; JButton replaceButton; JButton refreshButton; JComboBox skinCombo; JComboBox columnCombo; JComboBox selectionCombo; JTextField filterText; JButton filterButton ; JLabel filterLabel; JCheckBox filterCheckbox;
OrderModel myCellModel = new OrderModel(); MyChangeListener myChangeListener = new MyChangeListener(); MyCellListener myCellListener = new MyCellListener(); MyErrorListener myErrorListener = new MyErrorListener(); OrderPrintProperties printProp = new OrderPrintProperties();
try { //set database properties //simple text driver is just a sample implementation, this driver is not //production ready, so don't use in your projects dBTable1.connectDatabase("jdbc.SimpleText.SimpleTextDriver", "jdbc:SimpleText:" + System.getProperty("user.dir") + java.io.File.separatorChar + "database", "" ,"");
//To get the jdbc logs, remove the following comment //DriverManager.setLogStream(System.out);
//to get the debug messages for quicktable, uncomment //dBTable1.debug = true;
//allows users to press Ctrl-c to copy to excel //and press ctrl-p to paste the data copied from excel dBTable1.enableExcelCopyPaste();
//create a toolbar for this frame with all the buttons to print,search,skin,filter etc createToolBar();
//this method sets the queries, column properties and calls refresh() for order information
showOrders();
setVisible(true); }
public void actionPerformed(ActionEvent e) { if( e.getSource() == printButton) { //print the contents in quicktable using the printProperties dBTable1.print(printProp); } else if( e.getSource() == previewButton) { //show the print preview window using the given printProperties dBTable1.printPreview(printProp); } else if( e.getSource() == findButton) { //show the find window to search dBTable1.doFind(); } else if( e.getSource() == replaceButton) {
//show the find and replace window dBTable1.doFindAndReplace(); } else if( e.getSource() == refreshButton) { //execute the current query again & refresh the data try { //fetch the data from database to fill the table dBTable1.refresh(); } catch(SQLException ex) { ex.printStackTrace(); } } else if( e.getSource() == skinCombo) { //based on the user selection set the Skin String selection = (String)skinCombo.getSelectedItem();
else if(selection.equals("Cool")) { dBTable1.setSkin(new CoolSkin()); } else if(selection.equals("Pulse")) { dBTable1.setSkin(new PulseSkin()); } else { dBTable1.setSkin(null); } } else if( e.getSource() == selectionCombo) { //based on user selection show the respective data if(selectionCombo.getSelectedItem().equals("Order")) { showOrders(); } else if(selectionCombo.getSelectedItem().equals("Product")) { showProducts(); } else if(selectionCombo.getSelectedItem().equals("Customer"))
//set the first columns name to the filter label filterLabel.setText("Find " + dBTable1.getColumn(0).getColumnName());
//if the user has entered any valid filter text if( filterString != null && !("".equals(filterString)) ) { //search the first column , for the filter text dBTable1.filter(0, DBTable.EQUAL , filterText.getText()); } else { //show all the records without filtering dBTable1.filter(null);
} } else if( e.getSource() == filterCheckbox ) { //once this checkbox is selected, we show all the records //whose first column has an odd number if( filterCheckbox.isSelected()) dBTable1.filter(new OddFilter()); else dBTable1.filter(null);
public void showOrders() { //remove all the different settings which is used by products/customer query //this removes UpdateSql,InsertSql,DeleteSql,ErrorListener,CellListener,ChangeListener,cellPropertiesModel,Comparato r dBTable1.clearAllSettings();
try { // set the select sql to get data from the order table
//update the changes based on order number dBTable1.addUpdateSql("update order set Amount=?,Payment_Type=?,Paid=?,In_Stock=?,Delivery_Type=?,Status=?, Client_IP=?, Order_Date=?, file=? where Order_Number=?","3,4,5,6,7,8,9,10,11,1");
//Add the insert statements dBTable1.addInsertSql("insert into order (Order_Number,Company_Name,Amount,Payment_Type,Paid,In_Stock,Delivery_Type,Status, Client_IP,Order_Date,file) values (?,?,?,?,?,?,?,?,?,?,?)", "1,2,3,4,5,6,7,8,9,10,11");
//Add delete statements dBTable1.addDeleteSql("delete from order where Order_Number = ?", "1");
//before setting column properties, create the column model for table based on query //you can also call refresh() method first and then set column properties //but this is a cleaner approach dBTable1.createColumnModelFromQuery();
//set the properties for individual columns //although all these properties are automatically generated from database properties //its always better to set these properties, since most jdbc drivers don't give //the correct meta data
//we want the first column to be a numeric column , where the maximum length of the //number should be 7 digits and we don't want users to edit this cell since this is //a primary key Column c = dBTable1.getColumn(0); c.setReadOnly(true); c.setType(Types.NUMERIC); c.setPrecision(7); //we want the column header of this column to be "Order No". In this header we want the //"Order" to be in first line & "No." to be in second line. To get this we have to separate //the Order & No. by a newline character c.setHeaderValue("Order\nNo."); //we want the display width of the column to be 60 (this is not the maximum data length) c.setPreferredWidth(60);
//in the secind column we want to display images, instead of the actual data in database //if the database data is "amazon", we want to display amazon.gif , similarly other images //so we create a hashtable with the keys being the data in database & the values being the //corresponding ImageIcon of the image c = dBTable1.getColumn(1); c.setReadOnly(false); c.setHeaderValue("Company"); c.setPreferredWidth(139); Hashtable imageHash = new Hashtable(); imageHash.put("amazon",new ImageIcon(this.getClass().getResource("/images/amazon.gif")));
//the third column is going to hold a Double data of the type 653623.23 //for this number, scale will be the number of digits after the "." //and precision will be the total number of digits after //the "." and before the "." c = dBTable1.getColumn(2); c.setReadOnly(false); //most jdbc drivers don't give the precision & scale correctly //so don't forget to set them c.setType(Types.DOUBLE); c.setPrecision(10); c.setScale(2); //we don't want users to enter the negative(-) or postive (+) sign c.setSigned(false); c.setPreferredWidth(70);
c = dBTable1.getColumn(3); c.setReadOnly(false);
c.setHeaderValue("Payment\nType"); c.setPreferredWidth(60); Hashtable h = new Hashtable(); h.put("V",new ImageIcon(this.getClass().getResource("/images/visa.gif"))); h.put("A",new ImageIcon(this.getClass().getResource("/images/american.gif"))); h.put("M",new ImageIcon(this.getClass().getResource("/images/master.gif"))); dBTable1.setCellComponent(c,Column.IMAGE_CELL,h);
//we want the fifth column to be a checkbox column. whenever the database //value id "Y" , we want the checkbox to be checked and when the database value //if "N", we want the checkbox not to be checked. For this, we have to create //a hashtable mapping these database data to the actual checked or unchecked //case. To make it checked, set "new Boolean(true)", for not checked, set "new Boolean(false)" c = dBTable1.getColumn(4); c.setPreferredWidth(60); h = new Hashtable(); h.put("Y", new Boolean(true)); h.put("N", new Boolean(false)); dBTable1.setCellComponent(c,Column.CHECKBOX_CELL,h);
//we want the sixth column to be a radioButton column. whenever the database //value id "Y" , we want the radiobutton to be selected and when the database value //if "N", we want the radiobutton not to be selected. For this, we have to create
//a hashtable mapping these database data to the actual selected or not selected //case. To make it selected, set "new Boolean(true)", for not selected, set "new Boolean(false)" c = dBTable1.getColumn(5); c.setPreferredWidth(60); h = new Hashtable(); h.put("Y", new Boolean(true)); h.put("N", new Boolean(false)); dBTable1.setCellComponent(c,Column.RADIOBUTTON_CELL,h);
//In the seventh column, we don't want user to enter any data, we want the user to enter only //the valid values for this column "F" or "U". So when the user tries to edit this cell, we can show //a combo box and ask the user to select one of "F" or "U". Again, the database values "F" and "U" are //cryptic, we want to show the user a meaningful data "Fedex" or "UPS" c = dBTable1.getColumn(6); c.setPreferredWidth(60); c.setNullable(true); h = new Hashtable(); h.put("F","Fedex"); h.put("U","UPS"); dBTable1.setCellComponent(c,Column.COMBOBOX_CELL,h);
//this eighth column is similar to seventh, but instead of, we hardcoding the possible cell values in //the java code, we want to query the database & get the values c = dBTable1.getColumn(7); c.setBoundSql("select status from shipment_type");
c.setPreferredWidth(80);
//in the ninth column, we want the user to enter an ip address //but want to make sure , user enters the IP address in correct format //Java has a interface javax.swing.text.Document , which can validate the text //which a user enters into a textfield. To make sure the user enters the //correct IP format, we create a IPDocument class which implements Document //and assign that to this column c = dBTable1.getColumn(8); c.setDocument(new IPDocument()); c.setPreferredWidth(100);
//tenth column is a varchar column in datbase which stores date information //Still we want to use this //column like a Date type column in quicktable, so that the users can use the calendar bean to //to select a date instead of typing directly. To implement this we have to create a DataMap object //which does this conversion and the set that to this column c = dBTable1.getColumn(9); c.setHeaderValue("Date"); c.setDataMap(new DateStringMap()); c.setType(java.sql.Types.DATE); c.setDateFormat("MM/dd/yyyy"); c.setPreferredWidth(80);
//this column stores system file information, we want the user to enter a valid
//file path. Instead of allowing user to type the file path, when the user //tries to edit the cell, we want to show a file dialog and get the the file path //For this we have to create a class which implements the Cell component and set //it through setUserCellEditor c = dBTable1.getColumn(10); c.setUserCellEditor(new FileCell()); c.setPreferredWidth(120);
//if we don't want quicktable to handle all the errors //and if we want to show meaning full error messages to user //then we have to listen for error and show correct error messages dBTable1.addDBTableErrorListener(myErrorListener);
//whenever user changes a cell data, if you want to do some validation //add this listener dBTable1.addTableCellListener(myCellListener);
//Whenever a user tries to delete a record, we want to ask the user whether //he really wants to delete the record and once the record is delete, we //want to tell the user that this record is successfully deleted //to implement this we have to add a DatabaseChangeListener to quicktable
dBTable1.addDatabaseChangeListener(myChangeListener);
} catch(SQLException e) { e.printStackTrace(); System.out.println("Warning: This error may be due to a problem in simpletext driver which corrupts the database file during update/insert. Usually it complains \"Invalid Data format\". When you get this error copy all the files under backupdatabase to database directory and overwrite all files, then restart again"); System.out.println("This error is not due to quicktable , but due to the simple text jdbc driver"); JOptionPane.showMessageDialog(this,"Database file corrupted, copy all database files from backupdatabase to database directory"); } }
//remove all the different settings which is used by order/customer //this removes UpdateSql,InsertSql,DeleteSql,ErrorListener,CellListener,ChangeListener,cellPropertiesModel,Comparato r dBTable1.clearAllSettings();
try { // set the select sql to get data from the product table dBTable1.setSelectSql("SELECT * from Product");
//update the changes based on product number dBTable1.addUpdateSql("update Product set Product_Name=? where Product_Number=?","2,1");
//Add the insert statements dBTable1.addInsertSql("insert into Product (Product_Number,Product_Name) values (?,?)", "1,2");
//Add delete statements dBTable1.addDeleteSql("delete from Product where Product_Number = ?", "1");
} catch(SQLException e) { e.printStackTrace(); }
public void showCustomer() { //remove all the different settings which is used by order/products //this removes UpdateSql,InsertSql,DeleteSql,ErrorListener,CellListener,ChangeListener,cellPropertiesModel,Comparato r dBTable1.clearAllSettings();
//we want to load a comma delimtied customer file File customerFile = new File("customer.SDF"); Properties prop = new Properties();
//firstRowHasColumnNames is an optional parameter, if first row doesn't have columns //don't add this property at all prop.put("firstRowHasColumnNames","true");
try { dBTable1.refresh(customerFile,prop); } catch(Exception e) { JOptionPane.showMessageDialog( this,"Error loading " + customerFile.getPath() + " " + e.getMessage(),"",JOptionPane.OK_OPTION); } }
public void showHTTPCustomer() { //remove all the different settings which is used by order/products //this removes UpdateSql,InsertSql,DeleteSql,ErrorListener,CellListener,ChangeListener,cellPropertiesModel,Comparato r dBTable1.clearAllSettings();
//if you are within firewall, don't forget to set the proxy settings as below //for http: //System.setProperty("http.proxyHost", proxyUrl); //System.setProperty("http.proxyPort", proxyPort);
//we want to load a comma delimtied customer file try{ customerUrl = new java.net.URL("http://quicktable.org/customer.SDF"); }catch(Exception e){}
//firstRowHasColumnNames is an optional parameter, if first row doesn't have columns //don't add this property at all prop.put("firstRowHasColumnNames","true");
//if the url is protected by basic authentication, add the username, password //prop.put("username","quicktable"); //prop.put("password","quicktable");
try {
//This will be useful during using EJB/rmi/corba/java objects, where you don't have direct //access to database public void showEJBCustomer() { //remove all the different settings which is used by order/products //this removes UpdateSql,InsertSql,DeleteSql,ErrorListener,CellListener,ChangeListener,cellPropertiesModel,Comparato r dBTable1.clearAllSettings();
//usually through EJB, while using findXXX method, it will return a enumeration/collection of objects //to simulate that setup we are going to create enumeration of object first
Vector v = new Vector(); v.addElement(new Customer(1,"John","CA")); v.addElement(new Customer(2,"Aiden","NY")); v.addElement(new Customer(3,"Joe","PA")); Enumeration enum = v.elements();
//now this enum is similar to the enumeration which your receive from EJBs
class OrderModel extends CellPropertiesModel { public Color getForeground(int row, int col) { //if you want to have alternate rows colored differently //if(row % 2 == 0) // return Color.white; //else // return Color.yellow; //similarly you can try coloring alternate cols
//I want to highlight the cells whose product amounts are more
//more than 3000.00 if( col == 2) { Object cellValue = dBTable1.getTable().getValueAt(row, col); if( cellValue instanceof Double ) { if(((Double)cellValue).doubleValue() > 3000.00 ) { return Color.red; } } else { if( cellValue == null || "".equals(cellValue)) return null;
//in all other cases let the cell have the default color
return null;
public int getAlignment(int row, int col) { if( col== 0 || col == 6 || col ==7) return SwingConstants.LEFT; else if(col == 2) return SwingConstants.RIGHT; else return SwingConstants.CENTER; } }
//whenever user changes a cell data, if you want to do some validation //add this listener class MyCellListener implements DBTableCellListener { public Object cellValueChanged(int row, int col, Object oldValue, Object newValue) { if( col == 3) { //the oldvalue and the new value will be a String type or the actual data type Double
if( newValue instanceof Double ) { if(((Double)newValue).doubleValue() > 5000.00 ) { JOptionPane.showMessageDialog( CustomerOrderDemo.this,"Product price cannot be more than $5000.00","",JOptionPane.OK_OPTION);
//newvalue is the user changed value, since the new value is greater than 5000 //we should return this oldvalue, so that the cell will still have the oldvalue return oldValue;
//in the above you can also do //return "5000.00"; //in that case even if the user enters more than 5000.00 still he will get 5000.00 in the cell //the return value should always be string, but the data should be covertable to the correct format } else { return newValue; } } else {
if(Double.valueOf((String)newValue).doubleValue() > 5000.00 ) { JOptionPane.showMessageDialog( CustomerOrderDemo.this,"Product price cannot be more than $5000.00","",JOptionPane.OK_OPTION); return oldValue; } else { return newValue; } } } //we don't care about other columns else { //in all other cases return new value so that the new value will be set in the cells return newValue; } } }
//and if we want to show meaning full error messages to user //then we have to listen for error and show correct error messages class MyErrorListener implements DBTableErrorListener { //this method will be called by quicktable, whenever an error occurs public boolean errorOccured(int errorId, String errorMessage, Exception unexpectedException) {
//See DBTableErrorListener API help for errorId values if (errorId == DBTableErrorListener.DRIVER_NOT_FOUND) { JOptionPane.showMessageDialog( CustomerOrderDemo.this,"Installation Error: Add jdbc driver to classpath!","",JOptionPane.OK_OPTION);
//you have handled the error, so return true //if you return false, quicktable will show the default message dialog return true; } else if (errorId == DBTableErrorListener.UPDATE_ERROR) { //the error code used here varies in different drivers //so change this based on your driver if(((SQLException)unexpectedException).getErrorCode() == 0 ) { JOptionPane.showMessageDialog(CustomerOrderDemo.this,"Error: Your changes are not updated due to database error: " + unexpectedException.getMessage(),"",JOptionPane.OK_OPTION);
//you have handled the error, so return true //if you return false, quicktable will show the default message dialog return true; } else return false; } //there are lot of other error cases , refer to DBTableErrorListener javadoc
//all other errors need to be handled by dbtable, so return false return false; } }
//Whenever a user tries to delete a record, we want to ask the user whether //he really wants to delete the record and once the record is delete, we //want to tell the user that this record is successfully deleted //to implement this we have to add a DatabaseChangeListener to quicktable
//get the order number corresponding to this row Object orderNo = dBTable1.getTable().getValueAt(row, 0);
int option = JOptionPane.showConfirmDialog( CustomerOrderDemo.this,"Are you sure to delete the Order:" + orderNo,"",JOptionPane.OK_CANCEL_OPTION);
if( option == JOptionPane.OK_OPTION ) { //if you want to delete, return true return true; } else { //if you want to cancel this delete, return false return false; } }
{ //order No, is the primary key in this table, we don't want the user to create //a order number , we want to automatically create a order number , everytime //user inserts a new record Vector insertVector = new Vector();
//this is just for example, don't follow this example of creating unique key because it may create duplicates insertVector.addElement(new Integer(Math.abs(randomOrderNumber.nextInt())));
insertVector.addElement("");
//Float & double doesn't work with simpletext , so used the default string //usually supply the correct datatype //insertVector.addElement( new Float(0.0)); insertVector.addElement(""); insertVector.addElement(""); insertVector.addElement(""); insertVector.addElement(""); insertVector.addElement(""); insertVector.addElement(""); insertVector.addElement(""); insertVector.addElement(""); insertVector.addElement("");
try{
//since we have already inserted the record, return true, so that //quicktable doesn't insert the default record return false; }
//there are more methods in this class beforeUpdate, afterUpdate, beforeInsert, afterInsert, onDeleteButtonClick //you can use them too. }
//lets say, one of the column stores dates in the database, unfortunately, the data type of //this column was varchar, so it is stored as text in database. Still we want to use this //column like a Date type column in quicktable, so that the users can use the calendar bean to //to select a date instead of typing directly. This DataMap class handles the conversion between // Date to string & vice versa public class DateStringMap implements DataMap {
//this method will be called whenever we try to display this data public Object convertDataToDisplay(Object val) { //warning: please make sure, you don't put a time consuming code here, make it very simple //otherwise this will bring down the performance of quicktable
if( val instanceof java.util.Date) { //the data is already in date object, so no need to convert return val; } else { try { //convert the String to a Date return df.parse((String)val); } catch(Exception e) { //if the data is not in correct format, return today's date return new java.util.Date(); }
} }
//this method will be called whenever we try to save this data to database public Object convertDataToStore(Object val) { if( val instanceof java.util.Date) { try { return df.format((java.util.Date)val); } catch(Exception e) { //this case will never happen , if you use quicktable's default calender editor return null; } } else { //this case will also not happen, if you use quicktable's default calender editor return null; }
//we create a File selecter cell editor //this has a label & a button, when user wants to change the file to something else //instead of directly editing this cell, they click on the button which opens a file // dialog and user selects the file class FileCell extends JPanel implements CellComponent { JLabel jl = new JLabel(); JButton jb = new JButton("..."); JFileChooser jf;
public FileCell() { //use a border layout , rather than the used flow layout add(jl); add(jb);
// add a actionlistener to open file dialog when user clicks on the button jb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if( jf == null)
{ jf = new JFileChooser(); }
if( val != null && !("".equals(val)) ) return val; else return null; }
class OrderPrintProperties extends PrintProperties { String header = " %2%\nDate: "; Customer Order Summary Report\nPage: %1%\nSub Page:
//This filter is just to show an example of how to implement a filter //this filters all the records whose first column's data is a number and is a odd number public class OddFilter implements Filter { public int[] filter(javax.swing.table.TableModel tm) { Vector v = new Vector();
//finds all the records whose first column's data is a odd number for( int i=0; i< tm.getRowCount(); i++) { //get the first column's data Object data = tm.getValueAt(i,1);
//get the integer value of the data if( data instanceof Integer) val = ((Integer)data).intValue(); else val = Integer.parseInt(data.toString());
//check whether the data is odd //if the data is odd, add that row to the vector if( (val % 2) != 0 ) v.addElement(new Integer(i)); }
catch(Exception e){} }
//now we have the filtered rows in the vector //create a int array from thet vector int arr[] = new int[v.size()];
return arr; } }
//create Toolbar JToolBar jb = new JToolBar(); Insets i1 = new Insets(0, 0, 0, 0); printButton = new JButton(new ImageIcon(this.getClass().getResource("/images/print.gif"))); printButton.setToolTipText("Print"); printButton.setMargin(i1);
previewButton = new JButton(new ImageIcon(this.getClass().getResource("/images/print.gif"))); previewButton.setToolTipText("Print Preview"); previewButton.setMargin(i1); previewButton.setAlignmentY(Component.CENTER_ALIGNMENT); previewButton.addActionListener(this); jb.add(previewButton);
jb.addSeparator(); jb.addSeparator();
selectionCombo = new JComboBox(); selectionCombo.setMaximumSize(new Dimension(70,20)); selectionCombo.setAlignmentY(Component.CENTER_ALIGNMENT); selectionCombo.addItem("Order"); selectionCombo.addItem("Product"); selectionCombo.addItem("Customer"); selectionCombo.addActionListener(this); jb.add(selectionCombo);
jb.addSeparator(); jb.addSeparator();
skinCombo = new JComboBox(); skinCombo.setMaximumSize(new Dimension(70,20)); skinCombo.setAlignmentY(Component.CENTER_ALIGNMENT); skinCombo.addItem("None"); skinCombo.addItem("Simple"); skinCombo.addItem("Cool"); skinCombo.addItem("Pulse"); skinCombo.addActionListener(this); jb.add(skinCombo);
jb.addSeparator(); jb.addSeparator();
jb.add(filterText);
jb.addSeparator(); jb.addSeparator();
getContentPane().add(jb,BorderLayout.NORTH);
System.out.println("Warning: SimpleText driver fails to update using a prepared statement. It throws a \"Invalid data format\" error."); System.out.println("This error is not due to quicktable. But due to the jdbc driver. when you use a standard jdbc driver you won't get these errors"); System.out.println("Configure odbc datasource \"quicktabledemo\" using the quicktable.mdb file in database directory and try the samples under samples/MSAccessSample");
JOptionPane.showMessageDialog(myframe,"Modifying cell data, may fail due to simple text jdbc driver problem!"); } }