EJB m:n relationship in JBoss + Xdoclet

9

As a (promised) follow-up on the uni-direcional CMP/CMR example in EJB CMP/CMR example with JBoss+Xdoclet, we present a many-to-many (m:n) relationship example here, albeit in much less detail. For more details the reader is referred to the above post.

A many-to-many entity bean relationship

In EJB CMP/CMR example with JBoss+Xdoclet, there was an example of a bibliography, that may contain articles, books, etc. Now a book/article can be written by one or more authors, and an author may have written one or more books/articles, which clearly is m:n. Normalizing the BibTeXml schema in a relational database, the authors have to move to a separate table. In addition, this allows for a separation of initials, names, titles fields etc., which in turn enhances the flexibility of the output. But let’s stay on-topic.

How do we establish such a many-to-many relation with Xdoclet (in a JBoss 3.2.x environment)?

Let’s first have a look at the relevant CMR field of the BookBean.java

    /**
     * CMR Collection containing authors information
     *
     * @return the authorOReditors
     *
     * @ejb.interface-method
     *
     * @ejb.relation
     *      name="Book-Author"
     *      role-name="Book-written-by-Author"
     *      target-multiple="yes"
     *
     * @jboss.relation-mapping
     *      style="relation-table"
     *
     * @jboss.relation-table
     *      table-name="BookAuthor"
     *      create-table="true"
     *      remove-table="false"
     *
     * @jboss.relation
     *      related-pk-field="authorID"
     *      fk-column="publication_id_fk"
     *      fk-constraint="false"
     *
     */
    public abstract Collection getAuthors();

    /**
     * @param java.util.Set the new authors value
     *
     * @ejb.interface-method
     */
    public abstract void setAuthors(Collection authors);

Comments:

  • The collection contains AuthorLocal objects, AuthorLocal being generated by Xdoclet
  • The many-to-many character of our relationship is indicated by the target-multiple="yes" field of the @ejb.relation tag (for more details, see my previous post on this subject)
  • The linking table needed for the m:n relationship, is automatically generated by JBoss, if it does not exist already and is indicated by the @jboss.relation-mapping tag
  • Information on the linking table created by JBoss is contained in the @jboss.relation-table tag. The table is created if it didn’t exists already, but is not removed during un/redeployment (remove-table="false")
  • The @jboss.relation has already been discussed extensively in my previous post

On “the other side”, we have to define a matching relationship:

    /**
    * CMR Collection containing book information
    *
    * @return the book
    *
    * @ejb.interface-method
    *
    * @ejb.relation
    *      name="Book-Author"
    *      role-name="Author-writes-books"
    *      target-multiple="yes"
    *
    * @jboss.relation-table
    *      table-name="BookAuthor"
    *      create-table="true"
    *      remove-table="false"
    *
    * @jboss.relation-mapping
    *      style="relation-table"
    *
    * @jboss.relation
    *      related-pk-field="publicationIDfk"
    *      fk-column="author_id"
    *      fk-constraint="false"
    */
   public abstract Collection getBooks();

   /**
    * @param java.util.Set the new books value
    *
    * @ejb.interface-method
    */
   public abstract void setBooks(Collection books);

Comments:

  • The name of the @ejb.relation must be the same on both sides
  • Look carefully at at the way in which the @jboss.relation fields are defined
1 2
Share.

About Author

9 Comments

  1. Pingback: Credit Monitoring Services

  2. Pingback: Student Credit Cards

  3. Pingback: File Cabinets

  4. Pingback: Furniture Guys

  5. I’ve been trying to deploy a one to many relationship for two whole, I mean WHOLE days. ( I have to admit I did sleep), but no luck. Here is my code. I would appreciate your help.

    I’m using the current maxdb, jboss-3.2.1. I’m sure I am doing something really wrong that is not obvious to me. One of the things that I noticed is that ejb-jar.xml does not have the key-field auto generated.

    /*
    * Created on Dec 29, 2005
    *
    * To change the template for this generated file go to
    * Window>Preferences>Java>Code Generation>Code and Comments
    */
    package ejb;

    import javax.ejb.EntityBean;
    import javax.ejb.EntityContext;
    import java.util.Collection;

    /**
    * @ejb.bean name=”A”
    * jndi-name=”ABean”
    * type=”CMP”
    * primkey-field=”a_id”
    * schema=”a”
    * cmp-version=”2.x”
    *
    *–
    * This is needed for JOnAS.
    * If you are not using JOnAS you can safely remove the tags below.
    * @jonas.bean ejb-name=”A”
    * jndi-name=”ABean”
    * @jonas.jdbc-mapping jndi-name=”java:/SapdbDS” jdbc-table-name=”a”
    * –
    *
    * @ejb.persistence
    * table-name=”a”
    *
    * @ejb.finder
    * query=”SELECT OBJECT(a) FROM a as a”
    * signature=”java.util.Collection findAll()”

    *–
    * This is needed for JOnAS.
    * If you are not using JOnAS you can safely remove the tags below.
    * @jonas.finder-method-jdbc-mapping method-name=”findAll”
    * jdbc-where-clause=”"
    * @jonas.jdbc-mapping jndi-name=”java:/SapdbDS”
    * jdbc-table-name=”a”
    *
    *–
    *
    **/

    public abstract class ABean implements EntityBean {
    protected EntityContext ctx = null;

    // business methods
    /* public void addB() {

    BLocal b = null;

    try {
    InitialContext BContext = new InitialContext( );
    BLocalHome bHome = (BLocalHome)
    BContext.lookup(“java:comp/env/ejb/B”);
    b = bHome.create(getId());
    } catch (Exception e) {
    System.out.println(“exception @ BContext”);
    }

    Collection bCollection = this.getBBean( );
    bCollection.add(b);

    }
    */
    /**
    * CMR Collection containing bbean information
    *
    * @ return the bbean
    *
    *
    * @ejb.interface-method
    *
    * @ejb.relation
    * name=”a-refers-to-b”
    * role-name=”aTob”
    * target-ejb=”B”
    * target-role-name=”bToa”
    *
    * @jboss.relation-mapping
    * style=”relation-table”
    *
    * @jboss.relation-table
    * table-name=”b”
    * create-table=”false”
    * remove-table=”false”
    *
    *@jboss.relation
    * related-pk-field=”b_id”
    * fk-column=”a_id_fk”
    * fk-constraint=”false”
    */

    public abstract Collection getBidfk();

    /**
    *
    * @param java.util.Set the new b_id_fk value
    *
    * @ejb.interface-method
    *
    *
    */

    public abstract void setBidfk(Collection b);

    public void setEntityContext(EntityContext ctx) {
    this.ctx = ctx;
    }

    public void unsetEntityContext() {
    this.ctx = null;
    }

    /**
    * The ejbCreate method.
    *
    * @ejb.create-method
    */
    public java.lang.Long ejbCreate(Collection b) throws javax.ejb.CreateException {

    setAid(new Long(0));

    try {
    BLocalHome bLocalHome = BUtil.getLocalHome();

    java.util.Iterator it = b.iterator();
    while (it.hasNext()) {
    bLocalHome.create(getAid());
    }
    } catch (Exception e) {
    System.out.println(“a.ejbCreate”);
    System.out.println(e.getStackTrace());
    }

    return null;
    }

    /**
    * The container invokes this method immediately after it calls ejbCreate.
    *
    */
    public void ejbPostCreate(Collection b) throws javax.ejb.CreateException {

    try {
    BLocalHome bLocalHome = BUtil.getLocalHome();
    setBidfk(bLocalHome.findByForeignKey(getAid()));
    } catch (Exception e) {
    System.out.println(“a.ejbPostCreate”);
    System.out.println(e.getStackTrace());
    }

    }

    /**
    * Returns the a_id
    * @return the a_id
    *
    * @ejb.persistent-field
    * @ejb.persistence
    * column-name=”a_id”
    * sql-type=”fixed(10)”
    * @ejb.pk-field
    * @ejb.interface-method
    *
    * –
    * This is needed for JOnAS.
    * If you are not using JOnAS you can safely remove the tags below.
    * @jonas.cmp-field-jdbc-mapping field-name=”a_id”
    * jdbc-field-name=”a_id”
    *

    */
    public abstract java.lang.Long getA_id();

    /**
    * Sets the a_id
    *
    * @param java.lang.Long the new a_id value
    *
    * @ejb.interface-method
    */
    public abstract void setA_id(java.lang.Long a_id);

    }

    /*
    * Created on Dec 29, 2005
    *
    * To change the template for this generated file go to
    * Window>Preferences>Java>Code Generation>Code and Comments
    */
    package ejb;

    import javax.ejb.EntityBean;
    import javax.ejb.EntityContext;
    import java.lang.Long;

    /**
    * @ejb.bean name=”B”
    * jndi-name=”BBean”
    * type=”CMP”
    * primkey-field=”b_id”
    * schema=”b”
    * cmp-version=”2.x”
    *
    *–
    * This is needed for JOnAS.
    * If you are not using JOnAS you can safely remove the tags below.
    * @jonas.bean ejb-name=”B”
    * jndi-name=”BBean”
    * @jonas.jdbc-mapping jndi-name=”java:/SapdbDS” jdbc-table-name=”b”
    * –
    *
    * @ejb.persistence
    * table-name=”b”
    *
    * @ejb.finder
    * query=”SELECT OBJECT(a) FROM b as a”
    * signature=”java.util.Collection findAll()”
    *
    * @ejb.finder
    * query=”SELECT OBJECT(a) FROM a as a WHERE a_id_fk=?1″
    * signature=”java.util.Collection findByForeignKey(java.lang.Long a)”
    *–
    * This is needed for JOnAS.
    * If you are not using JOnAS you can safely remove the tags below.
    * @jonas.finder-method-jdbc-mapping method-name=”findAll”
    * jdbc-where-clause=”"
    * @jonas.jdbc-mapping jndi-name=”java:/SapdbDS”
    * jdbc-table-name=”b”
    *
    *–
    *
    **/

    public abstract class BBean implements EntityBean {

    /* public abstract ALocal getABean();

    public abstract void setABean(ALocal abean);

    */
    protected EntityContext ctx = null;

    public void setEntityContext(EntityContext ctx) {
    this.ctx = ctx;
    }

    public void unsetEntityContext() {
    this.ctx = null;
    }

    /**
    * The ejbCreate method.
    *
    * @ejb.create-method
    */
    public java.lang.Long ejbCreate(Long a_id_fk) throws javax.ejb.CreateException {

    setB_id(new Long(0));
    setAidfk(a_id_fk);

    return null;
    }

    /**
    * The container invokes this method immediately after it calls ejbCreate.
    *
    */
    public void ejbPostCreate() throws javax.ejb.CreateException {
    }

    /**
    * Returns the b_id
    * @return the b_id
    *
    * @ejb.persistent-field
    * @ejb.persistence
    * column-name=”b_id”
    * sql-type=”fixed(10)”
    * @ejb.pk-field
    * @ejb.interface-method
    *
    * –
    * This is needed for JOnAS.
    * If you are not using JOnAS you can safely remove the tags below.
    * @jonas.cmp-field-jdbc-mapping field-name=”b_id”
    * jdbc-field-name=”b_id”
    *

    */
    public abstract java.lang.Long getB_id();

    /**
    * Sets the b_id
    *
    * @param java.lang.Long the new b_id value
    *
    * @ejb.interface-method
    */
    public abstract void setB_id(java.lang.Long b_id);

    /**
    * Returns the a_id_fk
    * @return the a_id_fk
    *
    * @ejb.persistent-field
    * @ejb.persistence
    * column-name=”a_id_fk”
    * sql-type=”fixed(10)”
    *
    * @ejb.interface-method
    *
    * –
    * This is needed for JOnAS.
    * If you are not using JOnAS you can safely remove the tags below.
    * @jonas.cmp-field-jdbc-mapping field-name=”a_id_fk”
    * jdbc-field-name=”a_id_fk”
    *

    */
    public abstract java.lang.Long getAidfk();

    /**
    * Sets the a_id_fk
    *
    * @param java.lang.Long the new a_id_fk value
    *
    * @ejb.interface-method
    */
    public abstract void setAidfk(java.lang.Long aidfk);

    }

    Generated by XDoclet

    markup for those beans.
    –>

    B

    ejb.BHome
    ejb.B
    ejb.BLocalHome
    ejb.BLocal

    ejb.BCMP
    Container
    java.lang.Long
    False
    2.x
    b

    b_id

    aidfk

    b_id

    findAll

    findByForeignKey

    java.lang.Long

    A

    ejb.AHome
    ejb.A
    ejb.ALocalHome
    ejb.ALocal

    ejb.ACMP
    Container
    java.lang.Long
    False
    2.x
    a

    a_id

    a_id

    findAll

    markup for those beans.
    –>

    markup for those beans.
    –>

    a-refers-to-b

    aTob
    One

    A

    bidfk
    java.util.Collection

    bToa

    Many

    B

    markup.
    –>

  6. Zeger Hendrikse on

    I’m afraid not, but there is a comprehensive post on a uni-directional one-to-one relationship, see this post. Combining the information of these two posts, you should have enough information to get your specific case up and running without too much effort.

  7. stephen smithstone on

    Is there an article on uni-directonal one-to-many relationships anywhere on this site

  8. Zeger Hendrikse on

    I never implemented a case where you want to have an additional column in the link table. I’m afraid that you cannot make use of this "implicit table definition mechanism" anymore. However, you can always define your link table as a separate entity bean with the proper relations, of course, including additional columns.

    I’m not quite sure though what you mean with the registry, but hopefully this answer suffices already.

  9. Natalia Camargo on

    Hi
    This article is very clear, but I have a question: What if I need to
    have another column in the “relation table”? A different column related
    with the registry….
    Thanks for your help.