I am trying to refresh a JTable using the DefaultTableModel without accessing the table itself again, but only the existing, but then updated table model.
Yet, I tried to update the table model itself and then notify the model about it (see in the code). For some reason, the table will not update. I do not know, if this is an access problem or if it is just not possible.
//in the Gui_Main class
private static void addTables(){
JTable tblMain = new JTable(Util_Tables.dtm);
}
//in the Util_Tables class, if the tables needs to be updated
public static DefaultTableModel dtm;
public static void updateTable(){
dtm = new DefaultTableModel(data, columns);
dtm.fireTableDataChanged();
}
So you're basic structure is all over the place. When you create a new instance of DefaultTableModel and assign it to dtm, this won't be reflected by the JTable, as it is still using the instance it first grabbed when it was created.
Exposing dtm the way you have, opens it up to undesirable modification and voids one of the principles of OO - encapsulation, where the class is responsible for the management of its properties. This is also a reason to reconsider the use of static
A better start would be to create a getter which returns a single instance of DefaultTableModel, so each call to it is guaranteed to return the same instance of DefaultTableModel and stops any one else from changing the underlying reference
private static void addTables(){
JTable tblMain = new JTable(Util_Tables.getModel());
}
//in the Util_Tables class, if the tables needs to be updated
private DefaultTableModel model;
public static DefaultTableModel getModel() {
if (model == null) {
model = new DefaultTableModel();
}
}
Okay, so how about updating the model? Well, you need to start by modifying your updateTable method, so it can be used to actually update the model in some meaningful way
public static void updateTable(Object[][] data, Object[] columnIdentifiers){
model.setDataVector(data, columnIdentifiers);
}
The model will then generate the events it needs itself. If you find yourself calling the fireXxx methods yourself, then it's a good indication that you're doing something wrong.
Related
I have read this article about how to make TableColumn editable, I have made some modifications to obtain some reusable code as follows:
public static <T> void setEditableColumn(TableColumn<T, String> column, BiConsumer<T,String> modifiedField, String type){
column.setCellFactory(TextFieldTableCell.forTableColumn());
column.setOnEditCommit(event->{
T model = event.getRowValue();
if(!event.getNewValue().isEmpty()){
modifiedField.accept(model,event.getNewValue());
}
});
}
But I want for this method to return the modified Model.
Iit is clear that I retrieve the modified model inside a lambda expression what are the possible ways of doing this. My objective is to be able to save that model in a database.
EDIT : I am now able to access the tableData by making a MainFrame.java instance in my Database.java but it's not working. The rows is not added.
I am new to Java and still learning. I am making a desktop app with mySQL interaction. I have a JTable which is declared in MainFrame.java like so
public javax.swing.JTable tableData;
Then I have a Database.java which has all the methods if interacting to the database. Then in Database.java I have function
private void writeResultSet(ResultSet resultSet) throws SQLException {
MainFrame mainframe = new MainFrame();
DefaultTableModel model = (DefaultTableModel)mainframe.tableData.getModel();
// resultSet is initialised before the first data set
while (resultSet.next()) {
String id = resultSet.getString("id");
String name = resultSet.getString("name");
model.addRow(new Object[]{id, name});
}
}
Then on that function I want to access tableData JTable because I will append rows in there based from the resultSet. Is it possible? Or what is the best way?
best approach would be a book ^^
use the table model to access table data, best practice is to use DefaultTableModel
DefaultTableModel model = new DefaultTableModel();
JTable table = new JTable(model);
now you can easily add/remove data from your table by simply adding/removing data to your table model
now you can add your data to your table by querying your ResultSet
ResultSet rs; // i really hope you already have performed your query
while(rs.next() ){
String str = rs.getString(...); //your Code here
//now you can add your data to the model:
model.addRow(new Object[]{str});
}
honestly - this is only a 10-line-tutorial, i think you have to do some more practise! but dont feel embarresed, i think you will learn quickly ^_^
It would be better to fetch the result set and update the tableData in MainFrame class only.
Also if you want to update this in Database.java class you need to have instance of MainFrame.java here. And would not be recommended
This is a simplifed version of what I'm trying to do. I have a map which maps an integer id to a list of strings. One of these lists from the map is displayed by a JTable at all times. (Depending on which id needs to be displayed) All information for the map is coming from a database, and is constantly being added to and removed from.
My DataClass which stores the full map and receives the updates from the database:
DataClass {
Map(Integer, List<String>) map;
// TabelModel gets list here
public List<String> getList(int id) {
return map.get(id);
}
// Updates from database come here
public updateList(int id, String info) {
if (map.containsKey(id) {
map.get(id).add(info);
}
else {
map.put(id, new List<String>(info));
}
}
// Remove from list, etc all down here
...
}
My Table model class:
MyTableModel extends DefaultTableModel {
List data;
public void updateData(int id) {
data = getList(id)
fireTableDataChanged();
}
... All the other stuff needed ...
}
Since database updates occur in batches, I know that at the end of a batch I have to update the table. So the DataClass informs the UI class which informs the table to update. This causes the updateData(id) to be called which retrieves a new set of data from the DataClass and calls fireTableDataChanged();
My Question are:
Is this the right way to go about updating/storing the data in the table?
Should getList return a clone of the data? If I just return the reference all updates from the database would be accessing that same reference and since these updates aren't running in the EDT wouldn't that be bad/frowned upon/wrong?
How can I do this using Java Events? PropertyChangeEvent? If so, how?
To the extent that your questions are related,
No; when new data is available, your updateData() method should update the internal data structure of your TableModel and fire an appropriate event; because DefaultTableModel knows nothing of your List, extend AbstractTableModel, as shown here; the JTable will update itself in response.
No; your database access layer should retain no references to queried objects, and no cloning should be necessary; forward queried objects to your display layer from the process() method of SwingWorker, or similar.
Use a TableModelListener to learn when and how the TableModel has changed; update the database accordingly.
For some reason, nothing changes about this JTable when this is called (this method updates the JTable after a user submits an SQL query).
Givens:
The dataVector and columnNamesVector are verified to be populated correctly.
JTable is a private class variable.
private void updateData() {
updateDataVariables();
table = new JTable(dataVector, columnNamesVector)
{
#SuppressWarnings({ "unchecked", "rawtypes" })
public Class getColumnClass(int column)
{
for (int row = 0; row < getRowCount(); row++)
{
Object o = getValueAt(row, column);
if (o != null)
{
return o.getClass();
}
}
return Object.class;
}
};
}
Any ideas?
It's a common beginner's fallacy to confuse objects with reference variables, but you need to understand that they are quite distinct. When you call this:
table = new JTable(dataVector, columnNamesVector) {.....
You are creating a new JTable object and having the table variable refer to it, but this has no effect on the JTable object that is displayed by the GUI, the one that the table variable was referring to previously. So you're changing the property of the reference variable, but leaving the original object unchanged.
The solution: You should not be creating a new JTable but rather you should be creating a new TableModel and then place that TableModel into the existing and visualized JTable. You can change a table's model by calling setModel(newModel) on it.
Edit: or as wolfcastle noted you could update the existing TableModel rather than create one anew.
You need to tell JTable that the data in model was updated with firing appropriate event.
See this tutorial
Hi Im doing a project for college and have a JTable in my GUI. I want the user to be able to select rows without being able to change the data in the table. I am using two arrays to crate the table not the table model. Thanks
I dont want to use tablemodels!!!!
You can implement the isEditable() method in your table model and return false and your table will not be editable:
public class MyTableModel extends AbstractTableModel{
public void isEditable(){
return false;
}
}
Then you have a table and you set its model to a MyTableModel object eg.
JTable table = new JTable();
table.setModel(new MyTableModel());
Extra Information:
AbstractTableModel.isCellEditable(int,int) returns false by default, so you don't need to override it to get that behavior.
The DefaultTableModel implementation of that method returns true by default, so that one must be overriden if you wish to make cells un-editable.
Resource of this answer.
You should define your own table model, by extending AbstractTableModel, or DefaultTableModel. Just override isCellEditable(int row, int col) and make it return false.
You will need to override the table model's isCellEditable() method to always return false.