|
Replies:
15
-
Last Post:
Aug 10, 2006 8:04 AM
by: dags
|
|
|
|
|
|
|
DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 10:16 AM
|
|
|
Hi,
I checked out databuffer from CVS today and tried to make a simple test.
I got some "read errors" and after following stack trace I realized that the error was in the select sql string generated by TableCommand. I created my TableCommand using accounting.currencies as table name. In postgresql, that means : schema accounting, table currencies and could be used a qualified table name in queries.
TableCommand quotes table name with ", so the resulting sql is :
select * from "accounting.currencies"
while postgresql server doesn't understand "accounting.currencies" as schema.table but as a quoted table name. Quoted table names are used to preserve case or some symbols in table name. Postgresql tries to find "accounting.currencies" table in schema public instead of finding currencies table in schema accounting.
After modifying TableCommand a little, I got string :
select * from accounting.currencies
and now my example works OK but this change could prevent using table names like Currencies (upper and lowercase mixed).
Maybe TableCommand should have another property, Schema, to make it able to work with PostgreSQL ?. Quoting doesn't need to change in that case. We could get :
select * from "accounting"."currencies"
if (!schemaName.isEmpty()) buffer.apend("\"").append(schemaName).append("\"."); buffer.apend("\"").append(tableName).append("\"");
schemaName default value should be set to an empty string.
Regards, Diego.
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 10:36 AM
in response to: dags
|
|
|
Hi Diego
You're quick! I only just moved code in this morning, hadn't announced anything yet.
We definitely have an issue to resolve around how to handle SQL specific to particular vendors. Can you file an RFE, please? I need a few days to set the project up before and changes can been made.
Regards Patrick
--------------------------------------------------------------------- To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net For additional commands, e-mail: jdnc-help@jdnc.dev.java.net
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 10:44 AM
in response to: Patrick Wright
|
|
|
Patrick,
I will file an RFE later, at home.
You may want to review package declared for DataSetTest.java (I guess). It is org.jdesktop.dataset instead of org.jdesktop.databuffer, if I recall correctly.
Could you point me to a TableModel implementation based on DataTable ? Or any other examples of databuffer usage ?.
Thanks. Diego.
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 11:05 AM
in response to: dags
|
|
|
Hi Diego
> You may want to review package declared for DataSetTest.java (I guess). It is org.jdesktop.dataset instead of org.jdesktop.databuffer, if I recall correctly.
Thanks, I clobbered the refactoring of the test package names slightly. Will fix.
> Could you point me to a TableModel implementation based on DataTable ? Or any other examples of databuffer usage ?.
Ooof! This is old stuff, man. Meaning, yes, there used to be examples and some running demos but most of it would need to be dusted off. You can do a search in the incubator or swinglabs-demos for, say, DataSet and DataTable. Richard had integrated it with the binding library, so there was an adaptor that provided a TableModel to work with...As we are just in the process of splitting the projects, there is no code I can point to that is working right now, just the test cases, actually. But--soon to come. Your input is very welcome!
Patrick
--------------------------------------------------------------------- To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net For additional commands, e-mail: jdnc-help@jdnc.dev.java.net
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 10:40 AM
in response to: dags
|
|
|
Hey Diego,
Thanks for bringing this up. I think you are right and there should be a "schemaName" property. In addition it seems like there needs to be some support for SQL Dialects, because different databases understand different, well, dialects of SQL. You're definitely right that it isn't doing a very good job of it today 
For example, like you pointed out, Postgres requires exact case matching for tables listed in quotes. I believe other databases (I don't remember which now, I think it was mysql or derby) don't.
What do you think of having a SqlDialect class, which the TableCommand and SqlCommand would delegate to for creating the SQL statements? The default one could be a bit more intelligent, perhaps. It would be nice if the proper dialect could be used based on the database driver, or overridden by the developer.
What do you think? Richard
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 10:57 AM
in response to: rbair
|
|
|
Hi Richard,
I think TableCommand and SQLCommand should be as close as SQL specifications as possible. That should work on most databases ?. I don't know but sticking to standards is a good start always.
SqlDialect seems interesting, if differences between databases are enough to justify it.
What is the point in quoting table names if databases don't care ?. Maybe the standard should be to quote only when case preservation is needed.
I will review some SQL specs to see what I am missing.
Regards, Diego.
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 11:09 AM
in response to: dags
|
|
|
> I think TableCommand and SQLCommand should be as close as SQL specifications as possible. That should work on most databases ?. I don't know but sticking to standards is a good start always. > > SqlDialect seems interesting, if differences between databases are enough to justify it.
There are real differences in what databases allow, in particular what characters are allowed in column and table names. Some allow spaces (if quoted), some do not, etc. I've never made a list and haven't worked with them all myself. I'd say we have a decent base class and make it easy to extend and override if the DB is picky about some little iggly-piggly syntax issue. This problem came up before, one of the TODOs is to create a test suite we can run against a number of databases to find these sorts of problems.
Regards Patrick
--------------------------------------------------------------------- To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net For additional commands, e-mail: jdnc-help@jdnc.dev.java.net
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 3:48 PM
in response to: Patrick Wright
|
|
|
Patrick:
in DataTable.java, line 720 ( private void enforceColumnName(String columnName) {)
String msg = "Unknown Column \"{0]\" in table \"{1}\"";
should be
String msg = "Unknown Column \"{0}\" in table \"{1}\"";
I managed to extend AbstractTableModel using DataTable as data source, for a quick test, but fails to update database table because TableCommand doesn't set key columns: all table rows contains the same updated value. Is like :
update mytable set mycolumn = 'newvalue';
I got the following warning too :
ADVERTENCIA: !!! No key columns were specified, the entire table {0} will be updated!!
How could I specify key columns in TableCommand ?.
Regards, Diego.
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 9, 2006 5:03 PM
in response to: dags
|
|
|
Never mind. I Found it !.
Here is my code. It work with sligthy modified databuffer code.
/*
* DataBufferTest.java
*
* Created on 9 de agosto de 2006, 9:28
*/
package com.adminsa.accounting.model;
import com.adminsa.util.InitUI;
import org.jdesktop.databuffer.DataSet;
import org.jdesktop.databuffer.DataTable;
import org.jdesktop.databuffer.event.DataTableListener;
import org.jdesktop.databuffer.event.RowChangeEvent;
import org.jdesktop.databuffer.event.TableChangeEvent;
import org.jdesktop.databuffer.provider.sql.JDBCDataConnection;
import org.jdesktop.databuffer.provider.sql.SQLDataProvider;
import org.jdesktop.databuffer.provider.sql.TableCommand;
/**
*
* @author dags
*/
public class DataBufferTest extends org.jdesktop.swingx.JXPanel implements DataTableListener {
private CurrencyTableModel modelo = null;
private SQLDataProvider dp = null;
private JDBCDataConnection dc = null;
private TableCommand tc = null;
private DataSet ds = null;
private DataTable dt = null;
/** Creates new form BeanForm */
public DataBufferTest() {
initComponents();
dc = new JDBCDataConnection("org.postgresql.Driver", "jdbc:postgresql://mi_servidor/mag", "dags", "");
dc.setConnected(true);
tc = new TableCommand("currencies");
tc.setSchemaName("accounting");
dp = new SQLDataProvider();
dp.setConnection(dc);
dp.setCommand(tc);
ds = new DataSet("accounting");
dt = new DataTable(ds, "monedas");
dt.createColumn(String.class, "currency_id");
dt.createColumn(String.class, "currency_name");
dt.addDataTableListener(this);
dt.setDataProvider(dp);
dt.load();
dt.getColumn("currency_id").setKeyColumn(true);
modelo = new CurrencyTableModel(dt);
jXTable1.setModel(modelo);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Código Generado ">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jXTable1 = new org.jdesktop.swingx.JXTable();
setLayout(new java.awt.BorderLayout());
jXTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null},
{null, null},
{null, null},
{null, null}
},
new String [] {
"Moneda", "Denominación"
}
) {
Class[] types = new Class [] {
java.lang.String.class, java.lang.String.class
};
public Class getColumnClass(int columnIndex) {
return types [columnIndex];
}
});
jScrollPane1.setViewportView(jXTable1);
add(jScrollPane1, java.awt.BorderLayout.CENTER);
}// </editor-fold>
// Declaración de variables - no modificar
private javax.swing.JScrollPane jScrollPane1;
private org.jdesktop.swingx.JXTable jXTable1;
// Fin de declaración de variables
public static void main(String args[]) {
InitUI.initUI();
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
javax.swing.JFrame frame = new javax.swing.JFrame(" DataBufferTest ");
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
frame.setLayout(new java.awt.BorderLayout());
frame.add(new DataBufferTest(), java.awt.BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
});
}
public void rowChanged(RowChangeEvent evt) {
//System.out.println("rowChanged " + evt.toString());
}
public void tableChanged(TableChangeEvent evt) {
//System.out.println("tableChanged " + evt.toString());
switch(evt.getEventType()) {
case LOAD_COMPLETE:
System.out.println("RowCount = " + dt.getRowCount());
break;
case COLUMN_CHANGED:
break;
case LOAD_STARTED:
break;
case ROW_ADDED:
break;
case ROW_DELETED:
break;
case ROW_DISCARDED:
break;
case SAVE_COMPLETE:
break;
case SAVE_STARTED:
break;
case TABLE_CLEARED:
break;
}
}
}
Now, my database table gets updated OK using a JXTable !.
CurrencyTableModel is absolute dirty. If someone is interested, I will polish my code a little and will publish here.
Regards, Diego.
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 10, 2006 1:26 AM
in response to: dags
|
|
|
Hi Diego
Nice work, good to see that it all works in fairly few lines of code.
If you want, you could start an incubator example of how this works, until we can get the databuffer code cleaned up and proper examples built--having a simple TableModel that works with a DataTable would be good for people walking up to the project. Dirty is OK! And besides, I love seeing the Spanish mixed in with the English in the code.
Cheers Patrick
--------------------------------------------------------------------- To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net For additional commands, e-mail: jdnc-help@jdnc.dev.java.net
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 10, 2006 4:11 AM
in response to: Patrick Wright
|
|
|
Hi,
Here is my CurrencyTableModel, while setting up an incubator example.
Critics are welcomed !.
package com.adminsa.accounting.model;
import org.jdesktop.databuffer.DataTable;
/**
*
* @author dags
*/
public class CurrencyTableModel extends javax.swing.table.AbstractTableModel {
private DataTable dt = null;
// to hold header's titles, if needed
private String[] columnTitles = null;
private String[] columnNames = {
"currency_id",
"currency_name"};
/** Creates a new empty instance of CurrencyTableModel */
public CurrencyTableModel() {
}
/** Creates a new instance of CurrencyTableModel linked to a DataTable */
public CurrencyTableModel(DataTable dt) {
this();
setDataTable(dt);
}
public void setDataTable(DataTable dt) {
this.dt = dt;
// notify model has changed
fireTableDataChanged();
}
public int getColumnCount() {
return (dt == null ? 0 : dt.getColumns().size());
}
public int getRowCount() {
return (dt == null ? 0 : dt.getRowCount());
}
public Object getValueAt(int rowIndex, int columnIndex) {
return (dt == null ? null : dt.getValue(rowIndex, columnNames[columnIndex]));
}
public void setValueAt(Object value, int row, int col) {
if (dt == null) return;
dt.setValue(row, columnNames[col], value);
// save on every model's data change. Maybe is not needed if there is a Save button somewhere ...
dt.save();
//notify model has changed
fireTableCellUpdated(row, col);
}
public String getColumnName(int columnIndex) {
return columnTitles == null ? columnNames[columnIndex] : columnTitles[columnIndex];
}
public String[] getColumnTitles() {
return columnTitles;
}
public void setColumnTitles(String[] columnTitles) {
this.columnTitles = columnTitles;
}
public Class getColumnClass(int columnIndex) {
return getValueAt(0, columnIndex).getClass();
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return !dt.getColumn(columnNames[columnIndex]).isReadOnly();
}
public void populate() {
if (dt == null) return;
// clear DataTable and load again ...
dt.refresh();
//notify model has changed
fireTableDataChanged();
}
}
Regards, Diego.
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 10, 2006 4:14 AM
in response to: dags
|
|
|
Looks good--one thing, why not pull the default column names from the DataTable, instead of hard-coding?
> private String[] columnNames = { > "currency_id", > "currency_name"}; >
Patrick
--------------------------------------------------------------------- To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net For additional commands, e-mail: jdnc-help@jdnc.dev.java.net
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 10, 2006 5:22 AM
in response to: Patrick Wright
|
|
|
Hi Patrick,
I told you it was dirty ! 
Now, it seems that is a more generic TableModel.
package com.adminsa.accounting.model;
import java.util.ArrayList;
import java.util.List;
import org.jdesktop.databuffer.DataColumn;
import org.jdesktop.databuffer.DataTable;
/**
*
* @author dags
*/
public class CurrencyTableModel extends javax.swing.table.AbstractTableModel {
private DataTable dt = null;
// to hold header's titles, if needed
private String[] columnTitles = null;
// to hold column names, from DataTable
private ArrayList<String> columnNames = null;
/** Creates a new empty instance of CurrencyTableModel */
public CurrencyTableModel() {
}
/** Creates a new instance of CurrencyTableModel linked to a DataTable */
public CurrencyTableModel(DataTable dt) {
this();
setDataTable(dt);
}
public void setDataTable(DataTable dt) {
this.dt = dt;
List<DataColumn> columns = dt.getColumns();
columnNames = new ArrayList<String>();
for (DataColumn column : columns)
columnNames.add(column.getName());
// notify model has changed
fireTableDataChanged();
}
public int getColumnCount() {
return (dt == null ? 0 : dt.getColumns().size());
}
public int getRowCount() {
return (dt == null ? 0 : dt.getRowCount());
}
public Object getValueAt(int rowIndex, int columnIndex) {
return (dt == null ? null : dt.getValue(rowIndex, columnNames.get(columnIndex) ));
}
public void setValueAt(Object value, int row, int col) {
if (dt == null) return;
dt.setValue(row, columnNames.get(col), value);
// save on every model's data change. Maybe is not needed if there is a Save button somewhere ...
dt.save();
//notify model has changed
fireTableCellUpdated(row, col);
}
public String getColumnName(int columnIndex) {
// if column Titles are setup, use it. If not, use column names from database.
// columns titles are supposed to be more "human friendly"
return columnTitles == null ? columnNames.get(columnIndex) : columnTitles[columnIndex];
}
public String[] getColumnTitles() {
return columnTitles;
}
public void setColumnTitles(String[] columnTitles) {
this.columnTitles = columnTitles;
}
public Class getColumnClass(int columnIndex) {
return getValueAt(0, columnIndex).getClass();
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return !dt.getColumn( columnNames.get(columnIndex) ).isReadOnly();
}
public void populate() {
if (dt == null) return;
// clear DataTable and load again ...
dt.refresh();
//notify model has changed
fireTableDataChanged();
}
}
Regards, Diego.
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 10, 2006 5:29 AM
in response to: dags
|
|
|
Nice work.
Some possible changes: 1) make auto-save optional (in setValueAt())
2) don't allow constructor without DataTable--nothing will work without it (make empty constructor private)
3) add listeners to the datatable in case the datatable structure (columns, column names, number of rows, etc.) or datatable data changes programmatically
Your code is very easy to read and very good to have as an example.
Cheers! Patrick
Note to self: There are probably other neat examples we could do, like highlighters that are sensitive to a row's status (new, updated, deleted, etc).
--------------------------------------------------------------------- To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net For additional commands, e-mail: jdnc-help@jdnc.dev.java.net
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 10, 2006 5:45 AM
in response to: Patrick Wright
|
|
|
Patrick,
1) good point. I'll do it. 2) I use to have a empty constructor to ease the use in a IDE like Netbeans. I think is the only way to have it in the Palette. I add a TableModel from Palette and then change some of its properties, before adding to a JXTable (wich in turn was added from Palette). 3) good point too, even when I guess that DataTable's structure will rarely change in common DataTable usage. I will study a little more.
Thanks for for comments.
Diego.
|
|
|
|
|
|
|
|
Re: DataBuffer's TableCommand fails with PostgreSQL schemas ?
Posted:
Aug 10, 2006 8:04 AM
in response to: dags
|
|
|
Patrick,
I tried CurrencyTableModel with another table, 23 columns and 2639 rows, showing serious performance troubles.
After a little search, I found that the trouble was in the getColumnClass method of CurrencyTableModel, throwing NullPointerException. I changed to :
public Class getColumnClass(int columnIndex) {
System.out.println("getColumnClass " + columnIndex);
return dt.getColumn(columnNames.get(columnIndex)).getType();
// former wrong code
//return getValueAt(0, columnIndex).getClass();
}
and now works OK.
This is the code for the generic TableModel I am using now to learn about DataBuffer (someone has a better class name ?):
package com.adminsa.accounting.model;
import java.util.ArrayList;
import java.util.List;
import org.jdesktop.databuffer.DataColumn;
import org.jdesktop.databuffer.DataRow;
import org.jdesktop.databuffer.DataTable;
import org.jdesktop.databuffer.event.DataTableListener;
import org.jdesktop.databuffer.event.RowChangeEvent;
import org.jdesktop.databuffer.event.TableChangeEvent;
/**
*
* @author dags
*/
public class DataTableTableModel extends javax.swing.table.AbstractTableModel implements DataTableListener {
private DataTable dt = null;
private boolean autoSave = true;
/** to hold header's titles, if needed */
private String[] columnTitles = null;
/** to hold column names, from DataTable */
private ArrayList<String> columnNames = null;
/** Creates a new empty instance of DataTableTableModel */
public DataTableTableModel() {
}
/** Creates a new instance of DataTableTableModel linked to a DataTable */
public DataTableTableModel(DataTable dt) {
this();
setDataTable(dt);
}
public void setDataTable(DataTable dt) {
// remove this as a listener of the former datatable
if (this.dt != null)
this.dt.removeDataTableListener(this);
this.dt = dt;
this.dt.addDataTableListener(this);
List<DataColumn> columns = dt.getColumns();
columnNames = new ArrayList<String>();
for (DataColumn column : columns)
columnNames.add(column.getName());
/** notify model has changed */
fireTableDataChanged();
}
public void setAutoSave(boolean autoSave) {
this.autoSave = autoSave;
}
public boolean isAutoSave() {
return this.autoSave;
}
public int getColumnCount() {
return (dt == null ? 0 : dt.getColumns().size());
}
public int getRowCount() {
return (dt == null ? 0 : dt.getRowCount());
}
public Object getValueAt(int rowIndex, int columnIndex) {
return (dt == null ? null : dt.getValue(rowIndex, columnNames.get(columnIndex) ));
}
public void setValueAt(Object value, int row, int col) {
if (dt == null) return;
dt.setValue(row, columnNames.get(col), value);
// save changes only if autoSave is true (it is by default)
if (autoSave) {
/**
* It may be some troubles is autoSave is on, while trying to save
* a non primary key column value before primary key is set, like in
* a appended row.
*
* It can fails too with NOT NULL, Foreign Keys.
**/
dt.save();
}
//notify model has changed
fireTableCellUpdated(row, col);
}
public String getColumnName(int columnIndex) {
// if column Titles are setup, use it. If not, use column names from database.
// columns titles are supposed to be more "human friendly"
return columnTitles == null ? columnNames.get(columnIndex) : columnTitles[columnIndex];
}
public String[] getColumnTitles() {
return columnTitles;
}
public void setColumnTitles(String[] columnTitles) {
this.columnTitles = columnTitles;
}
public Class getColumnClass(int columnIndex) {
return dt.getColumn(columnNames.get(columnIndex)).getType();
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return !dt.getColumn( columnNames.get(columnIndex) ).isReadOnly();
}
public void populate() {
if (dt == null) return;
// clear DataTable and load again ...
dt.refresh();
// notify model has changed
fireTableDataChanged();
}
public void save() {
if (dt == null) return;
dt.save();
}
public DataRow appendRow() {
DataRow row = null;
if (dt == null) return row;
row = dt.appendRow();
// notify model has changed
fireTableRowsInserted(dt.getRowCount()-1, dt.getRowCount()-1);
return row;
}
public void deleteRow(int rowIndex) {
if (dt == null) return;
dt.deleteRow(rowIndex);
// notify model has changed
fireTableRowsDeleted(rowIndex, rowIndex);
return;
}
public void rowChanged(RowChangeEvent evt) {
// must see what to do when DataRow changes
System.out.println("rowChanged : " + evt.getEventType().toString());
}
public void tableChanged(TableChangeEvent evt) {
// must see what to do when DataTable changes
System.out.println("tableChanged : " + evt.getEventType().toString());
}
}
Regards, Diego.
|
|
|
|
|