Why we extends AbstractTabelModel when set values into JTabel? - java

Im developing a app for ordeing system and i have to set data into JTabels.
And this code is successfully working.I wanted to know what the importance of and whats happen in this class?
Why we need to import AbstractTabelModel.class?
OrderTabelModel Class:-
public class OrderTableModel extends AbstractTableModel{
protected static final String[] COLUMN_NAMES={"Item","Qty","Amount"};
private List<Order> rows;
public OrderTableModel(List<Order> rows){
this.rows = new ArrayList<>(rows);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return COLUMN_NAMES.length;
}
#Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value = null;
Order row = rows.get(rowIndex);
switch (columnIndex) {
case 0:
value = row.getItem();
break;
case 1:
value = row.getQty();
break;
case 2:
value = row.getAmount();
break;
}
return value;
}
}
this is other class
private void tblOrderListMouseClicked(java.awt.event.MouseEvent evt) {
int raw = tblOrderList.getSelectedRow();
Order or;
String item;
Double qty,amount,total;
ArrayList<Order> arrOrder = new ArrayList<Order>();
String selectedRaw = tblOrderList.getModel().getValueAt(raw, 0).toString();
System.out.println("order id : "+selectedRaw);
String sql = "select item,qty,amount from orderdetails where orderid='"+selectedRaw+"'";
con = new DBconnector().connect();
try {
Statement ps =con.createStatement();
ResultSet rs2 = ps.executeQuery(sql);
while(rs2.next()){
or = new Order();
or.setItem(rs2.getString("item"));
System.out.println("Item :" +rs2.getString("item"));
or.setQty(rs2.getDouble("qty"));
or.setAmount(rs2.getDouble("amount"));
arrOrder.add(or);
}
rs2.close();
ps.close();
OrderTableModel tblModel = new OrderTableModel(arrOrder);
tblOrderItems.setModel(tblModel);
} catch (Exception e) {
e.printStackTrace();
}
}
Can some one explain me the process of this please?

It is not always mandatory to extend the AbstractTableModel. You can simply extend the DefaultTableModel and only override the getValueAt() method if you have to.
But most of the time for simple usages it is not even needed to override the getValueAt() method either.
By using the DefaultTableModel, you have a limitation for the converting you data (imported from DB) to an object[][] or Vector types which may be a little boring.
But you asked what is the importance of using AbstractTabelModel?
In this case I can say when JTable is started to being rendered, it needs to determine the number of rows and number of the columns and also it needs to know which data should be renedered in each cell and so on. Based on this, JTable ask for this Information from the underlying TableModel. So it is needed for your TableModel(any child or implementation of TableModel) to have those methods which are used by JTable to retrieve the needed information.
Hope this would be helpful.
Good Luck.

Related

Converting List<ClassName> to array object to set as JComboBoxModel

I am designing classes based on DAO Pattern.
I have 3 classes and 1 GUI Form.
public interface SchoolYearDao {
List<SchoolYear> getAllSchoolYearInfo();
List<SchoolYear> getAllSchoolYearStart();
List<SchoolYear> getAllSchoolYearEnd();
List<SchoolYear> getSchoolYearById(int aSchoolYearId);
int getSchoolYearId(SchoolYear schoolyear);
boolean addSchoolYear(SchoolYear schoolyear);
}
public class SchoolYear {
//setters and getters
}
public class SchoolYearDaoImpl implements SchoolYearDao{
#Override
public List<SchoolYear> getAllSchoolYearStart() {
List<SchoolYear> listOfSchoolYearStart = new ArrayList<>();
SchoolYear mySchoolYear = new SchoolYear();
String SQL = "{CALL getAllSchoolYearInfo()}";
try(Connection con = DBUtil.getConnection(DBType.MYSQL);
CallableStatement cs = con.prepareCall(SQL);) {
try(ResultSet rs = cs.executeQuery();){
while(rs.next()){
mySchoolYear.setStart(rs.getInt("yearFrom"));
}
listOfSchoolYearStart.add(mySchoolYear);
}
} catch (SQLException e) {
JOptionPane.showMessageDialog(null,e.getMessage());
}
System.out.println(listOfSchoolYearStart);
return listOfSchoolYearStart;
}
}
The problem is with the GUI.
public class SchoolYearGUI extends javax.swing.JPanel {
public SchoolYearGUI() {
initComponents();
schoolYearStartJcbx.setModel(new DefaultComboBoxModel(schoolyear.getAllSchoolYearInfo().toArray());
schoolYearEndJcbx.setModel(new DefaultComboBoxModel(schoolyear.getAllSchoolYearEnd().toArray()));
}
}
I can't get the years to show correctly. I get this.
Instead of the actual integer numbers 2015,2016,2017 and so on...
I research online and found similar problems but most of them were not using a list of class as List<SchoolYear>. In this case, "SchoolYear" is the name of class.
I used toArray(); and tried Arrays.toString(array); but can't get it right.
I thought I'd change the return type to DefaultComboBoxModel of getAllSchoolYearStart() method but I realized I have to keep my List<SchoolYear> as return type in case I need to use the result set as model for JTables etc..
So, I want to just stick with List<SchoolYear> as return type. (If it's a good idea?)
What is the best way to get the actual value?
Thanks in advance.
=============== Solution ==============================
Thanks to MadProgrammer for the advice and to other answerers.
So I studied the listcellrenderer overnight and finally got the basic idea of how to use it.
public MainFrame() {
initComponents();
SchoolYearDaoImpl sy = new SchoolYearDaoImpl();
DefaultComboBoxModel model = new DefaultComboBoxModel(sy.getAllSchoolYearStart().toArray());
jcmbSchoolYearStart.setModel(model);
jcmbSchoolYearStart.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value instanceof SchoolYear){
SchoolYear schoolyear = (SchoolYear) value;
setText(""+schoolyear.getStart());
}
return this;
}
} );
}
I overridden the getListCellRendererComponent and created an if-statement to test if value is an instance of my class which is "SchoolYear" Then I cast whatever the raw value is to SchoolYear then used the getter of SchoolYear model, getStart() to get the value stored in the list.
I'm now moving the renderer to an external class file in my project.
Exactly as #MadProgrammer said, in new DefaultComboBoxModel(schoolyear.getAllSchoolYearInfo().toArray(), you put in an array of Objects, and the constructor of a JComboBox will try to use the toString() method to convert every instance of SchoolYear to present it as plain text. If you don't overwrite the default toString() method to present it as you like, you will see what you see in the combobox you have now: the class's name with some numbers.
You can implement the toString() method, but it's not the best way. You can construct some utility method, along with getSchoolYearId() you have, to get the ID of every object in the List and fill an array with the IDs.
private int[] getIDAndFillAnArray(List<SchoolYear> syrs) {
int[] ids = new int[syrs.size()];
for (int i=0; i<syrs.size(); i++) {
ids[i] = syrs.get(i).getSchoolYearId();
}
return ids;
}
And just use it like:
schoolYearStartJcbx.setModel(new DefaultComboBoxModel(getIDAndFillAnArray(schoolyear.getAllSchoolYearInfo()));
That's simple enough.
Use this class:
public class ComboItem {
private String value;
private String label;
public ComboItem(String value, String label) {
this.value = value;
this.label = label;
}
public String getValue() {
return this.value;
}
public String getLabel() {
return this.label;
}
#Override
public String toString() {
return label;
}
}
Put this method where you connect to your DB:
public ComboItem[] getListOfSchoolYearStart(params..)
{
List<ComboItem> result = new ArrayList<ComboItem>();
ComboItem[] items;
.....
rs = stmt.executeQuery(query);
while (rs.next()) {
ComboItem item = new ComboItem(rs.getInt("id_school") + "", rs.getString("description"));
result.add(item);
}
items = result.toArray(new ComboItem[result.size()]);
return items;
}
Add your JComboBox:
private ComboItem[] listOfSchoolYearStart;
private int selectedIdSchool=-1;
....
listOfSchoolYearStart= getListOfSchoolYearStart();
JComboBox comboList = new JComboBox(listOfSchoolYearStart);
//If you want to keep previous selection
if (listOfSchoolYearStart.length > 0)
{
boolean isFound=false;
for (ComboItem comb : listOfSchoolYearStart) {
if(Integer.parseInt(comb.getValue())==selectedIdSchool)
{
comboList.setSelectedItem(comb);
isFound=true;
break;
}
}
if(!isFound)
{
comboList.setSelectedIndex(0);
selectedIdSchool=Integer.parseInt(listOfSchoolYearStart[0].getValue());
}
}
This works for me at least, I hope it helps.

How to make columns editable in special abstract model JTable

Hey guys i doing my assignment and now i have the problem with non editable cells, actually it became editable, but the result of editing didn't set at arraylist, I tried many solution from internet, but it doesn't work.
So my work like registration system which get information about guest, and then stored it into csv file. In additional function the program must let display, update, delete and searching function.
I finished all, without update,delete and searching. Can you please looking my code and help me or give the advice, link or something useful.
this is my abstract model:
public class ddispmodel extends AbstractTableModel {
private final String[] columnNames = { "FirstName", "SecondName", "Date of
birth", "Gender", "Email", "Address", "Number", "Attending","ID" };
private ArrayList<String[]> Data = new ArrayList<String[]>();
private boolean editable;
public void AddCSVData(ArrayList<String[]> DataIn) {
this.Data = DataIn;
this.fireTableDataChanged();
}
#Override
public int getColumnCount() {
return columnNames.length;// length;
}
#Override
public int getRowCount() {
return Data.size();
}
#Override
public String getColumnName(int col) {
return columnNames[col];
}
#Override
public Object getValueAt(int row, int col) {
return Data.get(row)[col];
}
public boolean isCellEditable(int row, int col) {
setValueAt(Data, row, col);
this.fireTableCellUpdated(row, col);
return true;
}
}
This is part of my main class
It is action Listener of menu item witch activate displaying function
(I didn't copy all class, because it nearly 1000 lines, but if it necessary, i can submit all code )
dlog.addActionListener(new ActionListener (){
public void actionPerformed(ActionEvent e){
CSVFileDomestic Rd = new CSVFileDomestic();
ddispmodel ddispm = new ddispmodel();
ddisp.setModel(ddispm);
File DataFile = new File("D:\\cdne4\\WorkPlace\\Domestic.csv");
ArrayList<String[]> Rs2 = Rd.ReadCSVfile(DataFile);
ddispm.AddCSVData(Rs2);
System.out.println("Rows: " + ddispm.getRowCount());
System.out.println("Cols: " + ddispm.getColumnCount());
cl.show(cp, "dispDomPanel");
}
});
and File class which convert date from csv to arraylist
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
public class CSVFileDomestic {
private final ArrayList<String[]> Rs = new ArrayList<String[]>();
private String[] OneRow;
public ArrayList<String[]> ReadCSVfile(File DataFile) {
try {
BufferedReader brd = new BufferedReader(new
FileReader(DataFile));
while (brd.ready()) {
String st = brd.readLine();
OneRow = st.split(",");
Rs.add(OneRow);
System.out.println(Arrays.toString(OneRow));
}
}
catch (Exception e) {
String errmsg = e.getMessage();
System.out.println("File not found:" + errmsg);
}
return Rs;
I am new at Java and this is my first program , please can you explain more easily
but the result of editing didn't set at arraylist,
You need to override the setValueAt(...) method of the TableModel to save the data.
It would be something like:
String[] row = data.get(row);
row[column] = value;
this.fireTableCellUpdated(row, col);
Also, the isCellEditable(...) method should NOT do any processing. It simply returns true/false for the given column. If you want all columns editable then it should just be:
public boolean isCellEditable(int row, int col) {
//setValueAt(Data, row, col);
//this.fireTableCellUpdated(row, col);
return true;
}
I finish this)) and go to do others options.
camickr thank you for help, you give me the way, which bring me to answer))
I spend nearly 6 hours for trying to do it and finally I got it.
If someone interesting, answer is
public boolean isCellEditable(int row, int col) {
return true;
}
#Override
public void setValueAt(Object aValue, int row, int col){
Data.get(row)[col]= (String) aValue;
fireTableCellUpdated(row,col);
so I just make new method setValueAT which said me camickr.
than I went to Oracle site and read about it, and after try it to do, but
it doesn't, because aValue can't be converted from object to String, and finally I initiate aValue as a String, so now it works.However it is not all, it only change arraylist. Needs new method to convert arraylist to csv file. I am working about it now, maybe after I will show it. Sorry for my theory, I am new at java, just learn how it works))

Making an addRows() method for a Custom JTable TableModel

My explanation below rambles, boiling down, is there a way I can add a Row without firing off an event, such that I can add multiple rows and fire an event to update all of them at once? Without having to add code to contain the table data in the custom model?
I have a custom TableModel which extends from DefaultTableModel so that I can use DefaultTableModel to keep track of data for me, whilst still having some custom methods of my own.
The issue is, I was thinking it might be faster for me to have an "addRows(String[][] val)" method, when I wish to add multiple rows. I could then fire a single event, probably fireTableDataChanged() to update the rows all at once. For example, my current method:
JTable table1 = new JTable(new dgvTableModel(new String[] {<values>},0, new String[] {<values>}));
table1.addRow(new String[] {<values here>});
table1.addRow(new String[] {<values here>});
table1.addRow(new String[] {<values here>});
I would then repeat the above as many times as necessary. The issue is, each of those will fire off a seperate event. It would be much faster (I think), if I could do this using my custom table model:
JTable table1 = new JTable(new dgvTableModel(new String[] {<values>},0, new String[] {<values>}));
table1.addRows(new String[][] {{<values1 here}, {values2 here}, . . .}});
and then in the table model:
public void addRows(String[][] values) {
for (String[] vals : values)
super.addRow(vals);
}
fireTableDataChanged();
}
I can code this in easily. The issue is again, that "super.addRow(vals);" line will fire an event each time through. Is there a way, without adding code to have my model contain the table data itself, to prevent that event being fired each time I add a row? Such that it waits for the fireTableDataChanged() call in the addRows method?
For reference, the code for my custom table model:
import java.awt.Color;
import java.util.ArrayList;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
public class dgvTableModel extends DefaultTableModel {
//private DataTable tableVals = new DataTable();
private ArrayList<Color> rowColors;
//private ArrayList<Object[]> data = new ArrayList<>();
//default constructor has no data to begin with.
private int[] editableColumnNames;
public dgvTableModel(String[] colNames, int rowCount)
{
super(colNames, rowCount);
}
public dgvTableModel(String[] colNames, int rowCount, String[] editableColNames)
{
super(colNames, rowCount);
//this.tableVals.setColNames(colNames);
if (editableColNames!=null && editableColNames.length >0)
{
editableColumnNames = new int[editableColNames.length];
int count = 0;
for (int i =0; i< editableColNames.length;i++)
{
for (String val : colNames)
{
if (val.equalsIgnoreCase(editableColNames[i]))
{
editableColumnNames[count] = i;
count+=1;
break;
}
}
}
}
}
public dgvTableModel(String[] colNames, int rowCount, String[] editableColNames, boolean colorChanges)
{
super(colNames, rowCount);
Color defColor = UIManager.getDefaults().getColor("Table.background");
if (editableColNames!=null && editableColNames.length >0)
{
editableColumnNames = new int[editableColNames.length];
int count = 0;
if (colorChanges)
{
rowColors = new ArrayList<>();
}
for (int i =0; i< colNames.length;i++)
{
if (colorChanges)
{
rowColors.add(defColor);
}
for (String val : editableColNames)
{
if (val.equalsIgnoreCase(colNames[i]))
{
editableColumnNames[count] = i;
count+=1;
break;
}
}
}
}
else if (colorChanges)
{
rowColors = new ArrayList<>();
for (String val : colNames)
{
rowColors.add(defColor);
}
}
}
#Override
public boolean isCellEditable(int row, int column)
{
if(editableColumnNames!=null && editableColumnNames.length >0)
{
for (int colID : editableColumnNames)
{
if (column==colID)
return true;
}
}
return false;
}
public void setRowColor(int row, Color c)
{
rowColors.set(row, c);
fireTableRowsUpdated(row,row);
}
public Color getRowColor(int row)
{
return rowColors.get(row);
}
#Override
public Class getColumnClass(int column)
{
return String.class;
}
#Override
public String getValueAt(int row, int column)
{
return super.getValueAt(row, column).toString();
}
}
Surely firing one event to display every row, is faster than firing one event for each row?
'AbstractTableModel.fireTableDataChanged()' is used to indicate to the model (and the JTable UI which is notified by the model) that all possible data in the table may have changed and needs to be checked. This can (with emphasis on can) be an expensive operation. If you know which rows have been added, just use the 'AbstractTableModel.fireTableRowsInserted(int firstRow, int lastRow)' method instead. This will ensure only the effect rows are seen as changed. Take a look at all the fire* methods in AbstractTableModel. You can really exercise fine grained control over which rows, cells, etc are seen as dirty.
Then again what your doing might be premature optimalization. Unless you have fiftythousand records in your JTable this is probably not going to be noticable. But if you have a massive amount of records in your JTable you might be beter of lazy loading them anyway.

Implementing ArrayLists to Table models

Hi I have an arraylist of a class I created called Pets which has the variables below
private String name;
private String species;
private int age;
I wanted to display this arraylist into a jTable and I did that succesfully by using defaultTableModel and calling setModel().
However I needed to add a sorting and filtering function for the Jtable. I took a look at the java tutorials were they were creating a subclass of AbstractTableModel in order to sort and filter. However they were using arrays to store the data. So I tried modifying the code to use an arraylist isntead but Im stuck with this method
public Object getValueAt(int row, int col) {
return data[row][col];
}
How do I get all the values from one object from th arraylist?
Any help will be greatly appreciated. Thanks in advance.
Does your ArrayList hold a row that is it's own type of object? If so, and if your ArrayList is a generic ArrayList<RowItem> then you could do something like:
#Override
public Object getValueAt(int row, int col) {
if (row > getRowCount()) {
// throw an exception
}
RowItem rowItem = rowItemList.get(row);
switch (col) {
case 0:
return rowItem.getName();
case 1:
return rowItem.getLastSpecies();
case 2:
return rowItem.getAge();
}
return null; // or throw an exception
}
You can try this:
public Object getValueAt(int row, int col) {
switch(col) {
case 0:
return ((Pets)data.get(row)).getName();
case 1:
return ((Pets)data.get(row)).getSpecies();
case 2:
return ((Pets)data.get(row)).getAge();
}
return null;
}

Java JTable binding via JPA

I have tried to search for proper answers, but nothing helped me so far. I am quite new to java GUI programming, actually, to java itself.. I have however managers to understand JPA, how to retrieve, insert and delete using JPA.
Now I want the data in my database to be shown in a JTable.
I currently have the following mySQL table(which i want to show in a JTable
games
Id PK int
Title
Publisher
Genre
ReleaseDate
As far as coding concerns, I have succesfully retrieved the data contained in the table using the following:
public List<Game> getGames(){
List<Game> games;
try{
TypedQuery<Game> selectGamesQuery = entityManager.createQuery("SELECT g FROM Game g", Game.class);
games = selectGamesQuery.getResultList();
} catch(Exception e) {
System.out.println(e);
}
return games;
}
This succesfully returns a list of games whom I can iterate trough.
Then, in my view I have the following
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
tblGames = new JTable(new tblGamesModel());
tblGames.setShowVerticalLines(true);
tblGames.setShowHorizontalLines(true);
tblGames.setFillsViewportHeight(true);
scrollPane.setViewportView(tblGames);
Which ofcourse leads us to the table model,which is where I'm stuck.
public class tblGamesModel extends AbstractTableModel {
private GameRepository gameRepository;
private List<Game> games;
/**
*
*/
public tblGamesModel(){
gameRepository = new GameRepository();
games = gameRepository.getGames();
}
private static final long serialVersionUID = 1L;
#Override
public int getColumnCount() {
// TODO Auto-generated method stub
return 0;
}
#Override
public int getRowCount() {
// TODO Auto-generated method stub
return games.size();
}
#Override
public Object getValueAt(int arg0, int arg1) {
// TODO Auto-generated method stub
return null;
}
}
I know this is alot of code for a simple post, but I really don't know how else to show the current problem. Any good links would help, or advise on its own.
Thanks for taking the time to read the code and possibly help me out.
Remember, I am just a student programming, so I have a lot to learn about conventions etc aswell. So pointers are also welcome, as I am eager to learn from more experienced developers.
The simplest option is something like this:
#Override
public int getColumnCount() {
return 5;
}
...
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Game game = games.get(rowIndex);
switch (columnIndex) {
case 0:
return game.getId();
case 1:
return game.getTitle();
case 2:
return game.getPublisher();
case 3:
return game.getGenre();
case 4:
return game.getReleaseDate();
}
return null;
}
That can be prone to maintenance problems due to all the magic numbers - a solution would be to use an enumeration for the columns:
enum GameTableColumn {
ID, TITLE, PUBLISHER, GENRE, RELEASE_DATE;
}
And then get the enumeration instance for a column using GameTableColumn.values()[columnIndex].
A few style notes - tblGamesModel is a non-standard name for a Java class, class names always start with an upper case letter. A more Java name would be GamesTableModel. Hungarian notation prefixes (such as "tbl") are generally discouraged.
Also having a database fetch in a constructor is generally a bad idea. In a Swing application you want all fetches to be explicit so you can ensure they do not block the UI. Rather than getGames() I would suggest retrieveGames(). It may be best to construct the GamesRepository outside the table model and pass it in to the constructor. You could then perform the JPA query first in a different thread. This would prevent the UI thread from freezing while the database access is in progress.
Pass loaded list to the model via constructor parameter or setter method. Then you can use following model structure:
public class TblGamesModel extends AbstractTableModel {
private static final String[] COLUMNS = {"id", "title",
...........
private static final int COL_ID = 0;
private static final int COL_TITLE = 1;
private List<Game> list; //list that is injected via constructor or setter method
public int getRowCount() {
return list.size();
}
public int getColumnCount() {
return COLUMNS.length;
}
public Object getValueAt(int rowIndex, int columnIndex) {
Game game = list.get(rowIndex);
switch (columnIndex) {
case COL_ID:
return game.getId();
........
}
}
public String getColumnName(int column) {
return COLUMNS[column];
}

Categories