views:

1043

answers:

2

I'm pretty sure that I'm not understanding something about JPA (I'm using OpenJPA) and it's causing this problem. I want to make a copy of a Job entity.

@Entity
@Table(name="Job")
public class Job implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    @ManyToOne
    private Job job;

    @OneToMany(fetch = FetchType.EAGER)
    private Set<Job> jobCollection;
    ...

    public Job getJob() {
         return this.job;
    }

    public void setJob(Job job) {
         this.job = job;
    }

    public Set<Job> getCopies() {
         return this.jobCollection;
    }

    public void setCopies(Set<Job> jobCollection) {
         this.jobCollection = jobCollection;
    }
}

Running the following code works as I would expect when creating the first copy.

public void testCopyJob(){
 Job job = jobManager.findJobById(100);
 Job jobWithCopies = null;
 try {
  jobWithCopies = jobManager.copyJob(job, "test copy");
 } catch (Exception e) {
  fail(e.getMessage());
 }
 Set<Job> copies = jobWithCopies.getCopies();
 assertEquals("num copies", 1, copies.size());

 //make a second copy
 Job jobWithCopies2 = null;
 try {
  jobWithCopies2 = jobManager.copyJob(jobWithCopies, "test copy");
  assertEquals("multiple copies", 2, jobWithCopies2.getCopies().size());
 } catch (Exception e) {
  e.printStackTrace();
  fail(e.getMessage());
 }
}

Attempting to create a second copy fails with...

[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R <openjpa-1.2.1-SNAPSHOT-r422266:686069 fatal general error> org.apache.openjpa.persistence.PersistenceException: null
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1688)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.StateManagerImpl.assignObjectId(StateManagerImpl.java:523)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.StateManagerImpl.assignField(StateManagerImpl.java:608)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.StateManagerImpl.beforeAccessField(StateManagerImpl.java:1494)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.StateManagerImpl.accessingField(StateManagerImpl.java:1477)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at entities.Job.pcGetid(Job.java)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at entities.Job.hashCode(Job.java:402)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at java.util.HashMap.putImpl(Unknown Source)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at java.util.HashMap.put(Unknown Source)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at java.util.HashSet.add(Unknown Source)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.util.java$util$HashSet$proxy.add(Unknown Source)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at entities.controller.JobManager.copyJob(JobManager.java:140)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at entities.controller.JobManagerTest.testCopyJob(JobManagerTest.java:55)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:45)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at java.lang.reflect.Method.invoke(Method.java:599)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestCase.runTest(TestCase.java:154)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestCase.runBare(TestCase.java:127)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestResult$1.protect(TestResult.java:106)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestResult.runProtected(TestResult.java:124)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestResult.run(TestResult.java:109)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestCase.run(TestCase.java:118)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestSuite.runTest(TestSuite.java:208)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at junit.framework.TestSuite.run(TestSuite.java:203)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.cactus.server.runner.ServletTestRunner.run(ServletTestRunner.java:309)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.cactus.server.runner.ServletTestRunner.doGet_aroundBody0(ServletTestRunner.java:187)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.cactus.server.runner.ServletTestRunner.doGet_aroundBody1$advice(ServletTestRunner.java:225)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.cactus.server.runner.ServletTestRunner.doGet(ServletTestRunner.java:1)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at javax.servlet.http.HttpServlet.service(HttpServlet.java:718)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1449)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:790)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:443)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.servlet.ServletWrapperImpl.handleRequest(ServletWrapperImpl.java:175)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:3610)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest(WebGroup.java:274)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:926)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1557)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:173)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:455)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:384)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:272)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:138)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:202)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:766)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:896)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1527)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R Caused by: java.lang.UnsupportedOperationException
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.DetachedStateManager.getMetaData(DetachedStateManager.java:696)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.meta.strats.UntypedPCValueHandler.toRelationDataStoreValue(UntypedPCValueHandler.java:121)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.sql.RowImpl.setRelationId(RowImpl.java:327)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.sql.SecondaryRow.setRelationId(SecondaryRow.java:106)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.meta.strats.HandlerStrategies.set(HandlerStrategies.java:150)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.meta.strats.HandlerStrategies.set(HandlerStrategies.java:104)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.meta.strats.HandlerCollectionTableFieldStrategy.insert(HandlerCollectionTableFieldStrategy.java:154)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.meta.strats.HandlerCollectionTableFieldStrategy.insert(HandlerCollectionTableFieldStrategy.java:130)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.meta.FieldMapping.insert(FieldMapping.java:579)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.insert(AbstractUpdateManager.java:197)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.populateRowManager(AbstractUpdateManager.java:139)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.persistence.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:73)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at com.ibm.ws.persistence.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:60)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.flush(JDBCStoreManager.java:655)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.DelegatingStoreManager.flush(DelegatingStoreManager.java:130)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2010)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:1908)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1679)
[11/25/08 8:46:56:546 EST] 00000018 SystemErr     R     ... 52 more

This exception is thrown in the JobManager.copyJob() method (at line: attachedJob.getCopies().add(newJob);)...

@JPAManager(targetEntity=entities.Job.class)
public class JobManager {

      private EntityManager getEntityManager() {
              EntityManagerFactory emf = Persistence
                      .createEntityManagerFactory("import");
              return emf.createEntityManager();
      }

    @Action(Action.ACTION_TYPE.FIND)
    public Job findJobById(int id) {
            EntityManager em = getEntityManager();
            Job job = null;
            try {
               job = (Job) em.find(Job.class, id);
            } finally {
               em.close();
            }
            return job;
    }

    public Job copyJob(Job job, String newJobName) throws Exception {
            EntityManager em = getEntityManager();
            Job attachedJob = null;
            try {
                    em.getTransaction().begin();
                   //merge any changes made to job
                            attachedJob = em.merge(job);
                  //copy the job and establish bi-directional relationship
                            Job newJob = new Job(job);
                            newJob.setName(newJobName);
                            newJob.setJob(attachedJob);
                            em.persist(newJob);
                            attachedJob.getCopies().add(newJob);    
                  em.getTransaction().commit(); //commit changes to original job
             } catch (Exception ex) {
                    try {
                            if (em.getTransaction().isActive()) {
                                  em.getTransaction().rollback();
   }
                    } catch (Exception e) {
                            ex.printStackTrace();
                            throw e;
  }
                    throw ex;
             } finally {
                    em.close();
             }
            return attachedJob;
     }
}

This is the generated DB schema I'm using...

CREATE TABLE Job (id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, JOB_ID INTEGER, PRIMARY KEY (id));
CREATE TABLE Job_jobCollection (JOB_ID INTEGER, element VARCHAR(254));
ALTER TABLE Job ADD FOREIGN KEY (JOB_ID) REFERENCES Job (id);
ALTER TABLE Job_jobCollection ADD FOREIGN KEY (JOB_ID) REFERENCES Job (id);

Looking at the OpenJPA 1.2 source code shows that the DetachedStateManager.getMetaData() method is not implemented so I'm wondering why it's being called. Any tips??

A: 

I'm not familiar with JPA and therefore I don't know if your JobManager is doing the wrong thing.

What will be really helpful is if you could provide a longer stacktrace so we can tell what led you to DetachedStateManager.getMetaData() and start from somewhere there.

yc

yclian
I hope someone answers this question too...and explains what the "@ManyToOne private Job job;"means in the Job class. I don't see how there can be a "Many" relationship when the isn't some kind of Collection.
Randy Stegbauer
+1  A: 

You don't show what your implementation of the copy constructor for Job is, but if you are copying the id from one Job as the id of the copy of that Job, then attempting to save the copy job will fail because JPA will know that the instance of Job with that Id lives in another object, and it is that original object that should be persisted.

It looks like your problem is actually in hashCode, though, and your sample shows that you don't have a hashCode implementation. It looks like openjpa instrumented you a hashCode implementation based on the value of Id. When you add something to a Set, the Set will call hashCode() on your object as part of the process of determining if the object is already in the Set.

I would try changing id from an int to an Integer (openjpa appears to be trying to assign from an object which might just be the boxed Integer). Or implement your own hashCode that just returns the value of Id.

Brian Deacon