Выполнение операций CRUD в классе действий вместе с методом prepare() в Struts2

Предполагая следующий класс действий.

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value="struts-default")
public final class TestAction extends ActionSupport implements Serializable, ValidationAware, Preparable
{
    private Long currentPage=1L;

    //This method is called on page load. 
    //Validation is skipped, location is set to a valid action, not "redirectAction", the request is dispatched. 
    //It is mapped to an action of <s:form>.
    public String load() throws Exception
    {
        //Nothing to see here. Leave it empty.
        return ActionSupport.SUCCESS;
    }

    //Assuming necessary validators and action whose result type is set to "redirectAction". 
    //It is mapped to an action of <s:submit>.
    public String insert()
    {
        //Do something to either add or update data in a model based on a conditional check.
        return ActionSupport.SUCCESS;
    }

    //Assuming necessary validators and action whose loction is set to a valid action. not "redirectAction". 
    //The request is dispatched/forwarded. 
    //It is mapped to an action of <s:a>.
    public String edit()
    {
        //Nothing to see here. Leave it empty.
        return ActionSupport.SUCCESS;
    }

    //Assuming necessary validators and action whose result type is set to "redirectAction". 
    //It is mapped to an action of <s:submit>.
    public String delete()
    {
        //Do something to delete data from a model.
        return ActionSupport.SUCCESS;
    }

    @Override
    public void prepare() throws Exception
    {
        list= service.getList((int)(currentPage-1)*pageSize, pageSize);
    }
}

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

Здесь, когда, например, запускается действие, связанное с методом insert() (это выполняется <s:submit>), результатом будет действие перенаправления. Соответственно, будет создан новый экземпляр класса действия, который вызывает выполнение метода load(), который, в свою очередь, вызывает повторное выполнение метода prepare(). То же самое произойдет при обновлении и удалении.

Метод prepare() сначала выполняется, как только запускается действие, связанное с <s:submit> (или <s:link>), а затем еще раз, когда запрос перенаправляется (это можно понять, потому что перенаправление запроса приводит к созданию нового экземпляра класса действия, который вызывает действие, связанное с методом load(), которое должно выполняться, а prepare() выполняется один раз для каждого действия).

Единственная строка внутри метода prepare() имеет дорогостоящие операции. Чтобы метод getList() не выполнялся дважды, я выполняю некоторые условные проверки, как показано ниже.

@Override
public void prepare() throws Exception
{
    String actionName = ActionContext.getContext().getName();
    if(actionName.equals("load")||actionName.equals("edit"))
    {
        list= service.getList((int)(currentPage-1)*pageSize, pageSize);
    }
}

В этом методе может быть больше условных проверок и сложный код.

Этого еще недостаточно. Список не будет инициализирован, если из-за условия возникнут какие-либо ошибки проверки/преобразования. Ни один из hasErrors(), hasActionErrors() и hasFieldErrors() не будет оценен как истина в методе prepare() после любых ошибок. Для этого требуется, чтобы список был загружен внутри метода validate(), как показано ниже.

@Override
public void validate()
{
    if(hasErrors())
    {
        list= service.getList((int)(currentPage-1)*pageSize, pageSize);
    }
}

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

Есть ли лучший способ иметь какой-то механизм, гарантирующий, что извлечение списка из базы данных происходит только один раз после выполнения запроса на выполнение таких операций, как вставка, обновление, удаление?

Он не должен зависеть от того, сколько действий выполняется за сценой для каждого запроса. Список должен быть получен непосредственно перед завершением запроса только один раз, хотя есть некоторые ошибки преобразования/проверки.

Ни одна из аннотаций @Before, @BeforeResult, @After, кажется, не работает, чтобы обойти эту ситуацию.


Использование такого кода в методе validate(), который предназначен для извлечения/инициализации списка, не кажется хорошей практикой.

Я ожидаю, что есть способ получить этот список после операций CRUD. Поскольку получение этого списка из базы данных требует больших затрат, этот список следует инициализировать только один раз после завершения каждой операции (вставка, редактирование, обновление, удаление).


person Tiny    schedule 08.01.2014    source источник
comment
Я не уверен, что понимаю. Если вы сделаете перенаправление, вы получите новое действие и вам потребуются новые данные. Если есть ошибка проверки, выполняется метод input(). Вы можете либо жестко закодировать проверки в prepare(), либо создать prepareXxx() методы, где xxx заменяется именами методов, в которых вам нужен список. Все это в стороне, рассмотрите кеширование.   -  person Dave Newton    schedule 08.01.2014
comment
prepare вызывается для каждого действия, validate вызывается для проверки, оба выполняют разные задачи. Кажется, вы застряли с предоставленными методами. Создавайте свои собственные методы или классы и вызывайте эти методы или классы при необходимости.   -  person Roman C    schedule 08.01.2014


Ответы (1)


Выполнение в методе prepare(), который выполняется для каждого действия, тяжелой операции, такой как заполнение списков, не является хорошим способом. Потому что не все действия должны выполнять такую ​​операцию.

Но при использовании с проверкой никакое действие не выполняется вообще. Если возникают ошибки проверки, возвращается результат INPUT. Этот результат является результатом dispatcher в вашем сценарии использования, и он требует, чтобы списки были заполнены до того, как этот результат будет выполнен.

Вы выполняете ручные проверки в методе validate на наличие ошибок проверки, прежде чем метод validate завершится и будет возвращен результат INPUT, затем вы повторно заполните списки.

Эта логика уже реализована в workflow перехватчике, который является член defaultStack. Вы можете настроить этот перехватчик для вызова метода при возникновении ошибок проверки. В этом методе вы можете повторно заполнить списки.

public String input() throws Exception {
  list = service.getList((int)(currentPage-1)*pageSize, pageSize);
  return INPUT;
}

Теперь вы должны настроить этот метод для использования с перехватчиком workflow, разместив аннотацию к вашим грубым методам insert(), delete().

@InputConfig(methodName="input")

В методах edit() и load() вы можете просто вызвать input() вручную, если действие возвращает результат диспетчера, возможно, в том же месте, что и результат INPUT.

person Roman C    schedule 26.01.2014
comment
Я пробовал этот подход. Метод input() вызывается, как и ожидалось. Можно ли избежать кода для инициализации списка в методе prepare()? В большинстве случаев оба метода input() и prepare() будут иметь один и тот же повторяющийся фрагмент кода для инициализации списка. В моем случае, что я сейчас делаю, так это то, что когда возникает ошибка проверки/преобразования, список инициализируется в методе input() и в случае успеха список заполняется в методе prepare() (после некоторых уродливых условных проверок). Могу ли я иметь общий метод для инициализации этого списка (оба имеют одинаковый дублирующийся код)? - person Tiny; 27.01.2014
comment
Удалить метод prepare. Код, который инициализирует список, находится в методе input. - person Roman C; 27.01.2014
comment
После тщательного изучения это действительно кажется правильным путем! - person Tiny; 27.01.2014
comment
У меня сегодня было что-то очень похожее. Что я сделал, так это изменил свой стек перехватчиков и поместил перехватчик подготовки туда, где мне это нужно. - person Thrash Bean; 29.01.2014