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
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.
–>
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.
Is there an article on uni-directonal one-to-many relationships anywhere on this site
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.
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.