I want to create jcomponents dynamically using database. when I open any jframe or jpanel components like jlabel, jtextfields, jcombobox, etc should be created by reading database rows.
I am confused in how to give reference from database value i.e. in the String to the jcomponent's object.
this is my database table
try{
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db","root","pass");
stat = con.createStatement();
ResultSet rs = stat.executeQuery("select * from design");
while(rs.next()){
jTextField1 = new JTextField();
jTextField1.setSize(rs.getInt("height"),rs.getInt("width"));
jTextField1.setLocation(rs.getInt("x"), rs.getInt("y"));
}
rs.close();
stat.close();
con.close();
}
catch(Exception e){
System.out.println(e);
}
this is demo table.
I know this will not work because I can't communicate with objects and database.
I want to print jcomponents on jframe. I will write for loop to print them multiple times.
please help me.
First of all see this #AndrewThompson's wise advice:
Java GUIs might have to work on a number of platforms, on different
screen resolutions & using different PLAFs. As such they are not
conducive to exact placement of components. To organize the components
for a robust GUI, instead use layout managers, or combinations of
them, along with layout padding & borders for white space.
There are some helpful topics to understand what it means here:
Non resizable window border and positioning
Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Nested Layout Example
Providing white space in a Swing GUI
You'll see the use of methods like setLocation(), setBounds() or setSize() is highly discouraged. However I've seen this approach before applied to allow customizing forms. But instead of specific (x,y) coordinates and fixed (width,height) you can store constraints for GridBagLayout. Let's say you have a table like this:
I'd start first with a class to wrap data from the DB:
public class Data {
private String componentType, text;
private int column, row, width, height, weightX, weightY;
public Data(String componentType, int column, int row, int width, int height
,int weightX, int weightY, String text) {
this.componentType = componentType;
this.column = column;
this.row = row;
this.width = width;
this.height = height;
this.weightX = weightX;
this.weightY = weightY;
this.text = text;
}
// getters and setters here
}
As database calls are time consuming task you have to consider use a SwingWorker to do the database call (time consuming task) in a background thread and create/update your GUI in the Event Dispatch Thread.
Having said this you may have something like this:
public class Demo {
private JPanel content;
private JFrame frame;
private void createAndShowGUI() {
content = new JPanel(new GridBagLayout());
SwingWorker<Void, Data> worker = new SwingWorker<Void, Data>() {
#Override
protected Void doInBackground() {
try{
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db","root","password");
Statement stat = con.createStatement();
ResultSet rs = stat.executeQuery("select * from TableName");
while(rs.next()){
String componentType = rs.getString("component");
int column = rs.getInt("x");
int row = rs.getInt("y");
int width = rs.getInt("width");
int height = rs.getInt("height");
int weightx = rs.getInt("weightx");
int weighty = rs.getInt("weighty");
String text = rs.getString("text");
Data data = new Data(componentType, column, row, width, height
,weightx, weighty, text);
publish(data);
}
rs.close();
stat.close();
con.close();
} catch(Exception e) {
System.out.println(e);
}
return null;
}
#Override
protected void process(List<Data> chunks) {
for(Data data : chunks) {
JComponent component = null;
if(data.getComponentType().equalsIgnoreCase("JTextField")) {
component = new JTextField(data.getText());
}
if(data.getComponentType().equalsIgnoreCase("JComboBox")) {
component = new JComboBox();
}
if(data.getComponentType().equalsIgnoreCase("JLabel")) {
component = new JLabel(data.getText());
}
if(component != null) {
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = data.getColumn();
constraints.gridy = data.getRow();
constraints.gridwidth = data.getWidth();
constraints.gridheight = data.getHeight();
constraints.weightx = data.getWeightX();
constraints.weighty = data.getWeightY();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.BOTH;
constraints.insets = new Insets(8,8,8,8);
content.add(component, constraints);
}
}
}
#Override
protected void done() {
frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
};
worker.execute();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().createAndShowGUI();
}
});
}
}
And you'll see something like this:
Related
I have a JTable inside a JPanel which in turn is in a Jframe. The JTable loads users from a table (database in MySQL).
I have a search engine using DNIs in which key-to-key, with a KeyTyped event, is updating the contacts in the table, and show only those that meet the browser pattern (JTextField). If there are only 2-3-4 clients, the table is not resized to the customer size, but fills the rest of the table with a gray background. (see image). How could the lower bound of the table be reset?
Code:
public class Listado_clientes1 extends javax.swing.JFrame{
private TableRowSorter<DefaultTableModel> TRSFiltro;
public Listado_clientes1() {
this.getContentPane().setBackground(Color.orange);
panel_top.setBackground(Color.orange);
tabla_clientes.setPreferredScrollableViewportSize(
new Dimension(tabla_clientes.getPreferredSize().width, tabla_clientes.getRowHeight()*20)
);
try {
DefaultTableModel modelo = new DefaultTableModel();
tabla_clientes.setModel(modelo);
PreparedStatement ps = null;
ResultSet rs = null;
Connection con = Conexiones.conexion_a_BBDD("agenda");
String sql = "SELECT dni, nombre, apellidos, telefono, direccion, ciudad, email FROM clientes";
ps = con.prepareStatement(sql);
rs = ps.executeQuery();
ResultSetMetaData rsMd = (ResultSetMetaData) rs.getMetaData();
int cantidadColumnas = rsMd.getColumnCount();
modelo.addColumn("DNI");
modelo.addColumn("NOMBRE");
modelo.addColumn("APELLIDOS");
modelo.addColumn("TLFONO");
modelo.addColumn("DIRECCION");
modelo.addColumn("CIUDAD");
modelo.addColumn("EMAIL");
while (rs.next()) {
Object[] filas = new Object[cantidadColumnas];
for (int i=0; i<cantidadColumnas; i++) {
filas[i] = rs.getObject(i+1);
}
modelo.addRow(filas);
}
} catch (SQLException ex) {
System.err.println(ex.toString());
}
}
/**
* Filtrar: Buscar por DNI.
*/
public void filtrar_dni() {
int columna = 0; //Es la fila del DNI.
TRSFiltro.setRowFilter(RowFilter.regexFilter(textfield_buscar.getText(), columna));
}
private void textfield_buscarKeyTyped(java.awt.event.KeyEvent evt) {
Character letra = evt.getKeyChar();
evt.setKeyChar(Character.toUpperCase(letra));
textfield_buscar.addKeyListener(new KeyAdapter(){
public void keyReleased(final KeyEvent e){
String texto = (textfield_buscar.getText());
textfield_buscar.setText(texto);
filtrar_dni();
}
});
TRSFiltro = new TableRowSorter<DefaultTableModel>((DefaultTableModel) tabla_clientes.getModel());
tabla_clientes.setRowSorter(TRSFiltro);
}
}
Outline / Scheme:
Any time the number of rows in the view of the table changes you need to recalculate the
preferred scrollable viewport size of the table. Once this size is recalculated you need to invoke the layout manager of the panel containing the table:
A reusable method would be something like:
public void resetTablePreferredScrollableViewportSize(JTable table, int maxRows)
{
Dimension size = table.getPreferredSize();
int displayRows = Math.min(table.getRowCount(), maxRows);
size.height = displayRows * table.getRowHeight();
table.setPreferredScrollableViewportSize( size );
Container parent = SwingUtilities.getAncestorOfClass(JPanel.class, table);
parent.revalidate();
parent.repaint();
}
So after setting the row filter you could use:
tabla_clientes.setRowSorter(TRSFiltro);
resetTablePreferredScrollableViewportSize(tabla_clientes, 10);
Now the scroll pane should be sized to display up to 10 rows after which the scrollbar of the scroll pane will appear.
Edit:
I don't know how to put a reproducible example
Then you don't understand what your problem is.
Your question is that you want to:
Reset JTable (size) depending on number of rows
So the data is irrelevant, only the number of rows is relevant.
It is easy to test this because you can create a DefaultTableModel with a specified number of rows.
Below is a simple MRE. To test you:
enter a number in the text field
press enter
The table will be resized to display the specified number of rows. If the number is greater that 10 the table size will be fixed and a scrollbar will appear:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.table.*;
public class SSCCE extends JPanel
{
JTextField textField;
JTable table;
public SSCCE()
{
setBackground(Color.YELLOW);
textField = new JTextField(5);
add(textField);
table = new JTable(new DefaultTableModel(0, 4));
table.setPreferredScrollableViewportSize(table.getPreferredSize());
add( new JScrollPane(table) );
textField.addActionListener((e) ->
{
int rows = Integer.parseInt( textField.getText() );
table.setModel( new DefaultTableModel(rows, 4) );
resetTablePreferredScrollableViewportSize(table, 10);
});
}
public void resetTablePreferredScrollableViewportSize(JTable table, int maxRows)
{
Dimension size = table.getPreferredSize();
int displayRows = Math.min(table.getRowCount(), maxRows);
size.height = displayRows * table.getRowHeight();
table.setPreferredScrollableViewportSize( size );
Container parent = SwingUtilities.getAncestorOfClass(JPanel.class, table);
parent.revalidate();
parent.repaint();
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SSCCE());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// frame.pack();
frame.setSize(500, 250);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
I created a program to set an imageIcon in jtable fixed column, i created a jtable and getting a database records, then set a first column as fixed column. i set an image icon in fixed column. when i am compiling this program, i am getting only a path of the imageicon not getting an image. I fixed an imageIcon in project package folder.
This is the code i used
public void Frm_FlxD_Database() {
try{
TmpRow=0;
TmpMainPrj.PRJ_DB_CONNECTION_ASSGN();
TmpFlxMdl =(DefaultTableModel)FlxD.getModel();
TmpFlxDRow = 0;
TmpFlxSt=TmpGPrjVarDec.GContn.createStatement();
TmpFlxDRs=TmpFlxSt.executeQuery("SELECT * from activitymaster");
PRJ_FLX_DEFTL_ASSGN(FlxD, "BEGIN");
TmpFlxDRs.first();
do {
FlxD.setValueAt(TmpFlxDRs.getString("ACTVTYDESC"), TmpRow,1);
FlxD.setValueAt(TmpFlxDRs.getString("ACTVTYCODE"), TmpRow,2);
FlxD.setValueAt(TmpFlxDRs.getString("DISPSTATUS"), TmpRow,3);
FlxD.setValueAt(TmpFlxDRs.getString("ACTVTYID"), TmpRow,4);
TmpFlxMdl.addRow(new Object[]{""});
TmpRow = TmpRow + 1;
}while(TmpFlxDRs.next());
FRM_FLXD_PTR_DATA_ASSGN(TmpFlxDRow);
}
catch(Exception e){
System.out.println(e);
}
}
private void FRM_FLXD_PTR_DATA_ASSGN(int PFlxRow) {
TmpFlxDRow = PRJ_FLX_PTR_ASSGN(FlxD, PFlxRow, TmpFlxDRow);
}
private int PRJ_FLX_PTR_ASSGN(JTable PFlx, int PCurRow, int PPrvRow) {
ImageIcon TmpIcon;
System.out.println(PCurRow);
System.out.println(PPrvRow);
if (PCurRow != PPrvRow){
TmpIcon = new ImageIcon(getClass().getResource("Blank.gif"));
PFlx.setValueAt(TmpIcon,PPrvRow,0);
System.out.println(TmpIcon);
}
TmpIcon = new ImageIcon(getClass().getResource("Pointer.gif"));
PFlx.setValueAt(TmpIcon,PCurRow,0);
System.out.println(TmpIcon);
return(PCurRow);
}
JTable knows Icon/ImageIcon, simple example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.*;
import javax.swing.table.*;
public class TableIcon extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
private JTable table;
private JLabel myLabel = new JLabel("waiting");
private int pHeight = 40;
private boolean runProcess = true;
private int count = 0;
public TableIcon() {
ImageIcon errorIcon = (ImageIcon) UIManager.getIcon("OptionPane.errorIcon");
ImageIcon infoIcon = (ImageIcon) UIManager.getIcon("OptionPane.informationIcon");
ImageIcon warnIcon = (ImageIcon) UIManager.getIcon("OptionPane.warningIcon");
String[] columnNames = {"Picture", "Description"};
Object[][] data = {{errorIcon, "About"}, {infoIcon, "Add"}, {warnIcon, "Copy"},};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
table = new JTable(model) {
private static final long serialVersionUID = 1L;
// Returning the Class of each column will allow different
// renderers to be used based on Class
#Override
public Class getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
};
table.setRowHeight(pHeight);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane, BorderLayout.CENTER);
myLabel.setPreferredSize(new Dimension(200, pHeight));
myLabel.setHorizontalAlignment(SwingConstants.CENTER);
add(myLabel, BorderLayout.SOUTH);
EventQueue.invokeLater(new Runnable() {
public void run() {
}
});
new Thread(this).start();
}
public void run() {
while (runProcess) {
try {
Thread.sleep(750);
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ImageIcon myIcon = (ImageIcon) table.getModel().getValueAt(count, 0);
String lbl = "JTable Row at : " + count;
myLabel.setIcon(myIcon);
myLabel.setText(lbl);
count++;
if (count > 2) {
count = 0;
}
}
});
}
}
public static void main(String[] args) {
TableIcon frame = new TableIcon();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setLocation(150, 150);
frame.pack();
frame.setVisible(true);
}
}
You should not add an icon in your data model. You should add data (a boolean indicator, a String, whatever), and use a renderer for this column that will display the appropriate icon based on the data of the column.
See http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#editrender for information and examples about cell renderers.
And please, learn the Java naming conventions and stick to them. Your code is unreadable. See http://www.oracle.com/technetwork/java/codeconv-138413.html
Without getting to much into your code my guess is it has something to do with your tablemodel getColumnClass() method. There are plenty of tutorials how to fix that. Currently its probably rendered by tables defaultrenderer for object.
This thread should be helpful to you.
Good news is, you dont have to obfuscate your code, its already really hard to read and even harder to understand. You might want to read some java code guidelines to improve your code.
I'm trying to write a game applet that responds to keys typed by the player. I'm trying to use key bindings to accomplish this. But I can't get it to work. The applet (what little of it there is, at the moment) seems to display correctly in Appletviewer, but nothing happens when I press keys. I haven't been able to test it in a browser, as it doesn't always display correctly in a browser.
I'm running Sun Java 6 on Ubuntu. I managed to find mention of a Ubuntu bug where iBus would block keyboard input to applets. However, I don't have iBus running, and I've been able to use keyboard input with other applets (not written by me).
Here is the code so far
public class AlxTestVersion extends JApplet {
private GridBagLayout layout;
private GridBagConstraints layoutConstraints;
private JPanel mainPanel;
public void init() {
this.setLayout ( new FlowLayout(FlowLayout.LEFT) );
//Main frame.
mainPanel = new JPanel();
layout = new GridBagLayout();
layoutConstraints = new GridBagConstraints();
layoutConstraints.anchor = GridBagConstraints.NORTHWEST;
layoutConstraints.fill = GridBagConstraints.NONE;
mainPanel.setLayout(layout);
mainPanel.setBackground(Color.pink);
getContentPane().add(mainPanel);
//Map display
JPanel leftPanel = new JPanel();
GlobalData.mainMap = new MapCanvas(9);
addComponent(GlobalData.mainMap, 0, 0, 1, 1);
/*
Define other components...
*/
}
public class MapCanvas extends JPanel {
private int tileSize;
private int mapTileWidth;
private int mapOffset;
private int mapDim;
private MapSquare screenTiles[];
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon testImage = new ImageIcon("tiles/test_land.gif");
int x,y;
for (x=0;x<mapTileWidth;x++) {
for (y=0;y<mapTileWidth;y++) {
g.drawImage(testImage.getImage(), x*tileSize + mapOffset, y*tileSize + mapOffset, this);
}
}
}
public MapCanvas(int numTiles) {
//Set up window
tileSize = 48;
mapTileWidth = numTiles;
mapOffset = 4;
mapDim = (tileSize * mapTileWidth) + (2 * mapOffset);
Dimension dim = new Dimension(mapDim, mapDim);
this.setPreferredSize(dim);
this.setMinimumSize(dim);
this.setMaximumSize(dim);
this.setLayout( new GridLayout(numTiles, numTiles, 0, 0) );
this.setBackground(Color.black);
screenTiles = new MapSquare[numTiles^2];
//Map-related actions
InputMap im = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = this.getActionMap();
AbstractAction north = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Just for testing", "testing",
JOptionPane.PLAIN_MESSAGE);
}
};
am.put("North", north);
im.put(KeyStroke.getKeyStroke('2'), "North");
im.put(KeyStroke.getKeyStroke('i'), "North");
}
}
About the only difference I can find between what I've used and working examples found in various places is they add the keystroke to the inputmap before mapping the keystroke to the action. I tried switching the order, but it didn't seems to have any effect.
Can anyone see what I'm doing wrong here? I just know I'm missing something obvious.
Your code doesn't work for me either (on a mac), until I click inside the window. Adding the following as the last thing in init() seems to help (but is not totally reliable):
GlobalData.mainMap.requestFocus();
Your applet window probably does not have the focus when you press the keys.
Try adding this to your init():
GlobalData.mainMap.addFocusListener(new FocusDebugger("canvas"));
this.addFocusListener(new FocusDebugger("applet"));
Here's FocusDebugger:
public static class FocusDebugger implements FocusListener {
private final String name;
public FocusDebugger(String name) {
this.name = name;
}
public void focusGained(FocusEvent focusEvent) {
System.out.println(name + ".focusGained");
}
public void focusLost(FocusEvent focusEvent) {
System.out.println(name+".focusLost");
}
}
I have created one GUI in Swing Java in which I have used JTable.Now I want to display next page information into it by using pagination. How should I do that ?
Another option to implement this is to use a scrollbar-less scrollpane and a couple nav buttons to effect the control. The buttons that have been added are normal JButtons for the prototype.
A quick prototype is added below. It makes a couple assumptions, one of which is that the table model has all of its data. Work could be done to ensure that rows end up flush at the top of the view upon navigation.
private void buildFrame() {
frame = new JFrame("Demo");
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addStuffToFrame();
frame.setVisible(true);
}
private void addStuffToFrame() {
final JTable table = getTable();
final JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
final JButton next = new JButton("next");
final JButton prev = new JButton("prev");
ActionListener al = new ActionListener(){
public void actionPerformed(ActionEvent e) {
Rectangle rect = scrollPane.getVisibleRect();
JScrollBar bar = scrollPane.getVerticalScrollBar();
int blockIncr = scrollPane.getViewport().getViewRect().height;
if (e.getSource() == next) {
bar.setValue(bar.getValue() + blockIncr);
} else if (e.getSource() == prev) {
bar.setValue(bar.getValue() - blockIncr);
}
scrollPane.scrollRectToVisible(rect);
}
};
next.addActionListener(al);
prev.addActionListener(al);
JPanel panel = new JPanel(new BorderLayout());
JPanel buttonPanel = new JPanel();
buttonPanel.add(prev);
buttonPanel.add(next);
panel.add(buttonPanel, BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);
frame.getContentPane().add(panel);
}
private JTable getTable() {
String[] colNames = new String[]{
"col 0", "col 1", "col 2", "col 3"
};
String[][] data = new String[100][4];
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 4; j++) {
data[i][j] = "r:" + i + " c:" + j;
}
}
return new JTable(data,colNames);
}
alt text http://img7.imageshack.us/img7/4205/picture4qv.png
Paging in a Swing JTable looks like a nice article.
Here is an excerpt:
As far as I remember the solution for this problem lies in the concept of paging: just retrieve the data that the user wants to see and nothing more. This also means you have to sometimes get extra data from the db server (or appserver) if your user scrolls down the list.
Big was my surprise that there wasn't really an out-of-the-box solution (not even a copy- paste solution) for this problem. Anyone that knows one, please don't hesitate to extend my (rather limited) knowledge of the J2EE platform.
So we dug in, and tried to build a solution ourselves.
What we eventually came up with was an adapted TableModel class to takes care of the paging.
You can try with 2 query, first query is to count total rows in DB and second query is for the real data :) And for the UI side, you can try like this:
public class MainForm extends javax.swing.JFrame {
private void initDefaultValue() {
rowsPerPage = Integer.valueOf(cmbPageSize.getSelectedItem().toString());
totalRows = Main.getTablePagingService().countComments();
Double dblTotPage = Math.ceil(totalRows.doubleValue()/rowsPerPage.doubleValue());
totalPage = dblTotPage.intValue();
if (pageNumber == 1) {
btnFirst.setEnabled(false);
btnPrevious.setEnabled(false);
} else {
btnFirst.setEnabled(true);
btnPrevious.setEnabled(true);
}
if (pageNumber.equals(totalPage)) {
btnNext.setEnabled(false);
btnLast.setEnabled(false);
} else {
btnNext.setEnabled(true);
btnLast.setEnabled(true);
}
txtPageNumber.setText(String.valueOf(pageNumber));
lblPageOf.setText(" of " + totalPage + " ");
lblTotalRecord.setText("Total Record " + totalRows + " rows.");
List wPComments = Main.getTablePagingService().findAllComment(pageNumber, rowsPerPage);
jTable1.setModel(new CommentTableModel(wPComments));
autoResizeColumn(jTable1);
}
private void btnFirstActionPerformed(ActionEvent evt) {
pageNumber = 1; initDefaultValue();
}
private void btnPreviousActionPerformed(ActionEvent evt) {
if (pageNumber > 1) {
pageNumber -= 1; initDefaultValue();
}
}
private void btnNextActionPerformed(ActionEvent evt) {
if (pageNumber
And in service layer, you just need use limit function like this :
public List findAllComment(Integer pageNumber, Integer rowsPerPage) {
try {
List listWP = new ArrayList();
preparedFindAll.setInt(1, (rowsPerPage*(pageNumber-1)));
preparedFindAll.setInt(2, rowsPerPage);
ResultSet rs = preparedFindAll.executeQuery();
while (rs.next()) {
WPComment comment = new WPComment();
comment.setCommentID(rs.getInt("comment_ID"));
comment.setCommentAuthor(rs.getString("comment_author"));
comment.setCommentDate(rs.getDate("comment_date"));
comment.setCommentContent(rs.getString("comment_content"));
listWP.add(comment);
}
return listWP;
} catch (SQLException ex) {
Logger.getLogger(TablePagingServiceJDBC.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public Integer countComments() {
try {
Integer totalRows = 0;
ResultSet rs = preparedCount.executeQuery();
while (rs.next()) {
totalRows = rs.getInt("count(*)");
}
return totalRows;
} catch (SQLException ex) {
Logger.getLogger(TablePagingServiceJDBC.class.getName()).log(Level.SEVERE, null, ex);
}
return 0;
}
Or you can fork me on github at Project Page Table Paging on Swing :)
I have written a Java pagination tool dataj. It uses JQuery dataTables plug-in pagination metadata to build up the result page.
I have also added some client classes for Java Swing including a TableRowSorter that calls the (server side) sorting instead of sorting inside the table model.
Feel free to download it and contact me if you have any questions. It's under Apache 2 license.
Alternatively, you can make use of the QuickTable project.
Screenshot
Here is the DBTable component in action:
The DBTable component is embedded in a traditionnal JFrame.
Sample code
The following sample code produces the window shown in the previous screenshot:
import javax.swing.JFrame;
import javax.swing.UIManager;
import quick.dbtable.DBTable;
public class QuickTableFrame extends JFrame {
private static final long serialVersionUID = -631092023960707898L;
public QuickTableFrame() {
try {
// Use system look and feel
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// set Frame properties
setSize(300, 200);
setVisible(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// create a new quicktable
DBTable dBTable1 = new DBTable();
// add to frame
getContentPane().add(dBTable1);
// set the database driver to be used, we are using jdbc-odbc driver
dBTable1.setDatabaseDriver("org.h2.Driver");
/*
* set the jdbc url,"quicktabledemo" is the data source we have
* created for the database
*/
dBTable1.setJdbcUrl("jdbc:h2:mem:test;INIT=create table employee as select * from CSVREAD('test.csv');");
// set the select statement which should be used by the table
dBTable1.setSelectSql("select * from employee");
// to create the navigation bars for the table
dBTable1.createControlPanel();
// connect to database & create a connection
dBTable1.connectDatabase();
// fetch the data from database to fill the table
dBTable1.refresh();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// create a new table frame
QuickTableFrame myframe = new QuickTableFrame();
}
}
Resources and dependencies
test.csv
empid,emp_name,emp_dept,emp_salary
1,Azalia,ornare,114918
2,Jade,tristique,152878
3,Willa,In scelerisque scelerisque,166733
...
H2
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.187</version>
</dependency>
References
QuickTable basic tutorial
QuickTable official tutorials
Download latest jar
h2 database
I'm facing problems when fetching and processing a huge ResultSet from a database using JDBC (a few million rows), in this case MySQL's Connector/J. One of those problems is that even though I'm using a SwingWorker and taking measures not to perform any long processing on the Event Dispatch thread, the UI still freezes occasionally. This only happens with huge queries; the approach I'm using works for small ones.
Is there something that can be done to remedy this? Am I improperly handling such large ResultSet?
Note: I'm using the Employees sample database, more specifically the Salaries table.
Sample code
public class TestFrame extends javax.swing.JFrame {
static final String URL = "jdbc:mysql://localhost:3306/employees";
static final String USERNAME = "root";
static final String PASSWORD = "";
private Connection conn;
public TestFrame() {
initComponents();
initConnection();
}
public void initConnection() {
try {
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
System.out.println("Connected.");
} catch (SQLException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
jScrollPane1 = new javax.swing.JScrollPane();
uiTable = new javax.swing.JTable();
btnRun = new javax.swing.JButton();
txtQuery = new javax.swing.JTextField();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new java.awt.GridBagLayout());
jScrollPane1.setViewportView(uiTable);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.ipadx = 643;
gridBagConstraints.ipady = 374;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(6, 10, 0, 10);
getContentPane().add(jScrollPane1, gridBagConstraints);
btnRun.setText("Run");
btnRun.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnRunActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 625, 11, 10);
getContentPane().add(btnRun, gridBagConstraints);
txtQuery.setText("SELECT * FROM Salaries");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.ipadx = 660;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(11, 10, 0, 10);
getContentPane().add(txtQuery, gridBagConstraints);
pack();
}
private void btnRunActionPerformed(java.awt.event.ActionEvent evt) {
String query = txtQuery.getText();
SwingWorker<DefaultTableModel, Object> worker = new SwingWorker<DefaultTableModel, Object>() {
#Override
protected DefaultTableModel doInBackground() {
DefaultTableModel tableModel = null;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(query);
ResultSetMetaData rsmd = rs.getMetaData();
String[] columnNames = new String[rsmd.getColumnCount()];
for (int i = 1; i <= columnNames.length; i++) {
columnNames[i - 1] = rsmd.getColumnName(i);
}
tableModel = new DefaultTableModel(columnNames, 0);
while (rs.next()) {
Vector row = new Vector();
for (int i = 1; i <= columnNames.length; i++) {
row.add(rs.getString(i));
}
tableModel.addRow(row);
}
} catch (Exception e) {
e.printStackTrace();
}
return tableModel;
}
#Override
protected void done() {
try {
DefaultTableModel tableModel;
if ((tableModel = get()) != null) {
uiTable.setModel(tableModel);
}
} catch (InterruptedException | ExecutionException ex) {
}
}
};
worker.execute();
}
public static void main(String args[]) {
try {
javax.swing.UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
}
java.awt.EventQueue.invokeLater(() -> {
new TestFrame().setVisible(true);
});
}
private javax.swing.JButton btnRun;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField txtQuery;
private javax.swing.JTable uiTable;
}
It's also worth mentioning that the UI freezes even before I start to populate the table model.
The problem is that you shouldn't read the million records. Doesn't matter how you read the result set, if you read one million things it takes time... And if you put all the records in memory it takes a bunch of memory too.
The usual way to do that is reading some records, let's say ¿200?, presenting the 200 records and adding buttons to go forward and read another 200, and backward, to read the previous 200 records, so the user can navigate through the data without moving one million records between the server and yout client.
Just need to add some conditions in your query WHERE clausule and keep record of the last read record, so when you make a query you start the cursor from the last record:
select * from ***
where ****
AND ID > 'myStoredLastReadID'
Or something like that. Then, change the while ( rs.next()) for a
int ix = 0;
while ( rs.next() && (ix++) < 200 ) {
And add the buttons to your GUI. You will need something more, for example to go backward, but I think you can catch the idea.
If you really need to show one million records to your user... well, it will be slow whatever you do because you are going to move one million things from the server to your client, store one million things in memory, calculate which part of that million things the GUI must paint in the screen...
Use an AbstractTableModel on your own page caching, and just keep reading some pages when not in the cache. Keep the cache limited.
With COUNT(*). and LIMIT you can fetch the pages you are displaying, a kind of caching. This becomes even a bit harder with a faster paging technique, using index fields: say the ID. Then on a sorted table one could page by storing page border IDs.
You probably need to keep the connection open, reopen it on failure. Make a page cache.
Also create a Vector with an initial capacity of the correct size.
new Vector(columnNames.length);
This is an (quick and very dirty) example of how to call something slow from a button without freezing the main Swing thread.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TestUI extends JFrame implements ActionListener {
JButton but;
public TestUI() {
super( "Test...");
but = new JButton( "Test");
but.setActionCommand( "BUTTON");
but.addActionListener( this);
getContentPane().add( but, BorderLayout.CENTER);
pack();
setVisible( true);
}
public void actionPerformed( ActionEvent ev) {
if ( ev.getActionCommand().equals( "BUTTON") ) {
SlowClass sc = new SlowClass( TestUI.this);
sc.start();
/** THIS FREEEZES
try {
sc.join();
}
catch ( InterruptedException e) {
}
*/
}
else if ( ev.getActionCommand().equals( "QUERY_END") ) {
System.out.println( "END");
but.setText( "Query over");
}
}
public class SlowClass extends Thread {
ActionListener listener;
SlowClass( ActionListener listener) {
this.listener = listener;
}
public void run() {
try {
Thread.sleep( 10000);
listener.actionPerformed( new ActionEvent( this, 0, "QUERY_END"));
}
catch ( Exception ex) {
ex.printStackTrace();
}
}
}
public static void main( String args[]) throws Exception {
TestUI tui = new TestUI();
}
}
Create a new thread, start the thread from the event hadler and, using a listener, wait to be notified when the work is done. If you wait in the same invocation, in the commented lines, the main Swing thread freezes waiting for the end, so the listener is important.
This example starts a thread that doesn't consume CPU, so it works because nobady is fighting for the machine resources, you can resize the freame while the thread is working because Swing is free to work. But this is not your case, you are doing a lot of things, so we don't know if Swing hangs because there's something wrong in the code or because there isn't enough CPU and memory for Swing and the query.
Try something like this (cleaner, please, handling the listener correctly, not adding it in the constructor...) to see what happens. Or change the superquery for a sleep(20000) in your code to see if it hangs to get a hint of what happens.