I'm following this example code to create my own tableModel class to display data from a db using cachedRowSet, and use rowSetEvent to refresh the table when insert a row and delete a row to the cachedRowSet.
https://github.com/abissell/jdbctutorial/blob/master/src/com/oracle/tutorial/jdbc/CoffeesTableModel.java
public void rowChanged(RowSetEvent event) {
try {
tm.rs.moveToCurrentRow();
tm = new MyTableModel(tm.rs); // to get right row nums
table.setModel(tm);
} catch (SQLException er) {
er.printStackTrace();
}
}
public class MyTableModel extends AbstractTableModel {
CachedRowSet rs;
ResultSetMetaData meta;
int cols, rows;
public MyTableModel(CachedRowSet r) {
rs = r;
try {
meta = rs.getMetaData();
cols = meta.getColumnCount();
rs.beforeFirst();
rows = 0;
while (rs.next()) {
rows++;
}
rs.beforeFirst();
} catch(SQLException e) {
e.printStackTrace();
}
}
public void insert(String[] info) {
try {
rs.moveToInsertRow();
for(int i = 0;i < cols;i++) {
rs.updateString(i+1, info[i]);
}
rs.insertRow();
rs.moveToCurrentRow();
} catch(SQLException e) {
e.printStackTrace();
}
}
public void delete(int id) {
try {
rs.beforeFirst();
while(rs.next()) {
if(Integer.parseInt(rs.getString(1))==id) {
rs.deleteRow();
break;
}
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
#Override
public int getRowCount() {
return rows;
}
#Override
public int getColumnCount() {
return cols;
}
#Override
public String getColumnName(int col) {
try {
return meta.getColumnLabel(col + 1);
} catch (SQLException e) {
return e.toString();
}
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
try {
if (!rs.absolute(rowIndex + 1)) {
return null;
}
rs.absolute(rowIndex + 1);
Object o = rs.getObject(columnIndex + 1);
if (o == null) {
return null;
} else {
return o.toString();
}
} catch (SQLException e) {
e.printStackTrace();
return e.toString();
}
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
}
#Override
public void addTableModelListener(TableModelListener l) {
}
#Override
public void removeTableModelListener(TableModelListener l) {
}
}
con = DriverManager.getConnection(url);
st = con.prepareStatement("SELECT id,name,sex,age,cell FROM PATIENTS");
rs = st.executeQuery();
crs = new CachedRowSetImpl();
crs.setType(ResultSet.TYPE_SCROLL_SENSITIVE);
crs.setConcurrency(ResultSet.CONCUR_UPDATABLE);
crs.populate(rs);
tm is the tableModel.
The table will refresh immediately after a row deleted from the cachedRowSet, but when i insert a new row into cachedRowSet, then the table can't show the newly added row immediately. And i checked the rowChanged() been called and the row inserted inside the db.
Even tried to refetch the table data each time or repaint() still not working. I couldn't figure out why.
Maybe cause swing process racing then it can't refresh immediately? I just opened another JFrame to fill out the data and then insert.
Also, what if i use the resultSet directly inside the model? Will the table automatically updated when you insert a row in the db if i didn't close the connection. Or it's just used to get the initial data and you have to run query again to refresh.
Thank you
=================update==================
I tried to change the table model to use ArrayList to store and update db results, then the insert will be update the table immediately. Maybe cause cachedRowSet update slower?
Confused, don't know why.
Related
Hello when I delete a product from database through a table using a popupMenu (Right click Button) the row still shows up in the table list, the insertProduct method is working fine and inserts the row to table but the removeProduct method doesn't work can you tell me where is the problem in my code please ?
ProductTable.java:
public class ProductTable extends AbstractTableModel {
ProductsDao pd = new ProductsDao();
private final List<Products> products;
public ProductTable() throws Exception {
this.patients = (ArrayList<Products>) pd.getProductsList();
}
private String[] columnNames = {"PRODUCT NAME", "PRODUCT CATEGORY", "PRODUCT PRICE"};
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
#Override
public int getRowCount() {
return products.size();
}
#Override
public Object getValueAt(int row, int column) {
Products p = products.get(row);
switch (column)
{
case 0: return p.getProductName();
case 1: return p.getProductCategory();
case 2: return p.getProductPrice();
}
}
#Override
public void setValueAt(Object value, int row, int column) {
Products p = products.get(row);
switch (column) {
case 0: p.setProductName((String)value); break;
case 1: p.setProductCategory((Date)value); break;
case 2: p.setProductPrice((int)value); break;
}
fireTableCellUpdated(row, column);
}
public Products getProduct(int row) {
return products.get(row);
}
public void addProduct(Products p) {
insertProduct(getRowCount(), p);
}
public void insertProduct(int row, Products p) {
products.add(row, p);
fireTableRowsInserted(row, row);
}
public void deleteProduct(Products p) {
removeProduct(getRowCount(), p);
}
public void removeProduct(int row, Products p) {
products.remove(row);
fireTableRowsDeleted(row, row);
}
}
JFrame :
public void popupTable() {
JPopupMenu popupMenu = new JPopupMenu();
JMenuItem menuItem1 = new JMenuItem("Delete", new ImageIcon(getClass().getResource("")));
menuItem1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int row = jTable1.getSelectedRow();
String cell = jTable1.getModel().getValueAt(row, 0).toString();
Products p = new Products();
p.setId(cell);
try {
int count = ProductsDao.getInstance().delete(p);
if (count == 1) {
removeRowFromTable(p);
JOptionPane.showMessageDialog(null, "Deleted");
} else {
JOptionPane.showMessageDialog(null, "Faild");
}
} catch (Exception ex) {
Logger.getLogger(AddNewPatient.class.getName()).log(Level.SEVERE, null, ex);
}
});
popupMenu.add(menuItem1);
jTable1.setComponentPopupMenu(popupMenu);
}
public static void removeRowFromTable(Products data) {
ProductTable t = (ProductTable) jTable1.getModel();
t.deleteProduct(data);
}
ProductsDao.java:
#Override
public List<Products> getProductsList() throws Exception {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<Products> products = new ArrayList<Products>();
try {
con = getConnection();
String sql = "SELECT * FROM products ORDER BY PRODUCT_ID DESC";
ps = con.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
Products product = new Products();
product.setProductName(rs.getString("PRODUCT_NAME"));
product.setProductCategory(rs.getString("PRODUCT_CATEGORY"));
product.setProductPrice(rs.getInt("PRODUCT_PRICE"));
products.add(product);
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.getMessage());
} finally {
rs.close();
ps.close();
closeConnection(con);
}
return products;
}
These methods don't make sense I'm afraid:
public void deleteProduct(Products p) {
removeProduct(getRowCount(), p);
}
public void removeProduct(int row, Products p) {
products.remove(row);
fireTableRowsDeleted(row, row);
}
since you never really use the Products parameter, p, in any useful way to identify where it is in the table model. Also, I'm not really sure why you have two methods, and what they are each supposed to represent.
Instead, assuming that products in your TableModel is an ArrayList<Products>, and assuming that you've overridden Products equals and hashCode methods appropriately, you need to find the index of the selected item in the list and then remove it from the list and notify the listeners that this has been performed. Something like:
public void deleteProduct(Products p) {
int row = products.indexOf(p);
products.remove(row);
fireTableRowsDeleted(row, row);
}
This will only work if you override equals and hashCode correctly for Products.
I use this code to get my ID number of my JTable,
But "AWT-EventQueue-0" java.lang.ClassCastException occur,
public void actionPerformed(ActionEvent e) {
if (e.getSource() == dellButton) {
try {
int rowToDelete = 0;
int rowToModel = 0;
if (table.getSelectedRow() > -1) {
rowToDelete = table.getSelectedRow();
rowToModel = table.convertRowIndexToModel(rowToDelete);
tableModel.removeRow(rowToModel);
}
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
}
My TableModel.removeRow() method:
...
public void removeRow(int rowNumber) throws SQLException {
String removeQuery = "delete from mytable where id=?";
PreparedStatement pStatement = con.prepareStatement(removeQuery);
pStatement.setInt(1, rowNumber);
int rowsAffected = pStatement.executeUpdate();
}
...
int rowID = (int) table.getValueAt(selectedRowIndex, 0); is useless code line,
code line tableModel.removeRow(rowID, rowIndex); has wrong paramaters
test if any row is selected, otherwise selected row returns -1
everything could be only
#Override
public void actionPerformed(ActionEvent e) {
int rowToDelete = 0;
int rowToModel = 0;
if (table.getSelectedRow() > -1) {
rowToDelete = table.getSelectedRow();
rowToModel = table.convertRowIndexToModel(rowToDelete);
model.removeRow(rowToModel);
}
}
be sure that your SelectionMode will be, otherwise you would need to loop inside arrays of selected rows,
table.getSelectionModel().setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
I have a desktop application in swing with NetBeans IDE
the application has a JTable that displays
data from a lucene search operation.
Any time a new search is made ,
the table appends the new search results to
the previous search result. What I
want is for the table to replace any exiting
search results with the new search results. In order
words for the table to refresh and display the new
search results.
Any suggestions available
this is the code snippet for the datamodel
public class MyTableModel extends AbstractTableModel {
private Vector<Vector<String>> dataList = new Vector<>();
private String[] header = { "ID","SUBJECT","LETTTER FROM","LETTER DATE","DATE RECEIED",
"REMARKS","DATE DISPATCHED","DESTINATION OFFICE"};
public Vector<Vector<String>> getDataList() {
return dataList;
}
public void setDataList(Vector<Vector<String>> dataList) {
this.dataList = dataList;
fireTableDataChanged();
}
public void setHeader(String[] header) {
this.header = header;
}
public String[] getHeader() {
return header;
}
#Override
public int getRowCount() {
return dataList.size();
}
#Override
public int getColumnCount() {
return header.length;
}
#Override
public String getColumnName(int col) {
return header[col];
}
#Override
public Object getValueAt(int row, int col) {
return dataList.get(row).get(col);
}
}
this code passes the search result to the data model class
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {
try {
searchField = searchTextField.getText();
if(!searchField.isEmpty())
{
matrix = dbs.searchDatabase(searchField + "*");
myModel.setDataList(matrix);
}
} catch (CorruptIndexException ex) {
Logger.getLogger(GNSSJFrame.class.getName()).log(Level.SEVERE, null, ex);
} catch (LockObtainFailedException ex) {
Logger.getLogger(GNSSJFrame.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException | ParseException ex) {
Logger.getLogger(GNSSJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
If your table model is in this way,
class AllTableModel extends AbstractTableModel {
// Suppose this is the data list table is using,
List<TableData> tableData = new ArrayList<TableData>();
// Override methods goes here.
public void setTableData(List<TableData> tableData) {
this.tableData = tableData;
fireTableDataChanged();
}
}
Now, set the new data to the list using the table model instance.
allTableModel.setTableData(/* Set new search results to the list.*/);
This program is used to read data from database. In the database,there are three tables pki17, pki18, pkn18. For viewing different tables,JComboBox is used and it works by changing the TableModel of the table. Also by using tableChanged method I made table editable. When a cell value in the table is changed the corresponding value in the database has to change to.
When I use tableChanged and actionPerformed methods together, value in database doesn’t get changed when I’m editing a cell in the swing table. When I remove actionPerformed method, then I can update database by editing table cells.
I need to have both abilities, to choose a table from the database by using JComboBox and update database values by editing values in the swing table.
I think the problem exists because TableModel of the table is changed in both methods. But I don’t know how to solve it.
public class TableCombobox extends JPanel implements ActionListener, TableModelListener {
static JTable table;
static JComboBox box;
static MyTableModel model;
static Connection con = null;
static Statement stmt = null;
public TableCombobox() throws SQLException {
super(new BorderLayout());
table = new JTable(new MyTableModel("pki18"));
table.setPreferredScrollableViewportSize(new Dimension(500, 400));
table.setFillsViewportHeight(true);
table.getModel().addTableModelListener(this);
JScrollPane scrollPane = new JScrollPane(table);
JPanel menuPanel = new JPanel();
menuPanel.setLayout(new BoxLayout(menuPanel, BoxLayout.Y_AXIS));
menuPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1,
Color.black));
String[] dalykas = { "Chose groop", "pki17", "pki18", "pkn18" };
box = new JComboBox(dalykas);
box.setMaximumSize(new Dimension(150, 25));
box.setAlignmentX(Component.LEFT_ALIGNMENT);
box.addActionListener(this);
box.setSelectedIndex(2);
menuPanel.add(box);
JPanel cards = new JPanel(new CardLayout());
cards.add(scrollPane, "view");
add(menuPanel, BorderLayout.LINE_START);
add(cards, BorderLayout.CENTER);
}
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int col = e.getColumn();
model = (MyTableModel) e.getSource();
String stulpPav = model.getColumnName(col);
Object data = model.getValueAt(row, col);
Object studId = model.getValueAt(row, 0);
System.out.println("tableChanded works");
try {
new ImportData(stulpPav, data, studId);
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
public void actionPerformed(ActionEvent event) {
JComboBox cb = (JComboBox) event.getSource();
String pav = (String) cb.getSelectedItem();
if (pav != "Chose groop") {
try {
model = new MyTableModel(pav);
table.setModel(model);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void GUI() throws SQLException {
JFrame frame = new JFrame("E-gradebook");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new TableCombobox());
frame.pack();
frame.setSize(800, 400);
frame.setVisible(true);
}
public static void main(String[] args) throws SQLException {
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost/pki18",
"root", "");
GUI();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} finally {
if (stmt != null)
stmt.close();
}
}
static Connection getConnection() {
return con;
}
}
public class ImportData {
static Connection con = TableCombobox.getConnection();
public ImportData(String a, Object b, Object c)
throws ClassNotFoundException, SQLException {
Statement stmt = null;
try {
String stulpPav = a;
String duom = b.toString();
String studId = c.toString();
System.out.println(duom);
con.setAutoCommit(false);
stmt = con.createStatement();
stmt.addBatch("update pki18 set " + stulpPav + " = " + duom
+ " where studento_id = " + studId + ";");
stmt.executeBatch();
con.commit();
} catch (BatchUpdateException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (stmt != null)
stmt.close();
con.setAutoCommit(true);
System.out.println("Data was imported to database");
}
}
}
public class MyTableModel extends AbstractTableModel{
static int rowCount;
static Object data [][];
static String columnNames [];
public MyTableModel(String grupName) throws SQLException{
String query ="select Studento_id, vardas_pavarde, 1_semestras,"+
" 2_semestras, egzaminas, bendras_balas "+
"from pki18." + grupName;
ResultSet rs ;
Connection con = TableCombobox.getConnection();
Statement stmt = null;
stmt = con.createStatement();
rs = stmt.executeQuery(query);
rs.last();
rowCount = rs.getRow();
data = new Object[rowCount][6];
rs = stmt.executeQuery(query);
for (int iEil = 0; iEil < rowCount; iEil++){
rs.next();
data[iEil][0] = rs.getLong("Studento_id");
data[iEil][1] = rs.getString("Vardas_Pavarde");
data[iEil][2] = rs.getByte("1_semestras");
data[iEil][3] = rs.getByte("2_semestras");
data[iEil][4] = rs.getByte("Egzaminas");
data[iEil][5] = rs.getByte("bendras_balas");
}
String[] columnName = {"Asmens_kodas","Vardas_Pavarde","1_Semestras"
,"2_Semestras","Egzaminas","Bendras_Balas"};
columnNames = columnName;
}
public int getColumnCount(){
return columnNames.length;
}
public int getRowCount(){
return data.length;
}
public String getColumnName(int col){
return columnNames[col];
}
public Object getValueAt(int row, int col){
return data[row][col];
}
public Class getColumnClass(int col){
return getValueAt(0, col).getClass();
}
public boolean isCellEditable(int row, int col){
return true;
}
public void setValueAt(Object value, int row, int col){
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
In the constructor, you add the table model listener to the current model only:
table.getModel().addTableModelListener(this);
In the action event, however, you replace the table model:
model = new MyTableModel(pav);
table.setModel(model);
As a consequence, the new table model won't have the listener, and you won't receive notifications any more. Have the actionPerformed method add the listener as well, and your problem should be fixed.
I used to display my database data in a JTable and it was working fine. I found out that I need to implement AbstractTableModel or DefaultTableModel to update the data instantly.
I am not sure what I should write in getValueAt()? Where should I add fireDataChanged()? Any guidance is appreciated, thanks!
I used to retrieve my database data with this code:
Vector columnNames = new Vector();
Vector data = new Vector();
try
{
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection ("jdbc:mysql://localhost:3306/watchlist","root","root");
String sql = "SELECT * FROM watchlist";
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);
ResultSetMetaData md = rs.getMetaData();
int columns = md.getColumnCount();
for (int i = 1; i <= columns; i++)
{
columnNames.addElement( md.getColumnName(i));
}
int rowCount = md.getColumnCount();
while (rs.next())
{
Vector row = new Vector(rowCount);
for (int i=1; i <= rowCount; i++)
{
row.addElement( rs.getObject(i) );
}
data.addElement( row );
}
rs.close();
stmt.close();
con.close();
}
catch(Exception e)
{
System.out.println(e);
}
My AbstractTableModel:
public class MyTableModel extends AbstractTableModel
{
Vector columnNames = new Vector();
Vector data = new Vector();
public void connectionDB()
{
try
{
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/watchlist","root","root");
String sql = "SELECT * FROM watchlist";
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);
ResultSetMetaData md = rs.getMetaData();
}
catch(Exception e)
{
System.out.println(e);
}
}
public int getColumnCount()
{
return columnNames.size();
}
public int getRowCount()
{
return data.size();
}
public Object getValueAt()
{
return data;
}
public boolean isCellEditable(int row, int col)
{
return false;
}
}
Here is a sample of using AbstractTableModel:
public class CustomTableModel extends AbstractTableModel
{
private static final long serialVersionUID = 1L;
private static final String[] columnNames = new String[]{"ID", "Name", "Number", "Yes/No"};
protected List<Foo> lstFoo;
protected Class<?>[] types = new Class[]{Integer.class, String.class, String.class, Boolean.class};
public CustomTableModel(List<Foo> lstFoo)
{
this.lstFoo = lstFoo;
fireTableDataChanged();
}
#Override
public String getColumnName(int columnIndex)
{
return columnNames[columnIndex];
}
#Override
public Class<?> getColumnClass(int columnIndex)
{
return types[columnIndex];
}
#Override
public boolean isCellEditable(int row, int columnIndex)
{
return false;
}
#Override
public Object getValueAt(int row, int column)
{
if(row < 0 || row >= lstFoo.size()) return null;
Foo obj = lstFoo.get(row);
switch(column)
{
case 0: return obj.getId();
case 1: return obj.getName();
case 2: return obj.getNumber();
case 3: return obj.isYes();
default: return null;
}
}
#Override
public int getRowCount()
{
return lstFoo.size();
}
#Override
public int getColumnCount()
{
return columnNames.length;
}
}
just one point, two many connections to a database will reduce the speed and too much overhead. I had this issue and was able to solve it.