Monday February 11, 2008
Beans Binding Via The Road Less Travelled By (Part 1)
At the end of my blog entry yesterday, Mark Ashworth writes: "I would just like to know if there is a demo or tutorial on how to use binding with List and JTable." Well, Mark, especially for you I've created a small yet powerful demo that relies heavily on beans binding (JSR-295). It could also have made use of the Swing Application Framework (JSR-296), but, so as not to confuse things, I've not touched that area at all here.
At the end of this blog entry, you'll have a JFrame that contains a JTable. The JTable is filled with data from a database. Whenever you select a row in the JTable, the JTextFields below the JTable are filled with the data from the currently selected row:
And no code was typed to make all of this functionality possible. However, in the explanation that follows, we will ONLY use the code editor. Nothing will be generated. And, again, I could have used a Swing Application Framework template to generate all of the above (plus a lot more, such as CRUD functionality). However, let's only look at beans binding. We'll also assume that you are NOT using the Matisse GUI Builder, either because you don't like NetBeans IDE or you don't like having your code generated or because you actually want to learn what all the code does or some other reason. So everything will be hand coded. Let's get started.
- First, we create the JFrame that will contain all the other Swing components:
public class DemoJFrame extends JFrame { public DemoJFrame() { setTitle("Demo Frame"); setSize(400,400); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); } public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new DemoJFrame().setVisible(true); } }); } }Run the JFrame and you see a nice pristine space where all the action will take place.
- Next, you need to manually set up your database connection, which is not much fun, especially when you consider that all this can automatically be done for you. However, to set up the database, you need the following JARs on your classpath (again, these would be put there for you if you were to use the IDE's tools instead of doing it manually): beansbinding-1.2.1.jar, toplink-essentials.jar, toplink-essentials-agent.jar and derbyclient.jar. Next, you need an entity class called Customer.java, to represent the data in your database, which you can download here. Finally, for this step, you need to have a database started up and you need an XML file named "persistence.xml", in your application's META-INF folder. The content of the XML file should be as follows, assuming you're using the 'sample' database that comes with NetBeans IDE (so, you'd need to tweak it, as well as the Customer.java class, for your own purposes):
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="samplePU" transaction-type="RESOURCE_LOCAL"> <provider>oracle.toplink.essentials.PersistenceProvider</provider> <class>demoframe.Customer</class> <properties> <property name="toplink.jdbc.user" value="app"/> <property name="toplink.jdbc.password" value="app"/> <property name="toplink.jdbc.url" value="jdbc:derby://localhost:1527/sample"/> <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/> </properties> </persistence-unit> </persistence> - Now, having dealt with the formalities, let's create a JTable and bind its columns to columns in our database. Let's start by declaring some fields and variables:
private java.util.List
customerList; private javax.persistence.Query customerQuery; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTable jTable1; private javax.persistence.EntityManager samplePUEntityManager; private org.jdesktop.beansbinding.BindingGroup bindingGroup; Then, here is the actual code, which all belongs in the constructor, below the three lines we added previously. Note the pattern by which things happen: we create a binding group, we add other binding groups to it, within which we add bindings. The bindings use expression language to link to database column names, they also specify table column names and the type of the data.
//Create new binding group: bindingGroup = new BindingGroup(); //Persistence manager for our database: samplePUEntityManager = javax.persistence.Persistence.createEntityManagerFactory("samplePU").createEntityManager(); //Query that we call on the persistence manager: customerQuery = samplePUEntityManager.createQuery("SELECT c FROM Customer c"); //List that results from our query: customerList = customerQuery.getResultList(); //JScrollPane that will contain our table: jScrollPane1 = new javax.swing.JScrollPane(); //JTable that will contain our data: jTable1 = new javax.swing.JTable(); //Create the JTable binding: JTableBinding jTableBinding = SwingBindings.createJTableBinding( AutoBinding.UpdateStrategy.READ_WRITE, customerList, jTable1); //Add the first column binding to our table binding: ColumnBinding columnBinding = jTableBinding.addColumnBinding(ELProperty.create("${name}")); columnBinding.setColumnName("Name"); columnBinding.setColumnClass(String.class); //Add the second binding to our table binding: columnBinding = jTableBinding.addColumnBinding(ELProperty.create("${city}")); columnBinding.setColumnName("City"); columnBinding.setColumnClass(String.class); //Add the final binding to our table binding: columnBinding = jTableBinding.addColumnBinding(ELProperty.create("${zip}")); columnBinding.setColumnName("Zip"); columnBinding.setColumnClass(String.class); //Add our table binding to the binding group: bindingGroup.addBinding(jTableBinding); //Bind the table binding: jTableBinding.bind(); //Set the JTable in the JScrollPane's view port view: jScrollPane1.setViewportView(jTable1); //Add the JScrollPane in the North: getContentPane().add(jScrollPane1, java.awt.BorderLayout.NORTH); //Bind the whole group: bindingGroup.bind(); //Pack: pack();If you run the above, assuming you've actually gone through the pain of setting up the database manually, you'll see the columns in the database bound to the columns in the table. Now experiment a bit! For example, change the expression ${city} to ${email} which, if you look in your Customer.java class, maps to the "email" column in the table. Then run the application again and the City column is filled with e-mail data.
- Now we'll add our JTextFields. These we will bind to the columns in the table so that, whenever a row is selected, the fields will be filled with the data from the selected row.
First, declare the new Swing components that we will work with:
private javax.swing.JTextField jTextField1; private javax.swing.JTextField jTextField2; private javax.swing.JTextField jTextField3; private javax.swing.JPanel jPanel1;
Next, below the line that adds the JScrollPane to the South, add all the following code, simply for adding a JPanel with our three JTextFields:
jTextField1 = new javax.swing.JTextField(); jTextField2 = new javax.swing.JTextField(); jTextField3 = new javax.swing.JTextField(); jPanel1 = new javax.swing.JPanel(); jTextField1.setText(""); jTextField1.setColumns(10); jPanel1.add(jTextField1); jTextField2.setText(""); jTextField2.setColumns(10); jPanel1.add(jTextField2); jTextField3.setText(""); jTextField3.setColumns(10); jPanel1.add(jTextField3); getContentPane().add(jPanel1, java.awt.BorderLayout.SOUTH); - And, finally, we add the bindings. These will bind our JTextFields to columns in the table. The additions to the code I have marked in bold below:
jTextField1.setText(""); jTextField3.setEditable(false); jTextField1.setColumns(10); Binding binding1 = Bindings.createAutoBinding( AutoBinding.UpdateStrategy.READ_WRITE, jTable1, ELProperty.create("${selectedElement.name}"), jTextField1, BeanProperty.create("text")); bindingGroup.addBinding(binding1); jPanel1.add(jTextField1); jTextField2.setText(""); jTextField3.setEditable(false); jTextField2.setColumns(10); Binding binding2 = Bindings.createAutoBinding( AutoBinding.UpdateStrategy.READ_WRITE, jTable1, ELProperty.create("${selectedElement.city}"), jTextField2, BeanProperty.create("text")); bindingGroup.addBinding(binding2); jPanel1.add(jTextField2); jTextField3.setText(""); jTextField3.setEditable(false); jTextField3.setColumns(10); Binding binding3 = Bindings.createAutoBinding( AutoBinding.UpdateStrategy.READ_WRITE, jTable1, ELProperty.create("${selectedElement.zip}"), jTextField3, BeanProperty.create("text")); bindingGroup.addBinding(binding3); jPanel1.add(jTextField3);For what it's worth, here's a full list of my import statements:
import javax.swing.JFrame; import org.jdesktop.beansbinding.AutoBinding; import org.jdesktop.beansbinding.BeanProperty; import org.jdesktop.beansbinding.Binding; import org.jdesktop.beansbinding.BindingGroup; import org.jdesktop.beansbinding.Bindings; import org.jdesktop.beansbinding.ELProperty; import org.jdesktop.swingbinding.JTableBinding; import org.jdesktop.swingbinding.JTableBinding.ColumnBinding; import org.jdesktop.swingbinding.SwingBindings;
I know I haven't explained everything in detail. I'll do that another time (once I know what those details are). On top of that, though, I think the code is relatively easy to read. You should be able to figure out what is happening, in most cases. And remember: every single piece of code that you see above, every line, every JAR, everything, is generated for you in the IDE. You do not need to go through any of this at all. Unless, of course, you want to really understand beans binding!
Feb 11 2008, 01:58:45 PM PST Permalink
Here is another request. What is the best way of approaching a Netbeans RCP application which reads data off a JMS Bus, TCP/IP connection, etc., and populates a JTable (or whatever netbeans uses)?
I would like to try building a small stock trading application using Netbeans, which requires LOTS of streaming data, binding, etc. Do I introduce the streaming data sources as some sort of services or plug-ins?
Posted by falcon on February 11, 2008 at 08:04 PM PST #
That question has nothing to do with this blog entry and I don't know the answer. Please write to dev@openide.netbeans.org and someone there will probably have implemented something similar.
Posted by Geertjan on February 11, 2008 at 11:15 PM PST #
jTableBinding.bind();
...
bindingGroup.bind();
Is this really needed? Why I must order 2 bindings if the tableBinding was asigned to the groupBinding?
Posted by peregrino on February 18, 2008 at 01:15 AM PST #
How do you make the column bindings using Netbeans IDE only? If I look at the properties, bindings of a table I can only bind things like elements or selectedElement(s)? The columns tab also doesn't appear to contain anything to do with bindings?
Could the same technique be used with the swinglabs JXTable implementation?
Posted by Richard Osbaldeston on February 19, 2008 at 03:40 AM PST #
Oh think I've found the limits myself. I was being blocked by a "can't import data to the default package" message. So you can only bind columns to a database table? I'm using RMI (raw objects/pojos) guess the existing Netbeans editor can't help here?
Posted by Richard Osbaldeston on February 19, 2008 at 03:48 AM PST #
2Richard, from what I have understood at SwingX forums, yes, you should be able to bind to JXTable too.
Also, I would be interested in replies to Richard Osbaldeston's questions regarding binding POJOs in NB. I managed to do that "manually" but not in NB binding editor.
Related to POJOs is my question - did anyone got working create and delete from underlying table model, i.e. DefaultTableModel or class extending that. If so, how? The binding does not seem to listen on table model events...
Posted by Dale Cooper on February 19, 2008 at 05:23 PM PST #
I wrote something for NetBeans Zone today that attempts to answer some of the questions here. If it doesn't feel free to leave questions at the end of that article:
http://netbeans.dzone.com/news/binding-jtable-swing-controls-
Posted by Geertjan on February 20, 2008 at 12:39 AM PST #
Hi!
Thanks for your tutorial!
I did the same within a Netbeans RCP App, but when i write text into one of my TextFields, the corresponding Table Column doesn't get updated.
When i do the same outside Netbeans RCP Platform everything works perfect....
Whats wrong within Netbeans RCP?
Best Regards,
christian
Posted by Christian on April 10, 2008 at 02:50 AM PDT #
Has you tried this to work with EclipseLink JPA, resource local, i.e. the javax.persistence_1.0.0.jar?
Can you suggest a site that shows how to migrate toplink-essentials 1.0 to eclipselink?
By the way, this is a really informative example ... Some of us are using Java SE with JPA, and not just for testing.
Posted by Mike Rainville on August 12, 2008 at 03:48 PM PDT #


