Skip to Content
0
Former Member
Oct 27, 2004 at 02:43 PM

Defining a many-to-many relationship with CMP EJBs : does it work for you

32 Views

Curious to know whether someone has been able to set up a many to many relationship between two entity beans.

I have been struggling with this for days now and I can't get it to work.

In my test project I have two entity beans, resp. Consultant and Solution.

Consultant has a cmr field called 'solutions', which is a collection object from the opposite side (Solution).

This is the source of the ejb-jar.xml file

<?xml version="1.0" encoding="UTF-8"?>

http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>

<description>EJB JAR description</description>

<display-name>EJB JAR</display-name>

<enterprise-beans>

<session>

<ejb-name>ProfilerBean</ejb-name>

<home>com.atosorigin.tcc.testing.ejbses.profiling.ProfilerHome</home>

<remote>com.atosorigin.tcc.testing.ejbses.profiling.Profiler</remote>

<local-home>com.atosorigin.tcc.testing.ejbses.profiling.ProfilerLocalHome</local-home>

<local>com.atosorigin.tcc.testing.ejbses.profiling.ProfilerLocal</local>

<ejb-class>com.atosorigin.tcc.testing.ejbses.profiling.ProfilerBean</ejb-class>

<session-type>Stateless</session-type>

<transaction-type>Container</transaction-type>

<ejb-local-ref>

<ejb-ref-name>EJBTesting/Solution</ejb-ref-name>

<ejb-ref-type>Entity</ejb-ref-type>

<local-home>com.atosorigin.tcc.testing.ejbcmp.profiling.SolutionLocalHome</local-home>

<local>com.atosorigin.tcc.testing.ejbcmp.profiling.SolutionLocal</local>

<ejb-link>SolutionBean</ejb-link>

</ejb-local-ref>

<ejb-local-ref>

<ejb-ref-name>EJBTesting/Consultant</ejb-ref-name>

<ejb-ref-type>Entity</ejb-ref-type>

<local-home>com.atosorigin.tcc.testing.ejbcmp.profiling.ConsultantLocalHome</local-home>

<local>com.atosorigin.tcc.testing.ejbcmp.profiling.ConsultantLocal</local>

<ejb-link>ConsultantBean</ejb-link>

</ejb-local-ref>

</session>

<entity>

<ejb-name>ConsultantBean</ejb-name>

<home>com.atosorigin.tcc.testing.ejbcmp.profiling.ConsultantHome</home>

<remote>com.atosorigin.tcc.testing.ejbcmp.profiling.Consultant</remote>

<local-home>com.atosorigin.tcc.testing.ejbcmp.profiling.ConsultantLocalHome</local-home>

<local>com.atosorigin.tcc.testing.ejbcmp.profiling.ConsultantLocal</local>

<ejb-class>com.atosorigin.tcc.testing.ejbcmp.profiling.ConsultantBean</ejb-class>

<persistence-type>Container</persistence-type>

<prim-key-class>java.lang.String</prim-key-class>

<reentrant>False</reentrant>

<cmp-version>2.x</cmp-version>

<abstract-schema-name>Consultant</abstract-schema-name>

<cmp-field>

<field-name>firstname</field-name>

</cmp-field>

<cmp-field>

<field-name>lastname</field-name>

</cmp-field>

<cmp-field>

<field-name>country</field-name>

</cmp-field>

<cmp-field>

<field-name>id</field-name>

</cmp-field>

<primkey-field>id</primkey-field>

<query>

<query-method>

<method-name>findBySolution</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</query-method>

<ejb-ql>SELECT Object(c) FROM Consultant AS c,

IN(c.solutions) s WHERE s.id = ?1 </ejb-ql>

</query>

</entity>

<entity>

<ejb-name>SolutionBean</ejb-name>

<home>com.atosorigin.tcc.testing.ejbcmp.profiling.SolutionHome</home>

<remote>com.atosorigin.tcc.testing.ejbcmp.profiling.Solution</remote>

<local-home>com.atosorigin.tcc.testing.ejbcmp.profiling.SolutionLocalHome</local-home>

<local>com.atosorigin.tcc.testing.ejbcmp.profiling.SolutionLocal</local>

<ejb-class>com.atosorigin.tcc.testing.ejbcmp.profiling.SolutionBean</ejb-class>

<persistence-type>Container</persistence-type>

<prim-key-class>java.lang.String</prim-key-class>

<reentrant>False</reentrant>

<cmp-version>2.x</cmp-version>

<abstract-schema-name>Solution</abstract-schema-name>

<cmp-field>

<field-name>description</field-name>

</cmp-field>

<cmp-field>

<field-name>name</field-name>

</cmp-field>

<cmp-field>

<field-name>parent</field-name>

</cmp-field>

<cmp-field>

<field-name>id</field-name>

</cmp-field>

<primkey-field>id</primkey-field>

<query>

<query-method>

<method-name>findChildSolutions</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

</query-method>

<ejb-ql>SELECT Object(s) FROM Solution AS s WHERE

s.parent = ?1</ejb-ql>

</query>

<query>

<query-method>

<method-name>findTopLevelSolutions</method-name>

<method-params/>

</query-method>

<ejb-ql>Select Object(s) FROM Solution AS s WHERE

(s.parent = &apos;none&apos;)</ejb-ql>

</query>

</entity>

</enterprise-beans>

<relationships>

<ejb-relation>

<description>A consultant may have one or more areas of expertise</description>

<ejb-relation-name>Consultant_Solutions</ejb-relation-name>

<ejb-relationship-role>

<ejb-relationship-role-name>com.atosorigin.tcc.testing.ejbcmp.profiling.ConsultantBean</ejb-relationship-role-name>

<multiplicity>Many</multiplicity>

<relationship-role-source>

<ejb-name>ConsultantBean</ejb-name>

</relationship-role-source>

<cmr-field>

<cmr-field-name>solutions</cmr-field-name>

<cmr-field-type>java.util.Collection</cmr-field-type>

</cmr-field>

</ejb-relationship-role>

<ejb-relationship-role>

<ejb-relationship-role-name>com.atosorigin.tcc.testing.ejbcmp.profiling.SolutionBean</ejb-relationship-role-name>

<multiplicity>Many</multiplicity>

<relationship-role-source>

<ejb-name>SolutionBean</ejb-name>

</relationship-role-source>

</ejb-relationship-role>

</ejb-relation>

</relationships>

<assembly-descriptor>

<container-transaction>

<description>container-transaction</description>

<method>

<ejb-name>SolutionBean</ejb-name>

<method-name>*</method-name>

</method>

<method>

<ejb-name>ProfilerBean</ejb-name>

<method-name>*</method-name>

</method>

<method>

<ejb-name>ConsultantBean</ejb-name>

<method-name>*</method-name>

</method>

<trans-attribute>Required</trans-attribute>

</container-transaction>

</assembly-descriptor>

</ejb-jar>

I created a stateless session bean as a business facade for the two entity beans. I then created a webservice to test the beans.

What works :

- create a consultant

- get a consultant

- create a solution

- get a solution

(basically everything that doesn't involve the relationship field.

However, what doesn't work is the method call assignSolution:

- assign solution : this is implemented as follows

/**

  • Business Method.

*/

public void assignSolution(String consultantID, String solutionID)

throws ProfilingException {

// TODO : Implement

ConsultantLocal cons = null;

SolutionLocal sol = null;

try {

cons = consHome.findByPrimaryKey(consultantID);

sol = solHome.findByPrimaryKey(solutionID);

Collection solutions = cons.getSolutions();

solutions.add(sol);

//sol.getConsultants().add(cons);

} catch (FinderException ex) {

ex.printStackTrace();

throw new ProfilingException("failed to retrieve data from DB", ex);

}

}

As you can see I am trying to use the CM Relationship in this methhod. Adding the solution to a consultant should be as simple as adding a solution object to the collection retrieved with the getSolutions accessor. The Container is expected to persist the information in my MAXDB database.

However this doesn't happen.

The ORM details as defined in the persistent.xml follows :

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE persistent-ejb-map SYSTEM "persistent.dtd">

<persistent-ejb-map>

<locking

type="Table"/>

<db-properties>

<data-source-name>TCC_PORTAL_PROFILER</data-source-name>

<database-vendor

name="SAPDB"/>

</db-properties>

<entity-beans>

<entity-bean>

<ejb-name>ConsultantBean</ejb-name>

<table-name>PFL_CONSULTANTS</table-name>

<field-map

key-type="NoKey">

<field-name>firstname</field-name>

<column>

<column-name>FIRSTNAME</column-name>

</column>

</field-map>

<field-map

key-type="NoKey">

<field-name>lastname</field-name>

<column>

<column-name>LASTNAME</column-name>

</column>

</field-map>

<field-map

key-type="NoKey">

<field-name>country</field-name>

<column>

<column-name>COUNTRY</column-name>

</column>

</field-map>

<field-map

key-type="PrimaryKey">

<field-name>id</field-name>

<column>

<column-name>ID</column-name>

</column>

</field-map>

<finder-descriptor>

<method-name>findBySolution</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

<load-selected-objects

lock="read"/>

</finder-descriptor>

</entity-bean>

<entity-bean>

<ejb-name>SolutionBean</ejb-name>

<table-name>PFL_SAPSOLUTIONS</table-name>

<field-map

key-type="NoKey">

<field-name>description</field-name>

<column>

<column-name>DESCRIPTION</column-name>

</column>

</field-map>

<field-map

key-type="NoKey">

<field-name>name</field-name>

<column>

<column-name>NAME</column-name>

</column>

</field-map>

<field-map

key-type="NoKey">

<field-name>parent</field-name>

<column>

<column-name>PARENTID</column-name>

</column>

</field-map>

<field-map

key-type="PrimaryKey">

<field-name>id</field-name>

<column>

<column-name>ID</column-name>

</column>

</field-map>

<finder-descriptor>

<method-name>findChildSolutions</method-name>

<method-params>

<method-param>java.lang.String</method-param>

</method-params>

<load-selected-objects

lock="read"/>

</finder-descriptor>

<finder-descriptor>

<method-name>findTopLevelSolutions</method-name>

<method-params/>

<load-selected-objects

lock="read"/>

</finder-descriptor>

</entity-bean>

</entity-beans>

<relationships>

<table-relation>

<help-table>PFL_CONS_SOL_MAP</help-table>

<table-relationship-role

key-type="PrimaryKey">

<ejb-name>ConsultantBean</ejb-name>

<cmr-field>solutions</cmr-field>

<fk-column>

<column-name>CONSULTANTID</column-name>

<pk-field-name>id</pk-field-name>

</fk-column>

</table-relationship-role>

<table-relationship-role

key-type="PrimaryKey">

<ejb-name>SolutionBean</ejb-name>

<fk-column>

<column-name>SOLUTIONID</column-name>

<pk-field-name>id</pk-field-name>

</fk-column>

</table-relationship-role>

</table-relation>

</relationships>

</persistent-ejb-map>

The error in the default trace file when calling the method states, there is an "inconsistency in the number of primary keys". Details follow.

#

#1.5#000F1F188E5C004400000000000010480003E76C80EFD57A#1098880364327#com.sap.engine.services.ejb#com.atosorigin.tcc/EJBCMPProfilingTest#com.sap.engine.services.ejb#Guest#2####4d2b2370281411d9a40d000f1f188e5c#SAPEngine_Application_Thread[impl:3]_31##0#0#Error##Java###

[EXCEPTION]

#1#com.sap.engine.services.ejb.exceptions.BaseRemoteException: Exception in method assignSolution.

at com.atosorigin.tcc.testing.ejbses.profiling.ProfilerObjectImpl0.assignSolution(ProfilerObjectImpl0.java:822)

at com.atosorigin.tcc.testing.ejbses.profiling.Profiler_Stub.assignSolution(Profiler_Stub.java:533)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:324)

at com.sap.engine.services.ejb.session.stateless_sp5.ObjectStubProxyImpl.invoke(ObjectStubProxyImpl.java:187)

at $Proxy73.assignSolution(Unknown Source)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:324)

at com.sap.engine.services.webservices.runtime.EJBImplementationContainer.invokeMethod(EJBImplementationContainer.java:126)

at com.sap.engine.services.webservices.runtime.RuntimeProcessor.process(RuntimeProcessor.java:146)

at com.sap.engine.services.webservices.runtime.RuntimeProcessor.process(RuntimeProcessor.java:68)

at com.sap.engine.services.webservices.runtime.servlet.ServletDispatcherImpl.doPost(ServletDispatcherImpl.java:92)

at SoapServlet.doPost(SoapServlet.java:51)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)

at com.sap.engine.services.servlets_jsp.server.HttpHandlerImpl.runServlet(HttpHandlerImpl.java:385)

at com.sap.engine.services.servlets_jsp.server.HttpHandlerImpl.handleRequest(HttpHandlerImpl.java:263)

at com.sap.engine.services.httpserver.server.RequestAnalizer.startServlet(RequestAnalizer.java:339)

at com.sap.engine.services.httpserver.server.RequestAnalizer.startServlet(RequestAnalizer.java:317)

at com.sap.engine.services.httpserver.server.RequestAnalizer.invokeWebContainer(RequestAnalizer.java:810)

at com.sap.engine.services.httpserver.server.RequestAnalizer.handle(RequestAnalizer.java:238)

at com.sap.engine.services.httpserver.server.Client.handle(Client.java:92)

at com.sap.engine.services.httpserver.server.Processor.request(Processor.java:147)

at com.sap.engine.core.service630.context.cluster.session.ApplicationSessionMessageListener.process(ApplicationSessionMessageListener.java:37)

at com.sap.engine.core.cluster.impl6.session.UnorderedChannel$MessageRunner.run(UnorderedChannel.java:71)

at com.sap.engine.core.thread.impl3.ActionObject.run(ActionObject.java:37)

at java.security.AccessController.doPrivileged(Native Method)

at com.sap.engine.core.thread.impl3.SingleThread.execute(SingleThread.java:94)

at com.sap.engine.core.thread.impl3.SingleThread.run(SingleThread.java:162)

Caused by: com.sap.engine.services.applocking.exception.SAPAppLockingIllegalArgumentException: Inconsistency in number of primary keys

at com.sap.engine.services.applocking.TableLockingImpl.getArgument(TableLockingImpl.java:385)

at com.sap.engine.services.applocking.TableLockingImpl.lock(TableLockingImpl.java:128)

at com.sap.engine.services.applocking.TableLockingImpl.lock(TableLockingImpl.java:138)

at com.sap.engine.services.ejb.entity.pm.lock.TableLockingSystem.write(TableLockingSystem.java:82)

at com.sap.engine.services.ejb.entity.pm.PersistentM2M.create(PersistentM2M.java:172)

at com.sap.engine.services.ejb.entity.pm.PersistentCacheM2M.add(PersistentCacheM2M.java:197)

at com.sap.engine.services.ejb.entity.pm.multiple.CollectionM2M.add(CollectionM2M.java:57)

at com.atosorigin.tcc.testing.ejbses.profiling.ProfilerBean.assignSolution(ProfilerBean.java:201)

at com.atosorigin.tcc.testing.ejbses.profiling.ProfilerObjectImpl0.assignSolution(ProfilerObjectImpl0.java:771)

... 32 more

I can't figure out what the problem is with the ORM mapping defined.

On the database level I have three tables. Consultant maps to PFL_CONSULTANT, Solution to PFL_SAPSOLUTIONS. The third table PFL_CONS_SOL_MAP is the help table used for the relationship (having two fields, which are in fact foreign keys from the other two tables to express the assignment of solutions to consultants (where a consultant can have one or more solutions).

ps. : I am running Netweaver Developer Workplace (Netweaver 2004, at stack level 5)

Message was edited by: Theo Paesen