Как удалить объект с постоянным/непостоянным полем коллекции, которое равно нулю?

У меня есть класс с именем Parent, и я храню эти объекты в хранилище данных с высоким уровнем репликации.

Каждый объект имеет бесхозную связь с дочерними объектами, которыми я управляю, сохраняя список объектов Key.

У меня есть веб-служба REST, которая возвращает родительские и все дочерние объекты в виде представления JSON. Чтобы использовать маршаллер Джексона, я беру коллекцию дочерних объектов и добавляю эту коллекцию к родительскому, используя необработанную коллекцию без определенных параметризованных типов.

Я не собирался сохранять поле «Коллекция». Тем не менее, поскольку по словам Энди из DataNucleus, документация Google JDO @Persistent потенциально неверна, я не добавлял аннотацию @NotPersistent на поле. Теперь у меня есть пользовательские данные в хранилище данных, и мне нужно быть осторожным, чтобы случайно не уничтожить их, изменив родительский класс. Я не уверен, возможно ли это или может ли это произойти, поэтому я действую осторожно.

В этом нулевом значении Collection cardList нет данных; однако я часто получаю сообщения об ошибках, связанных с невозможностью отсоединить поле.

  • Без каких-либо аннотаций в поле (которое, по словам Энди, по умолчанию равно @Persistent), я не могу удалить объект.
  • Если я поставлю @NotPersistent в поле, то ни к каким данным не будет доступа и ничего не будет работать.

Вот класс "Родитель":

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 ...

Вот код, который я использую для удаления объекта:

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 при попытке удалить объект:

Обратите внимание, что опять же, для этого поля не сохраняются никакие данные. Я также не могу успешно отсоединить поле.

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)

Вопросы:

  • Как я могу удалить родительский объект?

  • Что мне нужно сделать, чтобы удалить пустое поле коллекции из хранилища данных без потери всех моих данных? Я чувствую, что лучшим подходом может быть использование двух отдельных, но зеркальных объектов: один для хранения родительского и дочернего ключей в хранилище данных, а другой для возврата родителя и всех связанных дочерних элементов в виде представления JSON.

  • Есть ли способ задним числом сделать поле Collection cardList NotPersistent, чтобы я мог просто использовать его для сериализации данных во внешнем интерфейсе?


person jmort253    schedule 22.02.2012    source источник
comment
Не уверен, поможет ли это, но когда я запускаю @NotPersistent в коллекции childList, я получаю: «Класс не найден в CLASSPATH». как JDOException.   -  person jmort253    schedule 23.02.2012


Ответы (1)


Если ваша Коллекция используется только для временного преобразования @Persistent childRealKeys, то она должна быть @NotPersistent childList.

В этом контексте:

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

Получаете ли вы еще одно исключение в этом случае?

ОБНОВЛЕНИЕ от @jmort253:

Проверено, чтобы убедиться, что в проект не включены дубликаты JAR-файлов appengine. Я обновился ранее, и некоторые из старых JARS не были удалены из CLASSPATH. Загрузчик классов может загружать более старые версии и игнорировать более новые версии, и это происходило в моем случае. Оказывается, разрешение зависимостей решает проблему.

Кроме того, я могу без проблем использовать @NotPersistent и отсоединить объект с помощью транзакции и свойство PMF DataNucleus DetachOnClose как Энди описывает в этой группе Google.

Удаление родителя:

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();
    }
}

Родительский класс:

Теперь можно добавить @NotPersistent, не получая предупреждений о пути к классам. В дополнение к проблемам с зависимостями JAR мне пришлось добавить параметризованный тип в определение коллекции. Если он опущен, вы получите следующее предупреждение:

  • Класс "" не найден в CLASSPATH. Пожалуйста, проверьте вашу спецификацию и ваш CLASSPATH.

     @PersistenceCapable(detachable = "true")
     public class Parent implements Serializable { 
        ...
        @NotPersistent
        private Collection<Child> childList;
        ...      
     }
    
person TheArchitect    schedule 23.02.2012
comment
Да, я добавил закомментированную вами строку в качестве шага по устранению неполадок. Казалось, это не имело значения. Кроме того, я упомянул в своем комментарии к вопросу, что добавление аннотации @NotPersistent привело к тому, что «Класс не был найден в CLASSPATH». Выбрасывается JDOException. - person jmort253; 23.02.2012
comment
Я обновил ваш ответ и принял его, поскольку то, что вы добавили, помогло мне в конечном итоге найти причину и решение моей проблемы. Спасибо! - person jmort253; 23.02.2012