| How do I make a bi-directional association between entity beans at their creation? |
|
Problem Scenario from an actual support case:I've been trying to setup a relatively simple example to try out a 1:1 relationship between 2 entity beans, call them A and B. The only interesting action is that I want the create() method of A to create the related B object. Object A has a CMP field that is a reference to B and B has a CMP field that is a reference to A.In A::ejbCreate, I can't create the B since the current instance of A
has no real object (getEJBObject returns null). So, I put the code to
create B into A::ejbPostCreate. Here's a few code fragments:
public void ejbCreate( String data )
throws CreateException, RemoteException {
_mid = new MessagePK().allocMID();
_data = new String( data );
System.out.println( "in ejbCreate: mid = " + _mid );
}
public void ejbPostCreate( String data )
throws CreateException, RemoteException {
System.out.println( "in ejbPostCreate( String )" );
// Create our associated meta data object.
// Have to do this here since we need a reference to ourself and in
// ejbCreate getEJBObject returns null.
_metaData =_mmdHome.create( _mid, (Message)_context.getEJBObject());
System.out.println( "Finish: mmd = " + _metaData );
}
Even though the print "Finish:..." shows a valid reference, the database
is not updated to reflect the value. This insert takes place at the end
of ejbCreate:
*cm* execute create "INSERT INTO Message (_mid, _data, _metaData) VALUES (?, ?, ?)" args: [31, Message number 0, null]As expected, the _metaData field is null since it hasn't been created. Yet, at the exit from ejbPostCreate, no database update takes place. So the DB value remains null. Am I doing something illegal? What is the "right" way to make this mutual cross-reference? ResolutionIf you look at the sequence diagrams for creating entity beans, the calls to the database occur between ejbCreate and ejbPostCreate. Thus, any changes that you make to the state in ejbPostCreate will be ignored, from a persistence perspective.There is no simple way to do what you are trying to do. It could be construed as a bug in the EJB specification or wrong usage. The only way to do what you want, is as follows:
If you wrap A's ejbCreate/ejbPostCreate within a single transaction (using either client-managed- transactions, or container-managed), then you will never see the temporarily incomplete B--except within the scope of that single transaction. Here are the relevant code fragments:
public void ejbCreate( String data ) throws CreateException, RemoteException {
_mid = new MessagePK().allocMID();
_data = new String( data );
System.out.println( "in ejbCreate: mid = " + _mid );
// Create our meta data object, MsgMetaData. We'll give it an initial null reference
// to us (since we really don't exist yet). Later, in ejbPostCreate,
// we'll set the reference properly.
_metaData = _mmdHome.create( _mid, null );
}
public void ejbPostCreate( String data ) throws CreateException, RemoteException {
System.out.println( "in ejbPostCreate( String )" );
// Update our associated meta data object's reference to us.
_metaData.setMessage( (Message) _context.getEJBObject() );
}
The MsgMetaData.setMessage() method is a single line of code setting the
member reference.
|