Uploaded image for project: 'OpenJPA'
  1. OpenJPA
  2. OPENJPA-2613

findByPrimaryKey error on EJB CMP with composite primary key

    XMLWordPrintableJSON

Details

    • Question
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 2.4.0
    • None
    • jpa, kernel
    • None
    • apache-tomee-plus-1.7.2

    Description

      Hello Openjpa community,
      I'm trying to get working a cmp ejb entity with a composite primary key.
      I know I can get this sample running with a simple Integer type as primary key, but some of cmp ejb have composite pk, so I want to test it. (not just for fun, I have some ejb oin this case !)
      create, findAll, customer finders are ok, but I encounter problem on findByPrimaryKey:

      java.rmi.RemoteException: The bean encountered a non-application exception; nested exception is: 
      	<openjpa-2.4.0-r422266:1674604 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: The given value "fr.chronopost.ejb.esd.customer.interfaces.CustomerPK@1" cannot be converted into an identity for "class openejb.fr.chronopost.ejb.esd.customer.server.CUSTOMER".  The value is the wrong type (fr.chronopost.ejb.esd.customer.interfaces.CustomerPK).
      	at org.apache.openejb.core.transaction.EjbTransactionUtil.handleSystemException(EjbTransactionUtil.java:155)
      	at org.apache.openejb.core.cmp.CmpContainer.findByPrimaryKey(CmpContainer.java:702)
      	at org.apache.openejb.core.cmp.CmpContainer.invoke(CmpContainer.java:269)
      	at org.apache.openejb.server.ejbd.EjbRequestHandler.doEjbHome_FIND(EjbRequestHandler.java:445)
      	at org.apache.openejb.server.ejbd.EjbRequestHandler.processRequest(EjbRequestHandler.java:198)
      	at org.apache.openejb.server.ejbd.EjbDaemon.processEjbRequest(EjbDaemon.java:344)
      	at org.apache.openejb.server.ejbd.EjbDaemon.service(EjbDaemon.java:240)
      	at org.apache.openejb.server.ejbd.EjbServer.service(EjbServer.java:86)
      	at org.apache.openejb.server.httpd.ServerServlet.service(ServerServlet.java:58)
      	at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
      	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
      	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
      	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
      	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
      	at org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:44)
      	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
      	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
      	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:957)
      	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
      	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
      	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
      	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620)
      	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
      	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
      	at java.lang.Thread.run(Thread.java:662)
      Caused by: <openjpa-2.4.0-r422266:1674604 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: The given value "fr.chronopost.ejb.esd.customer.interfaces.CustomerPK@1" cannot be converted into an identity for "class openejb.fr.chronopost.ejb.esd.customer.server.CUSTOMER".  The value is the wrong type (fr.chronopost.ejb.esd.customer.interfaces.CustomerPK).
      	at org.apache.openjpa.kernel.BrokerImpl.newObjectId(BrokerImpl.java:1311)
      	at org.apache.openjpa.kernel.DelegatingBroker.newObjectId(DelegatingBroker.java:315)
      	at org.apache.openjpa.persistence.EntityManagerImpl.find(EntityManagerImpl.java:485)
      	at org.apache.openejb.persistence.JtaEntityManager.find(JtaEntityManager.java:180)
      	at org.apache.openejb.core.cmp.jpa.JpaCmpEngine.loadBean(JpaCmpEngine.java:163)
      	at org.apache.openejb.core.cmp.CmpContainer.findByPrimaryKey(CmpContainer.java:688)
      	... 25 more
      Caused by: java.lang.Exception: java.lang.ClassCastException: fr.chronopost.ejb.esd.customer.interfaces.CustomerPK cannot be cast to java.lang.Number
      	at org.apache.openjpa.util.Exceptions.replaceNestedThrowables(Exceptions.java:254)
      	at org.apache.openjpa.persistence.ArgumentException.writeObject(ArgumentException.java:106)
      	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:597)
      	at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:940)
      	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)
      	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
      	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
      	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
      	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
      	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
      	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
      	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
      	at org.apache.openejb.client.ThrowableArtifact.writeExternal(ThrowableArtifact.java:57)
      	at java.io.ObjectOutputStream.writeExternalData(ObjectOutputStream.java:1429)
      	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1398)
      	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
      	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
      	at org.apache.openejb.client.EJBResponse.writeExternal(EJBResponse.java:186)
      	at org.apache.openejb.server.ejbd.EjbRequestHandler.processResponse(EjbRequestHandler.java:303)
      	at org.apache.openejb.server.ejbd.EjbDaemon.processEjbResponse(EjbDaemon.java:356)
      	at org.apache.openejb.server.ejbd.EjbDaemon.service(EjbDaemon.java:269)
      	... 20 more
      
      ejb-jar.xml
      <enterprise-beans>
      	<entity>
      		<description>
      			This bean represents customer
      		</description>
      		<ejb-name>CustomerEJB</ejb-name>
      		<home>fr.chronopost.ejb.esd.customer.interfaces.CustomerHome</home>
      		<remote>fr.chronopost.ejb.esd.customer.interfaces.CustomerRemote</remote>
      		<ejb-class&gt;fr.chronopost.ejb.esd.customer.server.CustomerBean</ejb-class&gt;
      		<persistence-type>Container</persistence-type>
      		<prim-key-class&gt;fr.chronopost.ejb.esd.customer.interfaces.CustomerPK</prim-key-class&gt;
      		<reentrant>false</reentrant>
      		<cmp-version>2.x</cmp-version>
      		<abstract-schema-name>CUSTOMER</abstract-schema-name>
      
      		<cmp-field>
      			<field-name>id</field-name>
      		</cmp-field>
      		<cmp-field>
      			<field-name>name</field-name>
      		</cmp-field>
      		<cmp-field>
      			<field-name>birthdate</field-name>
      		</cmp-field>
      		<cmp-field>
      			<field-name>sssNo</field-name>
      		</cmp-field>
      		<cmp-field>
      			<field-name>address</field-name>
      		</cmp-field>
      		<cmp-field>
      			<field-name>annualSalary</field-name>
      		</cmp-field>
      		<cmp-field>
      			<field-name>loanAmount</field-name>
      		</cmp-field>
      
      		<primkey-field>id</primkey-field> 
      
      		<query>
      			<query-method>
      				<method-name>findBySssNo</method-name>
      				<method-params>
      					<method-param>java.lang.String</method-param>
      				</method-params>
      			</query-method>
      			<ejb-ql>select distinct object(o) from CUSTOMER o where o.sssNo = ?1</ejb-ql>
      		</query>
      		<query>
      			<query-method>
      				<method-name>findAll</method-name>
      				<method-params />
      			</query-method>
      			<ejb-ql>select distinct object(o) from CUSTOMER o</ejb-ql>
      		</query>
      	</entity>
      </enterprise-beans>
      </ejb-jar>
      
      openejb-jar.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <openejb-jar xmlns="http://www.openejb.org/xml/ns/openejb-jar-2.1"
      	xmlns:nam="http://geronimo.apache.org/xml/ns/naming-1.1" xmlns:pkgen="http//www.openejb.org/xml/ns/pkgen-2.0"
      	xmlns:sec="http://geronimo.apache.org/xml/ns/security-1.1" xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.1">
      
      	<cmp-connection-factory>
      		<resource-link>ejbPool_esd</resource-link>
      
      	</cmp-connection-factory>
      
      	<enterprise-beans>
      
      		<entity>
      			<ejb-name>CustomerEJB</ejb-name>
      
               
      			<table-name>CUSTOMER</table-name>
      			<cmp-field-mapping>
      				<cmp-field-name>id</cmp-field-name>
      				<table-column>id</table-column>
      			</cmp-field-mapping>
      			<cmp-field-mapping>
      				<cmp-field-name>name</cmp-field-name>
      				<table-column>name</table-column>
      			</cmp-field-mapping>
      			<cmp-field-mapping>
      				<cmp-field-name>birthdate</cmp-field-name>
      				<table-column>birthdate</table-column>
      			</cmp-field-mapping>
      			<cmp-field-mapping>
      				<cmp-field-name>sssNo</cmp-field-name>
      				<table-column>sss_no</table-column>
      			</cmp-field-mapping>
      			<cmp-field-mapping>
      				<cmp-field-name>address</cmp-field-name>
      				<table-column>address</table-column>
      			</cmp-field-mapping>
      			<cmp-field-mapping>
      				<cmp-field-name>annualSalary</cmp-field-name>
      				<table-column>annual_salary</table-column>
      			</cmp-field-mapping>
      			<cmp-field-mapping>
      				<cmp-field-name>loanAmount</cmp-field-name>
      				<table-column>loan_amount</table-column>
      			</cmp-field-mapping>
      
      			<query>
      				<query-method>
      					<method-name>findBySssNo</method-name>
      					<method-params>
      						<method-param>java.lang.String</method-param>
      					</method-params>					
      				</query-method>
      				<ejb-ql>SELECT OBJECT(c) FROM CUSTOMER as c where c.sssNo = ?1</ejb-ql>
      			</query>
      
      			<query>
      				<query-method>
      					<method-name>findAll</method-name>
      					<method-params />
      				</query-method>
      				<ejb-ql>SELECT OBJECT(c) FROM CUSTOMER as c</ejb-ql>
      			</query>
      		</entity>
      	</enterprise-beans>
      </openejb-jar>
      
      CustomerPK.java
      public class CustomerPK implements java.io.Serializable, Comparable {
      
      	public Integer id;  
      	
      	public CustomerPK() {
      	}
      
      	public CustomerPK(Integer id) {
      		 this.id = id;
      	}
      	 
      	public boolean equals(Object obj) {
      		boolean ret = false;
      
      		if (obj == null || !(obj instanceof CustomerPK))
      			ret = false;
      		else if (obj == this)
      			ret = true;
      		else if ((((CustomerPK) obj).id).compareTo(id) == 0)
      			ret = true;
      
      		return ret;
      	}
      
      	public int hashCode() {
      		return id.hashCode();
      	}
      
      	public int compareTo(Object obj) {
      		return ((CustomerPK) obj).id.compareTo(id);
      	}
      }
      
      CustomerRemote.java
      public interface CustomerRemote extends EJBObject {
      	
      	public void setName(String name) throws RemoteException;
      	public String getName() throws RemoteException;
      	
      	public void setSssNo(String sssNo) throws RemoteException;
      	public String getSssNo() throws RemoteException;
      	
      	public void setAddress(String address) throws RemoteException;
      	public String getAddress() throws RemoteException;
      	
      	public void setBirthdate(Date birthdate) throws RemoteException;
      	public Date getBirthdate() throws RemoteException;
      	
      	public void setAnnualSalary(Double annualSalary) throws RemoteException;
      	public Double getAnnualSalary() throws RemoteException;
      	
      	public void setLoanAmount(Double loanAmount) throws RemoteException;
      	public Double getLoanAmount() throws RemoteException;
      }
      
      CustomerRemote.java
      public interface CustomerHome extends EJBHome {
      	public CustomerRemote create(Integer id,String name,Date birthdate,String sssNo,String address, Double annualSalary,Double loanAmount) throws CreateException,RemoteException;
      	
      	public CustomerRemote create(CustomerPK primaryKey)throws CreateException,RemoteException;
      
      	public CustomerRemote findByPrimaryKey(CustomerPK pk) throws FinderException, RemoteException;
      
      	public Collection findAll() throws FinderException, RemoteException;
      
      	public CustomerRemote findBySssNo(String sssNo) throws FinderException, RemoteException;
      }
      
      ...and here the test sample
      ...
      CustomerHome home = ...
      ...
      // create is OK
      home.create(new java.lang.Integer(11), "name", new java.util.Date(), "123", "address", new Double(2.0), new Double(2.0));			
      
      // findAll is OK
      Collection col = home.findAll(); 
      System.out.println("col.size=" + col.size());
      for (Iterator iterator = col.iterator(); iterator.hasNext();) {
      	Object object = (Object) iterator.next();
      	CustomerRemote remote = (CustomerRemote) object;
      	System.out.println(remote.getPrimaryKey() + " // " +  remote.getName() + " / " + remote.getAddress());
      			}
      // customer finder is OK too
      CustomerRemote cr =  home.findBySssNo("123");
      System.out.println(cr.getPrimaryKey() + " // " + cr.getName() + " / " + cr.getAddress());
      
      // but findByPrimaryKey throws an exception !! (see stacktrace)
      CustomerPK pk0 = new CustomerPK();
      pk0.id = new Integer(11);
      CustomerRemote remote =  home.findByPrimaryKey(pk0);
      System.out.println(remote.getPrimaryKey() + " // " + remote.getName() + " / " + remote.getAddress());
      ...
      

      EDIT: ok, I found the problem: firstable, I have to remove in ejb-jar.xml:
      <primkey-field>id</primkey-field>
      And, in my primary key class, when I create an entity, container throws a NullPointerExc.
      So, what is a correct implementation for hashCodeMethod ?
      I thought this one was a classic way:

      	
      public int hashCode() {
      	return id.hashCode();
      }
      

      But throws NPException, so the version below bypasses the problem:

      	
      public int hashCode() {
      	if (id == null) {
      		id = new Integer(0);
      	}
      	return id.hashCode();
      }
      

      What do you think of this implementation?
      Is it a correct analysis ?

      Attachments

        Activity

          People

            Unassigned Unassigned
            dolu DORE
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: