Неожиданные отсоединенные объекты с расширенным постоянным контекстом

Я пытаюсь сохранить состояние для нескольких вызовов, используя EXTENDED_PERSISTENT_CONTEXT. Насколько я понимаю, управляемые объекты не будут отсоединяться между вызовами, однако я продолжаю получать ошибки, связанные с отсоединенными объектами в вызовах, после того, как я ранее выдавал ошибки проверки. Состояние сохраняется в сеансовом компоненте с отслеживанием состояния:

@Named(SessionFacadeBean.SEAM_NAME)
@SessionScoped
@Stateful
@LocalBean
@AccessTimeout(value = 10, unit = TimeUnit.SECONDS)
public class SessionFacadeBean implements Serializable
{
    public static final String  SEAM_NAME        = "sessionCacheBean";

    @PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME, type = PersistenceContextType.EXTENDED)
    private EntityManager       em;

    private ParentOne sessionData;

    public synchronized ParentOne getSessionData() {
        if(sessionData == null) {
            sessionData = new ChildTwo();
        }
        return sessionData;
    }

    public boolean getLock() {
        return true;
    }

    public void clearLock() {
    }

    // Other stuff I don’t ‘think’ is relevant.
}

(Упрощенное) состояние сохраняется с использованием спящего режима. Он состоит из трех классов (родительский и два дочерних, один из которых содержит список дочерних элементов):

@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Class", length = 50)
@Entity
public class ParentOne 
{   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @XmlElement(name = "ID")
    private Long              iD;

    @XmlElement(name = "name")
    protected String              friendlyName          = "";
}


@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Entity
public class ChildOne extends ParentOne
{
    public ChildOne(String name, ParentOne child) {
        super(name);
        myChild = child;
    }

    @ManyToOne(cascade = CascadeType.ALL)
    protected ParentOne myChild;   
}


@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Entity
public class ChildTwo extends ParentOne
{
    public ChildTwo() {
            super(“common”);
    }
}

Я обращаюсь к bean-компоненту с состоянием из bean-компонента без сохранения состояния следующим образом:

@Stateless
@LocalBean
@Path("/")
public class MyService
{
    @PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME)
    private EntityManager       em;

    @Inject
    private SessionFacadeBean   sessionBean;

    @POST
    @Path("/create/item")
    @ValidateRequest
    public ComponentShortSummary addItem(@Form NewItemForm itemForm)
    {       
        if(sessionBean.getLock()) {
            try {
                if(itemForm.getName().equals("INVALID") == true) {
                    throw new ConstraintViolationException("Failed", new HashSet<ConstraintViolation<?>>());
                }

                ChildOne child = new ChildOne(itemForm.getName(), sessionBean.getSessionData());
                em.persist(child);
                return null;
            }
            finally {
                sessionBean.clearLock();
            }
        } else {
            return null;
        }
    }
}

Чтобы воспроизвести проблему, я выполняю следующую последовательность:

  • Вызовите addItem с допустимым именем (это сохранит элемент в базе данных).
  • Вызовите addItem с именем «INVALID», это вызовет исключение ограничения.
  • Вызовите addItem с допустимым именем (это приводит к ошибке отсоединенной сущности в строке em.persist(child).

Чего я не понимаю, так это того, как/почему я оказался с отсоединенными сущностями. В реальном коде я бы выполнял некоторую проверку запроса/состояния перед изменением состояния (поэтому я не вижу причин, по которым состояние было бы отсоединено).

Если я уберу вызов sessionBean.getLock(), проблема исчезнет (объекты сохраняются правильно). Целью методов блокировки, по сути, является сериализация доступа к состоянию сеанса, однако в настоящее время метод getLock() пуст, похоже, проблема может быть связана с тем фактом, что я вызываю bean-компонент с состоянием перед тем, как выдать исключение.

Может ли кто-нибудь объяснить, что происходит, что приводит к тому, что мои объекты становятся отсоединенными / если есть способ избежать этого (и в идеале указать мне на любую документацию, подтверждающую объяснение)?

Хотя, вероятно, есть способы обойти текущую проблему, выполняя проверку перед доступом к компоненту с отслеживанием состояния вообще, меня беспокоит общий случай (где любое исключение генерируется после обращения к компоненту с отслеживанием состояния в вызове). Существует ли общепринятая стратегия работы с исключениями, когда я не хочу, чтобы объекты из расширенного постоянного контекста были отсоединены?


person forsvarir    schedule 01.10.2012    source источник


Ответы (1)


Похоже, это ожидаемое поведение. Благодаря ссылке Скотта Марлоу на спецификацию JPA, раздел 3.3.2.

Откат транзакции Как в контексте транзакции, так и в контексте расширенного сохранения, откат транзакции приводит к тому, что все ранее существовавшие управляемые экземпляры и удаленные экземпляры[31] становятся отсоединенными. Состоянием экземпляров будет состояние экземпляров на момент отката транзакции. Откат транзакции обычно приводит к тому, что контекст сохранения находится в несогласованном состоянии в момент отката. В частности, состояние атрибутов версии и сгенерированное состояние (например, сгенерированные первичные ключи) могут быть несогласованными. Экземпляры, которые ранее управлялись контекстом постоянства (включая новые экземпляры, ставшие постоянными в этой транзакции), поэтому могут быть недоступны для повторного использования таким же образом, как и другие отсоединенные объекты — например, они могут дать сбой при передаче в операцию слияния.[32] ]

Таким образом, сущности, участвующие в активной транзакции, отсоединяются при откате транзакции, и, вызывая sessionBean, я вовлекаю его в транзакцию.

Один из способов обойти это, по-видимому, состоит в том, чтобы украсить допустимые исключения аннотацией @AppicationException. Это помечает исключение как нефатальное и предотвращает откат транзакции. Этот подход подробно описан Дэвидом Блевином.

person forsvarir    schedule 09.10.2012