EJB m:n relationship in JBoss + Xdoclet
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
AuthorLocalobjects,AuthorLocalbeing generated by Xdoclet - The many-to-many character of our relationship is indicated by the
target-multiple="yes"field of the@ejb.relationtag (for more details, see my previous post on this subject) - The linking table needed for the
m:nrelationship, is automatically generated by JBoss, if it does not exist already and is indicated by the@jboss.relation-mappingtag - Information on the linking table created by JBoss is contained in the
@jboss.relation-tabletag. The table is created if it didn’t exists already, but is not removed during un/redeployment (remove-table="false") - The
@jboss.relationhas 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
nameof the@ejb.relationmust be the same on both sides - Look carefully at at the way in which the
@jboss.relationfields are defined
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.
–>