简体   繁体   中英

How To Delete an Object With Persistent/Non-Persistent Collection Field That is null?

I have a class called Parent , and I'm storing these object's in the High replication datastore.

Each object has an unowned relationship with child objects that I manage by storing a List of Key objects.

I have a REST web service that returns the Parent and all Child objects as a JSON representation. In order to use the Jackson marshaller, I take the collection of Child objects and add that collection to the Parent using a raw Collection with no parameterized types defined.

The Collection field was not something I intended to persist. Yet, because According to DataNucleus's Andy, Google's JDO @Persistent documentation is potentially incorrect , I didn't put a @NotPersistent annotation on the field. Now I have user data in the datastore and need to be careful not to accidentally destroy it by modifying the Parent class. I'm not sure if that's possible or if it can happen, so I'm proceeding cautiously.

There is no data stored in this null Collection cardList value; however, I oftentimes get errors about not being able to detach the field.

  • Without any annotations on the field, (which according to Andy, defaults to @Persistent), I am unable to delete the object.
  • If I put @NotPersistent on the field, then none of the data can be accessed and nothing works.

Here is the class "Parent":

public class Parent implements Serializable {

    private static final long serialVersionUID = 1L;    

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String keyString;

    @Persistent
    private String name; 

    // store keys that associate with Child objects
    @Persistent 
    private List<Key> childRealKeys = new ArrayList<Key>();

    /** 
     * If @NotPersistent, no Parent is accessible.
     * If @NotPersistent is commented, the data loads, but I cannot delete the parent.
     *
     * This field was not intended to be stored and is just used to serialize the Parent
     * and Children to a single JSON object to be returned in a REST call.
     */
     // @NotPersistent
     private Collection childList = null;

     // getter for the field I don't want to store but just use to return children in the REST service as JSON
     public Collection getChildList() { return childList; }

     // remaining getters and setters follow ...

Here is the code I'm using to delete the object :

public void deleteParent(String keyString) {

    PersistenceManager pm = PMF.getInstance().getPersistenceManager();
    Parent parent = null;

    Key parentKey = KeyFactory.stringToKey(keyString);
    parent = pm.getObjectById(Parent.class, parentKey);

    // I tried detaching to see if that helps. It still says the field is not detached!
    Parent detachedParent = pm.detachCopy(parent);
    pm.deletePersistent(detachedParent.getChildList());
    pm.deletePersistent(detachedParent);

    pm.close();

}

StackTrace when trying to delete object:

Note that again, no data is stored for this field. I also can't seem to successfully detach the field.

javax.jdo.JDODetachedFieldAccessException: You have just attempted to access field "childList" yet this field was not detached when you detached the object. Either dont access this field, or detach it when detaching the object.
at com.fullcreative.loop.Parent.jdoGetChildList(Parent.java)
at com.fullcreative.loop.Parent.getChildList(Parent.java:112)
at com.fullcreative.loop.LoopDaoJdo.deleteParent(LoopDaoJdo.java:690)
at com.fullcreative.loop.LoopService.deleteParent(LoopService.java:551)
at com.fullcreative.loop.LoopController.deleteParent(LoopController.java:1022)
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 com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:104)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doDelete(FrameworkServlet.java:582)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:643)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:369)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at com.fullcreative.loop.security.auth.GaeAuthenticationFilter.doFilter(GaeAuthenticationFilter.java:227)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:168)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:35)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:60)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:78)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:362)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

Questions:

  • How can I delete the Parent object?

  • What do I need to do to remove the null Collection field from the datastore without losing all my data? I feel like the best approach might be to use 2 separate but mirrored objects: One for storing the parent and child keys in the datastore and another for returning the parent and all associated children as a JSON representation.

  • Is there a way to retroactively make the Collection cardList field NotPersistent so I can just use it for serializing data on the frontend?

If your Collection is only used for transforming @Persistent childRealKeys in a transient way, then it should be @NotPersistent childList.

In that context:

// pm.deletePersistent(detachedParent.getChildList()); becomes unnecessary, and
pm.deletePersistent(parent); //should work

Are you getting another exception in that case?

UPDATE by @jmort253:

Checked to be sure there aren't duplicate appengine JAR files included in the project. I upgraded earlier and some of the older JARS weren't removed from the CLASSPATH. The classloader may load the older versions and ignore the newer versions, and this was happening in my case. It turns out that resolving the dependencies takes care of the issue.

Additionally, I can then use @NotPersistent without issues, and I can detach the object using a Transaction and the PMF DataNucleus DetachOnClose property as Andy describes in this Google Group .

Deleting Parent :

public boolean deleteLoop(String parentId) {
    PersistenceManager pm = PMF.getInstance().getPersistenceManager();
    Transaction tx = pm.currentTransaction();
    try {
        tx.begin();
        pm.setDetachAllOnCommit(true);
        Parent parent = null;

        Key parentKey = KeyFactory.stringToKey(parentId);
        loop = pm.getObjectById(Parent.class, parentKey);

        Parent detachedParent = pm.detachCopy(parent);

                    // this was indeed not necessary
        //pm.deletePersistent(detachedParent.getChildList());

                    // no detachment issues, object deletes just fine.
        pm.deletePersistent(detachedParent);

        tx.commit();
        //pm.close();

    } catch(Exception e) {
        log.error("Exception trying to delete Parent :: ",e);
        e.printStackTrace();


    } finally {

        if(tx.isActive()) {
            tx.rollback();
        }

        pm.close();
    }
}

Parent Class:

Can now add @NotPersistent without getting classpath warnings. In addition to the JAR dependency issues, I had to add a parameterized type to the Collection definition. If it's omitted, you get the following warning:

  • Class "" was not found in the CLASSPATH. Please check your specification and your CLASSPATH.

      @PersistenceCapable(detachable = "true") public class Parent implements Serializable { ... @NotPersistent private Collection<Child> childList; ... } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM