JBoss, MySQL, and CMP

Last updated November 30, 2002

Introduction and Prerequisites

A very important feature that is making it into JBoss is the use of generated primary key values (or “unknown keys”) with CMP 2.x entity beans. It is very common in the real world to use features of a backend database to create integer or time-stamped columns that can be efficiently used in indecies to uniquely identify rows (as primary keys). Anyone having experience with the MySQL database engine is familiar with their AUTO_INCREMENT fields.

The problem I was faced with was how to use this feature within JBoss. I found mention of it on the forums, but no concrete examples. Using these hints, some Sun documentation, and the power of Open Source (which allowed me to look into JBoss to see how it expected to be used), I got it working for a simple User Entity EJB.

Before we start, you’ll need the right versions of some software:

  • JBoss 3.2.0beta2 or CVS head (or newer) — this is a bleeding-edge addition to the JBoss engine, so you’ll need the bleeding edge JBoss version. You can get it from their files page at SourceForge.
  • MySQL Connector/J 3 (or newer) — as of this writing, this is the beta version of the driver, but is the only one that implements the getGeneratedKeys() method that JBoss wants.

In the example below, consider a simple User entity bean. It contains a username, password, and some name information. Even though I was going to have unique usernames, I didn’t want to use a CHAR field as an primary key; the performance gain of an integer primary key field is to great to ignore (epecially as a foreign key). The column in my database is named “id”, and I wanted an “id” field in my bean, too.

Writing Your Bean

First, while authoring your bean, you’ll want to pay attention to the specification as it relates to unknown keys. I found a page in their tutorial which mentions 3 key things that need to be done. Generally speaking, you must use java.lang.Object in your code whereever the primary key is mentioned. Specifically, all of your ejbCreate() methods of the bean implementation should return one, and findByPrimaryKey() should take one as an argument.

Secondly comes the deployment descriptor. Again, as in your code, your primary key class will be a java.lang.Object. Here is some of my ejb-jar.xml:

<entity>
  <ejb-name>User</ejb-name>
  <description>
   Core user bean, CMP edition
  </description>
  <home>com.kylev.little.UserHomeRemote</home>
  <remote>com.kylev.little.UserRemote</remote>
  <ejb-class>com.kylev.little.UserBean</ejb-class>
  <persistence-type>Container</persistence-type>
  <prim-key-class>java.lang.Object</prim-key-class>
  <reentrant>False</reentrant>
  <cmp-version>2.x</cmp>
  <abstract-schema-name>User</abstract-schema-name>
  <cmp-field><field-name>username</field></cmp-field>
  <cmp-field><field -name>password</field></cmp-field>
  <cmp-field><field -name>firstName</field></cmp-field>
  <cmp-field><field -name>lastName</field></cmp-field>
...
</entity>

(I left out some parts since I have a query that might confuse the example).

It is worth pointing out that I haven’t omitted things about the primary key or an “id” cmp-field. Those are actually left unspecified on purpose. With no <primkey-field> specified, it falls through to the CMP behaviour which we specified above. In this case, you end up with a field named “id”, in column named “id” of type Integer, populated via an entity-command that knows how to get the value from the backend database.

Configuring CMP

At this point you have a working bean. However, it probably won’t deploy; JBoss will probably complain about the lack of a primary key field. To avoid this problem you need to configure the JBoss CMP Container. This is done via the jbosscmp-jdbc.xml file.

In our case case, we want to have an “id” auto_increment column in the table for my the User. You have to tell CMP about this thusly (the important part is the <unknown-pk>):

<jbosscmp-jdbc>
  <defaults>
   <datasource>java:/LittleDS</datasource>
   <create-table>false</create-table>
   <remove-table>false</remove-table>
   <unknown-pk>
    <unknown-pk-class>java.lang.Integer</unknown-pk-class>
    <field-name>id</field-name>
    <column-name>id</column-name>
    <jdbc-type>INTEGER</jdbc-type>
    <sql-type>INT(11)</sql-type>
    <auto-increment/>
   </unknown-pk>
   <entity-command name="mysql-get-generated-keys"/>
  </defaults>
</jbosscmp-jdbc>

I’ve done this in a <defaults> section, making this unknown key behaviour applicable to all such entity beans in this application. According to the DTD, you could specify <entity> elements here if you want to, overriding this default behaviour. That local definition should override the larger defaults.

It is critical to note that my file has the current DTD at the top of it: <!DOCTYPE jbosscmp-jdbc PUBLIC "-//JBoss//DTD JBOSSCMP-JDBC 3.2//EN" "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_2.dtd">. Without this, JBoss may default to some older DTD version and fail to verify your XML because some of the tags used are new.

Beyond the type definitions, the most important element here is the <entity-command> I used: it tells CMP what plugin to use when creating one of these Entities (this command is defined to reference a plugin in standardjbosscmp-jdbc.xml). Unless you override the JBoss default behaviour (as specified in standardjbosscmp-jdbc.xml in your conf directory), this entity command will use the org.jboss.ejb.plugins.cmp.jdbc.mysql.JDBCMySQLCreateCommand plugin to create entities. Therein lies the MySQL-specific special-sauce that uses the Connector/J getGeneratedKeys() to magically populate your unknown primary key!

This should be enough to get you started. To take this further, you’ll want to check out the other entity commands available to your in standardjbosscmp-jdbc.xml (in conf/), and the DTD for configuring CMP (included as docs/dtd/jbosscmp-jdbc_3_0.dtd in the JBoss distribution).