Discussions

EJB programming & troubleshooting: CMR - ejbCreate foreign key constraint NOT NULL problem

  1. I am new to CMR in EJB2.0 and am having problems trying to create dependent entity beans with foreign key not null constraints.

    Removing constraint in database is not an option, nor is removing NOT NULL tag on the foreign keys, as there are other applications that will access these tables and the database admin does not want to relax the integrity of the relationships.

    Any help is greatly appreciated. Details follow:

    1. I have a RFQ entity bean that has a 1-N uni-direction relationship with RFQProduct entity bean.
    a. RFQ – primary key rfq_id.
    b. RFQProduct – compound keys - rfq_id_fk (foreign key from RFQ) and product_id. There is a foreign key constraint of NOT NULL set on rfq_id_fk.
    2. I have a web page that takes in a rfq and 2 rfqproducts details. What are the steps for successfully creating a rfq entry and 2 rfqProduct entries?
    3. Environment - I am using weblogic 8.1, xdoclet 1.1 and ant 1.5 – SQL Server 2000.
    4. In the RFQBean (Entity Bean)
    public String ejbCreate(RFQVO newRFQ) throws CreateException
    {
    p("ejbCreate() - start");

    this.setRfqId(newRFQ.getRfqId());
    this.setRfqName(newRFQ.getRfqName());
    this.setOrganizationName(newRFQ.getOrganizationName());
    this.setRfqDate(newRFQ.getRfqDate());
    this.setCreator(newRFQ.getCreator());
    this.setSubmissionDate(newRFQ.getSubmissionDate());
    this.setStatus(newRFQ.getStatus());

    p("ejbCreate() - end");
    // EJB 2.0 spec says return null for CMP ejbCreate methods.
    return null;
    }
    public void ejbPostCreate(RFQVO newRFQ) throws CreateException
    {
    p("ejbPostCreate() - end");
    }
    /**
    * Returns a collection of related eaisc.interfaces.RFQProductLocal
    * @return a collection of related eaisc.interfaces.RFQProductLocal
    *
    * @ejb.interface-method
    * view-type="local"
    *
    * @ejb.value-object
        * aggregate="eaisc.interfaces.RFQProductVO"
        * aggregate-name="RFQProduct"
         * members="eaisc.interfaces.RFQProductLocal"
         * members-name="RFQProduct"
        * relation="external"
         * type="Collection"
         *
    * @ejb.relation
    * name="rfq-rfqproducts"
    * role-name="rfq-has-many-rfqproducts"
    * target-ejb="RFQProduct"
    * target-multiple="no"
    * target-role-name="rfqproduct-belongs-to-one-rfq"
    *
    * @weblogic.target-column-map
    * foreign-key-column="rfq_id_fk"
    */
       public abstract java.util.Collection getRfqProducts();

       /**
    * Sets a collection of related eaisc.interfaces.RFQProductLocal
    *
    * @param a collection of related eaisc.interfaces.RFQProductLocal
    * @param cities the new CMR value
    *
    * @ejb.interface-method
    * view-type="local"
    */
    public abstract void setRfqProducts(java.util.Collection rfqProducts);

    5. In RFQProductBean (Entity Bean)
    /**
    * The ejbCreate method.
    *
    * @ejb.create-method
    */

    public eaisc.interfaces.RFQProductPK ejbCreate(RFQProductVO newRFQProduct) throws CreateException
    {
    p("ejbCreate() - start");

    this.setProductId(newRFQProduct.getProductId());
    this.setQuantity(newRFQProduct.getQuantity());
    this.setProductName(newRFQProduct.getProductName());

    p("ejbCreate() - end");
    // EJB 2.0 spec says return null for CMP ejbCreate methods.
    return null;
    }

    public void ejbPostCreate(RFQProductVO newRFQProduct) throws CreateException
    {
    p("ejbPostCreate() - start");

    If I set the foreign key field as below, I get the following error:
    When a cmp-field and a cmr-field (relationship) are mapped to the same column, the setXXX method for the cmp-
    field may not be called. The cmp-field is read-only.

    this.setRfqIdFk(newRFQProduct.getRfqIdFk());


    If I comment the line below, I get the following error:
    Foreign key constraint violation – cannot be null from SQLServer & ejbcreate fails.

    p("ejbPostCreate() - end");
    }

    In SessionFacade, I call the relevant entity bean methods as follows:
    public void generateRFQ(eaisc.interfaces.RFQVO newRfqVO) throws ApplicationException
    {
    p("generateRFQ() - start");
    RFQLocalHome rfqLocalHome = null;
    RFQProductLocalHome rfqProductLocalHome = null;
    p("SessionFacadeEJBObject bean lookup " + RFQFacadeHome.JNDI_NAME);
    ServiceLocator serviceLocator = ServiceLocator.getInstance();
    try
    {
    p("buildRFQProducts() - use ServiceLocator and locate the local home of RFQLocal Entity Bean");
    rfqLocalHome = (RFQLocalHome) serviceLocator.locateEJBObjectLocalHome(RFQLocalHome.JNDI_NAME);
    p("buildRFQProducts() - use ServiceLocator and locate the local home of RFQProductLocal Entity Bean");
    rfqProductLocalHome = (RFQProductLocalHome) ServiceLocator.locateEJBObjectLocalHome
    (RFQProductLocalHome.JNDI_NAME);
    p("buildRFQProducts() - use ServiceLocator and locate the local home of RFQProductLocal Entity Bean - SUCCESS !!!");
    }
    catch (ServiceLocatorException e)
    {
    throw new ApplicationException(e);
    }

    p("generateRFQ() - creating new RFQ header record");
    newRfqVO.setStatus("open");
    RFQLocal rfqLocal = rfqLocalHome.create(newRfqVO);
    p("generateRFQ() - creating new RFQ header record - SUCCESS !!!");


    RFQProductVO[] rfqProductVOs = newRfqVO.getRFQProducts();
    for (int i = 0; i < rfqProductVOs.length; i++)
    {
    p("buildRFQProducts() - Creating product[" + i + "] ...");

    rfqProductVOs[i].setRfqIdFk(rfqLocal.getRfqId());
    RFQProductLocal rfqProductLocal = rfqProductLocalHome.create(rfqProductVOs[i]);
    p("buildRFQProducts() - Creating product[" + i + "] ...SUCCESS !!!");

    rfqLocal.getRfqProducts().add(rfqProductLocal);
    }

    p("buildRFQProducts() - end");
    }
  2. Hi,

      I have several cases like this one you're facing. Let me tell you the solution that we have adopted.
      Let's say that we have an entity A in a 1 to N relation with entity B.
      In the ejbPostCreate of B I am giving a reference not to the primary key of A but to a Local interface of A. So it's like this:

      ejbCreateB(..., ALocal alocal)
      {...}
      ejbPostCreateB(..., ALocal alocal)
      {
       setTA(alocal); <---here you are giving (as I've seen into your code) the pk but you should give a refference to the local interface of A
      }
      the setter used here in the ejbPostCreateB is the setter for the field in entity B that is refferenced as the <cmr-field-name> in your deployment descriptor(the one that implements the relation).

      The only problem with this is that the ForeignKey field in table B can't have the restriction not null and I will tell you why I believe it is so(anyone please correct me if I am wrong because I am also a newbie in CMP). I think that first gets created the entity B and inserted into table B and only after this gets created the relation. I think this is the reason why you can set a relation only in ejbPostCreate.
       Hope this helps you ;)).
       Sergiu
  3. Thanks for your prompt response. I have tried the scenario that you have suggested. Removing the NOT NULL constraint, I know, will resolve my problem too as explained & implemented correctly by you. However, I do not have the luxury of relaxing the NOT NULL constraint as specified in your response as well as in my earlier email.

    1. What exactly happens behind the scene, when you call setTA(alocal) method? Does the container update the foreign key (behind the scene) on the newly inserted record?

    2. Even with your suggestion, at then end of ejbCreate() method, the database record is still written to the database for Entity B, which fails due to null foreign key - (set only in ejbPostCreate). Is there anyway to let container know to postpone all database updates after the ejbPostCreate()? I read a weblogic setting to delay the insert after ejbPostCreate but that did not solve the problem either. Am I missing something? This problem situation seems to me to be a fairly common occurence in database design, and I am curious to find out how you guys have figured this out? Pls help!!!!

    > Hi,
    >
    >   I have several cases like this one you're facing. Let me tell you the solution that we have adopted.
    >   Let's say that we have an entity A in a 1 to N relation with entity B.
    >   In the ejbPostCreate of B I am giving a reference not to the primary key of A but to a Local interface of A. So it's like this:
    >
    >   ejbCreateB(..., ALocal alocal)
    >   {...}
    >   ejbPostCreateB(..., ALocal alocal)
    >   {
    >    setTA(alocal); <---here you are giving (as I've seen into your code) the pk but you should give a refference to the local interface of A
    >   }
    >   the setter used here in the ejbPostCreateB is the setter for the field in entity B that is refferenced as the <cmr-field-name> in your deployment descriptor(the one that implements the relation).
    >
    >   The only problem with this is that the ForeignKey field in table B can't have the restriction not null and I will tell you why I believe it is so(anyone please correct me if I am wrong because I am also a newbie in CMP). I think that first gets created the entity B and inserted into table B and only after this gets created the relation. I think this is the reason why you can set a relation only in ejbPostCreate.
    >    Hope this helps you ;)).
    >    Sergiu
  4. To answer your first question, setTA(alocal)is doing exactly what you are saying. It will set the appropriate foreign key in entity B. Relationship by spec can only be set in ejbPostCreate. Secondly as the previous post explained, do set CMR field for the relationship and do NOT define ur foreign keys as CMP fields.

    I am NOT sure why delay-database-insert-until set to ejbPostCreate is not working for you (somehow ur insert for entity B is getting to the database before u intend to). Logically ur insert for entity B should not be fired, so that means it is created in memory and then updated in memory. After ejbPostCreate, when insert statement is flushed to the database, the foreign key is already set (in ejbPostCreate).
    I can think of 2 options:
    You can try the delay-database-insert-until being set to 'commit'. This performs a bulk insert when the transaction commits. So basically before committing ur transaction, ur foreign key should have been set (in ejbPostCreate).
    Another very useful option is telling the database team to turn on 'checking database constraints at the end of transaction'. Atleast this feature in provided in oracle by name of 'deferred constraints'. Here the constraints will be checked at the end of transaction and not during running of each SQL statement in the transaction. This way DB team does not have to remove the constraints and also your problem will be solved.

    I am speaking mainly of weblogic app server above.
    Mohit
  5. Let me try your options and let you know. In the meantime...

    1. Could you give me an example on where and how the "delay-database-insert-until" option - maybe a listing of the cmp descriptor? I am not using the tags properly....it looks like!!!

    2. Currently, (to my knowledge) there is no way to generate the "delay..." tags in the XML descriptors using Xdoclet. I have to manually edit my file after ejbdoclet task has generated the descriptors. How did you include the tag in ...cmp descripor - manual editing?
  6. I got the whole thing to work!!!!!.

    There was a problem in the way that I had defined my 1-N CMR relationship using xdoclet tags. The ejb-jar.xml deployment descriptor was not generated as expected.

    By default Weblogic 8.x uses delay-on-insert after ejbPostCreate and setting the RFQLocal in the ejbPostCreate as correctly suggested by you guys worked without any constraint problems.

    Thanks for your help guys....
  7. same problem[ Go to top ]

    hey i'm kind of having the same trouble.. but i'm working with Oracle9iAS 9.0.3.0.0 i'm sorry but i'm a rockie and i do not hava the idea wheres that tag to delay after post create would you tell me please??
  8. Using ejbPostCreate[ Go to top ]

    Hi,
       Iam also getting the same problem.can u please help me in this regard.Iam using oracle9ias.how to use "delay..." tags in OC4J.can any one send me some sample.

    Thanks in advance
    Samy
  9. Hi,

    I am using CMR/CMP and things are working fine with "delay updates to commit" enabled. However we have a reason for wanting to DISABLE this and I am getting the same issue as mentioned below.

    My child object ejbCreate currently is:

    public void ejbPostCreate(String nmCreatedBy, Timestamp dtCreatedOn)
    throws CreateException {
      }