How to fill data in a JTable with database?
I would recommend taking the following approach:
- Create a
Row
class to represent a row read from yourResultSet
. This could be a simple wrapper around anObject[]
. - Create a
List<Row>
collection, and subclassAbstractTableModel
to be backed by this collection. - Use a
SwingWorker
to populate yourList<Row>
by reading from the underlyingResultSet
on a background thread (i.e. within thedoInBackground()
method). CallSwingWorker
'spublish
method to publishRow
s back to the Event Dispatch thread (e.g. every 100 rows). - When the
SwingWorker
'sprocess
method is called with the latest chunk of Rows read, add them to yourList<Row>
and fire appropriateTableEvent
s to cause the display to update. - Also, use the
ResultSetMetaData
to determine theClass
of each column within theTableModel
definition. This will cause them to be rendered correctly (which won't be the case if you simply use a 2DObject[][]
array).
The advantage of this approach is that the UI will not lock up when processing large ResultSet
s, 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();
Fill JTable with data from database
At every iteration, you're creating a new matrix data
with only the info of the last product iterated. This is why you're seeing only the last item. This is just a syntax problem.
Assuming the productList
already contains the results from your database, you could initialize the rows of your matrix data
with the number of products in your list, while the number of columns with the fixed value 4, which is the number of fields you want to display (id, name, price and category).
ArrayList<Produkt> productList;
private void createTable() {
String col[] = {"ID", "name", "price", "category"};
//Initialize the number of rows of your matrix with the number of products in your list
Object[][] data = new Object[productList.size()][4];
//Copy every i-th product field in its corresponding column
for (int i = 0; i < productList.size(); i++) {
data[i][0] = productList.get(i).getId();
data[i][1] = productList.get(i).getName();
data[i][2] = productList.get(i).getPrice();
data[i][3] = productList.get(i).getCategory();
}
productTable.setModel(new DefaultTableModel(data, col));
}
Populate JTable from database on login
I ended up solving the problem after alot of hours of research and many failed attempts. The introduction of a helper method that returns a table model to fire the update to the table was the key to the solution.
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.util.Vector;
@SuppressWarnings("unchecked")
public class FillTable extends JFrame
{
private Vector columnNames, data, row;
private Connection connection;
private final String dbUsername = "aUsername";
private final String dbPassword = "aPass";
private JTable groceryTable;
private TableColumn column;
private JScrollPane pane;
private JPanel panel;
public FillTable()
{
createComponents();
setSize(800,800);
setTitle("A filled Table");
}
public static void main(String[] args)
{
JFrame frame = new FillTable();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(false);
frame.setLocationRelativeTo(null);
frame.setResizable(true);
frame.setVisible(true);
}
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
try {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
connection = DriverManager.getConnection("redactedDBINFO", dbUsername, dbPassword);
String sql = "select upper(name) as Grocery, fname as ordered_by, dateordered as date_ordered from groceries inner join grocerylist on groceries.groceryid=grocerylist.groceryid inner join users on grocerylist.orderedby= users.userid";
Statement statement = connection.prepareStatement(sql);
ResultSet rset = statement.executeQuery(sql);
groceryTable.setModel(updateModel(rset));
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
public DefaultTableModel updateModel(ResultSet rset) throws SQLException
{
ResultSetMetaData metaData = rset.getMetaData();
Vector<String> columnNames = new Vector<String>();
int columnCount = metaData.getColumnCount();
for (int column = 1; column <= columnCount; column++) {
columnNames.add(metaData.getColumnName(column));
}
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
while (rset.next()) {
Vector<Object> vector = new Vector<Object>();
for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
vector.add(rset.getObject(columnIndex));
}
data.add(vector);
}
return new DefaultTableModel(data, columnNames);
}
private void createComponents()
{
JButton button = new JButton("Press me");
ActionListener buttonListener = new ButtonListener();
button.addActionListener(buttonListener);
groceryTable = new JTable();
pane = new JScrollPane(groceryTable);
panel = new JPanel();
panel.add(button);
panel.add(pane);
add(panel);
}
}
How to populate JTable from database on button event
Ive removed the local variable
No, you removed the instance variable. Did you actually try this with your real code or just edit the question?
I have no idea how to provide a testable database for this question
I already suggested you create a method to simplify the logic. Somethng like:
public TableModel getDataFromDatabase()
{
DefaultTableModel model = new DefaultTableModel(5, 5);
model.setValueAt("Hard", 0, 0);
model.setValueAt("Coded", 1, 1);
model.setValueAt("Data", 2, 2);
return model;
}
Then in your ActionListener you simple do something like:
table_ResQues.setModel( getDataFromDataBase() );
Once you get this basic logic working you move the SQL logic into the getDataFromDatabase()
method.
So now you create your SSCCE showing how you actually create the frame and add the components to the frame. The code should be compilable and executable.
Edit:
You have been told to display a table is a scrollpane. It is no extra effort to do this. Instead of using:
panel.add(table);
you use:
panel.add( new JScrollPane( table ) );
I would also suggest that to test your layout you can use code like the following to display a dummy table:
//JTable table_ResQues = new JTable();
JTable table_ResQues = new JTable(5,5);
Then when you use the setModel() method, only the data is affected, not the layout of the table.
I cant help but feel this is a fireTableDataChanged problem though.
I doubt it. That method is invoked by the TableModel when you change the data in the model. It is not used in this case because you are using the setModel(...)
method. This will cause the table to repaint() itself automatically.
How to populate JTable from ResultSet?
I think the simplest way to build a model from an instance of ResultSet
, could be as follows.
public static void main(String[] args) throws Exception {
// The Connection is obtained
ResultSet rs = stmt.executeQuery("select * from product_info");
// It creates and displays the table
JTable table = new JTable(buildTableModel(rs));
// Closes the Connection
JOptionPane.showMessageDialog(null, new JScrollPane(table));
}
The method buildTableModel
:
public static DefaultTableModel buildTableModel(ResultSet rs)
throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
// names of columns
Vector<String> columnNames = new Vector<String>();
int columnCount = metaData.getColumnCount();
for (int column = 1; column <= columnCount; column++) {
columnNames.add(metaData.getColumnName(column));
}
// data of the table
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
while (rs.next()) {
Vector<Object> vector = new Vector<Object>();
for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
vector.add(rs.getObject(columnIndex));
}
data.add(vector);
}
return new DefaultTableModel(data, columnNames);
}
UPDATE
Do you like to use javax.swing.SwingWorker
? Do you like to use the try-with-resources
statement?
public class GUI extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new GUI().setVisible(true);
}
});
}
private final JButton button;
private final JTable table;
private final DefaultTableModel tableModel = new DefaultTableModel();
public GUI() throws HeadlessException {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
table = new JTable(tableModel);
add(new JScrollPane(table), BorderLayout.CENTER);
button = new JButton("Load Data");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
loadData();
return null;
}
}.execute();
}
});
add(button, BorderLayout.PAGE_START);
setSize(640, 480);
}
private void loadData() {
LOG.info("START loadData method");
button.setEnabled(false);
try (Connection conn = DriverManager.getConnection(url, usr, pwd);
Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery("select * from customer");
ResultSetMetaData metaData = rs.getMetaData();
// Names of columns
Vector<String> columnNames = new Vector<String>();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
columnNames.add(metaData.getColumnName(i));
}
// Data of the table
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
while (rs.next()) {
Vector<Object> vector = new Vector<Object>();
for (int i = 1; i <= columnCount; i++) {
vector.add(rs.getObject(i));
}
data.add(vector);
}
tableModel.setDataVector(data, columnNames);
} catch (Exception e) {
LOG.log(Level.SEVERE, "Exception in Load Data", e);
}
button.setEnabled(true);
LOG.info("END loadData method");
}
}
How can I populate my JTable with my embedded database?
I have found the solution - I've been trying to wrap my head around the problem for way too long, and I finally fixed it.
Try setting the table model manually first.
JTable tigerTable = new JTable();
tigerTable.setModel(new DefaultTableModel(0, numberOfColumns));
When you don't specify a model, the JTable creates an anonymous inner class and generally doesn't behave how you want it to.
Populating jTable using database data
So, you need some way to "tell" the table that the model has been loaded. You could use a listener call back mechanism, but it might be easier to use a SwingWorker
instead.
This will allow you to make a call to the database in a background thread and when it's finished, update the UI from within the EDT.
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Vector;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import sun.applet.Main;
public class DataLoadWorker extends SwingWorker<TableModel, TableModel> {
private final JTable table;
public DataLoadWorker(JTable table) {
this.table = table;
}
@Override
protected TableModel doInBackground() throws Exception {
Vector data = new Vector();
Vector columns = new Vector();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String stmt = "SELECT * FROM APP.DATAVAULT";
ps = Main.getPreparedStatement(stmt);
rs = ps.executeQuery();
ResultSetMetaData md = rs.getMetaData();
int columnCount = md.getColumnCount();
//store column names
for (int i = 1; i <= columnCount; i++) {
columns.add(md.getColumnName(i));
}
columns.ensureCapacity(columnCount);
Vector row;
while (rs.next()) {
row = new Vector(columnCount);
for (int i = 1; i <= columnCount; i++) {
row.add(rs.getString(i));
}
data.add(row);
//Debugging
}
// List.setModel(tableModel);
} finally {
try {
ps.close();
} catch (Exception e) {
}
try {
rs.close();
} catch (Exception e) {
}
}
DefaultTableModel tableModel = new DefaultTableModel(data, columns);
return tableModel;
}
@Override
protected void done() {
try {
TableModel model = get();
table.setModel(model);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
}
You're example won't work either.
The Vector
s columns
and data
are declared withing the context of the try-catch
, which means they won't be visible to the remainder of the method, so DefaultTableModel tableModel = new DefaultTableModel(data, columns);
won't compile.
You should be, at the very least, dumping the stack trace of any exceptions, it will help you find where the actually error is, so instead of System.out.println(e.getMessage());
, you should use e.printStackTrace();
. A better solution is to use the Logger
either from the JDK or a third party logger like log4j
.
You are also resources, that is, if you open, you should close it. While you do call ps.close()
and rs.close()
, if, for what ever reason, an exception occurs, they will not be called, leaving resources open.
Add these to the finally
block of your try-catch
to ensure that they are closed and make all best efforts to close them.
Related Topics
Singleton Class with Several Different Classloaders
How to Call Launch() More Than Once in Java
How to Change Webservice Url Endpoint
How to Check Available Space on Android Device? on Sd Card
Show Only Two Digit After Decimal
How to Convert Firebase Data to Java Object...
How to Properly Use Backwards Compatible Vector Drawable with the Latest Android Support Library
Getting JSONobject from JSONarray
Is There Any Library or Algorithm for Persian (Shamsi or Jalali) Calendar in Android
How to Make a Jtable Non-Editable
How Returns Xxxsize from Jcomponent(S) Added to the Jlabel
JPA Native Query Select and Cast Object
Android Project Using Httpclient --> Http.Client (Apache), Post/Get Method
How to Use Okhttp to Upload a File
Opencv Template Matching Example in Android
How to Get Current Location in Googlemap Using Fusedlocationproviderclient