EJB CMP/CMR example with JBoss+Xdoclet

14

A concrete example, more details

Our example is taken from my Open Source BibTeXml project. In the JBoss container we have a main list of all publications, containing articles, books, conference proceedings, etc. It is basically a representation of a bookshelf. Let us focus on the entity bean that represents this list and its reference to the article bean, the bean managing all the articles. Aside from containing all publications, the publication bean also contains data that is common to all publications types, e.g. a note or a category field.

The publication bean has the following fields

  • a primary key publicationID (type String, represented by publication_id VARCHAR in the database)
  • a publicationType field (article, book, etc.)

Of course, here I omitted all the fields common to all publications for the sake of clarity.

The article bean has the following fields

  • a foreign-key publicationIDfk referring to the publicationID of the publication bean
  • Other article specific data fields, such as author, title, journal and year

We discuss the relevant code sections of the publication bean, BibliographyBean.java below:

/**
 * @ejb.bean name="Bibliography"
 *        jndi-name="BibliographyBean"
 *        type="CMP"
 *        primkey-field="publicationID"
 *        schema="BibtexmlSchema"
 *        cmp-version="2.x"
 *
 *  @ejb.persistence
 *   table-name="bibliography"
 *
 * @ejb.finder
 *    query="SELECT OBJECT(a) FROM BibtexmlSchema as a"
 *    signature="java.util.Collection findAll()"
 *
 * ... SKIPPED SOME MORE FINDER METHOD DEFINITIONS ...
 **/

Here the primary key field, the associated database column, the database table this bean represents, the EJB-QL schema and some finder methods are defined. Next follows the class definition:

public abstract class BibliographyBean implements EntityBean, BitexmlConstants
{
    protected EntityContext entityContext;

    /**
     * Sets the entityContext
     * @param javax.ejb.EntityContext the new entityContext value
     */
    public void setEntityContext(EntityContext entityContext)
    {
        this.entityContext = entityContext;
    }

    /**
     * Unsets the entityContext
     */
    public void unsetEntityContext()
    {
        this.entityContext = null;
    }

    /**
     * The  ejbCreate method for an article.
     * Only fills the compulsory article fields.
     * Use update to fill the remainder of the article fields, if necessary.
     *
     * @ejb.create-method
     */
    public java.lang.String ejbCreate(String publicationID,
        String author,
        String title,
        String journal,
        Integer year) throws javax.ejb.CreateException
    {
        try
        {
            // Create a new article row in article table using the article entity bean.
            // In the ejbPostCreate() we will take care off the CMR.
            ArticleLocalHome articleLocalHome = ArticleUtil.getLocalHome();
            ArticleLocal article =
                articleLocalHome.create(publicationID, author, title, journal, year);
        }
        catch (NamingException e)
        {
            System.out.println("Articlebean.ejbCreate()" + e);
        }
        // Set bibliography data itself
        setPublicationID(publicationID);
        setPublicationType(ARTICLE);
        return null;
    }

    /**
     * The container invokes this method immediately after it calls ejbCreate.
     *
     */
    public void ejbPostCreate(String publicationID,
                    String author,
                    String title,
                    String journal,
                    Integer year) throws javax.ejb.CreateException
    {
        // Add relation (CMR)
        try
        {
            // Create a new article object
            ArticleLocalHome articleLocalHome = ArticleUtil.getLocalHome();
            setArticle(articleLocalHome.findByPrimaryKey(publicationID));
        }
        catch (NamingException e)
        {
            System.out.println("Articlebean.ejbPostCreate()" + e);
        }
        catch (FinderException e)
        {
            System.out.println("Articlebean.ejbPostCreate()" + e);
        }
    }

Remarks:

  • The System.out.println() is bad programming practice and should be replaced by Log4j calls. At the time of writing the code, this wasn’t operational yet under my JBoss configuration.
  • The errors should preferrably be propagated, instead of handled locally. This way the client cannot find out what went wrong if something did not go well
  • The (container) context is store upon creation, so that it may be retrieved later
  • Note the creation of the BibliographyBean itself (including setting the compulsory fields), the creation of the ArticleBean, and the setting of the CMR field in the Bibliographybean in the ejbPostCreate() method

Finally, we list the definitions of both CMP primary field and CMR field

    /**
     *
     * @return the associated entry in the article table
     *
     * @ejb.interface-method
     *
     * @ejb.relation
     *    name="bibliography-may-contain-article"
     *    role-name="BibliographyToArticle"
     *    cascade-delete="yes"
     *    target-cascade-delete="yes"
     *    target-role-name="ArticleToBibliography"
     *    target-ejb="Article"
     *
     * @jboss.target-relation
     *    related-pk-field="publicationID"
     *    fk-column="publication_id_fk"
     */
    public abstract ArticleLocal getArticle();

    /**
     *
     * @return
     *
     * @ejb.interface-method
     */
    public abstract void setArticle(ArticleLocal article);

    /**
     * Returns the publicationID
     * @return the publicationID
     *
     * @ejb.persistent-field
     * @ejb.persistence
     *    column-name="publication_id"
     *    sql-type="VARCHAR"
     * @ejb.pk-field
     * @ejb.interface-method
     *
     */
    public abstract java.lang.String getPublicationID();

    /**
     * Sets the publicationID
     *
     * @param java.lang.String the new publicationID value
     *
     * @ejb.interface-method
     */
    public abstract void setPublicationID(java.lang.String publicationID);

Apart from the primary key field, all other details have already been address in the previous (abstract/theory) section.

1 2 3 4 5
Share.

About Author

14 Comments

  1. Did it and worked. In fact the CMR element -Container Managed Relations- is an obvious missing point. Do you know where I can get some good sources for CMR?
    Thanks!

  2. Hi everyone:

    I would like to know if there’s possible to generate querys in the .java’s bean with xdoclect, something like Jboss IDE or hibernate. I must define relationships and create new querys and can’t do enything of that with my Lomboz Eclipse plugin and Jboss server.

    I would be greateful at any kind of help

  3. Regarding Comment 4, that’s because it’s after the “@jboss.target-relation” tag, which refers to the blind side of the relation. In the case which you mentioned, one would use the “@jboss.relation” tag, which makes it work from the other side.

    It took me very long to figure this out…somehow most tutorials on the net use that kind of example with the CMR Relation on the blind side, which i personally find not very intuitive for a first example. When trying it with the tags on the side with the foreign key column this produces “unknown column in where query” exceptions.

  4. I did it and it works correctly (after some hacks :o)).
    Now i want to make tests around it. need i use cactus? is there another solution?
    I dont want to kludge without another ejb. :-(
    Thank you
    Breno Leitão.
    http://lcr.icmc.usp.br

  5. I got it!!!!
    “ss� and me have the following problem:
    XDoclet generates an empty {field-name} and {column -name}.
    Here is the reason for that behaviour:
    We got the source via copy and paste from this website. This leads us to the following line:

    related-pk-field="table1ID�

    The closing quotation mark is non ASCII! This confuses XDoclet. Just replace it with the correct one (“) and everything works properly.

    Yours
    Arne

  6. Zeger Hendrikse on

    This example is part of quite a large project in Eclipse, hence it is not possible to compose such a JAR instantly. However, I think it is a very good idea. Moreover, the statistics show that many people are struggling with these issues and are consulting my post.

    Although I cannot promise to deliver the JAR file tomorrow, I’ll do my best to make it available soon. If so, I’ll make a new post, instead of changing this one. Of course, I’ll leave a comment referring to the new post.

    If anyone else already has such a JAR file (preferrably based on my example) and is willing to share his/her efforts with the rest of the world, please let me know.

    Regards,

  7. It would be *very* nice if you can zip a complete working example (inclusive build.xml) and give it for download. Please.
    Yours
    Arne
    Btw.: Your examplanations are very understandable.

  8. I got the same problems as “ss” has.
    It would be *very* nice if you can zip a complete working example (inclusive build.xml) and give it for download. >> Please < <
    Yours
    Arne
    Btw.: Your examplanations are very understandable.

  9. Zeger Hendrikse on

    From your comment I cannot see what you find confusing. I did not post the database schema, since the intention was to let the container create the schema for me. But now that the schema has been created, I can post it, of course!

    An article is created in the Hypersonic database with this SQL statement:

    CREATE TABLE ARTICLE(PUBLICATION_ID_FK VARCHAR(256) NOT NULL,TITLE VARCHAR(256),JOURNAL VARCHAR(256),YEAR INTEGER,VOLUME VARCHAR(256),NUMBER INTEGER,PAGES VARCHAR(256),MONTH VARCHAR(256),NOTE VARCHAR(256),CONSTRAINT PK_ARTICLE PRIMARY KEY(PUBLICATION_ID_FK))
    

    The PUBLICATION_ID_FK is primary key and foreign key at the same time for the article table. This is sound, since we are dealing with a one-to-one relationship here.

    A bibliography is created with:

    CREATE TABLE BIBLIOGRAPHY(PUBLICATION_ID VARCHAR(256) NOT NULL,PUBLICATION_KEY VARCHAR(256),PUBLICATION_TYPE VARCHAR(256),ANNOTATE VARCHAR(256),CROSSREF VARCHAR(256),ABSTRACT VARCHAR(256),AFFILIATION VARCHAR(256),CONTENTS VARCHAR(256),COPYRIGHT VARCHAR(256),ISBN_ISSN VARCHAR(256),KEYWORDS VARCHAR(256),LANGUAGE VARCHAR(256),LCCN VARCHAR(256),LOCATION VARCHAR(256),SIZE INTEGER,MRNUMBER INTEGER,PRICE DECIMAL,URL VARCHAR(256),CATEGORY VARCHAR(256),CONSTRAINT PK_BIBLIOGRAPHY PRIMARY KEY(PUBLICATION_ID))
    

    I hope this helped!

  10. Matt Robinson on

    Looks like you may have things the wrong way around? Isn’t Table 2 actually referring to Table 1? Rather confusing. Also I find the Concrete example even more confusing! It would be better accompanied by a database schema to refer to.

  11. Thanks a lot for the tutorial. however,

    i used the following in my ant build script, but i had problems generating the <key -fields/> in jbosscmp-jdbc.xml.
    And hence i had problems deploying my bean. It threw the following exception.

    org.jboss.deployment.DeploymentException: Atleast one role of a foreign-key mapped relationship must have key fields (or <primkey -field> is missing from ejb-jar.xml): ejb-relation-name=FolderMayContainDevices

    Can you please help.

      &lt;relationships&gt;
        &lt;ejb -relation&gt;
          &lt;ejb -relation-name&gt;FolderMayContainDevices
          &lt;relation -table-mapping&gt;
          &lt;/relation&gt;
    
          &lt;ejb -relationship-role&gt;
              &lt;ejb -relationship-role-name&gt;FolderToDevices
          &lt;ejb -relationship-role&gt;&lt;ejb -relationship-role-name&gt;DeviceToFolder&lt;/ejb&gt;
              &lt;key -fields&gt;
                 &lt;key -field&gt;
                   &lt;field -name&gt;
                   &lt;column -name&gt;
                 &lt;/key&gt;
              &lt;/key&gt;
    
          &lt;/ejb&gt;
        &lt;/ejb&gt;
        &lt;ejb -relation&gt;
          &lt;ejb -relation-name&gt;FolderMayContainFoldersFolderToSubFolders
    		  &lt;key -fields/&gt;
    
          &lt;/key&gt;
          &lt;ejb -relationship-role&gt;&lt;ejb -relationship-role-name&gt;FolderToParentFolder&lt;/ejb&gt;
              &lt;key -fields&gt;
                 &lt;key -field&gt;
                   &lt;field -name&gt;&lt;/field&gt;
                   &lt;column -name&gt;&lt;/column&gt;
                 &lt;/key&gt;
              &lt;/key&gt;
    
          &lt;/ejb&gt;
    
         &lt!--
           To add jboss relationships for beans not managed by XDoclet, add
           a file to your XDoclet merge directory called jbosscmp-jdbc-relationships.xml that contains
           the <ejb -relation> markups for those beans.
         --&gt;
      &lt;/&gt;
    </ejb>

    the build.xml was as follows:

          &lt;taskdef name = "ejbdoclettask"  classname = "xdoclet.modules.ejb.EjbDocletTask"  classpathref="xdoclet.class.path"/&gt;ejbdoclettask destdir="${doclet.gensrc.dir}"  mergedir="parent-fake-to-debug"
                excludedtags="@version,@author,@todo"  addedtags = "@xdoclet-generated at ${TODAY},@copyright The XDoclet T
    eam, @author XDoclet,@version ${version}"   ejbspec="2.0" force="false" verbose="false"&gt;
                            &lt;fileset dir = "${source.dir}"&gt;
                                    &lt;include name = "**/*.java"/&gt;
                            &lt;/include&gt;
    
                            &lt; !-- Comment the next two lines, if you do not want to e
    xpose the Beans via the Remote Interface -->      <remoteinterface pattern= "{0}Remote"/&gt;
                            &lt;homeinterface pattern = "{0}Home"/&gt;
                            &lt;localinterface pattern = "{0}LocalInterface"/&gt;
                            &lt;localhomeinterface pattern = "{0}LocalHome"/&gt;
                            &lt;deploymentdescriptor destdir = "${jar.dir}/META-INF"/&gt;
    
                            &lt;utilobject kind="physical" includeGUID="true"/&gt;
                            &lt;entitypk /&gt;
                            &lt;jboss version="4.0"
                                    destdir="${jar.dir}/META-INF"
                                    validatexml="false"
                                    generateRelations="true"
                                    /&gt;
    
    
            &lt;/jboss&gt;&lt;/entitypk&gt;&lt;/utilobject&gt;&lt;/deploymentdescriptor&gt;&lt;/localhomeinterface&gt;&lt;/localinterface&gt;&lt;/homeinterface&gt;&lt;/remoteinterface&gt;&lt;/jboss&gt;&lt;/entitypk&gt;&lt;/utilobject&gt;&lt;/deploymentdescriptor&gt;&lt;/localhomeinterface&gt;&lt;/localinterface&gt;&lt;/homeinterface&gt;&lt;/remoteinterface&gt;&lt;/ejb&gt;
    </remoteinterface></remoteinterface>
  12. It was the first tutorial I found, which really helped me, to understand, how to make cmr with Xdoclet.
    Thanks for it.
    Janos