Clearing data from one JTable is also deleting the another JTable - java

I am doing an application in Java using Swing. I have two tables and I have to copy contents from one table to another (Replication.) The problem is if I clear the destination Table rows then my source table rows are also getting deleted.
If I press CopyAll then I will copy all the contents from Table-A to Table-B. If I press clear then I have to clear Table-B. But the problem is Table-A is also getting cleared.
For copying
public void copyAll() {
TableModel tableAModel = tableA.getModel();
tableB.setModel(tableAModel);
repaint();
}
For clearing rows (I am doing for table-B)
public void clearTableB() {
DefaultTableModel clearTableData = (DefaultTableModel) tableB.getModel();
clearTableData.setNumRows(0);
}
I think I am getting problem while copying in copyAll() method. I am getting tableA's Model and then clearing it at clearTable() method.
If the above copyAll() method is wrong please tell me how can I implement copyAll(), removeTableB().

You have copied the TableModel between the two tables. This means the two tables share the same data. If you delete the contents of the TableModel, both tables will loose their data.
You should create two separate TableModel instances, and keep them in sync (for example by using a listener as the TableModel fires events each time the model is updated)

In your copy version, you set the model of the first table to the second table. So the two tables share the same model. You should make a copy of the model :
public void copyAll() {
final TableModel tableAModel = tableA.getModel();
final DefaultTableModel copy = new DefaultTableModel(tableAModel.getRowCount(), 0);
for (int column = 0; column < tableAModel.getColumnCount(); column++) {
copy.addColumn(tableAModel.getColumnName(column));
for (int row = 0; row < tableAModel.getRowCount(); row++)
copy.setValueAt(tableAModel.getValueAt(row, column), row, column);
}
tableB.setModel(copy);
}

Both tables are using the same model. You have to give Table B it's own Model, copy the values manually. Your current copyAll method copies the reference to the Table Model, it doesn't copy the contents.

That is because you shared the TableModel for the two tables. In the copy method, you should create a clone of the Model and use the clone for the second table.

If you are using DefaultTableModel You can get Vector of data from the model using getDataVector() and clone() it.
public void copyAll() {
TableModel tableAModel = tableA.getModel(), tableModelB;
Vector tableModelBDataVector = ((DefaultTableModel)tableAModel).getDataVector();
int tableModelAColumnCount = tableAModel.getColumnCount();
Vector<String> tableModelAColumnVector = new Vector<String>(tableModelAColumnCount);
for (int i = 0; i < tableModelAColumnCount; i++)
tableModelAColumnVector.add(tableAModel.getColumnName(i));
tableModelB = new DefaultTableModel((Vector)tableModelBDataVector.clone(), (Vector)tableModelAColumnVector.clone());
tableB.setModel(tableModelB);
}

Related

Java swing jTable not being updated

I built a jTable using NetBeans GUI, and I want to update it inside the constructor of the class. I'm planning to add a search option on the frame so the whole update idea is quite critical for me.
My code:
public availableTrumps(TrumpistClient TC){
initComponents();
availableTrumpsTrumpistClient=TC;
String result=null;
String query="SELECT * FROM APP.TRUMPS";
result=this.availableTrumpsTrumpistClient.WritingReading("sql_select", query);
if (result.contains("empty")){
JOptionPane.showMessageDialog(this, "There are now trumps to show.");
}
else if (result.contains("error")){
JOptionPane.showMessageDialog(this, "Error in the connection. Please try again.");
}
else{
int i;
String []data = result.split("\r\n");
String [][] data2 = new String [data.length][];
for (i = 0; i < data.length; i++)
{
data2[i] = data[i].split("&");
}
String[] columnNames = {"From", "To", "Departure Time", "Remaining Places", "Proposer", "ClosingTime", "Cost Per Seat" };
this.jTable1 = new JTable(data2,columnNames);
this.jTable1.setPreferredScrollableViewportSize(new Dimension(500,100));
this.jTable1.setFillsViewportHeight(true);
JScrollPane jps = new JScrollPane(jTable1);
add(jps);
jTable1.revalidate();
}
}
The input two-dimentional array data2 is fine and validated.
I added the last 5 rows of the code to see if they help with something. I don't know if they are mandatory and in any case I do not want to change the graphical properties of the jTable I built with the GUI (just the data in it).
When I run the program, I see that the jTable remains empty.
Why?
I suggest you use a table model, whenever the data changes you change the model. Build the JTable instance only once, not whenever you need to change the data.
As others have said, you don't want to create multiple JTable instances. Create one like this:
DefaultTableModel model = new DefaultTableModel(new Object[0][0],
new String[]{"From", "To", "etc."});
JTable table = new JTable(model);
Then, when you need to add rows, use
model.addRow(dataForThisRow); // Object
If you want to change a cell:
model.setValueAt(newValue, row, col); // Object, int, int
Or, to remove row i:
model.removeRow(i); // int
For more information, see the DefaultTableModel documentation.
If, for some reason, it is imperative that you recreate the table each time, I believe the problem is that you are calling revalidate without calling repaint.

Removing Column from TableModel in Java

In Java I'm using the DefaultTableModel to dynamically add a column to a JTable.
//create DefaultTableModel with columns and no rows
DefaultTableModel tableModel = new DefaultTableModel(columnNames, 0);
JTable table = new JTable(tableModel);
The columnNames variable is a string array with the column names. So after the program is up and running the user has the option to add additional columns. I do so as follows
tableModel.addColumn("New column name");
Which dynamically adds the column to the table as desired. The user can also remove columns added. For this I use the following code:
TableColumn tcol = table.getColumnModel().getColumn(0);
table.getColumnModel().removeColumn(tcol);
which should remove the column at a specified index, I've also tried:
table.removeColumn(sheet.getColumn(assessmentName));
Both of them work (visually), but here's the problem. After deleting an added column, if another column is added and the table refreshes, the previously deleted column is there again. So while it is removing the column visually, neither of the last two code snippets actually removes it from the model. I'm assuming here that since the column was added to the model that is where it needs to be removed from? Is there a specific method that I need to call or some logic that I need to implement to remove the column?
For your table, try calling table.setAutoCreateColumnsFromModel(false);
This post has a good example as to how to delete column and the underlying data.
I'm assuming here that since the column was added to the model that is where it needs to be removed from?
Yes.
Is there a specific method that I need to call or some logic that I need to implement to remove the column?
No, but you can make up your own method:
moveColumn(...); // to move the column to the end
setColumnCount(...); // to remove the last column
As a side note if you want to give the users the ability to hide/show columns check out the Table Column Manager.
Acting at the TableColumn level, as you show, has only a visual impact but no impact on the TableModel whatsoever.
If you want to really remove a column from DefaultTableModel then you'll need to subclass it and then, in your subclass:
public class MyTableModel extends DefaultTableModel {
public void removeColumn(int column) {
columnIdentifiers.remove(column);
for (Object row: dataVector) {
((Vector) row).remove(column);
}
fireTableStructureChanged();
}
}
I haven't checked it, but it should work in your case.
Of course, removeColumn() should be called only from the EDT.
Note that I wouldn't encourage anyone to produce this kind of code; in particular, using, or deriving from, DefaultTableModel is not the best solution to define a TableModel.
The DefaultDataModel doesn't have a really removeColumn() function, so I wrote a function myself, which can actually solve the problem.
private void removeColumn(int index, JTable myTable){
int nRow= myTable.getRowCount();
int nCol= myTable.getColumnCount()-1;
Object[][] cells= new Object[nRow][nCol];
String[] names= new String[nCol];
for(int j=0; j<nCol; j++){
if(j<index){
names[j]= myTable.getColumnName(j);
for(int i=0; i<nRow; i++){
cells[i][j]= myTable.getValueAt(i, j);
}
}else{
names[j]= myTable.getColumnName(j+1);
for(int i=0; i<nRow; i++){
cells[i][j]= myTable.getValueAt(i, j+1);
}
}
}
DefaultTableModel newModel= new DefaultTableModel(cells, names);
myTable.setModel(newModel);
}

deleting datas in the table in GUI

I have question that how can I delete all datas from my jTable in GUI when a user entered a key?
thanks
You can set a new empty data model:
TableModel newModel = new DefaultTableModel();
jtable.setModel(newModel);
You need to understand that a JTable is a view of the data, while the actual data resides in the TableModel. If you need to clear out the table, then you need to clear out the TableModel.
If your TableModel is an AbstractTableModel, you must provide implementations of 3 methods:
public int getRowCount();
public int getColumnCount();
public Object getValueAt(int row, int column);
Frequently the actual data objects are stored in an additional data structure (e.g. a list), and then the AbstractTableModel queries that list.
List<DomainObject> objects = new ArrayList<DomainObject>();
public int getRowCount() { return objects.size(); }
// How many columns you make depends on what features of the objects you're exposing.
public int getColumnCount() { return NUMBER_OF_COLUMNS; }
public Object getValueAt(int row, int column) {
DomainObject object = objects.get(row);
... // pull out the property based on the column they pass in
}
// By exposing this method, you can allow your Controller code to reach into this model
// and delete all the rows.
public void clear() {
objects.clear()
}
What HH is suggesting you do is change the model of your JTable to reference an empty model, which will in effect clear out the table. However, the columns etc. will not be persisted correctly (the new DefaultTableModel has no idea what those column names would be).
After you've researched how the view and model fit together more, take a look at GlazedLists. It allows a very powerful way to create TableModels which provide dynamic views of your data, e.g. by filtering out rows that do not match certain criteria.
To sum up - you're not going to find a method on the JTable to clear out its contents, because that's the job of the TableModel. You need some way of ensuring that the TableModel's backing data structures are cleared out.
If you are using the DefaultTableModel then you can just use:
model.setRowCount(0);
This is better than creating a new DefaultTableModel. Creating a new TableModel causes the TableColumnModel to be recreated, which means all the TableColumns will be resize to default values and recreated in the order in which the columns exist in the model. The user may have changed these properties and shouldn't be forced to do it again.
If you are just deleting certain rows that contain a particulsar value, then you can use the DefaultTableModel.removeRow(...) method. Make sure you start by deleting row from the end of the model and count down to 0.
call removeAll of j_table method at addActionListener
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
j_table.removeAll();
data_model_table.setRowCount(0);
}
});

SWT table: auto resize all columns

Qt solution is a single call to resizeColumnsToContent(), in .NET one can use TextRenderer.MeasureText(), JTable could use AUTO_RESIZE_ALL_COLUMNS.
In SWT, is there a way to programmaticaly resize columns after populating them?
Calling computeSize(SWT.DEFAULT, SWT.DEFAULT) returns the same value thus disregarding character left overs in columns.
TableColumn has setWidth(), but how do I obtain the size hint for the current content taking into account font face, etc?
Solved with:
private static void resizeColumn(TableColumn tableColumn_)
{
tableColumn_.pack();
}
private static void resizeTable(Table table_)
{
for (TableColumn tc : table.getColumns())
resizeColumn(tc);
}
In many cases, the table entries change at run-time to reflect changes in the data model. Adding entry to the data model requires to resize columns as well, but in my case calling .pack() after the modification of the model does not solved completly the problem. In particolar with decorations the last entry is never resized. This seams to be due to async table viewer update. This snipped solved my problem:
public class LabelDecoratorProvider extends DecoratingStyledCellLabelProvider {
public LabelDecoratorProvider(IStyledLabelProvider labelProvider,
ILabelDecorator decorator, IDecorationContext decorationContext) {
super(labelProvider, decorator, decorationContext);
}
#Override
public void update(ViewerCell cell) {
super.update(cell);
if (TableViewer.class.isInstance(getViewer())) {
TableViewer tableViewer = ((TableViewer)getViewer());
Table table = tableViewer.getTable();
for (int i = 0, n = table.getColumnCount(); i < n; i++)
table.getColumn(i).pack();
}
}
}

How to fill data in a JTable with database?

I want to display a JTable that display the data from a DataBase table as it is.
Up till now, I have used JTable that displays data from Object [ ][ ].
I know one way to display the data is to first convert the database table into Object [ ][ ] but Is there any other which is easy yet more powerful and flexible.
I would recommend taking the following approach:
Create a Row class to represent a row read from your ResultSet. This could be a simple wrapper around an Object[].
Create a List<Row> collection, and subclass AbstractTableModel to be backed by this collection.
Use a SwingWorker to populate your List<Row> by reading from the underlying ResultSet on a background thread (i.e. within the doInBackground() method). Call SwingWorker's publish method to publish Rows back to the Event Dispatch thread (e.g. every 100 rows).
When the SwingWorker's process method is called with the latest chunk of Rows read, add them to your List<Row> and fire appropriate TableEvents to cause the display to update.
Also, use the ResultSetMetaData to determine the Class of each column within the TableModel definition. This will cause them to be rendered correctly (which won't be the case if you simply use a 2D Object[][] array).
The advantage of this approach is that the UI will not lock up when processing large ResultSets, and that the display will update incrementally as results are processed.
EDIT
Added example code below:
/**
* Simple wrapper around Object[] representing a row from the ResultSet.
*/
private class Row {
private final Object[] values;
public Row(Object[] values) {
this.values = values;
}
public int getSize() {
return values.length;
}
public Object getValue(int i) {
return values[i];
}
}
// TableModel implementation that will be populated by SwingWorker.
public class ResultSetTableModel extends AbstractTableModel {
private final ResultSetMetaData rsmd;
private final List<Row> rows;
public ResultSetTableModel(ResultSetMetaData rsmd) {
this.rsmd = rsmd;
this.rows = new ArrayList<Row>();
}
public int getRowCount() {
return rows.size();
}
public int getColumnCount() {
return rsmd.getColumnCount();
}
public Object getValue(int row, int column) {
return rows.get(row).getValue(column);
}
public String getColumnName(int col) {
return rsmd.getColumnName(col - 1); // ResultSetMetaData columns indexed from 1, not 0.
}
public Class<?> getColumnClass(int col) {
// TODO: Convert SQL type (int) returned by ResultSetMetaData.getType(col) to Java Class.
}
}
// SwingWorker implementation
new SwingWorker<Void, Row>() {
public Void doInBackground() {
// TODO: Process ResultSet and create Rows. Call publish() for every N rows created.
}
protected void process(Row... chunks) {
// TODO: Add to ResultSetTableModel List and fire TableEvent.
}
}.execute();
Another powerful and flexible way to display database data in a JTable is to load your query's resulting data into a CachedRowSet, then connect it to the JTable with TableModel adapter.
Query ---> Database data ---> RowSet
RowSet <--> TableModel adapter <--> JTable
This book by George Reese gives the source code for his class RowSetModel to adapt a RowSet as a TableModel. Worked for me out-of-the-box. My only change was a better name for the class: RowSetTableModel.
A RowSet is a subinterface of ResultSet, added in Java 1.4. So a RowSet is a ResultSet.
A CachedRowSet implementation does the work for you, instead of you creating a Row class, a List of Row objects, and ResultSetMetaData as discussed in other answers on this page.
Sun/Oracle provides a reference implementation of CachedRowSet. Other vendors or JDBC drivers may provide implementations as well.
RowSet tutorial
Depending on what you've done already and what you're willing to do, I've been using Netbeans with its Beans Binding support for a database-driven app very successfully. You bind your JTable to a database and it automatically builds the JPA queries.
Best way to fill jTable with ResultSet
Prerequisites
1) Result Set "rs" is populated with data you need.
2) JTable "jTable1" is created before hand
3) Table Header is implemented before hand
Implementation
java.sql.ResultSet rs = datacn.executeSelectQuery(query);
//Filling JTable with Result set
// Removing Previous Data
while (jTable1.getRowCount() > 0) {
((DefaultTableModel) jTable1.getModel()).removeRow(0);
}
//Creating Object []rowData for jTable's Table Model
int columns = rs.getMetaData().getColumnCount();
while (rs.next())
{
Object[] row = new Object[columns];
for (int i = 1; i <= columns; i++)
{
row[i - 1] = rs.getObject(i); // 1
}
((DefaultTableModel) jTable1.getModel()).insertRow(rs.getRow() - 1,row);
}
You have to create a custom TableModel There you can specify where and how the data is coming from.
You really have to fully understand first how JTable + TableModel works and then follow one of the previously posted answers.
I know the question is old but for anyone following Adamski's solution, care should be taken while sharing the ResultSet and ResultSetMetadata between gui and SwingWorker threads. I got an inconsistent internal state exception while using this approach with SQLite. The solution is to load any metadata to private fields before executing the SwingWorker and have the getter functions (getColumnName etc.) to return the fields instead.
I am giving a small method for display database table data in JTable. You need to pass only the resultset of the database table as parameter.
// rs is the ResultSet of the Database table
public void displayData(ResultSet rs)
{
//jt Represents JTable
//jf represents JFrame
int i;
int count;
String a[];
String header[] = {"1","2","3","4","5"}; //Table Header Values, change, as your wish
count = header.length;
//First set the Table header
for(i = 0; i < count; i++)
{
model.addColumn(header[i]);
}
jt.setModel(model); //Represents table Model
jf.add(jt.getTableHeader(),BorderLayout.NORTH);
a = new String[count];
// Adding Database table Data in the JTable
try
{
while (rs.next())
{
for(i = 0; i < count; i++)
{
a[i] = rs.getString(i+1);
}
model.addRow(a); //Adding the row in table model
jt.setModel(model); // set the model in jtable
}
}
catch (Exception e)
{
JOptionPane.showMessageDialog(null, "Exception : "+e, "Error", JOptionPane.ERROR_MESSAGE);
}
}

Categories