i have a question regarding a program i am working on. It`s a database manager for a MqSQL db , written in Java. So i have the following program structure.
So i have a main class that extends JFrame, whichh is the main frame of the interface, like this (removed the unecessary code not relevant to the discussion) :
public class MainInterface extends JFrame {
public MainInterface {
................
MainInterface.setLayout(new FlowLayout());
MainInterface.setVisible(true);
TopToolbar toolbar;
try {
toolbar = new TopToolbar();
MainInterface.add(toolbar);
ResultsPanel Results = new ResultsPanel();
MainInterface.add(Results);
} catch (IOException e) {
e.printStackTrace();
}
}
TopToolbar and ResultsPanel are 2 other classes that extend JPanel, the TopToolbar class having a JToolBar with buttonsadded to it (Move Forward, MoveBack, Add entry)
public class TopToolbar extends JPanel implements ActionListener {
TopToolBar()
{
//constructor in which i was adding button to the toolbar, not relevat
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (MoveFirst.equals(cmd)){
try {
DatabaseAccess disp = new DatabaseAccess();
disp.getResults();
int id = disp.return_id();
System.out.println(id);
} catch (//exceptions) {
e1.printStackTrace();
}
}
That is the ActionListener event for the next button, which should trigger reading the next entry in the db
DatabaseAccess is another class with initializes the DB connection , and has these 2 methods :
public void getResults() throws SQLException {
Connection con = (Connection) DriverManager.getConnection(URL, user, "")
sql = (Statement) con.createStatement();
result_set = sql.executeQuery("select * from persons");
while (result_set.next()) {
id = result_set.getInt(1);
name = result_set.getString(2);
}
}
public int return_id(){
return id;
}
The return_ID method returns (and it does work) the ID (first key in the database, will obviously add methods for the rest of the entries in the db).
Now i want to show the ID in the final JPanel, the one called ResultsSet (just 3 JLabels and 3 TextFields for the ID , Name etc., in a GridLayout).
Since the dababase class creation (and subsequently the methods forr reading the db and returning the result) is done inside the ActionPerformed method inside the TopToolBar Jpanel, i can`t access it from the MainInterface JFrame and then use something like
ResultsPanel DispResults = new ResultsPanel();
add(DispResults);
DispResults.setID(id_Value)
where setID would be a method in the ResultsPanel that uses the JTextBox setText to set the text .
Hoope i`ve managed to explain my issue as clear as i can.
I disagree with several of your choices.
You should not extend JFrame. Create a JPanel and give it to a JFrame to display.
I would dissociate the database interactions from the Swing UI. Create them as interface-based POJOs on their own, without a UI. Get them tested, written, and working perfectly.
Once the database interactions are perfect, give an instance of that object to the class that controls the application. Give it a text-only interface for driving the database actions.
Once the controller is obeying every text command perfectly, using your fully tested database component, then have it create an instance of your JPanel and give it to a JFrame. It will make the same calls to its controller owner that your text UI has already executed successfully.
Computer science is all about decomposition. You solve big problems by breaking them into smaller ones.
If you'd like to see a great example of what your app might look like, download SQL Squirrel.
Related
I am programming an application that deals with orders from a database. It has several pages, a navigation, a header that always should show information about the actual order you are working with and a content area, in which the details of said order get shown:
My MainProgram extends a JFrame and contains a CardLayout, in which the other pages are hosted, so when the user clicks on the page in the navigation, only the view of the content-area changes. Logo, header and the navigation stay the same. The header keeps displaying the order number.
As there are several different pages that contain details about the same order, I need to "send / transfer" information about the order from one page to the other so I can show some information in the header and in the content area from the order object.
But I am not getting this to work as intended, mostly to my misunderstand of static and when to use it, where objects get created exactly and also the complexity of my program: I am using a class that is intended for the navigation and therefore should also handle
the information transfer from one page to the other.
Since I am using a database, creating a MVCE will be hard, so instead I will show the important parts of my program.
MainProgram.java
Here the navigation and the content panel (centerPanel) get created, also the CardLayout. centerPanel and the CardLayout are static, so I can call this from other classes and switch the page that is shown (probably not a good idea?):
NavigationPanel navigationPanel = new NavigationPanel();
public static JPanel centerPanel = new JPanel();
public static CardLayout contentCardsLayout = new CardLayout();
I create the pages and put them into my CardLayout:
OverviewPage overviewPage = new OverviewPage();
BasicDataPage basicDataPage = new BasicDataPage();
centerPanel.setLayout(contentCardsLayout);
overviewPage.setName("overviewPage");
basicDataPage.setName("basicDataPage");
centerPanel.add(overviewPage, "overviewPage");
centerPanel.add(basicDataPage, "basicDataPage");
The main method, where I create a MainProgram object:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
MainProgram window = new MainProgram();
window.setVisible(true);
window.initialize();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
OverviewPage.java
The overview page contains a JTable which gets populated from a database. If the user double-clicks an entry, he gets transfered to the BasicDataPage where he can see the details of the order.
But in order to show the details, I need to somehow transfer the information of the order object into the target class and thats the point I am struggling with!
// I tried several things like object into constructor, static object, creating a method etc...
if (mouseEvent.getClickCount() == 2 && row != -1) {
String workNumberOfOrderObject = (String) table.getValueAt(row, 0);
OrderObject orderObject = GetOrderObject.getOrderObjectFromDatabase(workNumberOfOrderObject);
BasicDataPage basicDataPage = new BasicDataPage();
basicDataPage.recieveOrderObject(orderObject);
workNumberPanel.recieveOrderObject(orderObject);
workNumberPanel.setTxtWorkNumber(workNumberOfOrderObject);
MainProgram.contentCardsLayout.show(MainProgram.centerPanel, "basicDataPage");
}
I tried "sending" the order object to the BasicDataPage via the constructor and set the text in the JTextFields in the BasicDataPage accordingly. This did not work, the textfields simply stayed empty altough I can System.out.println(orderObject.toString()) the recieved object.
BasicDataPage.java
I also tried creating a method receiveOrderObject that I use in the OverviewPage, which should set the textfields of the basicDataPage AND the workNumberPanel, but the fields stay empty:
WorkNumberPanel workNumberPanel = new WorkNumberPanel();
JTextField txtCarWidth = new JTextField(TEXTFIELD_LENGTH);
JTextField txtCarDepth = new JTextField(TEXTFIELD_LENGTH);
JTextField txtCarHeight = new JTextField(TEXTFIELD_LENGTH);
public void recieveOrderObject(OrderObject orderObject){
txtCarDepth.setText(orderObject.getCar_depth());
}
Before posting my question I've read several Q/As here on SO like this:
Accessing UUID from another class in Java ... suggesting to use static for global variables.
I know that static variables are class variables, that all instances can use and only one version exists of. So I tried to send a static object from one class to the other.
But since I am using JTextFields, I had to mix static and non-static content, which either did not work at all or the textfields disappeared.
I have the feeling that I am getting a very basic concept in java wrong, so any help, no matter in which direction, is appreciated!
EDIT:
Based on Reşit Dönüks answer, I was able to fill the textfields by making BasicDataPage and loadBasicData(orderObject) in MainProgram static. Now I can do MainProgram.loadBasicData(orderObject); ... and the textfields in the BasicDataPage get filled as intended.
Is this a valid approach or do I get problems for using static for GUI-Elements? ..... Don't!
I realized that, your are creating BasicDataPage in each double click.
if (mouseEvent.getClickCount() == 2 && row != -1) {
String workNumberOfOrderObject = (String) table.getValueAt(row, 0);
OrderObject orderObject = GetOrderObject.getOrderObjectFromDatabase(workNumberOfOrderObject);
BasicDataPage basicDataPage = new BasicDataPage();
This is the main problem. Do not create BasicDataPage there, just reach the created instance and set the order object to that. My solution is below.
public class MainProgram implements OrderView{
//remove statics here
private JPanel centerPanel = new JPanel();
private CardLayout contentCardsLayout = new CardLayout();
private BasicDataPage basicPage;
public MainProgram() {
//other codes
OverviewPage overviewPage = new OverviewPage();
basicPage = new BasicDataPage();
centerPanel.setLayout(contentCardsLayout);
overviewPage.setName("overviewPage");
basicDataPage.setName("basicDataPage");
centerPanel.add(overviewPage, "overviewPage");
centerPanel.add(basicPage, "basicDataPage");
//oher codes
}
#Override
public void loadOrder(OrderObject order) {
basicPage.recieveOrderObject(orderObject);
contentCardsLayout.show(centerPanel, "basicDataPage");
}
}
public interface OrderView {
public void loadOrder(OrderObject order);
}
public class OverviewPage {
OrderView orderView;
public OverviewPage(OrderView orderView) {
this.orderView = orderView;
}
//in ActionPerformed
if (mouseEvent.getClickCount() == 2 && row != -1) {
String workNumberOfOrderObject = (String) table.getValueAt(row, 0);
OrderObject orderObject = GetOrderObject.getOrderObjectFromDatabase(workNumberOfOrderObject);
orderView.loadOrder(orderObject);
workNumberPanel.recieveOrderObject(orderObject);
workNumberPanel.setTxtWorkNumber(workNumberOfOrderObject);
}
}
As pointed already, Singleton is the way to go. I would just like to point out a mistake in the code provided in the answer before.
private static MainFrameinstance = null;
Rename MainFrameinstance to instance or vice-versa; because the same variable is checked by the getInstance() method.
This program return 0, need to stop/pause program, press button and then return ID.
static int ID=0;
static String log="";
static String pass="";
static SessionFactory factory;
public static int enterStudent(JPanel panel){
panel.setLayout(null);
JLabel jLabel=new JLabel("Login");
panel.add(jLabel);
jLabel.setBounds(10,10,100,30);
JLabel jLabel1=new JLabel("Password");
panel.add(jLabel1);
jLabel1.setBounds(110,10,100,30);
final JTextArea textArea=new JTextArea();
textArea.setBounds(10,50,100,50);
panel.add(textArea);
final JTextArea textArea2=new JTextArea();
textArea2.setBounds(110,50,100,50);
panel.add(textArea2);
JButton enterButton=new JButton("Enter");
enterButton.setBounds(10,100,200,50);
panel.add(enterButton);
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ActionListener
enterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e2) {
log=textArea.getText();
pass=textArea2.getText();
Session session = factory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
List students = session.createQuery("FROM Student").list();
for (Iterator iterator =
students.iterator(); iterator.hasNext();){
Student student = (Student) iterator.next();
if((student.getLogin().equals(log))&&(student.getPassword().equals(pass))){
ID=student.getId();//this should be returned
JOptionPane.showMessageDialog(null,"return="+ID);
break;
}
}
tx.commit();
}catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
});
return ID; //returns 0
}
This is function for LogIn, check login and pass in DATABASE. Need return ID of student, but program returns 0
You can't return anything from an ActionListener since its method, actionPerformed(...) is defined to return void, nothing. But fortunately you don't need to. This is a Swing GUI, and inside of your ActionListener, display the ID within a JLabel by calling myLabel.setText(...) or display it elsewhere where it needs to be displayed. Or if you don't want to display it, then call some class's setId(...) method.
Note that the true canonical answer to this question would be to use an M-V-C or Model-View-Control type design pattern, and in your ActionListener (part of the control), set the model's id field to the value found in the database. The GUI should have listeners attached to the model that would update the GUI's state (the data it displays) if the model's state changes. But for your simple program this would probably be a bit of over-kill.
Other side recommendations:
Your code shows a significant over-use of static modifier, and doing this can make it hard to extend or enhance your program, or get it to easily work with other classes.
Your code does not follow Java naming rules, and you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Edit: I now see that you're trying to return an int from the enterStudent method via the JButton's ActionListener. This won't work as written because you're returning the id field before the ActionListener has completed. Solutions include use a callback method of some sort, one that is perhaps passed into your enterStudent's method parameter, or else if you use a modal dialog such as is available with a JOptionPane. Otherwise, based on this increase in complexity, then yes, you're best off using an M-V-C structure which itself is based on use of listeners (callbacks).
That means program control is not reaching this block hence your ID is getting the default defined value which is 0.
if((student.getLogin().equals(log))&&(student.getPassword().equals(pass))){
ID=student.getId();//this should be returned
JOptionPane.showMessageDialog(null,"return="+ID);
break;
}
here the if condition is not getting true which means either of getLogin() and getPassword is not having values as "".
I have a method in class A and I want class B (Main GUI) to call that method, but class A needs to perform some action on a jTable within class B.
I do not want this method to be within class B as it needs a connection to the db and I don't want my GUI to hold any such methods.
Here is the code within class A:
public void populatejTable(TableModel x) {
try {
String stmt = "SELECT * FROM APP.DATAVAULT";
PreparedStatement ps = Main.getPreparedStatement(stmt);
ResultSet rs = ps.executeQuery();
x.setModel(DbUtils.resultSetToTableModel(rs));
ps.close();
rs.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
I need to pass a parameter of type jTable to set it's model. I've tried all different common data types such as String, int etc... and even tried jTable and TableModel.
I'm guessing you're not really supposed to do this, but I cannot see a better way around this?
Here is the code calling this method within class B (my GUI):
//table
Account acc = new Account();
acc.populatejTable(datavaultjTable);
How should I go about doing this?
I am using Netbeans 7.3 Beta 2 GUI builder to create the table and the GUI - writing in Java on OS X.
The code in class A doesn't compile. There is no setModel() method in TableModel. Your goal of not having database-related code in a GUI class is a good one, but if it leads to GUI code in database-related code, the result is even worse.
The GUI class should contain GUI code only. The data access code should contain database-related code only. The GUI should call methods on the database access code to get data. It should not pass JTable instances or even TableModel instances to the data access code:
public class GUI {
private JTable table;
private MyTableModel tableModel;
private DataAccess dataAccess;
...
public void fillTableWithDataFromDatabase() {
List<Product> products = dataAccess.getProductsFromDatabase();
tableModel.setProducts(products);
}
}
public class DataAccess {
public List<Product> getProductsFromDatabase() {
// TODO:
// create an empty list
// execute a query
// loop through each row
// for each row, create a Product instance and add it to a list
// return the list
}
}
I'm having a difficults to add rows to a table that located in different class.
Following are the classes structure:
The dashed arrow is the desired relation that I dont manage to have
in the AddPanel class I have a fileds and Addbutton.
when clicking the addButton I first creating an instance of Product (class located in Logic Package). Then I need to add row to the table (Using the TableModel.AddRow method).
Following are the GUI Looks (the focused tab is AddPannel):
I tried different approches but non of them were successed.
My last attempt was to create in the Table class the following method:
public void AddRow(Product p) {
tbmStock.addRow(new Object[] { p.getExternalId(), p.getName(),
p.getAmount(), p.getPriceForMe(), p.getPriceForCustomer() });
}
In addition, in the AddPanel class I tried to add the following method:
private void AddButtonAction() {
btnAddProduct.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
Product p = new Product(txtName.getText(), txtExternalID
.getText(), Integer.parseInt(txtAmount.getText()),
Double.parseDouble(txtPriceForMe.getText()),
Double.parseDouble(txtPriceForCustomer.getText()),
Integer.parseInt(txtFromYear.getText()), Integer
.parseInt(txtToYear.getText()), Integer
.parseInt(txtSupplier.getText()), Integer
.parseInt(txtCarType.getText()));
AddRow(p); //This call doesn't compiles
}
catch (Exception e1){
JOptionPane.showMessageDialog(null,"Error");
}
}
});
}
Any suggestions ? (actually I'm not sure even that my structure is good :S )
Provide a custom event and event listener. If you create a new product fire the event and notify all listeners. The MainPanel (assuming that's where you create the instance of AddPanel), should register a listener. In this listener you then can update the table as the MainPanel has access to the table.
This is also known as the Mediator pattern.
Some pointers:
Create an event class, e.g. ProductCreatedEvent extends EventObject. Pass the Product as an argument to the constructor and make it accessible with a getter.
Create an event listener class: interface ProductCreatedEventListener extends EventListener. Provide a method such as productCreated(ProductCreatedEvent productCreatedEvent).
In the AddPanel add something like:
private final List<ProductCreatedEventListener> productCreatedEventListeners = new ArrayList<>();
...
public void addProductCreatedEventListener(ProductCreatedEventListener productCreatedEventListener){
productCreatedEventListeners.add(productCreatedEventListener);
}
public void removeProductCreatedEventListener(ProductCreatedEventListener productCreatedEventListener){
productCreatedEventListeners.remove(productCreatedEventListener);
}
private void fireProductCreatedEvent(ProductCreatedEvent event){
for (ProductCreatedEventListener productCreatedEventListener : productCreatedEventListeners){
productCreatedEventListener.productCreated(event);
}
}
Replace:
AddRow(p); //This isn't working
with
fireProductCreatedEvent(new ProductCreatedEvent(AddPanel.this, p));
I Have 3 sets of JMenuItem on a View class of the MVC framework. I want to refer to them in the controll class EventController. Can someone show me this is achieved? Below is the event Controller. Class EventView consists of JMenuItem addEvent, editEvent, deleteEvent, how do I do the listeners for them in the Controller class. Can someone demonstrate using a sample code on top of my Controller class?
public class EventController implements ActionLister {
private EventModel model;
private EventView view;
private ActionListener actionListener;
public EventController(EventModel model, EventView view){
this.model = model;
this.view = view;
}
Second question, I can only update a JTable from the View class itself, so
public void updateEventTable() {
try {
String sql = "SELECT date as 'Date',eventName as 'Name', time as 'Time' FROM Event";
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
tableEvent.setModel(DbUtils.resultSetToTableModel(rs));
tableEvent.getColumnModel().getColumn(0).setPreferredWidth(80);
tableEvent.getColumnModel().getColumn(1).setPreferredWidth(170);
tableEvent.getColumnModel().getColumn(2).setPreferredWidth(110);
}
catch (Exception e ) {
JOptionPane.showMessageDialog(null, e);
} finally {
try {
rs.close(); pst.close();conn.close();;
} catch(SQLException e){}
}
}
This is bad practice having codes in View Class, Should ideally be in Model, but how to do this can you show me. The concepts is new to me and want to learn. Because of JTable I find it very difficult
Let your model export instances of Action that can be added to menus and buttons as needed. Because database access is inherently asynchronous, let each such action use a worker thread to query the database in the background while updating the table mode on the EDT. See also A Swing Architecture Overview concerning the relationship between Swing components and models.
Addendum: Can you show me a code sample?
FileMenu is a very basic example of using Action to encapsulate functionality.
The example cited here uses Action more extensively in a JToolBar.
JHotDraw, cited here, is a very complex example that changes the available Action instances based on context, as discussed here.
This example offers a general examination of MVC in Swing.