Details
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
<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>fr.chronopost.ejb.esd.customer.server.CustomerBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>fr.chronopost.ejb.esd.customer.interfaces.CustomerPK</prim-key-class> <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>
<?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>
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); } }
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; }
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; }
... 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 ?