JDBC TableModel for a JTable in Java? - java

I want to display a database table as a JTable. I have never used JTable before so I googled JTable and TableModel.
With that googling, I am able to write my own custom TableModel which show data stored in
Object[][] data;
Now, I want to show my database table data into JTable. I searched that also and have got an idea of that but still confused about what should goes where in the implementation class of AbstractTableModel.
Following is the code of custom TableModel.
public abstract class AbstractPOLDATTableModel extends AbstractTableModel {
protected boolean DEBUG = false;
private String[] columnNames;
private Object[][] data;
protected AbstractPOLDATTableModel(String[] columnNames, Object[][] data) {
this.columnNames = columnNames;
this.data = data;
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
#Override
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
#Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
if (col < 2) {
return false;
} else {
return true;
}
}
#Override
public void setValueAt(Object value, int row, int col) {
if (DEBUG) {
System.out.println("Setting value at " + row + "," + col
+ " to " + value
+ " (an instance of "
+ value.getClass() + ")");
}
data[row][col] = value;
fireTableCellUpdated(row, col);
if (DEBUG) {
System.out.println("New value of data:");
printDebugData();
}
}
private void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
Now, how to change the above code so that my JTable can have the follwing features:
It shows data from the database
User can edit the table directly and when he clicks on a "save" button, the changes reflect in the database data
User can insert data directly.
User can delete data directly.

I'm assuming you've implemented 1 according to what Adamski suggested in your previous question. Also, as suggested by Sanoj, change to using some sort of List to store your data.
To support 2-4, you must ensure that the data you're pulling out of the database comes from only one table and involves no derived data (eg. aggregations, column1 + column2). You will need to keep track of your where clause, if you intend to let the user filter rows.
Within each Row, store another Row (let's call it updatedRow) that represents the updates the user has made using the GUI. Once any update is made to the row, this field needs to be populated with a new Row containing the updated data . When "Save" is clicked, you run update queries for all Rows with a non-null updatedRow, updating the database with data in updatedRow which do not match those from the original Row (don't update if the user changes data back to how it was originally). You might also have another "Undo Updates" button which populates the table with the original data for all Rows with a non-null updatedRow.
I would strongly recommend that you store additional metadata on which fields form the primary key and prevent those from being changed, as updates to such columns may be expensive (since they are presumably indexed and may have some RI constraints attached). This should be reflected in the GUI by making such columns not editable. The where clause in updates would use only these fields rather than all fields (you will still need other filters that your user set in 1).
I'd suggest a two step process. The user starts by clicking a button which adds a row to the table. After entering data, the user clicks another button to insert into the database (or you can combine this functionality with updates in the "save" button). You will need to allow primary key fields to be editable for newly inserted rows if the primary key columns aren't some auto-generated ID.
To different between which Rows are already in the database and which ones aren't, I'd suggest storing a list of newly inserted Rows.
If you let the user pick only certain columns to display in the table, you will need to determine how to deal with the columns that aren't being displayed (eg. let the database set a default, populate with auto-generated ID).
Probably best to implement this by having a checkbox with each Row, then when the "Delete" button is clicked, it calls SQL to delete each checked Row using the filter from 1 and primary key metadata mentioned in 2, as well as to remove it from the table.
General considerations:
The same setValueAt method will be used for both updated and inserted Rows, but you want different behaviour. You want to set updatedRow for updates, but not when editting Rows you're about to insert.
How will you handle errors such as constraints not being met or invalid input (eg. 'abcde' in a numeric field)?
SQL injection.

Related

Java: Refreshing my jTable after data updates in child jDialog

I've investigated lots of different questions and answers around this, but can't find one that seems to work.
I'm new to Java, but have experience in a variety of different languages and, so far (in context to what I'm experimenting with), it's feeling a bit like VBA except with having to build up the actions/functions that you feel should be already there. This is, I expect, just down to my own inexperience though.
I'm using Netbeans IDE 8.2 with Java Runtime 1.80.
I have created jFrame that contains a jTable. The jTable is built with data like so:
public void showTasks(Boolean removeID) {
ArrayList<Tasks> list = tasksList("SELECT * FROM tasks");
JTable table = jTable1;
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
model.addRow(row);
}
// Remove the 'id' column from the table view
if(removeID) { table.removeColumn(table.getColumnModel().getColumn(0)); }
}
The background behind this is less relevant, but essentially tasksList is a function that applies the query to an SQL statement, returning an ArrayList. I build up my rows with 4 columns, then remove the first column so 'ID' is available but not visible (this final action has been segregated through testing/experimentation).
I have another area of code that opens a jDialog when a row is clicked, in which it is possible to update the MySQL DB.
Problem
I'm trying to throw in a function call so that the table data 'refreshes' when the jDialog is closed. I have temporarily added in a button to the jFrame (where the jTable lives) to test/debug this function.
I can't seem to get this to work, though. The closest I have achieved is to re-call showTasks(false), but this obvious just adds rows with updated data, rather than replacing the dataset. I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
As I'm new to Java, and may still be looking at it from a flawed method of thinking, I'm finding it difficult to apply any other examples to that of my own. I also can't seem to find a way to implement fireTableDataChanged().
Surely this is a simple concept I'm over-thinking?
Edit - based on below answer
Is there a reason why something like this would be considered incorrect, if deleting all rows and adding them back in is okay?
public void refreshTasks() {
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
int row_total = model.getRowCount();
for(int i= row_total -1;i>=0;i--) {
model.removeRow(i);
}
showTasks(false);
}
Edit: Button to invoke data update
Now works correctly (if not improperly) with the following:
private DefaultTableModel parentTable; // To store the parent 'Task' table model
public void setStart(int user,DefaultTableModel table) {
this.txt_taskID.setText(Integer.toString(user)); // Converts the ID to a string
addData(user); // Populates the fields
parentTable = table; // Sets parent TableModel to a variable
}
The above code is called from the Parent jFrame when the Dialog is opened, and passes the Table model and the 'ID' of the row I'm looking to edit. The table model is stored in parentTable.
There's also a 'Save' button, and a 'Cancel' button. I'm yet to separate these, and currently 'Save' does just that (SQL update and so on). My 'Cancel' button closes the dialog and refreshes the jTable, as per the below function:
private void btn_CancelActionPerformed(java.awt.event.ActionEvent evt) {
this.setVisible(false); // Hide the dialog
Menu menu = new Menu(); // for accessing the tasksList function
parentTable.setRowCount(0); // Clears the jTable data
// jTable data is then 'rebuilt' using the new data
ArrayList<Tasks> list = menu.tasksList("SELECT * FROM tasks");
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
parentTable.addRow(row);
}
}
I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
Yes that is probably the best practice.
The only other approach is to create a completely new TableModel and add it to the table using the setModel() method. The problem with this approach is that it will reset any custom renderers/editors you may have set on the table.
The easiest way to remove all the rows from the DefaultTableModel is to just use:
model.setRowCount(0);
I'm not sure how you want to do it but I'm gonna give you simple example for deleting and refreshing JTable maybe it's help you.
This following btnDelete Jbutton added to JFrame for deleting rows from table:
btnDelete.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowIndex = table.getSelectedRow();
if(rowIndex > -1) {
int x = (Integer) table.getModel().getValueAt(rowIndex, 0);
if (conn.removeContact(x) == true) { //here add your own code like removeID
model.removeRow(rowIndex);
lblInfo.setText("Contact deleted successfully.");
model.fireTableDataChanged();
} else {
lblInfo.setText("Cannot remove at this time!");
}
}else {
lblInfo.setText("At first you need select a row with single click!");
return;
}
}
});
and these following codes for refreshing table in primitive way :
btnRefresh.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowCount = model.getRowCount();
for (int i = rowCount - 1; i >= 0; i--) {//remove all rows
model.removeRow(i);
}
lblInfo.setText("Table datas updated successfully.");
for (Person p : conn.readAllContacts()) {//add all row from scratch
model.addRow(new Object[] { p.getId(), p.getName(), p.getLastName(), p.getPhone(), p.getEmail() });
}
}
});

How to extract named column from tablemodel in java swing

I have a table that shows the results of user defined queries in a swing project. I want to allow the user to extract the data from a particular names column, if present. At the moment I can select the data from a column when I click the column but I don't know how to do the same thing from a button so that only a particular column's data (the column is called HNum) is obtainable. The code I have so far is as follows. If this is impossible I could always try to make sure that HNum is the first column but I need something cleaner I think.
btnCompare.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object[] data_L = columnToArray(table,table.getSelectedColumn());
}
}
public Object[] columnToArray(JTable table, int columnIndex){
// get the row count
int rowCount = table.getModel().getRowCount();
// declare the array
Object [] data = new Object[rowCount];
// fetch the data
for(int i = 0; i < rowCount; i++){
data[i] = table.getModel().getValueAt(i, columnIndex);
}
return(data);
}
Do you tried to use a TableColumnModel ?
You can define all the treatment you need like a getColumnName{}
https://docs.oracle.com/javase/7/docs/api/javax/swing/table/TableColumnModel.html
edit :
an example
http://www.java2s.com/Tutorial/Java/0240__Swing/ExtendingAbstractTableModel.htm

How can i prevent the possibility to select a particular column in JTable?

I want to disable the possibility to select particular columns in a Jtable.
It's easy to disable the selection of certain rows with using the DefaultListSelectionModel class
But I don't know how to do that for columns.
Can any one give me a clue to implement this feature ?
You can override the method isCellEditable and implement as you want for example,
try this :
DefaultTableModel tableModel = new DefaultTableModel() {
#Override
public boolean isCellEditable(int row, int column) {
//Only the column nb 2
return column == 2;
}
};
table.setModel(tableModel);

Does Vaadin uses the itemId given to the Table.addItem() like a column in Vaadin Table UI component?

just a question about Vaadin's Table component. I have a table created with a TableFieldFactory, and I have noticed that if I count the number of times the TableFieldFactory is called for a Table with e.g. 8 rows and 4 columns, I get 40 as the result (instead of the expected 8 * 4 = 32).
So does Vaadin uses an hidden extra column when creating the table? Does this column contain the itemId given to the Table.addItem() method? Anyway here is the code I used for the field factory:
// UI's static field
public static int counter = 1;
// ... UI's init() method
tbl.setTableFieldFactory(new TableFieldFactory() {
#Override
public Field<?> createField(Container container, Object itemId,
Object propertyId, Component uiContext) {
TextField field = new TextField((String) propertyId);
counter++;
// User can only edit the "Numeric field"
if ("Numeric field".equals(propertyId)) {
field.setData(itemId);
// Remeber the field
valueFields.put((Integer) itemId, field);
// Focus if it is the first editable value
if ((Integer) itemId == 0) {
field.focus();
}
}
else {
field.setReadOnly(true);
}
return field;
}
});
// here counter is 40 for a 8x4 table
I guess the reason is the lazy loading of the vaadin table. I experienced that on the first load the table is initialized with one row (for better response to the user I suppose), after this it is re-initialized with the lazy-loading amount (in your case the whole table) this would explain the extra 8 calls.

remove or Add rows from JTable with AbstractTableModel

ive been trying to update the table after the insertion or deletion of items from a abstract table model but whenever i do that, instead of removing the old records and replace with the new ones, the old rows remains and it creates all the rows again without removing the old ones..so i get duplicate items, this is the code im using :
for the data inserted :
TestModel tm = new TestModel() ;
tm.fireTableRowsInserted(records.length, records.length);
and for the data deleted :
TestModel tm = new TestModel() ;
tm.fireTableRowsDeleted(records.length, records.length);
any clue of how to get around with that?
any help is greatly appreciated!
Kind regards,
Romulo Romero
Create a table with a boolean column. Since using this boolean column you can delete those rows that are selected for deletion. Just like the following screen shot,
Then in your TableModel make a List<StudentDO> such that it will hold all the table data.
Adding a Row:
To add a row just create a new StudentDO and send it to the table model and the model addRow method will add the object to the table list.
Deleting Rows:
For Deleting rows just call a delete method and this should fire event in TableModel such that model should traverse all the rows and check which ever row is selected and delete it.
Note: Deleting rows should be done from the end not from the beginning of the list.
StudentTableModel.java
class StudentTableModel {
// Required methods code goes here.
public void addRow(StudentDO do1) {
data.add(do1);
fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
}
public void deleteRow() {
for(int rowIndex = data.size() - 1; rowIndex >= 0; rowIndex--) {
if(data.get(rowIndex).isSelect()) {
data.remove(rowIndex);
}
}
fireTableDataChanged();
}
}
P.S: fireXXXMethods should be called only in the model. Because any data change will be responsible of the model.

Categories

Resources