BookNBPlatformCookbookCH04

Contents

Files and Data

Data is probably the most important part of any application. From a desktop application to an online game, all have one thing in common, the need to store and retrieve data. The chances are that the more complex your application gets the more likely you need to store data somehow.

So let’s build our data silo...

Use external files

Sometimes your application needs to use external files such as dll’s or external programs that your application will interact with like embedded databases (see Using Databases for more details). In chapter 1 the process on adding external files in the release folder is described. Here we’ll work with those files and interact with them. Also we’ll discuss the internal file system that allows creating, editing and deleting files from your application’s file system.

Preparation

No preparation needed for this recipe.

How to

Retrieve the File

Again, this was covered on chapter 1, but basically this is the important line of code:

InstalledFileLocator.getDefault().locate("sample.exe",
                    "project.com.packtpub.nbpcook.processbuilder", false); 

Let’s assume that our file is an executable that our application needs to call to get some data. An example would be an existing application, usually out of your control, that does what you need. For this purpose we’ll use a ProcessBuilder (http://download.oracle.com/javase/1.5.0/docs/api/java/lang/ProcessBuilder.html). ProcessBuilder is a great way to interact with other applications that might be written on a different language for example. Here’s the code to start the process:

try {
            //Make sure the working directory exists
            File dir = new File(workDir);
            dir.mkdir();

            File exeFile = InstalledFileLocator.getDefault().locate("sample.exe",
                    "project.com.packtpub.nbpcook.processbuilder", false);
            File xmlFile = new File(workDir + System.getProperty("file.separator") + "sample.xml");

            ProcessBuilder processBuilder = new ProcessBuilder(new String[]{
                        exeFile.toString(), xmlFile.getAbsolutePath()});
            exeFile = null;
            String line = null;
            InputStream stderr = null;
            InputStream stdout = null;
            Process process = processBuilder.start();
            stderr = process.getErrorStream();
            stdout = process.getInputStream();

            // clean up if any output in stdout
            BufferedReader brCleanUp = new BufferedReader(new InputStreamReader(stdout));
            String mess = "";
            while ((line = brCleanUp.readLine()) != null) {
                mess += "\n" + line;
            }
            if (!mess.isEmpty()) {
                DialogDisplayer.getDefault().notifyLater(
                        new NotifyDescriptor.Message("[SampleXML] " + mess,
                        NotifyDescriptor.INFORMATION_MESSAGE));
            }
            brCleanUp.close();
            mess = "";
            // clean up if any output in stderr
            brCleanUp = new BufferedReader(new InputStreamReader(stderr));
            while ((line = brCleanUp.readLine()) != null) {
                mess += "\n" + line;
            }
            if (!mess.isEmpty()) {
                DialogDisplayer.getDefault().notifyLater(
                        new NotifyDescriptor.Message("[SampleXML Error] " + mess,
                        NotifyDescriptor.INFORMATION_MESSAGE));
            }
            process.waitFor();
            line = null;
            brCleanUp.close();
            brCleanUp = null;
            process.destroy();
            process = null;
            processBuilder = null;
            exeFile = null;
            stderr = null;
            stdout = null;
            readAndDisplayXML(xmlFile);
        } catch (InterruptedException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }

Keep in mind that this code will wait for the process to finish. So if the process takes some time you might consider running this on a thread instead. This is done at process.waitFor(); The external application, sample.exe, is a C++ project. To run it either alone or from the application your environment needs to be configured properly. See http://netbeans.org/community/releases/69/cpp-setup-instructions.html for detailed instructions. Here’s the output:

File:5863-04-02.png

XML is probable the easiest and more flexible format to store data. See Displaying XML data in a tree for an example on how to process the data and display it to the user.

Let's Explain!

The ProcessBuilder allows Java to create operating system processes. It allows Java to get the process standard and error output streams so we can react to those as well. Basically the external process is ran, we capture any error or normal output and react to it and then we consume the output of that process in our application.

Displaying XML data in a tree

The NetBeans IDE already provide tools to extract data from XML documents so it’s easier to use this format. Let’s say we already have a well formed XML document. Let’s use the output of our application, sample.xml <?xml version="1.0" encoding="UTF-8"?> <sample-tag>

   <some-tag int-attribute="123" double-attribute="456.789">This is the text</some-tag>
   <empty-self-closed-tag/>
   <computed-name-tag text-attr="a bit of text"/>
   <deep-tag>
       
           <sub-tag-3/>
       
   </deep-tag>

</sample-tag> If we open this file in the IDE we can generate a Document Type Definition (DTD: http://en.wikipedia.org/wiki/Document_Type_Definition) document.

File:5863-04-03.png

This will create a DTD document that we’ll need for the reminder of the exercise. We can remove the xml file from the project at this point (unless used somewhere else). Now we can use another wizard to generate a DOM (http://en.wikipedia.org/wiki/Document_Object_Model) tree scanner. Right click on the DTD file and select “Generate DOM Tree Scanner”.

File:5863-04-04.png

The generated scanner is specific to the DTD provided. If the DTD changes the scanner needs to be regenerated/updated. After that you only need to pass an xml file, which conforms to that DTD, to the scanner like this:

        Document doc = null; //the file you want to parse and which conforms to the DTD
                    if (f != null && f.exists()) {
                        try {
                            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                            doc = factory.newDocumentBuilder().parse(f);
                            f = null;
                            factory = null;
                            SampleScanner scanner = new SampleScanner(doc);
                            doc = null;
                            Runtime.getRuntime().gc();
                            scanner.visitDocument();
                            Runtime.getRuntime().gc();
                        } catch (ParserConfigurationException ex) {
                            Exceptions.printStackTrace(ex);
                        } catch (SAXException ex) {
                            Exceptions.printStackTrace(ex);
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        } catch (Exception ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }

Keep in mind that the generated scanner basically doesn’t do anything but should be enough t get you started. For example you can modify it to build a tree for display. Just change the return type of the visit methods to a DefaultMutableTreeNode and follow the example code:

package project.com.packtpub.nbpcook.processbuilder;

import java.beans.PropertyChangeEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import org.babelserver.property.Property;
import org.babelserver.property.PropertyController;

/**
 *
 * @author Javier A. Ortiz Bultron<javier.ortiz.78@gmail.com>
 */
public class SampleScanner implements PropertyController {

    /**
     * org.w3c.dom.Document document
     */
    org.w3c.dom.Document document;

    /**
     * Create new SampleScanner with org.w3c.dom.Document.
     */
    public SampleScanner(org.w3c.dom.Document document) {
        this.document = document;
    }

    /**
     * Scan through org.w3c.dom.Document document.
     */
    public DefaultMutableTreeNode visitDocument() {
        org.w3c.dom.Element element = document.getDocumentElement();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode();
        if ((element != null) && element.getTagName().equals("sample-tag")) {
            newNode.add(visitElement_sample_tag(element));
        }
        if ((element != null) && element.getTagName().equals("some-tag")) {
            newNode.add(visitElement_some_tag(element));
        }
        if ((element != null) && element.getTagName().equals("empty-self-closed-tag")) {
           newNode.add( visitElement_empty_self_closed_tag(element));
        }
        if ((element != null) && element.getTagName().equals("computed-name-tag")) {
            newNode.add(visitElement_computed_name_tag(element));
        }
        if ((element != null) && element.getTagName().equals("deep-tag")) {
            newNode.add(visitElement_deep_tag(element));
        }
        if ((element != null) && element.getTagName().equals("sub-tag-2")) {
            newNode.add(visitElement_sub_tag_2(element));
        }
        if ((element != null) && element.getTagName().equals("sub-tag-3")) {
            newNode.add(visitElement_sub_tag_3(element));
        }
        return newNode;
    }

    /**
     * Scan through org.w3c.dom.Element named sample-tag.
     */
    DefaultMutableTreeNode visitElement_sample_tag(org.w3c.dom.Element element) {
        // <sample-tag>
        // element.getValue();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("sample-tag");
        org.w3c.dom.NodeList nodes = element.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node node = nodes.item(i);
            switch (node.getNodeType()) {
                case org.w3c.dom.Node.CDATA_SECTION_NODE:
                    ((org.w3c.dom.CDATASection) node).getData();
                    break;
                case org.w3c.dom.Node.ELEMENT_NODE:
                    org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
                    if (nodeElement.getTagName().equals("some-tag")) {
                        newNode.add(visitElement_some_tag(nodeElement));
                    }
                    if (nodeElement.getTagName().equals("empty-self-closed-tag")) {
                        newNode.add(visitElement_empty_self_closed_tag(nodeElement));
                    }
                    if (nodeElement.getTagName().equals("computed-name-tag")) {
                        newNode.add(visitElement_computed_name_tag(nodeElement));
                    }
                    if (nodeElement.getTagName().equals("deep-tag")) {
                        newNode.add(visitElement_deep_tag(nodeElement));
                    }
                    break;
                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
                    ((org.w3c.dom.ProcessingInstruction) node).getTarget();
                    ((org.w3c.dom.ProcessingInstruction) node).getData();
                    break;
            }
        }
        return newNode;
    }

    /**
     * Scan through org.w3c.dom.Element named some-tag.
     */
    DefaultMutableTreeNode visitElement_some_tag(org.w3c.dom.Element element) {
        // <some-tag>
        // element.getValue();
        org.w3c.dom.NamedNodeMap attrs = element.getAttributes();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("some-tag");
        for (int i = 0; i < attrs.getLength(); i++) {
            org.w3c.dom.Attr attr = (org.w3c.dom.Attr) attrs.item(i);
            if (attr.getName().equals("double-attribute")) {
                // <some-tag double-attribute="???">
                attr.getValue();
            }
            if (attr.getName().equals("int-attribute")) {
                // <some-tag int-attribute="???">
                attr.getValue();
            }
        }
        org.w3c.dom.NodeList nodes = element.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node node = nodes.item(i);
            switch (node.getNodeType()) {
                case org.w3c.dom.Node.CDATA_SECTION_NODE:
                    ((org.w3c.dom.CDATASection) node).getData();
                    break;
                case org.w3c.dom.Node.ELEMENT_NODE:
                    org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
                    newNode.add(new Property(element.getNodeName(), node.getNodeValue()));
                    break;
                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
                    ((org.w3c.dom.ProcessingInstruction) node).getTarget();
                    ((org.w3c.dom.ProcessingInstruction) node).getData();
                    break;
                case org.w3c.dom.Node.TEXT_NODE:
                    ((org.w3c.dom.Text) node).getData();
                    break;
            }
        }
        return newNode;
    }

    /**
     * Scan through org.w3c.dom.Element named empty-self-closed-tag.
     */
    DefaultMutableTreeNode visitElement_empty_self_closed_tag(org.w3c.dom.Element element) {
        // <empty-self-closed-tag>
        // element.getValue();
        org.w3c.dom.NodeList nodes = element.getChildNodes();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("empty-self-closed-tag");
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node node = nodes.item(i);
            switch (node.getNodeType()) {
                case org.w3c.dom.Node.CDATA_SECTION_NODE:
                    ((org.w3c.dom.CDATASection) node).getData();
                    break;
                case org.w3c.dom.Node.ELEMENT_NODE:
                    org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
                    newNode.add(new Property(element.getNodeName(), node.getNodeValue()));
                    break;
                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
                    ((org.w3c.dom.ProcessingInstruction) node).getTarget();
                    ((org.w3c.dom.ProcessingInstruction) node).getData();
                    break;
            }
        }
        return newNode;
    }

    /**
     * Scan through org.w3c.dom.Element named computed-name-tag.
     */
    DefaultMutableTreeNode visitElement_computed_name_tag(org.w3c.dom.Element element) {
        // <computed-name-tag>
        // element.getValue();
        org.w3c.dom.NamedNodeMap attrs = element.getAttributes();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("computed-name-tag");
        for (int i = 0; i < attrs.getLength(); i++) {
            org.w3c.dom.Attr attr = (org.w3c.dom.Attr) attrs.item(i);
            if (attr.getName().equals("text-attr")) {
                // <computed-name-tag text-attr="???">
                attr.getValue();
            }
        }
        org.w3c.dom.NodeList nodes = element.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node node = nodes.item(i);
            switch (node.getNodeType()) {
                case org.w3c.dom.Node.CDATA_SECTION_NODE:
                    ((org.w3c.dom.CDATASection) node).getData();
                    break;
                case org.w3c.dom.Node.ELEMENT_NODE:
                    org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
                    newNode.add(new Property(element.getNodeName(), node.getNodeValue()));
                    break;
                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
                    ((org.w3c.dom.ProcessingInstruction) node).getTarget();
                    ((org.w3c.dom.ProcessingInstruction) node).getData();
                    break;
            }
        }
        return newNode;
    }

    /**
     * Scan through org.w3c.dom.Element named deep-tag.
     */
    DefaultMutableTreeNode visitElement_deep_tag(org.w3c.dom.Element element) {
        // <deep-tag>
        //element.getValue();
        org.w3c.dom.NodeList nodes = element.getChildNodes();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("computed-name-tag");
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node node = nodes.item(i);
            switch (node.getNodeType()) {
                case org.w3c.dom.Node.CDATA_SECTION_NODE:
                    ((org.w3c.dom.CDATASection) node).getData();
                    break;
                case org.w3c.dom.Node.ELEMENT_NODE:
                    org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
                    if (nodeElement.getTagName().equals("sub-tag-2")) {
                        newNode.add(visitElement_sub_tag_2(nodeElement));
                    }
                    break;
                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
                    ((org.w3c.dom.ProcessingInstruction) node).getTarget();
                    ((org.w3c.dom.ProcessingInstruction) node).getData();
                    break;
            }
        }
        return newNode;
    }

    /**
     * Scan through org.w3c.dom.Element named sub-tag-2.
     */
    DefaultMutableTreeNode visitElement_sub_tag_2(org.w3c.dom.Element element) {
        // <sub-tag-2>
        // element.getValue();
        org.w3c.dom.NodeList nodes = element.getChildNodes();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("sub-tag-2");
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node node = nodes.item(i);
            switch (node.getNodeType()) {
                case org.w3c.dom.Node.CDATA_SECTION_NODE:
                    ((org.w3c.dom.CDATASection) node).getData();
                    break;
                case org.w3c.dom.Node.ELEMENT_NODE:
                    org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
                    if (nodeElement.getTagName().equals("sub-tag-3")) {
                        newNode.add(visitElement_sub_tag_3(nodeElement));
                    }
                    break;
                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
                    ((org.w3c.dom.ProcessingInstruction) node).getTarget();
                    ((org.w3c.dom.ProcessingInstruction) node).getData();
                    break;
            }
        }
        return newNode;
    }

    /**
     * Scan through org.w3c.dom.Element named sub-tag-3.
     */
    DefaultMutableTreeNode visitElement_sub_tag_3(org.w3c.dom.Element element) {
        // <sub-tag-3>
        // element.getValue();
        org.w3c.dom.NodeList nodes = element.getChildNodes();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("sub-tag-3");
        for (int i = 0; i < nodes.getLength(); i++) {
            org.w3c.dom.Node node = nodes.item(i);
            switch (node.getNodeType()) {
                case org.w3c.dom.Node.CDATA_SECTION_NODE:
                    ((org.w3c.dom.CDATASection) node).getData();
                    break;
                case org.w3c.dom.Node.ELEMENT_NODE:
                    org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
                    newNode.add(new Property(element.getNodeName(), node.getNodeValue()));
                    break;
                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
                    ((org.w3c.dom.ProcessingInstruction) node).getTarget();
                    ((org.w3c.dom.ProcessingInstruction) node).getData();
                    break;
            }
        }
        return newNode;
    }

    @Override
    public void propertyChangeCallback(PropertyChangeEvent pce) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}

Here’s the output:

File:5863-04-05.png

Using Databases

This is not intended to teach you everything about databases. Databases are a theme for books on its own so don’t get frustrated if you can’t build the perfect database on your first try. Where needed, you will be pointed to sources of additional information. You can play around with dummy databases for learning purposes, but on a real application you should avoid performing converting a database to code until you have a mature database design. This doesn’t mean that the database will be perfect, none is. But will greatly reduce the amount of rework done in the code if you need to modify the database after you are advanced in the application development. Is a lot easier adding new tables than modifying an existing one. A well designed database will help you attain this easier.

Preparation

You might ask yourself: Why using a database if I can use a file? The answer: You don’t have too. The questions you really need to answer are:

  • How much data do I expect?
  • How often is the data going to be retrieved?
  • Do I need to analyze/process the data?
  • Will multiple users of the application need access to the same data concurrently?
  • Will the date change constantly?

Probably you already figured out where I’m going, performance. If you expect barely any data at all I suggest staying with basic file I/O, but if instead you expect lots of data, often storage and retrieval of it and/or lots of analysis or processing of the data you will need a database at some point.

Database Design

It’s a great idea to identify this need early on the application design process since moving from File to database for data storing can shake your application from its foundation really quick. In fact, it’ll change the design of the application all together. And believe me; you don’t want that kind of headaches late into the game. How to do it... Let’s say that you already have that great database design and you are ready to access it from your application. Adding code to access it from your application is a simple as:

  1. Select the database to connect
  2. Create Java Entities from the database
  3. Use the entities from the application
  4. Create an User Interface to work with the database

I just went to fast. Don’t worry, we’ll go into detail. But believe it or not is basically that simple. Most of the work is done on the database design phase unless you didn’t spend enough time on it. If you didn’t; you’ll regret it. Why? You’ll find errors or discover things that will affect the design of the database. And that will deeply change the code to access it. Now we can go onto explore or discuss key aspects of the topic

Selecting the correct database type

There are many answers to the phrase “types of databases” depending on your point of view. It can be based on the way the data is stored, the purpose or type of data, the physical location of the data (centralized vs. distributed), etc. For us the important thing is where the data will be stored. This divides the database types in two: Distributed (Embedded) and Centralized. If you answer yes to the following you’ll need an embedded database:

  • Is the data irrelevant to the application as a whole, meaning that there’s no need for user A to be aware of user’s B data?
  • Do you need user’s data for your application to work? (i.e. login information, email, etc)
  • The application is not impacted if the data is lost, corrupted or manually modified?

What this means?

  • If answered yes to all questions it means that your data is temporary or only important to the end user.
  • If answered no to one or more but not all questions you probably need either a centralized database or both.
  • If you answered no to any of the above either you need a centralized database.

This is just a guide. At the end of the day is your call. But take this suggestions based on years of database design. Embedded databases An embedded database system is a database management system (DBMS) which is tightly integrated with an application software that requires access to stored data, such that the database system is “hidden” from the application’s end-user and requires little or no ongoing maintenance. Don’t confuse the term embedded with embedded applications (mobile applications for example). The embedded term refers to the database being part of the application. The user is most of the time unaware of its usage. Benefits

  • Reduced setup from for the end user.
  • Easier to predict the database engine used in the application.
  • Easier to update the database engine
  • Application is portable including its data
  • To add the database usually just need to add a file to the application.

Drawbacks

  • Can be removed / modified by the end user either on purpose or by mistake.
  • End user is unable to use the application on the DBMS of his choice.
  • Is harder to integrate with other applications as the database is isolated.

There are various databases from this type that are open sourced. Here are the options with direct support from the NetBeans IDE:

Centralized databases This is the common database management system (DBMS). Benefits

  • It usually provides better performance and scalability.
  • Handles large amount of data.
  • Well tested and supported.

Drawbacks

  • Additional application to install and maintain.
  • Application usually needs to support various versions due to being unable to control the database used.
  • Need to provide it to the users.

Here are the options with direct support from the NetBeans IDE:

There are more available and are easy to use from the IDE. Accessing the Database When you talk about database access there are two major options: JDBC and JPA. There are many benefits and drawbacks for each but for me the key is portability. What if you need for some reason switch DBMS. If your approach was JDBC you might need to change various parts of the code, especially if you use DBMS specific functions. From the other side of the coin there are many performance implications if an abstraction layer is added to overcome this. But the benefits overweight the drawbacks. Being able to use the same code in basically any DBMS is priceless. You can even move from embedded to a regular DBMS with just some changes in the configuration. Usually there’s a module in the application just for this database layer for the following reasons:

  • Easier to update the database if is self contained.
  • You can easily change the database layer without affecting the rest of the application.

Basically the module will be divided as follows:

  • Embedded Database (if you are using embedded database based on Selecting the correct database type section)
  • Entities: Java objects representing the database data.
  • Controllers: Responsible of performing CRUD applications on entities
  • Managers: Utilities to perform complex operations with entities and controllers

Managers

You might argue that Controllers and Managers might be redundant but since Entities and Controllers are generated by the IDE you should prevent modifying those when possible. That will make updates and changes to those easier. Just change the database and re-run the wizards to create the objects. That will keep your manual code changes mostly limited to one module: Managers. This of course adds some configuration but NetBeans make it easier. So let’s start. First we need a project. Create a new NetBeans Platform Application. Call it “Shop”. Create a module named Database. Use example.db as the default package. Make sure to select that the layer is created. Now we need a database. If you installed Java DB with JDK it should be registered in your IDE. The Sample database is more than enough to start and have data for our shop application.

File:5863-04-01.png

At the moment of writing this chapter the template to create Entities from Database is not available from RCP applications. (See Issue 180950) While that’s addressed, you’ll need to use the wizards from a plain Java project. Follow the following steps extracted from a tutorial at http://platform.netbeans.org/tutorials/nbm-crud.html:

  1. In the IDE, choose File | New Project, followed by Java | Java Class Library to create a new library project named DataBaseAccess.
  2. In the Projects window, right-click the library project and choose New | Other, followed by Persistence | Entity Classes from Database. In the wizard, select your database and all the tables. (in this example we’ll use the sample database that ships with Java DB)
  3. Select “Create” as the table generation strategy. We'll choose EclipseLink as the persistence provider if we have multiple options.
  4. Specify "project.com.packtpub.nbpcook.dbaccess" as the name of the package where the entity classes will be generated.
  5. Click Next. Make sure to select Attributes for Regenerating Tables.
  6. Click Finish. Once you have completed this step, look at the generated code and notice that, among other things, you now have a persistence.xml file in a folder called META-INF, as well as entity classes for each of your tables.

Now we’ll create the controller classes.

  1. In the Projects window, right-click the library project and choose New | Other, followed by Persistence | JPA Controller Classes from Entity Classes. In the wizard, select your created entities.
  2. Click Next. Enter “project.com.packtpub.nbpcook.dbaccess.controller” as package name.
  3. Click Finish.
  4. Build the Java Library and you will have a JAR file in the library project's "dist" folder.
  5. Add the resulting jar as a new library module for the Shop application. Name it as DatabaseLayer and use “project.com.packtpub.nbpcook.db.layer” as Code name Base.

At this point the Manager classes mentioned earlier are not required if the operations required for your application are mostly atomic. In most real life cases there are enough common and complex database interactions that a Manager class is needed. For example queries that need interaction with multiple entity types. Basically anything your application will use frequently and is not part of the generated code (Entity/JPAController) should be in one of these classes. I recommend adding unit testing to any class besides Entities and JPAControllers. The use of unit testing this early in the process will both validate your database design and keep your code coverage as high as possible. Notice that at this point in the process, besides any unit test written, almost no code has been written and you have the database layer built! Deployment and Update As your application evolves probably the data it uses will evolve as well. By using JPA the database updates itself. The only issue would be adding standard inserts. This can be done by adding scripts ran during the related module Installer code.

Let's Explain!

JPA provides an additional layer between your application and the database. This additional layer allows your application to work with any supported database without modifying the application itself. To your application the data in the database is just a plain Java object.

Reusing the Platform Database Support

NetBeans already has Database Support. Sometimes you need to provide the same kind of support in your application. Don't reinvent the wheel, let's recycle!

Preparation

Create a Module in your application to hold a database driver of your choice. For this example I'll use MySQL.

Since I'm using Maven, I just need to add this on the module's pom dependencies section:

<dependencies>
        <dependency>
            <groupId>org.netbeans.api</groupId>
            <artifactId>org-netbeans-api-annotations-common</artifactId>
            <version>${netbeans.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.23</version>
        </dependency>
        <dependency>
            <groupId>org.netbeans.api</groupId>
            <artifactId>org-openide-modules</artifactId>
            <version>${netbeans.version}</version>
        </dependency>
        <dependency>
            <groupId>org.netbeans.api</groupId>
            <artifactId>org-openide-util</artifactId>
            <version>${netbeans.version}</version>
        </dependency>
        <dependency>
            <groupId>org.netbeans.api</groupId>
            <artifactId>org-netbeans-modules-db</artifactId>
            <version>${netbeans.version}</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <artifactId>org-openide-windows</artifactId>
            <groupId>org.netbeans.api</groupId>
            <type>jar</type>
            <version>${netbeans.version}</version>
        </dependency>
    </dependencies>

The netbeans.version variable is defined in the parent pom as RELEASE73, the latest release at the date of writing this section.

In the buid /plugins section make sure to configure the module as follows:

             <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>nbm-maven-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <moduleType>autoload</moduleType>
                    <codeNameBase>my.code.name.base/1</codeNameBase>
                    <publicPackages>
                        <publicPackage>com.mysql.*</publicPackage>
                        <publicPackage>org.gjt.mm.*</publicPackage>
                    </publicPackages>
                </configuration>
            </plugin>

This will expose all the driver classes.

Registering a Driver at startup

Now let's create a module activator to do some stuff at loading. See Chapter 1 for details on how to create the installer file. Make add the following to the file:

import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.db.explorer.ConnectionManager;
import org.netbeans.api.db.explorer.DatabaseConnection;
import org.netbeans.api.db.explorer.DatabaseException;
import org.netbeans.api.db.explorer.JDBCDriver;
import org.netbeans.api.db.explorer.JDBCDriverManager;
import org.openide.modules.ModuleInstall;
import org.openide.util.Exceptions;
import org.openide.windows.WindowManager;

public class Installer extends ModuleInstall {

    public static final String mysqlDriverClass = "com.mysql.jdbc.Driver";
    private static final String defaultConnection = "MySQL Template";
    private static final Logger LOG =
            Logger.getLogger(Installer.class.getCanonicalName());

    @Override
    public void restored() {
        WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
            @Override
            public void run() {
                //Check drivers
                if (JDBCDriverManager.getDefault().getDrivers(mysqlDriverClass).length == 0) {
                    try {
                        JDBCDriverManager.getDefault().addDriver(
                                JDBCDriver.create("mysql", "MySQL",
                                mysqlDriverClass,
                                new URL[]{new URL(
                            "nbinst:/modules/ext/mysql/mysql-connector-java-5.1.23.jar")}));
                    } catch (DatabaseException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (MalformedURLException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }
            }
        });
    }
}

The following section of the code, takes care of registering a Driver in the system:

if (JDBCDriverManager.getDefault().getDrivers(mysqlDriverClass).length == 0) {
                    try {
                        JDBCDriverManager.getDefault().addDriver(
                                JDBCDriver.create("mysql", "MySQL",
                                mysqlDriverClass,
                                new URL[]{new URL(
                            "nbinst:/modules/ext/mysql/mysql-connector-java-5.1.23.jar")}));
                    } catch (DatabaseException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (MalformedURLException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }

First checks that the Driver is not already defined and creates it otherwise.

Define a Connection at startup

If we add the following to the Installer code above:

//Check defined database location
                if (ConnectionManager.getDefault().getConnection(defaultConnection) == null) {
                    //None there, create the default
                    if (JDBCDriverManager.getDefault().getDrivers(mysqlDriverClass).length > 0) {
                        try {
                            ConnectionManager.getDefault().addConnection(DatabaseConnection.create(
                                    JDBCDriverManager.getDefault().getDrivers(mysqlDriverClass)[0],
                                    "jdbc:mysql://localhost:3306/test",
                                    "user", "", "password", false,
                                    defaultConnection));
                        } catch (Exception ex) {
                            LOG.log(Level.WARNING, "Error adding connection: " 
                                    + defaultConnection, ex);
                        }
                    }
                }

This will try to register a default connection if not defined already.

//TODO: There's still a bug somewhere that keeps trying to define the connection, even when is already there.

Result

Here's how it looks:

Image:5863-04-06.png

Navigation

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo