Chapter 4. Connecting to a Real Database

Modern enterprise applications revolve around relational databases, and our application is no exception. However, up to this point we have barely paid any attention to configuring the all-important database tier. We’ve been using the embedded Hypersonic database and delegating all the schema management to JBoss. This has allowed us to focus on making our application work first, without needing to bother with the added complexity of a database.

Developing an application with this approach makes a lot of sense, but now that our application is up and running, it’s time to turn our attention toward the database, and have our application access a more robust, production-friendly database. We’ll use MySQL for the application.

Setting Up MySQL

MySQL is a free download that is available from http://dev.mysql.com for every major computing platform. You’ll want to download and install the most current MySQL distribution available for your platform. We’ll be working from the MySQL Standard 4.1.8 GA version.

We’ll assume you can follow the instructions for installing MySQL on your platform. Once you have MySQL running, you’ll need to create a database to be used by JBoss. You’ll also need to create a database user that has access to the JBoss database for JBoss to use.

How do I do that?

MySQL is administered using the mysql command found in your MySQL installation. On our machine it is found in /usr/local/mysql/bin/mysql. You’ll want to run with the mysql client as the root user, using the -u option:

    $ /usr/local/mysql/bin/mysql -u root
    Welcome to the MySQL monitor.  Commands end with ; or \g.
    Your MySQL connection id is 4 to server version: 4.1.8-standard

    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

    mysql>

After connecting to the database, we’ll create a database named jbossdb, using the create database command:

Note

If you want to run MySQL on a different host, you need to give the database user access from the host JBoss is running on.

    mysql> create database jbossdb;
    Query OK, 1 row affected (0.01 sec)

Now we need to create a user account to be used when accessing the jbossdb database. The grant command accomplishes this:

    mysql> grant all privileges on jbossdb.* to todoapp@localhost identified
    by 'secretpassword';
    Query OK, 0 rows affected (0.38 sec)

This creates a user called todoapp whose password is secretpassword. This user is granted full rights to everything in the jbossdb database when connecting from the same host. You can examine the mysql.user table to verify that the user was created correctly:

    mysql> select user,host,password from mysql.user;
    +-----------+------------+-------------------------------------------+
    | user      | host       | password                                  |
    +-----------+------------+-------------------------------------------+
    | root      | localhost  |                                           |
    | root      | toki.local |                                           |
    |           | toki.local |                                           |
    |           | localhost  |                                           |
    | todoapp   | localhost  | *F89FFE84BFC48A876BC682C4C23ABA4BF64711A4 |
    +-----------+------------+-------------------------------------------+
    6 rows in set (0.00 sec)

Note that todoapp only has permission to access the database locally, and has no remote access. You can test the username and password using the -u and -password options:

    $ /usr/local/mysql/bin/mysql -u todoapp --password=secretpassword

You can also test remote access to the database by running the mysql command from a remote machine, using the -h option to specify the machine that is running MySQL. Since we’ve only granted access from localhost, the access attempts should fail.

What just happened?

You’ve created a database to be used by JBoss, and you’ve created a user who has permission to access the database. You don’t need to worry about creating special tables or loading any data. JBoss will take care of creating the database tables needed.

Adding the JDBC Driver

As mature as relational database technologies are, there is still no standard network protocol for accessing them. JDBC saves us from needing to couple our application to proprietary database APIs by letting us hide the proprietary access protocols behind a nice, standard Java interface. To do that, we will need to provide the JDBC driver code to JBoss.

How do I do that?

The first step is to locate the JDBC driver code. The MySQL driver is called MySql Connector/J. The current production version is Connector/J 3.0, which you can download from http://www.mysql.com/products/connector/j/. The driver JAR file is located inside of the archive you downloaded. For the 3.0.16 version, which is current as of this writing, the name of the file is mysql-connector-java-3.0.16-ga-bin.jar.

Once you’ve located the JDBC JAR file, you will need to copy it to the lib directory of your JBoss server configuration. As you recall, we are working from the default configuration, so that would be server/default/lib.

Note

The decision to make a library JAR file static or hot deployable is always tough.

What just happened?

Well, nothing happened. We’ve talked about the hot deploy feature of JBoss, but that works only for items placed in the deploy directory. The lib directory works differently. JARs in the lib directory are added to the topmost JBoss classloader and cannot be redeployed or removed. The lib directory is ignored after JBoss starts up. The only way to get JBoss to notice the new JDBC driver is to restart the server, so you’ll need to shut down and restart your JBoss instance after installing the driver.

How do we know that JBoss has found our driver? We would hope that JBoss would give us some kind of warning or error when we try to use the database connection, but that is a very delayed form of feedback when we haven’t yet seen how to use the new database.

There is a solution, though. We’ll use the JMX Console to check on our class. Open the JMX Console in your web browser (http://localhost:8080/jmx-console). Near the top of the screen you will see the JMImplementation domain, which has three managed beans (MBeans), as shown here:

    JMImplementation
      • name=Default,service=LoaderRepository
      • type=MBeanRegistry
      • type=MBeanServerDelegate

Selecting the Loader repository brings you to the management interface for the loader repository (see Figure 4-1).

The loader repository MBean

Figure 4-1. The loader repository MBean

We can use the displayClassInfo operation, which is shown in Figure 4-1, to find out what JBoss knows about the class. The operation takes a Java class name as input, so enter the name of our MySQL driver (com.mysql.jdbc.Driver) and click the Invoke button. The MBean will respond with the following text:

Note

You can use this to check whether any class is visible in JBoss.

    com.mysql.jdbc.Driver Information
    Not loaded in repository cache


    ### Instance0 found in UCL: org.jboss.mx.loading.UnifiedClassLoader3@f8d6a6
    ...

We haven’t used the class yet, so JBoss has not yet loaded the class into memory. However, it can see that the driver class is visible in one of JBoss’s classloaders. If JBoss had already loaded the class, we would have gotten much more information back from the displayClassInfo operation, as shown in the following listing:

    com.mysql.jdbc.Driver Information
    Repository cache version:
    com.mysql.jdbc.Driver(85d49e).ClassLoader=org.jboss.mx.loading.UnifiedClassLoader3@
    f8d6a6{ url=file:/private/tmp/jboss-4.0.2/server/default/tmp/deploy/tmp14828jboss-
    service.xml ,addedOrder=2}
    ..org.jboss.mx.loading.UnifiedClassLoader3@f8d6a6{ url=file:/private/tmp/jboss-
    4.0.2/server/default/tmp/deploy/tmp14828jboss-service.xml ,addedOrder=2}
    ....file:/private/tmp/jboss-4.0.2/server/default/tmp/deploy/tmp14828jboss-service.xml
    ....file:/private/tmp/jboss-4.0.2/server/default/lib/activation.jar
    ....file:/private/tmp/jboss-4.0.2/server/default/lib/avalon-framework.jar
    ....file:/private/tmp/jboss-4.0.2/server/default/lib/bcel.jar
       ...
    ++++CodeSource:
      (file:/private/tmp/jboss-4.0.2/server/default/lib/mysql-connector-
      java-3.0.16-ga-bin.jar )
    Implemented Interfaces:

    ### Instance0 found in UCL: org.jboss.mx.loading.UnifiedClassLoader3@f8d6a6

Note

Class loading can be tricky, but JBoss provides all the tools you need to understand what is going on.

This tells us that we have indeed loaded the MySQL driver into memory, and JBoss is using the version from the JAR file we put in server/default/lib. This might seem like a lot more detail than we want, but when you have different versions of JARs in different packages and are trying to understand exactly how your classes are being loaded, the extra information is invaluable. But for now, we just want to know that JBoss can locate our driver class.

What about...

...placing the JDBC driver in the deploy directory?

It certainly seems harsh that we had to restart JBoss to make it see our little JDBC driver. Why can’t we just put the JDBC driver in the deploy directory and let it be hot deployed? Well, we could have. And it would work almost the same. However, it’s generally accepted that it’s better to put fundamental service code in the lib directory. JAR files in the deploy directory will be redeployed if you accidentally touch them, through an automated build script, for example. That can create havoc if several applications share the JDBC driver.

Note

Wouldn’t it be nice if relational databases had a standard network protocol so that we didn’t have to mess with drivers?

We really just don’t think of a JDBC driver as being an application component. It’s generally thought of as system-level code and is placed with the other system-level JAR files in the lib directory. However, if it’s more convenient, feel free to put the JDBC driver along with your application code in the deploy directory.

Creating a Datasource

With a database running and the drivers loaded, the application could connect directly to the database and issue queries if we wanted it to. But in a J2EE world we don’t need to do that. JBoss, like all J2EE application servers, can manage database connections for us, allowing us to write our applications free of the need to configure connections or maintain connection pools ourselves.

Note

Datasources are shared through JNDI.

Even though we haven’t specified the datasource for the ToDo application, JBoss has connected us to a default datasource called, conveniently enough, DefaultDS. It connects to our embedded Hypersonic database. Now we want to create a new datasource definition that will connect to the MySQL database we created in the first lab.

How do I do that?

In JBoss, a datasource is a deployable entity just like our application WAR and EAR files. It is represented by a special *-ds.xml file in the deploy directory. The Hypersonic datasource is configured in the hsqldb-ds.xml file. We’ll look at that as an example. Here’s an abbreviated version of it:

    <?xml version="1.0" encoding="UTF-8"?>

    <datasources>
       <local-tx-datasource>
          <jndi-name>DefaultDS</jndi-name>

          <connection-url>
              jdbc:hsqldb:${jboss.server.data.dir}${/}hypersonic${/}localDB
          </connection-url>
          <driver-class>org.hsqldb.jdbcDriver</driver-class>

          <user-name>sa</user-name>
          <password></password>

          <min-pool-size>5</min-pool-size>
          <max-pool-size>20</max-pool-size>
          <idle-timeout-minutes>0</idle-timeout-minutes>
          <track-statements/>

          <security-domain>HsqlDbRealm</security-domain>

          <metadata>
              <type-mapping>Hypersonic SQL</type-mapping>
          </metadata>

          <depends>jboss:service=Hypersonic,database=localDB</depends>
       </local-tx-datasource>
    </datasources>

Note

External clients can’t look up a datasource and use it.

While there is a bit of noise in the file, it’s pretty easy to guess what is going on. This datasource is named DefaultDS. It’s bound under the JNDI name java:DefaultDS, meaning that it, like everything in the java: context, is only available inside the JVM. You can also see the connection URL and the database username and password, as well as a few other minor connection pool details.

It’s really a simple format, and it would be pretty easy to duplicate this file, adding the connection details for our MySQL database. But it’s even easier than that. The docs/examples/jca directory (under the root of your JBoss installation, not the server configuration directory you are working in) contains sample templates for the most popular database servers (and most of the less popular ones too).

The MySQL template is mysql-ds.xml:

    <?xml version="1.0" encoding="UTF-8"?>

    <!-- $Id: ch04.xml,v 1.5 2005/08/05 18:55:20 ellie Exp $ -->
    <!--  Datasource config for MySQL using 3.0.9 available from:
    http://www.mysql.com/downloads/api-jdbc-stable.html
    -->

    <datasources>
      <local-tx-datasource>
        <jndi-name>MySqlDS</jndi-name>
        <connection-url>jdbc:mysql://mysql-hostname:3306/jbossdb
        </connection-url>
        <driver-class>com.mysql.jdbc.Driver</driver-class>
        <user-name>x</user-name>
        <password>y</password>

          <!-- corresponding type-mapping in the
               standardjbosscmp-jdbc.xml (optional) -->
          <metadata>
             <type-mapping>mySQL</type-mapping>
          </metadata>
      </local-tx-datasource>

    </datasources>

To get started, you need to copy mysql-ds.xml to the deploy directory and change the items in bold to the correct values for the server. The MySQL JNDI name is fine, so we’ll start with connection-url. We’ll assume that our database is located on the same machine as our JBoss instance, so we’ll use localhost as the machine name.

    <connection-url>jdbc:mysql://localhost:3306/jbossdb</connection-url>

The only other step is to tell JBoss the database user to connect as. We used the user todoapp, so we’ll specify that, and the password, here:

    <user-name>todoapp</user-name>
    <password>secretpassword</password>

What just happened?

You’ve now deployed a datasource in JBoss. You can look to the console log to make sure everything went right. If all went well, you’ll see a line that looks like the following:

    20:09:23,756 INFO [WrapperDataSourceService] Bound connection factory for resource
    adapter for ConnectionManager 'jboss.jca name=MySqlDS,service=DataSourceBinding to
    JNDI name 'java:MySqlDS'

Note

You should profile your application to determine the optimal connection pool size.

If you’ve edited and saved the file a few times, the console should show that you’ve redeployed it several times. We’ll make one more change, explicitly specifying the datasource connection pool size, and watch it redeploy.

Since we don’t expect much traffic, we’ll ask JBoss to keep just two connections ready in the pool, and to keep a maximum of ten connections open. To do that, add the following min-pool-size and max-pool-size elements to your mysql-ds.xml file:

    <?xml version="1.0" encoding="UTF-8"?>

    <!-- $Id: ch04.xml,v 1.5 2005/08/05 18:55:20 ellie Exp $ -->
    <!--  Datasource config for MySQL using 3.0.9 available from:
    http://www.mysql.com/downloads/api-jdbc-stable.html
    -->
    <datasources>
        <local-tx-datasource>
            <jndi-name>MySqlDS</jndi-name>
            <connection-url>jdbc:mysql://mysql-hostname:3306/jbossdb
            </connection-url>
            <driver-class>com.mysql.jdbc.Driver</driver-class>
            <user-name>todoapp</user-name>
            <password>secretpassword</password>

            <min-pool-size>2</min-pool-size>
               <max-pool-size>10</max-pool-size>

            <!-- corresponding type-mapping in the
                 standardjbosscmp-jdbc.xml (optional) -->
            <metadata>
                <type-mapping>mySQL</type-mapping>
            </metadata>
        </local-tx-datasource>

    </datasources>

When you save the file you’ll see that the datasource successfully redeployed in the console log. This is all the feedback JBoss gives you directly. In the "Monitoring the Connection Pool" lab later in this chapter, we’ll see how to verify our pool size and check on other aspects of the connection pool, such as how many connections are in use. For a full set of configuration options, see jboss-ds_1_5.dtd in the docs/dtd directory. Other template datasource descriptors are located in docs/examples/jca.

What about...

...making our datasource be DefaultDS?

That is possible. If we removed hsql-ds.xml and changed our datasource JNDI name to DefaultDS, JBoss would use our MySQL database as the default for all applications. However, keep in mind that internal services such as JMS use DefaultDS. While they can be easily configured to use other databases, and indeed they should be if we are deploying a production server using those services, it’s generally better to be explicit about which datasource you are using.

Note

XA datasources are a real pain. Just because your database and driver claim to support XA transactions doesn’t mean it really works. If you plan to use XA transactions, make sure to test them thoroughly.

And what about XA datasources?

You might be wondering what the local-tx-datasource element means in the datasource file. It means that the datasource only supports local transactions and can’t be combined with other resources in a true distributed (XA) transaction. If your database supports XA (MySQL doesn’t), you can change the local-tx-datasource tag to xa-datasource. Examples of XA datasource configurations are available in doc s/examples/jca and are named *-xa-ds.xml.

Linking the Datasource to Our Application

With the datasource in place, we need to tell our application to use it. The amount of effort involved depends on how coupled our application is to the datasource. If an application used the datasource directly, we would need to adjust the lookup code, or at least the environment references, and update our SQL statements to be compatible with MySQL. Fortunately for us, the ToDo application uses container-managed persistence and the database access is completely transparent. So, migrating the application will be quite simple.

How do I do that?

This one is going to be easy. JBoss looks for the container-managed persistence configuration in a jbosscmp-jdbc.xml deployment descriptor. The default datasource for the application is set in the defaults section of the file, as shown here:

    <!DOCTYPE jbosscmp-jdbc PUBLIC
        "-//JBoss//DTD JBOSSCMP-JDBC 4.0//EN"
        "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_4_0.dtd">

    <jbosscmp-jdbc>
        <defaults>
               <datasource>java:MySqlDS</datasource>
               <datasource-mapping>mySQL</datasource-mapping>
               </defaults>
    </jbosscmp-jdbc>

The datasource element contains the JNDI name of the datasource we specified in the mysql-ds.xml file earlier. The datasource-mapping name declares the type of the database. The database type determines how to generate SQL and how to map types for a particular database.

You can look at the conf/standardjbosscmp-jdbc.xml file to see the full set of datasource mappings available in JBoss. Inside the type-mappings section, you should find the mySQL type mapping referenced in the jbosscmp-jdbc.xml file:

    <type-mapping>
        <name>mySQL</name>
        ...
    </type-mapping>

The type mapping defines SQL templates for schema generation, EJB-QL, and JBOSS-QL function mappings, as well as the default mapping for standard Java types. Here is the mapping for java.lang.String:

    <mapping>
        <java-type>java.lang.String</java-type>
        <jdbc-type>VARCHAR</jdbc-type>
        <sql-type>VARCHAR(250) BINARY</sql-type>
    </mapping>

If you need to change the mapping for strings for all MySQL databases in the system, you can edit the standardjbosscmp-jdbc.xml file. In general you’ll probably want to edit the mapping on a field-by-field basis, but it is nice to know that you have the power to edit the systemwide mapping if need be.

The jbosscmp-jdbc.xml file is placed in the META-INF directory of the EJB JAR, right next to the ejb-jar.xml and jboss.xml deployment descriptors. In order to include the MySQL-specific jbosscmp-jdbc.xml file, add -Doptional.dd=mysql to the ant commands used to build the ToDo application, as shown here:

    $ ant -Doptional.dd=mysql main deploy

This builds and deploys the MySQL version of the application.

What just happened?

The ToDo application is no longer speaking to the internal Hypersonic database. Instead it is speaking to the MySQL instance you set up. And just as before, JBoss automatically created the database schema appropriate for our entity beans. You can check to see that our tables were created correctly using the show tables command:

    mysql> use jbossdb;
    Database changed
    mysql> show tables;
    +-------------------+
    | Tables_in_jbossdb |
    +-------------------+
    | Comment           |
    | Task              |
    +-------------------+
    2 rows in set (0.19 sec)

You can check the generated schema for the table using the describe command:

    mysql> describe task;
    +---------------+--------------+------+-----+---------+-------+
    | Field         | Type         | Null | Key | Default | Extra |
    +---------------+--------------+------+-----+---------+-------+
    | id            | varchar(250) |      | PRI |         |       |
    | name          | varchar(250) | YES  |     | NULL    |       |
    | user          | varchar(250) | YES  |     | NULL    |       |
    | startedDate   | datetime     | YES  |     | NULL    |       |
    | completedDate | datetime     | YES  |     | NULL    |       |
    +---------------+--------------+------+-----+---------+-------+
    5 rows in set (0.42 sec)

If you access the application and create a few tasks, you can easily query the data from the mysql tool and verify that everything is working:

    mysql> select name,user,startedDate from task;
    +---------------------+-------+---------------------+
    | name                | user  | startedDate         |
    +---------------------+-------+---------------------+
    | take over the world | brain | 2005-01-18 00:50:29 |
    | eat the box         | pinky | 2005-01-18 00:51:22 |
    +---------------------+-------+---------------------+
    2 rows in set (0.20 sec)

Monitoring the Connection Pool

We’ve spent all our time looking at how to create the datasource, but we haven’t mentioned how you can interact with the underlying connection pool during the life of your application. You need to set up the datasource only once, but your application will use it every day. In this lab we will find the connection pool and see how to interact with its management interface to monitor and tune it.

How do I do that?

Each datasource declared in a ds.xml file translates into several MBeans that you can interact with in the JMX Console. All the datasource-related objects are in the jboss.jca domain. You can find them by searching through the JMX Console page, or by using jboss.jca:* as the query filter.

This is enough of a filter to spot the items related to the MySQL datasource, but you could use a more specific filter, such as jboss.jca:name=MySqlDS,*, to see only the MySQL entries. In either case, four MBeans will be related to the MySQL datasource:

  • name=MySqlDS,service=DataSourceBinding

  • name=MySqlDS,service=LocalTxCM

  • name=MySqlDS,service=ManagedConnectionFactory

  • name=MySqlDS,service=ManagedConnectionPool

While each plays a critical role in providing the datasource functionality in JBoss, you are most likely to need to interact with the connection pool. Click the connection pool MBean to expose the management attributes and operations.

The mysql-ds.xml file we’ve been using specified a minimum connection pool size of 2 and a maximum pool size of 10. You’ll see those values reflected in the MinSize and MaxSize attributes. You can change the values in the running server by adjusting the values and clicking Apply Changes.

Setting the values here affects the connection pool only in memory. To change the configuration permanently, update the mysql-ds.xml file. Try setting the pool sizes there. When you save the file, JBoss will redeploy the datasource and the new pool sizes will be displayed when you reload the page.

You might occasionally want to adjust the pool size to account for usage; you are more likely to be curious how much of the connection pool is being used. The ConnectionCount attribute shows how many connections are currently open to the database. However, open connections are not necessarily in use by application code. The InUseConnectionCount attribute shows how many of the open connections are in use. Viewing the statistic from the other direction, AvailableConnectionCount shows how much room is left in the pool.

Finally, the MBean has several statistics that track connection pool usage over the pool’s lifetime. ConnectionCreatedCount and Connection-DestroyedCount keep running totals of the number of connections created and destroyed by the pool. If IdleTimeout is greater than 0, connections will eventually timeout, be destroyed, and be replaced by fresh connections. This will cause the created and destroyed counts to rise constantly. The MaxConnectionsInUseCount attribute keeps track of the highest number of connections in use at a time.

If you notice anything awkward in the connection pool, or you just want to reset the statistics, you can flush the connection pool using the flush operation on the MBean. This will cause a new connection pool to be created, abandoning the previous connection pool.

What just happened?

You’ve seen a whole set of attributes that will prove invaluable when you need to understand how an application is using its database connections. All the MBean attributes are visible through the JMX management tool of your choice. We’ve used the JBoss JMX Console in this lab, but other options exist.

Note

We will use some of the more advanced monitoring and alerting features of the web console in Chapter 8.

To give you a taste of what you can do, Figure 4-2 shows a graph of the ConnectionCount attribute over a short, 10-minute time period that we used the ToDo application. The graph was created by the JBoss Web Console application, the GUI companion to the JMX Console we’ve been using.

A graph of a datasource connection count

Figure 4-2. A graph of a datasource connection count

Get JBoss: A Developer's Notebook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.