Creating A Custom Java Desktop Database Application
Creating A Custom Java Desktop Database Application
Application
Note: JSR-296 (Swing Application Framework) is no longer developed and will not become part of the
official Java Development Kit as was originally planned. You can still use the Swing Application Framework
library as it is, but no further development is expected. If you are looking for a Swing-based application
framework, consider using the NetBeans Platform, which is a full-featured platform suitable for creating
complex and scalable desktop applications. The Platform contains APIs that simplify the handling of
windows, actions, files, and many other typical application elements. Refer to the NetBeans Platform CRUD
Application Tutorial, which describes a similar scenario.
This tutorial guides you through the creation of a complete desktop database application that enables its
user to browse and edit customer records and purchase history. The resulting application includes the
following main features:
A main view that enables users to browse customer records and customer purchases.
Separate dialog boxes for entering new records or modifying existing records.
Introduction
See Also
To complete this tutorial, you need the following software and resources.
Software or Resource
Version Required
NetBeans IDE
version 6
Introduction
This application takes advantage of the following technologies:
The Java Persistence API (JPA), which helps you interact with a database using Java code.
Beans Binding, which enables you to keep Swing component properties synchronized.
The Swing Application Framework, which simplifies basic application functions such as persisting session
information, handling actions, and managing resources.
The tutorial makes use of IDE wizards and other code generation features to provide much of the boilerplate
code. It also shows you how to customize the generated code and hand code other parts of the application.
This tutorial takes approximately 2 hours to complete. For a shorter tutorial that shows the creation of a less
customized user interface, see Building a Java Desktop Database Application.
Below is a screenshot of the working application that you will have when you complete the tutorial.
In the Services window, right-click the MySQL Server node and choose Start.
2.
3.
4.
5.
If the Connect dialog box appears, type the password that you have set for the database server.
6.
If the Advanced tab of the dialog box opens, click OK to close the dialog box.
-->
7.
Scroll down to the node for connection that you have just created. The node should have
the
icon.
8.
9.
Copy the contents of the MyBusinessRecords SQL script and paste them into the SQL Command 1
tab of the Source Editor.
The SQL script specifies the InnoDB storage engine in order to handle the foreign keys in this database.
MySQL's default storage engine, MyISAM, will not work with this tutorial.
The data is split among several tables to reduce duplication and the possibility for inconsistencies. Some
tables are connected to each other through foreign keys.
All of the tables use MySQL's AUTO_INCREMENT attribute so that there is a unique identifier for each row in
those tables. This identifier is created by the database management software, so your application and/or
your application's user do not have to create this identifier. (So that the AUTO_INCREMENT is used correctly
within the application, the IDE adds
the@GeneratedValue(strategy=GenerationType.IDENTITY annotation for that column in the table's
entity class. This ensures that the application does not try to submit a value for that column when you
create a new record.)
The foreign key in the ORDERS table is there to link each order record with a customer. In the application's
user interface, ORDER records are only displayed for the selected CUSTOMER.
The ON CASCADE DELETE attribute for the foreign key to the CUSTOMERS class ensures that a customer's
orders are also deleted when a customer is deleted.
The foreign key in the CUSTOMERS table points to a COUNTRIES table. You will use this relationship in the
application to enable the user to select a customer's country from a combo box.
The ORDERS table has a foreign key to the PRODUCTS table. When adding a new order record, the user will
be able to choose a product from a combo box.
The COUNTRIES and PRODUCTS tables are pre-populated with data so that you can choose from those
tables when the user of the application is adding customer and order records.
Though this tutorial does not cover it, you might find it useful to create separate applications to populate the
COUNTRIES and PRODUCTS tables. Such applications could be created with the Java Desktop Application
project template and would not require additional hand-coding.
A main application frame that contains tables for customer details and customer orders.
A main application class that handles basic application life-cycle functions, including persisting of window
state between sessions and resource injection.
2.
Select the Java category and the Java Desktop Application template. Then click Next.
3.
In the Name and Location page of the wizard, follow these steps:
1.
2.
3.
Click Next.
4.
Move the ID entry from the Columns to Include column to Available Columns.
Click Next.
5.
Click the Table radio button to create a JTable for the ORDERS table.
Move the ID entry from the Columns to Include column to Available Columns.
6.
The Customers and Orders entity classes, which represent the data from the CUSTOMERS AND ORDERS
database tables.
The main form with two JTable components that provide a master/detail view of the CUSTOMERS and
ORDERS database tables. The view also contains buttons which are connected to actions for creating,
deleting, and saving records.
The master table is bound to a list of Customers objects. That list of objects represents all of the rows of the
CUSTOMERS database table.
The detail table is bound to a collection of Orders objects. That collection represents all of the rows of the
ORDERS database table that are linked with the currently selected customer in the master table.
At this point you can choose Run > Run Main Project to see the main application window. However, the
application does not yet function properly, because the database has some attributes for which the wizard
did not generate necessary code. You will add this code in the next section of the tutorial.
When you run the application, the application's frame has the title "Database Application Example". For a
simple JFrame, you would normally change the title by modifying the title property in the property sheet
for the component. However, the frame of this application uses the Swing Application
Framework's FrameView class, and the title is a general application property. You can modify such
application properties in the Project Properties window.
To change the title of the main frame of the application:
1.
In the Projects window, select the project's node and choose Properties.
2.
3.
4.
If desired, modify the other properties as well, such as Description, and Splash Screen.
Customize the Customers entity class to refer to the Countries entity class.
Customize the Orders entity class to refer to the Products entity class.
Update the binding code for the master and detail tables in the main form so that the Countries and
Products entity classes are used.
Create entity classes for the Countries and Products tables by right-clicking
the customerrecordspackage and choosing New > Entity Classes from Database.
2.
3.
4.
Click Next.
5.
6.
7.
2.
3.
@Column(name = "COUNTRY_ID")
private Integer countryId;
5.
6.
In the setCountryId() method, change the types of countryId and oldCountryId from
Integer to Countries.
To establish the relation between the Orders and Products entity classes:
1.
2.
3.
4.
@Basic(optional = false)
@Column(name = "PRODUCT_ID")
private int productId;
with this code:
6.
7.
8.
In the setProductId() method, change the types of the productId parameter and
the oldProductId variable from int to Products.
2.
In the Design view of the class, right-click the top table and choose Table Contents.
3.
4.
5.
6.
Change the Title from Country Id to Country . This affects the column heading in the running
application.
7.
In the Design view of the CustomerRecordsView class, right-click the bottom table and choose Table
Contents.
2.
3.
4.
Change the Expression to ${productId.brand}. After you do so, the type should also change to
String.
5.
6.
7.
Select the row that has just been added to the table.
8.
9.
For Expression, select productId > prodType from the drop-down list.
10. Click the Insert button again and select the newly added row.
11. For Title, type Model.
12. For Expression, select productId > model from the drop-down list.
13. Click the Insert button again and select the newly added row.
14. For Title, type Price.
15. For Expression, select productId > price from the drop-down list.
16. Click Close to apply the changes.
At this point, the application is partially functional. You can run the application and add, edit, delete, and
save records. However, you can not yet properly modify the fields that are based on the Countries and
Products entity classes. In addition, you have some work to make the Order Date field behave in a more
user-friendly way.
You could make some adjustments to the Country and Model columns to use combo boxes that are bound to
their respective tables. That would enable the user to select those fields without having to hand enter them.
Instead, you will use dialog boxes as data entry mechanisms for these tables to make it harder for the user
to accidentally delete data while browsing it.
Create dialog boxes to edit data for each of the tables on the main form.
Create intermediary beans to carry the data between the dialogs and form.
Unpack the zip of file of utility classes and unzip its contents on your system.
2.
On your system, copy all of the files from the zip file and paste them into the folder that contains
the project's customerrecords folder.
3.
If your classes are in a different package than customerreccords, adjust the package statement
in each of the files you have just added to the project.
2.
3.
In the Set Action Dialog box, change the Text property to New Customer.
4.
5.
Click OK.
6.
7.
In the Set Action Dialog box, change the Text property to Enter Order.
8.
Change the Tool Tip field to Create a new customer order record.
9.
Click OK.
Right-click the package that contains your classes and choose New > Other. Select Swing GUI
Forms > JDialog Form template and name it CustomerEditor.
2.
From the Palette window drag, drop, and arrange components for the customer's personal details.
Add labels for each of the following fields: first name, last name, address, city, state, zip code,
country, and phone number.
Add text fields for all of the above fields, except for Country.
For Country, add a combo box.
3.
4.
5.
(Optional) Rename all of the components you have added to more memorable names, such
as firstNameLabel. You can do this inline in the Inspector window.
6.
7.
The resulting layout should look something like what you see below.
Note: For a detailed guide to using the GUI Editor's layout features, see Designing a Swing GUI in NetBeans
IDE.
At the top of the design area of the CustomerEditor form, click the Source tab. Click somewhere
within the class, such as in the line below the constructor.
2.
Press Alt-Insert (or right-click and choose Insert Code) and choose Add Property.
3.
In the Add Property dialog, name the property currentRecord, give it the type Customers,
select Generate Getter and Setter, and select Generate Property Change Support.
4.
You now need to customize the generated setCurrentRecord method to fire a property change
notification.
2.
3.
4.
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
5.
CustomerEditor ce = new CustomerEditor(mainFrame, false);
6.
ce.setCurrentRecord(c);
7.
ce.setVisible(true);
You can now proceed with the binding of the dialog box's fields. You will bind the text property of each text
field to the corresponding property of the Customers object represented bycurrentRecord. Similarly, you
will bind the combo box's selectedItem property to the countryId property of currentRecord. You
will bind the combo box's elements property to a list of Countries entities.
To bind the text fields to properties of the currentRecord bean:
1.
2.
3.
4.
5.
In the Bind dialog box, select Form as the Binding Source. (Note that Form is at the very bottom of
the drop-down list.)
6.
In the Binding Expression drop-down list, expand the currentRecord node and select the property
corresponding to the text field that you are binding.
7.
8.
2.
Click Import Data to Form, select the database connection, and select the Countries
table. countriesList should appear as the binding source. Click OK.
3.
Right-click the combo box again and choose Bind > selectedItem.
4.
Select Form as the binding source and currentRecord > countryId as the expression. Click OK.
The combo box is almost ready to work properly in the dialog. It is set up to draw its values from the
COUNTRIES database table, and the item that the user selects is then applied to the country field in the
current record. However, you still need to customize the rendering of the combo box, since the values bound
to the combo box are Countries objects, not simple names. You will do that by specifying a custom cell
renderer. (For JTables and JLists, the beans binding library enables you to specify display expressions, thus
avoiding the need to create a custom renderer. However, that feature does not exist yet for combo boxes.)
To get the combo boxes to render country names, do the following:
1.
In the Project's window, right-click CountryListCellRenderer and choose Compile File. Compiling the
file enables you to treat it as a bean that you can add to the form by dragging and dropping from
within the IDE's GUI builder.
2.
Select the CustomerEditor form in the Source Editor and select the Design view.
3.
Drag the class from the Projects window to the white space surrounding the form, as shown in the
screenshot below.
Doing so adds the renderer to your form as a bean, much like dragging a component from the
Palette adds that component to your form.
4.
5.
6.
Scroll to the renderer property and choose countryListCellRenderer1 from the drop-down
list for that property.
Now you should be able to run the application, press the first New button, and enter data in the dialog. You
should also be able to select a country from the Country combo box. The Save and Cancel buttons on the
dialog do not do anything yet, but you can save the records from the main frame. You will code those
buttons later.
3.
A label to display the date format that needs to be entered. Give this label the text (MMM DD,
YYYY - e.g. Apr 17, 2008). If you plan to use a different date format, add text that
corresponds to the format that you plan to use.
4.
5.
Now that the Order Editor has its visual design, you need to do the following things:
Create an intermediary bean to carry the record values back to the main form.
Connecting the Order Editor Dialog Box With the Main Form
As you did for the CustomerEditor dialog box, you will have to create an intermediary bean property of type
Orders to hold the record. When the user presses Enter Order, the property will be given the value of the
currently selected order record.
To create the bean property:
1.
At the top of the design area of the OrderEditor form, click the Source tab.
2.
Click somewhere within the class, such as in the line below the constructor.
3.
Press Alt-Insert (or right-click and choose Insert Code) and choose Add Property.
4.
In the Add Property dialog, name the property currentOrderRecord, give it the type Orders,
select Generate Getter and Setter, and select Generate Property Change Support.
5.
6.
Now you need to add code to open the Order Editor dialog box when the user clicks the Enter Order button.
In addition, you need code to clear the currentOrderRecord property.
To connect the dialog with the Enter Order button:
1.
2.
3.
4.
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
5.
OrderEditor oe = new OrderEditor(mainFrame, false);
6.
oe.setCurrentOrderRecord(o);
7.
oe.setVisible(true);
2.
3.
4.
Select the value property and click the ellipsis (...) button that is next to the property.
5.
In the Bind dialog box, select Form as the binding source, and select currentOrderRecord >
orderDate as the binding expression.
6.
Select the combo box for the product and click the ellipsis (...) button for the elements property.
7.
8.
Select the MyBusinessRecords database connection and select the products table.
When you import this data to the form, code for a List object of Products is generated. This object
(listProducts) is then set as the binding source.
9.
10. Click the ellipsis (...) button for the combo box's selectedItem property.
11. Select Form as the binding source, and select currentOrderRecord > productId as the binding
expression.
12. Select the Quantity text field.
13. Click the ellipsis (...) button for the text property.
14. Select Form as the binding source, and select currentOrderRecord > quantity as the binding
expression.
15. Select the price's formatted field.
16. Click the ellipsis (...) button for the value property.
17. Select Form as the binding source, and select ${currentOrderRecord.productId.price} as
the binding expression.
18. Select the total's formatted field.
19. Click the ellipsis (...) button for the value property.
20. Select Form as the binding source, and
type ${currentOrderRecord.productId.price*currentOrderRecord.quantity} as the
binding expression.
This custom binding expression enables you to generate the total price for the order by multiplying
the price of the selected item times the quantity selected by the user.
For the order date, price, and total, you have added formatted text fields, which make it easy to provide
formatting.
To set the date and currency formatting for those fields:
1.
2.
3.
4.
In the formatterFactory property editor, select date in the Category column. Then
select Default in the Format column.
5.
6.
7.
In the Properties window, clear the editable property. (You do not want users to edit this field.
The value of this field will be derived from the price property of the selected item in the product
combo box.)
8.
In the formatterFactory property editor, select currency in the Category column. Then
select Default in the Format column.
9.
2.
3.
Drag the class from the Projects window to the white space surrounding the form in the same way
that you did for the CountryListCellRenderer.
4.
5.
In the Properties window, scroll to the renderer property and choose productListCellRenderer1
from the drop-down list for that property.
2.
3.
4.
o.setOrderDate(new java.util.Date());
o.setQuantity(1);
Now when you run the application, the current date should appear in the Date field when you open the
dialog box and the default quantity should be 1.
2.
3.
4.
5.
6.
7.
You will set this property's value in event handling code for the buttons.
To create the event listeners and handlers:
1.
2.
3.
4.
5.
In the Handlers for actionPerformed dialog box, add a handler called saveCustomer.
6.
Within the saveCustomer method in the Source Editor (where the cursor jumps after you create
the new handler), type the following code:
7. setCustomerConfirmed(true);
8. setVisible(false);
9.
Repeat steps 2-5 for the Cancel button and call its handler cancelCustomer.
11. setCustomerConfirmed(false);
12. setVisible(false);
13.
In the CustomerRecordsView class, navigate to the newRecord() method and add the following code to the
bottom of the method:
if (ce.isCustomerConfirmed()) {
save().run();
} else {
refresh().run();
}
Since the save() and refresh() actions act on any changes made during the application's session, you
should make the dialog modal and make the tables in the main form uneditable. Another reason to make the
dialog modal is so that when the user presses either the Save or Cancel button, the setVisible() method
does not return until the event handler (which includes the setCustomerConfirmed method) has run.
To make the dialog modal:
1.
2.
3.
In the Properties window, click Properties and select the checkbox for the modal property.
2.
3.
4.
5.
Click Close.
You should now be able to create new records and save them from the Customer Editor. You should also be
able to create a new record and cancel from the Customer Editor.
In the RefreshTask inner class, Thread.sleep is called four times to slow down the rollback code to better
demonstrate how Swing Application Framework tasks work. You do not need this code for this application,
so delete those four statements. Similarly, you do not need a try/catch block here, so delete
the try and catch statements as well (but leave the rest of the body of the try block).
2.
3.
4.
5.
6.
7.
You will set this property's value in event handling code for the buttons.
To create event handling code for the buttons:
1.
2.
3.
4.
5.
In the Handlers for actionPerformed dialog box, add a handler called saveOrder.
6.
Within the saveOrder method in the Source Editor (where the cursor jumps after you create the
new handler), type the following code:
7. setOrderConfirmed(true);
8. setVisible(false);
9.
Repeat steps 2-5 for the Cancel button and call its handler cancelOrder.
11. setOrderConfirmed(false);
12. setVisible(false);
13.
In the CustomerRecordsView class, navigate to the newDetailRecord() method and add the following
code to the bottom of the method:
if (oe.isOrderConfirmed()) {
save().run();
} else {
refresh().run();
}
To make the dialog modal:
1.
2.
3.
In the Properties window, click Properties and select the checkbox for the modal property.
Open the CustomerRecordsView class in the Source Editor and select the Design view.
2.
3.
4.
5.
Click Close.
2.
Delete the deleteRecord() method and replace it with the following code:
3.
4.
5.
@Action(enabledProperty = "recordSelected")
public void deleteRecord() {
int n = JOptionPane.showConfirmDialog(null, "Delete the records
permanently?", "Warning",
6.
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null);
7.
if (n == JOptionPane.YES_OPTION) {
8.
int[] selected = masterTable.getSelectedRows();
9.
List<customerrecords.Customers> toRemove = new
ArrayList<customerrecords.Customers>(selected.length);
10.
for (int idx = 0; idx < selected.length; idx++) {
11.
customerrecords.Customers c =
list.get(masterTable.convertRowIndexToModel(selected[idx]));
12.
toRemove.add(c);
13.
entityManager.remove(c);
14.
}
15.
list.removeAll(toRemove);
16.
save().run();
17.
18.
19.
} else {
refresh().run();
}
}
22.
23.
24.
25.
@Action(enabledProperty = "detailRecordSelected")
public void deleteDetailRecord() {
Object[] options = {"OK", "Cancel"};
int n = JOptionPane.showConfirmDialog(null, "Delete the records
permanently?", "Warning",
26.
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null);
27.
if (n == JOptionPane.YES_OPTION) {
28.
int index = masterTable.getSelectedRow();
29.
customerrecords.Customers c =
list.get(masterTable.convertRowIndexToModel(index));
30.
List<customerrecords.Orders> os = c.getOrdersList();
31.
int[] selected = detailTable.getSelectedRows();
32.
List<customerrecords.Orders> toRemove = new
ArrayList<customerrecords.Orders>(selected.length);
33.
for (int idx = 0; idx < selected.length; idx++) {
34.
selected[idx] =
detailTable.convertRowIndexToModel(selected[idx]);
35.
int count = 0;
36.
Iterator<customerrecords.Orders> iter = os.iterator();
37.
while (count++ < selected[idx]) {
38.
iter.next();
39.
}
40.
customerrecords.Orders o = iter.next();
41.
toRemove.add(o);
42.
entityManager.remove(o);
43.
}
44.
os.removeAll(toRemove);
45.
masterTable.clearSelection();
46.
masterTable.setRowSelectionInterval(index, index);
47.
list.removeAll(toRemove);
48.
save().run();
49.
} else {
50.
refresh().run();
51.
}
}
You can now run the application and click New Customers to add a new record. When you press Save in the
New Customers dialog, the record is saved. When you press Cancel, the new record you have changed is
rolled back.
However, you can no longer edit existing records, because disabled editing of the tables in the main form.
To solve this, you will add Edit buttons to the main customer form so that you can edit existing records. For
event-handling, you will take advantage of the Swing Application Framework's Action facility.
To add the button and its corresponding event-handling code, do the following:
1.
2.
3.
Drag a button from the palette into the opening just created.
4.
5.
6.
7.
8.
Click the Advanced Tab and select recordSelected for the Enabled Property.
This generates an annotation attribute to ensure that the button and any other trigger for the action
(e.g. a menu item) are only enabled when a record is selected.
9.
11.
12.
setSaveNeeded(true);
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
13.
CustomerEditor ce = new CustomerEditor(mainFrame, false);
14.
ce.setCurrentRecord(list.get(masterTable.convertRowIndexToModel(masterTable
.getSelectedRow())));
15.
ce.setVisible(true);
16.
if (ce.isCustomerConfirmed()) {
17.
save().run();
18.
} else {
19.
refresh().run();
20.
}
Most of that code is copied straight from the newRecord action. The key difference is the
linece.setCurrentRecord(list.get(masterTable.convertRowIndexToModel(masterTable.get
SelectedRow())));, which populates the current record in the dialog with the currently selected record.
The Customer part of the application is almost completely set. You should be able to freely add, edit, and
delete records from your CUSTOMERS table using the specialized GUI you have created.
To add the Edit Orders button and its corresponding event-handling code, do the following:
1.
In the Design view of the CustomerRecordsView class, delete the Refresh and Save buttons.
2.
3.
Drag a button from the palette into the opening just created.
4.
5.
6.
7.
8.
Click the Advanced Tab and select detailRecordSelected for the Enabled Property.
9.
11.
12.
13.
setSaveNeeded(true);
int index = masterTable.getSelectedRow();
customerrecords.Customers c =
list.get(masterTable.convertRowIndexToModel(index));
14.
List<customerrecords.Orders> os = c.getOrdersList();
15.
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
16.
OrderEditor oe = new OrderEditor(mainFrame, false);
17.
oe.setCurrentOrderRecord(os.get(detailTable.getSelectedRow()));
18.
oe.setVisible(true);
19.
if (oe.isOrderConfirmed()) {
20.
save().run();
21.
} else {
22.
refresh().run();
23.
}
Now when you run the application, all of the key elements are in place. You can create, retrieve, update,
and delete records for customers and orders. In the screenshot below, you can see the Order Editor dialog
as it appears after having selected a record and pressed the Edit Order button.
The section below shows some other things that you can do to enhance and fine tune the application.
You have handled the formatting of the dates and currencies within the Orders Editor, but you have not yet
done so for the CustomerRecordsView class. You do not need to do anything for the date field. The format is
effectively passed between the Order Editor and the main form. However, that is not the case for the Price
field. You will need to add a currency renderer class to render that field correctly.
To render the Price field with currency formatting in the main view:
1. In the Projects window, right-click the CurrencyCellRenderer class and choose Compile File.
2.
3.
Drag the CurrencyCellRenderer class from the Projects window and drop it in white area
surrounding the form.
A node called currencyCellRenderer1 should appear in the Inspector window.
4.
Right-click the lower table in the form and choose Table Contents.
5.
6.
7.
Now when you run the application, the price should appear with a dollar sign ($), a decimal point, and two
digits after the decimal point.
master table a text field for the search string. For this binding you will need a binding converter so that the
table knows how to respond to the search string.
First of all, add a label and a text field for the search field as shown below.
Drag the class from the Projects window and drop it in the white area surrounding the form.
3.
You will use this converter when you create the binding.
To create the binding:
1.
In the main form, right-click the Search text field and choose Bind > text.
2.
In the Bind dialog, select masterTable as the binding source and rowSorter as the expression.
3.
4.
5.
Now when you run the application, you should be able to type in the Search Filter field and see that the list
of rows is reduced to only rows that contain text matching what you have typed.
However, adding this search feature creates a side effect. If you use the search and then click New
Customer, an exception appears because the code to select the new row is determined according to number
of records in the database table, not according to the number of records currently displayed in the table.
In the Projects window, right-click the DateVerifier class and choose Compile File.
2.
3.
Drag the DateVerifier class from the Projects window to the white space that surrounds the form.
4.
5.
In the Properties window, click the Properties tab, and select the dateVerifier1 from the combo
box for the Input Verifier property. This property appears within the Other Properties section of the
tab.
Software or Resource
NetBeans IDE
Version Required
version 6.9 or higher
2.
Select the Java category and select the Java Application template. Click Next.
3.
In the Name and Location page of the wizard, perform the following operations:
Leave the Use Dedicated Folder for Storing Libraries checkbox unselected. (If you are using
NetBeans IDE 6.0, this option is not available.)
4.
5.
In the Projects window, right-click the NumberSlider project node and choose New > JFrame Form.
(If JFrame Form is not available in the New menu, choose Other. Then in the New File wizard, select
the Swing GUI Forms category and select the JFrame Form template.)
6.
In the Name and Location page of the wizard, perform the following operations:
7.
From the Swing Controls section of the Palette, drag a slider component into the design area. (If the
Palette window is not open, choose Window > Palette.)
9.
From the Palette, drag a text field component to the design area.
The resulting form might look something like the screenshot below. However, positioning is not
important for purposes of this example.
Right-click the text field component and choose Bind > text to open the Bind dialog box.
2.
3.
From the Binding Expression combo box, select value int as shown in the image below.
4.
Click OK.
You have just bound the value bean property of the slider to the text value of the text field.
In the design area, the text field should show the value 50. This value reflects the fact that the slider is in
the middle position and the default range of values for the slider is from 0 to 100.
You can now run the application and see the binding in action.
To run the project:
1.
2.
In the Run Project dialog box, click OK to accept numberslider.NumberSliderForm as the main
class.
The applications should start in a separate window. Adjust the slider in the running application and watch
the value change in the text field.
Add the bean to the Palette so that you can add it to a form just as you would use add a standard Swing
component.
Add the bean class to your project and compile the bean.
To add a bean to the Palette window:
1.
2.
3.
If you want to create a new palette category for the bean, click New Category and enter the desired
name before you add the bean.
4.
Click Add from JAR, Add from Library, or Add from Project and complete the wizard to add the bean.
In the Project's window, right-click the node for the bean and choose Compile File.
2.
The example in the first section of this tutorial shows a straightforward binding with some default behaviors.
But sometimes you might want or need to configure your binding differently. If that is the case, you can use
the Advanced tab of the Binding dialog box.
The Advanced tab of the dialog box contains the following fields:
Name. Enables you to create a name for the binding, which gives you more flexibility for managing your
bindings. The name is added to the constructor of the binding and can be referenced with the
binding's getName() method.
Update Mode. Specifies the way that the properties are kept synchronized. The possible values are:
Always sync (read/write). Whenever a change is made to either the source or the target, the other is
updated.
Only read from source (read only). The target is only updated the first time the source value is set.
Changes that are made to the source are updated in the target. Changes made to the target are not
updated in the source.
Read from source once (read once). The target is only updated when the target and source are initially
bound.
Update Source When (available only to the text property of JTextField and JTextArea components).
Enables you to select the frequency with which the properties are synchronized.
Ignore Adjusting (available to the value property of JSlider; to the selectedElement property of
JTable and JList; and to the selectedElements property of JTable and JList). If this checkbox is selected,
any changes made to one property are not propagated to the other property until the user is finished
making the change. For example, when the application's user drags a slider, the value of the property to
which the slider's value property is bound is only updated once the user releases the mouse button.
Converter. If your binding involves properties with different data types, you can specify code that converts
values between the types. The beans binding library handles many commonly needed conversions, but you
might need to provide your own converters for other combinations of property types. Such converters need
to extend theorg.jdesktop.beansbinding.Converter class.
The Converter drop-down list is populated with any converters that have been added as beans to your form.
You can also add the conversion code directly by clicking the ellipsis (...) button, and selecting Custom Code
from the Select Converter Property Using drop-down list.
Below is a list of conversions for which you do not need to provide a converter:
Validator. Enables you to specify code to validate a change in the target property value before propagating
that change back to the source property. For example, you can use a validator to make sure that an integer
property value is within a specific range.
Validators need to extend the org.jdesktop.beansbinding.Validator class.
The Validator drop-down list is populated with any validators that have been added as beans to your form.
You can also add the validation code directly by clicking the ellipsis (...) button, and selecting Custom Code
Unreadable Source Value. Enables you to specify a different value to use if the binding expression cannot
be resolved when the binding is attempted. This field corresponds with
thesetSourceUnreadableValue() method of the org.jdesktop.beansbinding.Binding class.
Note: To better understand the classes and methods mentioned above, you can access the beans binding
Javadoc documentation directly from the IDE. Choose Help > Javadoc References > Beans Binding. In the
browser window that opens, click the org.jdesktop.beansbinding link to access documentation for
those classes.
Created classes that represent the database tables to which you want to bind. Steps on creating the entity
classes for binding data to a component are given below.
Note: You can also use the New Java Desktop Application project wizard to quickly create a whole working
application that has CRUD (create, read, update, and delete) features in a simple master/detail view.
See Building a Java Desktop Database Application for more information.
To create entity classes to represent the database that is to be bound to the JTable:
1.
In the Projects window, right-click your project and choose New > Other, select the Persistence
category, and select the Entity Classes from Database template.
2.
In the Database Tables page of the wizard, select the database connection.
3.
Once the Available Tables column is populated, select the tables that you want to use in your
application and click Add to move them to the Selected Tables column. Click Next.
4.
In the Entity Classes page of the wizard, make sure the Generate Named Query Annotations for
Persistent Fields dialog box is selected.
5.
Make any customizations that you want to make to the names of the generated classes and their
location.
6.
7.
In the Create Persistence Unit dialog box, make sure of the following things:
8.
Click Finish.
You should see nodes for the entity classes in the Projects window.
2.
Connect to the database that contains the table that you want to add to the form. (You can connect
to the database by right-clicking the node for the database connection and choosing Connect.)
3.
Expand the node for the connection, and expand its Tables node.
4.
Drag the node for the table on to the form and press Ctrl as you drop the table.
A JTable is created and its columns are bound to the columns in the database table.
Right-click the component in the GUI Builder and choose Bind > elements.
2.
Click Import Data to Form. From the Import Data to Form dialog box, select the database table to
which you want to bind your components. Click OK.
3.
From the Binding Source combo box, select the item that represents the result list of the entity
class. For example, if the entity class is called, Customer.java, the list object would be generated
as customerList.
4.
5.
If there are any database columns that you do not want to appear in the JTable, select those
columns in the Selected list and move them to the Available list.
6.
Select the Advanced tab to further configure the binding. For example, you can specify a validator
or converter, or you can specify behavior if the binding source is null or unreadable.
7.
Click OK.
Right-click the component in the GUI Builder and choose Bind > elements.
2.
Click Import Data to Form. From the Import Data to Form dialog box, select the database table to
which you want to bind your components. Click OK.
3.
From the Binding Source combo box, select the item that represents the result list of the entity
class. For example, if the entity class is called, Customer.java, the list object would be generated
as customerList.
4.
5.
In the Display Expression drop-down list, select the property that represents the database column
that contains the values that you want to display in the list.
6.
7.
Click OK.
2.
Click Import Data to Form. From the Import Data to Form dialog box, select the database table to
which you want to bind your components. Click OK.
3.
From the Binding Source combo box, select the item that represents the result list of the entity
class. For example, if the entity class is called, Customer.java, the list object would be generated
as customerList.
4.
5.
Right-click the combo box again and choose Bind > selectedItem.
6.
Bind to the property that you want to be affected by the user selection. Click OK.
The Beans Binding library (as of version 1.2.1) does not have a DetailBinding class that enables you to
specify how to derive the display values for the JComboBox. So you will need to write some custom code.
One approach is to write a custom cell renderer, as shown below.
To render the combo box propertly:
1.
2.
In the Properties tab of the Properties window, select the renderer property.
3.
4.
In the combo box at the top of the property editor, select Custom Code.
5.
In the text area, enter code similar to the following (where jComboBox1 is the name of the
JComboBox instance, MyEntityClass is the your entity class,
andgetPropertyFromMyEntityClass() is the getter for the property in the entity class which
you are binding.
jComboBox1.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 MyEntityClass) {
MyEntityClass mec = (MyEntityClass)value;
setText(mec.getPropertyFromMyEntityClass());
}
return this;
}
})
Note: You can also create a custom renderer in its own source file, compile the file, drag the renderer on to
the form, and then set the combo box's renderer property to use this bean. You can see this approach used
in the Creating a Custom Desktop Database Application tutorial.
Below is a list of the synthetic properties added by the beans binding libraries:
Component
Property
Description
AbstractButton selected
JComboBox
selectedItem
value
value_IGNORE_ADJUSTING
selectedElement
selectedElements
JSlider
JList
JTable
selectedElement
{column0=column0val
ue,
column1=column1valu
e, ...} If nothing is
selected, the property
evaluates to null.
selectedElements
Same as
"selectedElement" but
selectedElement_IGNORE_ADJUSTI does notify of change
NG
while the table
selection is being
updated.
Same as
"selectedElements" but
selectedElements_IGNORE_ADJUST does not notify of
ING
change while the table
selection is being
updated.
text
JTextCompone
text_ON_FOCUS_LOST
nt (including
its sub-classes
JTextField,
JTextArea, and
JEditorPane)
text_ON_ACTION_OR_FOCUS_LOS
T
The Java Persistence API (JPA), which helps you use Java code to interact with databases.
Beans Binding (JSR-295), which provides a way for different JavaBeans components to have property values
that are synchronized with each other. For example, you can use beans binding to keep the values of cells in
a JTable visual component in synch with the values of fields in an entity class. (In turn, the entity class
represents the database table.)
The Swing Application Framework (JSR-296), which provides some useful building blocks for quickly creating
desktop applications.
We will create a database CRUD (create, read, update, delete) application with a custom component used
for visualizing the data (car design preview).
This tutorial is largely based on a screencast that was based on a development build of a previous version of
the IDE. Some of the user interface has changed since that demo was made, so you might notice some
differences between this tutorial and the demo. You can view the demo (about 9 minutes) now or download
a zip of the demo.
Expected duration: 45 minutes
Contents
Next Steps
To complete this tutorial, you need the software and resources listed in the following table.
Software or Resource
Version
Required
NetBeans IDE
version 6
CarPreview project
2.
Database Location. Enter the folder where you want the databases to be stored.
3.
Click OK.
In the Services window, right-click Databases > Java DB and choose Start Server.
If you do not already have a location set for the database, the Set Database Location dialog box appears.
Enter a location for the database server to store the databases. You can create a new folder there if you
wish.
Once the server is started, Java DB Database Process tab opens in the Output window and displays a
message similar the following:
In the Services window, right-click Databases > Java DB and choose Create Database.
2.
For the Database Name text field, type car_database. Also set the User Name and Password to
nbuser.
3.
Click OK.
Switch to the Services window (Ctrl+5) and expand the Databases node to see your new database.
2.
3.
Expand the connection node and note that there are several schema subnodes. Right-click the APP
node and choose Set as Default Schema.
4.
Expand the APP schema node, right-click its Tables subnode, and choose Execute Command.
5.
Copy the contents of the car.sql file and paste them into the SQL Command tab of the SQL Source
Editor.
This is the SQL script which will populate the database with data about cars.
6.
7.
Right-click the Tables node of the APP schema and choose Refresh to view the new Car table.
2.
In the first panel of the wizard, expand the Java category and select the Java Desktop Application
template. Click Next.
The Java Desktop Application template provides many basics of a visual application, including basic
menu items and commands.
3.
4.
In the Name and Location page of the wizard, do the following things:
a.
In the Project Name field, type CarsApp. The value of this field sets the display name for
the project in the Projects window.
b.
c.
(Optional) Edit the Project Location field to change the location of your project metadata.
d.
(Optional) Select the Use Dedicated Folder for Storing Libraries checkbox and specify the
location for the libraries folder. See Sharing Project Libraries for more information on this
option.
5.
e.
f.
Click Next.
In the Master Table page of the wizard, select the database connection for the CAR database. The
listing for the database should look something like the
following:jdbc:derby://localhost:1527/car_database[nbuser on NBUSER]
6.
Select the bottom five column names (beginning with SUN_ROOF and ending with MODERNNESS)
and click the < button to move them to the left column. Click Next.
7.
Ability to view and modify values in five columns of the CAR database.
Persistence of its window state between sessions. When you close the application, the window position and
size are remembered. So when you reopen the application, the window opens in the same position as it was
when you closed it.
.properties files containing the labels in the user interface. Using .properties files is a good way to
keep the logic of your code separate from the text that appears in the user interface of your application.
Such separation is useful for making it easier to localize your program, among other reasons.
To see some of the features that are already built into the application, follow these steps:
1.
After a few seconds, the application starts and a window called Database Application Example
appears. This window contains a table and several controls that enable you to edit the CARS
database.
2.
3.
Select the Price text field and replace the existing value with 46999. Then press Enter.
The value should appear updated in the table. (However, that value will not be reflected in the
database until you click Save.)
Similarly, you can update any other values in the table.
4.
Click New to create a new record. Then fill in values for each of the fields (Make, Model, Price, Body
Style, Color). For example, you can fill in Trabant, Classic, 1000, wagon, andblue. Click Save
to save the entry in the database.
5.
Click the Database Application Example title bar and drag the application to a different place on
your screen.
6.
Click the left border of the Database Application Example window and drag to the left to increase
the size of the window.
7.
In the Database Application Example menu bar, choose File > Exit.
8.
In the IDE, right-click the project's node and choose Run Project.
The Database Application Example window will open in the same size and position it was in when
you closed the application.
combination of the following mechanisms, all of which have been generated by the IDE:
The Car.java entity class, which is used to read and write data to the CAR database table. Entity classes
are a special type of class that enable you to interact with databases through Java code. Entity classes use
Java annotations to map class fields to database columns.
The META-INF/persistence.xml file, which defines a connection between the database and the entity
class. This file is also known as the persistence unit.
Using beans binding to connect the properties of the entity class with the properties of the JTable
component. Beans binding is a new technology based on JSR 295 and which will probably be included in a
future Java SE release.
The entityManager, query, and list objects, which are defined in the CarsView class and which are
listed in the Inspector window.
The entity manager object is used to retrieve and commit data within the defined persistence unit scope.
The query object defines how the particular data collection is retrieved from the entity manager. (You can
change the way that the query object works by selecting the query object in the Inspector window and
changing the query property in the property sheet. The query property uses JPA query language.
The list object is an observable collection that holds the data from the query. An observable collection is a
special kind of collection on which you can place a listener to find out when changes to the collection have
been made.
Using the Inspector window and the property sheet, you can follow these steps to see how the JTable is
bound to data:
1.
In the Inspector window, select the mainPanel[JPanel] > masterScrollPane [ScrollPane] >
masterTable [JTable] node. Then click the Binding tab in the Properties window.
2.
3.
Click the ellipsis [...] button to open the Bind masterTable.elements customizer, where you can
further customize the binding between the table and the database. For example, you can see that
the customizer enables you to specify which table columns are bound.
Besides the Binding category in property sheet you can also use the Bind menu in context menu.
Add the first slider by clicking the Slider button in the Palette window and then clicking in the form
just above the New button. Before clicking in the form to insert the slider, make sure that no
horizontal slotted guiding lines are shown. These lines indicate that the slider will be inserted in the
same line as the fields or the buttons. See the figure below to see where you should drop the slider
into the form.
Note: If you drop the component in a place you do not want and thus cause several undesired
layout changes, you can use the Undo command to reverse the changes. Choose Edit > Undo or
press Ctrl-Z.
2.
If necessary, stretch the slider to the left to align it with the left side of the text field components.
3.
Stretch the slider to the right to span the whole form width.
4.
Add a label to the left of the slider and set its text to Tire Size. (Click the label to make it
editable.)
5.
Add another slider below the first slider, and adjust its width and alignment where necessary.
6.
Add another label below the Tire Size label and set its text to Modernness.
7.
Add two checkboxes below the sliders. Set their text to Spoiler and Sun Roof. (Make the display
text editable by clicking the checkbox once, pausing, and then clicking the checkbox again. You can
also right-click the checkbox and choose Edit Text.)
In the form, right-click the Tire Size slider and choose Bind > value.
2.
In the Binding Source drop-down list of the Binding dialog box, select masterTable.
3.
In the Binding Expression drop-down list, select the selectedElement > tiresize node and
click OK.
4.
5.
In the form, right-click the Modernness slider and choose Bind > value.
6.
In the Binding Source drop-down list of the Binding dialog box, select masterTable.
7.
In the Binding Expression drop-down list, select selectedElement > modernness and click OK.
8.
In the form, right-click the Spoiler checkbox and choose Bind > selected.
2.
In the Binding Source drop-down list of the Binding dialog box, select masterTable.
3.
4.
5.
In the form, right-click the Sun Roof checkbox and choose Bind > selected.
6.
In the Binding Source drop-down list of the Binding dialog box, select masterTable.
7.
In the Binding Expression drop-down list, select selectedElement> sunRoof and click OK.
8.
You should now be able to change database entries using the slider and checkboxes.
To verify that the sliders and checkboxes work:
1.
2.
Make sure the IDE has a connection to the database by right-clicking Databases >
3.
Right-click
the Databases > jdbc:derby;//localhost:1527/car_database > APP > Tables > CAR no
de and choose View Data.
4.
Look at the SUN_ROOF, SPOILER, TIRE_SIZE, and MODERNNESS values for the first record.
5.
6.
7.
8.
9.
elements. Then when you run the application again, the car preview will be modified as you change the
selected row and change the values of the various fields.
To make the CarPreview component available for the CarsApp project:
1.
If you have not already done so, download the CarPreview.zip file.
2.
Using a standard zip tool, extract the archives of the zip file.
3.
Choose File > Open Project and navigate into the extracted contents of the zip file and select the
CarPreview project.
4.
5.
This component was written as a JavaBeans component, so you could add it to the Palette, which would be
convenient for adding the component to multiple applications. But for now we will simply drag the
component directly into your application directly from the Projects window.
To add the CarPreview component to the application:
1.
In the Projects window, expand the Car Preview > Source Packages > carpreview node.
2.
Drag the CarPreview.java class to the form. To insert it properly just below the menus, place it
over the table left aligned with the other controls and snapped to the bottom of the menu bar, as
shown in the image below.
3.
4.
In the same way that you bound the sliders and checkboxes to elements in
the masterTable component, bind all the binding properties of the CarPreview component to the
corresponding selectedElement attributes of the masterTable. Use the Bind popup menu or the
Binding tab in the property sheet.
5.
The main build command in the IDE is the Clean and Build command. The Clean and Build command deletes
previously compiled classes and other build artifacts and then rebuilds the entire project from scratch.
Notes: There is also a Build command, which does not delete old build artifacts, but this command is
disabled by default. See the Compile on Save section of the Creating, Importing, and Configuring Java
Projects guide for more information.
To build the application:
Output folders that have been generated by previous build actions are deleted ("cleaned"). (In most cases,
these are the build and dist folders.)
build and dist folders are added to your project folder (hereafter referred to as
the PROJECT_HOME folder).
All of the sources are compiled into .class files, which are placed into the PROJECT_HOME/build folder.
A JAR file containing your project is created inside the PROJECT_HOME/dist folder.
If you have specified any libraries for the project (in addition to the JDK), a lib folder is created in
the dist folder. The libraries are copied into dist/lib.
The manifest file in the JAR is updated to include entries that designate the main class and any libraries that
are on the project's classpath.
2.
the IDE.
Using your system's file explorer or file manager, navigate to the CarsApp/dist directory.
3.
2.
3.
4.
Send the file to the people who will use the application. Instruct them to unpack the zip file, making
sure that the CarsApp.jar file and the lib folder are in the same folder.
3.
Instruct the users to follow the steps in the Running the Application Outside of the IDE section
above.
Next Steps
This tutorial has provided an introduction to support for the Swing Application Framework and Beans Binding
in the IDE.
For information on using Hibernate for a Swing application's persistence layer, see Using Hibernate in a Java
Swing Application.
For a more information on designing GUI applications, see Designing a Swing GUI.
ackage org.kodejava.example.net;
package org.kodejava.example.net;
import java.net.InetAddress;
}
}
import java.io.*;
import java.net.*;
public class ReachableTest {
public static void main(String args[]) {
try {
InetAddress address = InetAddress.getByName("web.mit.edu");
System.out.println("Name: " + address.getHostName());
System.out.println("Addr: " + address.getHostAddress());
System.out.println("Reach: " + address.isReachable(3000));
}
catch (UnknownHostException e) {
System.err.println("Unable to lookup web.mit.edu");
}
catch (IOException e) {
System.err.println("Unable to reach web.mit.edu");
}
}
}
package examples;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.commons.net.telnet.TelnetClient;
import org.apache.commons.net.telnet.TelnetNotificationHa
ndler;
import org.apache.commons.net.telnet.SimpleOptionHandler;
import org.apache.commons.net.telnet.EchoOptionHandler;
import org.apache.commons.net.telnet.TerminalTypeOptionHa
ndler;
import org.apache.commons.net.telnet.SuppressGAOptionHand
ler;
import org.apache.commons.net.telnet.InvalidTelnetOptionE
xception;
import java.util.StringTokenizer;
/***
* This is a simple example of use of TelnetClient.
if (args.length > 1)
{
remoteport = (new Integer(args[1])).intValue(
);
}
else
{
remoteport = 23;
}
try
{
fout = new FileOutputStream ("spy.log", true)
;
}
catch (Exception e)
{
System.err.println(
"Exception while opening the spy file: "
+ e.getMessage());
}
tc = new TelnetClient();
TerminalTypeOptionHandler ttopt = new TerminalTyp
eOptionHandler("VT100", false, false, true, false);
EchoOptionHandler echoopt = new EchoOptionHandler
(true, false, true, false);
SuppressGAOptionHandler gaopt = new SuppressGAOpt
ionHandler(true, true, true, true);
try
{
tc.addOptionHandler(ttopt);
tc.addOptionHandler(echoopt);
tc.addOptionHandler(gaopt);
}
catch (InvalidTelnetOptionException e)
{
System.err.println("Error registering option
handlers: " + e.getMessage());
}
while (true)
{
boolean end_loop = false;
try
{
tc.connect(remoteip, remoteport);
Thread reader = new Thread (new TelnetCli
entExample());
tc.registerNotifHandler(new TelnetClientE
xample());
System.out.println("TelnetClientExample")
;
System.out.println("Type
YT telnet command");
System.out.println("Type
eport of status of options (0-24)");
System.out.println("Type
ster a new SimpleOptionHandler");
System.out.println("Type
register an OptionHandler");
System.out.println("Type
the spy (connect to port 3333 to spy)");
System.out.println("Type
ying the connection");
AYT to send an A
OPT to print a r
REGISTER to regi
UNREGISTER to un
SPY to register
UNSPY to stop sp
reader.start();
OutputStream outstr = tc.getOutputStream(
);
byte[] buff = new byte[1024];
int ret_read = 0;
do
{
try
{
ret_read = System.in.read(buff);
if(ret_read > 0)
{
(new Boolean(st.nextToken())).booleanValue();
boolean acceptlocal =
(new Boolean(st.nextToken())).booleanValue();
boolean acceptremote
= (new Boolean(st.nextToken())).booleanValue();
SimpleOptionHandler o
pthand = new SimpleOptionHandler(opcode, initlocal, initr
emote,
acceptlocal, acceptremote);
tc.addOptionHandler(o
pthand);
}
catch (Exception e)
{
if(e instanceof Inval
idTelnetOptionException)
{
System.err.printl
n("Error registering option: " + e.getMessage());
}
else
{
System.err.printl
n("Invalid REGISTER command.");
System.err.printl
n("Use REGISTER optcode initlocal initremote acceptlocal
acceptremote");
System.err.printl
n("(optcode is an integer.)");
System.err.printl
n("(initlocal, initremote, acceptlocal, acceptremote are
boolean)");
}
}
}
else if((new String(buff, 0,
ret_read)).startsWith("UNREGISTER"))
{
StringTokenizer st = new
StringTokenizer(new String(buff));
try
{
st.nextToken();
int opcode = (new Int
eger(st.nextToken())).intValue();
tc.deleteOptionHandle
r(opcode);
}
catch (Exception e)
{
if(e instanceof Inval
idTelnetOptionException)
{
System.err.printl
n("Error unregistering option: " + e.getMessage());
}
else
{
System.err.printl
n("Invalid UNREGISTER command.");
System.err.printl
n("Use UNREGISTER optcode");
System.err.printl
n("(optcode is an integer)");
}
}
}
else if((new String(buff, 0,
ret_read)).startsWith("SPY"))
{
try
{
tc.registerSpyStream(
fout);
}
catch (Exception e)
{
System.err.println("E
rror registering the spy");
}
}
else if((new String(buff, 0,
ret_read)).startsWith("UNSPY"))
{
tc.stopSpyStream();
}
else
{
try
{
outstr.write(buff
, 0 , ret_read);
outstr.flush();
}
catch (Exception e)
{
end_loop = true;
}
}
}
}
catch (Exception e)
{
System.err.println("Exception whi
le reading keyboard:" + e.getMessage());
end_loop = true;
}
}
while((ret_read > 0) && (end_loop == fals
e));
try
{
tc.disconnect();
}
catch (Exception e)
{
System.err.println("Exception w
hile connecting:" + e.getMessage());
}
}
catch (Exception e)
{
System.err.println("Exception while c
onnecting:" + e.getMessage());
System.exit(1);
}
}
}
/***
* Callback method called when TelnetClient receives
an option
* negotiation command.
* <p>
* @param negotiation_code type of negotiation command received
* (RECEIVED_DO, RECEIVED_DONT, RECEIVED_WILL, RECEIV
ED_WONT)
* <p>
* @param option_code - code of the option negotiated
* <p>
***/
public void receivedNegotiation(int negotiation_code,
int option_code)
{
String command = null;
if(negotiation_code == TelnetNotificationHandler.
RECEIVED_DO)
{
command = "DO";
}
else if(negotiation_code == TelnetNotificationHan
dler.RECEIVED_DONT)
{
command = "DONT";
}
else if(negotiation_code == TelnetNotificationHan
dler.RECEIVED_WILL)
{
command = "WILL";
}
else if(negotiation_code == TelnetNotificationHan
dler.RECEIVED_WONT)
{
command = "WONT";
}
System.out.println("Received " + command + " for
option code " + option_code);
}
/***
* Reader thread.
* Reads lines from the TelnetClient and echoes them
* on the screen.
***/
public void run()
{
InputStream instr = tc.getInputStream();
try
{
byte[] buff = new byte[1024];
int ret_read = 0;
do
{
ret_read = instr.read(buff);
if(ret_read > 0)
{
System.out.print(new String(buff, 0,
ret_read));
}
}
while (ret_read >= 0);
}
catch (Exception e)
{
System.err.println("Exception while reading s
ocket:" + e.getMessage());
}
try
{
tc.disconnect();
}
catch (Exception e)
{
import org.apache.commons.net.telnet.TelnetClient;
import java.io.InputStream;
import java.io.PrintStream;
public class AutomatedTelnetClient {
private TelnetClient telnet = new TelnetClient();
private InputStream in;
private PrintStream out;
private String prompt = #;
public AutomatedTelnetClient(String server, String user, String password) {
try {
// Connect to the specified server
telnet.connect(server, 23);
// Get input and output stream references
in = telnet.getInputStream();
out = new PrintStream(telnet.getOutputStream());
// Log the user on
readUntil(login: );
write(user);
readUntil(Password: );
write(password);
// Advance to a prompt
readUntil(prompt + );
}
catch (Exception e) {
e.printStackTrace();
}
}
public void su(String password) {
try {
write(su);
readUntil(Password: );
write(password);
prompt = #;
readUntil(prompt + );
}
catch (Exception e) {
e.printStackTrace();
}
}
public String readUntil(String pattern) {
try {
char lastChar = pattern.charAt(pattern.length() 1);
StringBuffer sb = new StringBuffer();
boolean found = false;
char ch = (char) in.read();
while (true) {
System.out.print(ch);
sb.append(ch);
if (ch == lastChar) {
if (sb.toString().endsWith(pattern)) {
return sb.toString();
}
}
ch = (char) in.read();
}
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void write(String value) {
try {
out.println(value);
out.flush();
System.out.println(value);
}
catch (Exception e) {
e.printStackTrace();
}
}
public String sendCommand(String command) {
try {
write(command);
return readUntil(prompt + );
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void disconnect() {
try {
telnet.disconnect();
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
AutomatedTelnetClient telnet = new AutomatedTelnetClient(127.0.0.1,
username,
password);
telnet.sendCommand(ps -ef );
telnet.disconnect();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
A main view that enables users to browse customer records and customer purchases.
Separate dialog boxes for entering new records or modifying existing records.
Introduction
See Also
To complete this tutorial, you need the following software and resources.
Software or Resource
Version Required
NetBeans IDE
version 6
Introduction
This application takes advantage of the following technologies:
The Java Persistence API (JPA), which helps you interact with a database using Java code.
Beans Binding, which enables you to keep Swing component properties synchronized.
The Swing Application Framework, which simplifies basic application functions such as persisting session
information, handling actions, and managing resources.
The tutorial makes use of IDE wizards and other code generation features to provide much of the boilerplate
code. It also shows you how to customize the generated code and hand code other parts of the application.
This tutorial takes approximately 2 hours to complete. For a shorter tutorial that shows the creation of a less
customized user interface, see Building a Java Desktop Database Application.
Below is a screenshot of the working application that you will have when you complete the tutorial.
In the Services window, right-click the MySQL Server node and choose Start.
2.
3.
4.
5.
If the Connect dialog box appears, type the password that you have set for the database server.
6.
If the Advanced tab of the dialog box opens, click OK to close the dialog box.
-->
7.
Scroll down to the node for connection that you have just created. The node should have
the
icon.
8.
9.
Copy the contents of the MyBusinessRecords SQL script and paste them into the SQL Command 1
tab of the Source Editor.
The SQL script specifies the InnoDB storage engine in order to handle the foreign keys in this database.
MySQL's default storage engine, MyISAM, will not work with this tutorial.
The data is split among several tables to reduce duplication and the possibility for inconsistencies. Some
tables are connected to each other through foreign keys.
All of the tables use MySQL's AUTO_INCREMENT attribute so that there is a unique identifier for each row in
those tables. This identifier is created by the database management software, so your application and/or
your application's user do not have to create this identifier. (So that the AUTO_INCREMENT is used correctly
within the application, the IDE adds
the@GeneratedValue(strategy=GenerationType.IDENTITY annotation for that column in the table's
entity class. This ensures that the application does not try to submit a value for that column when you
create a new record.)
The foreign key in the ORDERS table is there to link each order record with a customer. In the application's
user interface, ORDER records are only displayed for the selected CUSTOMER.
The ON CASCADE DELETE attribute for the foreign key to the CUSTOMERS class ensures that a customer's
orders are also deleted when a customer is deleted.
The foreign key in the CUSTOMERS table points to a COUNTRIES table. You will use this relationship in the
application to enable the user to select a customer's country from a combo box.
The ORDERS table has a foreign key to the PRODUCTS table. When adding a new order record, the user will
be able to choose a product from a combo box.
The COUNTRIES and PRODUCTS tables are pre-populated with data so that you can choose from those
tables when the user of the application is adding customer and order records.
Though this tutorial does not cover it, you might find it useful to create separate applications to populate the
COUNTRIES and PRODUCTS tables. Such applications could be created with the Java Desktop Application
project template and would not require additional hand-coding.
A main application frame that contains tables for customer details and customer orders.
A main application class that handles basic application life-cycle functions, including persisting of window
state between sessions and resource injection.
2.
Select the Java category and the Java Desktop Application template. Then click Next.
3.
In the Name and Location page of the wizard, follow these steps:
1.
2.
3.
Click Next.
4.
Move the ID entry from the Columns to Include column to Available Columns.
Click Next.
5.
Click the Table radio button to create a JTable for the ORDERS table.
Move the ID entry from the Columns to Include column to Available Columns.
6.
The Customers and Orders entity classes, which represent the data from the CUSTOMERS AND ORDERS
database tables.
The main form with two JTable components that provide a master/detail view of the CUSTOMERS and
ORDERS database tables. The view also contains buttons which are connected to actions for creating,
deleting, and saving records.
The master table is bound to a list of Customers objects. That list of objects represents all of the rows of the
CUSTOMERS database table.
The detail table is bound to a collection of Orders objects. That collection represents all of the rows of the
ORDERS database table that are linked with the currently selected customer in the master table.
At this point you can choose Run > Run Main Project to see the main application window. However, the
application does not yet function properly, because the database has some attributes for which the wizard
did not generate necessary code. You will add this code in the next section of the tutorial.
When you run the application, the application's frame has the title "Database Application Example". For a
simple JFrame, you would normally change the title by modifying the title property in the property sheet
for the component. However, the frame of this application uses the Swing Application
Framework's FrameView class, and the title is a general application property. You can modify such
application properties in the Project Properties window.
To change the title of the main frame of the application:
1.
In the Projects window, select the project's node and choose Properties.
2.
3.
4.
If desired, modify the other properties as well, such as Description, and Splash Screen.
Customize the Customers entity class to refer to the Countries entity class.
Customize the Orders entity class to refer to the Products entity class.
Update the binding code for the master and detail tables in the main form so that the Countries and
Products entity classes are used.
Create entity classes for the Countries and Products tables by right-clicking
the customerrecordspackage and choosing New > Entity Classes from Database.
2.
3.
4.
Click Next.
5.
6.
7.
2.
3.
@Column(name = "COUNTRY_ID")
private Integer countryId;
5.
6.
In the setCountryId() method, change the types of countryId and oldCountryId from
Integer to Countries.
To establish the relation between the Orders and Products entity classes:
1.
2.
3.
4.
@Basic(optional = false)
@Column(name = "PRODUCT_ID")
private int productId;
with this code:
6.
7.
8.
In the setProductId() method, change the types of the productId parameter and
the oldProductId variable from int to Products.
2.
In the Design view of the class, right-click the top table and choose Table Contents.
3.
4.
5.
6.
Change the Title from Country Id to Country . This affects the column heading in the running
application.
7.
In the Design view of the CustomerRecordsView class, right-click the bottom table and choose Table
Contents.
2.
3.
4.
Change the Expression to ${productId.brand}. After you do so, the type should also change to
String.
5.
6.
7.
Select the row that has just been added to the table.
8.
9.
For Expression, select productId > prodType from the drop-down list.
10. Click the Insert button again and select the newly added row.
11. For Title, type Model.
12. For Expression, select productId > model from the drop-down list.
13. Click the Insert button again and select the newly added row.
14. For Title, type Price.
15. For Expression, select productId > price from the drop-down list.
16. Click Close to apply the changes.
At this point, the application is partially functional. You can run the application and add, edit, delete, and
save records. However, you can not yet properly modify the fields that are based on the Countries and
Products entity classes. In addition, you have some work to make the Order Date field behave in a more
user-friendly way.
You could make some adjustments to the Country and Model columns to use combo boxes that are bound to
their respective tables. That would enable the user to select those fields without having to hand enter them.
Instead, you will use dialog boxes as data entry mechanisms for these tables to make it harder for the user
to accidentally delete data while browsing it.
Create dialog boxes to edit data for each of the tables on the main form.
Create intermediary beans to carry the data between the dialogs and form.
Unpack the zip of file of utility classes and unzip its contents on your system.
2.
On your system, copy all of the files from the zip file and paste them into the folder that contains
the project's customerrecords folder.
3.
If your classes are in a different package than customerreccords, adjust the package statement
in each of the files you have just added to the project.
2.
3.
In the Set Action Dialog box, change the Text property to New Customer.
4.
5.
Click OK.
6.
7.
In the Set Action Dialog box, change the Text property to Enter Order.
8.
Change the Tool Tip field to Create a new customer order record.
9.
Click OK.
Right-click the package that contains your classes and choose New > Other. Select Swing GUI
Forms > JDialog Form template and name it CustomerEditor.
2.
From the Palette window drag, drop, and arrange components for the customer's personal details.
Add labels for each of the following fields: first name, last name, address, city, state, zip code,
country, and phone number.
Add text fields for all of the above fields, except for Country.
For Country, add a combo box.
3.
4.
5.
(Optional) Rename all of the components you have added to more memorable names, such
as firstNameLabel. You can do this inline in the Inspector window.
6.
7.
The resulting layout should look something like what you see below.
Note: For a detailed guide to using the GUI Editor's layout features, see Designing a Swing GUI in NetBeans
IDE.
At the top of the design area of the CustomerEditor form, click the Source tab. Click somewhere
within the class, such as in the line below the constructor.
2.
Press Alt-Insert (or right-click and choose Insert Code) and choose Add Property.
3.
In the Add Property dialog, name the property currentRecord, give it the type Customers,
select Generate Getter and Setter, and select Generate Property Change Support.
4.
You now need to customize the generated setCurrentRecord method to fire a property change
notification.
2.
3.
4.
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
5.
CustomerEditor ce = new CustomerEditor(mainFrame, false);
6.
ce.setCurrentRecord(c);
7.
ce.setVisible(true);
You can now proceed with the binding of the dialog box's fields. You will bind the text property of each text
field to the corresponding property of the Customers object represented bycurrentRecord. Similarly, you
will bind the combo box's selectedItem property to the countryId property of currentRecord. You
will bind the combo box's elements property to a list of Countries entities.
To bind the text fields to properties of the currentRecord bean:
1.
2.
3.
4.
5.
In the Bind dialog box, select Form as the Binding Source. (Note that Form is at the very bottom of
the drop-down list.)
6.
In the Binding Expression drop-down list, expand the currentRecord node and select the property
corresponding to the text field that you are binding.
7.
8.
2.
Click Import Data to Form, select the database connection, and select the Countries
table. countriesList should appear as the binding source. Click OK.
3.
Right-click the combo box again and choose Bind > selectedItem.
4.
Select Form as the binding source and currentRecord > countryId as the expression. Click OK.
The combo box is almost ready to work properly in the dialog. It is set up to draw its values from the
COUNTRIES database table, and the item that the user selects is then applied to the country field in the
current record. However, you still need to customize the rendering of the combo box, since the values bound
to the combo box are Countries objects, not simple names. You will do that by specifying a custom cell
renderer. (For JTables and JLists, the beans binding library enables you to specify display expressions, thus
avoiding the need to create a custom renderer. However, that feature does not exist yet for combo boxes.)
To get the combo boxes to render country names, do the following:
1.
In the Project's window, right-click CountryListCellRenderer and choose Compile File. Compiling the
file enables you to treat it as a bean that you can add to the form by dragging and dropping from
within the IDE's GUI builder.
2.
Select the CustomerEditor form in the Source Editor and select the Design view.
3.
Drag the class from the Projects window to the white space surrounding the form, as shown in the
screenshot below.
Doing so adds the renderer to your form as a bean, much like dragging a component from the
Palette adds that component to your form.
4.
5.
6.
Scroll to the renderer property and choose countryListCellRenderer1 from the drop-down
list for that property.
Now you should be able to run the application, press the first New button, and enter data in the dialog. You
should also be able to select a country from the Country combo box. The Save and Cancel buttons on the
dialog do not do anything yet, but you can save the records from the main frame. You will code those
buttons later.
3.
A label to display the date format that needs to be entered. Give this label the text (MMM DD,
YYYY - e.g. Apr 17, 2008). If you plan to use a different date format, add text that
corresponds to the format that you plan to use.
4.
5.
Now that the Order Editor has its visual design, you need to do the following things:
Create an intermediary bean to carry the record values back to the main form.
Connecting the Order Editor Dialog Box With the Main Form
As you did for the CustomerEditor dialog box, you will have to create an intermediary bean property of type
Orders to hold the record. When the user presses Enter Order, the property will be given the value of the
currently selected order record.
To create the bean property:
1.
At the top of the design area of the OrderEditor form, click the Source tab.
2.
Click somewhere within the class, such as in the line below the constructor.
3.
Press Alt-Insert (or right-click and choose Insert Code) and choose Add Property.
4.
In the Add Property dialog, name the property currentOrderRecord, give it the type Orders,
select Generate Getter and Setter, and select Generate Property Change Support.
5.
6.
Now you need to add code to open the Order Editor dialog box when the user clicks the Enter Order button.
In addition, you need code to clear the currentOrderRecord property.
To connect the dialog with the Enter Order button:
1.
2.
3.
4.
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
5.
OrderEditor oe = new OrderEditor(mainFrame, false);
6.
oe.setCurrentOrderRecord(o);
7.
oe.setVisible(true);
2.
3.
4.
Select the value property and click the ellipsis (...) button that is next to the property.
5.
In the Bind dialog box, select Form as the binding source, and select currentOrderRecord >
orderDate as the binding expression.
6.
Select the combo box for the product and click the ellipsis (...) button for the elements property.
7.
8.
Select the MyBusinessRecords database connection and select the products table.
When you import this data to the form, code for a List object of Products is generated. This object
(listProducts) is then set as the binding source.
9.
10. Click the ellipsis (...) button for the combo box's selectedItem property.
11. Select Form as the binding source, and select currentOrderRecord > productId as the binding
expression.
12. Select the Quantity text field.
13. Click the ellipsis (...) button for the text property.
14. Select Form as the binding source, and select currentOrderRecord > quantity as the binding
expression.
15. Select the price's formatted field.
16. Click the ellipsis (...) button for the value property.
For the order date, price, and total, you have added formatted text fields, which make it easy to provide
formatting.
To set the date and currency formatting for those fields:
1.
2.
3.
4.
In the formatterFactory property editor, select date in the Category column. Then
select Default in the Format column.
5.
6.
7.
In the Properties window, clear the editable property. (You do not want users to edit this field.
The value of this field will be derived from the price property of the selected item in the product
combo box.)
8.
In the formatterFactory property editor, select currency in the Category column. Then
select Default in the Format column.
9.
2.
3.
Drag the class from the Projects window to the white space surrounding the form in the same way
that you did for the CountryListCellRenderer.
4.
5.
In the Properties window, scroll to the renderer property and choose productListCellRenderer1
from the drop-down list for that property.
2.
3.
4.
o.setOrderDate(new java.util.Date());
o.setQuantity(1);
Now when you run the application, the current date should appear in the Date field when you open the
dialog box and the default quantity should be 1.
2.
3.
4.
5.
6.
7.
You will set this property's value in event handling code for the buttons.
To create the event listeners and handlers:
1.
2.
3.
4.
5.
In the Handlers for actionPerformed dialog box, add a handler called saveCustomer.
6.
Within the saveCustomer method in the Source Editor (where the cursor jumps after you create
the new handler), type the following code:
7. setCustomerConfirmed(true);
8. setVisible(false);
9.
Repeat steps 2-5 for the Cancel button and call its handler cancelCustomer.
11. setCustomerConfirmed(false);
12. setVisible(false);
13.
In the CustomerRecordsView class, navigate to the newRecord() method and add the following code to the
bottom of the method:
if (ce.isCustomerConfirmed()) {
save().run();
} else {
refresh().run();
}
Since the save() and refresh() actions act on any changes made during the application's session, you
should make the dialog modal and make the tables in the main form uneditable. Another reason to make the
dialog modal is so that when the user presses either the Save or Cancel button, the setVisible() method
does not return until the event handler (which includes the setCustomerConfirmed method) has run.
To make the dialog modal:
1.
2.
3.
In the Properties window, click Properties and select the checkbox for the modal property.
2.
3.
4.
5.
Click Close.
You should now be able to create new records and save them from the Customer Editor. You should also be
able to create a new record and cancel from the Customer Editor.
In the RefreshTask inner class, Thread.sleep is called four times to slow down the rollback code to better
demonstrate how Swing Application Framework tasks work. You do not need this code for this application,
so delete those four statements. Similarly, you do not need a try/catch block here, so delete
the try and catch statements as well (but leave the rest of the body of the try block).
2.
3.
4.
5.
6.
7.
You will set this property's value in event handling code for the buttons.
To create event handling code for the buttons:
1.
2.
3.
4.
5.
In the Handlers for actionPerformed dialog box, add a handler called saveOrder.
6.
Within the saveOrder method in the Source Editor (where the cursor jumps after you create the
new handler), type the following code:
7. setOrderConfirmed(true);
8. setVisible(false);
9.
Repeat steps 2-5 for the Cancel button and call its handler cancelOrder.
11. setOrderConfirmed(false);
12. setVisible(false);
13.
In the CustomerRecordsView class, navigate to the newDetailRecord() method and add the following
code to the bottom of the method:
if (oe.isOrderConfirmed()) {
save().run();
} else {
refresh().run();
}
To make the dialog modal:
1.
2.
3.
In the Properties window, click Properties and select the checkbox for the modal property.
Open the CustomerRecordsView class in the Source Editor and select the Design view.
2.
3.
4.
5.
Click Close.
2.
Delete the deleteRecord() method and replace it with the following code:
3.
4.
5.
@Action(enabledProperty = "recordSelected")
public void deleteRecord() {
int n = JOptionPane.showConfirmDialog(null, "Delete the records
permanently?", "Warning",
6.
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null);
7.
if (n == JOptionPane.YES_OPTION) {
8.
int[] selected = masterTable.getSelectedRows();
9.
List<customerrecords.Customers> toRemove = new
ArrayList<customerrecords.Customers>(selected.length);
10.
for (int idx = 0; idx < selected.length; idx++) {
11.
customerrecords.Customers c =
list.get(masterTable.convertRowIndexToModel(selected[idx]));
12.
toRemove.add(c);
13.
entityManager.remove(c);
14.
15.
16.
17.
18.
19.
}
list.removeAll(toRemove);
save().run();
} else {
refresh().run();
}
}
22.
23.
24.
25.
@Action(enabledProperty = "detailRecordSelected")
public void deleteDetailRecord() {
Object[] options = {"OK", "Cancel"};
int n = JOptionPane.showConfirmDialog(null, "Delete the records
permanently?", "Warning",
26.
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null);
27.
if (n == JOptionPane.YES_OPTION) {
28.
int index = masterTable.getSelectedRow();
29.
customerrecords.Customers c =
list.get(masterTable.convertRowIndexToModel(index));
30.
List<customerrecords.Orders> os = c.getOrdersList();
31.
int[] selected = detailTable.getSelectedRows();
32.
List<customerrecords.Orders> toRemove = new
ArrayList<customerrecords.Orders>(selected.length);
33.
for (int idx = 0; idx < selected.length; idx++) {
34.
selected[idx] =
detailTable.convertRowIndexToModel(selected[idx]);
35.
int count = 0;
36.
Iterator<customerrecords.Orders> iter = os.iterator();
37.
while (count++ < selected[idx]) {
38.
iter.next();
39.
}
40.
customerrecords.Orders o = iter.next();
41.
toRemove.add(o);
42.
entityManager.remove(o);
43.
}
44.
os.removeAll(toRemove);
45.
masterTable.clearSelection();
46.
masterTable.setRowSelectionInterval(index, index);
47.
list.removeAll(toRemove);
48.
save().run();
49.
} else {
50.
refresh().run();
51.
}
}
To add the button and its corresponding event-handling code, do the following:
1.
2.
3.
Drag a button from the palette into the opening just created.
4.
5.
6.
7.
8.
Click the Advanced Tab and select recordSelected for the Enabled Property.
This generates an annotation attribute to ensure that the button and any other trigger for the action
(e.g. a menu item) are only enabled when a record is selected.
9.
11.
12.
setSaveNeeded(true);
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
13.
CustomerEditor ce = new CustomerEditor(mainFrame, false);
14.
ce.setCurrentRecord(list.get(masterTable.convertRowIndexToModel(masterTable
.getSelectedRow())));
15.
ce.setVisible(true);
16.
if (ce.isCustomerConfirmed()) {
17.
save().run();
18.
} else {
19.
refresh().run();
20.
}
Most of that code is copied straight from the newRecord action. The key difference is the
linece.setCurrentRecord(list.get(masterTable.convertRowIndexToModel(masterTable.get
SelectedRow())));, which populates the current record in the dialog with the currently selected record.
The Customer part of the application is almost completely set. You should be able to freely add, edit, and
delete records from your CUSTOMERS table using the specialized GUI you have created.
To add the Edit Orders button and its corresponding event-handling code, do the following:
1.
In the Design view of the CustomerRecordsView class, delete the Refresh and Save buttons.
2.
3.
Drag a button from the palette into the opening just created.
4.
5.
6.
7.
8.
Click the Advanced Tab and select detailRecordSelected for the Enabled Property.
9.
11.
12.
13.
setSaveNeeded(true);
int index = masterTable.getSelectedRow();
customerrecords.Customers c =
list.get(masterTable.convertRowIndexToModel(index));
14.
List<customerrecords.Orders> os = c.getOrdersList();
15.
JFrame mainFrame =
CustomerRecordsApp.getApplication().getMainFrame();
16.
OrderEditor oe = new OrderEditor(mainFrame, false);
17.
oe.setCurrentOrderRecord(os.get(detailTable.getSelectedRow()));
18.
oe.setVisible(true);
19.
if (oe.isOrderConfirmed()) {
20.
save().run();
21.
} else {
22.
refresh().run();
23.
}
Now when you run the application, all of the key elements are in place. You can create, retrieve, update,
and delete records for customers and orders. In the screenshot below, you can see the Order Editor dialog
as it appears after having selected a record and pressed the Edit Order button.
The section below shows some other things that you can do to enhance and fine tune the application.
You have handled the formatting of the dates and currencies within the Orders Editor, but you have not yet
done so for the CustomerRecordsView class. You do not need to do anything for the date field. The format is
effectively passed between the Order Editor and the main form. However, that is not the case for the Price
field. You will need to add a currency renderer class to render that field correctly.
To render the Price field with currency formatting in the main view:
1. In the Projects window, right-click the CurrencyCellRenderer class and choose Compile File.
2.
3.
Drag the CurrencyCellRenderer class from the Projects window and drop it in white area
surrounding the form.
A node called currencyCellRenderer1 should appear in the Inspector window.
4.
Right-click the lower table in the form and choose Table Contents.
5.
6.
7.
Now when you run the application, the price should appear with a dollar sign ($), a decimal point, and two
digits after the decimal point.
master table a text field for the search string. For this binding you will need a binding converter so that the
table knows how to respond to the search string.
First of all, add a label and a text field for the search field as shown below.
Drag the class from the Projects window and drop it in the white area surrounding the form.
3.
You will use this converter when you create the binding.
To create the binding:
1.
In the main form, right-click the Search text field and choose Bind > text.
2.
In the Bind dialog, select masterTable as the binding source and rowSorter as the expression.
3.
4.
5.
Now when you run the application, you should be able to type in the Search Filter field and see that the list
of rows is reduced to only rows that contain text matching what you have typed.
However, adding this search feature creates a side effect. If you use the search and then click New
Customer, an exception appears because the code to select the new row is determined according to number
of records in the database table, not according to the number of records currently displayed in the table.
In the Projects window, right-click the DateVerifier class and choose Compile File.
2.
3.
Drag the DateVerifier class from the Projects window to the white space that surrounds the form.
4.
5.
In the Properties window, click the Properties tab, and select the dateVerifier1 from the combo
box for the Input Verifier property. This property appears within the Other Properties section of the
tab.