I am currently working on a Scheduling Program that will be used by one of my managers for multiple purposes, but mainly to set up the weekly schedules for the employees. When accessing the SQL Database from a remote location it obviously takes more than a split second to load, and I would like to provide the user with some feedback so that they know the program is still running.
I would really like a progress bar that shows a percentage of the rows in the table that have been loaded out of the total rows while it is being loaded, but I don't know if that's possible. I was thinking something such as a JProgressBar that is indeterminate, that just moves while the connection is being made and the result set is being processed. Thanks!
The following code is the method that is connecting to the SQL database and processing the Result Set:
public static String[][] getEmps(String sqlQuery) {
String[][] employee = new String[countRows("SELECT COUNT(*) FROM employee;")][10]; //creates array for employees
ResultSet rs = null; //declares result set
try(Connection conn = DriverManager.getConnection(url,uname,pass)) { //connects to database
Integer count = 0;
try(Statement stmt = conn.createStatement()) {
rs = stmt.executeQuery(sqlQuery);
while (rs.next()){
employee[count][0] = rs.getString("fname"); //Employee's First Name
employee[count][1] = rs.getString("lname"); //Employee's Last Name
employee[count][2] = String.valueOf(rs.getInt("enum")); //Employee's Employee ID
employee[count][3] = rs.getString("dept"); //Employee's Department
employee[count][4] = rs.getString("position"); //Employee's Position in Dept
employee[count][5] = String.valueOf(rs.getInt("class")); //Employee's Class
employee[count][6] = String.valueOf(rs.getInt("cashNum")); //Employee's Cashier Num
employee[count][7] = String.valueOf(rs.getInt("assNum")); //Employee's Assistant Num
employee[count][8] = String.valueOf(rs.getInt("age")); //Employee's Age
employee[count][9] = String.valueOf(rs.getDate("dob")); //Employee's Date of Birth
count++;
}
return employee;
}catch (SQLException ex) { //Prints Errors When DB Connection Fails
System.err.println("SQLException: " + ex.getMessage());
System.err.println("SQLState: " + ex.getSQLState());
System.err.println("VendorError: " + ex.getErrorCode());
System.exit(1);
}
}catch (SQLException ex) { //Prints Errors When DB Connection Fails
System.err.println("SQLException: " + ex.getMessage());
System.err.println("SQLState: " + ex.getSQLState());
System.err.println("VendorError: " + ex.getErrorCode());
System.exit(1);
}
return null;
}
...and the Progress Bar class which I would like to have appear and display the progress:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.Dimension;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.JLabel;
import javax.swing.border.TitledBorder;
import java.awt.Font;
import java.awt.Color;
import java.awt.Cursor;
public class ProgressBar extends JFrame {
private JPanel contentPane;
private JPanel pnlProgressBar;
private String title = null;
private ProgressBar progressWindow = this;
private BufferedImage icon;
private JProgressBar progressBar;
private JPanel panel;
private JPanel panel_1;
public static void main(String[] args){
ProgressBar progressbar = new ProgressBar("TEST");
}
public JProgressBar getProgressBar() {
return progressBar;
}
public ProgressBar(String title){
this.title = title;
mbOSBoot.setLaf();
initialize();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
setVisible(true);
}
private void initialize() {
setTitle("Loading.");
setPreferredSize(new Dimension(400, 125));
setLocationRelativeTo(null);
setBounds(new Rectangle(0, 0, 400, 125));
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(100, 100, 400, 125);
//Animates a Loading... as title of window
class AnimatedLoading extends TimerTask {
@Override
public void run() {
String currTitle = progressWindow.getTitle();
switch(currTitle){
case "Loading.": progressWindow.setTitle("Loading..");
break;
case "Loading..": progressWindow.setTitle("Loading...");
break;
case "Loading...": progressWindow.setTitle("Loading.");
break;
}
}
}
EventQueue.invokeLater(new Runnable() {
public void run() {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new AnimatedLoading(), 0, 1000);
}
});
//End Window Animation
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
{
pnlProgressBar = new JPanel();
pnlProgressBar.setBorder(new TitledBorder(null, title, TitledBorder.LEADING, TitledBorder.TOP, null, new Color(59, 59, 59)));
contentPane.add(pnlProgressBar, BorderLayout.CENTER);
pnlProgressBar.setLayout(new BorderLayout(0, 0));
{
progressBar = new JProgressBar(0,100);
progressBar.setFont(new Font("SansSerif", Font.BOLD, 13));
progressBar.setStringPainted(false);
pnlProgressBar.add(progressBar);
progressBar.setVisible(true);
}
{
panel = new JPanel();
pnlProgressBar.add(panel, BorderLayout.NORTH);
}
{
panel_1 = new JPanel();
pnlProgressBar.add(panel_1, BorderLayout.SOUTH);
}
}
}
}
So, I realize that I left out some important info...The method getEmps()
is being called by another class, which needs to be given the resulting String[][]
to create the GUI. Would I have to include all of the code regarding the SwingWorker and such in the class that is calling the getEmps()
method, or can I do this in a separate class for the sake of organization? The piece of code that calls the getEmps()
method is as follows:
if(SQLDatabase.countRows("SELECT COUNT(*) FROM employee;") == 0){
JOptionPane.showMessageDialog(null, "No Employees Could Be Found in the Database. If You Believe This is an Error, Please Contact Your Network Administrator");
}else{
for(int i = 0; i < SQLDatabase.countRows("SELECT COUNT(*) FROM employee;"); i++){
String[][] emps = new String[SQLDatabase.countRows("SELECT COUNT(*) FROM employee;")][10];
emps = SQLDatabase.getEmps();
EmployeeInfoPanel empInfoPanel = new EmployeeInfoPanel(pnlViewPane, emps, i, title, frmGuiAllEmps);
}
}
As suggested here, you should retrieve rows in the background using SwingWorker
. To see the effect, start from this complete example.
Add a JProgressBar
:
f.add(model.jpb, BorderLayout.NORTH);
…
private static class JDBCModel extends AbstractTableModel {
private final JProgressBar jpb = new JProgressBar();
…
}
Set the indeterminate
property when the worker thread starts:
JDBCWorker worker = new JDBCWorker();
jpb.setIndeterminate(true);
worker.execute();
Clear it in the worker's implementation of done()
:
@Override
protected void done() {
jpb.setIndeterminate(false);
}
See this related example if you decide to show intermediate progress.